✓ Human-authored analysis; AI used for formatting and proofreading.
Can an SCP require EKS clusters to use customer-managed KMS keys?
No. Because the information the SCP needs doesn't exist in the place the SCP can look.
The request context is incomplete
What happens when someone creates an EKS cluster with encryption:
aws eks create-cluster \
--name production \
--encryption-config '[{
"provider": {
"keyArn": "arn:aws:kms:us-east-1:123456789012:key/abc-123"
},
"resources": ["secrets"]
}]' \
...
The SCP sees this request context. It can evaluate:
- The action:
eks:CreateCluster - The key ARN:
arn:aws:kms:us-east-1:123456789012:key/abc-123 - The condition key:
eks:encryptionConfigProviderKeyArns
The SCP can deny if no key ARN is provided. That's straightforward:
{
"Sid": "DenyEKSWithoutEncryptionKey",
"Effect": "Deny",
"Action": "eks:CreateCluster",
"Resource": "*",
"Condition": {
"Null": {
"eks:encryptionConfigProviderKeyArns": "true"
}
}
}
But can the SCP deny if the provided key is AWS-managed instead of customer-managed?
No.
Look at the key ARN: arn:aws:kms:us-east-1:123456789012:key/abc-123. It's a string. There's nothing in the ARN that says "customer-managed" or "AWS-managed." Both types have identical ARN formats. The distinction lives in the KMS key's metadata. Specifically the KeyManager field which is stored on the key object in KMS, not in the request to create an EKS cluster.
The SCP evaluates the request. The answer is in the key's metadata. The SCP can't cross that boundary.
The workaround is a governance band-aid
The community's best workaround: enforce a naming convention on key aliases, then write an SCP that denies if the key alias doesn't match the convention.
{
"Sid": "DenyGrantOnAWSManagedEKSKey",
"Effect": "Deny",
"Action": "kms:CreateGrant",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "eks.us-east-1.amazonaws.com"
},
"ForAnyValue:StringLike": {
"kms:ResourceAliases": "alias/aws/eks"
}
}
}
This blocks the AWS-managed key specifically (alias/aws/eks). It works for the narrow case of blocking the default AWS-managed key.
But it doesn't prove the key that is used is customer-managed. It proves the key isn't the specific AWS-managed key with the alias/aws/eks alias. If someone creates a key with a different alias, or no alias, or an alias that matches your naming convention but is an AWS-managed key from another service, the SCP passes it through.
The workaround works when everyone follows the naming convention. "When everyone follows the convention" is the security equivalent of "when everyone remembers to lock the door." It's the same rubber-stamping pattern SOC 2 auditors keep catching. The control looks correct on paper, the underlying property isn't verified.
The snapshot contains the truth
Here's what the KMS key looks like in a configuration snapshot, the actual key object:
{
"KeyId": "arn:aws:kms:us-east-1:123456789012:key/abc-123",
"KeyManager": "CUSTOMER",
"Origin": "AWS_KMS",
"KeyState": "Enabled",
"KeyUsage": "ENCRYPT_DECRYPT",
"Description": "EKS secrets envelope encryption",
"CreationDate": "2025-03-15T10:30:00Z",
"Enabled": true
}
Here's the EKS cluster's encryption configuration in the same snapshot:
{
"ClusterName": "production",
"EncryptionConfig": [
{
"Provider": {
"KeyArn": "arn:aws:kms:us-east-1:123456789012:key/abc-123"
},
"Resources": ["secrets"]
}
],
"Status": "ACTIVE"
}
The KeyManager field is right there. "CUSTOMER" or "AWS". Binary. No naming convention needed. No alias enforcement or workaround. The snapshot contains the truth about the key — not the request's claim about the key, but the key's own metadata.
A snapshot-based verification tool joins the two:
- Read the EKS cluster's
EncryptionConfig.Provider.KeyArn - Look up that ARN in the KMS key inventory
- Check
KeyManager == "CUSTOMER" - If
KeyManager == "AWS": FAIL - If
KeyManager == "CUSTOMER": PASS - If no
EncryptionConfigat all: FAIL (no encryption configured) - If the key ARN doesn't resolve (key deleted, cross-account, or missing from snapshot): FAIL (can't verify — fail loud, don't assume)
Deterministic. Same answer every time. No convention to follow or alias to enforce. The property is verified against the configuration state, not inferred from the request.
Why this gap is structural, not accidental
The SCP limitation isn't a bug. It's a consequence of where SCPs operate in the architecture.
SCPs evaluate request context at API call time. The request context contains what the caller sent. The action, resource ARN, tags the caller attached and the condition keys AWS defined for that API. It does not contain the metadata of resources the request references. When you create an EKS cluster and specify a KMS key ARN, the SCP sees the ARN string. It does not query KMS to check what kind of key that ARN points to. That cross-service metadata lookup doesn't happen during policy evaluation.
This is by design. SCPs need to evaluate in microseconds. A cross-service lookup (call KMS to check KeyManager for every key ARN in every request) would add latency, create circular dependencies, and introduce failure modes. AWS made the right engineering decision. The SCP evaluates what's in front of it, fast.
But that means any property that requires joining data from two services such as the EKS cluster's encryption config AND the KMS key's metadata is structurally invisible to the SCP. The SCP can see either one in isolation. It can't join them.
A configuration snapshot can. The snapshot contains both services' configuration at the same point in time. The join is a lookup, not a cross-service API call. The EKS cluster says "I use key ARN X." The KMS inventory says "key ARN X has KeyManager: AWS." The verification engine reads both and produces the verdict.
The pattern generalizes
EKS encryption is one instance. The same structural gap applies anywhere a preventive policy needs to verify a property that lives in a different service's metadata:
- S3 bucket encryption — is the KMS key customer-managed or AWS-managed?
- RDS encryption — same question for database encryption keys
- EBS volume encryption — is the default EBS encryption key a CMK?
- Lambda function environment encryption — is the KMS key attached to the function a CMK?
-
Secrets Manager encryption — is the secret encrypted with a CMK or the default
aws/secretsmanagerkey? - SQS/SNS encryption — is the queue/topic using a CMK?
Every one of these requires joining the resource's configuration with the KMS key's metadata. Every one of these is invisible to an SCP for the same structural reason. Every one of these is a deterministic check against a configuration snapshot.
The pattern: SCPs verify properties within the request context. Snapshots verify properties across the configuration graph. Both are needed. SCPs prevent. They block requests that violate policy. Snapshots verify. They prove the configuration satisfies the property after deployment. The SCP catches the violation at creation time (when it can see enough to evaluate). The snapshot catches the violation at any time (because it can see everything).
The gap between prevention and verification
Most organizations treat SCPs as the security mechanism and stop there. "We have an SCP that requires encryption keys." The property they think they've verified: EKS clusters use customer-managed keys. The property they've verified: EKS clusters provide a key ARN. Whether that key ARN points to a customer-managed key is unverified. Because the SCP structurally can't check it.
This is the gap between prevention and verification. Prevention says "I blocked the bad request." Verification says "the resulting configuration satisfies the property." Prevention operates at request time with partial context. Verification operates on the full configuration with complete context. Prevention is necessary. Verification proves the property holds.
Stave is an open-source tool that verifies cloud configuration against declared invariants using three formal reasoning engines (CEL, Z3, Soufflé). It reads configuration snapshots without any credentials and joins data across services to verify properties that per-service or per-request controls structurally cannot see. The EKS/KMS check described above is one of 3,000+ controls in the catalog.
Top comments (0)