Secret Management — Secrets Never in Code
API keys, DB passwords, and certs must be fetched at runtime from a secrets manager — never in source code, env files, or container images.
When to use
- Always — for any credential, API key, certificate, or signing key
Tradeoffs
- Secrets manager is a runtime dependency (must be HA; startup fails if unavailable)
- Rotation requires app support (connection re-initialization, graceful reload)
- Go
- Python
func loadSecret(ctx context.Context, secretID string) (string, error) {
client, err := secretsmanager.NewFromConfig(awsCfg)
if err != nil {
return "", fmt.Errorf("secrets manager unavailable: %w", err)
}
out, err := client.GetSecretValue(ctx, &secretsmanager.GetSecretValueInput{
SecretId: aws.String(secretID),
})
if err != nil {
// fail fast — no fallback to defaults
return "", fmt.Errorf("cannot load secret %s: %w", secretID, err)
}
return aws.ToString(out.SecretString), nil
}
// Call at startup; re-call on SIGHUP for rotation support
func main() {
dbPass, err := loadSecret(ctx, "prod/db/password")
if err != nil {
log.Fatal(err) // hard exit — do not proceed with empty secret
}
initDB(dbPass)
}
import boto3, signal, sys
_db_client = None
def load_secret(secret_id: str) -> str:
client = boto3.client("secretsmanager")
resp = client.get_secret_value(SecretId=secret_id)
return resp["SecretString"]
def init_db(password: str) -> None:
global _db_client
_db_client = connect_db(password) # re-initializes on rotation
def reload_on_rotation(signum, frame):
password = load_secret("prod/db/password")
init_db(password)
signal.signal(signal.SIGHUP, reload_on_rotation)
if __name__ == "__main__":
try:
password = load_secret("prod/db/password")
except Exception as e:
sys.exit(f"cannot load secret: {e}") # fail fast
init_db(password)
Gotcha: Rotating a secret and NOT restarting services that cached it in memory is a partial rotation. Automate the restart, or use dynamic short-lived secrets (Vault dynamic credentials) so rotation is continuous.