How to Create a Policy That Whitelists Access to Sensitive Amazon S3 Buckets

When it comes to securing access to your Amazon S3 buckets, AWS provides various options. You can utilize access control lists (ACLs), AWS Identity and Access Management (IAM) user policies, and S3 access policies. Even within S3 access policies, you have options to consider. You can use the Principal element, which allows you to utilize the default-deny capabilities of the policy language to grant access to, for example, a list of AWS accounts. There is also an often-overlooked “sibling” to the Principal element, the NotPrincipal element, which enables more-granular whitelisting. The NotPrincipal element allows you to ensure explicitly that no one—except a few select users—has access to a specific resource.

In this blog post, I will demonstrate how to create an S3 access policy that uses the NotPrincipal element to whitelist access to sensitive S3 buckets.

The Principal element

Before, I dive into a use case that will show the NotPrincipal element at work, I will first explain the Principal element.

The Principal element specifies the user, account, service, or other entity that is allowed or denied access to a resource. It is used in the trust policies for IAM roles and in resource-based policies—that is, in policies that can be attached directly to a resource, such as an S3 bucket or an Amazon SQS queue.

The Principal element is not used in policies that you attach to IAM users and groups. Similarly, in the access policy for an IAM role, you do not specify a principal. In those cases, the principal is implicitly the user that the policy is attached to (for IAM users) or the user who assumes the role (for role access policies). If the policy is attached to an IAM group, the principal is the member of the group who is making the request.

How to use the NotPrincipal element

The NotPrincipal element lets you specify an exception to a list of principals. For example, you can use this element to allow all AWS accounts except a specific account to access a resource. Conversely, you can deny access to all principals except the one named in the NotPrincipal element. As with the Principal element, you specify the user or account that should be allowed or denied permission. The difference is that the NotPrincipal element applies to everyone except that person or account. When used in conjunction with an IAM user policy that also explicitly allows that entity access to the specific resources, the NotPrincipal element can help ensure that only necessary parties can access the sensitive information within an S3 bucket.

One use case that demonstrates the effectiveness of the NotPrincipal element is the creation of a centralized credential store within S3. When you want to store credentials in a centralized store, you need to ensure that you can protect the credentials from misuse. S3 provides a number of these capabilities natively. This post will not explain in detail how to configure the following capabilities, but we recommend enabling:

It is also a best practice to access the bucket only via an encrypted channel such as HTTPS, which can also be enforced via an S3 bucket policy.

After the bucket has been created and properly configured, the organization needs to start thinking about the IAM roles necessary to operate and utilize this new credential store. For the purpose of this use case, we will be creating the following two levels of access:

  • Credential manager
  • Credential user

The credential manager role will have read and write access into the bucket to ensure that he can place new credentials or key files in the bucket. However, the credential user will have only read access to specific bucket directories. For purposes of this blog post, I have given the credential manager access to all of the subdirectories (i.e., prefixes) in the credential bucket. If you are looking for more granular control, the credential manager’s permissions can also be confined to specific subdirectories. Another optional configuration for the credential manager role is the requirement that the role authenticate via multi-factor authentication (MFA).

To begin writing the S3 resource policy, we first have to create a statement that allows both the credential manager (CredMgr) and credential user (CredUsr) to be able to see the credential bucket (CredentialBucket). We will be using a Deny statement along with the NotPrincipal element to ensure that only the individuals specifically listed in the policy are granted access to the credentials within the S3 buckets. That policy would look something like the following resource policy (the text in red should be replaced with your organization-specific information).

{
     "Sid": "ListRelevantDirectories20150907",
     "Effect": "Deny",
     "NotPrincipal": {
          "AWS": [
               "arn:aws:iam::123456789012:role/CredMgr",
               "arn:aws:iam::123456789012:role/CredUsr",
               "arn:aws:sts::123456789012:assumed-role/CredMgr/Mgr1",
               "arn:aws:sts::123456789012:assumed-role/CredUsr/User1",
               "arn:aws:sts::123456789012:assumed-role/CredUsr/User2"
          ]
     },
     "Action": [
          "s3:ListBucket"
     ],
     "Resource": "arn:aws:s3:::CredentialBucket"
}

Now that the authorized users can see the CredentialBucket, we have to ensure that the CredMgr user has the ability to put objects in and get objects from the bucket. This will allow this role to update credentials stored in the bucket.

{
     "Sid": "UseRelevantDirectories20150907",
     "Effect": "Deny",
     "NotPrincipal": {
          "AWS": [
               "arn:aws:iam::123456789012:role/CredMgr",
               "arn:aws:sts::123456789012:assumed-role/CredMgr/Mgr1"
          ]
     },
     "Action": [
          "s3:PutObject",
     ],
     "Resource": "arn:aws:s3:::CredentialBucket/*"
}

Finally, we also have to create the policy that will allow credential users and managers the ability to get the credentials for the specific service for which they are authorized to get the credentials.

{
     "Sid": "ReadOnlyServiceB20150522",
     "Effect": "Deny",
     "NotPrincipal": {
          "AWS": [
               "arn:aws:iam::123456789012:role/CredUsr",
               "arn:aws:sts::123456789012:assumed-role/CredUsr/User2"
               "arn:aws:iam::123456789012:role/CredMgr"
               "arn:aws:sts::123456789012:assumed-role/CredMgr/Mgr1"
          ]
     },
 
     "Action": [
          "s3:GetObject"
     ],
          "Resource": "arn:aws:s3:::CredentialBucket/ServiceB/*"
}

Notice the NotPrincipal element along with the Deny statement in each of those policies. This ensures that even if an IAM administrator creates new IAM users or IAM roles that have access to the CredentialBucket, they will not be able to access the sensitive credentials within the bucket because those users have not been explicitly given whitelisted access in the S3 access policy. Also, notice in the above policies that within the NotPrincipal element, there are ARNs for both the IAM roles (CredMgr and CredUser) and the STS-generated ARNs for the specific users of the CredMgr and CredUser roles for that policy. Because the NotPrincipal element requires specific ARNs to work, both of these are required for these policies to work correctly.

There are many ways to help ensure the security of sensitive information within an S3 bucket. The NotPrincipal element gives you another method for deploying secure resources within AWS.

Please leave comments or questions below, or go to the IAM forum.

- Matt

Comments