DevOps Security with Trivy (OWASP) and Argo CD - Part 1
Introduction
Building and deploying a secure Node.js application requires more than simply writing code. A modern DevOps workflow should incorporate version control, continuous integration, automated security scanning, containerization, orchestration, GitOps, and continuous monitoring to deliver reliable and secure applications.
In this guide, you'll build a complete DevOps pipeline using industry-standard tools such as Git, GitHub, Jenkins, SonarQube, OWASP Dependency-Check, Docker, Trivy, Docker Hub, Kubernetes (Kops), Argo CD, Helm, Prometheus, and Grafana on a Google Cloud Platform (GCP) Virtual Machine.
Throughout this series, you'll learn how to automate application delivery while integrating security at every stage of the software development lifecycle.
Prerequisites
Before getting started, ensure you have the following environment prepared:
- Google Cloud Platform (GCP) account with access to Compute Engine
- A GCP Virtual Machine running Ubuntu or Debian
- Basic knowledge of Linux commands and shell scripting
- A GitHub repository to manage application source code
- Jenkins installed and configured on the VM
- Docker installed and running
- Kubernetes cluster created using Kops
- Argo CD installed for GitOps deployments
- Helm installed for Kubernetes package management
- Prometheus and Grafana configured for monitoring
- Trivy and SonarQube installed for security analysis
Tools Used in This Project
The following tools are used throughout the deployment pipeline.
| Tool | Purpose | Default Port |
|---|---|---|
| Git | Distributed version control system for tracking source code changes | โ |
| GitHub | Cloud-hosted Git repository for collaboration and source management | โ |
| Jenkins | Continuous Integration and Continuous Deployment automation server | 8080 |
| SonarQube | Static code analysis and code quality inspection platform | 9000 |
| Node.js | JavaScript runtime for building scalable backend applications | โ |
| OWASP Dependency-Check | Scans project dependencies for known vulnerabilities | โ |
| Docker | Containerization platform for packaging applications | โ |
| Trivy | Container image and vulnerability scanner | โ |
| Docker Hub | Cloud container image registry | โ |
| Kubernetes (Kops) | Production-grade Kubernetes cluster management | โ |
| Argo CD | GitOps Continuous Delivery for Kubernetes | 8080 / 443 |
| Helm | Kubernetes package manager | โ |
| Prometheus | Metrics collection and monitoring system | 9090 |
| Grafana | Dashboarding and visualization platform | 3000 |
What is Trivy?
Trivy is an open-source security scanner developed by Aqua Security for cloud-native environments.
It helps developers, DevOps engineers, and security teams identify vulnerabilities and configuration issues across applications, containers, infrastructure, and source code before deployment.
Unlike traditional vulnerability scanners that focus on only one component, Trivy provides comprehensive security scanning across multiple layers of the application stack.
What Can Trivy Scan?
Container Images
Scans Docker and OCI container images for known operating system package and application dependency vulnerabilities.
Filesystems
Scans local project directories to identify:
- Vulnerable dependencies
- Security misconfigurations
- Exposed secrets
Git Repositories
Analyzes source code repositories to detect:
- Hardcoded secrets
- API keys
- Vulnerable libraries
- Security risks before deployment
Infrastructure as Code (IaC)
Scans infrastructure configuration files including:
- Terraform
- Kubernetes YAML manifests
- Dockerfiles
- Helm Charts
to detect security misconfigurations before infrastructure is provisioned.
Why Use Trivy in DevOps Pipelines?
Integrating Trivy into a CI/CD pipeline provides several security benefits.
Shift-Left Security
Identify vulnerabilities early during development rather than after deployment.
Fast and Lightweight
Runs quickly, making it ideal for automated CI/CD workflows without significantly increasing build times.
OWASP Top 10 Support
Helps identify common security risks that align with the OWASP Top 10 recommendations.
GitOps Ready
Works seamlessly with GitOps workflows and can be integrated with tools like Argo CD to validate Kubernetes manifests before deployment.
Easy CI/CD Integration
Supports integration with popular automation platforms, including:
- Jenkins
- GitHub Actions
- GitLab CI/CD
- Azure DevOps
- CircleCI
- Bitbucket Pipelines
Step 1: Set Up a GCP Virtual Machine
The first step is to provision the Google Cloud infrastructure required for this project. In this guide, you'll create two Compute Engine virtual machines:
- kops-cluster-poc โ Used to create and manage the Kubernetes cluster using Kops.
- pipeline-poc โ Used to host the DevOps tools such as Jenkins, Docker, Trivy, SonarQube, Argo CD, Prometheus, and Grafana.
1.1 Create the Kubernetes Cluster VM
Navigate to Google Cloud Console โ Compute Engine โ VM instances.
Click Create Instance and configure the VM with the following settings:
| Setting | Value |
|---|---|
| Name | kops-cluster-poc |
| Machine Type | e2-medium (2 vCPUs, 4 GB RAM) |
| Boot Disk | Ubuntu 24.04 LTS |
| Firewall | Allow HTTP, HTTPS, and SSH |
| Region & Zone | Choose your preferred location |
Click Create to provision the virtual machine.
1.2 Install and Configure Kops
Install Kops
curl -Lo kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x ./kops
sudo mv ./kops /usr/local/bin/
Install kubectl
curl -Lo kubectl https://dl.k8s.io/release/$(curl -s -L https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
Configure the Kops State Store
export KOPS_STATE_STORE=gs://kubernetes-clusters-9863235
echo 'export KOPS_STATE_STORE=gs://kubernetes-clusters-9863235' >> ~/.bashrc
source ~/.bashrc
Verify Google Cloud Authentication
gcloud auth list --filter=status:ACTIVE --format="value(account)"
gcloud config get-value project
gcloud compute instances describe $(hostname) --format="value(serviceAccounts[].scopes)"
Create the Kubernetes Cluster
kops create cluster simple.k8s.local \
--zones us-central1-a \
--state ${KOPS_STATE_STORE} \
--project=mineral-hangar-453509-r7
Validate the Cluster
kops get cluster --state ${KOPS_STATE_STORE}
kops validate cluster --state=${KOPS_STATE_STORE}
Export kubeconfig
kops export kubeconfig \
--state=${KOPS_STATE_STORE} \
--name=simple.k8s.local
export KUBECONFIG=~/.kube/config
Verify the Cluster
kubectl cluster-info
kubectl get nodes
Configure the kubectl Context (Optional)
kubectl config get-contexts
kubectl config use-context simple.k8s.local
kubectl get nodes
Check Namespaces and Cluster Configuration
kubectl get ns
kubectl config view --minify
Revalidate the Cluster
kops validate cluster --state=${KOPS_STATE_STORE}
kubectl get nodes
1.3 Create the Pipeline Virtual Machine
Navigate to Google Cloud Console โ Compute Engine โ VM instances.
Click Create Instance and configure the VM using the following settings:
| Setting | Value |
|---|---|
| Name | pipeline-poc |
| Machine Type | e2-medium (2 vCPUs, 4 GB RAM) |
| Boot Disk | Ubuntu 24.04 LTS |
| Firewall | Allow HTTP, HTTPS, and SSH |
| Region & Zone | Choose your preferred location |
Click Create to provision the virtual machine.
Step 2: Update System Packages
Before installing the required DevOps tools, update the system packages to ensure you have the latest security patches and software updates.
Run the following command:
sudo apt update && sudo apt upgrade -y
Step 3: Install Git
Git is used for source code management and integrates with platforms such as GitHub to enable version control throughout the CI/CD pipeline.
Install Git using:
sudo apt install git -y
Verify the installation:
echo "Verifying Git installation..."
git --version
Step 4: Install Docker
Docker is used to package applications and their dependencies into lightweight, portable containers that can run consistently across different environments.
Install the required packages:
sudo apt install -y ca-certificates curl gnupg
Create the Docker keyring directory:
sudo install -m 0755 -d /etc/apt/keyrings
Download the official Docker GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
Set the correct permissions:
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the Docker repository:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Install Docker Engine and related components:
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify the installation:
docker --version
Enable and start the Docker service:
sudo systemctl enable --now docker
Allow the current user to run Docker commands without using sudo:
sudo usermod -aG docker $USER
newgrp docker
Step 5: Install Jenkins
Jenkins is an open-source automation server used to build, test, and deploy applications as part of a Continuous Integration and Continuous Deployment (CI/CD) pipeline.
Install Java (required by Jenkins):
sudo apt install -y fontconfig openjdk-17-jre
Add the Jenkins repository key:
curl -fsSL https://pkg.jenkins.io/debian/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
Add the Jenkins repository:
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
Install Jenkins:
sudo apt update && sudo apt install -y jenkins
Enable and start the Jenkins service:
sudo systemctl enable --now jenkins
Check the Jenkins service status:
sudo systemctl status jenkins
Retrieve the initial administrator password:
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Open your browser and access:
http://\<VM_PUBLIC_IP>:8080
Use the retrieved administrator password to unlock Jenkins and complete the initial setup.
Step 6: Install and Configure SonarQube
SonarQube is a static code analysis platform that helps identify bugs, code smells, security vulnerabilities, and maintainability issues before applications are deployed.
In this guide, SonarQube is deployed using a Docker container.
6.1 Run the SonarQube Container
Start the SonarQube Community Edition container:
docker run -d \
--name sonar \
-p 9000:9000 \
sonarqube:lts-community
Command Explanation
-dโ Runs the container in detached mode.--name sonarโ Assigns the container the name sonar.-p 9000:9000โ Maps port 9000 of the container to port 9000 on the host VM.sonarqube:lts-communityโ Pulls and runs the latest Long-Term Support (LTS) Community Edition image.
Open your browser and navigate to:
http://\<VM_PUBLIC_IP>:9000
6.2 Log in to SonarQube
Use the default administrator credentials:
| Username | Password |
|---|---|
admin | admin |
After logging in for the first time, SonarQube will prompt you to change the default administrator password.
6.3 Create a Project and Generate a Token
Create a project manually.
- Click Create Project.
- Select Locally as the project setup method.
- Generate a project token.
- Set the token expiration to 30 Days.
- Click Generate.
- Copy the generated token for later use.
- Select your operating system.
- Copy the Execute the Scanner command provided by SonarQube.
This token will be required when integrating SonarQube with Jenkins.
6.4 Create a Jenkins User in SonarQube
To allow Jenkins to authenticate with SonarQube, create a dedicated user.
Navigate to:
Administration โ Security โ Users โ Create User
Provide the following details:
| Field | Example |
|---|---|
| Login | jenkins |
abcs@gmail.com | |
| Password | Choose a secure password |
Click Create.
6.5 Generate a Token for Jenkins
Log in using the newly created jenkins user.
Navigate to:
User Settings โ Security โ Generate Tokens
Configure the token:
| Setting | Value |
|---|---|
| Token Name | Jenkins-30Days-Token |
| Expiration | 30 Days |
Click Generate and copy the generated token.
6.6 Store the Token in Jenkins
Open the Jenkins dashboard.
Navigate to:
Manage Jenkins โ Credentials
Add a new credential with the following configuration:
| Field | Value |
|---|---|
| Kind | Secret text |
| Secret | Paste the generated SonarQube token |
| ID | Jenkins-30Days-Token |
Save the credential.
Jenkins can now securely authenticate with SonarQube during pipeline execution.
Step 7: Install the Required Jenkins Plugins
Jenkins plugins extend the functionality of the Jenkins automation server, enabling integrations with tools such as Node.js, SonarQube, Docker, Trivy, and OWASP Dependency-Check.
7.1 Open the Plugin Manager
From the Jenkins Dashboard, navigate to:
Manage Jenkins โ Manage Plugins
7.2 Install the Required Plugins
Open the Available Plugins tab.
Use the search box to locate and install the following plugins:
- NodeJS Plugin
- SonarQube Scanner
- OWASP Dependency-Check
- Docker Pipeline
- Trivy Scanner
After selecting the required plugins, click Install without restart.
Wait until all plugins are successfully installed before proceeding.
Step 8: Configure Global Tools in Jenkins
After installing the required plugins, configure the development tools that Jenkins will use during pipeline execution.
Navigate to:
Dashboard โ Manage Jenkins โ Global Tool Configuration
Configure the following tools.
8.1 Configure JDK
Add a JDK installation.
| Setting | Value |
|---|---|
| Name | jdk17 |
| Version | jdk-17.0.8.1+1 |
| Java Home | /path/to/jdk |
8.2 Configure Node.js
Add a Node.js installation.
| Setting | Value |
|---|---|
| Name | node16 |
| Version | 16.2.0 |
Enable Install automatically if Node.js is not already installed.
8.3 Configure SonarQube Scanner
Add a SonarQube Scanner installation.
| Setting | Value |
|---|---|
| Name | sonar-scanner |
| Version | 5.0.1.3006 |
Enable Install automatically if required.
8.4 Configure OWASP Dependency-Check
Add the Dependency-Check tool.
| Setting | Value |
|---|---|
| Name | DP-Check |
| Version | 6.5.1 |
Install the tool automatically from GitHub if it is not already available.
After saving the configuration, Jenkins will be ready to use these tools within pipeline jobs.
Step 9: Create and Configure the Jenkins Pipeline
With Jenkins and the required tools configured, the next step is to create a Pipeline job that automates the complete CI/CD workflowโfrom source code checkout and code quality analysis to security scanning, Docker image creation, and publishing to Docker Hub.
9.1 Create a New Pipeline Job
- Open the Jenkins Dashboard.
- Click New Item.
- Enter the job name as My Deployment.
- Select Pipeline.
- Click OK.
- Scroll to the Pipeline section.
- Select Pipeline script.
- Paste the Jenkins Pipeline script.
- Click Save.
9.2 Generate Docker Registry Pipeline Syntax
Jenkins provides a Pipeline Syntax Generator to simplify authentication with Docker Hub.
- Navigate to Pipeline Syntax.
- Select with Docker Registry from the dropdown.
- Choose your Docker Hub credentials.
- Click Generate Pipeline Script.
- Copy the generated syntax into your Jenkins pipeline.
9.3 Create the Dockerfile
Create a Dockerfile in the root directory of your Node.js project.
FROM node:16-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
This Dockerfile installs the application dependencies, builds the project, exposes port 3000, and starts the Node.js application.
9.4 Configure the Jenkins Pipeline
Paste the following Jenkins Pipeline into the Pipeline section of the Jenkins job.
pipeline {
agent any
tools {
jdk 'java'
nodejs 'nodejs'
}
environment {
SCANNER_HOME = "/var/lib/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/mysonar"
}
stages {
stage("Clean Workspace") {
steps {
cleanWs()
}
}
stage("Checkout Code") {
steps {
git branch: 'main', url: 'https://github.com/pavan5779/Zomato-Repo.git'
}
}
stage("SonarQube Analysis") {
steps {
withSonarQubeEnv('mysonar') {
sh '''$SCANNER_HOME/bin/sonar-scanner \
-Dsonar.projectKey=zomato \
-Dsonar.sources=. \
-Dsonar.host.url=http://34.93.59.82:9000 \
-Dsonar.login=squ_58d4d2054f6ee2b19117afdade6540fcb4dc52d7'''
}
}
}
stage("Quality Gate Check") {
steps {
script {
def qg = sh(returnStdout: true, script: """
curl -u squ_58d4d2054f6ee2b19117afdade6540fcb4dc52d7: 'http://34.93.59.82:9000/api/qualitygates/project_status?projectKey=zomato'
""").trim()
echo "Quality Gate Status: ${qg}"
}
}
}
stage("Install Dependencies") {
steps {
sh 'npm install'
}
}
stage("OWASP Dependency Check") {
options {
retry(2)
timeout(time: 60, unit: 'MINUTES')
}
steps {
script {
dependencyCheck additionalArguments: """
--scan ./src \
--disableYarnAudit \
--disableNodeAudit \
--failOnCVSS 9 \
--format HTML
""", odcInstallation: 'Dp-Check'
dependencyCheckPublisher pattern: '**/dependency-check-report.html'
}
}
}
stage("Trivy Security Scan") {
steps {
sh "trivy fs . > trivyfs.txt"
}
}
stage("Verify Docker Access") {
steps {
script {
sh "id && groups && ls -l /var/run/docker.sock"
}
}
}
stage("Build Docker Image") {
steps {
script {
sh "newgrp docker && docker build -t image1 ."
}
}
}
stage("Push Docker Image") {
steps {
script {
withDockerRegistry(credentialsId: 'dockerhub') {
sh """
docker tag image1 pavan5779/kubernetesproject:CA
docker push pavan5779/kubernetesproject:CA
"""
}
}
}
}
stage("Scan Docker Image") {
steps {
sh 'trivy image pavan5779/kubernetesproject:CA'
}
}
}
}
The pipeline performs the following tasks:
- Cleans the Jenkins workspace.
- Clones the application source code from GitHub.
- Performs static code analysis with SonarQube.
- Validates the SonarQube Quality Gate.
- Installs project dependencies.
- Runs OWASP Dependency-Check.
- Performs filesystem security scanning with Trivy.
- Verifies Docker access.
- Builds the Docker image.
- Pushes the Docker image to Docker Hub.
- Performs a vulnerability scan on the published Docker image.
9.5 Validate the Pipeline Results
After the pipeline completes successfully, verify the following:
- The OWASP Dependency-Check report is generated.
- The SonarQube Quality Gate passes successfully.
- The Docker image is built without errors.
- The Docker image is successfully pushed to Docker Hub.
Step 10: Perform Code Quality and Security Analysis
The pipeline integrates SonarQube to analyze the application's source code.
A successful analysis should indicate:
- Quality Gate Passed
- No new bugs
- No critical vulnerabilities
- No security hotspots
- Maintainability Rating: A
- Reliability Rating: A
- Security Rating: A
Step 11: Build and Publish the Docker Image
Once all quality and security checks pass, Jenkins builds the application container and publishes it to Docker Hub.
Docker image details:
| Property | Value |
|---|---|
| Repository | pavan5779/kubernetesproject |
| Tag | CA |
| Platform | linux/amd64 |
| Compressed Size | 237.3 MB |
Conclusion
In this first part of the series, you built the foundation of a secure DevOps pipeline by provisioning infrastructure on Google Cloud Platform, configuring Jenkins, integrating SonarQube, OWASP Dependency-Check, and Trivy for security scanning, containerizing the application with Docker, and publishing the image to Docker Hub.
In Part 2, you'll continue by deploying the application to a Kubernetes cluster using Argo CD and implementing observability with Prometheus and Grafana for monitoring and alerting.