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 mogelijkLeast 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
subclaim te breed is (bijv.repo:ORG/*in plaats vanrepo: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.
Verder lezen in de kennisbank
Deze artikelen in het portaal geven je meer achtergrond en praktische context:
- De cloud — andermans computer, jouw verantwoordelijkheid
- Containers en Docker — wat het is en waarom je het moet beveiligen
- Encryptie — de kunst van het onleesbaar maken
- Least Privilege — geef mensen alleen wat ze nodig hebben
Je hebt een account nodig om de kennisbank te openen. Inloggen of registreren.
Gerelateerde securitymaatregelen
Deze artikelen bieden aanvullende context en verdieping: