How to Set Up Jenkins for Security Scanning with Nessus

How to Set Up Jenkins for Security Scanning with Nessus

Nessus was one of the first vulnerability scanners I ever used, long before I got into CI/CD pipelines, and it’s still one of the most trusted tools for infrastructure and network vulnerability assessment. When I started building out DevSecOps pipelines, wiring Nessus into Jenkins felt like a natural fit — it let me catch missing patches, misconfigurations, and known CVEs on freshly provisioned servers automatically, right after infrastructure changes went out, rather than waiting for a quarterly compliance scan to catch them months later.

This guide covers the full setup: from installing and configuring Nessus, to triggering scans from Jenkins, parsing results, and gating builds.

Why Nessus in a CI/CD Pipeline

Nessus, built by Tenable, is primarily a network and host vulnerability scanner. It checks for missing patches, outdated software, weak configurations, and known CVEs across servers, network devices, and even some cloud assets. In a DevOps context, it’s most commonly used to:

  • Scan newly provisioned infrastructure (VMs, containers hosts) before they’re added to production load balancers.
  • Run scheduled compliance scans tied to build pipelines for audit trails.
  • Validate that configuration management tools (Ansible, Terraform) haven’t introduced insecure defaults.

Jenkins Architecture Context

Nessus exposes a REST API (available in Nessus Professional, Nessus Manager, and Tenable.io/Tenable.sc) that Jenkins can call to launch scans, check status, and pull results. Unlike DAST tools that scan running web apps, Nessus typically targets IP ranges or hostnames directly, meaning it usually fits into your pipeline right after infrastructure provisioning, rather than after an application-layer deployment.

Prerequisites

  • A running Nessus instance (Nessus Professional, Nessus Manager, or Tenable.io) with API access enabled.
  • A Nessus API key (access key + secret key) or session-based authentication.
  • Network reachability between Jenkins agents and the Nessus scanner, and between the Nessus scanner and target hosts.
  • Jenkins with HTTP Request Plugin, Pipeline Utility Steps, and Credentials Binding Plugin.

Step 1: Generate Nessus API Keys

In the Nessus web UI, go to Settings > My Account > API Keys and generate an access key and secret key. Store both securely.

Step 2: Store Nessus Credentials in Jenkins

Add two Secret text credentials in Jenkins:

  • nessus-access-key
  • nessus-secret-key

Step 3: Create a Scan Policy/Template in Nessus (One-Time Setup)

Before automating via API, create a reusable scan policy in the Nessus UI (e.g., “Basic Network Scan” or a custom compliance policy), and note its template_uuid or policy ID — you’ll reference this when launching scans programmatically.

Step 4: Jenkinsfile for Nessus Scan Integration

pipeline {
    agent any

    environment {
        NESSUS_HOST = 'https://nessus.internal:8834'
        SCAN_POLICY_UUID = 'ab4bacd2-05f6-425c-9d79-6e8cdf5f1b19' // your policy UUID
        TARGET_HOSTS = '10.0.1.10,10.0.1.11,10.0.1.12'
    }

    stages {
        stage('Provision Infrastructure') {
            steps {
                sh 'terraform apply -auto-approve'
            }
        }

        stage('Create Nessus Scan') {
            steps {
                withCredentials([
                    string(credentialsId: 'nessus-access-key', variable: 'ACCESS_KEY'),
                    string(credentialsId: 'nessus-secret-key', variable: 'SECRET_KEY')
                ]) {
                    script {
                        def response = httpRequest(
                            url: "${NESSUS_HOST}/scans",
                            httpMode: 'POST',
                            customHeaders: [
                                [name: 'X-ApiKeys', value: "accessKey=${ACCESS_KEY}; secretKey=${SECRET_KEY}"],
[name: ‘Content-Type’, value: ‘application/json’]

], requestBody: “””{ “uuid”: “${SCAN_POLICY_UUID}”, “settings”: { “name”: “Jenkins-Build-${env.BUILD_NUMBER}”, “text_targets”: “${TARGET_HOSTS}” } }”””, ignoreSslErrors: true ) def json = readJSON text: response.content env.SCAN_ID = json.scan.id echo “Created scan with ID: ${env.SCAN_ID}” } } } } stage(‘Launch Scan’) { steps { withCredentials([ string(credentialsId: ‘nessus-access-key’, variable: ‘ACCESS_KEY’), string(credentialsId: ‘nessus-secret-key’, variable: ‘SECRET_KEY’) ]) { httpRequest( url: “${NESSUS_HOST}/scans/${env.SCAN_ID}/launch”, httpMode: ‘POST’, customHeaders: [[name: ‘X-ApiKeys’, value: “accessKey=${ACCESS_KEY}; secretKey=${SECRET_KEY}”]], ignoreSslErrors: true ) } } } stage(‘Poll Scan Status’) { steps { withCredentials([ string(credentialsId: ‘nessus-access-key’, variable: ‘ACCESS_KEY’), string(credentialsId: ‘nessus-secret-key’, variable: ‘SECRET_KEY’) ]) { script { def status = ‘running’ timeout(time: 60, unit: ‘MINUTES’) { while (status != ‘completed’) { sleep(60) def check = httpRequest( url: “${NESSUS_HOST}/scans/${env.SCAN_ID}”, customHeaders: [[name: ‘X-ApiKeys’, value: “accessKey=${ACCESS_KEY}; secretKey=${SECRET_KEY}”]], ignoreSslErrors: true ) def json = readJSON text: check.content status = json.info.status echo “Scan status: ${status}” } } } } } } stage(‘Evaluate Results’) { steps { withCredentials([ string(credentialsId: ‘nessus-access-key’, variable: ‘ACCESS_KEY’), string(credentialsId: ‘nessus-secret-key’, variable: ‘SECRET_KEY’) ]) { script { def resultResponse = httpRequest( url: “${NESSUS_HOST}/scans/${env.SCAN_ID}”, customHeaders: [[name: ‘X-ApiKeys’, value: “accessKey=${ACCESS_KEY}; secretKey=${SECRET_KEY}”]], ignoreSslErrors: true ) def json = readJSON text: resultResponse.content def critical = json.hosts.findAll { it.severitycount.item.find { s -> s.severitylevel == 4 } } if (critical.size() > 0) { error “Critical vulnerabilities found on ${critical.size()} host(s). Failing build.” } else { echo “No critical vulnerabilities found.” } } } } } } post { failure { mail to: ‘security-team@example.com’, subject: “Nessus Scan Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}”, body: “Critical vulnerabilities detected on scanned infrastructure. Check Nessus dashboard.” } } }

Exporting and Archiving Reports

Nessus supports exporting scan results in multiple formats (Nessus, HTML, PDF, CSV). Adding an export stage makes results easy to archive and share:

stage('Export Report') {
    steps {
        withCredentials([
            string(credentialsId: 'nessus-access-key', variable: 'ACCESS_KEY'),
            string(credentialsId: 'nessus-secret-key', variable: 'SECRET_KEY')
        ]) {
            script {
                def exportResp = httpRequest(
                    url: "${NESSUS_HOST}/scans/${env.SCAN_ID}/export",
                    httpMode: 'POST',
                    customHeaders: [
                        [name: 'X-ApiKeys', value: "accessKey=${ACCESS_KEY}; secretKey=${SECRET_KEY}"],
[name: ‘Content-Type’, value: ‘application/json’]

], requestBody: ‘{“format”: “pdf”}’, ignoreSslErrors: true ) echo “Export triggered: ${exportResp.content}” } } } }

You’d then poll the export status and download the file token similarly to how the scan itself is polled, then archive it with archiveArtifacts.

Integrating with Terraform and Ansible

Nessus fits particularly well right after terraform apply:

stage('Provision & Scan') {
    steps {
        sh 'terraform apply -auto-approve'
        script {
            env.TARGET_HOSTS = sh(script: "terraform output -raw instance_ips", returnStdout: true).trim()
        }
    }
}

Pulling the target IPs directly from Terraform output means you never have to hardcode IP addresses — new infrastructure gets scanned automatically no matter how it’s provisioned.

With Ansible, a common pattern is triggering a Nessus scan as a post-playbook validation step, confirming that configuration changes haven’t introduced any new vulnerabilities or left default credentials active.

Integrating with Cloud Platforms

Tenable.io (the cloud-hosted version of Nessus) integrates with AWS, Azure, and GCP asset discovery, automatically importing newly launched instances as scan targets. This is especially useful in auto-scaling environments where instances are created and destroyed dynamically — you don’t want to hardcode a static IP list in your Jenkinsfile when your infrastructure is elastic.

Monitoring and Long-Term Tracking

Nessus/Tenable.io dashboards provide historical vulnerability trends across scans. I recommend combining this with Jenkins build history — tagging each scan with the build number (as shown in the example) makes it trivial to trace which infrastructure change introduced a new vulnerability.

Troubleshooting Common Issues

  • SSL certificate errors — Nessus typically uses a self-signed cert by default; use ignoreSslErrors: true in the HTTP Request plugin for internal scanners, or install a proper CA-signed cert for production use.
  • Scan takes far longer than expected — Nessus scan duration scales heavily with the number of plugins enabled and target count; use a lighter “Basic Network Scan” policy for CI/CD and reserve full compliance scans (like PCI or CIS benchmarks) for scheduled nightly runs.
  • Authentication errors (401) — double check API key format in the X-ApiKeys header; a common mistake is missing the semicolon separator between accessKey and secretKey.

Best Practices

  • Use lightweight scan policies for per-build scans; reserve deep compliance scans (CIS, PCI-DSS templates) for scheduled runs.
  • Pull target IPs dynamically from Terraform/cloud provider outputs rather than hardcoding them.
  • Export and archive scan reports as Jenkins artifacts for audit trails.
  • Gate builds only on critical/high severity findings initially to avoid pipeline fatigue.
  • Secure Nessus API keys via Jenkins Credentials and rotate them periodically.

FAQs

Can Nessus scan cloud-native resources like S3 buckets or IAM policies? Standard Nessus is primarily host/network focused; for cloud configuration scanning, Tenable.io’s Cloud Security module or a dedicated CSPM tool is a better fit alongside Nessus.

How is Nessus different from Qualys? Both are vulnerability management platforms with overlapping functionality; the choice often comes down to existing licensing, UI/workflow preference, and specific compliance template support your organization needs.

Do I need a Nessus scanner on every network segment? Yes — Nessus scanners need direct network reachability to targets, so distributed infrastructure across VPCs or on-prem segments typically requires multiple scanner appliances or a properly routed network path.

Can I run authenticated scans for deeper results? Yes, and it’s strongly recommended — providing SSH/Windows credentials to Nessus enables authenticated scans that detect far more issues (like missing OS patches) than unauthenticated network-only scans.

Summary

Integrating Nessus into Jenkins lets you catch infrastructure-level vulnerabilities as part of your normal delivery pipeline rather than during a separate, disconnected security review. By launching scans via the Nessus REST API right after provisioning, polling for completion, and gating builds on severity thresholds, you build continuous vulnerability visibility directly into your CI/CD workflow — closing the gap between “when a vulnerability appears” and “when someone notices.”

References

  • Tenable Nessus API Documentation: https://developer.tenable.com/reference/navigate
  • Tenable.io Documentation: https://docs.tenable.com/tenableio.htm
  • Jenkins HTTP Request Plugin: https://plugins.jenkins.io/http_request/
  • Jenkins Pipeline Utility Steps Plugin: https://plugins.jenkins.io/pipeline-utility-steps/

Total
1
Shares

Leave a Reply

Previous Post
How to Set Up Jenkins for Container Vulnerability Scanning with Trivy

How to Set Up Jenkins for Container Vulnerability Scanning with Trivy

Next Post
How to Set Up Jenkins for Security Scanning with Qualys

How to Set Up Jenkins for Security Scanning with Qualys

Related Posts