Implementing DevSecOps in a GitHub Actions Pipeline

Introduction

Ever wondered how security practices are integrated into DevOps pipelines? Welcome to the world of DevSecOps, where security is a fundamental part of the development lifecycle rather than an afterthought. In this tutorial, we will walk through the implementation of DevSecOps in a GitHub Actions pipeline, ensuring security checks at each step.

We will use a Tic-Tac-Toe application as an example and focus on building, scanning, and deploying a secure Docker image. Here is the ๐Ÿ”— GitHub Repository: [Here].

So Grab a coffee, and let's dive in!

DevSecOps Pipeline Overview

The pipeline consists of several key steps:

  1. Triggering the pipeline on pull and push requests to the main branch.

  2. Running jobs:

    • Unit Testing: Ensuring the code works correctly.

    • Static Code Analysis: Checking for code quality and vulnerabilities.

    • Building Artifacts: Preparing the project for deployment.

    • Building and Pushing Docker Image:

      • Authenticate Docker

      • Build the image

      • Scan the image with Trivy

      • Send vulnerability reports to the admin

      • Push the image to a private container registry

    • Updating Kubernetes Deployment: Replacing the old image with the newly built one.

Prerequisites

To follow along, ensure you have:

  • A Dockerfile

  • A GitHub token as a secret for Docker registry login

  • A Kubernetes secret for private registry access

  • A Kubernetes cluster (optional for deployment testing)


Step-by-Step Implementation

Step 1: Setting Up GitHub Actions Workflow

A GitHub Actions workflow automates the CI/CD process. The workflow is triggered on every push or pull request to the main branch while ignoring certain files like README.md to avoid unnecessary builds.

Step 2: Running Unit Tests

Unit tests validate code functionality before deployment.

  • Checkout the repository

  • Set up Node.js

  • Install dependencies

  • Run tests

  test:
    name: Unit Testing
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - name: Install dependencies
        run: npm ci
      - name: Run tests
        run: npm test || echo "No tests found, would add tests in a real project"

Step 3: Static Code Analysis

Static analysis checks for code quality and security vulnerabilities.

  • Checkout the repository

  • Set up Node.js

  • Install dependencies

  • Run ESLint

  lint:
    name: Static Code Analysis
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - name: Install dependencies
        run: npm ci
      - name: Run ESLint
        run: npm run lint

Step 4: Building Artifacts

This step compiles the project and prepares it for deployment.

  • Checkout the repository

  • Set up Node.js

  • Install dependencies

  • Build the project

  • Upload build artifacts

Step 5: Docker Build and Security Scan

5.1 Building the Docker Image

  • Checkout the repository

  • Set up Docker Buildx

  • Login to GitHub Container Registry

  • Build the Docker image

5.2 Scanning the Image with Trivy

  • Run Trivy vulnerability scanner

  • Generate a security report

  • Send the report via email

5.3 Pushing the Image to a Private Registry

  • Push the Docker image to GitHub Container Registry
  docker:
    name: Docker Build and Push
    runs-on: ubuntu-latest
    needs: [build]
    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.TOKEN }}
      - name: Build Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: false
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
          format: "table"
          severity: "CRITICAL,HIGH"
          output: "trivy-report-${{ github.sha }}.txt"
      - name: Push Docker image
        uses: docker/build-push-action@v5
        with:
          push: true

Step 6: Updating Kubernetes Deployment

Once the image is built and pushed, update the Kubernetes deployment file.

  • Checkout the repository

  • Modify deployment.yaml with the new image tag

  • Commit and push changes

  update-k8s:
    name: Update Kubernetes Deployment
    runs-on: ubuntu-latest
    needs: [docker]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Update Kubernetes deployment file
        run: |
          sed -i "s|image:.*|image: ghcr.io/${{ github.repository }}:sha-${{ github.sha }}|g" kubernetes/deployment.yaml
      - name: Commit and push changes
        run: |
          git add kubernetes/deployment.yaml
          git commit -m "Update deployment with new image tag"
          git push

Step 7: Setting Up Kubernetes and ArgoCD

If you wish to automate deployments, follow these steps:

  1. Create a Kubernetes cluster with Kind:
kind create cluster --name=devsecops
  1. Create a Kubernetes Secret for Private Registry Access:
kubectl create secret docker-registry github-container-registry \
  --docker-server=ghcr.io \
  --docker-username=YOUR_GITHUB_USERNAME \
  --docker-password=YOUR_GITHUB_TOKEN \
  --docker-email=YOUR_EMAIL
  1. Deploy with ArgoCD:

    1. Log in to the ArgoCD UI.

    2. Click Create App.

    3. Fill in the necessary details like the application name, Git repository link, and Kubernetes cluster details.

    4. Click Create to deploy your application.

For more details, refer to my Kind and Argo Setup Article Here

Conclusion

By following this tutorial, we have successfully integrated security into our DevOps pipeline using GitHub Actions, Trivy, and ArgoCD. Every code change undergoes testing, static analysis, vulnerability scanning, and secure deployment.

This is just the beginning! You can extend this pipeline with dynamic analysis, infrastructure security checks, and runtime monitoring for even stronger security.

Happy Coding! ๐Ÿš€

0
Subscribe to my newsletter

Read articles from Prasad Zungare Patil directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Prasad Zungare Patil
Prasad Zungare Patil