diff --git a/README.md b/README.md index b10f181..5bb2e1b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,33 @@ +# AWS Organization SCP Terraform Module + Inspired by the great work documenting AWS security practices in [asecure.cloud](https://asecure.cloud/whatsnew), this module is meant to define common Service Control Policies (SCP) to apply to accounts or Organizational Units (OU) in an AWS Organization. The following policies are supported -* Deny the root user from taking any action +## Organizations + * Deny the ability for an AWS account to leave an AWS organization -* Require S3 objects be encrypted +* Deny all access to an AWS account + +## IAM + +* Deny the root user from taking any action * Deny the ability to create IAM users and access keys in an AWS account +* Protect sensitive IAM roles from modification or deletion + +## S3 + +* Protect sensitive S3 buckets from deletion + +## KMS + * Deny the ability to delete KMS keys + +## Route53 + * Deny the ability to delete Route53 zones -* Deny all access to an AWS account -* Deny the ability to delete Cloudwatch logs + +## CloudWatch Logs + +* Deny the ability to delete CloudWatch Logs ## Usage @@ -36,7 +56,7 @@ resource "aws_organizations_organizational_unit" "suspended" { module "org_scps" { source = "trussworks/org-scp/aws" - version = "~> 1.2.0" + version = "~> 1.4.0" # applies to all accounts # - don't allow all accounts to be able to leave the org @@ -53,9 +73,22 @@ module "org_scps" { # applies to all prod accounts # - don't allow deleting KMS keys # - don't allow deleting Route53 zones + # - don't allow deleting CloudWatch logs + # - protect terraform statefile bucket + # - protect OrganizationAccountAccessRole deny_deleting_kms_keys_target_ids = [aws_organizations_organizational_unit.prod.id] deny_deleting_route53_zones_target_ids = [aws_organizations_organizational_unit.prod.id] deny_deleting_cloudwatch_logs_target_ids = [aws_organizations_organizational_unit.prod.id] + protect_s3_bucket_target_ids = [aws_organizations_organizational_unit.prod.id] + protect_iam_role_target_ids = [aws_organizations_organizational_unit.prod.id] + + protect_s3_bucket_resources = [ + "arn:aws:s3:::prod-terraform-state-us-west-2", + "arn:aws:s3:::prod-terraform-state-us-west-2/*" + ] + protect_iam_role_resources = [ + "arn:aws:iam::*:role/OrganizationAccountAccessRole" + ] # applies to all suspended accounts # - don't allow any access @@ -76,11 +109,15 @@ module "org_scps" { |------|-------------|------|---------|:-----:| | deny\_all\_access\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP dening all access | `list(string)` | `[]` | no | | deny\_creating\_iam\_users\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying the ability to create IAM users or Access Keys | `list(string)` | `[]` | no | -| deny\_deleting\_cloudwatch\_logs\_target\_ids | Target ids (AWS Account or Organizational Unit) to delete VPC flow logs, log groups, or log streams | `list(string)` | `[]` | no | +| deny\_deleting\_cloudwatch\_logs\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying deletion of CloudWatch, flowlogs, log groups, or log streams | `list(string)` | `[]` | no | | deny\_deleting\_kms\_keys\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying deleting KMS keys | `list(string)` | `[]` | no | | deny\_deleting\_route53\_zones\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying deleting Route53 Hosted Zones | `list(string)` | `[]` | no | | deny\_leaving\_orgs\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying the ability to leave the AWS Organization | `list(string)` | `[]` | no | | deny\_root\_account\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP denying the root user from taking any action | `list(string)` | `[]` | no | +| protect\_iam\_role\_resources | IAM role resource ARNs to protect from modification and deletion | `list(string)` |
[| no | +| protect\_iam\_role\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP protecting IAM roles | `list(string)` | `[]` | no | +| protect\_s3\_bucket\_resources | S3 bucket resource ARNs to protect from bucket and object deletion | `list(string)` |
""
]
[| no | +| protect\_s3\_bucket\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP protecting S3 buckets and objects | `list(string)` | `[]` | no | | require\_s3\_encryption\_target\_ids | Target ids (AWS Account or Organizational Unit) to attach an SCP requiring S3 encryption | `list(string)` | `[]` | no | ## Outputs diff --git a/main.tf b/main.tf index 5f68c25..3ce6497 100644 --- a/main.tf +++ b/main.tf @@ -231,3 +231,72 @@ resource "aws_organizations_policy_attachment" "deny_deleting_cloudwatch_logs" { policy_id = aws_organizations_policy.deny_deleting_cloudwatch_logs.id target_id = element(var.deny_deleting_cloudwatch_logs_target_ids.*, count.index) } + +# +# Protect S3 Buckets +# + +data "aws_iam_policy_document" "protect_s3_buckets" { + statement { + effect = "Deny" + actions = [ + "s3:DeleteBucket", + "s3:DeleteObject", + "s3:DeleteObjectVersion", + ] + resources = var.protect_s3_bucket_resources + } +} + +resource "aws_organizations_policy" "protect_s3_buckets" { + count = length(var.protect_s3_bucket_target_ids) + name = "protect-s3-buckets" + description = "Protect S3 buckets form bucket and object deletion" + content = data.aws_iam_policy_document.protect_s3_buckets.json +} + +resource "aws_organizations_policy_attachment" "protect_s3_buckets" { + count = length(var.protect_s3_bucket_target_ids) + + policy_id = aws_organizations_policy.protect_s3_buckets[0].id + target_id = element(var.protect_s3_bucket_target_ids.*, count.index) +} + +# +# Protect IAM roles +# + +data "aws_iam_policy_document" "protect_iam_roles" { + statement { + effect = "Deny" + actions = [ + "iam:AttachRolePolicy", + "iam:DeleteRole", + "iam:DeleteRolePermissionsBoundary", + "iam:DeleteRolePolicy", + "iam:DetachRolePolicy", + "iam:PutRolePermissionsBoundary", + "iam:PutRolePolicy", + "iam:UpdateAssumeRolePolicy", + "iam:UpdateRole", + "iam:UpdateRoleDescription" + ] + resources = var.protect_iam_role_resources + } +} + +resource "aws_organizations_policy" "protect_iam_roles" { + count = length(var.protect_iam_role_target_ids) + + name = "protect-iam-roles" + description = "Protect IAM roles from modification or deletion" + content = data.aws_iam_policy_document.protect_iam_roles.json +} + +resource "aws_organizations_policy_attachment" "protect_iam_roles" { + count = length(var.protect_iam_role_target_ids) + + policy_id = aws_organizations_policy.protect_iam_roles[0].id + target_id = element(var.protect_iam_role_target_ids.*, count.index) +} + diff --git a/variables.tf b/variables.tf index dd45966..f978ff4 100644 --- a/variables.tf +++ b/variables.tf @@ -41,7 +41,31 @@ variable "require_s3_encryption_target_ids" { } variable "deny_deleting_cloudwatch_logs_target_ids" { - description = "Target ids (AWS Account or Organizational Unit) to delete VPC flow logs, log groups, or log streams" + description = "Target ids (AWS Account or Organizational Unit) to attach an SCP denying deletion of CloudWatch, flowlogs, log groups, or log streams" type = list(string) default = [] } + +variable "protect_s3_bucket_target_ids" { + description = "Target ids (AWS Account or Organizational Unit) to attach an SCP protecting S3 buckets and objects" + type = list(string) + default = [] +} + +variable "protect_s3_bucket_resources" { + description = "S3 bucket resource ARNs to protect from bucket and object deletion" + type = list(string) + default = [""] +} + +variable "protect_iam_role_target_ids" { + description = "Target ids (AWS Account or Organizational Unit) to attach an SCP protecting IAM roles" + type = list(string) + default = [] +} + +variable "protect_iam_role_resources" { + description = "IAM role resource ARNs to protect from modification and deletion" + type = list(string) + default = [""] +}
""
]