Key policies¶
Both KMS keys carry a policy that is the single source of permission grants. Neither IAM role has an attached identity policy, so there is exactly one place to read to know who can do what.
Four principal classes¶
Each policy grants the same four classes, mirroring the sibling signing module:
| Class | Gets | Explicitly does not get |
|---|---|---|
| account root | kms:* |
— |
key_administrator_arns |
key lifecycle: describe, enable/disable, tag, aliases | kms:Sign, kms:DeriveSharedSecret, kms:PutKeyPolicy |
automation_role_arn |
manage the key as a Terraform resource | kms:Sign, kms:DeriveSharedSecret |
| the reader / certifier role | exactly one crypto action each | the other one |
The separation that matters most is the third row. The apply pipeline can create, tag and re-policy these keys, and cannot use them. A full compromise of the runner yields no ability to decrypt a message or rebind the certificate.
Why account root keeps kms:*¶
This looks like an over-broad grant and is deliberate. It is AWS's recommended break-glass provision, and it is what guarantees the key can never become unmanageable — a key whose policy has locked out every administrator is unrecoverable in a way that no amount of care prevents.
It is scoped to the root principal specifically, rather than the whole account, which is a narrower grant than the AWS default "Enable IAM policies" statement.
Why there is no Deny¶
A tempting hardening is an explicit Deny with NotPrincipal, fencing
kms:DeriveSharedSecret to the reader role so that no future policy edit can
widen it.
It was considered and rejected. NotPrincipal combined with Deny on a KMS
key policy is a well-known way to brick a key irrecoverably — the statement
denies everyone not listed, including principals you did not think about, and
the fix requires a principal the policy now denies. The estate convention, and
AWS's guidance, is to retain account-root kms:* and keep policies allow-only.
Defence in depth is not worth a key that can lock out its own owner. Especially this key, where the failure mode is an unreadable archive.
Why resources = "*"¶
Every statement uses resources = ["*"], which security scanners flag. In a
key policy that wildcard resolves to the single key the policy is attached
to — key policies are resource-based, not identity-based, so there is no
"arbitrary resources" reading available.
The module carries checkov:skip annotations with that justification inline
rather than suppressing the checks globally, so a reviewer sees the reasoning
at the statement rather than in a config file somewhere else.
Administrator deletion rights¶
kms:ScheduleKeyDeletion is withheld from administrators unless
allow_administrator_key_deletion is set — AWS's named control for accidental
key deletion is the key policy, and deleting an asymmetric encryption key is
the one mistake here that cannot be undone.
kms:DeleteAlias is granted regardless, because aliases are cheap to recreate.
kms:CancelKeyDeletion is always granted because cancelling is protective
rather than destructive.
[!NOTE] Administrator actions are enumerated explicitly rather than by wildcard, and
kms:PutKeyPolicyis not among them — so an administrator cannot rewrite the policy to grant themselves deletion. The sibling signing module'skms:Put*would have permitted exactly that; a wildcard granting the power to rewrite the grant is not a guardrail.The control is still not absolute: the account root holds
kms:*for break-glass, and the automation role can change the policy through a reviewed plan/apply.
A caveat about testing¶
These policies are reviewed but, at the time of writing, unexercised against
real AWS. Nothing in the module's gate can catch a policy that is too narrow:
tofu validate checks shape, and checkov and trivy check for grants that are
too broad. A role that cannot perform the operation it exists for passes all
three.
One such defect was found by reading rather than by testing — see roles and trust. Expect the first real use to be the first genuine exercise of these policies, and treat a permissions error as a likely module bug rather than a KMS limitation.
See also¶
- IAM permissions reference — the exact statements.
- Rotation, retention and deletion