SCPs
General Info¶
- Definition
-
- AWS Service Control Policies (SCPs) are a way of restricting the actions that can be taken in an AWS account so that all IAM users and roles, and even the
root
user, cannot perform them - This feature is part of AWS Organizations, and the SCPs are controlled by the Organization Management (Master) account
- AWS Service Control Policies (SCPs) are a way of restricting the actions that can be taken in an AWS account so that all IAM users and roles, and even the
- Restrictions
-
- SCPs cannot restrict the Management account of the Organization
- This is a primary reason why it is best practice not to use the Organization Master account for anything other than Organization activities
- SCPs cannot restrict principals outside of the Organization
- If an S3 bucket is public, an SCP will not be able to stop Internet users from accessing it
- SCPs are similar to IAM boundaries, in that they define the maximum set of actions that can be allowed, but do not actually grant any privileges.
- The default SCP is
Allow *
on*
, but this doesn't mean that anyone in the accounts can do anything - It only means that the SCP is not further restricting them
- The default SCP is
- The Member accounts of the Organization are unable to see the SCPs that have been applied to them
- Further, when actions are denied, there is no way to know whether that is due to an IAM policy, an SCP, or something else
- SCPs cannot restrict the Management account of the Organization
Sample SCPs¶
Deny ability to leave Organization
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
}
Deny API calls from root users
{
"Version": "2012-10-17",
"Statement": {
"Sid": "DenyRootUser",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" }
}
}
}
Require MFA to perform an API action
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyStopAndTerminateWhenMFAIsNotPresent",
"Effect": "Deny",
"Action": [
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {"BoolIfExists": {"aws:MultiFactorAuthPresent": false}}
}
]
}
Deny ability to disrupt CloudTrail
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"cloudtrail:PutEventSelectors",
"cloudtrail:UpdateTrail"
],
"Resource": ["arn:aws:cloudtrail:*:*:trail/exampletrail"]
}
}
Deny ability to disrupt GuardDuty
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"guardduty:DeleteDetector",
"guardduty:DisassociateFromMasterAccount",
"guardduty:UpdateDetector",
"guardduty:CreateFilter",
"guardduty:CreateIPSet"
],
"Resource": "*"
}
}
Prevent also modifying its configuration:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"guardduty:AcceptInvitation",
"guardduty:ArchiveFindings",
"guardduty:CreateDetector",
"guardduty:CreateFilter",
"guardduty:CreateIPSet",
"guardduty:CreateMembers",
"guardduty:CreatePublishingDestination",
"guardduty:CreateSampleFindings",
"guardduty:CreateThreatIntelSet",
"guardduty:DeclineInvitations",
"guardduty:DeleteDetector",
"guardduty:DeleteFilter",
"guardduty:DeleteInvitations",
"guardduty:DeleteIPSet",
"guardduty:DeleteMembers",
"guardduty:DeletePublishingDestination",
"guardduty:DeleteThreatIntelSet",
"guardduty:DisassociateFromMasterAccount",
"guardduty:DisassociateMembers",
"guardduty:InviteMembers",
"guardduty:StartMonitoringMembers",
"guardduty:StopMonitoringMembers",
"guardduty:TagResource",
"guardduty:UnarchiveFindings",
"guardduty:UntagResource",
"guardduty:UpdateDetector",
"guardduty:UpdateFilter",
"guardduty:UpdateFindingsFeedback",
"guardduty:UpdateIPSet",
"guardduty:UpdatePublishingDestination",
"guardduty:UpdateThreatIntelSet"
],
"Resource": "*"
}
]
}
Deny ability to disable Config or changing its rules
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"config:DeleteConfigRule",
"config:DeleteConfigurationRecorder",
"config:DeleteDeliveryChannel",
"config:StopConfigurationRecorder"
],
"Resource": "*"
}
]
}
Deny ability to disrupt CloudWatch Event collection
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": [
"events:DeleteRule",
"events:DisableRule",
"events:RemoveTargets"
],
"Resource": "arn:aws:events:*:*:rule/default/NAME"
}
}
Prevent also altering its configuration:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"cloudwatch:DeleteAlarms",
"cloudwatch:DeleteDashboards",
"cloudwatch:DisableAlarmActions",
"cloudwatch:PutDashboard",
"cloudwatch:PutMetricAlarm",
"cloudwatch:SetAlarmState"
],
"Resource": "*"
}
]
}
Deny ability to modify an important IAM role
- Such as the ones used by Security teams:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAccessToASpecificRole",
"Effect": "Deny",
"Action": [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::*:role/NAME"
]
}
]
}
Prevent deletion or modification of IAM roles created by the Information Security team
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventInfoSecRoleActions",
"Effect": "Deny",
"Action": ["*"],
"Resource": ["arn:aws:iam::*:role/InfoSec*"],
"Condition": {
"StringNotLike": {"aws:PrincipalARN": "arn:aws:iam::*:role/InfoSec*"}
}
}
]
}
Prevent creation of IAM users with a login profile
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventIAMUserWithLoginprofile",
"Effect": "Deny",
"Action": [
"iam:ChangePassword",
"iam:CreateLoginProfile",
"iam:UpdateLoginProfile",
"iam:UpdateAccountPasswordPolicy"
],
"Resource": ["*"]
}
]
}
Deny ability to create IAM access keys
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": ["iam:CreateAccessKey"],
"Resource": "*"
}
}
Deny ability to create IAM access keys for the root user
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny",
"Action": "iam:CreateAccessKey",
"Resource": ["*"],
"Condition": {
"StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" }
}
}
}
Require the use of IMDSv2
- Require role credentials for an EC2 to have been retrieved using the IMDSv2:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "RequireAllEc2RolesToUseV2", "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "NumericLessThan": { "ec2:RoleDelivery": "2.0" } } } ] }
- Ensure that EC2s can only be created if you enforce that they use IMDSv2:
{ "Version": "2012-10-17", "Statement": { "Sid": "RequireImdsV2", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotEquals": { "ec2:MetadataHttpTokens": "required" } } } }
- Deny option to change type of metadata:
{ "Version": "2012-10-17", "Statement": { "Effect": "Deny", "Action": "ec2:ModifyInstanceMetadataOptions", "Resource": "*" } }
- Ensure the hop count that can be set is restricted:
{ "Version": "2012-10-17", "Statement": { "Sid": "MaxImdsHopLimit", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "NumericGreaterThan": {"ec2:MetadataHttpPutResponseHopLimit": "1"} } } }
Prevent public S3 access account-wide
- Prevents modifying S3 public access block settings account-wide:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventS3PublicAccess",
"Action": ["s3:PutAccountPublicAccessBlock"],
"Resource": "*",
"Effect": "Deny"
}
]
}
Prevent deleting Amazon VPC flow logs
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:DeleteFlowLogs",
"logs:DeleteLogGroup",
"logs:DeleteLogStream"
],
"Resource": "*"
}
]
}
Deny ability to make a VPC accessible from the Internet that isn't already
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:AttachInternetGateway",
"ec2:CreateInternetGateway",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateVpcPeeringConnection",
"ec2:AcceptVpcPeeringConnection",
"globalaccelerator:Create*",
"globalaccelerator:Update*"
],
"Resource": "*"
}
]
}
Allow only approved services
{
"Version": "2012-10-17",
"Statement": {
"Sid": "AllowList",
"Effect": "Allow",
"Action": ["a4b:*","access-analyzer:*","account:*","acm:*","acm-pca:*","amplify:*","apigateway:*","application-autoscaling:*","applicationinsights:*","appmesh:*","appmesh-preview:*","appstream:*","appsync:*","arsenal:*","artifact:*","athena:*","autoscaling:*","autoscaling-plans:*","aws-marketplace:*","aws-marketplace-management:*","aws-portal:*","backup:*","backup-storage:*","batch:*","budgets:*","cassandra:*","ce:*","chatbot:*","chime:*","cloud9:*","clouddirectory:*","cloudformation:*","cloudfront:*","cloudhsm:*","cloudsearch:*","cloudtrail:*","cloudwatch:*","codebuild:*","codecommit:*","codedeploy:*","codeguru-profiler:*","codeguru-reviewer:*","codepipeline:*","codestar:*","codestar-notifications:*","cognito-identity:*","cognito-idp:*","cognito-sync:*","comprehend:*","comprehendmedical:*","compute-optimizer:*","config:*","connect:*","cur:*","dataexchange:*","datapipeline:*","datasync:*","dax:*","dbqms:*","deeplens:*","deepracer:*","detective:*","devicefarm:*","directconnect:*","discovery:*","dlm:*","dms:*","ds:*","dynamodb:*","ebs:*","ec2:*","ec2-instance-connect:*","ec2messages:*","ecr:*","ecs:*","eks:*","elastic-inference:*","elasticache:*","elasticbeanstalk:*","elasticfilesystem:*","elasticloadbalancing:*","elasticmapreduce:*","elastictranscoder:*","es:*","events:*","execute-api:*","firehose:*","fms:*","forecast:*","frauddetector:*","freertos:*","fsx:*","gamelift:*","glacier:*","globalaccelerator:*","glue:*","greengrass:*","groundstation:*","groundtruthlabeling:*","guardduty:*","health:*","iam:*","imagebuilder:*","importexport:*","inspector:*","iot:*","iot-device-tester:*","iot1click:*","iotanalytics:*","iotevents:*","iotsitewise:*","iotthingsgraph:*","kafka:*","kendra:*","kinesis:*","kinesisanalytics:*","kinesisvideo:*","kms:*","lakeformation:*","lambda:*","launchwizard:*","lex:*","license-manager:*","lightsail:*","logs:*","machinelearning:*","macie:*","managedblockchain:*","mechanicalturk:*","mediaconnect:*","mediaconvert:*","medialive:*","mediapackage:*","mediapackage-vod:*","mediastore:*","mediatailor:*","mgh:*","mobileanalytics:*","mobilehub:*","mobiletargeting:*","mq:*","neptune-db:*","networkmanager:*","opsworks:*","opsworks-cm:*","organizations:*","outposts:*","personalize:*","pi:*","polly:*","pricing:*","qldb:*","quicksight:*","ram:*","rds:*","rds-data:*","rds-db:*","redshift:*","rekognition:*","resource-groups:*","robomaker:*","route53:*","route53domains:*","route53resolver:*","s3:*","sagemaker:*","savingsplans:*","schemas:*","sdb:*","secretsmanager:*","securityhub:*","serverlessrepo:*","servicecatalog:*","servicediscovery:*","servicequotas:*","ses:*","shield:*","signer:*","sms:*","sms-voice:*","snowball:*","sns:*","sqs:*","ssm:*","ssmmessages:*","sso:*","sso-directory:*","states:*","storagegateway:*","sts:*","sumerian:*","support:*","swf:*","synthetics:*","tag:*","textract:*","transcribe:*","transfer:*","translate:*","trustedadvisor:*","waf:*","waf-regional:*","wafv2:*","wam:*","wellarchitected:*","workdocs:*","worklink:*","workmail:*","workmailmessageflow:*","workspaces:*","xray:*"],
"Resource": "*"
}
}
Region enforcement
- Enforce that only
eu-west-1
can be used, along with the global services:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictRegion",
"Effect": "Deny",
"NotAction": [
"a4b:*",
"budgets:*",
"ce:*",
"chime:*",
"cloudfront:*",
"cur:*",
"globalaccelerator:*",
"health:*",
"iam:*",
"importexport:*",
"mobileanalytics:*",
"organizations:*",
"route53:*",
"route53domains:*",
"shield:*",
"support:*",
"trustedadvisor:*",
"waf:*",
"wellarchitected:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-west-1"
]
}
}
}
]
}
Additional Samples
Other collections of SCPs:
- terraform_aws_scp: collection of SCPs written in Terraform (generic, PCI, HIPAA)
- AWS IAM Permissions Guardrails
- Securing resource tags (ABAC) used for authorization
Terraform Sample¶
The following sample defines an SCP which denies accounts the ability to leave the Organization, and attaches it to an OU named prod
(aws_organizations_organizational_unit.prod.id
):
resource "aws_organizations_policy" "deny_leave_org" {
name = "Deny ability to leave Organization"
type = "SERVICE_CONTROL_POLICY"
content = <<CONTENT
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
}
]
}
CONTENT
}
resource "aws_organizations_policy_attachment" "deny_leave_org_ou_prod" {
policy_id = aws_organizations_policy.deny_leave_org.id
target_id = aws_organizations_organizational_unit.prod.id
}
Tooling¶
Tool | Description |
---|---|
aws-allowlister | Automatically compile an AWS Service Control Policy that ONLY allows AWS services that are compliant with your preferred compliance frameworks |