- Cloud Security Lab a Week (S.L.A.W)
- Posts
- Master the S3 Perimeter with Resource Control Policies
Master the S3 Perimeter with Resource Control Policies
Learn how to use the just-released Resource Control Policies to lock down S3 with nuance and style!
Prerequisites
An AWS Organization. Preferably the one we’ve been building.
The Lesson
One of my martial arts masters once said, “Timing is everything.” Or maybe it was “in timing there is everything”, and maybe it was from a movie, or maybe I saw it in an episode of American Ninja? But it definitely wasn’t a Yoda quote, I know those all cold.
Anyway…
In our last lab we learned how to make an S3 bucket public using a bucket policy. If you recall, we could make it public to the Internet via an open IP address (0.0.0.0/0), or we could just make it available to any AWS principal, which opens it up to direct access via API calls.
Literally the day I released that lab, AWS released the long-anticipated Resource Control Policies (RCPs). This was (well, is — it JUST HAPPENED) incredibly exciting for us cloud security professionals, because it addresses some of the thorniest issues around managing consistent access at scale.
One of the concepts we are building towards with all our focus on AWS Organizations, IAM, SCPs, and even VPC endpoints, is the Data Perimeter. We aren’t quite where I want us to be to go in-depth yet, but after nearly a year of these labs you have an incredibly sound foundation for when we start in-depth implementation. Conceptually, a data perimeter is the combination of various cloud security controls used to ensure you have effective control over how and where your cloud data is accessed.
Network perimeters are easy: plop some firewalls in place and write rules about sources and destinations. Enterprise-scale cloud data perimeters are hard: you need to control access to data at both the network and IAM levels, and do so consistently in heterogenous environments where you don’t own the backbone (the cloud service provider), everything is managed over the Internet (you know, public cloud), and we are in a multi-tenant and multi-account environment where we actually need to share across silos for legit business purposes.
RCPs are the missing link which provides centralized rules for consistent control of resource sharing. RCPs are like Service Control Policies and Permissions Boundaries; they are the basis for Security Invariants. Rules which are consistently enforced, without need for exceptions.
How Resource Control Policies (RCPs) Work
Explaining Resource Control Policies to y’all is damn simple, since we just covered bucket policies and previously covered SCPs. An RCP is like an SCP combined with a resource-based policy:
Like an SCP it does not grant permissions, it restricts permissions. Even if an RCP has an Allow statement, the resource’s directly-associated resource-based policy (e.g., bucket policy) also must allow access.
RCPs use the same policy language we’ve been using for everything else.
RCPs are evaluated even before SCPs. Why? Because, as you may recall, resources may allow direct access which isn’t associated with an API call (for some resource types).
For example, you’ve seen that I share our CloudFormation templates by just giving you a URL to an S3 bucket. An SCP couldn’t restrict this access because it’s not associated with an IAM principal or a regular API call.
As with all other policy types, any explicit deny in any policy blocks access. But remember, as we discussed last week, an allow in a resource-based policy can skip past any implicit denies. RCPs give us a top-down way to manage that different evaluation flow!
RCPs can apply to accounts or Organizational Units, just like SCPs.
Like SCPs, RCPs won’t work on the Organization management account. Another reason delegated administration is so important, and I introduced it so early.
By default AWS applies an Allow All policy at every level when you turn on RCPs. As with SCPs, this prevents catastrophic breaks. Remember, this doesn’t grant permissions — it just doesn’t block permissions.
RCPs support conditions, and some of these (like only allow encrypted S3 connections) are a bit different than how we used them in SCPs.
Here’s the updated policy evaluation logic, which I won’t go into. I just want you to notice that RCPs are evaluated right near the top.
RCPs only work for a subset of AWS services, but they include some important ones:
Amazon S3: Everyone uses S3. Everyone needs to share S3. This is the big one, and where we will focus today.
AWS Security Token Service: STS, huh? Yep, this is also huge — you can now set rules like “only allow entities in my AWS Organization to assume this role.” We can now block cross-organization use/abuse of our roles, and some dev or attacker in an account can’t override our decisions.
AWS Key Management Service: Enterprise-wide management of access to our encryption keys? Yes please!
Amazon SQS: We use Simple Queue Service a lot in cloud-native application architectures to move data and logic around, including across accounts. RCP support here is more important than a lot of people probably realize.
AWS Secrets Manager: Secrets Manager stores credentials and sekrit squirrel data.
Here are some examples of how we can use these to protect our organizations. Remember, the value in RCPs is that we can enforce our rules throughout or organization, and a local admin can’t override them!
Only allow someone to assume a role if they used our organization’s IAM Identity Center.
Only allow principals in my organization access to our KMS encryption keys.
Only allow principals in my organization access to an S3 bucket tagged with classification: sensitive.
That last one? Let’s go test it out.
Key Lesson Points
Resource Control Polices are like Service Control Policies. They define the maximum allowable permissions for the included resources.
RCPs are a powerful tool to manage resource-level access across an entire AWS organization. They are now an important element of any data perimeter strategy.
RCPs are managed at the Organizations level, and applied to Organizational Units or individual accounts.
RCPs are early in AWS policy evaluation, even before SCPs. This ensures they deny any access/usage even if access is granted via a directly-associated resource-based policy or IAM policy.
The Lab
🚨 WARNING WARNING WARNING 🚨
AWS is in the midst of updating the entire AWS user experience. This is rolling out incrementally and will look different in different parts of the console, and may or may not even be visible to you when you log in.
So far this is a “look and feel” update and does not change menu and button placements, so you should be fine with these screenshots and video no matter which version you are on.
For now. Hopefully.
We have three steps:
Turn on RCPs.
Write an RCP to deny anyone outside our organization from accessing any resource tagged as sensitive. The template is below, so you just need to swap in your Organization’s ID.
Apply the RCP to our Workloads OU.
Keep in mind that because this relies on a tag we aren’t using, it won’t affect our current buckets. But, as you might imagine, we will test that out in a couple weeks after we turn on Access Analyzer to help know when buckets are shared.
Video Walkthrough
Step-by-Step
Another reminder that the user experience may look different, but in my testing all the boxes are in the same places — just… rounder.
Start at your IAM Identity Center Sign-In Portal > CloudSLAW [your management account] > AdministratorAccess > Organizations > Policies > Resource control policies:
Notice that, so far, we only have Service control policies enabled
I realize this might shock some of you, but Enable resource control policies:
The next part can take a minute, so stay on this screen; when it’s done you’ll see the following:
Just like with SCPs, AWS creates a full access policy. Remember this is because once you turn on a policy the default deny (implicit deny) kicks in and blocks EVERYTHING unless you have an Allow. This full access policy is applied at every level of your organization to make sure you don’t blow the entire thing up.
Okay, at this point RCPs are enabled and the full access policy ensures everything works like before. Let’s write and then apply our first policy.
We’ll use this template. First, copy and paste this into a text editor. Then get your Organization’s ID on the left side of the screen. Paste the org ID where this template says my-org-id (make sure you don’t mess up the quotes). Then copy this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessToSensitiveData",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"StringEqualsIfExists": {
"aws:ResourceTag/classification": "sensitive"
},
"StringNotEqualsIfExists": {
"aws:SourceOrgID": "my-org-id"
},
"BoolIfExists": {
"aws:PrincipalIsAWSService": "false"
}
}
}
]
}
Here’s how it works:
Effect: Deny: everything matching will be denied.
Principal: *: any and every principal (e.g., IAM user or role).
Action: S3:*: Anything in S3.
Resource: *: Everything in S3 (all buckets and objects).
Condition: This is where things get interesting. All conditions need to match, so…
The resource (bucket) has a tag and that tag has a key of “classification” and a value of “sensitive”. The StringEqualsIfExists means “If the tag isn’t there don’t worry about it, if the tag which is there matches this key/value.”
If the request is not from the current organization. This is one of those double negatives, and this one says “deny if the request has an organization, but that organization is not mine.” Yeah, I talk like this in real life now and my kids hate AWS for it. I mean, what kid wants to get yelled at and hear, “don’t clean any room other than your room if your room exists.”?
Then we finish up and we tell the RCP to allow requests from AWS services (technically this can be abused via a Confused Deputy Attack, which I haven’t really gone into yet, but there are other policies we can use later to block that).
In summary, deny any API calls if the resource is tagged classified and the request comes from outside my organization, but allow AWS services to still make these requests.
Alrighty, let’s do this! Click Create policy. Then use the following name and description (ignore my typo in the screenshot):
Name: S3 Classified Sensitive
Description: If a bucket is classified sensitive disallow external access
Yes, I messed up my description, be better than me.
Now scroll down and Paste in the RCP JSON… double check your Org ID, then Create policy:
You can ignore the warning. This policy is derived from Amazon’s own example, and that warning isn’t relevant in the larger context of how we wrote this.
With that saved we need to apply it. For this lab we will only apply it to our Production workload accounts. We will apply it at our OU level. This allows developers freedom in non-prod, but the moment something has production data and goes into production we lock it down. Remember, this relies on the resource tag and doesn’t lock everything down.
Go to AWS accounts > Workloads > Prod:
Click Policies. Notice how that full access policy shows up 3 times? That’s because it’s attached to the org root, to the Workloads OU, and to the Prod OU. This isn’t inheritance — it’s literally attached to everything. This is good, because the alternate is bad (I made that up myself!).
Now click Attach in the Resource control policies section:
Click the box next to S3 Classified Sensitive, and then Attach policy:
If you scroll down it will look like this now:
Thus, for any account in your Prod OU, if you tag an S3 bucket with classification:sensitive, it will only be accessible to principals within your own organization.
This doesn’t actually share the bucket/objects — it just ensures that even if someone writes a bucket policy which shares them outside your organization, the RCP will override that policy and block access!
Lab Key Points
When you enable RCPs, a full access policy is attached to everything in your organization. This prevents… lighting your entire org on fire.
As with SCPs, you will need a lot of “StringEqualsIfNotExists” style conditionals.
RCPs don’t grant permissions — they define maximum possible permissions. You still need to share the resource, like an S3 bucket, directly using an appropriate resource-based policy, such as a bucket policy.
-Rich
Reply