<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rihab Haddad</title>
    <description>The latest articles on Forem by Rihab Haddad (@rihab_haddad_5a138728dcc2).</description>
    <link>https://forem.com/rihab_haddad_5a138728dcc2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2411388%2Fb333b311-c032-4611-bf87-9777ae3d2518.png</url>
      <title>Forem: Rihab Haddad</title>
      <link>https://forem.com/rihab_haddad_5a138728dcc2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rihab_haddad_5a138728dcc2"/>
    <language>en</language>
    <item>
      <title>Secure Your CI/CD Pipeline with Jenkins, ArgoCD, and Vault – A Hands-On DevSecOps Guide</title>
      <dc:creator>Rihab Haddad</dc:creator>
      <pubDate>Mon, 05 May 2025 12:02:00 +0000</pubDate>
      <link>https://forem.com/rihab_haddad_5a138728dcc2/building-a-production-ready-devsecops-pipeline-using-jenkins-argocd-trivy-and-vault-a-hands-on-4i00</link>
      <guid>https://forem.com/rihab_haddad_5a138728dcc2/building-a-production-ready-devsecops-pipeline-using-jenkins-argocd-trivy-and-vault-a-hands-on-4i00</guid>
      <description>&lt;p&gt;In a world where security breaches and unstable deployments can instantly ruin user trust, building a secure and automated CI/CD pipeline is no longer optional - &lt;strong&gt;it's essential&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this hands-on guide, you'll walk through a real-world DevSecOps pipeline integrating powerful open-source tools like Jenkins, ArgoCD, Trivy, SonarQube, and Vault, enhanced with Prometheus and Grafana for monitoring.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you'll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automate Builds, Tests, and Deployments using &lt;strong&gt;Jenkins&lt;/strong&gt; and GitOps with &lt;strong&gt;ArgoCD&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enforce Security and Code Quality with &lt;strong&gt;SonarQube&lt;/strong&gt; and &lt;strong&gt;Trivy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Manage Secrets Securely using &lt;strong&gt;HashiCorp Vault&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Deploy Declaratively with &lt;strong&gt;GitOps&lt;/strong&gt;, enabling rollbacks and auditability&lt;/li&gt;
&lt;li&gt;Gain Real-Time Visibility with &lt;strong&gt;Prometheus&lt;/strong&gt; and &lt;strong&gt;Grafana&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What We'll Cover:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitOps Foundation&lt;/strong&gt;: Setting up our Git repositories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins Automation&lt;/strong&gt;: Configuring Jenkins to drive our pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker and Initial Security&lt;/strong&gt;: Containerizing our app and scanning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline Deep Dive&lt;/strong&gt;: Building, scanning, and pushing our Docker image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitOps Deployments&lt;/strong&gt;: Automating deployments with ArgoCD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vault Secrets&lt;/strong&gt;: Securely managing credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Monitoring&lt;/strong&gt;: Using Prometheus and Grafana for observability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Our architecture combines the strengths of Continuous Integration (CI), Continuous Delivery (CD), GitOps, and DevSecOps principles to create a secure and automated workflow. Here's a visual representation of the key components and their interactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins&lt;/strong&gt; - Automates the pipeline: build, test, scan, deploy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; - Builds and runs containerized apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trivy&lt;/strong&gt; - Scans code and images for vulnerabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SonarQube&lt;/strong&gt; - Analyzes code quality and security&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vault&lt;/strong&gt; - Securely store and manage credentials and other sensitive data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ArgoCD&lt;/strong&gt; - Syncs Kubernetes clusters from Git repositories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus + Grafana&lt;/strong&gt; - Provides real-time monitoring of the application and system&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: During the project implementation, you will likely face issues that you will need to solve independently. Use the official documentation, Google, Stack Overflow, or ChatGPT to find solutions. This is a normal part of the learning process and reflects real-world DevOps challenges. Developing your ability to find necessary information is a crucial skill for a DevOps engineer.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into the implementation, ensure the following tools are installed and configured on your local environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git, Docker, Jenkins, kubectl&lt;/li&gt;
&lt;li&gt;Minikube (for local Kubernetes)&lt;/li&gt;
&lt;li&gt;SonarQube, Trivy, Vault CLI&lt;/li&gt;
&lt;li&gt;Visual Studio Code (or preferred IDE)&lt;/li&gt;
&lt;li&gt;Helm (optional, for ArgoCD/Vault)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Glossary Reminder: If you encounter unfamiliar terms, please refer to the glossary at the end of the article.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Create Two GitHub Repositories
&lt;/h2&gt;

&lt;p&gt;To establish a clean and modular CI/CD pipeline aligned with GitOps best practices, we'll begin by creating two distinct GitHub repositories.&lt;/p&gt;

&lt;h3&gt;
  
  
  DevSecOps-pipeline
&lt;/h3&gt;

&lt;p&gt;This repository will house your application's source code, the Dockerfile defining how to containerize your application, and the Jenkinsfile which outlines the automation pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DevSecOps-pipeline/
├── Dockerfile
├── Jenkinsfile
├── src/
└── ... (your application source code)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitOps
&lt;/h3&gt;

&lt;p&gt;This repository will serve as the single source of truth for your Kubernetes cluster's desired state. It will contain Kubernetes manifests (e.g., deployment.yaml, service.yaml) and any application-specific configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitOps/
├── config/
│   └── app.yaml
│
└── k8s/
├── deployment.yaml
├── service.yaml
└── ... (other Kubernetes manifests)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;h3&gt;
  
  
  Why this structure?
&lt;/h3&gt;

&lt;p&gt;Keeping source code and deployment configuration in separate repositories enables GitOps best practices and promotes clear separation of concerns.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Installing and Configuring Jenkins
&lt;/h2&gt;

&lt;p&gt;Jenkins will act as the central orchestrator of our CI/CD pipeline, automating the build, test, scan, and deployment processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required Jenkins Plugins
&lt;/h3&gt;

&lt;p&gt;Go to Manage Jenkins &amp;gt; Plugins and install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Pipeline&lt;/li&gt;
&lt;li&gt;Git Plugin&lt;/li&gt;
&lt;li&gt;SonarQube Scanner&lt;/li&gt;
&lt;li&gt;Trivy Plugin&lt;/li&gt;
&lt;li&gt;HashiCorp Vault Plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of adding secrets manually under Manage Jenkins &amp;gt; Credentials, we now store all sensitive data in Vault and retrieve it dynamically during pipeline execution using the &lt;code&gt;withVault&lt;/code&gt; block.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets managed in Vault:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Access Token → &lt;code&gt;secret/github&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Docker Hub credentials → &lt;code&gt;secret/dockerhub&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SonarQube Token → &lt;code&gt;secret/sonarqube&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GitOps SSH Key → &lt;code&gt;secret/gitops&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MongoDB Credentials → &lt;code&gt;secret/mongodb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Each of these is fetched securely from Vault at runtime using the AppRole authentication method. This eliminates the need to hardcode or store secrets in Jenkins and ensures better traceability and secret rotation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configure Vault Plugin in Jenkins:
&lt;/h3&gt;

&lt;p&gt;Navigate to Manage Jenkins &amp;gt; Configure System &amp;gt; Vault Plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vault URL: &lt;code&gt;https://vault.your-domain:port&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Credential Type: AppRole&lt;/li&gt;
&lt;li&gt;Vault AppRole Role ID: &lt;code&gt;&amp;lt;your-role-id&amp;gt;&lt;/code&gt; (configured in Vault later)&lt;/li&gt;
&lt;li&gt;Vault AppRole Secret ID: &lt;code&gt;&amp;lt;your-secret-id&amp;gt;&lt;/code&gt; (configured in Vault later)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once configured, Jenkins is ready to securely communicate with Vault and fetch secrets during pipeline execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configure SonarQube Scanner in Jenkins
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to Manage Jenkins &amp;gt; Configure System.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll to SonarQube servers and click Add SonarQube:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: SonarQube&lt;/li&gt;
&lt;li&gt;Server URL: &lt;code&gt;http://&amp;lt;your-sonarqube-url&amp;gt;:9000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check Enable injection of SonarQube server configuration as build environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to Manage Jenkins &amp;gt; Global Tool Configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to SonarQube Scanner and click Add SonarQube Scanner:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name: SonarScanner&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check Install Automatically (or specify a local path)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Configure Trivy Plugin in Jenkins
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to Manage Jenkins &amp;gt; Configure System.&lt;/li&gt;
&lt;li&gt;Scroll to the Trivy section and click Add Trivy Installation:&lt;/li&gt;
&lt;li&gt;Name: Trivy&lt;/li&gt;
&lt;li&gt;Check Install automatically (or specify the path to an existing Trivy binary)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now Trivy is ready to scan Docker images for vulnerabilities as part of your pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Containerize the Application (Multi-Stage Dockerfile)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Dockerize?
&lt;/h3&gt;

&lt;p&gt;Docker ensures that your app runs in a consistent environment across all stages of development, testing, and production.&lt;/p&gt;

&lt;p&gt;In your app repo (&lt;code&gt;DevSecOps-pipeline/&lt;/code&gt;), create the following Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# -------- STAGE 1: Build --------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-bullseye&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# Copy package definition files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="c"&gt;# Install system and Node.js dependencies required for build&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    python3 &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential &lt;span class="se"&gt;\
&lt;/span&gt;    libssl-dev &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# Install Node dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--legacy-peer-deps&lt;/span&gt;
&lt;span class="c"&gt;# Copy full application source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# -------- STAGE 2: Runtime --------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# Copy only the build artifacts and necessary files from previous stage&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app .&lt;/span&gt;
&lt;span class="c"&gt;# Expose the app port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3001&lt;/span&gt;
&lt;span class="c"&gt;# Launch the Node.js application&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local Testing (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;Before integrating the Docker build into the Jenkins pipeline, it's a good practice to validate the image locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-node-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:3001 my-node-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt; to verify your app runs correctly in the container.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Pipeline Breakdown: Jenkinsfile Configuration
&lt;/h2&gt;

&lt;p&gt;The Jenkins pipeline outlined here automates the process of building, testing, scanning, and deploying your containerized application. The pipeline includes stages for each of these tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set Environment Variables
&lt;/h3&gt;

&lt;p&gt;We define global environment variables at the start of our Jenkinsfile. These variables store reusable values like image names, repository URLs, and Vault secret paths. This makes the pipeline more readable and easier to maintain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IMAGE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rihab26/nodejs-app"&lt;/span&gt;
    &lt;span class="n"&gt;REGISTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"docker.io"&lt;/span&gt;
    &lt;span class="n"&gt;GIT_REPO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/RihabHaddad/DevSecOps-pipeline.git"&lt;/span&gt;
    &lt;span class="n"&gt;GITOPS_REPO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git@github.com:RihabHaddad/GitOps.git"&lt;/span&gt;
    &lt;span class="n"&gt;VAULT_SECRET_GITHUB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'secret/github'&lt;/span&gt;
    &lt;span class="n"&gt;VAULT_SECRET_DOCKERHUB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'secret/dockerhub'&lt;/span&gt;
    &lt;span class="n"&gt;VAULT_SECRET_SONAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'secret/sonarqube'&lt;/span&gt;
    &lt;span class="n"&gt;VAULT_SECRET_GITOPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'secret/gitops'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code Checkout
&lt;/h3&gt;

&lt;p&gt;The pipeline begins by retrieving source code securely from GitHub. Credentials are retrieved dynamically from Vault to avoid storing secrets in Jenkins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout Code'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s2"&gt;"${VAULT_SECRET_GITHUB}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'GITHUB_TOKEN'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'token'&lt;/span&gt;&lt;span class="o"&gt;]]]])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="nl"&gt;scm:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;$class&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'GitSCM'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nl"&gt;branches:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'*/main'&lt;/span&gt;&lt;span class="o"&gt;]],&lt;/span&gt;
                        &lt;span class="nl"&gt;userRemoteConfigs:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;
                            &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s2"&gt;"${GIT_REPO}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="nl"&gt;credentials:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="s1"&gt;'rihabhaddad'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;password:&lt;/span&gt; &lt;span class="s2"&gt;"${GITHUB_TOKEN}"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
                        &lt;span class="o"&gt;]]&lt;/span&gt;
                    &lt;span class="o"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Checkout failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;IMAGE_TAG&lt;/code&gt; is dynamically generated using the latest commit hash to ensure that each build has a unique and traceable Docker image tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Prepare'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s2"&gt;"git rev-parse --short HEAD"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Failed to determine image tag: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Static Code Analysis (SonarQube)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why use SonarQube?
&lt;/h3&gt;

&lt;p&gt;It detects bugs, vulnerabilities, and code smells.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;docker-compose.yml&lt;/code&gt; file to launch SonarQube in a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonarqube:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonarqube&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9000:9000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarqube_data:/opt/sonarqube/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarqube_extensions:/opt/sonarqube/extensions&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube_extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command to launch SonarQube in detached mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access: &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt; - login with admin/admin&lt;/p&gt;

&lt;h3&gt;
  
  
  Jenkins Stage
&lt;/h3&gt;

&lt;p&gt;The SonarQube Analysis stage scans the application's source code for quality and security issues using SonarQube. The stage dynamically retrieves the SonarQube token from Vault, ensuring that no sensitive information is stored in Jenkins. It performs the analysis on the entire codebase, excluding Java files, and reports any potential issues or vulnerabilities found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SonarQube Analysis'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s2"&gt;"${VAULT_SECRET_SONAR}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'SONAR_TOKEN'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'token'&lt;/span&gt;&lt;span class="o"&gt;]]]])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;scannerHome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'SonarQube Scanner'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="s1"&gt;'hudson.plugins.sonar.SonarRunnerInstallation'&lt;/span&gt;
                    &lt;span class="n"&gt;withSonarQubeEnv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SonarQube'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"""
                        ${scannerHome}/bin/sonar-scanner \
                        -Dsonar.projectKey=nodejs-app \
                        -Dsonar.sources=. \
                        -Dsonar.exclusions=**/*.java \
                        -Dsonar.login=$SONAR_TOKEN
                        """&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"SonarQube analysis failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ef53altv3v81s0mzmgw.png" alt="Image description" width="800" height="317"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Security Scanning with Trivy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Scan Code and Images?
&lt;/h3&gt;

&lt;p&gt;You need to scan both your code and Docker images to catch known vulnerabilities before they reach production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filesystem Scan
&lt;/h3&gt;

&lt;p&gt;Scans the application's filesystem for known vulnerabilities using Trivy. The &lt;code&gt;--severity HIGH,CRITICAL&lt;/code&gt; flag ensures that only high and critical vulnerabilities are reported. The &lt;code&gt;|| true&lt;/code&gt; allows the pipeline to continue even if vulnerabilities are found (you can adjust this to fail the build if necessary).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Security Scan with Trivy (FS)'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'trivy fs --scanners vuln --no-progress --severity HIGH,CRITICAL --format table --output trivy-fs-report.txt . || true'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cat trivy-fs-report.txt'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Trivy scan failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build Docker Image
&lt;/h3&gt;

&lt;p&gt;Builds the Docker image using the Dockerfile in the repository, tagging it with the generated &lt;code&gt;IMAGE_NAME&lt;/code&gt; and &lt;code&gt;IMAGE_TAG&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build Docker Image'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Docker build failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Scan
&lt;/h3&gt;

&lt;p&gt;Scans the newly built Docker image for vulnerabilities using Trivy. The &lt;code&gt;--timeout&lt;/code&gt; flag prevents the scan from running indefinitely. The build will fail if high or critical vulnerabilities are detected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Scan Docker Image'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s2"&gt;"""
                    trivy image --timeout 10m \
                    --scanners vuln \
                    --no-progress \
                    --severity HIGH,CRITICAL \
                    --format table \
                    --output trivy-report.txt \
                    ${IMAGE_NAME}:${IMAGE_TAG}
                    """&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;returnStatus:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cat trivy-report.txt'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Docker image scan failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Push Docker Image to Registry
&lt;/h2&gt;

&lt;p&gt;The built and scanned Docker image is then pushed to Docker Hub. Vault credentials for Docker Hub are securely retrieved to log in to the registry and push the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Push Image to Docker Hub'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s2"&gt;"${VAULT_SECRET_DOCKERHUB}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_USER'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
                    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_PASS'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;]]])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"docker push ${IMAGE_NAME}:${IMAGE_TAG}"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Docker push failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5az8omhk4e4r9oq7cfns.png" alt="Image description" width="800" height="143"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  GitOps Update and ArgoCD Sync
&lt;/h2&gt;

&lt;p&gt;Here, we update the GitOps repository with the new image tag. This update triggers ArgoCD to synchronize the Kubernetes cluster with the GitOps repository, ensuring that the latest image is deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GitOps Update'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s2"&gt;"${VAULT_SECRET_GITOPS}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'SSH_KEY'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="o"&gt;]]]])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf temp-repo'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"mkdir -p ~/.ssh &amp;amp;&amp;amp; echo \"\$SSH_KEY\" &amp;gt; ~/.ssh/id_rsa &amp;amp;&amp;amp; chmod 600 ~/.ssh/id_rsa"&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"git clone ${GITOPS_REPO} temp-repo"&lt;/span&gt;
                    &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'temp-repo'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"sed -i 's|image: .*|image: ${IMAGE_NAME}:${IMAGE_TAG}|' k8s/deployment.yaml"&lt;/span&gt;
                        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                            &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s2"&gt;"git status --porcelain"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"git add ."&lt;/span&gt;
                                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"git commit -m 'Update image tag to ${IMAGE_TAG}'"&lt;/span&gt;
                                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"git push origin main"&lt;/span&gt;
                            &lt;span class="o"&gt;}&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"GitOps update failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitOps Update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Securely clones the GitOps repository using an SSH key retrieved from Vault.&lt;/li&gt;
&lt;li&gt;Updates the image tag in the &lt;code&gt;k8s/deployment.yaml&lt;/code&gt; file of the cloned GitOps repository with the newly built &lt;code&gt;IMAGE_NAME&lt;/code&gt; and &lt;code&gt;IMAGE_TAG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Commits and pushes the changes to the GitOps repository. Crucially, it only adds the modified &lt;code&gt;deployment.yaml&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the GitOps repo is updated, ArgoCD will automatically detect and sync the changes to your Kubernetes cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sync ArgoCD'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"argocd app sync nodejs-app"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;currentBuild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;
                &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"ArgoCD sync failed: ${e.message}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F070pn2c1nl2mncv0demk.png" alt="Image description" width="800" height="100"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 5: Prepare Your GitOps Repository
&lt;/h2&gt;

&lt;p&gt;The GitOps repository serves as the declarative source of truth for your Kubernetes cluster's desired state. ArgoCD continuously monitors this repository and automatically applies any changes to your cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Kubernetes Deployment Manifest (&lt;code&gt;k8s/deployment.yaml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This manifest defines how your application will be deployed and managed in Kubernetes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-ns&lt;/span&gt; &lt;span class="c1"&gt;# Ensure this matches the namespace in your ArgoCD Application&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-app-access&lt;/span&gt; &lt;span class="c1"&gt;# Service account for Vault access (configured later)&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rihab26/nodejs-app:latest&lt;/span&gt; &lt;span class="c1"&gt;# The image tag will be updated by Jenkins&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3001&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MONGODB_URI&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connection_string&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-exporter&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/node-exporter&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Points:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;image&lt;/code&gt; field will be dynamically updated by the Jenkins pipeline with the unique &lt;code&gt;IMAGE_TAG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The namespace should match the one defined in your ArgoCD Application.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;serviceAccountName&lt;/code&gt; will be used for authenticating with Vault from within the Kubernetes pod to access secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example ArgoCD Application YAML
&lt;/h3&gt;

&lt;p&gt;To instruct ArgoCD to manage our application deployment, create an Application resource within your Kubernetes cluster (typically in the &lt;code&gt;argocd&lt;/code&gt; namespace):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes.default.svc&lt;/span&gt; &lt;span class="c1"&gt;# Your Kubernetes API server address&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-ns&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;git@github.com:RihabHaddad/GitOps.git'&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your GitOps repo URL&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;recurse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*-secret.yaml'&lt;/span&gt;
  &lt;span class="na"&gt;syncPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;automated&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;prune&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;      &lt;span class="c1"&gt;# Delete resources no longer in Git&lt;/span&gt;
      &lt;span class="na"&gt;selfHeal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# Revert changes made outside of Git&lt;/span&gt;
      &lt;span class="na"&gt;allowEmpty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateNamespace=true&lt;/span&gt; &lt;span class="c1"&gt;# Automatically create the destination namespace if it doesn't exist&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ServerSideApply=true&lt;/span&gt; &lt;span class="c1"&gt;# Use Server-Side Apply for better conflict management&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Replace=true&lt;/span&gt;       &lt;span class="c1"&gt;# Replace resources instead of patching (use with caution)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;destination&lt;/code&gt;: Specifies the Kubernetes cluster and namespace where the application will be deployed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;source&lt;/code&gt;: Points to your GitOps repository, the target revision (branch), and the path to the Kubernetes manifests (&lt;code&gt;k8s/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syncPolicy&lt;/code&gt;: Configures how ArgoCD should synchronize the cluster with the Git repository. &lt;code&gt;automated&lt;/code&gt; enables automatic syncing upon changes in the Git repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a Service Account for Vault Access in Kubernetes
&lt;/h3&gt;

&lt;p&gt;To enable your application pods to securely access secrets from Vault, create a dedicated Service Account in your application namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-mongodb-access&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-ns&lt;/span&gt;
&lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1"&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-app-access&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-ns&lt;/span&gt;
&lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply this manifest using &lt;code&gt;kubectl apply -f file -n app-ns&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the same directory (&lt;code&gt;..&lt;/code&gt;), you'll also find other Kubernetes manifests (such as &lt;code&gt;rbac.yaml&lt;/code&gt; and potentially others) that are necessary for configuring Vault access within your cluster. We will link this Service Account to a specific Vault policy later to control the secrets it can access."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobd1t9etnp5oxyr7ug6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobd1t9etnp5oxyr7ug6a.png" alt="Image description" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Secrets Management with HashiCorp Vault (Secure Credential Handling)
&lt;/h2&gt;

&lt;p&gt;Managing secrets like API keys, database passwords, or tokens directly in source code is a serious security risk. Vault helps you store and access these secrets securely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use Vault?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Centralized and secure storage of secrets&lt;/li&gt;
&lt;li&gt;Fine-grained access control (ACL policies)&lt;/li&gt;
&lt;li&gt;Dynamic secrets generation (e.g., DB credentials)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installing Vault using Helm (Simplified Deployment)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace vault
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm &lt;span class="nb"&gt;install &lt;/span&gt;vault hashicorp/vault &lt;span class="nt"&gt;--namespace&lt;/span&gt; vault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initializing Vault (One-Time Setup)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VAULT_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;your-vault-ip-or-hostname&amp;gt;:8200 &lt;span class="c"&gt;# Replace with your Vault service address&lt;/span&gt;
vault operator init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vault will return several Unseal Keys and a Root Token. Store these keys securely, as they are required for unsealing Vault and performing administrative actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unseal Vault:
&lt;/h3&gt;

&lt;p&gt;Vault is sealed by default for security reasons. You need to provide a minimum of 3 Unseal Keys (out of 5) to unseal Vault.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault operator unseal &amp;lt;Unseal-Key1&amp;gt;
vault operator unseal &amp;lt;Unseal-Key2&amp;gt;
vault operator unseal &amp;lt;Unseal-Key3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login to Vault using the Root Token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault login &amp;lt;Root-Token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable the Key-Value Secrets Engine (v2)
&lt;/h3&gt;

&lt;p&gt;Vault supports multiple secrets engines. Here, we use the KV (Key-Value) version 2 engine to store our secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault secrets &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret kv-v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Storing Secrets in Vault
&lt;/h3&gt;

&lt;p&gt;Now, securely store the necessary secrets under the &lt;code&gt;secret/&lt;/code&gt; path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault kv put secret/github &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_github_token"&lt;/span&gt;
vault kv put secret/dockerhub &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_docker_user"&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_docker_pass"&lt;/span&gt;
vault kv put secret/sonarqube &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_sonar_token"&lt;/span&gt;
vault kv put secret/gitops &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Ensure your SSH private key for GitOps is available&lt;/span&gt;
vault kv put secret/mongodb &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_username"&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_password"&lt;/span&gt; &lt;span class="nv"&gt;connection_string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mongodb://username:password@mongo-service:27017"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure AppRole Authentication (for Jenkins)
&lt;/h3&gt;

&lt;p&gt;AppRole authentication allows Jenkins to securely authenticate with Vault using a &lt;code&gt;role_id&lt;/code&gt; and &lt;code&gt;secret_id&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable AppRole Authentication:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault auth &lt;span class="nb"&gt;enable &lt;/span&gt;approle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a policy for Jenkins:&lt;/strong&gt;
Create a file named &lt;code&gt;jenkins-policy.hcl&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# jenkins-policy.hcl&lt;/span&gt;
&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"secret/data/github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"secret/data/dockerhub"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"secret/data/sonarqube"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"secret/data/gitops"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault policy write jenkins-policy jenkins-policy.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create an AppRole and bind the policy:&lt;/strong&gt;
This command creates an AppRole (&lt;code&gt;jenkins-role&lt;/code&gt;) with the &lt;code&gt;jenkins-policy&lt;/code&gt; that allows reading from the &lt;code&gt;secret/&lt;/code&gt; path.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault write auth/approle/role/jenkins-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;token_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"jenkins-policy"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;token_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1h &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;token_max_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve Role ID and Secret ID&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault &lt;span class="nb"&gt;read &lt;/span&gt;auth/approle/role/jenkins-role/role-id
vault write &lt;span class="nt"&gt;-f&lt;/span&gt; auth/approle/role/jenkins-role/secret-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of these commands provides the &lt;code&gt;role_id&lt;/code&gt; and &lt;code&gt;secret_id&lt;/code&gt; that you configured in the Jenkins Vault Plugin settings in Step 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Vault Kubernetes Authentication for Dynamic Secrets Access
&lt;/h3&gt;

&lt;p&gt;This allows your application pods running in Kubernetes to authenticate with Vault using their Service Account and retrieve secrets securely.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable Kubernetes Authentication in Vault&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault auth &lt;span class="nb"&gt;enable &lt;/span&gt;kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure Kubernetes Auth Backend&lt;/strong&gt;
This step allows Vault to authenticate Kubernetes service accounts. You'll need to provide details about your Kubernetes cluster.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault write auth/kubernetes/config &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;kubernetes_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;KUBERNETES_API_SERVER&amp;gt;:&amp;lt;PORT&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;kubernetes_ca_cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;token_reviewer_jwt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /var/run/secrets/kubernetes.io/serviceaccount/token&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://kubernetes.default.svc.cluster.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;disable_iss_validation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define a Vault Policy for Access Control (e.g., MongoDB Credentials):&lt;/strong&gt;
You'll define a Vault policy for controlling access to the MongoDB credentials. This policy ensures that only specific roles can access the sensitive data.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault policy write mongodb - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
path "secret/data/mongodb" {
  capabilities = ["read"]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Role for Kubernetes Authentication&lt;/strong&gt;
This role links the Kubernetes Service Account (&lt;code&gt;vault-mongodb-access&lt;/code&gt; in the &lt;code&gt;app-ns&lt;/code&gt; namespace) to the &lt;code&gt;mongodb&lt;/code&gt; policy, allowing pods with this Service Account to read MongoDB credentials.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault write auth/kubernetes/role/mongodb-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;bound_service_account_names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"vault-mongodb-access"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;bound_service_account_namespaces&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"app-ns"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mongodb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"24h"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your application pods running with the &lt;code&gt;vault-app-access&lt;/code&gt; Service Account in the &lt;code&gt;app-ns&lt;/code&gt; namespace can authenticate to Vault using the Kubernetes Auth method and retrieve the MongoDB credentials defined in the &lt;code&gt;mongodb&lt;/code&gt; policy. You would typically use a Vault client library within your application to perform this authentication and secret retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7opvt80xbnzrqowwc4xp.png" alt="Image description" width="800" height="357"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 7: Monitoring with Prometheus &amp;amp; Grafana
&lt;/h2&gt;

&lt;p&gt;Implementing monitoring is crucial for gaining visibility into your application's performance and the health of your Kubernetes cluster. Prometheus will collect metrics, and Grafana will provide insightful visualizations through dashboards.&lt;/p&gt;

&lt;p&gt;You can find comprehensive configuration files for Prometheus and Grafana in the &lt;code&gt;monitoring/&lt;/code&gt; folder of the example GitOps repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing Application Metrics with ServiceMonitor
&lt;/h3&gt;

&lt;p&gt;To enable Prometheus to scrape metrics from your Node.js application, you need to expose a &lt;code&gt;/metrics&lt;/code&gt; endpoint and define a ServiceMonitor resource in Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring.coreos.com/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceMonitor&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app-monitor&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt; &lt;span class="c1"&gt;# Ensure Prometheus is in the 'monitoring' namespace&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt; &lt;span class="c1"&gt;# Label selector for Prometheus&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001"&lt;/span&gt; &lt;span class="c1"&gt;# The port your application exposes metrics on&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/metrics&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;
  &lt;span class="na"&gt;namespaceSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-ns&lt;/span&gt; &lt;span class="c1"&gt;# Monitor services in your application namespace&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app&lt;/span&gt; &lt;span class="c1"&gt;# Select the service for your Node.js application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply this manifest to your Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;This connects Prometheus to your app's &lt;code&gt;/metrics&lt;/code&gt; endpoint on port 3001&lt;/p&gt;

&lt;h3&gt;
  
  
  Prometheus Setup with RBAC &amp;amp; ConfigMap
&lt;/h3&gt;

&lt;p&gt;Prometheus needs specific permissions to discover and scrape metrics. The configuration is typically defined in a ConfigMap. The example &lt;code&gt;prometheus.yml&lt;/code&gt; in the GitOps repository demonstrates how to configure Prometheus to scrape metrics from Kubernetes pods based on labels:&lt;/p&gt;

&lt;p&gt;Here's a snippet from the &lt;code&gt;prometheus.yml&lt;/code&gt; ConfigMap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;global:&lt;/span&gt;
      &lt;span class="s"&gt;scrape_interval: 15s&lt;/span&gt;
    &lt;span class="s"&gt;scrape_configs:&lt;/span&gt;
    &lt;span class="s"&gt;- job_name: 'nodejs-app'&lt;/span&gt;
      &lt;span class="s"&gt;kubernetes_sd_configs:&lt;/span&gt;
      &lt;span class="s"&gt;- role: pod&lt;/span&gt;
        &lt;span class="s"&gt;relabel_configs:&lt;/span&gt;
        &lt;span class="s"&gt;- source_labels: [__meta_kubernetes_pod_label_app]&lt;/span&gt;
          &lt;span class="s"&gt;action: keep&lt;/span&gt;
          &lt;span class="s"&gt;regex: nodejs-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can extend this config to monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes API servers&lt;/li&gt;
&lt;li&gt;Node Exporters&lt;/li&gt;
&lt;li&gt;kube-state-metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prometheus is also granted access via ServiceAccount, ClusterRole, and ClusterRoleBinding defined in the same GitOps folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying Grafana and Accessing Dashboards
&lt;/h3&gt;

&lt;p&gt;Grafana provides a user-friendly interface for visualizing the metrics collected by Prometheus. Deploy Grafana to your Kubernetes cluster (refer to the example GitOps repository for the Deployment and Service manifests).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana-deployment&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access Grafana by port-forwarding the Grafana service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/grafana-service &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring 3000:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, open your web browser and navigate to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. The default login credentials are admin / admin. Change these immediately in a production environment.&lt;/p&gt;

&lt;p&gt;Once logged in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add Prometheus as a Data Source:&lt;/strong&gt;&lt;br&gt;
Navigate to Configuration &amp;gt; Data sources.&lt;br&gt;
Click Add data source and select Prometheus.&lt;br&gt;
In the HTTP Settings section, enter the Prometheus service URL: &lt;code&gt;http://prometheus-service.monitoring.svc:9090&lt;/code&gt;.&lt;br&gt;
Click Save &amp;amp; test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Import Dashboards:&lt;/strong&gt;&lt;br&gt;
Navigate to Create &amp;gt; Import.&lt;br&gt;
You can import pre-built dashboards from the Grafana community by entering their ID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js app metrics: Dashboard ID 1860&lt;/li&gt;
&lt;li&gt;Kubernetes cluster overview: Dashboard ID 315 or 6417&lt;/li&gt;
&lt;li&gt;Node Exporter full metrics: Dashboard ID 1860&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, you can create your own custom dashboards tailored to your specific monitoring needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh72wdxeh2ce8eas84mkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh72wdxeh2ce8eas84mkn.png" alt="Image description" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can also deploy the entire Prometheus + Grafana stack using Helm for a quick, modular, and maintainable setup:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm &lt;span class="nb"&gt;install &lt;/span&gt;monitoring prometheus-community/kube-prometheus-stack
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This installs Prometheus, Grafana, Alertmanager, node-exporter, and more directly into your Kubernetes cluster.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📚 Glossary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: Practices to automate building, testing, and deploying software changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevSecOps&lt;/strong&gt;: Integrating security practices into the DevOps lifecycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitOps&lt;/strong&gt;: Using Git as the source of truth for infrastructure and deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins&lt;/strong&gt;: Open-source server for managing CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt;: Platform for building and running applications in containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trivy&lt;/strong&gt;: Vulnerability scanner for containers and repositories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SonarQube&lt;/strong&gt;: Tool for continuous code quality inspection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ArgoCD&lt;/strong&gt;: GitOps tool for Kubernetes continuous delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vault&lt;/strong&gt;: Secret management system for storing sensitive information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt;: Monitoring and alerting toolkit for time-series data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt;: Visualization platform for metrics and logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt;: Command-line tool for managing Kubernetes clusters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minikube&lt;/strong&gt;: Tool for running a local Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm&lt;/strong&gt;: Package manager for Kubernetes apps.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This tutorial walked you through building a secure, production-grade DevSecOps pipeline using Jenkins, ArgoCD, Trivy, SonarQube, and Vault, creating a foundation for rapid, reliable, and secure software delivery.&lt;/p&gt;

&lt;p&gt;Remember, security is a continuous journey - keep refining your pipeline and adapting to evolving best practices.&lt;/p&gt;

&lt;p&gt;I'm committed to continuous improvement. To that end, I plan to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add GitHub Actions for CI (to showcase pipeline flexibility)&lt;/li&gt;
&lt;li&gt;Investigate canary/blue-green deployments (for enhanced release reliability)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's work together to make DevOps more secure and accessible. Contributions and discussions are welcome!&lt;/p&gt;

&lt;p&gt;Reach out with questions or collaboration ideas - or just drop a ⭐️ if you find it useful!&lt;/p&gt;

&lt;p&gt;GitHub Repositories:&lt;br&gt;
🔗 [App code]: &lt;a href="https://github.com/RihabHaddad/DevSecOps-pipeline" rel="noopener noreferrer"&gt;DevSecOps - pipeline&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 [GitOps config]: &lt;a href="https://github.com/RihabHaddad/GitOps" rel="noopener noreferrer"&gt;GitOps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find me on LinkedIn: &lt;a href="https://www.linkedin.com/in/rihab-haddad/" rel="noopener noreferrer"&gt;Rihab Haddad&lt;/a&gt; or email [&lt;a href="mailto:rihabhaddad26@gmail.com"&gt;rihabhaddad26@gmail.com&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>devops</category>
      <category>devsecops</category>
      <category>cicd</category>
      <category>security</category>
    </item>
  </channel>
</rss>
