SCP Starter Pack

No organization is secure unless they implement Service Control Policies (SCPs), my favorite part of AWS security. I briefly covered them earlier, but they’re so important that I could dedicate a few posts to them alone.

SCPs are the final boss of AWS security. They’re a way of adding guardrails to enforce what users and roles are allowed to do in different accounts. They do not grant any additional permissions. You can define them at the Organizational Unit (OU) level or at the individual account level. Accounts inherit SCPs from their parent OUs, starting with the root OU. A potential hierarchy might look like this:

  • Root OU
    • Sandbox OU
      • Application account
    • PreProd OU
      • Ops account
      • Security/Audit account
      • Application account
    • Production OU
      • Ops account
      • Security/Audit account
      • Application account

Remember, you must both enable AWS Organizations and create at least one additional AWS account in the organization for SCPs to work. I’ve seen many cases, mostly small companies, where they’ve enabled Organizations, but for some reason, are still using the management account for their deployments, and it can be a PITA to redeploy everything into another account… hopefully not!

By default, when you create an organization, it will have the FullAWSAccess SCP policy attached to the root OU and any member accounts. It’s a *:* policy allowing any service to perform any action. This lets you incrementally adopt more explicit deny policies. A more hardened configuration would implement deny-all-by-default policy at the root OU, and add explicit allow policies at sub-levels. The decision can be broken down as follows:

  • Allow all by default: easier to implement as everything is permitted unless explicitly defined, but requires ongoing management to deny access to unnecessary (and newly introduced!) services
  • Deny all by default: more secure baseline by blocking everything, but higher overhead and slower to implement as every service/action needs to be explicitly allowed

While, in general, the latter least-privilege model is better from a security purist standpoint, it’s easier to start off with a permissive policy and then deny specific dangerous actions… assuming you can cover them all. And that’s a big IF. Still, I think it’s a good middle ground to start and if you haven’t fully understood how your workloads interact with overly restrictive default deny policy decisions in subtle ways that can make troubleshooting difficult.

Over time, you could use IAM Access Analyzer to define more specific SCPs so that you can invert the default permissions to a default deny. But I think if you stay with the spirit of SCPs, then allow all seems like a more reasonable default, so let’s go with that.

AWS’s built-in managed policies let you quickly get up and running with a service, but they could be a security trap, since policies could include elevated privileges, and certainly have in past versions. Unless you want to audit AWS-managed policies or write your own custom ones, SCPs are the only scalable way to restrict access at a higher level and ensure a proper permissions boundary in an AWS Organization.

If you’re using a CI/CD environment that deploys infrastructure, ensure that it has its own role with permissions separate from application workloads that are running. I’ll cover setting up a proper IaC automation role in a future post.

Developers should never be permitted to assume the AdministratorAccess role in any environment. Even SystemAdministrator grants many privileges, but is a better starting point. An SCP can be used to further restrict access, like, to allow full access in the Sandbox OU, limited in PreProd OU, and none in Production OU.

Things to watch out for:

  • AWSServiceRoleFor* service-linked roles may grant broad service-level access
  • Users with IAM PassRole permissions could launch EC2 instances and gain access to elevated permissions through accessing the EC2 instance’s temporary credentials. Alerting for this is non-trivial.

Review the following policy statements, and add them to an SCP policy attached to the root OU. Whether you make them a single SCP or individual ones is up to you.

It’s assumed that you’ll have created an administrator access role in IAM Identity Center. In the SCP statements below, I assume that this role isn’t used for day-to-day operations, nor given to “superadmin” trusted employees, since many of the SCP statements specifically don’t apply to this role.

How to Use

The subsequent sections include SCP statements that can be incorporated into a larger SCP or attached as individual policies. It may make sense to create a few different SCPs attached to the root OU, instead of one giant one, given the 5KB size restriction per SCP.

I’ve used the following placeholders for your search-and-replacing convenience:

  • ACCOUNT_ID: management account ID
  • SSO_REGION: region where IAM Identity Center is deployed
  • SSO_ADMIN_ROLE_NAME: automatically-generated administrator role name (starts with AWSReservedSSO_)
  • TRAIL_NAME: CloudTrail trail name
  • ALLOWED_REGIONS: JSON array of region names where to allow resource deployment, for example: ["us-east-1", "us-west-1"]

Account

Deny Enabling or Disabling Regions

Why: Enabling new AWS regions increases the surface area for potential security issues and unanticipated costs. Limit resource deployments only to your organization’s approved regions.

{
    "Sid": "DenyRegionEnablement",
    "Effect": "Deny",
    "Action": ["account:EnableRegion", "account:DisableRegion"],
    "Resource": "*",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny account:EnableRegion and account:DisableRegion actions.

Note that regions introduced before March 20, 2019 are enabled by default, so be sure to disable ones that you aren’t using.

Deny Actions in Unsupported Regions

Why: AWS regions that aren’t being used should be restricted to prevent unintended deployments and minimize security risks.

{
    "Sid": "DenyUnsupportedRegions",
    "Effect": "Deny",
    "NotAction": [
        "a4b:*",
        "acm:*",
        "aws-marketplace-management:*",
        "aws-marketplace:*",
        "aws-portal:*",
        "budgets:*",
        "ce:*",
        "chime:*",
        "cloudfront:*",
        "config:*",
        "cur:*",
        "directconnect:*",
        "ec2:DescribeRegions",
        "ec2:DescribeTransitGateways",
        "ec2:DescribeVpnGateways",
        "fms:*",
        "globalaccelerator:*",
        "health:*",
        "iam:*",
        "importexport:*",
        "kms:*",
        "mobileanalytics:*",
        "networkmanager:*",
        "organizations:*",
        "pricing:*",
        "route53:*",
        "route53domains:*",
        "route53-recovery-cluster:*",
        "route53-recovery-control-config:*",
        "route53-recovery-readiness:*",
        "s3:GetAccountPublic*",
        "s3:ListAllMyBuckets",
        "s3:ListMultiRegionAccessPoints",
        "s3:PutAccountPublic*",
        "shield:*",
        "sts:*",
        "support:*",
        "trustedadvisor:*",
        "waf-regional:*",
        "waf:*",
        "wafv2:*",
        "wellarchitected:*"
    ],
    "Resource": "*",
    "Condition": {
        "StringNotEquals": {
            "aws:RequestedRegion": ALLOWED_REGIONS
        },
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: The triple negation is confusing, but the net effect is to deny all actions in unsupported regions (not in the ALLOWED_REGIONS list), unless it’s an action in the list. This doesn’t apply to administrators. The list of excepted actions isn’t exhaustive, so you’ll want to adjust it accordingly. If you blanket-deny all actions, you risk blocking global services that aren’t tied to specific regions.

Prevent Accounts from Leaving the Organization

Why: Accounts can become “independent” and leave the organization, which is a potential security risk.

{
  "Sid": "PreventAccountSeparation",
  "Effect": "Deny",
  "Action": ["organizations:LeaveOrganization"],
  "Resource": "*"
}

How: Deny the action.

Config

Deny Disabling or Changing Config Rules

Why: Disabling or changing AWS Config rules can undermine security, since it’s used for resource configuration tracking and security compliance monitoring.

{
    "Sid": "DenyDisablingOrChangingConfigRules",
    "Action": [
        "config:DeleteConfigRule",
        "config:DeleteConfigurationRecorder",
        "config:DeleteDeliveryChannel",
        "config:StopConfigurationRecorder"
    ],
    "Resource": "*",
    "Effect": "Deny",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny actions that could disable or alter Config rules.

IAM

Deny Creating IAM Identity Center Instances at the Account Level

Why: Only use IAM Identity Center at the organization level.

{
    "Sid": "DenyMemberAccountInstances",
    "Effect": "Deny",
    "Action": ["sso:CreateInstance"],
    "Resource": "*"
}

How: Deny sso:CreateInstance action.

Deny Root User Actions

Why: When you create a new AWS member account and attach it to an organization, a root user is created with an invalid password, but if you reset the password and need to make changes to the member account, you likely now have an additional password (and MFA) to store somewhere securely. All root access should be blocked in member accounts and delegated to the management account.

{
    "Sid": "DenyRootUserAccess",
    "Effect": "Deny",
    "Action": "*",
    "Resource": ["arn:aws:iam::*:root"]
}

How: Deny all actions by root user, including ability to login even if the password and MFA are correct.

Deny Root User Access Key Creation

Why: There’s no reason to allow creation of access keys for the root user.

{
    "Sid": "DenyRootUserAccessKeyCreation",
    "Effect": "Deny",
    "Action": ["iam:CreateAccessKey"],
    "Resource": ["arn:aws:iam::*:root"]
}

How: Deny iam:CreateAccessKey action.

Deny Creating Users or Access Keys

Why: Prevent creation of IAM users or access keys as all access by human users should be through IAM Identity Center. Programmatic access should use role assumption when possible.

{
    "Sid": "DenyIAMUserOrAccessKeyCreation",
    "Action": ["iam:CreateUser", "iam:CreateAccessKey"],
    "Resource": "*",
    "Effect": "Deny",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny the iam:CreateUser and iam:CreateAccessKey actions. This does not prevent IAM Identity Center usage.

Deny Role Modification

Why: Arbitrarily allowing IAM role modification can lead to privilege escalation or unauthorized access.

{
    "Sid": "DenyRoleModification",
    "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/*", "arn:aws:iam::*:user/*"],
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny role modification actions except for the administrator role.

Deny Privileges to All

Why: The only thing that should be creating or modifying IAM entities is either an administrator doing one-off management.

{
    "Sid": "DenyIAMPrivilgesToAll",
    "Action": [
        "iam:AddUserToGroup",
        "iam:AttachRolePolicy",
        "iam:AttachUserPolicy",
        "iam:CreateAccessKey",
        "iam:CreateLoginProfile",
        "iam:CreatePolicyVersion",
        "iam:CreateRole",
        "iam:CreateUser",
        "iam:DeleteRole",
        "iam:DeleteRolePermissionsBoundary",
        "iam:DeleteRolePolicy",
        "iam:DeleteUserPermissionsBoundary",
        "iam:DeleteUserPolicy",
        "iam:DetachRolePolicy",
        "iam:PassRole",
        "iam:PutRolePermissionsBoundary",
        "iam:PutRolePolicy",
        "iam:PutUserPermissionsBoundary",
        "iam:SetDefaultPolicyVersion",
        "iam:UpdateAssumeRolePolicy",
        "iam:UpdateLoginProfile",
        "iam:UpdateLoginProfile",
        "iam:UpdateRole",
        "iam:UpdateRoleDescription",
        "sts:AssumeRole"
    ],
    "Resource": "*",
    "Effect": "Deny",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny most IAM actions and the ability to assume roles, except to the administrator.

EC2

Deny VPC Flow Log Modification or Deletion

Why: VPC flow logs record network traffic passing through a VPC and they’re invaluable in forensics security postmortems. Ensure they aren’t prone to accidental or malicious deletion.

{
    "Sid": "DenyVPCFlowLogDeletion",
    "Action": [
        "ec2:DeleteFlowLogs",
        "logs:DeleteLogGroup",
        "logs:DeleteLogStream"
    ],
    "Resource": [
        "arn:aws:ec2:*:*:vpc-flow-log/*",
        "arn:aws:logs:*:*:log-group:/aws/vpc-flows*"
    ],
    "Effect": "Deny",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny deleting any VPC flog log, and deny deleting any CloudWatch Logs group or stream with the prefix /aws/vpc-flows. These can be bypassed by the administrator.

Deny Default VPC & Subnet Creation

Why: Using the default VPC indicates a lack of network planning, and the default VPC security group is overly permissive.

{
    "Sid": "DenyDefaultVPCSubnetCreation",
    "Effect": "Deny",
    "Action": ["ec2:CreateDefaultSubnet", "ec2:CreateDefaultVpc"],
    "Resource": "*"
}

How: Deny creating a default subnet and VPC.

Deny Launching EC2 with Public IP

Why: Direct public access to EC2 instances is almost always a bad idea.

{
    "Sid": "DenyEC2WithPublicIP",
    "Effect": "Deny",
    "Action": "ec2:RunInstances",
    "Resource": "*",
    "Condition": {
        "Bool": {
            "ec2:AssociatePublicIpAddress": "true"
        }
    }
}

How: Deny associating a public IP address with an EC2 instance at launch time.

Deny Adding a Public IP to an EC2 Instance

Why: It’s possible to attach a public IP to an EC2 instance after launch, so additional protections are needed.

{
    "Sid": "DenyAttachingPublicIP",
    "Effect": "Deny",
    "Action": [
        "ec2:AssociateAddress",
        "ec2:CreateNetworkInterface",
        "ec2:ModifyNetworkInterfaceAttribute"
    ],
    "Resource": "*",
    "Condition": {
        "BoolIfExists": {
            "ec2:PublicIpAddress": "true"
        }
    }
}

How: Deny the ec2:AssociateAddress action as well as creating or modifying an ENI to have a public IP address.

Deny Unencrypted EBS Snapshots

Why: Unencrypted EBS volume snapshots can expose sensitive data.

{
    "Sid": "DenyUnencryptedEBSVolumeSnapshots",
    "Effect": "Deny",
    "Action": ["ec2:CreateSnapshot", "ec2:CopySnapshot"],
    "Resource": "*",
    "Condition": {
        "Bool": {
            "ec2:Encrypted": "false"
        }
    }
}

How: Deny creating or copying of snapshots that aren’t encrypted using the ec2:Encrypted condition key.

Deny EBS Snapshot Sharing

Why: Sharing EBS snapshots with other AWS accounts, even if inside your organization, could be a security risk.

{
    "Sid": "DenyEBSSnapshotSharing",
    "Effect": "Deny",
    "Action": ["ec2:ModifySnapshotAttribute", "ec2:CopySnapshot"],
    "Resource": "arn:aws:ec2:*:*:snapshot/*",
    "Condition": {
        "StringEquals": {
            "ec2:Attribute": "createVolumePermission"
        }
    }
}

How: Deny modifications to the createVolumePermission EC2 attribute to prevent users from changing the list of AWS accounts allowed to create EBS volumes from shared snapshots.

Deny Unencrypted EBS Volumes

Why: Creating or mounting unencrypted EBS volumes pose a security risk in exposing sensitive data.

{
    "Sid": "DenyUnencryptedEBSVolumes",
    "Effect": "Deny",
    "Action": ["ec2:CreateVolume", "ec2:RunInstances", "ec2:AttachVolume"],
    "Resource": "*",
    "Condition": {
        "Bool": {
            "ec2:Encrypted": "false"
        }
    }
}

How: Deny creation and mounting of unencrypted EBS volumes and running an EC2 instance with an unencrypted EBS volume using the ec2:Encrypted condition key.

Deny Disabling of EBS Encryption

Why: It shouldn’t be possible to disable EBS volume encryption by default, nor to change the default KMS key that is used for EBS volume encryption. Since there is no built-in automatic protection for blocking deletion of KMS keys that were used to encrypt EBS volumes.

{
    "Sid": "DenyDisablingEBSDefaultEncryption",
    "Effect": "Deny",
    "Action": [
        "ec2:DisableEbsEncryptionByDefault",
        "ec2:ModifyEbsDefaultKmsKeyId"
    ],
    "Resource": "*"
}
{
    "Sid": "DenyModifyingEBSVolumeEncryption",
    "Effect": "Deny",
    "Action": [
        "ec2:ModifyVolume",
        "ec2:CreateVolume",
        "ec2:ModifySnapshotAttribute"
    ],
    "Resource": "*",
    "Condition": {
        "Bool": {
            "ec2:Encrypted": "false"
        }
    }
}

How: Deny disabling EBS encryption by default, modifying the default KMS key used for EBS, and creating or modifying EBS volumes that aren’t encrypted.

Deny EC2 without IMDSv2

Why: Instance Metadata Service Version 2 (IMDSv2) uses a session-based access method instead of IMDSv1’s request/response model, mitigating Server Side Request Forgery vulnerabilities, namely credential extraction with improperly-handled URLs in user input.

{
    "Sid": "DenyEC2WithoutIMDSv2",
    "Effect": "Deny",
    "Action": "ec2:RunInstances",
    "Resource": "*",
    "Condition": {
        "StringEquals": {
            "ec2:MetadataHttpTokens": "required"
        }
    }
}

How: Deny starting EC2 instances without IMDSv2 enabled, using the ec2:MetadataHttpTokens condition key.

Deny Attaching to an Elastic IP Instance

Why: EC2 instances should never be publicly accessible. Instead, prefer Elastic Load Balancers (ELBs) for distributing traffic to EC2 instances.

{
    "Sid": "DenyEIPForEC2InstancesExceptELB",
    "Effect": "Deny",
    "Action": ["ec2:AssociateAddress", "ec2:AllocateAddress"],
    "Resource": "*",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalArn": "arn:aws:iam::*:role/aws-service-role/elasticloadbalancing.amazonaws.com/*"
        }
    }
}

How: Deny actions that allocate or associate EIPs, except in the context of an ELB.

Prevent VPCs from Obtaining Internet Access

Why: A VPC without Internet access should not be able to obtain Internet access, by means of adding an Internet gateway or VPC peering connection.

{
    "Sid": "DenyVPCFromGainingInternetAccess",
    "Effect": "Deny",
    "Action": [
        "ec2:AttachInternetGateway",
        "ec2:CreateInternetGateway",
        "ec2:CreateEgressOnlyInternetGateway",
        "ec2:CreateVpcPeeringConnection",
        "ec2:AcceptVpcPeeringConnection",
        "globalaccelerator:Create*",
        "globalaccelerator:Update*"
    ],
    "Resource": "*"
}

How: Deny creating or attaching Internet gateways, VPC peering connections, and AWS Global Accelerator instances.

EFS

Prevent Unencrypted EFS Volumes

Why: AWS Elastic File System (EFS) can be used to create unencrypted file systems.

{
  "Sid": "DenyUnencryptedEFS",
  "Effect": "Deny",
  "Action": "elasticfilesystem:CreateFileSystem",
  "Resource": "*",
  "Condition": {
    "Bool": {
      "elasticfilesystem:Encrypted": "false"
    }
  }
}

How: Deny actions using where elasticfilesystem:Encrypted condition key is false.

KMS

Deny KMS Key Deletion

Why: AWS Key Management Service (KMS) is used in encrypting and decrypting data. Accidental or malicious deletion of KMS keys will result in permanent loss of access to encrypted data. This is a critical component of many AWS services, since encryption is often enabled by default.

{
    "Sid": "DenyKMSKeyDeletion",
    "Effect": "Deny",
    "Action": ["kms:ScheduleKeyDeletion", "kms:Delete*"],
    "Resource": "*",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalArn": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny kms:ScheduleKeyDeletion and kms:DeleteKey actions, except for an IAM Identity Center user with a specific AdministratorAccess role.

S3

Deny S3 Deletion

Why: You may want to disable deletion of buckets and objects in S3 for compliance and security reasons. This is especially true if you’re delivering sensitive logs or data into S3 buckets and need to guarantee no-delete semantics.

{
    "Sid": "DenyS3Deletion",
    "Action": [
        "s3:DeleteBucket",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion"
    ],
    "Resource": "*",
    "Effect": "Deny",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny S3 delete actions at the bucket and object levels. Note that replacing an object is still allowed and this policy isn’t written to enforce full write-only semantics. For that, you’d likely need to restrict more actions.

Deny S3 Modification without Admin

Why: Unauthorized modifications to S3 buckets or objects could affect security or availability.

{
    "Sid": "DenyS3ModificationWithoutAdmin",
    "Effect": "Deny",
    "Action": [
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:PutBucketPolicy",
        "s3:DeleteBucket",
        "s3:PutBucketAcl",
        "s3:DeleteBucketPolicy"
    ],
    "Resource": "arn:aws:s3:::*",
    "Condition": {
        "StringNotLike": {
            "aws:PrincipalArn": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny S3 actions that modify or delete buckets or objects.

Deny Glacier Deletion

Why: Glacier vaults and archives often contain data intended for long-term archival storage for compliance or other reasons.

{
    "Sid": "DenyGlacierDeletion",
    "Effect": "Deny",
    "Action": ["glacier:DeleteArchive", "glacier:DeleteVault"],
    "Resource": ["arn:aws:glacier:*:*:vaults/*"],
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny Glacier delete actions except by the administrator.

Remote Access Manager

Deny Resource Sharing Outside the Organization

Why: AWS Remote Access Manager (RAM) lets you share share resources within an organization and with IAM. It’s typically used to avoid duplicating resources in AWS accounts in the organization, but can also be used to share with AWS accounts that are outside your organization.

{
    "Sid": "DenyExternalResourceSharing",
    "Action": "*",
    "Resource": "*",
    "Effect": "Deny",
    "Condition": {
        "Bool": {
            "ram:AllowsExternalPrincipals": "true"
        },
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny any action where the ram:AllowsExternalPrincipals condition key is true. AWS automatically sets this value to true depending on the RAM resource share configuration. If you’re sharing within the org, it will be set to false, thereby allowing the action.

CloudTrail

Deny CloudTrail Modification

Why: CloudTrail should be treated as an append-only audit log of all activity in the account, and should never be deleted directly, especially outside of a lifecycle policy.

{
    "Sid": "DenyCloudTrailModification",
    "Effect": "Deny",
    "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:PutEventSelectors",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail"
    ],
    "Resource": ["arn:aws:cloudtrail:*:*:trail/TRAIL_NAME"]
}

How: Deny deleting, stopping, or modifying a trail or what it captures.

Systems Manager

Deny SSM Login to EC2

Why: EC2 instances that are registered with Systems Manager may allow users to start an interactive session with remote code execution, even if the instance has no SSH server running.

{
    "Sid": "DenySSMStartSession",
    "Effect": "Deny",
    "Action": ["ssm:StartSession"],
    "Resource": "*",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny ssm:StartSession action, preventing a channel from being started.

Deny SSM ECS Exec

Why: ECS clusters with ECS Exec enabled allow users to use Systems Manager Session Manager to connect remotely both as interactive sessions and to execute one-off commands inside a container of a running ECS task. This should be restricted.

{
    "Sid": "DenyECSExec",
    "Effect": "Deny",
    "Action": ["ecs:ExecuteCommand"],
    "Resource": "*",
    "Condition": {
        "ArnNotLike": {
            "aws:PrincipalARN": [
                "arn:aws:iam::ACCOUNT_ID:role/aws-reserved/sso.amazonaws.com/SSO_REGION/SSO_ADMIN_ROLE_NAME"
            ]
        }
    }
}

How: Deny ssm:StartSession and ecs:ExecuteCommand actions. Both are needed because ECS Exec allows both interactive sessions and command execution, which use two different AWS service actions.

RDS

Deny Unencrypted RDS

Why: You should keep all RDS databases, snapshots, and backups encrypted at rest.

{
    "Sid": "DenyUnencryptedRDSInstances",
    "Effect": "Deny",
    "Action": [
        "rds:CreateDBInstance",
        "rds:RestoreDBInstanceFromDBSnapshot",
        "rds:RestoreDBInstanceToPointInTime"
    ],
    "Resource": "*",
    "Condition": {
        "Bool": {
            "rds:StorageEncrypted": "false"
        }
    }
}
{
    "Sid": "DenyUnencryptedRDSSnapshots",
    "Effect": "Deny",
    "Action": ["rds:CreateDBSnapshot", "rds:CopyDBSnapshot"],
    "Resource": "*",
    "Condition": {
        "Bool": {
            "rds:StorageEncrypted": "false"
        }
    }
}

How: Deny RDS actions using the rds:StorageEncrypted condition key.

Summary

You may not need all of these policies in your environment, or you may have more stringent requirements, but these should give you a good starting point for further environment hardening.

Although these SCPs deny many sensitive operations, they also provide an exception for the administrator role. As I’ve mentioned numerous times by now, you should absolutely not use this in day-to-day operations or issue credentials for use in your local AWS CLI environment! It’s a good idea to setup alerting for when this role is assumed, to ensure it’s only used in exceptional circumstances.

Useful References

If you need to look up specific services’ actions, resource types, and condition keys, refer to Service Authorization Reference.

Verify SCP Enforcement

Unfortunately, IAM Policy Simulator isn’t useful for evaluating SCPs, given that nearly all of the ones I showed include condition keys, which the policy simulator doesn’t support.

Instead, the best way to see the effect of SCPs is to actually make real live API calls. This is why it’s a good idea to have an identically configured environment where you can test SCPs’ effect on roles and permissions. A non-live PreProd environment could serve as a testing ground for SCPs, if it’s an exact clone of Production with the same IAM entities.

If you have more useful SCPs that I missed, let me know!