Menerapkan zero trust di tokokita: validasi JWT dan OAuth2 di gateway, rate limiting token bucket untuk login, secrets management dengan Vault dan External Secrets Operator, NetworkPolicy, supply chain security dengan Trivy dan cosign, serta perlindungan PII

Di episode 4-18 kita membangun banyak layanan dan menghubungkannya. Episode ini mempertanyakan semua yang sudah dibuat: "apakah kalian mempercayai jaringan ini?" Jawaban zero trust adalah tidak — percayai identitas, otentikasi setiap akses, enkripsi setiap komunikasi, dan anggap setiap titik rawan sebagai permukaan serangan.
Mengapa keamanan microservices beda dengan monolith? Karena memperbesar permukaan serangan: lebih banyak API, lebih banyak secrets yang dipakai antar service, lebih banyak komponen pada rantai suplai. Satu secret bocor di satu service bisa jadi jalan masuk ke semuanya tanpa segmentasi yang benar.
Prinsip yang sudah kita jatuhkan di episode 10 kini menjadi kebijakan resmi: gateway adalah satu-satunya tempat yang menerima token user. Layanan internal memverifikasi machine identity sendiri (internal token/mTLS — episode 11/18), dan tidak menerima ataupun mewarisi token user.
api-gateway → validasi access token user (JWT/JWKS)
layanan internal → verifikasi identitas machine (mTLS / x-service-token)Untuk integrasi "login dengan Google" atau otorisasi delegatif, standar de-facto adalah OAuth2 + OpenID Connect. flow Authorization Code + PKCE dipakai untuk web/mobile; token dinilai sesaat lewat validator OIDC, bukan di-re-implementasi manual.
web-app → /authorize?code_challenge=... → provider login
← authorization code
web-app → /token (code + code_verifier) → access/info token
api-gateway → /userinfo (introspection/JWKS) untuk klaim identitasBelajar mendalam stack OAuth2 bisa disambung ke series otentikasi tersendiri; intinya di tokokita: jangan menulis token service sendiri — pakai provider (Auth.js di web, atau layanan OIDC) dan validasi di gateway.
Rate limiting melindungi API dari brute-force dan banjir. Algoritma yang paling masif digunakan: token bucket — bucket berisi token, tiap request memakai satu, terisi kembali dengan laju tetap. Di Redis per IP/user:
const BUCKET = 100 // kapasitas token
const REFILL_RATE = 20 // token per menit
export async function allow(key: string): Promise<boolean> {
// Lua script atomic: hitung token tersisa
const remaining = await redis.eval(`
local t = redis.call('GET', KEYS[1])
local last = redis.call('GET', KEYS[2])
if not t then return ARGV[1] end
local tokens = math.min(ARGV[1], t + (ARGV[2] - last) * ARGV[3])
if tokens >= 1 then
redis.call('SET', KEYS[1], tokens - 1, 'XX')
return 1
else
return 0
end
`, [key, `${key}:ts`], [BUCKET, Date.now(), REFILL_RATE / 60000])
return remaining === 1
}Kebijakan minimum di tokokita:
Jangan pernah memasukkan secrets ke git, env container, atau image. Di Kubernetes, Secret native disimpan base64 (bukan enkripsi) — rahasia nyata harus ada di luar cluster:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: auth-secrets
namespace: tokokita
spec:
secretStoreRef:
name: vault
kind: SecretStore
target:
name: tokokita-secrets
data:
- secretKey: JWT_SECRET
remoteRef:
key: tokokita/auth/jwt-secretPola lahirnya: manifest di git hanya berisi referensi (ExternalSecret), nilainya diambil saat runtime — kata kunci "referensikan, jangan salin".
Di Kubernetes, semua pod bisa bicara semua pod secara default. Zero trust memperlihatkan aturan yang lebih ketat — siapa layak memanggil siapa:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: order-ingress-gateway-only
namespace: tokokita
spec:
podSelector:
matchLabels:
app: order-service
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: api-gateway
ports:
- port: 3004Menuliskannya butuh disiplin: setiap layanan mendaftar "siapa yang sah memanggil saya" — daftar ini juga jadi dokumentasi arsitektur yang hidup. (Catatan: enforcement-nya butuh CNI yang mendukung NetworkPolicy, misal Calico; pada cluster cloud, ini level komponen.)
Image yang kita bangun di episode 16 masih bisa disusupi di rantai suplai. Tiga lapis pertahanan:
trivy image ghcr.io/tokokita/order-service:v1.4.2
trivy fs services/order-service # scan repo saat CIcosign sign ghcr.io/tokokita/order-service:v1.4.2
cosign verify ghcr.io/tokokita/order-service:v1.4.2bun audit/npm audit di pipeline; naikkan package yang punya CVE; lockb/lockfile wajib di-commit.Zero trust untuk data — PII (email, nama) di database (auth-service) wajib dilindungi:
request_id (episode 10) sebagai referensi, bukan PII.function maskEmail(email: string) {
const [local, domain] = email.split('@')
return `${local[0]}***@${domain}`
}
logger.info({ user: maskEmail(email), requestId })Warning
"Enkripsi at rest" mencegah pencurian disk, bukan kebodohan aplikasi. Dua tempat PII paling sering bocor di microservices justru: log yang mencetak body request penuh, dan debug tool yang menampilkan token/password. Mulai dari prinsip jangan log apa pun yang tidak wajib, baru pikirkan enkripsi. Masking harus menjadi kebijakan tim, bukan insiden.
Episode 19 menerapkan zero trust di tokokita:
Di episode 20 selanjutnya, kita masuk observability: OpenTelemetry untuk tracing, metrics, dan log — span per HTTP/event, propagasi traceparent melintasi auth → order → payment → Kafka → notif, metrik RED, log JSON terstruktur, dan backend Prometheus/Grafana/Tempo/Loki. Sampai jumpa di episode 20!