Integrate SonarQube with Kubernetes security scanning workflows for continuous code quality analysis

Advanced 45 min Apr 03, 2026 13 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up SonarQube scanner in Kubernetes pods with admission controllers for automated security scanning. Configure CI/CD pipeline integration and security reporting for continuous code quality analysis in containerized environments.

Prerequisites

  • Kubernetes cluster with kubectl access
  • SonarQube server instance
  • Helm 3.x installed
  • Administrative privileges
  • Basic understanding of YAML and containers

What this solves

This tutorial configures SonarQube security scanning within Kubernetes workflows to automatically analyze code quality and security vulnerabilities in containerized applications. You'll set up scanner pods, admission controllers, and automated reporting to catch security issues before deployment.

Step-by-step configuration

Install prerequisites and update system

Start by updating your system packages and installing required tools for Kubernetes and SonarQube integration.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget jq git kubectl helm
sudo dnf update -y
sudo dnf install -y curl wget jq git kubernetes-client helm

Create SonarQube namespace and security contexts

Set up a dedicated namespace for SonarQube operations with proper security contexts and RBAC permissions.

kubectl create namespace sonarqube-security
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sonarqube-scanner
  namespace: sonarqube-security
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: sonarqube-scanner-role
rules:
  • apiGroups: [""]
resources: ["pods", "configmaps", "secrets"] verbs: ["get", "list", "create", "update", "patch"]
  • apiGroups: ["apps"]
resources: ["deployments", "replicasets"] verbs: ["get", "list"]
  • apiGroups: ["admissionregistration.k8s.io"]
resources: ["validatingadmissionwebhooks"] verbs: ["get", "list", "create", "update"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: sonarqube-scanner-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: sonarqube-scanner-role subjects:
  • kind: ServiceAccount
name: sonarqube-scanner namespace: sonarqube-security
kubectl apply -f sonarqube-rbac.yaml

Configure SonarQube scanner ConfigMap

Create a ConfigMap with SonarQube scanner configuration and security scanning rules for different programming languages.

apiVersion: v1
kind: ConfigMap
metadata:
  name: sonarqube-scanner-config
  namespace: sonarqube-security
data:
  sonar-scanner.properties: |
    sonar.host.url=https://sonarqube.example.com
    sonar.projectKey=${PROJECT_KEY}
    sonar.projectName=${PROJECT_NAME}
    sonar.projectVersion=${BUILD_NUMBER}
    sonar.sources=.
    sonar.exclusions=/test/,/vendor/,/node_modules/
    sonar.security.hotspots=REVIEW
    sonar.qualitygate.wait=true
    sonar.qualitygate.timeout=300
  security-rules.json: |
    {
      "securityRules": {
        "java": {
          "rules": ["java:S2068", "java:S2078", "java:S5146"],
          "severity": "BLOCKER"
        },
        "javascript": {
          "rules": ["javascript:S2068", "javascript:S4426", "javascript:S5122"],
          "severity": "CRITICAL"
        },
        "python": {
          "rules": ["python:S2068", "python:S5245", "python:S4507"],
          "severity": "CRITICAL"
        },
        "docker": {
          "rules": ["docker:S6471", "docker:S6472", "docker:S6473"],
          "severity": "MAJOR"
        }
      }
    }
kubectl apply -f scanner-config.yaml

Create SonarQube scanner pod template

Set up a pod template for running SonarQube scans with security contexts and resource limits.

apiVersion: v1
kind: Pod
metadata:
  name: sonarqube-scanner-pod
  namespace: sonarqube-security
  labels:
    app: sonarqube-scanner
spec:
  serviceAccountName: sonarqube-scanner
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
  initContainers:
  - name: git-clone
    image: alpine/git:2.40.1
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    command: ['/bin/sh']
    args:
    - -c
    - |
      git clone ${GIT_REPO_URL} /workspace
      cd /workspace
      git checkout ${GIT_BRANCH:-main}
    env:
    - name: GIT_REPO_URL
      value: "https://github.com/example/app.git"
    - name: GIT_BRANCH
      value: "main"
    volumeMounts:
    - name: workspace
      mountPath: /workspace
    - name: tmp
      mountPath: /tmp
  containers:
  - name: sonar-scanner
    image: sonarsource/sonar-scanner-cli:5.0.1
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    env:
    - name: SONAR_TOKEN
      valueFrom:
        secretKeyRef:
          name: sonarqube-token
          key: token
    - name: PROJECT_KEY
      value: "k8s-security-scan"
    - name: PROJECT_NAME
      value: "Kubernetes Security Scan"
    - name: BUILD_NUMBER
      value: "1.0"
    workingDir: /workspace
    volumeMounts:
    - name: workspace
      mountPath: /workspace
    - name: scanner-config
      mountPath: /opt/sonar-scanner/conf
      readOnly: true
    - name: tmp
      mountPath: /tmp
    resources:
      limits:
        memory: "2Gi"
        cpu: "1000m"
      requests:
        memory: "1Gi"
        cpu: "500m"
  volumes:
  - name: workspace
    emptyDir: {}
  - name: scanner-config
    configMap:
      name: sonarqube-scanner-config
  - name: tmp
    emptyDir: {}
  restartPolicy: Never

Create SonarQube authentication secret

Store your SonarQube authentication token securely as a Kubernetes secret.

kubectl create secret generic sonarqube-token \
  --from-literal=token="your-sonarqube-token-here" \
  --namespace=sonarqube-security
Note: Replace "your-sonarqube-token-here" with your actual SonarQube token. You can generate this token in your SonarQube instance under User > My Account > Security.

Deploy admission controller webhook

Set up a validating admission controller that triggers SonarQube scans before allowing deployments.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonarqube-admission-controller
  namespace: sonarqube-security
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sonarqube-admission-controller
  template:
    metadata:
      labels:
        app: sonarqube-admission-controller
    spec:
      serviceAccountName: sonarqube-scanner
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: admission-controller
        image: admission-controller:latest
        ports:
        - containerPort: 8443
          name: webhook
        env:
        - name: TLS_CERT_FILE
          value: "/etc/certs/tls.crt"
        - name: TLS_KEY_FILE
          value: "/etc/certs/tls.key"
        - name: SONAR_URL
          value: "https://sonarqube.example.com"
        - name: SONAR_TOKEN
          valueFrom:
            secretKeyRef:
              name: sonarqube-token
              key: token
        volumeMounts:
        - name: certs
          mountPath: "/etc/certs"
          readOnly: true
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"
      volumes:
      - name: certs
        secret:
          secretName: admission-controller-certs
---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-admission-service
  namespace: sonarqube-security
spec:
  selector:
    app: sonarqube-admission-controller
  ports:
  - port: 443
    targetPort: webhook
    protocol: TCP
    name: webhook

Configure validating admission webhook

Create the admission webhook configuration that intercepts deployment requests and triggers security scans.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionWebhook
metadata:
  name: sonarqube-security-validator
webhooks:
  • name: security-scan.sonarqube.example.com
clientConfig: service: name: sonarqube-admission-service namespace: sonarqube-security path: "/validate" caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t... rules: - operations: ["CREATE", "UPDATE"] apiGroups: ["apps"] apiVersions: ["v1"] resources: ["deployments"] - operations: ["CREATE", "UPDATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"] namespaceSelector: matchLabels: sonarqube-scan: "enabled" admissionReviewVersions: ["v1", "v1beta1"] sideEffects: None failurePolicy: Fail timeoutSeconds: 30
kubectl apply -f validating-webhook.yaml

Create CI/CD integration CronJob

Set up automated scanning with a CronJob that triggers regular security scans and integrates with your CI/CD pipeline.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: sonarqube-scheduled-scan
  namespace: sonarqube-security
spec:
  schedule: "0 2   *"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: sonarqube-scanner
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            fsGroup: 1000
          initContainers:
          - name: git-clone
            image: alpine/git:2.40.1
            securityContext:
              runAsNonRoot: true
              runAsUser: 1000
              allowPrivilegeEscalation: false
              readOnlyRootFilesystem: true
              capabilities:
                drop:
                - ALL
            command: ['/bin/sh']
            args:
            - -c
            - |
              git clone ${GIT_REPO_URL} /workspace
              cd /workspace
              git checkout ${GIT_BRANCH:-main}
            env:
            - name: GIT_REPO_URL
              value: "https://github.com/example/app.git"
            - name: GIT_BRANCH
              value: "main"
            volumeMounts:
            - name: workspace
              mountPath: /workspace
            - name: tmp
              mountPath: /tmp
          containers:
          - name: security-scanner
            image: sonarsource/sonar-scanner-cli:5.0.1
            securityContext:
              runAsNonRoot: true
              runAsUser: 1000
              allowPrivilegeEscalation: false
              readOnlyRootFilesystem: true
              capabilities:
                drop:
                - ALL
            env:
            - name: SONAR_TOKEN
              valueFrom:
                secretKeyRef:
                  name: sonarqube-token
                  key: token
            - name: PROJECT_KEY
              value: "scheduled-security-scan"
            workingDir: /workspace
            command: ['/bin/sh']
            args:
            - -c
            - |
              sonar-scanner \
                -Dsonar.projectKey=${PROJECT_KEY} \
                -Dsonar.sources=. \
                -Dsonar.host.url=https://sonarqube.example.com \
                -Dsonar.login=${SONAR_TOKEN} \
                -Dsonar.qualitygate.wait=true
              
              # Send results to webhook
              curl -X POST https://webhook.example.com/sonar-results \
                -H "Content-Type: application/json" \
                -d "{\"project\": \"${PROJECT_KEY}\", \"status\": \"completed\"}"
            volumeMounts:
            - name: workspace
              mountPath: /workspace
            - name: tmp
              mountPath: /tmp
            resources:
              limits:
                memory: "2Gi"
                cpu: "1000m"
              requests:
                memory: "1Gi"
                cpu: "500m"
          volumes:
          - name: workspace
            emptyDir: {}
          - name: tmp
            emptyDir: {}
          restartPolicy: OnFailure
kubectl apply -f cicd-cronjob.yaml

Deploy security reporting service

Create a service that collects SonarQube scan results and generates security reports with alerting capabilities.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonarqube-reporting
  namespace: sonarqube-security
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube-reporting
  template:
    metadata:
      labels:
        app: sonarqube-reporting
    spec:
      serviceAccountName: sonarqube-scanner
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: reporting
        image: python:3.11-alpine
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        env:
        - name: SONAR_URL
          value: "https://sonarqube.example.com"
        - name: SONAR_TOKEN
          valueFrom:
            secretKeyRef:
              name: sonarqube-token
              key: token
        - name: SLACK_WEBHOOK
          valueFrom:
            secretKeyRef:
              name: notification-secrets
              key: slack-webhook
        command: ['/bin/sh']
        args:
        - -c
        - |
          pip install --user requests
          python3 -c "
          import requests
          import json
          import os
          import time
          
          def check_quality_gates():
              headers = {'Authorization': f'Bearer {os.environ["SONAR_TOKEN"]}'}
              response = requests.get(f'{os.environ["SONAR_URL"]}/api/qualitygates/project_status', headers=headers)
              return response.json()
          
          def send_alert(message):
              webhook_url = os.environ.get('SLACK_WEBHOOK')
              if webhook_url:
                  requests.post(webhook_url, json={'text': message})
          
          while True:
              try:
                  status = check_quality_gates()
                  if status.get('projectStatus', {}).get('status') == 'ERROR':
                      send_alert(f'🚨 Security scan failed for project: {status.get("projectStatus", {}).get("projectKey")}' )
                  time.sleep(300)  # Check every 5 minutes
              except Exception as e:
                  print(f'Error: {e}')
                  time.sleep(60)
          "
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        resources:
          limits:
            memory: "256Mi"
            cpu: "200m"
          requests:
            memory: "128Mi"
            cpu: "100m"
      volumes:
      - name: tmp
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-reporting-service
  namespace: sonarqube-security
spec:
  selector:
    app: sonarqube-reporting
  ports:
  - port: 8080
    targetPort: 8080
kubectl apply -f reporting-service.yaml

Enable namespace for security scanning

Label target namespaces to enable automatic security scanning for deployments.

kubectl label namespace default sonarqube-scan=enabled
kubectl label namespace production sonarqube-scan=enabled

Create notification secrets

Configure notification endpoints for security alerts and scan results.

kubectl create secret generic notification-secrets \
  --from-literal=slack-webhook="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK" \
  --from-literal=email-smtp="smtp.example.com:587" \
  --namespace=sonarqube-security

Verify your setup

Test the SonarQube Kubernetes integration by running scans and checking admission controller functionality.

# Check all components are running
kubectl get pods -n sonarqube-security
kubectl get validatingadmissionwebhooks

Test manual scan

kubectl apply -f scanner-pod-template.yaml kubectl logs -f sonarqube-scanner-pod -n sonarqube-security

Verify admission controller

kubectl get events -n sonarqube-security --sort-by='.lastTimestamp'

Check CronJob status

kubectl get cronjobs -n sonarqube-security kubectl get jobs -n sonarqube-security

Common issues

SymptomCauseFix
Scanner pod fails with permission deniedIncorrect security context or RBACkubectl describe pod -n sonarqube-security and check RBAC rules
Admission controller webhook timeoutService not responding or TLS issuesCheck service endpoints: kubectl get endpoints -n sonarqube-security
SonarQube authentication failsInvalid or expired tokenRegenerate token in SonarQube and update secret
Quality gate check failsNetwork connectivity or API issuesTest connection: kubectl exec -it pod-name -- curl https://sonarqube.example.com/api/system/status
CronJob not triggering scansSchedule syntax or resource limitsCheck CronJob logs: kubectl describe cronjob sonarqube-scheduled-scan -n sonarqube-security

Next steps

Automated install script

Run this to automate the entire setup

#sonarqube #kubernetes #security-scanning #admission-controllers #cicd

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer