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:
Triggering the pipeline on pull and push requests to the main branch.
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 tagCommit 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:
- Create a Kubernetes cluster with Kind:
kind create cluster --name=devsecops
- 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
Deploy with ArgoCD:
Log in to the ArgoCD UI.
Click Create App.
Fill in the necessary details like the application name, Git repository link, and Kubernetes cluster details.
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! ๐
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
