jan-karel.nl
Home / Securitymaatregelen / Cloudbeveiliging / CI/CD Pipeline Hardening

CI/CD Pipeline Hardening

CI/CD Pipeline Hardening

Guardrails Voor Elke Deploy

In de cloud is consistentie cruciaal: policy in code, minimale rechten en zicht op drift.

Voor CI/CD Pipeline Hardening is automatisering leidend: guardrails in code, least privilege en continue driftcontrole.

Zo houd je snelheid in de cloud, zonder dat veiligheid afhankelijk wordt van handmatig geluk.

Directe maatregelen (15 minuten)

Waarom dit telt

De kern van CI/CD Pipeline Hardening is risicoreductie in de praktijk. Technische context ondersteunt de maatregelkeuze, maar implementatie en borging staan centraal.

Verdedigingsmaatregelen

SLSA Framework

Supply-chain Levels for Software Artifacts (SLSA, uitgesproken als “salsa”) is een framework van Google dat de integriteit van de software supply chain adresseert:

SLSA Niveau Vereisten Wat het beschermt tegen
Level 0 Niets Niets
Level 1 Build proces is gedocumenteerd, provenance gegenereerd Onbekende build herkomst
Level 2 Hosted build service, authenticated provenance Gemanipuleerde build omgeving
Level 3 Hardened build platform, non-falsifiable provenance Gecompromitteerde build service
Level 4 Two-party review, hermetic builds Insider threats
# Voorbeeld: SLSA provenance genereren in GitHub Actions
- uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
  with:
    base64-subjects: "${{ needs.build.outputs.hashes }}"

Sigstore

Sigstore is een ecosysteem voor het ondertekenen en verifieren van software-artefacten:

# Cosign: container image signing
# Teken een image
cosign sign --key cosign.key REGISTRY/IMAGE:TAG

# Verifieer een image
cosign verify --key cosign.pub REGISTRY/IMAGE:TAG

# Keyless signing met OIDC (geen sleutelbeheer nodig)
cosign sign REGISTRY/IMAGE:TAG
# Authenticeert via je OIDC identity (GitHub, Google, etc.)

# In Kubernetes: Kyverno policy om alleen gesignde images toe te staan
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  rules:
  - name: verify-cosign
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "registry.company.com/*"
      attestors:
      - entries:
        - keyless:
            subject: "https://github.com/company/*"
            issuer: "https://token.actions.githubusercontent.com"

Ephemeral runners

# GitHub Actions: gebruik altijd GitHub-hosted runners voor publieke repos
runs-on: ubuntu-latest  # Ephemeral, schone omgeving per job

# Self-hosted runners: configureer als ephemeral
# ./config.sh --ephemeral
# Runner wordt na elke job automatisch de-registered en opnieuw aangemaakt

# Docker-based ephemeral runners
# Elke job draait in een verse container
# Geen cross-job contaminatie mogelijk

Least privilege tokens

# GitHub Actions: minimale GITHUB_TOKEN permissies
permissions:
  contents: read
  packages: write
  # Alleen wat nodig is, niets meer

# GitLab CI: gebruik scoped variables
variables:
  DEPLOY_TOKEN:
    value: $CI_DEPLOY_TOKEN
    # protected: alleen beschikbaar op protected branches
    # masked: verborgen in build logs

# Jenkins: gebruik credential scoping
// Jenkinsfile
withCredentials([
    usernamePassword(
        credentialsId: 'deploy-staging',  // Specifiek per omgeving
        usernameVariable: 'USER',
        passwordVariable: 'PASS'
    )
]) {
    // Credentials alleen beschikbaar in dit blok
    sh 'deploy.sh'
}

OIDC Federation

# AWS OIDC met GitHub Actions (geen langlevende credentials)
# Stap 1: Configureer de OIDC provider in AWS
# Stap 2: Maak een IAM role met trust policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:ORG/REPO:ref:refs/heads/main"
                }
            }
        }
    ]
}

# Stap 3: Gebruik in de workflow
# - uses: aws-actions/configure-aws-credentials@v4
#   with:
#     role-to-assume: arn:aws:iam::ACCOUNT:role/GitHubActionsRole
#     aws-region: eu-west-1
# Geen AWS_ACCESS_KEY_ID, geen AWS_SECRET_ACCESS_KEY
# Alleen een kortlevend session token
# GCP Workload Identity Federation met GitHub Actions
# Vergelijkbaar principe: OIDC token → GCP service account
- uses: google-github-actions/auth@v2
  with:
    workload_identity_provider: 'projects/PROJECT_NUM/locations/global/workloadIdentityPools/github/providers/github-provider'
    service_account: 'github-actions@PROJECT.iam.gserviceaccount.com'

Tip: OIDC federation is de beste manier om langlevende credentials uit CI/CD pipelines te elimineren. Maar let op de condition in de trust policy. Als de sub claim te breed is (bijv. repo:ORG/* in plaats van repo:ORG/REPO:ref:refs/heads/main), kan elke repository in de organisatie de rol aannemen. Controleer altijd de trust policy conditions.

Jenkins Hardening

Jenkins is de oudste en meest wijdverbreide CI/CD-server, maar ook de meest geconfigureerde-door-iemand-die-er-eigenlijk-geen-tijd-voor-had. De standaardinstellingen zijn ronduit gevaarlijk.

Script Console beveiligen

De Groovy Script Console (/script) geeft volledige RCE op de Jenkins-server. Dit is de eerste deur die aanvallers proberen.

Maatregel Implementatie
Script Console beperken Alleen admin-gebruikers via Matrix Authorization Strategy
CLI uitschakelen jenkins.CLI.disabled=true in jenkins.model.JenkinsLocationConfiguration.xml
Agent-to-controller beperken Manage Jenkins → Security → Agent → Controller Access Control
CSRF Protection Standaard ingeschakeld sinds Jenkins 2.0, verifieer dat het niet is uitgeschakeld

Credential Management

// Jenkinsfile: credentials scoped per folder, niet globaal
withCredentials([
    usernamePassword(
        credentialsId: 'deploy-prod',  // Folder-scoped credential
        usernameVariable: 'DEPLOY_USER',
        passwordVariable: 'DEPLOY_PASS'
    )
]) {
    // Credentials alleen beschikbaar in dit blok
    sh 'deploy.sh --user $DEPLOY_USER --pass $DEPLOY_PASS'
}
// Buiten het blok zijn de variabelen niet meer beschikbaar
Maatregel Implementatie
Folder-scoped credentials Credentials per project-folder, niet globaal
Credential rotation Automatische rotatie via externe secret manager (HashiCorp Vault, AWS Secrets Manager)
Audit trail Credentials Usage plugin voor wie welke credential gebruikt
Geen plaintext secrets Gebruik altijd het Credentials plugin, nooit environment variables in de UI

Plugin Security

Maatregel Implementatie
Minimaliseer plugins Verwijder alle plugins die niet actief gebruikt worden
Update policy Wekelijks plugins updaten, security advisories monitoren
Geen onbekende plugins Alleen plugins van de officiële Jenkins Update Center
Plugin audit Manage Jenkins → Manage Plugins → Installed regelmatig reviewen

RBAC en Isolation

// Pipeline als code: beperk wat pipelines mogen
// Configureer in Manage Jenkins → Configure Global Security
// → Authorization → Project-based Matrix Authorization Strategy

// Voorbeeld: Sandbox voor untrusted pipelines
properties([
    // Beperk welke agents deze pipeline mag gebruiken
    pipelineTriggers([]),
    disableConcurrentBuilds()
])
Maatregel Implementatie
RBAC plugin Role-Based Authorization Strategy plugin
Per-project permissions Lees/schrijf/execute per project toewijzen
Ephemeral agents Jenkins agents als containers die na elke build worden vernietigd
Shared library review Alle shared libraries onder versiebeheer met code review

Referentietabel

Techniek Categorie MITRE ATT&CK Platform Complexiteit
Expression injection Code Execution T1059.004 - Unix Shell GitHub Actions Middel
Self-hosted runner abuse Initial Access T1195.002 - Software Supply Chain GitHub/GitLab Middel
GITHUB_TOKEN abuse Credential Access T1528 - Steal Application Access Token GitHub Actions Laag
pull_request_target exploit Credential Access T1528 GitHub Actions Middel
Secret exfiltration via artifacts Credential Access T1552.001 - Credentials In Files Alle platforms Laag
Shared runner exploitation Lateral Movement T1021 - Remote Services GitLab CI Middel
CI/CD variable extraction Credential Access T1552.001 GitLab CI Laag
Protected branch bypass Defense Evasion T1562.001 - Disable or Modify Tools Alle platforms Middel
Include directive abuse Execution T1059.004 GitLab CI Middel
Jenkins Script Console RCE Execution T1059.007 - JavaScript Jenkins Laag
Groovy sandbox escape Execution T1059 - Command and Scripting Jenkins Hoog
Jenkins credential theft Credential Access T1555 - Credentials from Password Stores Jenkins Middel
Pipeline as code injection Execution T1195.002 Jenkins Middel
Environment variable leaks Credential Access T1552.001 Alle platforms Laag
Build log secrets Credential Access T1552.001 Alle platforms Laag
Terraform state secrets Credential Access T1552.001 Alle platforms Laag
Dependency confusion Initial Access T1195.001 - Software Dependencies npm/PyPI/NuGet Middel
Typosquatting Initial Access T1195.001 npm/PyPI/NuGet Laag
Compromised Actions/Orbs Execution T1195.002 GitHub/CircleCI Middel
Malicious packages Execution T1195.001 Alle package managers Middel
Build artifact tampering Persistence T1195.002 Alle platforms Middel
ArgoCD exploitation Initial Access T1190 - Exploit Public-Facing Application ArgoCD/K8s Middel
Helm chart injection Execution T1610 - Deploy Container Kubernetes/Helm Middel
values.yaml secret extraction Credential Access T1552.001 Kubernetes/Helm Laag
Status check manipulation Defense Evasion T1562.001 GitHub/GitLab Middel
Review circumvention Defense Evasion T1562.001 Alle platforms Middel
SLSA provenance forgery Defense Evasion T1195.002 Alle platforms Hoog

De software supply chain is het fundament waarop moderne software wordt gebouwd. Het is ook een fundament met scheuren die we pas beginnen te zien. De aanvallen in dit hoofdstuk – van dependency confusion tot pipeline injection – exploiteren niet zozeer technische kwetsbaarheden als wel het vertrouwen dat we in onze tools en processen stellen. En vertrouwen, zo leert de ervaring, is het eerste wat een aanvaller misbruikt.

In de volgende hoofdstukken verlaten we de bouwketen en kijken we naar de cloud-omgevingen waar deze software uiteindelijk draait: AWS, Azure en GCP. Andere omgevingen, dezelfde fouten.

Op de hoogte blijven?

Ontvang maandelijks cybersecurity-inzichten in je inbox.

← Cloudbeveiliging ← Home