Deepen PracticeOrdered learning track

Secret Management Mental Model

Learn Java Microservices File Handling, State, Configuration and Secret Management - Part 045

Mental model secret management untuk Java microservices: secret sebagai capability, identity, least privilege, rotation, TTL, audit, dan failure model.

13 min read2437 words
PrevNext
Lesson 4570 lesson track39–58 Deepen Practice
#java#microservices#secrets#security+3 more

Part 045 — Secret Management Mental Model

A secret is not a string.

A secret is a temporary capability with blast radius.

Banyak engineer memperlakukan secret seperti configuration value yang “lebih sensitif”. Itu awal dari desain yang rapuh.

Secret bukan hanya value. Secret adalah capability: sesuatu yang memberi kemampuan kepada workload untuk melakukan tindakan terhadap resource lain.

Contoh:

  • database password memberi kemampuan membaca/menulis database;
  • object storage access key memberi kemampuan membaca/menulis object;
  • API token memberi kemampuan memanggil service eksternal;
  • TLS private key memberi kemampuan membuktikan identitas endpoint;
  • signing key memberi kemampuan membuat token yang dipercaya pihak lain;
  • encryption key memberi kemampuan membuka data yang seharusnya tertutup.

Karena secret adalah capability, pertanyaan desainnya bukan hanya:

Where do we store the secret?

Pertanyaannya harus menjadi:

Who receives which capability, for what purpose, for how long,
under which identity, with what audit trail, and how do we revoke it?

Di part ini kita membangun mental model secret management yang akan dipakai untuk Kubernetes Secret, Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager, External Secrets Operator, SOPS, Sealed Secrets, dan secret rotation tanpa downtime.


1. Secret vs Config

Config dan secret sama-sama bisa masuk ke aplikasi melalui environment variable, mounted file, config server, atau remote API. Tetapi semantic-nya berbeda.

DimensionConfigurationSecret
TujuanMengubah behaviorMemberi capability/access
SensitivityBisa non-sensitiveSensitive by default
LifecycleBisa stabil lamaHarus bisa rotate/revoke
AuditChange auditAccess + issuance + use audit
Failure modeBehavior salahData breach / privilege abuse
Default exposureBisa ditampilkan sebagianHarus redacted
OwnerService/domain/platformSecurity/platform/service
Runtime handlingValidasi dan precedenceTTL, lease, reload, revocation

Contoh yang sering salah diklasifikasikan:

# Bukan secret
payment:
  gateway-base-url: https://api.payment.example
  connect-timeout: 2s
  retry-max-attempts: 3

# Secret
payment:
  api-key: ${PAYMENT_API_KEY}
  webhook-signing-secret: ${PAYMENT_WEBHOOK_SECRET}

Rule praktis:

If knowing the value grants access, impersonation, decryption,
signing, or policy bypass capability, treat it as secret.

2. Secret Is a Capability

Lihat secret sebagai capability token.

Secret yang baik punya boundary:

  • who: workload identity yang boleh mengambil secret;
  • what: capability apa yang diberikan;
  • where: resource mana yang bisa diakses;
  • when: TTL, expiry, activation window;
  • how much: scope/permission;
  • why: purpose dan owner;
  • proof: audit trail dan issuance log.

Secret yang buruk:

One static admin password shared by all services forever.

Secret yang lebih baik:

Service-specific credential, least privilege, short TTL where possible,
issued to authenticated workload identity, rotated regularly,
audited, and revocable.

3. Four-Plane Model

Secret management menyentuh empat plane.

3.1 Identity Plane

Workload harus punya identitas.

Contoh identity source:

  • Kubernetes ServiceAccount;
  • cloud workload identity / IAM role;
  • Vault Kubernetes auth;
  • SPIFFE/SPIRE identity;
  • mTLS client certificate;
  • instance metadata identity;
  • CI/CD OIDC identity.

Tanpa identity plane yang baik, secret manager hanya menjadi database password yang lebih mewah.

3.2 Policy Plane

Policy menjawab:

Which identity can access which secret path or capability?

Contoh Vault policy:

path "kv/data/prod/evidence-service/*" {
  capabilities = ["read"]
}

path "database/creds/evidence-service" {
  capabilities = ["read"]
}

Policy harus least privilege. Jangan memberi service akses wildcard karena “lebih gampang”.

3.3 Delivery Plane

Secret bisa dikirim ke runtime dengan beberapa cara:

DeliveryContohTrade-off
Env varDB_PASSWORDSimple, tetapi sulit reload dan mudah bocor via process/env dump
Mounted fileKubernetes Secret volume, Vault Agent templateLebih reloadable, bisa dibatasi file permission
App fetch APIJava client ke Vault/Secrets ManagerKontrol tinggi, tetapi app harus handle auth/retry/cache
Sidecar/agentVault Agent, CSI driverMemindahkan sebagian logic ke platform, tetapi tetap perlu lifecycle handling
Build-time injectionSecret baked into imageAnti-pattern untuk runtime secret

3.4 Consumption Plane

Java service tetap bertanggung jawab atas consumption:

  • redaction;
  • connection pool refresh;
  • token expiry handling;
  • reload strategy;
  • fallback behavior;
  • readiness/liveness effect;
  • observability;
  • avoiding accidental logs;
  • not caching beyond TTL.

Secret manager tidak otomatis membuat consumer aman.


4. Secret Lifecycle

Secret lifecycle minimal:

Tahap-tahapnya:

StageMakna
RequestedWorkload meminta capability
IssuedSecret dibuat/diambil dari authority
ActiveConsumer memakai secret
RenewedLease diperpanjang jika renewable
RotatingVersi baru dipersiapkan
DualCredentialWindowCredential lama dan baru sama-sama valid sementara
NewActiveConsumer sudah memakai secret baru
OldRevokedSecret lama dicabut
ExpiredTTL habis
RetiredTidak boleh dipakai lagi

Invariant:

A Java service must know whether the secret it uses is static, versioned,
leased, renewable, rotatable, or revocable.

Jika service tidak tahu lifecycle secret, service tidak bisa recovery dengan benar.


5. Secret Types

Tidak semua secret sama.

TypeContohConcern Utama
Static secretAPI key eksternalRotation manual/periodic
Versioned static secretkv-v2 secret versionVersion pinning, rollback
Dynamic secretVault DB credentialLease, TTL, revocation
TokenOAuth client token, service tokenExpiry, refresh
CertificatemTLS certExpiry, trust chain, reload
Private keyJWT signing key, TLS keyLeakage impact tinggi
Data encryption keyEnvelope encryption DEKKey wrapping, rotation
Root/break-glass secretEmergency admin credentialExtreme control, offline audit

Desain consumer berbeda untuk tiap jenis.

5.1 Static Secret

Static secret paling mudah dipakai, paling mudah dilupakan.

Risiko:

  • tidak punya TTL natural;
  • sering dipakai lintas service;
  • rotation dianggap event langka;
  • leakage bisa lama tidak terdeteksi.

Mitigasi:

  • owner jelas;
  • versioning;
  • periodic rotation;
  • access audit;
  • scoped capability;
  • no shared credential;
  • expiry policy di secret manager atau upstream provider.

5.2 Dynamic Secret

Dynamic secret dibuat on-demand, biasanya dengan lease.

Contoh: Vault database secret engine membuat username/password database sementara untuk role tertentu.

Keuntungan:

  • unique per consumer/session;
  • TTL jelas;
  • revocable;
  • audit issuance;
  • blast radius lebih kecil.

Risiko:

  • consumer harus handle expiry;
  • connection pool harus refresh;
  • lease renewal failure bisa memutus akses;
  • secret manager outage memengaruhi runtime.

5.3 Certificate

Certificate bukan hanya secret value. Ia membawa identity.

Pertanyaan:

  • siapa CA?
  • trust bundle dikelola bagaimana?
  • kapan cert expire?
  • apakah app reload TLS material tanpa restart?
  • bagaimana mencegah cert lama tetap dipercaya?
  • bagaimana observability days-until-expiry?

5.4 Signing Key

Signing key lebih berbahaya daripada banyak password karena bisa membuat artifact yang tampak valid.

Contoh:

  • JWT signing key;
  • webhook signing secret;
  • document signing key;
  • token minting key.

Rotation signing key harus memikirkan:

  • kid / key ID;
  • verification key set;
  • overlap window;
  • issued token TTL;
  • revocation;
  • audit;
  • emergency key compromise response.

6. Threat Model

Secret management harus dimulai dari threat model, bukan tool.

6.1 Threat: Secret Leaks in Logs

Contoh leak:

log.info("Connecting to database with url={}", jdbcUrl);

Jika jdbcUrl mengandung password, selesai.

Mitigasi:

  • jangan embed password di URL string yang dilog;
  • gunakan separate username/password property;
  • redaction filter;
  • avoid toString() on credential object;
  • secure log access;
  • scanning log pipeline.

6.2 Threat: Over-Permissioned Service

Service hanya perlu read metadata, tetapi mendapat admin DB password.

Mitigasi:

  • role per service;
  • schema-level permission;
  • read/write split;
  • deny DDL;
  • no cross-domain table access;
  • audit query/connection where available.

6.3 Threat: Secret Exposed via Env/Actuator

Env var mudah digunakan, tetapi bisa bocor melalui:

  • process introspection;
  • debugging shell;
  • crash diagnostics;
  • /actuator/env jika tidak diamankan;
  • support bundle;
  • CI logs.

Mitigasi:

  • disable sensitive actuator exposure;
  • use mounted file or agent where better;
  • restrict debug access;
  • redact diagnostics;
  • avoid env var untuk high-value long-lived secret.

6.4 Threat: Secret Rotation Breaks Service

Secret dirotasi, tetapi Java connection pool menyimpan koneksi lama.

Mitigasi:

  • dual credential window;
  • max connection lifetime;
  • refresh hook;
  • canary rotation;
  • monitor old credential use;
  • avoid infinite pool lifetime.

6.5 Threat: Secret Manager Outage

Jika secret manager tidak bisa diakses, apakah service mati?

Jawabannya tergantung jenis secret.

SecretStartup BehaviorRuntime Behavior
Required DB credentialFail startupDegrade/refresh retry/readiness fail before expiry
Optional external API tokenStart if feature disabledDisable integration temporarily
Signing keyUsually fail closedStop issuing new token if key unavailable
Trust bundleFail if invalidMaintain current valid bundle until expiry

7. Delivery Pattern Decision Matrix

PatternGood ForAvoid When
Kubernetes Secret env varLow-risk simple app configNeed runtime rotation/reload
Kubernetes Secret volumeFile-based credential, cert, reloadable materialApp cannot watch/reload file
Vault Agent templateDynamic secrets, file delivery, renewal by agentNeed app-level secret semantics
Spring Cloud VaultJava property source integration, Vault-native appApp team cannot handle Vault operational model
Cloud SDK fetchFine-grained control and cacheApp should not own secret client complexity
External Secrets OperatorGitOps/K8s native sync from external managerNeed immediate rotation without K8s Secret sync delay
Secrets Store CSI DriverMount external secrets as filesNeed native env var injection only

No pattern is universally best. Choose based on:

  • reload requirement;
  • TTL/lease behavior;
  • operational ownership;
  • app maturity;
  • audit requirement;
  • blast radius;
  • platform standard.

8. Java Secret Consumption Model

A good Java service should not scatter secret access across code.

Buruk:

String password = environment.getProperty("db.password");

Lebih baik:

public interface SecretProvider {
    SecretSnapshot getCurrent(String secretName);
}

public record SecretSnapshot(
    String name,
    SecretValue value,
    String version,
    Instant loadedAt,
    Instant expiresAt
) {}

Wrapper value:

public final class SecretValue {
    private final String value;

    private SecretValue(String value) {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException("Secret value must not be blank");
        }
        this.value = value;
    }

    public static SecretValue of(String value) {
        return new SecretValue(value);
    }

    public String reveal() {
        return value;
    }

    @Override
    public String toString() {
        return "[REDACTED]";
    }
}

Secret snapshot membantu observability tanpa leak:

log.info(
    "Secret loaded name={} version={} loadedAt={} expiresAt={}",
    snapshot.name(),
    snapshot.version(),
    snapshot.loadedAt(),
    snapshot.expiresAt()
);

Jangan log snapshot.value().


9. Configuration Binding vs Secret Binding

Spring Boot memudahkan binding property ke Java object. Tetapi untuk secret, jangan asal expose field.

Contoh buruk:

@ConfigurationProperties(prefix = "payment")
public record PaymentProperties(
    String baseUrl,
    String apiKey
) {}

Jika record ini ter-log, apiKey bisa ikut keluar.

Lebih baik:

@ConfigurationProperties(prefix = "payment")
@Validated
public record PaymentProperties(
    @NotBlank String baseUrl,
    @NotBlank String apiKey
) {
    @Override
    public String toString() {
        return "PaymentProperties[baseUrl=" + baseUrl + ", apiKey=[REDACTED]]";
    }

    public SecretValue apiKeySecret() {
        return SecretValue.of(apiKey);
    }
}

Lebih baik lagi, pisahkan non-secret config dan credential object:

@ConfigurationProperties(prefix = "payment")
@Validated
public record PaymentClientProperties(
    @NotBlank String baseUrl,
    @NotNull Duration timeout
) {}

public interface PaymentCredentialProvider {
    SecretValue currentApiKey();
}

Pemisahan ini membuat config lifecycle dan secret lifecycle tidak tercampur.


10. Runtime Reload Model

Secret reload harus eksplisit.

Untuk database, secret reload bukan hanya mengganti string password. Ia memengaruhi connection pool.

Checklist:

  • apakah pool membuat connection baru dengan credential baru?
  • apakah connection lama punya max lifetime?
  • apakah credential lama masih valid selama overlap?
  • apakah health check membuktikan credential baru berhasil?
  • apakah old credential usage bisa dideteksi?
  • apakah rollback mungkin?

11. Secret Observability

Secret tidak boleh terlihat, tetapi lifecycle-nya harus terlihat.

Metric yang aman:

secret_load_success_total{name="db-main"}
secret_load_failure_total{name="db-main",reason="access_denied"}
secret_seconds_until_expiry{name="db-main"}
secret_version_current{name="db-main",version="v42"}
secret_refresh_duration_seconds{name="db-main"}
secret_rotation_active{name="db-main"}
secret_redaction_failure_total

Audit event:

{
  "eventType": "SECRET_REFRESHED",
  "secretName": "evidence-service-db",
  "version": "v42",
  "workload": "evidence-service",
  "namespace": "prod-regulatory",
  "expiresAt": "2026-07-05T12:00:00Z",
  "correlationId": "..."
}

Tidak boleh ada raw secret value.

Alert:

secret_seconds_until_expiry < 600 and last_refresh_failed == true
secret_load_failure_total increased sharply
old_credential_used_after_rotation > 0
unexpected_secret_access_detected

12. Secret Failure Model

Secret failure harus diklasifikasi.

FailureMeaningDefault Response
Secret missingMisconfiguration/deployment issueFail startup
Access deniedIAM/RBAC/policy wrong or attackFail closed + alert
Secret expiredTTL/renewal failureRefresh/reconnect; degrade before hard fail
Secret manager timeoutDependency issueRetry with backoff; use valid cached secret if safe
Secret version invalidBad rotation/payloadReject new version; keep old if still valid
Secret leakedSecurity incidentRevoke, rotate, investigate, audit
Secret mismatchConsumer/upstream not alignedEnter rotation recovery playbook

Important distinction:

Using a cached secret during secret-manager outage is safe only if:
- the secret is still valid,
- the secret has not been revoked,
- the app can observe expiry,
- the capability does not require immediate revocation semantics.

13. Secret Design Review Questions

Untuk setiap secret:

Identity

  • Workload identity apa yang boleh membaca secret?
  • Apakah identity berbasis pod/service account/cloud role?
  • Bagaimana mencegah service lain di namespace sama membaca secret?

Scope

  • Resource apa yang bisa diakses?
  • Permission minimal apa yang dibutuhkan?
  • Apakah credential shared antar service?
  • Apakah credential bisa dipisah read/write?

Lifecycle

  • Static atau dynamic?
  • Versioned atau unversioned?
  • TTL ada atau tidak?
  • Renewable atau tidak?
  • Rotation period berapa?
  • Bagaimana revocation?

Delivery

  • Env var, mounted file, API fetch, sidecar, CSI, atau operator?
  • Apakah perlu runtime reload?
  • Apakah app bisa detect update?
  • Apakah delivery path punya audit?

Consumption

  • Bagaimana Java app menghindari log leak?
  • Bagaimana connection/client refresh?
  • Apa behavior saat refresh gagal?
  • Apakah readiness berubah saat secret mendekati expiry?

Incident

  • Bagaimana revoke cepat?
  • Bagaimana rotate semua consumer?
  • Bagaimana membuktikan siapa mengakses?
  • Bagaimana mendeteksi old credential masih dipakai?

14. Anti-Patterns

14.1 Secret in Git

spring:
  datasource:
    password: super-secret-prod-password

Masalah:

  • history Git menyimpan secret;
  • fork/clone memperluas blast radius;
  • sulit revoke semua copy;
  • audit consumption tidak ada.

14.2 Secret Baked into Docker Image

ENV DB_PASSWORD=prod-password

Masalah:

  • image registry menjadi secret store;
  • secret pindah antar environment;
  • rotation butuh rebuild;
  • layer history bisa menyimpan value.

14.3 One Secret for All Services

Masalah:

  • tidak ada attribution;
  • rotation memengaruhi semua service;
  • blast radius besar;
  • least privilege mustahil.

14.4 Treating Base64 as Encryption

Base64 adalah encoding, bukan encryption. Ini akan dibahas detail di Part 046.

14.5 Ignoring Consumer Rotation

Secret manager sudah rotate, tetapi app tidak reconnect. Hasilnya outage atau old credential masih dipakai.

14.6 Logging Full Configuration

log.info("Properties: {}", properties);

Jika object properties berisi secret, log pipeline menjadi breach vector.


15. Reference Architecture

Arsitektur ini punya beberapa boundary:

  • Kubernetes identity tidak otomatis sama dengan cloud identity;
  • Secret manager adalah authority;
  • operator/CSI/agent adalah delivery mechanism;
  • Kubernetes Secret/mounted file adalah local projection;
  • Java app adalah consumer yang harus handle runtime lifecycle;
  • protected resource harus punya audit dan least privilege.

16. Production Invariants

Secret management harus menjaga invariant berikut:

  1. No secret without owner.
  2. No secret without purpose.
  3. No production secret in source code or image.
  4. No shared credential across unrelated services.
  5. No secret value in logs, metrics, traces, errors, or audit payload.
  6. No static admin credential for normal runtime path.
  7. No rotation without consumer readiness.
  8. No dynamic secret without TTL handling.
  9. No secret access without identity and policy.
  10. No secret incident without revocation and audit path.

17. Key Takeaways

Secret management bukan “di mana menyimpan password”. Secret management adalah lifecycle capability management.

Yang harus tertanam:

  • Secret adalah capability, bukan string.
  • Identity plane lebih penting daripada storage format.
  • Least privilege mengurangi blast radius.
  • TTL/lease mengubah cara Java app harus consume secret.
  • Delivery mechanism tidak menghapus tanggung jawab consumer.
  • Rotation harus didesain sebagai normal operation, bukan emergency procedure.
  • Observability harus menunjukkan lifecycle secret tanpa membocorkan nilainya.
  • Secret manager adalah authority/custodian; Java service tetap consumer yang accountable.

Di part berikutnya, kita masuk ke Kubernetes Secrets secara realistis: base64, RBAC, encryption at rest, mounting, env var, immutable Secret, update semantics, dan failure mode production.


References

Lesson Recap

You just completed lesson 45 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.