Creating a Data Perimeter with Resource Control Policies (RCPs) and AWS KMS

November 19, 2024
Jason Kao

On November 13th, 2024, AWS released Resource Control Policies (RCPs). These are not Service Control Policies (SCPs), but rather a good complement to SCPs. We see Resource Control Policies as a good way to enforce data perimeters and to protect resources.

In this post, we'll analyze Resource Control Policies, explain the benefits of RCPs vs SCPs, and give 5 examples of how to use RCPs to build a multi-layered data perimeter to protect data. As of the release, AWS Resource Control Policies only support KMS, Secrets Manager, SQS (Simple Queue Service), STS (Security Token Service), and S3. We'll cover how to creatively use RCPs to protect data across multiple AWS services including those not covered by RCPs.  These use cases including layered methods to keep secrets non-public, building zones of trust and preventing cross-environment data access, and more.

SCPs control actions that IAM principals under one's management can perform, while RCPs control actions that can be performed on resources under one's management (resources within the Organization). There's an overlap for SCPs and RCPs for actions performed by IAM Principals within the organization on resources within the organization.

When to use RCPs and SCPs

When using RCPs, keep in mind the examples below utilize Deny statements which override any explicit Allows in other policies. We and AWS recommend testing RCPs thoroughly. Additionally, the RCPFullAWSAccess policy is attached to the organization root, every OU, and every account when RCPs are enabled. This policy does not grant access and cannot be detached.

GitHub Repository: https://github.com/FogSecurity/aws-data-perimeter-iam/tree/main/policies/resource_control_policies

Use Cases

Create a Data Perimeter for Data Access within the Organization and Deny Outside Access

Prerequisites: Use of Customer Managed Keys (and not AWS Owned Keys)

RCPs today only support KMS, Secrets Manager, SQS, STS, and S3. Thus, to protect data across other services (such as DynamoDB), we need to think creatively. If those resources are encrypted with KMS, we can use KMS in a RCP to create a data perimeter as such where we:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceEncryptionReadPerimeter",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:PrincipalOrgID": "ORG_ID_HERE"
                },
                "BoolIfExists": {
                    "aws:PrincipalIsAWSService": "false"
                }
            }
        }
    ]
}

We can also add a statement to prevent against confused deputy issues.

{
            "Sid": "EnforceConfusedDeputyProtection",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:SourceOrgID": "ORG_ID_HERE"
                },
                "Null": {
          			"aws:SourceAccount": "false"
        		},
                "Bool": {
                    "aws:PrincipalIsAWSService": "true"
                }
            }
        }

Create a Data Perimeter to Limit Cross-Environment Data Access and Build Zones of Trust

We can take the data perimeter one step further. This also is predicated on use of encryption via AWS KMS CMKs.

Assuming a similar structure to AWS's recommended structure for Organizations, this RCP can be attached to the Production OUs. This RCP limits decryption and thus data access to production data by limiting access to principals within the Production OUs.

By using these RCPs, we can create a zone of trust around the Production OUs and limit access to data from Non-Production OUs.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceEncryptionReadPerimeter",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:PrincipalOrgPaths": [
                      	"<ProductionOrgPath1>",
                        "<ProductionOrgPath2>"
                    ]
                },
                "BoolIfExists": {
                    "aws:PrincipalIsAWSService": "false"
                }
            }
        },
        {
            "Sid": "EnforceProductionConfusedDeputyProtection",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:SourceOrgPaths": [
                        "<ProductionOrgPath1>",
                        "<ProductionOrgPath2>"
                    ]
                },
                "Null": {
          			"aws:SourceAccount": "false"
        		},
                "Bool": {
                    "aws:PrincipalIsAWSService": "true"
                }
            }
        }
    ]
}

Prevent KMS Key Lockout

Previously, when working with key policies that were too restrictive - unlocking the key policies required using the account root principal, which has security implications. Although now, with the release of AssumeRoot, using the root principal can be less dangerous.

This RCP prevents putting a key policy on a key that renders the key unmanageable.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceKMSEncryption",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
            	"kms:PutKeyPolicy",
                "kms:CreateKey"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                	"kms:BypassPolicyLockoutSafetyCheck": true
                }
            }
        }
    ]
}

Require KMS Encryption for S3

While S3 has features such as bucket encryption settings, the feature does not require all object uploads to use AWS KMS (objects can be uploaded with AWS Owned encryption).

One of AWS's examples includes a statement to enforce KMS encryption for S3. We add more to this example to ensure all new objects in the S3 bucket are encrypted with a KMS Key. This policy adds a condition to 2 actions that can write in S3: s3:ReplicateObject and s3:PutObject to ensure all object puts including replication are encrypted with AWS KMS.

This policy does the following:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceKMSEncryption",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
            	"s3:PutObject",
                "s3:ReplicateObject"
                ],
            "Resource": "*",
            "Condition": {
                "Null": {
                    "s3:x-amz-server-side-encryption-aws-kms-key-id": "true"
                }
            }
        }
    ]
}

Protect Secrets from Public Exposure

This example RCP has 2 parts and thus 2 layers of security:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnforceOrgForSecretsManager",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "secretsmanager:*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "aws:PrincipalOrgID": "<ORG_ID_HERE>"
                },
                "BoolIfExists": {
                    "aws:PrincipalIsAWSService": "false"
                }
            }
        },
        {
            "Sid": "DenyPublicPolicyUpdate",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
              "secretsmanager:PutResourcePolicy"
            ],
            "Resource": "*",
            "Condition": {
              "BoolIfExists": {
                "secretsmanager:BlockPublicPolicy": "false"
              }
            }
      }
    ]
}

Conclusion

The examples listed above are examples of how to use AWS's new policy feature, resource control policies, to create data perimeters and protect your AWS resources at scale.

Run, don't walk to implement RCPs in your accounts today!

If you're interested in talking more about data perimeters and cloud encryption, reach out to us at info@fogsecurity.io.

References

AWS Resource Control Policy Announcement

AWS GitHub RCP Samples

AWS User Guide: Manage RCPs

AWS Documentation: Organizing AWS Environment

AWS Documentation: Best Practices for Organization Structure

Subscribe to stay up to date on cloud data security and our work.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.