kubernetes/kops · error

failed to get grant for key %q in bucket %q: %w

Error message

failed to get grant for key %q in bucket %q: %w

What it means

S3Path.IsPublic inspects the object ACL via GetObjectAcl to see whether the AllUsers group has READ permission. This error wraps any failure of that call — most commonly the caller lacks s3:GetObjectAcl, or the key/bucket doesn't exist. The AWS error is embedded via %w.

Source

Thrown at util/pkg/vfs/s3fs.go:667

	// return allowsAnonymousRead, nil
}

func (p *S3Path) IsPublic() (bool, error) {
	if p.scheme == "linode" {
		// Akamai (Linode) does not implement GetObjectAcl. In that case we conservatively treat the object as non-public and continue.
		return false, nil
	}
	ctx := context.TODO()
	client, err := p.client(ctx)
	if err != nil {
		return false, err
	}
	acl, err := client.GetObjectAcl(ctx, &s3.GetObjectAclInput{
		Bucket: &p.bucket,
		Key:    &p.key,
	})
	if err != nil {
		return false, fmt.Errorf("failed to get grant for key %q in bucket %q: %w", p.key, p.bucket, err)
	}

	for _, grant := range acl.Grants {
		if aws.ToString(grant.Grantee.URI) == "http://acs.amazonaws.com/groups/global/AllUsers" {
			return grant.Permission == types.PermissionRead, nil
		}
	}
	return false, nil
}

type terraformS3File struct {
	Bucket   string                   `json:"bucket" cty:"bucket"`
	Key      string                   `json:"key" cty:"key"`
	Content  *terraformWriter.Literal `json:"content,omitempty" cty:"content"`
	Acl      *string                  `json:"acl,omitempty" cty:"acl"`
	SSE      *string                  `json:"server_side_encryption,omitempty" cty:"server_side_encryption"`
	Provider *terraformWriter.Literal `json:"provider,omitempty" cty:"provider"`
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Grant s3:GetObjectAcl on the bucket/objects to the calling principal if the wrapped error is AccessDenied.
  2. If ACLs are disabled (Object Ownership: Bucket owner enforced), use GetBucketPolicyStatus or GetObjectAttributes instead of ACL-based checks.
  3. Verify the key exists (head the object) before the ACL check to rule out NoSuchKey.
  4. Confirm bucket name and region if the wrapped error is NoSuchBucket.

Example fix

// before (policy)
{"Action":["s3:GetObject"]}
// after
{"Action":["s3:GetObject","s3:GetObjectAcl"]}
Defensive patterns

Strategy: type-guard

Validate before calling

// check key existence and ACL permission first
_, err := client.HeadObject(ctx, &s3.HeadObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)})
if err != nil { // NoSuchKey/AccessDenied — resolve before GetObjectAcl }
// verify ACLs aren't disabled (Object Ownership: Bucket owner enforced)
ownership, _ := client.GetBucketOwnershipControls(ctx, &s3.GetBucketOwnershipControlsInput{Bucket: aws.String(bucket)})

Type guard

func aclAPIDisabled(ownership *s3.GetBucketOwnershipControlsOutput) bool {
    return ownership != nil && ownership.OwnershipControls != nil &&
        ownership.OwnershipControls.Rules[0].ObjectOwnership == types.ObjectOwnershipBucketOwnerEnforced
}

Try / catch

isPublic, err := s3Path.IsPublic()
if err != nil {
    if vfs.AWSErrorCode(err) == "AccessDenied" || strings.Contains(err.Error(), "ObjectOwnershipControls") {
        // ACLs disabled — audit via bucket policy instead of object ACL
        return s3Path.IsBucketPublic()
    }
    return false, err
}

Prevention

When it happens

Trigger: Calling IsPublic on an S3Path when GetObjectAcl fails: AccessDenied on the object, NoSuchKey (object deleted), or NoSuchBucket — anything except a successful ACL response.

Common situations: Security scans checking state-file ACLs with an IAM role that has GetObject but not GetObjectAcl; buckets with Object Ownership set to 'Bucket owner enforced' (ACLs disabled) causing API failures; audited keys that were since deleted.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/0d127e9c5d1f7c98. Report an issue: GitHub.