{"record":{"id":"0d127e9c5d1f7c98","repo":"kubernetes/kops","slug":"failed-to-get-grant-for-key-q-in-bucket-q-w","errorCode":null,"errorMessage":"failed to get grant for key %q in bucket %q: %w","messagePattern":"failed to get grant for key %q in bucket %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/pkg/vfs/s3fs.go","lineNumber":667,"sourceCode":"\t// return allowsAnonymousRead, nil\n}\n\nfunc (p *S3Path) IsPublic() (bool, error) {\n\tif p.scheme == \"linode\" {\n\t\t// Akamai (Linode) does not implement GetObjectAcl. In that case we conservatively treat the object as non-public and continue.\n\t\treturn false, nil\n\t}\n\tctx := context.TODO()\n\tclient, err := p.client(ctx)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tacl, err := client.GetObjectAcl(ctx, &s3.GetObjectAclInput{\n\t\tBucket: &p.bucket,\n\t\tKey:    &p.key,\n\t})\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to get grant for key %q in bucket %q: %w\", p.key, p.bucket, err)\n\t}\n\n\tfor _, grant := range acl.Grants {\n\t\tif aws.ToString(grant.Grantee.URI) == \"http://acs.amazonaws.com/groups/global/AllUsers\" {\n\t\t\treturn grant.Permission == types.PermissionRead, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n\ntype terraformS3File struct {\n\tBucket   string                   `json:\"bucket\" cty:\"bucket\"`\n\tKey      string                   `json:\"key\" cty:\"key\"`\n\tContent  *terraformWriter.Literal `json:\"content,omitempty\" cty:\"content\"`\n\tAcl      *string                  `json:\"acl,omitempty\" cty:\"acl\"`\n\tSSE      *string                  `json:\"server_side_encryption,omitempty\" cty:\"server_side_encryption\"`\n\tProvider *terraformWriter.Literal `json:\"provider,omitempty\" cty:\"provider\"`\n}","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/util/pkg/vfs/s3fs.go#L649-L685","documentation":"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.","triggerScenarios":"Calling IsPublic on an S3Path when GetObjectAcl fails: AccessDenied on the object, NoSuchKey (object deleted), or NoSuchBucket — anything except a successful ACL response.","commonSituations":"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.","solutions":["Grant s3:GetObjectAcl on the bucket/objects to the calling principal if the wrapped error is AccessDenied.","If ACLs are disabled (Object Ownership: Bucket owner enforced), use GetBucketPolicyStatus or GetObjectAttributes instead of ACL-based checks.","Verify the key exists (head the object) before the ACL check to rule out NoSuchKey.","Confirm bucket name and region if the wrapped error is NoSuchBucket."],"exampleFix":"// before (policy)\n{\"Action\":[\"s3:GetObject\"]}\n// after\n{\"Action\":[\"s3:GetObject\",\"s3:GetObjectAcl\"]}","handlingStrategy":"type-guard","validationCode":"// check key existence and ACL permission first\n_, err := client.HeadObject(ctx, &s3.HeadObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)})\nif err != nil { // NoSuchKey/AccessDenied — resolve before GetObjectAcl }\n// verify ACLs aren't disabled (Object Ownership: Bucket owner enforced)\nownership, _ := client.GetBucketOwnershipControls(ctx, &s3.GetBucketOwnershipControlsInput{Bucket: aws.String(bucket)})","typeGuard":"func aclAPIDisabled(ownership *s3.GetBucketOwnershipControlsOutput) bool {\n    return ownership != nil && ownership.OwnershipControls != nil &&\n        ownership.OwnershipControls.Rules[0].ObjectOwnership == types.ObjectOwnershipBucketOwnerEnforced\n}","tryCatchPattern":"isPublic, err := s3Path.IsPublic()\nif err != nil {\n    if vfs.AWSErrorCode(err) == \"AccessDenied\" || strings.Contains(err.Error(), \"ObjectOwnershipControls\") {\n        // ACLs disabled — audit via bucket policy instead of object ACL\n        return s3Path.IsBucketPublic()\n    }\n    return false, err\n}","preventionTips":["Grant s3:GetObjectAcl alongside s3:GetObject for any identity that audits object visibility.","Prefer policy-based checks (GetBucketPolicyStatus) when the bucket has ACLs disabled.","Head the object first to avoid confusing NoSuchKey with ACL failures.","Standardize on Bucket-owner-enforced ownership and policy-based public checks org-wide."],"tags":["aws","s3","iam","acl","security"],"backgroundTag":"s3-getobjectacl-access-denied","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}