Part 6 · 2 chapters · ~18 min

Identity and Secrets

IAM as the real perimeter: principals, actions, resources and conditions, identity and resource policies, evaluation and guardrails, roles over users and least privilege; then secrets: the anti-patterns, the managed store, workload identity, fetching, rotation, and removing the secret altogether.

18

IAM models, roles and policies

identity is the perimeter
  1. Every request is a principal, an action, a resource and some conditions.
  2. Identity policies say what a principal may do. In GCP, role bindings on the resource hierarchy do the same.
  3. Resource policies say who may use a resource. Cross-account access needs both sides to agree.
  4. Evaluation: an explicit deny wins, an allow is required, and every guardrail must also allow.
  5. Roles, not users: SSO for humans, platform roles for workloads, and no long-lived keys.
  6. Least privilege: generate policies from actual usage, scope them and review them.
code
// the payments service's role: only what it calls, only where, only how
{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::acme-kyc-private/kyc/*",
      "Condition": { "StringEquals": { "aws:SourceVpce": "vpce-0a1b2c" } } },
    { "Effect": "Allow",
      "Action": ["secretsmanager:GetSecretValue"],
      "Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:prod/payments/*" },
    { "Effect": "Allow",
      "Action": ["kms:Decrypt", "kms:GenerateDataKey"],
      "Resource": "arn:aws:kms:eu-west-1:111122223333:key/9f1e…",
      "Condition": { "StringEquals": { "kms:ViaService": "s3.eu-west-1.amazonaws.com" } } }
  ]
}
the Capital One lesson
In 2019 an SSRF in a WAF reached the instance metadata endpoint, took the instance role's credentials, and that role could list and read far more buckets than the WAF ever needed. An over-broad role turned a bug into a breach of 100 million records. IMDSv2 (session tokens) and least privilege close both halves of that chain.
IAM: WHO CAN DO WHAT TO WHICH THING
principals, policies, roles and the evaluation that decides every API call
swipe the figure sideways, or tap expand for full screen
1/6
the request
The request: principal, action, resource, conditions. "Role payments-api wants s3:PutObject on arn:aws:s3:::acme-kyc-private/kyc/u_7f2/… from VPC endpoint vpce-0a1, with MFA false, at 14:02." Everything IAM decides is a function of those fields.
19

Secrets management and workload identity

no human pastes a secret
  1. Anti-patterns: repos, .env files, broad CI variables, image layers, plain environment variables.
  2. The store is versioned, KMS-encrypted, controlled by IAM per secret and audited.
  3. Workload identity: the platform gives the service its credentials. There is no bootstrap secret.
  4. Fetching: inject at start, or fetch with a cache so rotations land without a restart.
  5. Rotation: scheduled and tested, with an overlap period so old versions keep working.
  6. Remove the secret entirely where you can: IAM database auth, OIDC federation.
code
# GitHub Actions → AWS without stored keys (OIDC federation)
permissions: { id-token: write, contents: read }
steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::111122223333:role/gha-deploy-payments
      aws-region: eu-west-1
# the role's trust policy only accepts tokens for this repo and the main branch:
#   "token.actions.githubusercontent.com:sub": "repo:acme/payments:ref:refs/heads/main"
the frontend's share
The browser gets none of these secrets. Anything in the bundle is public (the Disciplines course part 6). The frontend's server side (a BFF, SSR functions) uses workload identity like any other service. Publishable keys are restricted by origin at the provider.
SECRETS AND WORKLOAD IDENTITY
how a running service gets a database password or an API key without anyone having pasted it anywhere
swipe the figure sideways, or tap expand for full screen
1/6
anti-patterns
The anti-patterns: secrets in the repo (forever in git history), in .env files copied between laptops, in CI variables readable by every workflow, baked into images (any layer, any registry reader), or in plain environment variables visible in the console and in crash dumps. Each spreads the secret to more places than anyone can track.