AWS Secrets Manager Integration

Hard-coding a database password into a Kubernetes manifest puts it in cluster state, image history, and every backup. AWS Secrets Manager keeps the credential in a managed store, scoped by IAM, and rotates it on a schedule. This page integrates it into a Python service so the secret only ever exists in memory as a SecretStr.

AWS Secrets Manager is the native option in the enterprise secrets section for teams on AWS, feeding credentials into the same pydantic-settings model that validates the rest of the configuration.

Secure implementation

# secrets/asm.py
import json
import time
import boto3
from pydantic import SecretStr

_client = boto3.client("secretsmanager")
_cache: dict[str, tuple[float, dict]] = {}
TTL_SECONDS = 600  # shorter than the rotation interval


def get_secret(secret_id: str) -> dict[str, SecretStr]:
    now = time.monotonic()
    cached = _cache.get(secret_id)
    if cached and now - cached[0] < TTL_SECONDS:
        return cached[1]                       # serve from in-memory cache

    raw = _client.get_secret_value(SecretId=secret_id)["SecretString"]
    parsed = {k: SecretStr(v) for k, v in json.loads(raw).items()}  # mask every field
    _cache[secret_id] = (now, parsed)
    return parsed


creds = get_secret("prod/db")
password = creds["password"].get_secret_value()  # unwrap only at the point of use

The cache keeps API calls within rate limits; SecretStr keeps the value out of logs; the credential is unwrapped only at the moment it is handed to the database driver.

Configuration reference

Parameter Type Default Security implication
SecretId ARN/name Scope IAM to this exact ARN
TTL_SECONDS int 600 Keep below rotation interval
SecretStr wrap masked Prevents leakage in logs/tracebacks
IAM action GetSecretValue Least privilege; no wildcard
VersionStage AWSCURRENT current Pin to current after rotation

Deployment parity: local to production

  1. Local dev — developers assume a read-only role via SSO and fetch a non-prod secret; no plaintext on disk.
  2. CI — the pipeline uses an OIDC role scoped to test secrets only.
  3. Staging/Production — the pod’s IAM role grants GetSecretValue on its own secret ARNs; rotation runs on the Secrets Manager schedule.

Security boundaries & guardrails

  • IAM policy lists explicit secret ARNs — never Resource: "*".
  • Cache in memory only; never write the fetched secret to disk.
  • Wrap every field in SecretStr and unwrap at the call site only.
  • Set the cache TTL below the rotation interval so stale credentials expire quickly.
  • Enable CloudTrail on GetSecretValue to audit access.

Troubleshooting

  • AccessDeniedException — the role lacks GetSecretValue on that ARN; scope the policy to it.
  • Stale credential after rotation — the cache TTL is longer than the rotation interval; shorten it. See Caching AWS Secrets in Memory.
  • Throttling / ThrottlingException — too many GetSecretValue calls; the in-memory cache is missing or disabled.
  • Secret value in logs — a field is a plain string; wrap in SecretStr.

Frequently asked questions

How do I avoid hitting the AWS Secrets Manager API on every request?

Cache the fetched secret in memory with a short TTL and refresh on expiry. Secrets Manager has rate limits and per-call cost, so a 5–15 minute in-memory cache wrapped in SecretStr is the standard pattern.

What IAM permissions does the application need?

Grant only secretsmanager:GetSecretValue on the specific secret ARNs the service uses, scoped by resource. Never attach a wildcard secretsmanager:* policy to an application role.

How does rotation work without redeploying?

Secrets Manager rotates the underlying credential and updates the stored value; your app picks it up when its cache TTL expires and it re-fetches. Keep the TTL shorter than the rotation interval so stale credentials are never used for long.

Conclusion

The invariant: the credential lives in Secrets Manager, reaches the app only as a TTL-cached SecretStr, and is accessed through an IAM role scoped to its exact ARN. Rotation is the store’s job, not a redeploy.