Sitelet https://github.com/kubernetes/kops/commit/6c1cd053e2537a8b69a5584cdb6556a18992a6a6
Skip to content

Commit 6c1cd05

Browse files
committed
aws: tighten KMS policy comments and release note
1 parent a4ea3c9 commit 6c1cd05

3 files changed

Lines changed: 18 additions & 34 deletions

File tree

‎docs/releases/1.37-NOTES.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ As part of this removal, the `protokube` component, whose only remaining respons
3030

3131
* On AWS, `ec2:DescribeRegions` is no longer granted for etcd-manager, which reads the region from the instance metadata service instead of calling the EC2 API.
3232

33-
* On AWS, the KMS permissions granted for using customer managed keys (etcd volume encryption, EBS CSI volumes, Karpenter root volumes, and the state store) are now conditional: `kms:CreateGrant` is limited to grants created by an AWS service on the role's behalf (`kms:GrantIsForAWSResource`), and the remaining KMS actions are limited to calls made through EC2 and S3 (`kms:ViaService`). When `spec.encryptionConfig` is enabled, the control-plane roles keep unconditional access to the encrypt and decrypt actions so that a kms-plugin can call KMS directly. Workloads that call other KMS APIs directly under an instance role, including `kms:CreateGrant`, need to be granted access through `spec.additionalPolicies` or a service account IAM role.
33+
* On AWS, the KMS permissions for customer managed keys now restrict `kms:CreateGrant` to grants created by AWS services on the role's behalf, and the remaining KMS actions to calls made through EC2 or S3. With `spec.encryptionConfig` enabled, control-plane roles keep unconditional access to the data actions so a kms-plugin can call KMS directly. Other direct KMS calls under an instance role, including `kms:CreateGrant`, require `spec.additionalPolicies` or a service account IAM role.
3434

3535
* On AWS, instance roles that require no permissions, such as the bastion role and the default worker node role, no longer have an inline IAM policy. `kops update cluster` deletes the previously created inline policy from such roles. With the Terraform target, the corresponding `aws_iam_role_policy` resources are removed from the configuration and destroyed on the next apply.
3636

‎pkg/model/iam/iam_builder.go‎

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ func (p *Policy) AddEC2CreateAction(actions, resources []string) {
113113
// document as "no inline policy": it skips creating one and deletes one that already exists.
114114
func (p *Policy) AsJSON() (string, error) {
115115
if p.kmsAWSResourceGrant {
116-
// kms:GrantIsForAWSResource only matches when an AWS service integrated
117-
// with KMS creates the grant on the principal's behalf, so this cannot
118-
// be used to hand out grants on arbitrary keys directly.
116+
// kms:GrantIsForAWSResource only matches grants created by an AWS service on the principal's
117+
// behalf, so this cannot be used to delegate access to arbitrary keys directly.
119118
p.Statement = append(p.Statement, &Statement{
120119
Effect: StatementEffectAllow,
121120
Action: stringorset.String("kms:CreateGrant"),
@@ -1467,17 +1466,12 @@ func AddKubeRouterPermissions(b *PolicyBuilder, p *Policy) {
14671466
)
14681467
}
14691468

1470-
// addKMSIAMPolicies grants the KMS permissions needed to use customer managed
1471-
// keys for EBS volume encryption (etcd volumes, EBS CSI volumes, Karpenter
1472-
// root volumes) and SSE-KMS on the state store. In all of these flows an AWS
1473-
// service makes the KMS calls on the role's behalf: EC2 creates the grant that
1474-
// authorizes it to use the volume's key and performs the data-plane operations,
1475-
// so kms:CreateGrant is guarded with kms:GrantIsForAWSResource and everything
1476-
// else with kms:ViaService.
1469+
// addKMSIAMPolicies grants the KMS permissions for customer managed keys used for EBS encryption
1470+
// (etcd, EBS CSI, and Karpenter root volumes) and state store SSE-KMS. AWS services make the KMS
1471+
// calls on the role's behalf, so kms:CreateGrant requires kms:GrantIsForAWSResource and all other
1472+
// actions require kms:ViaService.
14771473
func addKMSIAMPolicies(p *Policy, bypassViaService bool) {
1478-
// Grants are only ever created by EC2 for EBS encryption; even a direct
1479-
// KMS client like a kms-plugin sidecar (bypassViaService) never calls
1480-
// CreateGrant itself.
1474+
// Even a kms-plugin calling KMS directly (bypassViaService) never creates grants; only EC2 does.
14811475
p.kmsAWSResourceGrant = true
14821476

14831477
dataActions := []string{
@@ -1488,17 +1482,10 @@ func addKMSIAMPolicies(p *Policy, bypassViaService bool) {
14881482
"kms:ReEncrypt*",
14891483
}
14901484

1491-
// bypassViaService is set by callers whose role may need to talk to KMS
1492-
// without an AWS service in the call chain -- currently only the API server
1493-
// role when EncryptionConfig is enabled, since a user-supplied kms-plugin
1494-
// sidecar wired to the instance role calls KMS directly from kube-apiserver.
1495-
// In that mode kms:ViaService never matches and the conditional grant
1496-
// would deny every encrypt/decrypt of etcd data.
1497-
//
1498-
// An empty region also falls back to unconditional grants to preserve
1499-
// existing behavior for callers (e.g. unit tests) that have not populated
1500-
// PolicyBuilder.Region; kmsViaServices needs the region to build the
1501-
// service endpoint strings.
1485+
// bypassViaService is set for control-plane roles when EncryptionConfig is enabled: a
1486+
// user-supplied kms-plugin then calls KMS directly from kube-apiserver, where kms:ViaService
1487+
// never matches. An empty region also falls back to unconditional actions, as kmsViaServices
1488+
// needs the region to build the service endpoint strings.
15021489
if bypassViaService || p.region == "" {
15031490
p.unconditionalAction.Insert(dataActions...)
15041491
return

‎pkg/model/iam/iam_builder_test.go‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,8 @@ func TestEmptyPolicy(t *testing.T) {
309309
}
310310

311311
func TestAddKMSIAMPolicies(t *testing.T) {
312-
// The complete policy rendered when kms:ViaService applies: CreateGrant is
313-
// only allowed for grants created by an AWS service on the role's behalf,
314-
// and all other KMS actions only through EC2 or S3.
312+
// The full rendering when kms:ViaService applies: grants only via an AWS service, data-plane
313+
// actions only through EC2 or S3.
315314
wantConditional := `{
316315
"Statement": [
317316
{
@@ -346,10 +345,9 @@ func TestAddKMSIAMPolicies(t *testing.T) {
346345
],
347346
"Version": "2012-10-17"
348347
}`
349-
// With bypassViaService (or no region to build the ViaService values from)
350-
// the data-plane actions are unconditional, but CreateGrant keeps the
351-
// GrantIsForAWSResource guard: even a kms-plugin sidecar calling KMS
352-
// directly never creates grants itself.
348+
// With bypassViaService (or no region to build ViaService values from), the data-plane actions
349+
// are unconditional; CreateGrant keeps the GrantIsForAWSResource guard, as even a direct KMS
350+
// client never creates grants.
353351
wantBypass := `{
354352
"Statement": [
355353
{
@@ -467,8 +465,7 @@ func TestAddKarpenterPermissions(t *testing.T) {
467465
t.Fatalf("unexpected error: %v", err)
468466
}
469467

470-
// Render the policy so the synthesized KMS statements can be
471-
// checked in full, including their resources and conditions.
468+
// The KMS statements are synthesized in AsJSON, so render before checking them.
472469
rendered, err := p.AsJSON()
473470
if err != nil {
474471
t.Fatalf("failed to render policy: %v", err)

0 commit comments

Comments
 (0)