How to enforce a CMK on almost all of a bucket, but allow SSE-S3 for a specific prefix?

0

I'm looking at a bucket that has a default SSE-KMS customer-managed key (CMK), and was previously misconfigured with a resource policy that was intended to require the use of that CMK for every PutObject, but was coded with an incorrect (root) principal, and so never enforced that rule.

Thankfully, nearly all users of that bucket just let the encryption default. But... I've discovered that one prefix in that bucket is used as a destination for Redshift UNLOAD commands, which cannot use a CMK; it uses SSE-S3.

Is it possible to write a rule that:

  • Requires the CMK for every principal and every object except that prefix,
  • Does not require encryption headers on the actual PutObject request (e.g. aws cp foobar s3://my_bucket/not-redshift/foobar should encrypt with the default key with no difficulty)
  • and preferably requires SSE-S3 for every principal and every object that is named with that prefix ?

Or do I need to split this prefix out onto its own bucket to get that behavior?

Just so nobody runs into these dead-ends:

  • PutObject does not support conditions based on the s3:prefix attribute.
  • While default encryption provides a virtual s3:x-amz-server-side-encryption-aws-kms-key-id value that can be used in resource policy conditions, it does not provide a virtual x-amz-server-side-encryption value, AFAICT.
2개 답변
0

Have you tried with a Service Control Policy and add Condition to not apply to a specific path? Just an idea as I haven’t tested that approach.

profile picture
전문가
답변함 7달 전
  • Thanks! I might have gone down that route, but all I needed in the end was NotResource.

0

I got help from AWS Support on this (thanks, Manish!). The trick is the NotResource clause, which I'd never noticed in the documentation before:

        {
            "Sid": "DenyWrongKey",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "NotResource": "arn:aws:s3:::MYBUCKET/redshift_exports/*",
            "Condition": {
                "ArnNotEquals": {
                    "s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:REGION:ACCOUNT:key/KEY_ID"
                }
            }
        },
        {
            "Sid": "RequireRedshiftBehaviorInRedshiftPrefix",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::MYBUCKET/redshift_exports/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption": "AES256"
                }
            }
        },

This does exactly what we wanted:

  1. If the PutObject is inside the Redshift prefix, it must have the behavior redshift does.
  2. If it's outside the Redshift prefix, it must be encrypted with the default CMK for this bucket.

Of course, having to use 3 NOTs to make an assertion isn't exactly intuitive (Deny, NotResource, ArnNotEquals). It also doesn't generalize well for default cases; e.g., it's hard to write a rule that says "if the x-amx-acl argument exists, it must have value 'bucket-owner-full-control'", because ...IfExists doesn't work in DENY. The ArnNotEquals works above because in the default case, S3 still acts like that header was provided.

답변함 7달 전

로그인하지 않았습니다. 로그인해야 답변을 게시할 수 있습니다.

좋은 답변은 질문에 명확하게 답하고 건설적인 피드백을 제공하며 질문자의 전문적인 성장을 장려합니다.

질문 답변하기에 대한 가이드라인

관련 콘텐츠