CI/CD tanpa access key
Access key di dalam secret repositori adalah kredensial abadi yang bisa dipakai siapa pun dengan akses tulis ke pipeline. OIDC menggantinya dengan token berumur menit yang hanya berlaku untuk repositori dan cabang tertentu.
Intisari
- GitHub menerbitkan token OIDC; AWS menukarnya dengan kredensial sementara.
- Tidak ada access key yang perlu disimpan, dirotasi, atau bisa bocor.
- Trust policy harus membatasi
subke repositori dan cabang spesifik. - Pipeline: lint โ analisis statis โ tes โ build image โ migrasi โ deploy.
- Migrasi dijalankan sebagai task tersendiri, sebelum service diperbarui.
Menyiapkan penyedia OIDC
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012: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:organisasi/portal:ref:refs/heads/main"
}
}
}]
}
Baris sub itu adalah seluruh keamanannya. Menulis
repo:organisasi/* berarti setiap repositori di organisasimu bisa mengambil role ini โ
termasuk repositori yang dibuat siapa pun besok. Menulis repo:organisasi/portal:* berarti setiap
cabang, termasuk cabang dari pull request pihak luar. Sebutkan repositori dan cabang, atau
lebih baik lagi, environment GitHub yang butuh persetujuan manual.
Alur GitHub Actions
name: deploy
on:
push:
branches: [main]
permissions:
id-token: write # WAJIB untuk OIDC
contents: read
jobs:
periksa:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.4', coverage: none }
- run: composer install --prefer-dist --no-interaction
- run: ./vendor/bin/pint --test
- run: ./vendor/bin/phpstan analyse --error-format=github --no-progress
- run: php artisan test --parallel
deploy:
needs: periksa
runs-on: ubuntu-latest
environment: production # butuh persetujuan manual
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/portal-deploy
aws-region: ap-southeast-1
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- name: Bangun dan dorong image
env:
REPO: ${{ steps.ecr.outputs.registry }}/portal
SHA: ${{ github.sha }}
run: |
docker buildx build --platform linux/amd64 \
--cache-from type=gha --cache-to type=gha,mode=max \
-t "$REPO:$SHA" --push .
- name: Jalankan migrasi
run: |
TASK=$(aws ecs run-task \
--cluster portal --task-definition portal-migrasi \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG]}" \
--query 'tasks[0].taskArn' --output text)
aws ecs wait tasks-stopped --cluster portal --tasks "$TASK"
KODE=$(aws ecs describe-tasks --cluster portal --tasks "$TASK" \
--query 'tasks[0].containers[0].exitCode' --output text)
test "$KODE" = "0"
- name: Perbarui service
run: |
aws ecs update-service --cluster portal --service portal-web \
--task-definition portal-web --force-new-deployment
aws ecs update-service --cluster portal --service portal-worker \
--task-definition portal-worker --force-new-deployment
- name: Tunggu sampai stabil
run: |
aws ecs wait services-stable --cluster portal \
--services portal-web portal-worker
Tiga baris yang paling sering terlewat. Pertama, memeriksa exit code task migrasi โ tanpa
itu, migrasi yang gagal tetap diikuti deploy, dan kodemu berjalan di atas skema yang salah. Kedua,
aws ecs wait services-stable โ tanpa itu, pipeline berwarna hijau padahal task baru sedang gagal
menyala berulang kali. Ketiga, memperbarui service worker juga; kalau tidak, worker lama
memproses job baru dengan kode lama.
Izin untuk role deploy
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "*" },
{ "Effect": "Allow",
"Action": ["ecr:BatchCheckLayerAvailability", "ecr:PutImage",
"ecr:InitiateLayerUpload", "ecr:UploadLayerPart",
"ecr:CompleteLayerUpload", "ecr:BatchGetImage"],
"Resource": "arn:aws:ecr:ap-southeast-1:123456789012:repository/portal" },
{ "Effect": "Allow",
"Action": ["ecs:UpdateService", "ecs:DescribeServices",
"ecs:RunTask", "ecs:DescribeTasks",
"ecs:RegisterTaskDefinition", "ecs:DescribeTaskDefinition"],
"Resource": "*" },
{ "Effect": "Allow",
"Action": "iam:PassRole",
"Resource": ["arn:aws:iam::123456789012:role/portal-execution",
"arn:aws:iam::123456789012:role/portal-task"] }
]
}
iam:PassRole harus dibatasi ke role yang spesifik. Dengan "Resource": "*",
siapa pun yang bisa mengubah pipeline bisa menjalankan task ECS memakai role apa pun di akunmu โ
termasuk yang punya akses administratif. Ini eskalasi hak akses yang sering lolos dari review karena
terlihat seperti detail teknis.
Urutan pipeline yang benar
- Lint dan analisis statis โ paling cepat, gagalkan lebih dulu.
- Tes dengan database yang sama jenisnya dengan produksi.
- Build image, beri tag SHA commit.
- Pindai kerentanan pada image; gagalkan kalau ada temuan kritis.
- Migrasi sebagai task tersendiri; periksa exit code-nya.
- Perbarui service โ web, worker, dan penjadwal.
- Tunggu stabil, lalu jalankan pemeriksaan asap terhadap URL produksi.
Kenapa migrasi sebelum deploy, bukan sesudah. Selama rolling update, kode lama dan kode baru berjalan bersamaan. Migrasi yang dijalankan lebih dulu berarti kode baru selalu menemukan skema yang dibutuhkannya โ dan kode lama tetap bekerja, asalkan migrasinya bersifat aditif. Inilah alasan pola expand & contract dari Fase 2 bukan teori: ia adalah syarat supaya urutan ini aman.
Latihan: siapkan penyedia OIDC dan role deploy dengan sub yang dibatasi ke satu
repositori dan cabang main. Jalankan pipeline dari cabang lain dan pastikan ia
ditolak โ itu bukti pembatasannya benar-benar bekerja.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.