In this hands-on tutorial, you’ll integrate OPA Conftest into a Jenkins pipeline to enforce custom policy-as-code for Kubernetes Deployments and Services. Scanning your manifests before they reach the cluster helps prevent misconfigurations and potential security vulnerabilities.
Prerequisites
Jenkins server with Docker installed
Kubernetes cluster and kubectl configured
OPA Conftest CLI available locally or via Docker
Ensure your kubeconfig credentials are stored in Jenkins (e.g., under credentialsId: 'kubeconfig') before starting.
Jenkins Pipeline Stages
Add a vulnerability scan stage between the Docker build and Kubernetes deployment:
pipeline {
agent any
stages {
stage( 'Docker Build and Push' ) {
steps {
withDockerRegistry([ credentialsId : 'docker-hub' , url : '' ]) {
sh 'sudo docker build -t siddharth67/numeric-app:${GIT_COMMIT} .'
sh 'docker push siddharth67/numeric-app:${GIT_COMMIT}'
}
}
}
stage( 'Vulnerability Scan - Kubernetes' ) {
steps {
sh '''\
docker run --rm \
-v $(pwd):/project \
openpolicyagent/conftest test \
--policy opa-k8s-security.rego \
k8s_deployment_service.yaml
'''
}
}
stage( 'Kubernetes Deployment - DEV' ) {
steps {
withKubeConfig([ credentialsId : 'kubeconfig' ]) {
sh 'sed -i "s|replace|siddharth67/numeric-app:${GIT_COMMIT}|" k8s_deployment_service.yaml'
sh 'kubectl apply -f k8s_deployment_service.yaml'
}
}
}
}
}
Defining the OPA Policy
Create opa-k8s-security.rego at the root of your project:
package main
deny[msg] {
input.kind == "Service"
not input.spec.type == "NodePort"
msg = "Service type should be NodePort"
}
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.containers[0].securityContext.runAsNonRoot
msg = "Containers must not run as root - set runAsNonRoot: true"
}
Resource Kind Rule Description Service spec.type must be NodePortDeployment Containers require securityContext.runAsNonRoot
Running Conftest
From your project directory, run:
docker run --rm \
-v $( pwd ) :/project \
openpolicyagent/conftest test \
--policy opa-k8s-security.rego \
k8s_deployment_service.yaml
If any policy is violated, Conftest exits with a non-zero code and prints the error. Your Jenkins pipeline will fail until you address the violation.
Example failure output:
FAIL: k8s_deployment_service.yaml - main - Containers must not run as root - set runAsNonRoot: true
script returned exit code 1
Fixing Policy Violations
1. Enforce runAsNonRoot
Update your Deployment spec to include a securityContext:
apiVersion : apps/v1
kind : Deployment
metadata :
name : devsecops
labels :
app : devsecops
spec :
replicas : 2
selector :
matchLabels :
app : devsecops
template :
metadata :
labels :
app : devsecops
spec :
containers :
- name : devsecops-container
image : replace
securityContext :
runAsNonRoot : true
---
apiVersion : v1
kind : Service
metadata :
name : devsecops
labels :
app : devsecops
spec :
type : NodePort
ports :
- port : 8080
targetPort : 8080
protocol : TCP
selector :
app : devsecops
Re-run the Conftest command to confirm that all tests pass.
2. Specify Numeric User (Optional)
If you encounter a CreateContainerConfigError due to a non-numeric user, add runAsUser:
securityContext :
runAsNonRoot : true
runAsUser : 100
Commit, push, and trigger a new build.
Verifying Deployment
After a successful pipeline run, validate your resources:
You should see the Deployment and Service running without errors.
Links and References