AWS KMS Key Manager
The AWS KMS Key Manager stores the Trust Domain Server's signing keys in AWS Key Management Service (KMS) rather than in a Kubernetes CRD. This provides HSM-backed key storage, centralized key management, and CloudTrail audit logging of all signing operations.
How it works
On startup, the Trust Domain Server discovers its signing keys in KMS using reserved tags applied at key creation time. New keys are created during key rotation, not on startup. Cryptographic operations (signing, key generation) are performed inside KMS; the private key material never leaves AWS infrastructure.
How to Deploy
The instructions below are suitable for new installations or migrations between key manager backends.
Migration between key manager backends is a zero-downtime operation that relies on the automatic background key rotation built into Defakto. The timeline to full migration depends on your key rotation schedule and configured SVID TTL.
Applying a KeyManager configuration tells the Trust Domain Server which key manager to use when it generates the next key set during its automatic rotation. Applying the configuration does not trigger an early rotation, so the AWS KMS Key Manager does not take effect until the next scheduled rotation. See the Signing Key Rotation Runbook to trigger a rotation manually.
Step 1 — Set Up AWS IAM
The Trust Domain Server needs an IAM role with the following permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:CreateKey",
"kms:GetPublicKey",
"kms:ScheduleKeyDeletion",
"kms:Sign",
"kms:TagResource"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"tag:GetResources"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}
KMS authorizes an operation only when both the caller's IAM policy and the key's own key policy allow it. The policy above covers the IAM side. See Configuration reference for the key policies attached to the keys the Trust Domain Server creates.
AWS Service Control Policies can be set at an Organizational Unit (OU) level and can interfere with the permissions granted to the Trust Domain Server's IAM role. Ensure that your company's Service Control Policies allow the necessary KMS actions for the role.
For EKS clusters running the EKS Pod Identity Agent add-on, create a pod identity association between this role and the Trust Domain Server's Kubernetes service account. If you add the pod identity association after the server is already deployed, run kubectl rollout restart on the Deployment for the AWS changes to take effect.
For other EKS clusters, use IRSA (IAM Roles for Service Accounts) to associate this role with the Trust Domain Server's Kubernetes service account.
Step 2 — Update cluster configuration
This configuration document enables the AWS KMS Key Manager (see Configuration reference for more settings):
section: KeyManager
schema: v1
spec:
extensions:
awsKMS:
region: us-east-1
Apply it using spirlctl:
spirlctl config set trust-domain-deployment --id <deployment-id> key-manager.yaml
Or using Terraform:
resource "spirl_trust_domain_deployment_config" "key_manager" {
trust_domain_deployment_id = spirl_trust_domain_deployment.example.id
sections = {
KeyManager = <<-YAML
section: KeyManager
schema: v1
spec:
extensions:
awsKMS:
region: us-east-1
YAML
}
}
Once a configuration document passes validation and is stored, the Defakto control plane syncs it to your Trust Domain Servers automatically. No server or agent restart is required.
Step 3 — Verify
New installations
Check CloudTrail for CreateKey events to confirm keys are being generated in KMS.
Migrations
The existing keys continue to handle all signing operations until the next rotation, when the Trust Domain Server creates the new key set in KMS. Agents and workloads are not impacted during the transition.
To validate the configuration without waiting, use the Signing Key Rotation Runbook to manually force a key rotation. Otherwise, the new configuration is used at the next scheduled key rotation.
Security Considerations
- Key material never leaves KMS. All signing is performed via KMS API calls. Private key bytes are not accessible to the Trust Domain Server process.
- CloudTrail audit. Every
kms:Signcall appears in CloudTrail with the caller identity, timestamp, and key ID. - Automatic key cleanup. Defakto automatically schedules unused keys for deletion in KMS. Orphaned keys are cleaned up every 48 hours, and stale keys from decommissioned deployments are removed every 24 hours.
- Region availability. The Trust Domain Server depends on KMS availability in the configured region. Plan for cross-region failover if required.
Configuration reference
The following settings apply to the awsKMS configuration block:
| Field | Required | Default | Description |
|---|---|---|---|
region | No | AWS SDK default (local region) | AWS region where KMS keys will be created and stored. |
keyPolicyFile | No | Auto-generated from Trust Domain Server IAM role | Path to a custom KMS key policy JSON file, accessible from the Trust Domain Server. |
keyTags | No | None | User-defined tags applied to all KMS keys created by the Trust Domain Server. Tag keys cannot use the aws: or defakto- prefixes. |
The examples below show the key policies applied to the KMS keys the Trust Domain Server creates.
Auto-generated key policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCallerAccess",
"Effect": "Allow",
"Principal": {
"AWS": "<TRUST DOMAIN SERVER ROLE ARN>"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowKMSConsoleDisplay",
"Effect": "Allow",
"Principal": {
"AWS": "<ROOT USER ARN>"
},
"Action": [
"kms:Describe*",
"kms:List*",
"kms:Get*"
],
"Resource": "*"
}
]
}
Minimal key policy
The minimal key policy below does not allow any external management or visibility of keys outside the Trust Domain Server. It shows only the minimum set of operations required.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCallerAccess",
"Effect": "Allow",
"Principal": { "AWS": "<TRUST DOMAIN SERVER ROLE ARN>" },
"Action": [
"kms:GetPublicKey",
"kms:Sign",
"kms:TagResource",
"kms:ScheduleKeyDeletion",
"kms:PutKeyPolicy"
],
"Resource": "*"
}
]
}
To use a custom key policy, create a Kubernetes ConfigMap holding the policy JSON and mount it into the Trust Domain Server using these Helm chart values:
trustDomainDeployment.deployment.additionalVolumestrustDomainDeployment.deployment.additionalVolumeMounts
An example values.yaml for a ConfigMap named td-key-policy with the policy stored under the key key-policy.json:
trustDomainDeployment:
# Other values...
deployment:
additionalVolumes:
- name: td-key-policy
configMap:
name: td-key-policy
additionalVolumeMounts:
- name: td-key-policy
mountPath: "/etc/spirl/key-policy"
readOnly: true
Then set keyPolicyFile to the mounted path in the awsKMS configuration block:
section: KeyManager
schema: v1
spec:
extensions:
awsKMS:
keyPolicyFile: "/etc/spirl/key-policy/key-policy.json"
Tag reference
The following tags are applied to KMS resources created by the Trust Domain Server. These tags are reserved. Removing or modifying them prevents the Trust Domain Server from discovering its keys.
| Tag | Description |
|---|---|
defakto-mint-server/managed | Set to true for all KMS keys managed by the Trust Domain Server. |
defakto-mint-server/trust-domain-id | The Defakto ID of the trust domain associated with the KMS key. |
defakto-mint-server/trust-domain-deployment-id | The Defakto deployment ID of the trust domain server associated with the KMS key. |
defakto-mint-server/key-id | The Defakto ID of the KMS key. |
defakto-mint-server/last-heartbeat | The timestamp of the last heartbeat from the Trust Domain Server for the KMS key. Heartbeats are performed every 6 hours. |