TDCTF Academy Logo TDCTF ACADEMY

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:

  1. Hermetic build: Build tidak boleh bergantung pada sumber daya eksternal yang tidak terkontrol.
  2. Build isolation: Setiap build berjalan di lingkungan yang terisolasi.
  3. Non-falsifiable provenance: Provenance harus ditandatangani dan tidak dapat dipalsukan.
  4. 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
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
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
uses: slsa-framework/slsa-verifier/actions/[email protected]
- 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
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
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.

PADA HALAMAN INI