Jack RyanAug 25, 2022 12:00:00 AM9 min read

Understanding AWS Service Control Policies

Understanding AWS Service Control Policies

Understanding AWS Service Control Policies

Access management within your cloud environment can be one of the most demanding challenges within AWS, with the constant addition of new resources and services that your business may wish to utilize. AWS Service Control Policies (SCPs) can be a powerful tool as part of a layered approach to enforcing least privilege.

Whether you are leveraging generic AWS-managed IAM policies for one account or creating custom policies to grant least privilege across hundreds of accounts, SCPs can be applied to your AWS Organization to define the maximum allowable privileges for any identity and to ensure specific conditions are met when a particular action is utilized.

As the name suggests, Service Control Policies allow you to define the list of AWS Services and IAM actions permissible within your AWS environments. If your organization is mandated to adhere to a regulatory standard or compliance requirements, entire AWS services can be implicitly or explicitly denied to ensure your policies are not violated, regardless of the IAM policies attached to an identity. Beyond regulatory or compliance mandates, SCPs are also often leveraged to “opt-in” to which AWS services can be consumed, restrict the AWS regions you allow within your accounts, and to ensure that specific actions which do not align with your security policies are disallowed.

Implementation Patterns

AWS SCPs require the usage of AWS Organizations, specifically the ‘All Features’ feature set; it is not possible with only Consolidated Billing enabled.

SCPs can be applied to any of 3 levels within your AWS Organization:

  • Organization’s Root level (applies to all OUs, all AWS accounts other than the AWS Organization’s Management account)
  • Organizational Unit (OU) level (applies to all accounts in the OU and Sub-OUs)
  • Account level (applies only to the account(s) it’s attached to)
Credit: https://aws.amazon.com/blogs/mt/implement-read-only-service-control-policy-in-aws-organizations/

Credit: https://aws.amazon.com/blogs/mt/implement-read-only-service-control-policy-in-aws-organizations/

There are two overarching ways to implement SCPs: Implicit Allow with Explicit Deny (Deny List), or Implicit Deny with Explicit Allow (Allow List). The default SCP applied to an AWS Organization grants FullAWSAccess and does not perform any restrictions. To remedy this and restrict access, you can either apply your custom Allow-List SCP and detach the FullAWSAccess policy, or retain the FullAWSAccess policy and apply a separate policy to deny specific services/actions.

The proper direction for your organization will depend on whether you are beholden to regulatory or compliance requirements, as well as your risk appetite.

Deny List:

  • Explicit Deny, Implicit Allow

  • FullAWSAccess + Deny Action

    • This is best leveraged only when you wish to disallow a limited subset of actions, and leave the majority of privilege management to your AWS IAM policies. One risk you are allowing your Users and Roles to leverage AWS Managed Policies, when a new service/action is added to that policy, your SCP may not automatically deny the new action.

    • FullAWSAccess Policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
    • Deny List Policy Example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDisallowedActions",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:PutEventSelectors",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail",
        "config:DeleteConfigRule",
        "config:DeleteConfigurationAggregator",
        "config:DeleteConfigurationRecorder",
        "config:DeleteDeliveryChannel",
        "config:DeleteEvaluationResults",
        "config:DeleteRetentionConfiguration",
        "config:StopConfigurationRecorder",
        "ec2:DeleteFlowLogs",
        "guardduty:DisassociateFromMasterAccount",
        "logs:DeleteLogGroup",
        "logs:DeleteLogStream",
        "organizations:LeaveOrganization"
      ],
      "Resource": "*"
    }
  ]
}

Allow List:

  • Explicit Allow, Implicit Deny

  • Create and apply a Service Control Policy that grants the specific actions you wish to permit, and ensure the FullAWSAccess policy is removed

    • Only the AWS Services and actions you specify will be permitted.

    • This pattern avoids consuming an additional SCP per Organization hierarchy level, allowing for 5 custom SCPs per level before reaching the service limit.

    • Allow List Example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ExplicitAllow",
      "Effect": "Allow",
      "Action": [
        "access-analyzer:*",
        "account:*",
        "backup:*",
        "billing:*",
        "billingconductor:*",
        "budgets:*",
        "cloudformation:*",
        "cloudshell:*",
        "cloudtrail:*",
        "codebuild:*",
        "codecommit:*",
        "config:*",
        "ec2:*",
        "elasticloadbalancing:*",
        "events:*",
        "health:*",
        "iam:*",
        "kms:*",
        "lambda:*",
        "logs:*",
        "organizations:*",
        "pricing:*",
        "s3:*",
        "secretsmanager:*",
        "servicecatalog:*",
        "sns:*",
        "sqs:*",
        "sso-directory:*",
        "sso:*",
        "sts:*",
        "support:*",
        "sustainability:*",
        "tax:*"
      ],
      "Resource": "*"
    }
  ]
}
  • Deny NotAction

    • If retaining the FullAWSAccess policy is desired, but you wish to ensure only actions you explicitly specify are permitted, a Deny policy paired with the NotAction element can meet your needs.

    • Deny NotAction example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNotAction",
      "Effect": "Deny",
      "NotAction": [
        "access-analyzer:*",
        "account:*",
        "backup:*",
        "billing:*",
        "billingconductor:*",
        "budgets:*",
        "cloudformation:*",
        "cloudshell:*",
        "cloudtrail:*",
        "codebuild:*",
        "codecommit:*",
        "config:*",
        "ec2:*",
        "elasticloadbalancing:*",
        "events:*",
        "health:*",
        "iam:*",
        "kms:*",
        "lambda:*",
        "logs:*",
        "organizations:*",
        "pricing:*",
        "s3:*",
        "secretsmanager:*",
        "servicecatalog:*",
        "sns:*",
        "sqs:*",
        "sso-directory:*",
        "sso:*",
        "sts:*",
        "support:*",
        "sustainability:*",
        "tax:*"
      ],
      "Resource": "*"
    }
  ]
}

As AWS Organizations themselves are rarely static, your footprint is likely to continue to increase both from an Account and an Organizational Unit perspective as you onboard additional teams, create additional workloads, and build out further segmentation and security boundaries within your environment.

To increase the manageability and success of your Service Control Policies, consider leveraging HashiCorp Terraform to define and provision them via Infrastructure as Code (IaC). When access is restricted to and implemented through your CI/CD pipeline, you can ensure that unauthorized changes are not made, and that your developers are empowered to understand the specific conditions that must be met when they are contributing infrastructure and resources to your AWS environments.

Example SCP attached at the Root level, defined in Terraform HCL:

data "aws_organizations_organization" "org" {}

data "aws_organizations_organizational_units" "ou" {
 parent_id = data.aws_organizations_organization.org.roots[0].id
}

resource "aws_organizations_policy_attachment" "root_allow_list" {
 policy_id = aws_organizations_policy.root_allow_list.id
 target_id = data.aws_organizations_organization.org.roots[0].id
}

resource "aws_organizations_policy" "root_allow_list" {
 name = "root_allow_list"
 type = "SERVICE_CONTROL_POLICY"

 content = data.aws_iam_policy_document.root_allow_list.json
}

data "aws_iam_policy_document" "root_allow_list" {
 statement {
   sid       = "AllowedActions"
   effect    = "Allow"
   resources = ["*"]
   actions = [
     "s3:*",
     "ec2:*",
     "iam:*".
   ]
 }
}

SCP Limitations:

When exploring SCPs, there are a few important limitations to be aware of and design compensating controls around.

  • SCPs have no effect in your AWS Organization’s Management account, often called the “Organization Root Account” or “Billing Account”. Therefore, it’s important to ensure access granted to the Management account is heavily restricted, and that resources and workloads are not deployed to this account when avoidable.
  • SCPs do not apply to AWS Service-Linked Roles, meaning they can not be utilized to restrict the permissions available to AWSServiceRoleForElasticLoadBalancing for example.
  • SCPs do not apply to principals that exist outside of your account; where a Resource-based policy such as an S3 Bucket Policy is leveraged to grant access to principals outside your account, your SCPs do not have the ability to restrict the actions taken.
  • SCPs have a finite size limit of 5120 bytes, and an attachment limit per Organization level of 5. If nested OUs are being leveraged, for example nesting a “development” OU under your “workloads” OU, each level may have 5 SCPs attached.

When your Service Control Policy statement is an “Allow”, not a “Deny”, you are unable to utilize the following Policy elements:

  • NotAction
  • Resource
  • Condition

This means that in order to permit an action with a specific condition or resource in place, or permit all actions other than a specific subset of actions, you must utilize a Deny statement and adjust your logic accordingly.

AWS SCP Examples with Condition Elements:

  • AWS region restriction
    • If your organization has a requirement to operate in only a subset of geographic regions, SCPs can be leveraged to deny access to non-global services in other regions.
    • For example, to deny attempts to launch EC2 instances outside of the US regions:
{
  "Sid": "RestrictRegionExample",
  "Resource": "*",
  "Effect": "Deny",
  "Action": ["ec2:RunInstances"],
  "Condition": {
    "StringNotEquals": {
      "aws:RequestedRegion": [
        "us-east-1",
        "us-east-2",
        "us-west-1",
        "us-west-2"
      ]
    }
  }
}
  • Root principal restriction
    • Under normal circumstances, no actions should be taken by an AWS Account’s Root identity; it is highly privileged, and usage of a generic identity not tied to any specific individual may not allow for non-repudiation.
    • When properly restricting Root access, establish a process to temporarily lift root restriction on a specific account to allow for actions such as S3 Bucket Policy recovery.
    • To disallow all actions taken as the Root user:
{
  "Sid": "DenyAllRootAccess",
  "Effect": "Deny",
  "Action": ["*"],
  "Resource": "*",
  "Condition": {
    "StringLike": {
      "aws:PrincipalArn": ["arn:aws:iam::*:root"]
    }
  }
}
  • IP restriction
    • You can restrict access to a public AWS API to only a specific IP address or subnet that you control, such as your VPN Concentrators, PAM solution, or Bastion hosts, that can be implemented via SCPs.
    • For example, to only allow sessions from IP address 1.2.3.4 to delete S3 Buckets:
{
  "Sid": "IPRestrictDeleteBucket",
  "Resource": "*",
  "Effect": "Deny",
  "Action": ["s3:DeleteBucket"],
  "Condition": {
    "NotIpAddress": {
      "aws:SourceIp": ["1.2.3.4/32"]
    }
  }
}
  • EC2 Image Source Enforcement
    • Disallow deploying EC2 instances from an image that is not controlled and owned by one of your AWS accounts.
    • For example, if your organization has a requirement that all EC2 instances must leverage one of your Golden Images to ensure your hardening, security suite, and other requirements are present.
{
  "Sid": "EnforceGoldenImageSourceAccounts",
  "Resource": "*",
  "Effect": "Deny",
  "Action": ["ec2:RunInstances"],
  "Condition": {
    "StringNotEquals": {
      "ec2:Owner": ["123456789012"]
    }
  }
}
  • Restrict specific actions to a specific identity
    • Some actions may be restricted to ensure only specific identities, such as an IAM role are able to perform given actions in your Organization.
    • For example, to allow only your Terraform Pipeline’s IAM role to create and delete VPCs:
{
  "Sid": "DenyAccessWithException",
  "Effect": "Deny",
  "Action": ["ec2:CreateVPC", "ec2:DeleteVPC"],
  "Resource": "*",
  "Condition": {
    "StringNotLike": {
      "aws:PrincipalARN": "arn:aws:iam::123456789012:role/terraform_role"
    }
  }
}

Testing, Error Messages, and Troubleshooting:

  • When a request is denied by a Service Control Policy, the error message will explicitly denote it was a Service Control Policy that denied the action, not an IAM policy, resource-based policy, trust policy, Permissions Boundary, etc.

    • {ARN} is not authorized to perform: {service:action} with an explicit deny in a service control policy
  • If you are receiving an Explicit Deny error message, ensure that each level of your AWS Organization applicable to the AWS Account has a statement that will allow the action, and that there is not a statement present which would explicitly deny the action.

  • As SCPs can have an impact on your operations when not properly tested, it is of utmost importance to develop a testing capability for your SCPs. If your existing AWS account architecture has separate logical environments for development vs production, ensure changes are applied to only your lower environments, tested, and then promoted. Otherwise, a “policy staging” AWS account can be provisioned in your AWS organization to allow for testing the effects of your SCPs without affecting any running workloads.

Summary

AWS Service Control Policies are a powerful tool to limit your organization’s exposure to new, intentionally disallowed, or simply unfamiliar AWS Services and Resources at any organizational maturity level. Particularly where your organization may have several mechanisms to create and manage AWS IAM policies, SCPs can help ensure overly permissive access is not granted to services, resources, and even specific configuration conditions that could expose your organization to security risks if they are not already addressed via other means or explicitly denied on every single IAM Policy you utilize.

RELATED ARTICLES

The information presented in this article is accurate as of 7/19/23. Follow the ScaleSec blog for new articles and updates.