Skip to content

AWS IAM Permissions

AWS permissions are only needed if you’re using DynamoDB-backed features. Filesystem keystores with SQLite or PostgreSQL anti-slashing require no AWS access at all.

Three DynamoDB-backed paths need IAM permissions:

  • DynamoDB anti-slashing — stores EIP-3076 slashing protection data in DynamoDB
  • DynamoDB + KMS key source — stores encrypted BLS validator keys in DynamoDB under a chamber master key protected by AWS KMS-wrapped Shamir shares
  • Signer state — stores chamber state, operator credentials, auth policies, auth tokens, and optional Nitro auto-unseal state

The DynamoDB anti-slashing backend uses transactions for atomicity. It never uses BatchWriteItem or BatchGetItem.

ActionPurpose
dynamodb:DescribeTableTable existence check at startup
dynamodb:GetItemRead watermarks and signing roots
dynamodb:PutItemWrite watermark updates
dynamodb:TransactGetItemsAtomic multi-item reads
dynamodb:TransactWriteItemsAtomic conditional writes (slashing protection)

No GSI is needed, so the resource ARN is the table only.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DynamoDBAntiSlashing",
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:TransactGetItems",
"dynamodb:TransactWriteItems"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/TABLE_NAME"
}
]
}

Replace REGION, ACCOUNT_ID, and TABLE_NAME with your values.

The terraform/examples/ configurations create the table and wire up this policy to your signer IAM role.

The same signer role is used for KMS wrapping and unwrapping. There is no separate first-boot KMS key, runtime KMS key, or setup role in the application model.

The signer calls kms:Encrypt when it creates or rewrites KMS-wrapped Shamir shares: initialization, KMS key rotation, mode rotation, and Nitro auto-unseal blob refresh. It calls kms:Decrypt when it reconstructs the master key from enough shares to meet the configured threshold.

Grant both actions to the signer role for every Shamir KMS key. In cross-account deployments, both the caller’s IAM policy and the target key policy must allow the operation.

Each KMS key used for Shamir shares must have a key policy granting the signer role access. If you use the Terraform modules, this is configured automatically. For manual key creation:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMDelegation",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_ID:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowSignerRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_ID:role/SIGNER_ROLE_NAME"
},
"Action": ["kms:Encrypt", "kms:Decrypt"],
"Resource": "*"
}
]
}

On EKS, use IAM Roles for Service Accounts (IRSA) or EKS Pod Identity to bind the IAM role to your signer pod without static credentials.

ServiceAccount annotation (IRSA):

apiVersion: v1
kind: ServiceAccount
metadata:
name: containment-chamber
namespace: validators
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME

Trust policy for the IAM role:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/oidc.eks.REGION.amazonaws.com/id/OIDC_ID"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.REGION.amazonaws.com/id/OIDC_ID:sub": "system:serviceaccount:validators:containment-chamber",
"oidc.eks.REGION.amazonaws.com/id/OIDC_ID:aud": "sts.amazonaws.com"
}
}
}
]
}

The signer uses the standard AWS SDK credential chain, so IRSA works without any configuration changes. See the Kubernetes deployment guide for the full pod spec and ServiceAccount setup.

For maximum custody separation, split the master key across KMS keys in separate AWS accounts. A 2-of-3 setup means any two accounts can reconstruct the master key, but no single account can do so alone.

The strongest pattern is N KMS keys in N AWS accounts, with each account administered by a different person or team. The signer stores one Shamir share per KMS key, so separating both the keys and the administrators makes unilateral recovery much harder: one compromised account, one malicious admin, or one accidental key-policy change is not enough to decrypt the master key.

Match the Shamir threshold to your governance model. For example, 2-of-3 tolerates one unavailable account while preventing any single account from recovering the master key. 3-of-5 raises the collusion bar but requires more accounts to be healthy during unseal and rotation.

Account A (primary, runs signer)
KMS Key 1 + DynamoDB table + IAM signer role
Account B (secondary custody)
KMS Key 2 — key policy grants kms:Encrypt and kms:Decrypt to Account A signer role
Account C (tertiary custody)
KMS Key 3 — key policy grants kms:Encrypt and kms:Decrypt to Account A signer role

The terraform/examples/multi-account/ example uses provider aliases to create KMS keys in secondary accounts with cross-account key policies, and adds all key ARNs to the signer IAM role policy.

See the KMS Key Management guide for the full multi-account deployment sequence.

ExampleWhat it creates
terraform/examples/single-account/DynamoDB tables (keystore + antislashing), 3 KMS Shamir keys, table encryption CMKs, IAM role with instance profile — all in one account
terraform/examples/multi-account/Same resources, but KMS keys split across 3 AWS accounts using provider aliases with cross-account key policies

Each example includes a README with usage instructions and the matching signer config.yaml.