Deploy Starbucks Clone with DevSecOps

Deploy Starbucks Clone with DevSecOps

DevSecOps integrates security into the development process. This approach ensures that security is considered at every stage of the software lifecycle, from design to deployment.

This proactive strategy helps in creating more secure applications while maintaining development speed and efficiency.

Deployment Stages to Build a Starbucks Clone

  1. Git Checkout: Start by checking out the code from GitHub. This ensures you have the latest version of the project.

  2. SonarQube Analysis: Run SonarQube to identify any vulnerabilities in the code. This step is crucial for maintaining security and code quality.

  3. Quality Gate: Use the Quality Gate feature to scan the repository for security issues. It acts as a checkpoint to ensure the code meets certain standards before moving forward.

  4. Trivy Scan: Utilize Trivy to scan the files for any potential threats. This tool helps in identifying vulnerabilities early in the process.

  5. Build Docker Image: Create a Docker image of the application. This image will serve as the deployable unit for the application.

  6. Tag and Push Image to DockerHub: Tag the Docker image appropriately and push it to DockerHub. This step makes the image available for deployment.

  7. Docker Scout Image: Use Docker Scout to inspect the image for any issues or vulnerabilities. This ensures the image is safe and ready for deployment.

  8. Deploy to Container: Finally, deploy the container using the Docker image we built. This step brings the application to life, making it accessible for use.

Steps to Deploy Starbucks Clone App on EC2

  1. Create EC2 Instance:

    • Configure a t2.xlarge instance with Ubuntu and attach 30GB of General Purpose SSD or Magnetic Storage.

    • Set up security groups to allow access to various ports:

      • TCP 22: SSH access to the EC2 instance.

      • TCP 8080: Jenkins access.

      • TCP 9000: SonarQube access.

      • TCP 3000: Access the React and NodeJS app.

  2. Configure EC2 Ubuntu Instance:

    • Install AWS CLI:

        bashCopysudo apt install unzip -y
        curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
        unzip awscliv2.zip
        sudo ./aws/install
      
    • Install Jenkins on Ubuntu:

        bashCopysudo apt update -y
        wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo tee /etc/apt/keyrings/adoptium.asc
        echo "deb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
        sudo apt update -y
        sudo apt install temurin-17-jdk -y
        curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
        echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
        sudo apt-get update -y
        sudo apt-get install jenkins -y
        sudo systemctl start jenkins
        sudo systemctl status jenkins
      
    • Install Docker:

        bashCopysudo apt-get update
        sudo apt-get install ca-certificates curl
        sudo install -m 0755 -d /etc/apt/keyrings
        sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
        sudo chmod a+r /etc/apt/keyrings/docker.asc
        echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
        sudo apt-get update
        sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
        sudo usermod -aG docker ubuntu
        sudo chmod 777 /var/run/docker.sock
        newgrp docker
        sudo systemctl status docker
      
  3. Verify Installations: Check that Docker, AWS CLI, and Jenkins are properly installed:

     jenkins --version
     docker --version
     aws --version
    

  4. Install Trivy Scan:

     sudo apt update
     sudo apt install -y curl apt-transport-https
     curl -sSfL https://github.com/aquasecurity/trivy/releases/latest/download/trivy_0.29.2_Linux-64bit.tar.gz | sudo tar -xz -C /usr/local/bin
     trivy --version
    
  5. Installing Docker-Scout: to scan docker images

    • Login to DockerHub:

        docker login -u <username>
      
    • Install Docker Scout:

        curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sudo sh -s -- -b /usr/local/bin
      
    • Verify the installation:

        docker-scout version
      
    • Change the permissions of docker.sock.

Installation Completed


Login to Jenkins and Setup Plugins

URL: localhost:8080 login Jenkins

Jenkins with SonarQube (with docker container)

Run the SonarQube with docker container:

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

Login to SonarQube : http://<IP-add>:9000

Integrate Sonarqube and Jenkins

  1. Go to Sonarqube > Administration

    Go to Configuration > Webhooks

    Click on Create:

    We are all set: as you can see we successfully created webhook on sonarqube

  2. Do same for jenkins → First create user in Sonarqube

    Go to Sonarqube > Security > user

    Create a token in Administrator to authenticate jenkins

  3. Login to Jenkins : to add sonarqube token in Jenkins

    Go to manage jenkins > Credentials

    Go to global > Add credentials

    Click on “add credentials”

    Add credentials: The one we copied the token from sonarqube

    1. Go to Jenkins > Manage Jenkins > Sonarqube Installation

      Congrats! You have successfully integrated Sonar and Jenkins

Install Required Software ( Jenkins → Tools)

  • Git

  • JDK 17: The first thing you need is JDK 17. Download and install it. Name it JDK 17 and choose the appropriate version.

  • SonarQube Scanner: Add the sonar-scanner plugin to Jenkins. This will help in static code analysis.

  • Node.js: Install Node.js version 16.20.0.

  • Dependency-Check: Install this from GitHub for your project’s security scanning.

  • Docker: Make sure Docker is installed, preferably the latest version.

Configure System Settings for SonarQube

  • In Jenkins, navigate to Manage Jenkins > Configure System.

  • For SonarQube:

    • Add a new SonarQube server.

    • Enter the SonarQube URL (e.g., http://localhost:9000).

    • Provide the Authentication Token created in Jenkins credentials.

Set Up Gmail Email Notification

  1. Login to Gmail
  • For Gmail notifications, you’ll need to set up an app password for your Gmail account.

    • Log in to Gmail and create an app password by navigating to your Google Account > Security > App Passwords.

    • Use this app password when setting up email notifications in Jenkins.

    • Add to global credential in Jenkins with token

  1. Now go to Jenkins
  • In Manage Jenkins > Configure System, scroll to Extended Email Notification.

    • Set the SMTP server as smtp.gmail.com.

    • Port: 465 (for SSL).

    • Credentials: Use the Gmail credentials you created earlier.

    • Enable SSL and OAuth2 for secure communication.

Configure Jenkins Credentials

  • Navigate to Manage Jenkins > Manage Credentials.

  • Add the following credentials:

    • SonarQube Token (authentication token you created earlier).

    • Docker Hub Credentials (if you're pushing images to Docker Hub).

    • Gmail Credentials (use your Gmail email address and app password).

Don’t forget to check out github repo: github.com/amitsinghs98/starbucks/tree/main

Explanation of the Jenkins Pipeline Script before reading script (helps)

Here’s a breakdown of what each stage does:

  • Clean Workspace: Clears the workspace to ensure a fresh start.

  • Checkout Code: Checks out the latest code from the Git repository.

  • SonarQube Analysis: Runs static code analysis using SonarQube.

  • Install Dependencies: Installs npm dependencies required for the Node.js application.

  • OWASP Dependency Check: Scans the project for any vulnerabilities.

  • Docker Build and Push: Builds a Docker image and pushes it to Docker Hub.

  • Docker Security Scan: Uses Docker Scout to analyze the security of the Docker image.

  • Deploy to Docker Container: Deploys the Docker container to run the application.

  • Notify via Email: Sends an email notification with the build results and any attached logs.

Create Jenkins Pipeline Job

  • Go to New Item, name the project (e.g., Starbucks), and choose Pipeline.

  • Define the Pipeline script. Below is a basic template for your pipeline:

pipeline {
    agent any
    tools {
        jdk 'jdk17'
        nodejs 'node16'
    }
    environment {
        SCANNER_HOME = tool 'sonar-scanner'
    }
    stages {
        stage ("clean workspace") {
            steps {
                cleanWs()
            }
        }
        stage ("Git checkout") {
            steps {
                git branch: 'main', url: 'https://github.com/yeshwanthlm/starbucks.git'
            }
        }
        stage("Sonarqube Analysis ") {
            steps {
                withSonarQubeEnv('sonar-server') {
                    sh ''' $SCANNER_HOME/bin/sonar-scanner -Dsonar.projectName=starbucks \
                    -Dsonar.projectKey=starbucks '''
                }
            }
        }
        stage("quality gate") {
            steps {
                script {
                    waitForQualityGate abortPipeline: false, credentialsId: 'Sonar-token' 
                }
            }
        }
        stage("Install NPM Dependencies") {
            steps {
                sh "npm install"
            }
        }
        stage('OWASP FS SCAN') {
            steps {
                dependencyCheck additionalArguments: '--scan ./ --disableYarnAudit --disableNodeAudit', odcInstallation: 'DP-Check'
                dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
            }
        }
        stage("Trivy File Scan") {
            steps {
                sh "trivy fs . > trivy.txt"
            }
        }
        stage ("Build Docker Image") {
            steps {
                sh "docker build -t starbucks ."
            }
        }
        stage ("Tag & Push to DockerHub") {
            steps {
                script {
                    withDockerRegistry(credentialsId: 'docker') {
                        sh "docker tag starbucks amitsinghs98/starbucks:latest"
                        sh "docker push amitsinghs98/starbucks:latest"
                    }
                }
            }
        }
        stage('Docker Scout Image') {
            steps {
                script {
                    withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
                        sh 'docker-scout quickview amitsinghs98/starbucks:latest'
                        sh 'docker-scout cves amitsinghs98/starbucks:latest'
                        sh 'docker-scout recommendations amitsinghs98/starbucks:latest'
                    }
                }
            }
        }
        //docker
                stage ("Deploy to Conatiner") {
            steps {
                sh 'docker run -d --name starbucks -p 3000:3000 amitsinghs98/starbucks:latest'
            }
        }
    }
    post {
        always {
            emailext attachLog: true,
                subject: "'${currentBuild.result}'",
                body: """
                    <html>
                    <body>
                        <div style="background-color: #FFA07A; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">Project: ${env.JOB_NAME}</p>
                        </div>
                        <div style="background-color: #90EE90; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">Build Number: ${env.BUILD_NUMBER}</p>
                        </div>
                        <div style="background-color: #87CEEB; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">URL: ${env.BUILD_URL}</p>
                        </div>
                    </body>
                    </html>
                """,
                to: 'provide_your_Email_id_here',
                mimeType: 'text/html',
                attachmentsPattern: 'trivy.txt'
        }
    }
}

Testing the Pipeline

  • Once your Jenkins job is set up, run the pipeline.

  • Jenkins will execute each stage in sequence.

  • After the pipeline completes, check your Gmail inbox for the notification.

Conclusion

You now have a fully automated Jenkins pipeline that includes:

  • Static code analysis with SonarQube.

  • Dependency checks with OWASP.

  • Docker image build, push, and security scanning.

  • Email notifications with build results and logs.

Key Steps Recap:

  1. Jenkins Build: We set up the Jenkins pipeline to automate the build process, integrating tools like SonarQube for code quality checks and OSF SFS for security scans.

  2. Docker Image Creation: We used Docker to build an image for the application and pushed it to Docker Hub.

  3. Docker Scout: We used Docker Scout to analyze the image for vulnerabilities and security risks.

  4. Deployment: The final step was the deployment of the Docker container on an instance and exposing it on port 3000 for public access.

You can try deployment with kubernetes too

pipeline {
    agent any
    tools {
        jdk 'jdk17'
        nodejs 'node16'
    }
    environment {
        SCANNER_HOME = tool 'sonar-scanner'
    }
    stages {
        stage ("clean workspace") {
            steps {
                cleanWs()
            }
        }
        stage ("Git checkout") {
            steps {
                git branch: 'main', url: 'https://github.com/yeshwanthlm/starbucks.git'
            }
        }
        stage("Sonarqube Analysis ") {
            steps {
                withSonarQubeEnv('sonar-server') {
                    sh ''' $SCANNER_HOME/bin/sonar-scanner -Dsonar.projectName=starbucks \
                    -Dsonar.projectKey=starbucks '''
                }
            }
        }
        stage("quality gate") {
            steps {
                script {
                    waitForQualityGate abortPipeline: false, credentialsId: 'Sonar-token' 
                }
            }
        }
        stage("Install NPM Dependencies") {
            steps {
                sh "npm install"
            }
        }
        stage('OWASP FS SCAN') {
            steps {
                dependencyCheck additionalArguments: '--scan ./ --disableYarnAudit --disableNodeAudit', odcInstallation: 'DP-Check'
                dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
            }
        }
        stage("Trivy File Scan") {
            steps {
                sh "trivy fs . > trivy.txt"
            }
        }
        stage ("Build Docker Image") {
            steps {
                sh "docker build -t starbucks ."
            }
        }
        stage ("Tag & Push to DockerHub") {
            steps {
                script {
                    withDockerRegistry(credentialsId: 'docker') {
                        sh "docker tag starbucks amitsinghs98/starbucks:latest"
                        sh "docker push amitsinghs98/starbucks:latest"
                    }
                }
            }
        }
        stage('Docker Scout Image') {
            steps {
                script {
                    withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
                        sh 'docker-scout quickview amitsinghs98/starbucks:latest'
                        sh 'docker-scout cves amitsinghs98/starbucks:latest'
                        sh 'docker-scout recommendations amitsinghs98/starbucks:latest'
                    }
                }
            }
        }
        // Kubernetes Deployment Stage
        stage('Deploy to Kubernetes') {
            steps {
                script {
                    // Assuming you have set the Kubernetes credentials in Jenkins
                    withKubeConfig(credentialsId: 'kubeconfig-credentials') {
                        sh 'kubectl apply -f deployment.yaml'  // Apply your deployment YAML file
                        sh 'kubectl rollout status deployment/starbucks' // Wait for deployment to complete
                    }
                }
            }
        }
    }
    post {
        always {
            emailext attachLog: true,
                subject: "'${currentBuild.result}'",
                body: """
                    <html>
                    <body>
                        <div style="background-color: #FFA07A; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">Project: ${env.JOB_NAME}</p>
                        </div>
                        <div style="background-color: #90EE90; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">Build Number: ${env.BUILD_NUMBER}</p>
                        </div>
                        <div style="background-color: #87CEEB; padding: 10px; margin-bottom: 10px;">
                            <p style="color: white; font-weight: bold;">URL: ${env.BUILD_URL}</p>
                        </div>
                    </body>
                    </html>
                """,
                to: 'provide_your_Email_id_here',
                mimeType: 'text/html',
                attachmentsPattern: 'trivy.txt'
        }
    }
}

Deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: starbucks
  labels:
    app: starbucks
spec:
  replicas: 3
  selector:
    matchLabels:
      app: starbucks
  template:
    metadata:
      labels:
        app: starbucks
    spec:
      containers:
      - name: starbucks
        image: amitsinghs98/starbucks:latest
        ports:
        - containerPort: 3000

service.yml

apiVersion: v1
kind: Service
metadata:
  name: starbucks
spec:
  selector:
    app: starbucks
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: LoadBalancer

We are all set with the project, hope you have liked it.