Skip to main content

Command Palette

Search for a command to run...

Building a Secure DevSecOps CI/CD Pipeline for a Spring Boot Application Using Jenkins, Snyk, SonarQube, Docker, Trivy, and AWS EKS

Updated
29 min read
Building a Secure DevSecOps CI/CD Pipeline for a Spring Boot Application Using Jenkins, Snyk, SonarQube, Docker, Trivy, and AWS EKS

1. Overview

In today’s fast-paced software development world, speed alone is not enough. Applications must be secure, reliable, and production-ready before they ever reach users. This is where DevSecOps comes into the picture integrating security directly into the CI/CD pipeline instead of treating it as a separate, last-minute activity.

In this blog, we walk through a real-time, industry-style Jenkins pipeline that demonstrates how DevSecOps is implemented in practice. This pipeline does not stop at just building and deploying an application; it continuously scans source code, third-party dependencies, Docker images, and even the Kubernetes cluster itself.

The objective of this pipeline is simple and solid:

  • Detect security issues as early as possible

  • Automate quality and security checks

  • Ensure only secure artifacts reach production

  • This setup reflects how modern organizations use GitHub, Jenkins, Snyk, Docker, Trivy, SonarQube, and Kubernetes (EKS) together to build a secure CI/CD ecosystem.

2. What is DevSecOps?

DevSecOps is the next evolution of DevOps that integrates security practices directly into every stage of the software development lifecycle. Rather than addressing security only after deployment, DevSecOps ensures that security is built-in from the very first code commit to production.

Modern DevSecOps pipelines combine various types of security testing, including:

  • SAST (Static Application Security Testing): White-box testing that analyzes the source code or binaries for vulnerabilities without running the application. Tools like SonarQube are typically used for SAST.

  • DAST (Dynamic Application Security Testing): Black-box testing that tests the running application for security issues, simulating real-world attacks.

  • SCA (Software Composition Analysis): Scans third-party libraries and dependencies for known vulnerabilities (CVEs) and license issues. Tools like Snyk handle SCA.

Key Benefits of DevSecOps:

  • Early detection of vulnerabilities: Identify security risks before they reach production.

  • Automated and faster secure deployments: Security checks are part of the CI/CD process, not a separate step.

  • Improved collaboration among Dev, Sec, and Ops teams: Everyone shares responsibility for security.

  • Continuous monitoring and compliance: Security and compliance checks run automatically, even after deployment.

3.Launch Ubuntu Server (t2.large)

I started by launching a t2.large EC2 instance with the Ubuntu Server image from AWS. This instance will serve as our CI server.

  • Instance Type: t2.large or t3.large (2 vCPU, 8 GB RAM)

  • Storage: 15 GB (gp3)

  • Storage below 15 GB may struggle under tool load.

4. Installing Jenkins on Ubuntu

  • Started with an Ubuntu EC2 instance (t2.large or t3.large).

  • Connect to your console and install Jenkins.

  • Run this script in root user

vi jenkins.sh # Run this script in root user (sudo su -)
#!/bin/bash
sudo apt update -y
#sudo apt upgrade -y
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | tee /etc/apt/keyrings/adoptium.asc
echo "deb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
sudo apt update -y
sudo apt install temurin-17-jdk -y
/usr/bin/java --version
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
                  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
                  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
                              /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update -y
sudo apt-get install jenkins -y
sudo systemctl start jenkins
sudo systemctl status jenkins

  • Run this script in root user (sudo su -)

  • Once Jenkins is installed, you’ll need to allow traffic on port 8080 the default port Jenkins uses for its web interface.
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

5. Install Docker & Configure Permissions

sudo apt-get update
sudo apt-get install docker.io -y
sudo usermod -aG docker $USER   # ubuntu or ec2-user 
newgrp docker
sudo chmod 777 /var/run/docker.sock

  • Check the Docker info after this to ensure it is working fine.

  • Give Docker permissions to the Jenkins users.
sudo usermod -aG docker jenkins
sudo systemctl restart jenkins

groups jenkins This command checks which groups the Jenkins user belongs to.
At first, it will show: This means Jenkins is only in its own group not in the docker group yet. So Jenkins cannot run Docker commands for now.

  • sudo usermod -aG docker jenkins
    This command adds the Jenkins user into the Docker group.
    -aG means: add to the group without removing existing ones.
    Now Jenkins will have permission to run Docker.

  • getent group docker
    This checks who is inside the Docker group.
    If you see output like this:

    That means Jenkins is now successfully added to the Docker group. Good job!

  • sudo systemctl restart jenkins
    Restart Jenkins service so it can apply the new group changes.
    Now Jenkins is ready to use Docker in your pipeline.

6. Run SonarQube Container

docker run -d --name sonar -p 9000:9000 sonarqube:lts-community

  • After running the container, verify that it is running:
docker ps
  • Now, enable port 9000 in your AWS Security Group (Inbound rules → Add Rule → Port 9000 → Allow).

    Once the port is enabled, open SonarQube in your browser:

  • Now you can see that SonarQube is opened successfully.

7. Create Jenkins Pipeline Job

  • Go to your Jenkins dashboard.

  • Click “New Item”, give your job a name (e.g.,DevOps).

7.1 Required Jenkins Plugins

  • Docker: Docker, Docker Pipeline, Docker Build Step

  • SonarQube: SonarQube Scanner, Quality Gates Plugin

7.2 Configure Maven

  • Go to Manage Jenkins > Global Tool Configuration.

  • Under Maven, click Add Maven.

  • Name it something like maven-3.9.9.

  • Select Install automatically and choose version 3.9.9.

  • Save your configuration.

8. Snyk Integration (Security Scanning)

8.1 Overview

  • Snyk is a developer-first security tool used in DevSecOps pipelines to identify and fix vulnerabilities early in the software development lifecycle.

  • Snyk performs white-box security testing, meaning it analyzes the actual source code and dependencies rather than testing a running application from outside.

  • By integrating Snyk into Jenkins, we ensure that security scanning happens automatically during CI, not after deployment.

8.2 Key Purposes of Using Snyk

  • Detect security issues before production

  • Scan both custom code and open-source dependencies

  • Automate security checks inside Jenkins pipelines

  • Enforce security as code

8.3 Step-by-Step Snyk Integration in Jenkins

  • Before integrating Snyk with Jenkins, we must create a Snyk account and generate an API token. Jenkins uses this token to authenticate and perform security scans.

8.4 Create a Snyk Account

  • Snyk is a cloud-based security platform.

  • To scan projects and fetch vulnerability data, Jenkins must authenticate with Snyk using an API token, which is generated only after creating an account.

  • How to Create a Snyk Account

  • Go to https://snyk.io

  • Click Sign Up

  • Choose a sign-up method: GitHub ,Google ,Bitbucket

  • In this setup, the account is created using a Google account.

  • Complete the login and reach the Snyk dashboard.

8.5 Generate Snyk API Token for CI/CD Pipelines

  • In Snyk dashboard, go to: Account Settings

  • On this page, you will see an Auth Token with the message:

  • “Use this token to authenticate the Snyk CLI and in CI/CD pipelines.”

  • This token is automatically generated by Snyk when you create and log in to your account (using Google, GitHub, etc.).

What You Need to Do

  • Copy the generated Snyk API Token shown on this page keep this token secure.

  • This token will be used by Jenkins / CI/CD pipelines to authenticate with Snyk

  • Do not hardcode this token in pipeline scripts.


8.6 Install Snyk Plugin in Jenkins

The Snyk Jenkins plugin enables Jenkins pipelines to communicate securely with Snyk services.

  • Go to Jenkins Dashboard

  • Navigate to: Manage Jenkins → Plugin

  • Search for Snyk Security Plugin

  • Install the plugin

  • The plugin acts as a bridge between Jenkins and Snyk.


8.7 Add Snyk Token to Jenkins Credentials

  • Prevents exposing tokens in pipeline scripts

  • Ensures secure authentication

  • Allows token reuse across multiple jobs

  • Go to Manage Jenkins → Credentials

  • System → Global → Add Credentials Fill the details:

  • Fill the details

  • Kind: Secret Text

  • Secret: Paste your Snyk API Token

  • ID: SNYK_TOKEN

  • Description: SNYK_TOKEN

  • Click Create

  • This allows Jenkins pipelines to access Snyk securely without hardcoding secrets.


8.8 Install Node.js, NPM, and Snyk CLI (Required for Snyk CLI)

Why Node.js Is Required

  • The Snyk CLI is built on Node.js, so the Jenkins server must have the following installed:

  • Node.js – Runtime environment for Snyk CLI

  • NPM (Node Package Manager) – Used to install Snyk CLI

  • Snyk CLI – Executes security scans in CI/CD pipelines

  • Without Node.js and NPM, Snyk CLI cannot run install Node.js and NPM Snyk CLI

sudo apt update
sudo apt install -y nodejs npm 
sudo npm install -g snyk

The Snyk CLI performs automated security scans during the CI pipeline, including:

  • SAST (Static Application Security Testing)
    Scans application source code to identify security vulnerabilities.

  • SCA (Software Composition Analysis)
    Scans open-source dependencies for known vulnerabilities and license issues.

Verify Installation

node -v
snyk -v
npm -v

If versions are displayed, installation is successful

8.9 Writing the Initial Jenkins Pipeline Script

Let's first verify that Jenkins can successfully clone your code and build it using Maven. This is a basic test to ensure everything is set up correctly.

Goal: To check if the build process works without errors.

  1. Clone your code from GitHub

  2. Build the project using Maven

  • No credentials required Since we are using a public GitHub repository, there’s no need to configure any credentials for Git access but git must be installled

  • Important Requirement Although no credentials are needed, Git must be installed on the Jenkins server to allow repository cloning.

sudo apt install git -y

pipeline {
    agent any

    tools {
        maven 'maven3.9.9'
    }

    stages {

        stage('Checkout') {
            steps {
                echo "Cloning Repository..."
                git branch: 'feature',
                    url: 'https://github.com/KandlaguntaVenkataSivaNiranjanReddy/spring-boot-mongo-docker-kkfunda.git'
            }
        }

        stage('Maven Build') {
            steps {
                echo "Building the Maven Project..."
                sh 'mvn clean package -DskipTests'
            }
        }

    }
}
  • This confirms that your pipeline is running correctly. Next, you can proceed to the following stages.

  • So far: Code is cloned , Build is successful

  • But build success ≠ security.

  • Even if the application works perfectly, it can still have:

  • Vulnerable third-party libraries

  • Outdated dependencies

  • Known CVEs (security issues)

  • That’s why the next step is Snyk.

8.10 Adding Snyk to the Jenkins Pipeline

  • Snyk is introduced to scan: Project dependencies ,Third-party libraries.

  • Jenkins authenticates using the Snyk API token.

  • Vulnerabilities are identified and reported.

Outcome:
Security risks are detected early in the CI pipeline.

pipeline {
    agent any

    tools {
        maven 'maven3.9.9'
    }

    environment {
        SNYK_TOKEN = credentials('SNYK_TOKEN') // Snyk API token
    }

    stages {

        stage('Checkout') {
            steps {
                echo "Cloning Repository..."
                git branch: 'feature',
                    url: 'https://github.com/KandlaguntaVenkataSivaNiranjanReddy/spring-boot-mongo-docker-kkfunda.git'
            }
        }

        stage('Maven Build') {
            steps {
                echo "Building the Maven Project..."
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('Snyk Repo Scan') {
            steps {
                echo "Running Snyk scan for dependency vulnerabilities..."
                sh '''
                    echo "Authenticating with Snyk..."
                    snyk auth $SNYK_TOKEN
                    echo "Running Snyk Test..."
                    snyk test --all-projects || true
                    snyk monitor --all-projects || true
                '''
            }
        }

    }

8.11 Key Snyk Commands Used in the Jenkins Pipeline

Snyk plays a critical role in our DevSecOps pipeline by providing dependency-level security visibility without blocking the CI/CD flow. The following commands are intentionally designed to detect, report, and monitor vulnerabilities while allowing the pipeline to continue.


1. Understanding snyk test --all-projects || true

This command is used for pre-deployment security analysis of application dependencies.

What snyk test --all-projects Does

  • Scans all supported project types in the Jenkins workspace

  • Automatically detects dependency manifests such as:

    • pom.xml (Maven)

    • package.json (Node.js)

    • requirements.txt (Python)

    • Other supported files

  • Analyzes:

    • Direct project dependencies

    • Transitive (indirect) dependencies

    • Third-party libraries

Output Provided

  • Total number of dependencies tested

  • Total vulnerabilities found

  • Severity breakdown: CRITICAL ,HIGH ,MEDIUM ,LOW

  • CVE IDs with vulnerability descriptions

This scan helps identify security issues before containerization or deployment.

Why || true Is Used

snyk test --all-projects || true
  • Prevents the Jenkins build from failing

  • Ensures:

    • Vulnerabilities are reported, not blocked

    • The pipeline continues to Docker build, image scanning, and deployment

  • Ideal when security findings are required for visibility and analysis, not for stopping delivery

2. Understanding snyk monitor --all-projects || true (Important)

  • Without this command, the project will NOT appear in the Snyk Dashboard and cannot be monitored.
    snyk test only scans locally, but snyk monitor is mandatory for dashboard visibility and continuous monitoring.

This command goes beyond scanning.

  • What snyk monitor Does

  • Takes a snapshot of the project’s current dependency state

  • Uploads:

    • Project metadata

    • Dependency graph

    • Vulnerability context
      to the Snyk Dashboard

  • Enables continuous monitoring of the application

Why This Matters

  • Even after the pipeline finishes, Snyk continues to:

    • Monitor for newly discovered vulnerabilities

    • Alert when dependencies become insecure in the future

  • Converts one-time scans into ongoing security visibility


8.12 Jenkins Job Execution

  • The Jenkins job runs successfully

  • What We See in Jenkins Console Output

  • From the console output:

  • Jenkins authenticates with Snyk using the token

  • Snyk scans the Jenkins workspace

  • Output shows:

  • Number of dependencies tested

  • Number of vulnerabilities found

  • Severity levels (Critical, High, Medium, Low)

  • Example meaning: “Tested 78 dependencies, found 168 issues”

  • This does NOT fail the build it only reports security findings

  • Purpose: To detect security issues in third-party libraries used by the application

8.13 Results in Snyk Dashboard

  • After the scan:

  • The project is automatically sent to the Snyk Dashboard

  • You can log in to Snyk and see:

  • The project is automatically sent to the Snyk Dashboard

  • In the Snyk UI, we can see:

  • Project name

  • Vulnerable libraries

  • CVE details

  • Severity levels

  • Fix and upgrade recommendations

  • Which dependency introduced the issue

Benefits to the Development Team

This helps developers to:

  • Decide which dependencies need upgrades

  • Avoid insecure libraries

  • Keep safe and stable dependencies

  • Fix vulnerabilities early (Shift Left Security)

  • Prevent insecure components from reaching production

  • This proves that security is enforced during the CI stage, not after deployment.

  • Overall, Snyk revealed high-risk CVEs in third-party dependencies, allowing us to quickly decide what to upgrade and secure before production deployment.

9. Steps to Create SonarQube Token and Configure It in Jenkins

  • Integrating SonarQube with Jenkins is a critical step in implementing code quality and static code analysis inside a CI/CD pipeline.

  • For Jenkins to communicate securely with SonarQube, two configurations are mandatory:

  • Creating an Authentication Token in SonarQube

  • Configuring the SonarQube Server and Token in Jenkins

This setup allows Jenkins to trigger SonarQube scans automatically during pipeline execution.

9.1 Generate SonarQube Token

  • Log in to your SonarQube dashboard.

  • Click on your profile → My Account > Security.

  • Generate a new token:

  • Give it a name like jenkins

  • Copy the generated token immediately you won’t be able to see it again later

9.2 Add SonarQube Token in Jenkins Credentials

  • Go to Jenkins Dashboard

  • Navigate to: Manage Jenkins → Credentials → (Global) → Add Credentials

  • Fill the details:

  • Kind: Secret Text

  • Secret: Paste the SonarQube token you generated

  • Description: sonartoken

  • Click Save

  • Now the token is securely stored in Jenkins.

  • Adding credentials alone is not sufficient. Jenkins also needs to know the SonarQube server address (IP + Port).

9.3 Configure SonarQube Server in Jenkins

  • Go to: Manage Jenkins → Configure System

  • Scroll down to SonarQube Servers section

  • Click on Add SonarQube

  • Fill the details:

  • Name: sonar

  • Server URL: http://<sonarqube-server-ip>:9000

  • Authentication Token: Select the credential sonartoken from the dropdown

  • Click Save

9.4 Configure Sonar Scanner in Jenkins

  • Go to Manage Jenkins > Global Tool Configuration.

  • Under SonarQube Scanner, click Add SonarQube Scanner.

  • Name: SonarScanner

  • Select Install automatically

9.5 Running Jenkins Build with SonarQube Integration

Once you’ve configured your SonarQube server and added the authentication token in Jenkins, you can now run your Jenkins builds and trigger code analysis with SonarQube.

Update Pipeline with Sonar Stage:

  • The name 'sonar' used here must match the name you defined under:
    Manage Jenkins → Configure System → SonarQube Servers

  • Jenkins will automatically use the SonarQube URL and authentication token from this configuration.

  • You do not need to manually pass the token or URL in the pipeline.

  • After configuration is complete, trigger the Jenkins pipeline.

  • Check the Build Status: The build should complete successfully

pipeline {
    agent any

    tools {
        maven 'maven3.9.9'
    }

    environment {
        SNYK_TOKEN = credentials('SNYK_TOKEN') // Snyk API token
    }

    stages {

        stage('Checkout') {
            steps {
                echo "Cloning Repository..."
                git branch: 'feature',
                    url: 'https://github.com/KandlaguntaVenkataSivaNiranjanReddy/spring-boot-mongo-docker-kkfunda.git'
            }
        }

        stage('Maven Build') {
            steps {
                echo "Building the Maven Project..."
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('sonar') {
                    echo "Running SonarQube analysis..."
                    sh '''
                        mvn sonar:sonar \
                        -Dsonar.projectKey=spring-boot-mongo \
                        -Dsonar.projectName="Spring Boot Mongo Project"
                    '''
                }
            }
        }

        stage('Snyk Repo Scan') {
            steps {
                echo "Running Snyk scan for dependency vulnerabilities..."
                sh '''
                    echo "Authenticating with Snyk..."
                    snyk auth $SNYK_TOKEN

                    echo "Running Snyk Test..."
                    snyk test --all-projects || true

                    echo "Sending results to Snyk dashboard..."
                    snyk monitor --all-projects || true
                '''
            }
        }

    }

  • Open your SonarQube dashboard (e.g., http://<your-ip>:9000) and log in.
    You’ll see the project Spring Boot Mongo Project listed, confirming that the analysis was successful.

  • You’ll see the project Spring Boot Mongo Project listed, confirming that the analysis was successful.

10. Docker Authentication and Image Security Scanning in Jenkins

  • After completing code build, code quality checks, and dependency security scanning, the next critical phase in the DevSecOps pipeline is containerization.

In this stage:

  • Jenkins builds a Docker image

  • Scans the image for vulnerabilities using Trivy

  • Pushes the verified image to Docker Hub

  • This step ensures that only scanned and verified container images move forward for deployment.

10.1 Why Docker Authentication Is Required in Jenkins

  • In our pipeline, Jenkins builds a Docker image and pushes it to Docker Hub.

  • Docker Hub does not allow image push operations without authentication.
    Therefore, Jenkins must log in to Docker Hub automatically and securely.

  • Important Security Note: We do NOT use Docker Hub passwords

  • We use Docker Hub Personal Access Tokens

  • Tokens are: More secure , Recommended for CI/CD pipelines

10.2 Generate Docker Hub Personal Access Token

  • Click on your profile (top-right) → Account Settings

  • Select Personal Access Token

  • Give it Read, Write, Delete permissions

  • Click to generate the token.

  • Copy the token immediately it will not be shown again

10.3 Add Docker Hub Credentials to Jenkins

Navigate to: Jenkins Dashboard → Manage Jenkins → Credentials

  • Under the appropriate domain (usually global), click:
    Add Credentials

  • In the Kind dropdown, select: Username and password

  • Fill in the form:

  • Username: Your Docker Hub username

  • Password: Paste your Docker authentication token

  • ID: eg: docker

  • Description: e.g: docker-cred

  • Click OK to save the credentials.

10.4 Installing Trivy on Jenkins Server

Why install Trivy on Jenkins?

  • Jenkins executes pipeline steps

  • Trivy command must be available on Jenkins machine

  • Jenkins will scan locally built Docker images

You created a script:

vi trivy.sh

Then pasted:

sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy -y

Then executed:

sh trivy.sh

10.5 Prerequisites Recap Before Docker Build

  • Before building and pushing Docker images, ensure:

  • Jenkins is installed and running

  • Docker is installed on Jenkins server

  • Jenkins user is added to Docker group

  • Jenkins user must be added to the Docker group. (sudo usermod -aG docker jenkins)

  • Docker Hub credentials (username + token) added to Jenkins as Username & Password

  • GitHub repo has a valid Dockerfile

10.6 Using Jenkins Snippet Generator for Docker Authentication

To safely authenticate Docker commands (like docker push) from a Jenkins pipeline, you can use the built-in snippet generator:

  • Navigate to your Jenkins job → Click on Pipeline Syntax (usually bottom left or under job configuration)

  • From the dropdown, select: withDockerRegistry: Set up Docker registry endpoint

  • Docker registry URL: ( Leave this empty) Docker Hub is a public registry, so no URL is required

  • Credentials: Choose the one you just added (docker-cred)

  • Click Generate Pipeline Script.

  • Jenkins will provide you with a block like this, which you can use in your pipeline script.


10.7 Pipeline Script for Building and Pushing Docker Image and trivy stages

  • After completing GitHub → Jenkins → Maven Build → Sonarqube→Snyk Scan, the next stage in the DevSecOps pipeline is containerization and image security scanning.

  • This step converts the application into a Docker image, scans it for vulnerabilities, and then pushes it to Docker Hub.

Docker Authentication via Jenkins

  • Jenkins securely authenticates to Docker Hub using stored credentials

  • Credentials are never exposed in logs or pipeline files

  • This allows Jenkins to perform Docker push operations safely.

Docker Image Build

  • Jenkins uses the Dockerfile present in the repository

  • Application source code + runtime environment are packaged into a Docker image

  • At this point, the application officially becomes a container image

Trivy Image Scan (Security Scan)

  • After the image is built:

  • Trivy scans the Docker image

  • It analyzes:

  • OS packages

  • Application dependencies inside the image

  • Trivy detects: Known CVEs

  • Vulnerability severity: Critical ,High ,Medium ,Low

stage('Build & Tag Docker Image') {
    steps {
        script {
            echo "Building Docker image..."
            withDockerRegistry(credentialsId: 'docker') {
                sh 'docker build -t niranjanreddy1231/mongospring:latest .'
            }
        }
    }
}

stage('Trivy Image Scan') {
    steps {
        echo "Scanning Docker image for vulnerabilities using Trivy..."
        sh '''
            trivy image --exit-code 0 --severity HIGH,CRITICAL niranjanreddy1231/mongospring:latest
            trivy image --exit-code 1 --severity CRITICAL niranjanreddy1231/mongospring:latest || true
        '''
    }
}

stage('Push Docker Image') {
    steps {
        script {
            echo "Pushing Docker image to DockerHub..."
            withDockerRegistry(credentialsId: 'docker') {
                sh 'docker push niranjanreddy1231/mongospring:latest'
            }
        }
    }
}
  • Now run the Jenkins job Build Status: SUCCESS

10.8 What We See in Jenkins Console Output

  • In the console output, Trivy displays:

  • Vulnerable packages present inside the Docker image

  • CVE IDs for each detected vulnerability

  • Severity levels such as: Critical, High, Medium, Low

  • Fixed versions (if available) that can resolve the issue

  • This gives a clear picture of which component is risky and how it can be fixed.

  • Critical vulnerabilities in libraries like logback, jackson

  • Clear indication of what version is vulnerable and what version fixes it

10.9 Docker Authentication & Image Push Confirmation

  • Jenkins securely uses the Docker Hub credentials configured in Jenkins under the credentials ID docker.

  • These credentials are stored safely in Jenkins

  • They are never exposed in the pipeline script or console logs

  • Jenkins uses them only when required (during image build and push)

  • Image is built and pushed to your Docker Hub repository.

  • You can confirm it in the Docker Hub UI.

11. EKS Cluster Setup and Jenkins Integration

11.1 Why EKS Setup Is Required

After building, scanning, and pushing the Docker image, the next step is deployment.

To deploy the application into Kubernetes, Jenkins must be able to:

  • Communicate with AWS

  • Access the EKS control plane

  • Execute kubectl commands

For this, the Jenkins server itself must be prepared with the required Kubernetes and AWS tools.

11.2 Tools Required on Jenkins Server

The following tools must be installed on the Jenkins EC2 server:

  • AWS CLI – To authenticate and interact with AWS services

  • kubectl – To manage Kubernetes resources

  • eksctl – To create and manage the EKS cluster

  • AWS credentials – To authorize Jenkins with AWS (Access key, Secret key, Region, Output format)


11.3 Install AWS CLI on Jenkins Server

sudo apt update
sudo apt install unzip curl -y

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Check installation

aws --version

11.4 Configure AWS Credentials

Jenkins needs AWS credentials to access EKS.

aws configure

Enter the following:

  • Access Key ID

  • Secret Access Key

  • Default region (e.g. ap-south-1)

  • Output format : table

11.5 Install kubectl

kubectl is the CLI used to manage Kubernetes clusters.

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
  • curl -LO ... downloads the latest stable version of kubectl from the Kubernetes website.

  • sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl moves kubectl to the /usr/local/bin/ directory with appropriate permissions.

Check version

kubectl version --client

11.6 Install eksctl

eksctl simplifies EKS cluster creation and management.

curl -LO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_Linux_amd64.tar.gz"
tar -xzf eksctl_Linux_amd64.tar.gz
sudo mv eksctl /usr/local/bin/
eksctl version

Check the version

eksctl version

11.7 Create the EKS Cluster Using eksctl

eksctl create cluster \
--name my-cluster \
--region ap-south-1 \
--nodegroup-name mynodes \
--node-type t3.medium \
--nodes 2 \
--nodes-min 2 \
--nodes-max 3 \
--managed

What this does:

  • Creates a managed EKS cluster named my-cluster in the Mumbai region (ap-south-1)

  • Adds a managed node group called mynodes

  • Uses t3.medium EC2 instances

  • Starts with 2 nodes and allows scaling up to 3

This creates the following resources when you run eksctl create cluster:

  • VPC (Virtual Private Cloud)
    A private network where your entire EKS infrastructure lives.

  • Subnets & Route Tables
    Public and private subnets are created with routing rules.

    • Public subnets allow access to the internet (e.g., for Load Balancers).

    • Private subnets are used for internal communication (e.g., between pods and worker nodes).

  • Internet Gateway
    Attached to the public subnets, this enables the cluster to access the internet (like pulling Docker images or updates).

  • NAT Gateway (optional)
    Allows private subnets (e.g., worker nodes) to securely access the internet.

  • Security Groups
    Act like firewalls control which ports are open (like 443 for API server, 22 for SSH).

    • Includes ingress rules (incoming) and egress rules (outgoing).
  • EKS Control Plane
    AWS-managed Kubernetes control plane that handles the API server, scheduler, etc.

    • You don’t manage this manually AWS runs it for you.
  • EC2 Worker Nodes (Node Group)
    These are actual EC2 instances created inside the private subnets.

    • Your application pods run on these nodes.
  • IAM Roles & Policies
    Automatically created for EKS to securely access and control AWS services.

Check if Cluster is Created

  • Run this command to list all EKS clusters:

  • You should see my-cluster in the output.

aws eks list-clusters

Check kubeconfig File

cat /home/ubuntu/.kube/config
  • This file contains the cluster connection details used by kubectl.

  • This command displays the contents of the kubeconfig file, which is used by kubectl to manage your Kubernetes cluster.

Check Worker Nodes

kubectl get nodes
  • You should see 2 EC2 worker nodes in "Ready" status.

  • This confirms the worker nodes joined the cluster.

Check Namespaces

kubectl get namespaces
  • Default Kubernetes namespaces should appear:

11.8 Verify EKS Cluster in AWS Console

  • Go to AWS Console

  • Search for “EKS” service

  • Click on your cluster: my-cluster

  • Click the “Compute” tab

  • Here you’ll see:

  • The node group (mynodes)

  • EC2 instances created

  • Scaling status

12. Configure Jenkins for Kubernetes (EKS Integration)

12.1 Install Required Jenkins Plugins

  • AWS Credentials Plugin

  • Kubernetes Plugin

  • Kubernetes CLI Plugin

  • Kubernetes Credentials

  • To interact with an EKS cluster from Jenkins, you typically need to AWS Credentials Plugin

12.2 Add AWS Credentials in Jenkins

Why Add AWS Credentials in Jenkins?

  • Jenkins doesn't know your AWS credentials by default.

  • This injects AWS credentials temporarily into your Jenkins shell can authenticate securely with AWS and Kubernetes.

  • Go to Manage Jenkins page, click on "Manage Credentials”

  • Now, under (global) or in a specific domain if required, click on (Global credentials)

  • Credentials → (Global) → Add Credentials

  • Select the AWS Credentials Type:

  • In the Kind dropdown, select "AWS Credentials".

Fill in the AWS Credentials:

  • ID: Provide a unique name for the credentials (e.g., aws-eks-cred).

  • Access Key ID: Enter your AWS_ACCESS_KEY_ID.

  • Secret Access Key: Enter your AWS_SECRET_ACCESS_KEY.

  • This is used in the withCredentials block in the pipeline.

12.3 Start Writing the Jenkins Pipeline

After Jenkins is configured with AWS credentials, we proceed with the pipeline stages related to Kubernetes.

12.4 Git Checkout Stage

  • Now here Jenkins is cloning the code from GitHub repo exactly from the feature branch.

12.5 Setup AWS + KubeConfig

  • Here Jenkins is injecting AWS credentials that we already stored with ID aws-eks-cred.
    This contains Access Key + Secret Key securely stored in Jenkins credentials.

  • Connects Jenkins to your EKS cluster using AWS CLI

  • Generates or updates .kube/config file so that kubectl can talk to the EKS control plane.

12.6 Deploy Application to Kubernetes

Important: YAML File Must Be Correct

The file springappmongo.yaml is:

  • Already present inside the GitHub repository

  • This file is the blueprint for your Kubernetes deployment.

This YAML Defines:

  • Deployment

  • It tells Kubernetes how to create pods for your Spring Boot + MongoDB application.

  • Example: number of replicas, container image, ports.

  • Service

  • It exposes your application so it can be accessed.

  • If it’s a LoadBalancer type, AWS will create an ELB for you.

Note : If the filename is wrong or not present in the repo, the pipeline will fail

The --validate=false flag is used to skip any API schema checks just apply and go.
This step actually creates the Kubernetes pods and services in your EKS environment."

12.7 Verify Pods and Services

Once deployed, we use kubectl get pods to check if the application is running and kubectl get svc to verify if a LoadBalancer or ClusterIP service is created.

12.8 Trivy Kubernetes Cluster Scan

  • After successfully deploying the application to the Kubernetes (EKS) cluster, the next critical DevSecOps step is cluster-level security scanning using Trivy.

  • This scan is performed after deployment, focusing on the Kubernetes environment, not just the code or image.

Why Cluster Scanning Is Important Even if:

  • Source code is secure (SAST – SonarQube)

  • Dependencies are scanned (Snyk)

  • Docker image is scanned (Trivy Image Scan)

  • Security risks can still exist at the Kubernetes cluster level.

  • These risks usually come from:

  • Misconfigured Kubernetes resources

  • Insecure container runtime settings

  • Excessive permissions (RBAC issues)

  • Vulnerable libraries running inside pods

  • Exposed services or outdated components


12.9 What Happens During Trivy Cluster Scan

  • Trivy scans the running workloads inside the Kubernetes cluster

  • It inspects:

    • Deployed containers

    • Application libraries

    • Framework dependencies

    • OS packages inside pods

  • It identifies known CVEs that are active after deployment

  • Cluster scanning ensures that the runtime environment itself is secure, not just the application.

Full pipeline

pipeline {
    agent any

    tools {
        maven 'maven3.9.9'
    }

    environment {
        SNYK_TOKEN = credentials('SNYK_TOKEN') // Snyk API token
    }

    stages {

        stage('Checkout') {
            steps {
                echo "Cloning Repository..."
                git branch: 'feature',
                    url: 'https://github.com/KandlaguntaVenkataSivaNiranjanReddy/spring-boot-mongo-docker-kkfunda.git'
            }
        }

        stage('Maven Build') {
            steps {
                echo "Building the Maven Project..."
                sh 'mvn clean package -DskipTests'
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('sonar') {
                    echo "Running SonarQube analysis..."
                    sh '''
                        mvn sonar:sonar \
                        -Dsonar.projectKey=spring-boot-mongo \
                        -Dsonar.projectName="Spring Boot Mongo Project"
                    '''
                }
            }
        }

        stage('Snyk Repo Scan') {
            steps {
                echo "Running Snyk scan for dependency vulnerabilities..."
                sh '''
                    echo "Authenticating with Snyk..."
                    snyk auth $SNYK_TOKEN
                    echo "Running Snyk Test..."
                    snyk test --all-projects || true
                    snyk monitor --all-projects || true
                '''
            }
        }

        stage('Build & Tag Docker Image') {
            steps {
                script {
                    echo "Building Docker image..."
                    withDockerRegistry(credentialsId: 'docker') {
                        sh "docker build -t niranjanreddy1231/mongospring:latest ."
                    }
                }
            }
        }


        stage('Trivy Image Scan') {
            steps {
                echo "Scanning Docker image for vulnerabilities using Trivy..."
                sh '''
                    trivy image --exit-code 0 --severity HIGH,CRITICAL niranjanreddy1231/mongospring:latest
                    trivy image --exit-code 1 --severity CRITICAL niranjanreddy1231/mongospring:latest || true
                '''
            }
        }

        stage('Push Docker Image') {
            steps {
                script {
                    echo "Pushing Docker image to DockerHub..."
                    withDockerRegistry(credentialsId: 'docker') {
                        sh "docker push niranjanreddy1231/mongospring:latest"
                    }
                }
            }
        }

        stage('Setup KubeConfig') {
            steps {
                withCredentials([[$class: 'AmazonWebServicesCredentialsBinding',
                                  credentialsId: 'aws-eks-cred']]) {
                    sh '''
                        echo "Setting up kubeconfig for EKS cluster..."

                        # Export AWS credentials explicitly
                        export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
                        export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
                        export AWS_DEFAULT_REGION=ap-south-1

                        echo "Verifying AWS Identity..."
                        aws sts get-caller-identity

                        echo "Updating kubeconfig for EKS cluster..."
                        aws eks update-kubeconfig --region ap-south-1 --name my-cluster

                        echo "Kubeconfig setup complete."
                    '''
                }
            }
        }

        stage('Deploy to Kubernetes') {
            steps {
                echo "Deploying application to EKS..."
                withCredentials([[$class: 'AmazonWebServicesCredentialsBinding',
                                  credentialsId: 'aws-eks-cred']]) {
                    sh '''
                        kubectl apply -f springappmongo.yaml --validate=false
                    '''
                }
            }
        }

        stage('Verify Pods and Services') {
            steps {
                echo "Verifying Kubernetes resources..."
                withCredentials([[$class: 'AmazonWebServicesCredentialsBinding',
                                  credentialsId: 'aws-eks-cred']]) {
                    sh '''
                        kubectl get pods -o wide
                        kubectl get svc -o wide
                    '''
                }
            }
        }

        stage('Trivy Cluster Scan') {
            steps {
                echo "Scanning Kubernetes cluster for vulnerabilities..."
                withCredentials([[$class: 'AmazonWebServicesCredentialsBinding',
                                  credentialsId: 'aws-eks-cred']]) {  
                    sh '''
                        export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
                        export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
                        export AWS_DEFAULT_REGION=ap-south-1

                        echo "Running Trivy Kubernetes cluster scan..."
                        trivy k8s --report summary cluster || true
                    '''
                }
            }
        }
    }

    post {
        success {
            echo "Pipeline completed successfully."
        }
        failure {
            echo "Pipeline failed. Please check the logs for details."
        }
    }
}

12.10 Run the Jenkins Job

  • Once the pipeline is set up, go ahead and click “Build Now” in Jenkins.

In the console output, you’ll see:

  • Now you can see Jenkins first pulling the source code from our GitHub repository, specifically the feature branch. This is our application code for Spring Boot with MongoDB.

  • Jenkins is securely injecting our AWS credentials

  • At this stage, Jenkins updates the kubeconfig file.This connects kubectl to our EKS cluster without this, Jenkins can't run K8s commands.

  • Now Jenkins is deploying the application by running:
    kubectl apply -f springappmongo.yaml

  • This file has all our K8s objects like the Deployment and Service definitions

  • Jenkins is verifying everything by listing Pods and Services. It runs:

  • kubectl get pods to see if the app is running

  • kubectl get svc to see if the LoadBalancer service is ready”

  • Even though Jenkins shows everything in the console output, it's a good habit to manually verify from your EC2 server terminal (Jenkins server)

  • When you open the Jenkins Console Output, you can clearly see that Trivy Displays:

  • Vulnerable packages running inside the Kubernetes cluster

  • CVE IDs for each vulnerability

  • Severity levels: CRITICAL , HIGH ,MEDIUM ,LOW

  • Installed version vs Fixed version

  • Description of the vulnerability

  • Reference links (Aqua / NVD URLs)

  • This confirms that Trivy is actively scanning the live cluster environment.

  • From the console output shown:

  • Spring Framework vulnerabilities: spring-beans , spring-web,spring-webmvc

  • Thymeleaf vulnerability

  • Template injection issue

  • Vulnerabilities such as:

    • Remote Code Execution (RCE)

    • Unsafe deserialization

    • Template injection

  • Clear indication of:

    • Vulnerable version

    • Safe / fixed version

  • These issues exist inside running Kubernetes pods, not just in source code.

12.11 Access the Application

  • Now, you can copy this LoadBalancer URL from the console, paste it into your browser.

  • Now you can add users to your application.

13. Conclusion

In this project, we successfully built a complete end-to-end DevSecOps CI/CD pipeline using Jenkins and AWS EKS.

Key outcomes of this implementation:

  • Automated code build and quality analysis using Maven and SonarQube

  • Early dependency vulnerability detection using Snyk (SAST + SCA)

  • Secure container image scanning using Trivy before deployment

  • Automated Kubernetes deployment on Amazon EKS

  • Post-deployment cluster security scanning, ensuring runtime security

This pipeline demonstrates how security can be embedded into every stage of CI/CD, rather than treated as a separate process.
By integrating security early, we reduce risk, improve reliability, and deliver production-ready applications with confidence.