One Cloud Please

Resource Control Policies: Closing the data perimeter gap

17 November 2024

It’s pre:Invent season, and one of the most consequential identity and access management features was just released by the identity team at AWS. Resource Control Policies, a strong tool for establishing data perimeters, is now available for organization administrators.

This post explores this new feature, how it helps, what its limits are, and what we might see in the future.

Intro to RCPs

Resource Control Policies, or RCPs, is a feature available in AWS Organizations that allows you to control the maximum permissions allowable to certain resources or resource types for accounts within your organization.

Like Service Control Policies (SCPs), RCPs are permission policies which represent a boundary of maximum permissions that can be applied within an account. This means that RCPs are policies which cannot grant authority for a certain action and can only deny actions from taking place. This makes it a tool that is likely to be used by organizational administrators who wish to establish strong controls for a data perimeter around sensitive resources within their organization.

To put it in other words, whilst an SCP statement could be described as:

despite what the policy on the identity says, the following action is not permitted

An RCP statement could similarly be described as:

despite what the policy on the resource says, the following action is not permitted

Building an effective perimeter

In order to build an effective data perimeter, administrators need to enforce the use of trusted identities, expected networks, and known resources. RCPs assist in enforcing organization-wide compliance with ensuring resources can only be accessed by trusted identities, and only via expected networks. The data perimeter adds an additional coarse-grained layer of protection to the existing practices of fine-grained protections, applied via least privilege role-based access control, network firewalls and resource policies.

Trusted identity enforcement

Let’s take a look at how to apply an RCP to ensure only identities within your organization may access the sensitive resources or data that lies within your accounts. The following policy can be used to ensure that sensitive material from S3, SQS, KMS and Secrets Manager cannot be accessed by identities outside of your organization:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "NoAccessOutsideOrg",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:*",
                "sqs:*",
                "kms:*",
                "secretsmanager:*",
                "sts:*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:PrincipalOrgID": "<YOURORGID>"
                },
                "BoolIfExists": {
                    "aws:PrincipalIsAWSService": "false"
                }
            }
        }
    ]
}

The effect of the policy is that any API call to these services must originate from an identity within your organization, or be on behalf of an AWS service. Additionally, outside principals cannot use STS to assume an identity within the organization to bypass the block. If a user within the organization attempts to, for example, allow s3:GetObject to an external account via an S3 Bucket Policy, the external account would still be forbidden from accessing objects within the bucket as the RCP will override the allow with its explicit deny.

Those with a keen sense of potential exploits may see the carve out for AWS services and remember the confused deputy problem as a potential problem. Thankfully, RCPs also have an answer to this in the form of enforceable confused deputy protections. We can add the following statement to our RCP to guard against this potential:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceConfusedDeputyProtection",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:*",
                "sqs:*",
                "kms:*",
                "secretsmanager:*",
                "sts:*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:SourceOrgID": "<YOURORGID>"
                },  
                "Null": {
                    "aws:SourceAccount": "false"
                },
                "Bool": {
                    "aws:PrincipalIsAWSService": "true"
                }
            }
        }
    ]
}

The above statement applies specifically when the calling principal is an AWS service, and enforces that the aws:SourceOrgID must be equal to your organization ID (that is, the AWS service is using a principal to access the resource on behalf of another resource that belongs to your organization). The use of aws:SourceAccount is used in the Null condition operator so that the control applies only when the request has the context of an originating account (i.e. is susceptible to the cross-service confused deputy problem).

Expected network enforcement

We can also use RCPs to ensure that access is only granted from expected networks and that data doesn’t traverse through an unexpected network path. The following policy can be used to ensure data from S3, SQS, KMS and Secrets Manager can only be accessed if the caller is within the corporate network:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceNetworkPerimeter",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:*",
                "sqs:*",
                "kms:*",
                "secretsmanager:*",
                "sts:*"
            ],
            "Resource": "*",
            "Condition": {
                "NotIpAddressIfExists": {
                    "aws:SourceIp": "<YOURIPRANGE>"
                },
                "StringNotEqualsIfExists": {
                    "aws:SourceVpc": "<YOURVPCID>"
                },
                "BoolIfExists": {
                    "aws:PrincipalIsAWSService": "false",
                    "aws:ViaAWSService": "false"
                },
                "ArnNotLikeIfExists": {
                    "aws:PrincipalArn": [
                        "arn:aws:iam::*:role/aws:ec2-infrastructure"
                    ]
                }
            }
        }
    ]
}

The effect of the policy is that any attempt to access the resources within these services (or use STS to assume a role to do so) is blocked where the caller’s IP address falls outside the expected CIDR range or originates from a VPC ID that isn’t the expected one. Again, we specifically carve out an exception for AWS services, including those which use forward access sessions. We also have an additional carve out for EBS volume decryption, which uses a known IAM role to call KMS for decryption of the data key for volumes it manages.

A small note that all of the above examples don’t consider OIDC-based identities for readability purposes. Check out the aws-samples repository for a more detailed version which allows for those scenarios.

IAM Access Analyzer

With the introduction of RCPs come additions to IAM Access Analyzer’s External access finding details. Because RCPs have the ability to affect the effective permissions of a call, some of the automated findings may also be rendered invalid. To combat this without outright exposing potentially sensitive details of the RCP itself, the External access finding now has a field which indicates whether or not an RCP may affect a specific finding.

Limitations of RCPs

At launch, RCPs only support actions for S3, SQS, KMS, Secrets Manager and STS. This is a short list of likely the most impactful services for organization administrators to establish a data perimeter for. I’m confident this list will quickly expand based on customer demand.

Unfortunately, RCPs do not allow the use of the * wildcard by itself in the Action field, but instead enforce that all actions need to be scoped to a service namespace. This disallows a kind of automatic opt-in to protections as they become available via RCPs. RCPs also do not support the NotPrincipal element or the NotAction element.

Like SCPs, RCPs also do not apply to the organization management account. Administrators should ensure extra security is applied to this account to compensate. RCPs do however apply to delegated administrator accounts.

RCPs do not apply to services which use service-linked roles, as this would break specific requirements in order for some services to operate correctly. These roles do however fall directly in the AWS side of the Shared Responsibility Model.

Finally, RCPs do have limits and quotas which are very similar to SCPs, including a 5kb policy limit and a limit of 5 policies at each organizational OU, account or root level.

Time to start building

RCPs close a gap in the quest to better protect an organization’s sensitive data through the use of effective data perimeters by giving administrators a new tool to apply these guardrails. This does however introduce another layer of complexity which, if mismanaged, could lead to unexpected consequences such as outages. Administrators should carefully evaluate all the effects of these policies before applying them and in particular investigate specific nuances with how the various AWS services may use differing access mechanisms to reach resources.

Though service support is still limited at launch, I’d encourage administrators to explore the use of RCPs and to start using specific, limited policies to protect resources with known access patterns within their organization.

If you liked what I’ve written, or want to hear more on this topic, reach out to me on 𝕏 at @iann0036.