Pipeline Security
Pendahuluan
Pipeline CI/CD adalah tulang punggung pengembangan perangkat lunak modern. Namun, pipeline yang tidak aman dapat menjadi vektor serangan yang sangat berbahaya - attacker yang berhasil mengompromikan pipeline dapat menyuntikkan kode berbahaya ke dalam artifact build, mencuri kredensial production, atau merusak integritas rantai pasok perangkat lunak.
Artikel ini membahas prinsip-prinsip secure pipeline, jenis-jenis serangan yang umum terjadi pada pipeline, mekanisme artifact signing, kerangka kerja SLSA (Supply-chain Levels for Software Artifacts), dan SBOM attestation. Setiap konsep dilengkapi dengan contoh implementasi konkret.
Prinsip Secure Pipeline
1. Least Privilege
Setiap komponen pipeline - job, step, action, runner - harus memiliki akses seminimal mungkin. Prinsip ini diterapkan di semua lapisan:
- Identity level: Setiap job hanya memiliki permission yang diperlukan.
- Network level: Runner tidak boleh memiliki akses ke jaringan internal yang tidak diperlukan.
- Credential level: Setiap secret hanya diekspos ke job yang benar-benar membutuhkan.
2. Isolation
Setiap eksekusi pipeline harus dijalankan di lingkungan yang terisolasi dan ephemeral. Ini mencegah kontaminasi data antar eksekusi.
Contoh isolasi dengan container:
# GitHub Actions - setiap job di runner VM baru
jobs:
build:
runs-on: ubuntu-latest
container:
image: node:18-alpine
steps:
- run: npm ci
- run: npm run build
Contoh isolasi dengan Kubernetes (Tekton):
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
generateName: build-task-
spec:
taskSpec:
steps:
- name: build
image: golang:1.21
script: |
go build -o app .
3. Immutability
Artifact yang dihasilkan pipeline tidak boleh dapat dimodifikasi setelah dibuat. Ini memastikan bahwa artifact yang diuji adalah artifact yang sama dengan yang di-deploy.
Praktik immutability:
- Gunakan content-addressable storage untuk artifact.
- Hash artifact dan simpan hash-nya.
- Signature artifact sebelum disimpan.
4. Traceability
Semua tindakan dalam pipeline harus tercatat dan dapat diaudit. Setiap perubahan pada pipeline harus melalui code review.
# Pipeline disimpan sebagai kode - dapat di-review dan diaudit
# .github/workflows/deploy.yml atau .gitlab-ci.yml
Pipeline Attacks
1. Dependency Confusion
Serangan dependency confusion terjadi ketika attacker mempublikasikan package dengan nama yang sama dengan package internal perusahaan di public registry. Pipeline yang dikonfigurasi untuk mengambil package dari public registry secara default akan mengambil package berbahaya tersebut.
Contoh dependency confusion:
Seorang developer menggunakan package internal
@acme/internal-lib di package.json. Jika
ada yang mempublikasikan @acme/internal-lib ke npm
registry publik, pipeline akan mengunduh versi berbahaya tersebut.
Mitigasi:
# npm - pastikan registry internal diprioritaskan
# .npmrc
registry=https://npm.internal.acme.com/
always-auth=true
# pip - gunakan index-url yang aman
# requirements.txt dengan --index-url
pip install --index-url https://pypi.internal.acme.com/simple/ \
--extra-index-url https://pypi.org/simple/ \
-r requirements.txt
Mitigasi di GitHub Actions:
# Gunakan scope registry yang spesifik
- name: Install dependencies
run: |
npm config set @acme:registry https://npm.internal.acme.com/
npm ci
2. Cache Poisoning
Cache poisoning terjadi ketika attacker menyuntikkan data berbahaya ke dalam cache pipeline. Karena cache biasanya tidak melalui security scanning ulang, data berbahaya dapat digunakan tanpa terdeteksi.
Mitigasi cache poisoning:
# GitHub Actions - batasi cache key
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
# Gunakan hash dari lock file - bukan nama branch
# Jangan gunakan github.ref di cache key
restore-keys: |
${{ runner.os }}-npm-
Contoh kunci cache yang tidak aman - jangan gunakan ini:
# BERBAHAYA - cache key berbasis branch memudahkan poisoning
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ github.ref }}
3. Credential Theft
Credentials dalam pipeline adalah target paling berharga bagi attacker. Metode pencurian meliputi:
- Log exposure: Secret tercetak di log karena kesalahan konfigurasi.
- Artifact extraction: Credentials disimpan di artifact build.
- Environment variable dump: Semua environment variable diekspos oleh malicious action.
Mitigasi credential theft:
# 1. Gunakan secret yang spesifik per environment
# 2. Jangan pernah menampilkan secret di log
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
# Jangan lakukan ini:
# echo "API Key: $API_KEY"
# echo "API_KEY=$API_KEY" >> .env
# Lakukan ini:
./deploy.sh
Praktik tambahan:
# Gunakan short-lived tokens via OIDC
jobs:
deploy:
permissions:
id-token: write
steps:
- name: Get OIDC token
run: |
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=aws" \
> /tmp/oidc-token
4. Pipeline Execution Hijacking
Attacker dapat mengubah konfigurasi pipeline untuk mengeksekusi kode berbahaya. Ini bisa terjadi melalui:
- Pull request dari fork yang memodifikasi workflow.
- Compromised maintainer account.
- Dependency yang berbahaya di script pipeline.
Mitigasi:
# GitHub Actions - batasi workflow untuk fork PR
on:
pull_request_target:
types: [labeled]
branches: [main]
# Jangan pernah menggunakan pull_request_target untuk
# checkout kode dari fork pengguna
# Batasi siapa yang bisa mengubah workflow
# Settings > Actions > General > Fork pull request workflows
# Pilih: "Require approval for all outside collaborators"
Artifact Signing dengan Cosign dan Sigstore
Artifact signing adalah proses menandatangani artifact digital untuk memverifikasi keaslian dan integritasnya. Cosign adalah tool signing untuk container images yang merupakan bagian dari proyek Sigstore.
Instalasi Cosign
# Instal Cosign
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
Menandatangani Container Image
# Generate key pair
cosign generate-key-pair
# Sign container image
cosign sign --key cosign.key ghcr.io/organisasi/app:latest
Verifikasi Signature
# Verify signature
cosign verify --key cosign.pub ghcr.io/organisasi/app:latest
Keyless Signing dengan Sigstore
Sigstore memungkinkan keyless signing menggunakan OIDC identity - tidak perlu mengelola key pair.
# Keyless signing - menggunakan identitas GitHub/GitLab
cosign sign ghcr.io/organisasi/app:latest
# Keyless verification
cosign verify ghcr.io/organisasi/app:latest \
--certificate-identity-regexp "https://github.com/organisasi/" \
--certificate-oidc-issuer "https://oauth2.sigstore.dev/auth"
Integrasi Signing di Pipeline
# GitHub Actions - signing keyless
- name: Sign container image
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign \
--yes \
ghcr.io/organisasi/app:${{ github.sha }}
# GitLab CI - signing dengan key
sign-image:
stage: sign
image: alpine:latest
before_script:
- apk add --no-cache cosign
script:
- cosign sign --key $COSIGN_PRIVATE_KEY $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
only:
- main
SLSA Framework
SLSA (Supply-chain Levels for Software Artifacts) adalah kerangka kerja keamanan rantai pasok yang mendefinisikan level kematangan dari L0 hingga L4. Setiap level membutuhkan kontrol keamanan yang lebih ketat.
Level SLSA
| Level | Deskripsi | Persyaratan Utama |
|---|---|---|
| L0 | Tidak ada jaminan | Dokumentasi ad-hoc |
| L1 | Build process terdokumentasi | Build script, provenance |
| L2 | Build dari source | |
| Host isolation | ||
| Provenance diproduksi oleh build service, signed provenance | ||
| L3 | Hardened build | |
| Increased resistance | ||
| Non-falsifiable provenance, hermetic build | ||
| L4 | Maximum security | Two-person review, reproducible build |
Implementasi SLSA Level 3
SLSA Level 3 membutuhkan:
- Hermetic build: Build tidak boleh bergantung pada sumber daya eksternal yang tidak terkontrol.
- Build isolation: Setiap build berjalan di lingkungan yang terisolasi.
- Non-falsifiable provenance: Provenance harus ditandatangani dan tidak dapat dipalsukan.
- No external network access: Build tidak boleh mengakses jaringan eksternal (kecuali yang sudah ditentukan).
Contoh pipeline SLSA L3:
# GitHub Actions - pendekatan SLSA Level 3
name: SLSA L3 Pipeline
on:
push:
branches: [main]
permissions:
contents: read
id-token: write
packages: write
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
# Build dalam container terisolasi
- name: Build
run: |
docker build \
--no-cache \
--network=none \
-t app:${{ github.sha }} .
# Generate provenance dengan SLSA generator
- name: Generate provenance
with:
image: app:${{ github.sha }}
digest: ${{ steps.build.outputs.digest }}
# Push image
- name: Push
run: |
docker tag app:${{ github.sha }} ghcr.io/org/app:${{ github.sha }}
docker push ghcr.io/org/app:${{ github.sha }}
verify:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Verify provenance
- name: Verify artifact
run: |
slsa-verifier verify-artifact \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/org/repo \
--source-tag main \
app.tar.gz
Provenance Attestation
Provenance adalah metadata yang mendokumentasikan bagaimana sebuah artifact dibuat - termasuk source repository, commit SHA, build command, dan build parameters.
Contoh provenance dalam format in-toto:
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "app:abc123",
"digest": {
"sha256": "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15"
}
}
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"builder": {
"id": "https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main"
},
"buildType": "https://github.com/actions/build@v1",
"invocation": {
"configSource": {
"uri": "git+https://github.com/org/repo@refs/heads/main",
"digest": {
"sha1": "abc123def456"
},
"entryPoint": ".github/workflows/build.yml"
}
},
"materials": [
{
"uri": "git+https://github.com/org/repo@refs/heads/main",
"digest": {
"sha1": "abc123def456"
}
}
]
}
}
SBOM Attestation
SBOM (Software Bill of Materials) adalah daftar semua komponen yang membentuk sebuah perangkat lunak. Attestation memastikan bahwa SBOM diterbitkan oleh pihak yang berwenang dan tidak dimodifikasi.
Membuat SBOM
# Menggunakan Syft untuk membuat SBOM
syft ghcr.io/organisasi/app:latest -o spdx-json > sbom.spdx.json
# Atau menggunakan Trivy
trivy image --format spdx-json --output sbom.spdx.json ghcr.io/organisasi/app:latest
Menandatangani SBOM dengan Cosign
# Sign SBOM
cosign attest \
--predicate sbom.spdx.json \
--type spdx \
ghcr.io/organisasi/app:latest
Verifikasi SBOM Attestation
# Verify attestation
cosign verify-attestation \
--type spdx \
--certificate-identity-regexp "https://github.com/organisasi/" \
ghcr.io/organisasi/app:latest
Integrasi SBOM di Pipeline
# GitHub Actions - generate dan sign SBOM
jobs:
sbom:
runs-on: ubuntu-latest
permissions:
id-token: write
packages: read
steps:
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ghcr.io/org/app:${{ github.sha }}
format: spdx-json
- name: Sign SBOM
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign attest \
--predicate sbom.spdx.json \
--type spdx \
ghcr.io/org/app:${{ github.sha }}
Contoh Pipeline SLSA Level 3 Lengkap
Berikut adalah contoh pipeline implementasi SLSA Level 3 dengan artifact signed dan SBOM attested:
name: SLSA L3 - Signed Build
on:
push:
branches: [main]
permissions:
contents: read
jobs:
build-and-sign:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
# Build hermetic - tanpa akses jaringan
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
# Sign image dengan Cosign keyless
- name: Sign image
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign \
--yes \
ghcr.io/${{ github.repository }}:${{ github.sha }}
# Generate SLSA provenance
- name: Generate provenance
id: provenance
with:
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
digest: ${{ steps.build.outputs.digest }}
verify:
needs: [build-and-sign]
runs-on: ubuntu-latest
steps:
- name: Verify signature
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign verify \
--certificate-identity-regexp "https://github.com/${{ github.repository }}/.github/workflows/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Verify attestation
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign verify-attestation \
--type spdx \
--certificate-identity-regexp "https://github.com/${{ github.repository }}/.github/workflows/" \
ghcr.io/${{ github.repository }}:${{ github.sha }}
deploy:
needs: [build-and-sign, verify]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Deploy signed artifact
run: |
echo "Deploying signed image: ghcr.io/${{ github.repository }}:${{ github.sha }}"
# Deployment dengan verifikasi signature di sisi target
Kesimpulan
Keamanan pipeline CI/CD adalah fondasi dari DevSecOps modern. Prinsip least privilege, isolasi, immutability, dan traceability harus diterapkan di setiap pipeline. Ancaman seperti dependency confusion, cache poisoning, dan credential theft dapat dimitigasi dengan konfigurasi yang tepat. Penggunaan artifact signing dengan Cosign/Sigstore dan implementasi SLSA framework memberikan jaminan integritas rantai pasok perangkat lunak. SBOM attestation melengkapi keamanan dengan menyediakan inventori komponen yang terverifikasi.