
ZenVault: Automasi AWS Secrets di API Go Minimalis
Tutorial External Secrets Operator (ESO) + AWS Secrets Manager di k3d. Plus, lesson learned kenapa pindah dari GCP WIF ke AWS demi efisiensi infra.
Oke, jadi ceritanya gue lagi pengen eksplor sesuatu yang udah lama ada di wishlist: gimana caranya bikin API yang bisa ambil rahasia dari cloud (API key, password, dsb) secara otomatis, tanpa hardcode, dan tanpa nulis kode SDK cloud di dalam source code?
Hasilnya lahirlah ZenVault β sebuah "lab" yang menggabungkan:
- πΉ Go sebagai bahasa API, dikompilasi jadi binary statis
- π Docker dengan base image
scratch(literally image kosong, nggak ada OS-nya) - βΈοΈ k3d buat simulasi cluster Kubernetes di lokal
- π External Secrets Operator (ESO) sebagai "kurir" yang ngambil secret dari cloud ke Kubernetes
- βοΈ AWS Secrets Manager buat nyimpen rahasianya
- π cert-manager + ZeroSSL buat otomatisasi sertifikat HTTPS
Spoiler: perjalanannya nggak mulus. Ada drama GCP, ada WIF yang gagal, ada Org Policy yang kayak firewall manusia. Tapi endingnya manis. Let's dive in.
Awal Mula: PRD dan Rencana Besar
Sebelum nulis kode, gue bikin PRD (Product Requirements Document) dulu buat mastiin arahnya jelas. Intinya:
- Bikin API Go yang cuma baca env var
APP_DEBUG_KEY - Secret itu nggak boleh hardcode β harus ditarik otomatis dari cloud
- Image Docker harus ringan, target < 15MB
- TLS otomatis via ZeroSSL
Simplenya gini alurnya:
Cloud Secret β ESO β K8s Secret β Pod Env β API Response
Keliatan elegan. Tapi eksekusinya... mari kita bahas.
Fase 1: Bikin API-nya Dulu
Kode main.go intentionally super sederhana. Cuma dua endpoint:
// /healthz β buat ngecek masih hidup apa nggak
// /v1/debug β return APP_DEBUG_KEY dari environment
os.Getenv("APP_DEBUG_KEY")
Nggak ada import "github.com/aws/aws-sdk-go". Nggak ada credentials di kode. Beneran zero. Nanti yang ngurusin itu Kubernetes-nya.
Buat Docker image-nya, gue pakai multi-stage build:
- Stage 1: Compile Go binary pakai
golang:alpine - Stage 2: Copy binary ke
FROM scratchβ literally image kosong tanpa OS
Hasilnya? π 5.96MB. Di bawah target 15MB. Mantap.
Fase 2: Drama GCP β Org Policy yang Nge-block Segalanya
Rencananya modal awal pakai Google Secret Manager (GSM). Gue udah bikin ServiceAccount zenvault-eso, kasih role secretmanager.secretAccessor. Tinggal download JSON key-nya dan masukin ke Kubernetes.
Eh, ternyata...
ERROR: iam.disableServiceAccountKeyCreation: enforced: true
Org Policy di level Workspace Google nge-blokir pembuatan JSON key buat semua ServiceAccount di project. Bahkan Owner project nggak bisa bypass ini. Ini bukan bug, ini fitur keamanan yang memang disengaja β tapi buat lab kita, ini jadi tembok besar.
Usaha Pertama: Workload Identity Federation (WIF)
Gue nggak nyerah gitu aja. Solusi resminya untuk auth tanpa JSON key di GCP adalah Workload Identity Federation (WIF) β cara autentikasi "keyless" yang pakai OIDC token dari cluster Kubernetes.
Setup WIF itu lumayan kompleks:
- Bikin Workload Identity Pool di GCP
- Upload
openid-configurationdanjwks.jsondari cluster k3d ke GCS Bucket publik (buat jadi OIDC Discovery Endpoint) - Daftarin GCS Bucket URL itu sebagai OIDC Provider di WIF
- Bikin IAM binding antara Kubernetes ServiceAccount dan GCP ServiceAccount
- Re-create cluster k3d dengan flag
--service-account-issuerbaru
Semua berhasil di-setup. Provider terdaftar. IAM binding aktif. Cluster selesai dibuat ulang.
Tapi pas ESO dicoba connect ke GCP...
failed to lookup workload identity:
unable to get project id: Get "http://169.254.169.254/computeMetadata/v1/project/project-id":
dial tcp 169.254.169.254:80: connect: connection refused
π GCE Metadata Server. IP 169.254.169.254 itu IP khusus yang hanya ada di VM Google Cloud (GKE, Compute Engine). Di laptop kita yang pakai k3d? Nggak ada. Provider gcpsm di ESO ternyata hardcoded nyari IP itu kalau mode Workload Identity aktif.
Lesson learned ini yang paling mahal: WIF itu bagus dan aman, tapi dia "designed for cloud" bukan untuk lokal.
Pivot: Pindah ke AWS
Daripada stuck, kita pivot. AWS Secrets Manager jadi pilihan pengganti. Alasannya simpel: AWS pakai IAM User dengan Access Key + Secret Key yang bisa dipakai di mana saja β nggak perlu infrastruktur cloud khusus.
Setup AWS dalam 5 Langkah
1. Bikin secret di AWS:
aws secretsmanager create-secret \
--name "zenvault/app_debug_key" \
--secret-string "nilai-rahasianya" \
--region ap-southeast-1
2. Bikin IAM User + policy khusus (least privilege β cuma bisa baca dua secret itu):
aws iam create-user --user-name zenvault-eso
aws iam create-access-key --user-name zenvault-eso
3. Simpen ke Kubernetes Secret:
kubectl create secret generic aws-secret \
--from-literal=access-key="AKIAXXXXXXX" \
--from-literal=secret-access-key="xxxxxxxx" \
--namespace external-secrets
4. Deploy ClusterSecretStore pakai provider AWS:
spec:
provider:
aws:
service: SecretsManager
region: ap-southeast-1
auth:
secretRef:
accessKeyIDSecretRef:
name: aws-secret
key: access-key
5. Apply semua manifest:
kubectl apply -f manifests/
Hasilnya langsung:
clustersecretstore.external-secrets.io/aws-store Valid ReadWrite True β
Test endpoint:
curl -k https://127.0.0.1/v1/debug -H "Host: playlab.my.id"
> {"app":"zenvault","app_debug_key":"nilai-rahasianya"}
π Bekerja sempurna. Secret dari AWS muncul di response API. Tanpa satu baris kode AWS SDK.
Fase TLS: ZeroSSL dan cert-manager
Untuk TLS, kita pakai cert-manager dengan ACME provider ZeroSSL (alternatif Let's Encrypt yang lebih enterprise-friendly).
Trik yang perlu dikenali: ClusterIssuer hanya bisa baca secret di namespace cert-manager, bukan default. Jadi HMAC Key ZeroSSL (yang kita simpan via ESO) harus punya ExternalSecret tersendiri di namespace yang benar.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: zerossl-eab-secret
namespace: cert-manager # β kunci ada di sini
spec:
data:
- secretKey: ZEROSSL_EAB_HMAC
remoteRef:
key: zenvault/zerossl_eab_hmac
Setelah fix ini: ClusterIssuer langsung berstatus:
The ACME account was registered with the ACME server β
Tapi order sertifikat domain playlab.my.id? Masih pending. Karena buat complete HTTP-01 challenge, ZeroSSL perlu akses internet ke server kita β dan laptop di balik WiFi/NAT nggak bisa direach dari luar. Ini expected behavior di lokal, bukan bug kita.
GCP vs AWS: Head-to-Head
| Aspek | π΅ GCP | π AWS |
|---|---|---|
| Auth Method | Service Account JSON Key | IAM Access Key |
| Hambatan di Lokal | Org Policy blokir SA Key | Tidak ada |
| WIF Support | Teknis iya, tapi ESO-nya stuck di GCE | N/A |
| ESO Integrasi | gcpsm (tergantung GCE) |
aws (fleksibel) |
| Setup Speed | Lambat (troubleshooting WIF) | Cepat dan mulus |
| Rekomendasi | Production di GKE | Local dev + Multi-cloud |
Lessons Learned
1. Org Policy itu Real
Di dunia kerja nyata, banyak hal yang "secara teknis bisa" tapi diblokir oleh kebijakan perusahaan. Kemampuan beradaptasi β bukan ngotot β yang menentukan seberapa cepat kamu deliver.
2. Cloud-native Auth β Universal
WIF itu solusi yang bagus. Tapi dia didesain buat ekosistemnya sendiri (GKE). Kalau kamu mau eksperimen di lokal, static credentials masih jadi raja.
3. Kubernetes Operator = Superpower
Dengan ESO, kamu bisa inject secret dari cloud ke dalam Pod tanpa menyentuh kode aplikasinya sama sekali. Ini bukan cuma security improvement β ini perubahan paradigma cara kerja.
4. ClusterIssuer dan Namespace itu Penting
ClusterIssuer di cert-manager itu cluster-scoped, tapi dia baca secret dari namespace tertentu. Satu detail kecil ini butuh satu percobaan tersendiri buat nyadarinya.
5. HTTP-01 vs DNS-01 ACME Challenge
Untuk dev lokal tanpa IP publik, HTTP-01 nggak akan pernah berhasil. Solusinya: pakai DNS-01 (dengan API Cloudflare/Route53) atau mkcert untuk self-signed cert lokal.
Kesimpulan
ZenVault bukan cuma tentang "cara bikin API kecil." Ini tentang cara berfikir tentang keamanan, automasi, dan adaptasi di lingkungan cloud-native.
Pipeline akhirnya:
AWS Secrets Manager
ββ ESO ClusterSecretStore
ββ Kubernetes Secret
ββ Pod Env Variable
ββ GET /v1/debug β {"app_debug_key": "..."} β
Aplikasi Golang-nya literally nggak tau dia nyambung ke AWS. Dia cuma baca environment variable β sisanya urusan Kubernetes.
Dan itulah yang disebut Zero-ops Secret Management.
Source code ada di: github.com/stayrelevantid/zenvault
Diskusi & Komentar
Automasi Zenith-X: Dari Nol ke GitOps Auto-Deploy
Next ArticleSAWIT-X: Sistem Manajemen Perkebunan Digital
Artikel Terkait
SkyBridge Enterprise: Infrastruktur Cloud Skalabel
Bangun arsitektur cloud 3-tier skalabel & aman dengan AWS dan Terraform. Pelajari teknik zero-downtime deployment sekarang juga!
SAWIT-X: Sistem Manajemen Perkebunan Digital
Cerita SAWIT-X, sistem manajemen perkebunan kelapa sawit serverless berbasis Go. Solusi pencatatan operasional ringan dengan UI via Slack dan Google Sheets.
Automasi Zenith-X: Dari Nol ke GitOps Auto-Deploy
Perjalanan bangun infra cloud-native Zenith-X di GCP. Pelajari setup GKE, ArgoCD, CI/CD, & Secret Management berbasis GitOps. Hindari error pemula!