Configure AWS KMS for secured intercept

Audience: Platform engineers / cloud security
Time: ~20 min
Prerequisites: Secured intercept overview, Bouncer on Kubernetes or Docker, an AWS account with KMS

Use this guide when your organization already operates AWS KMS and you want the Bouncer to resolve upstream credentials from envelope metadata stored in the Control Plane — without placing plaintext secrets in Control Core storage.

What you configure

ComponentYour responsibilityControl Core stores
Customer master key (CMK)Create and own the CMK in your AWS accountKey ARN reference only
IAM / workload identityGrant the Bouncer identity kms:Decrypt on that CMKNothing
CiphertextEncrypt upstream secrets with your CMKOpaque ciphertext_ref blob only
Control PlaneRegister settings + envelope rowsMetadata only — never plaintext

Before you start

  1. Enable Settings → Feature Flags → flags.crypto.dual_custody_intercept (default off).
  2. Register at least one Bouncer in Settings → Bouncer Management (status: connected).
  3. Confirm Policy Bridge sync is healthy for your environment.

Step 1 — Create a customer-managed key (~5 min)

In your AWS account (production region):

  1. Open AWS KMS and create a symmetric CMK dedicated to Control Core upstream credentials.
  2. Note the key ARN (example shape: arn:aws:kms:REGION:ACCOUNT:key/KEY-ID).
  3. Apply your standard key policy: your security team retains full administrative control; the Bouncer role receives Decrypt only.

Troubleshooting: If decrypt fails later with AccessDeniedException, verify the key policy and the IAM role policy both allow kms:Decrypt on this CMK.

Step 2 — Bind IAM to the Bouncer workload (~10 min)

The Bouncer must call KMS as your workload, not as a shared platform user.

  1. Create an IAM role with a policy limited to kms:Decrypt (and kms:DescribeKey if your standards require it) on your CMK ARN.
  2. Associate the role with the Bouncer Kubernetes service account using IAM Roles for Service Accounts (IRSA) or your cloud provider’s equivalent pod-identity mechanism.
  3. Set the Bouncer deployment AWS region to the CMK region in your Helm or Compose environment settings.

Other platforms

Use the same least-privilege principle: whichever identity runs the Bouncer container must be able to decrypt with your CMK and nothing broader.

Bouncer deployment settingPurposeExample
Dual-custody interceptTurns on envelope resolution at interceptenabled
Secret providerUse KMS envelope modekms_envelope
Cloud regionRegion of your CMKca-central-1
KMS key ARN prefix (optional)Defense-in-depth: only listed key families may be usedarn:aws:kms:ca-central-1:123456789012:

Apply settings through your Helm values or container environment as described in your deployment package. Restart the Bouncer after changes.

Step 3 — Register the provider in the Control Plane (~3 min)

curl -sS -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  "$CONTROL_PLANE_URL/v1/crypto/settings" \
  -d '{
    "provider": "kms_envelope",
    "kms_key_arn": "arn:aws:kms:ca-central-1:123456789012:key/abc-123",
    "notes": "Production payments upstream CMK"
  }'

You can also use Settings → Cryptographic Provider when the dual-custody feature flag is on.

Step 4 — Encrypt and register envelope metadata (~5 min)

Encrypt the upstream credential in your AWS account (CLI, SDK, or approved pipeline). Register only the ciphertext and key reference:

# Example: produce CiphertextBlob with AWS CLI (run in your account)
CIPHERTEXT=$(aws kms encrypt \
  --key-id "arn:aws:kms:ca-central-1:123456789012:key/abc-123" \
  --plaintext fileb://upstream-secret.bin \
  --encryption-context "env=production,service=payments-api" \
  --query CiphertextBlob --output text)

curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  "$CONTROL_PLANE_URL/v1/crypto/envelope-secrets" \
  -d "{
    \"logical_path\": \"protected-resources/payments-api/upstream-tls\",
    \"key_id\": \"arn:aws:kms:ca-central-1:123456789012:key/abc-123\",
    \"ciphertext_ref\": \"${CIPHERTEXT}\",
    \"aad\": \"env=production,service=payments-api\",
    \"provider\": \"kms_envelope\"
  }"

The logical_path is an operator-defined label that policies and protected resources can reference. It is not a secret.

Step 5 — Verify (~5 min)

  1. Send test traffic through the Bouncer to a protected route that requires the upstream credential.
  2. Confirm Audit shows allow/deny decisions without bearer tokens or decrypted values in event payloads.
  3. List registrations: GET /v1/crypto/envelope-secrets — responses must contain metadata only.

Troubleshooting: 403 on crypto APIs → enable the dual-custody feature flag. Intercept works but upstream auth fails → re-check IAM role, region, and CMK ARN on the envelope row.

Key rotation

  1. Re-encrypt upstream material with the new CMK (or new key version) in your KMS workflow.
  2. Update the envelope row in the Control Plane with the new ciphertext_ref and key_id if the ARN changed.
  3. Roll Bouncer pods if your identity or region settings changed.

Control Core does not hold a copy of the plaintext during rotation.