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.
Two features 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, protected by AWS KMS
Required Permissions
Section titled “Required Permissions”The DynamoDB anti-slashing backend uses transactions for atomicity. It never uses BatchWriteItem or BatchGetItem.
| Action | Purpose |
|---|---|
dynamodb:DescribeTable | Table existence check at startup |
dynamodb:GetItem | Read watermarks and signing roots |
dynamodb:PutItem | Write watermark updates |
dynamodb:TransactGetItems | Atomic multi-item reads |
dynamodb:TransactWriteItems | Atomic 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: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 key source needs two sets of permissions: DynamoDB for key storage, and KMS for decryption.
DynamoDB:
| Action | Purpose |
|---|---|
dynamodb:DescribeTable | Table existence check at startup |
dynamodb:GetItem | Read individual validator keys |
dynamodb:PutItem | Store new validator keys |
dynamodb:Query | Load keys via GSI (parallel shard scan) |
KMS (steady state):
| Action | Purpose |
|---|---|
kms:Decrypt | Decrypt Shamir shares to reconstruct the master key |
kms:DescribeKey | Verify key availability at startup |
The index/* resource is required for dynamodb:Query on the GSI. The table ARN alone is not sufficient.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "KMSAccess", "Effect": "Allow", "Action": [ "kms:Decrypt", "kms:DescribeKey" ], "Resource": [ "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID_1", "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID_2", "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID_3" ] }, { "Sid": "DynamoDBAccess", "Effect": "Allow", "Action": [ "dynamodb:DescribeTable", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query" ], "Resource": [ "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/TABLE_NAME", "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/TABLE_NAME/index/*" ] } ]}The terraform/examples/ configurations create the DynamoDB table, KMS keys, IAM role, and wire up all the resource ARNs automatically.
First Boot Permissions
Section titled “First Boot Permissions”On first boot, the signer generates a random master key, splits it into N Shamir shares (one per KMS key ARN), and encrypts each share with its corresponding KMS key. This requires kms:Encrypt.
On every subsequent boot, the signer reads the encrypted shares from DynamoDB and decrypts at least kms_threshold of them to reconstruct the master key. This only needs kms:Decrypt.
kms:Encrypt is granted in the KMS key policy, not the IAM role policy. The Terraform module handles this automatically. Your signer role’s IAM policy only ever holds kms:Decrypt and kms:DescribeKey.
KMS Key Policy
Section titled “KMS Key Policy”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", "kms:DescribeKey"], "Resource": "*" } ]}For KMS keys in a secondary account, the key policy grants decrypt to the primary signer role and encrypt to a setup role:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "EnableIAMDelegation", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::SECONDARY_ACCOUNT_ID:root" }, "Action": "kms:*", "Resource": "*" }, { "Sid": "AllowSignerDecrypt", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::PRIMARY_ACCOUNT_ID:role/SIGNER_ROLE_NAME" }, "Action": ["kms:Decrypt", "kms:DescribeKey"], "Resource": "*" }, { "Sid": "AllowSetupEncrypt", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::PRIMARY_ACCOUNT_ID:role/SETUP_ROLE_NAME" }, "Action": ["kms:Encrypt", "kms:DescribeKey"], "Resource": "*" } ]}If you don’t have a separate setup role, use the signer role ARN for both.
IRSA Setup (EKS)
Section titled “IRSA Setup (EKS)”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: v1kind: ServiceAccountmetadata: name: containment-chamber namespace: validators annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAMETrust 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.
Cross-Account KMS
Section titled “Cross-Account KMS”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.
Account A (primary, runs signer) KMS Key 1 + DynamoDB table + IAM signer role
Account B (secondary custody) KMS Key 2 — key policy grants kms:Decrypt + kms:DescribeKey to Account A signer role
Account C (tertiary custody) KMS Key 3 — key policy grants kms:Decrypt + kms:DescribeKey to Account A signer roleThe 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.
Terraform Modules
Section titled “Terraform Modules”| Example | What 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.