├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── main.tf └── modules ├── hardening-global ├── cloudtrail.tf ├── config.tf ├── flowlogs.tf ├── main.tf ├── outputs.tf ├── password.tf └── variables.tf └── hardening-region ├── config.tf ├── flowlogs.tf ├── main.tf ├── outputs.tf ├── variables.tf └── vpc.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | *.tfstate 3 | *.tfstate.backup 4 | *.tfvars 5 | 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All contributions and suggestions are welcome! 4 | 5 | * If you want to report a bug, or suggest a feature, please go to the GitHub "Issues" page. 6 | * If you want to contribute to the codebase, feel free to open a Pull Request. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Kiratech SpA 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-hardening 2 | 3 | This repository contains a Terraform module providing a secure baseline for a newly created [Amazon Web Services](https://aws.amazon.com) account. AWS accounts default settings are more oriented towards easiness of use rather than applying the recommended secure best practices. `terraform-aws-hardening` is implemented as a set of Terraform configurations applying the best practices to all AWS regions. 4 | 5 | **Make sure to use it only on accounts not containing any user-created resource (servers, databases, VPCs, etc.) as it has been tested only on newly created accounts** 6 | 7 | The tool is heavily inspired by the [AWS Benchmarks - CIS Amazon Web Services Foundations v1.2.0](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf) and by the automated tool [CloudSploit](https://cloudsploit.com/). However, this tool does not guarantee a full compliance for an existing account: it operates only on newly created accounts by providing saner defaults. Again: do not use it on accounts that are not brand new (i.e., not containing user-created objects or data). 8 | 9 | As today, applying this tool gets you a 100% green score on CloudSploit for a newly created AWS account. 10 | 11 | ## Features 12 | This is the list of features provided by this module. When applicable, the relevant CIS Benchmark section is provided. Some of the features are not included in the [AWS free tier](https://aws.amazon.com/free/). 13 | 14 | * Identity and Access Management 15 | * Ensure credentials unused for 90 days or greater are disabled (`CIS 1.3`) 16 | * Ensure IAM password policy requires at least one uppercase letter (`CIS 1.5`) 17 | * Ensure IAM password policy require at least one lowercase letter (`CIS 1.6`) 18 | * Ensure IAM password policy require at least one symbol (`CIS 1.7`) 19 | * Ensure IAM password policy require at least one number (`CIS 1.8`) 20 | * Ensure IAM password policy requires minimum length of 14 or greater (`CIS 1.9`) 21 | * Ensure IAM password policy prevents password reuse (`CIS 1.10`) 22 | * Ensure IAM password policy expires passwords within 90 days or less (`CIS 1.11`) 23 | * Logging 24 | * Ensure CloudTrail is enabled in all regions (`CIS 2.1`) 25 | * Ensure CloudTrail log file validation is enabled (`CIS 2.2`) 26 | * Ensure the S3 bucket CloudTrail logs to is not publicly accessible (`CIS 2.3`) 27 | * Ensure CloudTrail trails are integrated with CloudWatch Logs (`CIS 2.4`) 28 | * Ensure AWS Config is enabled in all regions (`CIS 2.5`) 29 | * Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (`CIS 2.6`) 30 | * Ensure CloudTrail logs are encrypted at rest using KMS CMKs (`CIS 2.7`) 31 | * Ensure rotation for customer created CMKs is enabled (`CIS 2.8`) 32 | * Ensure VPC flow logging is enabled in all VPCs (`CIS 2.9`) *Only for the default VPC on each supported region* 33 | * Networking 34 | * Ensure the default security group of every VPC restricts all traffic (`CIS 4.3`) *Only for the default VPC on each supported region* 35 | 36 | ## How it works 37 | 38 | ### Requirements 39 | * [Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html) >= 0.11 40 | 41 | ### Usage 42 | Copy and paste into your Terraform configuration, and run `terraform init`: 43 | ``` 44 | module "aws-hardening" { 45 | source = "github.com/kiratech/terraform-aws-hardening" 46 | } 47 | ``` 48 | 49 | ## Contributing 50 | Contributions are very welcome! Check out the [Guidelines](CONTRIBUTING.md) for instructions. 51 | 52 | ## License 53 | This project is licensed under the terms of the **MIT** license. Check out the full license [here](LICENSE). 54 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | ## Global account hardening 2 | module "aws-global" { 3 | aws_region = "us-east-1" 4 | source = "modules/hardening-global" 5 | } 6 | 7 | ## Region-specific hardening 8 | ## List taken from: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html 9 | 10 | module "aws-us-east-2" { 11 | aws_region = "us-east-2" 12 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 13 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 14 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 15 | source = "modules/hardening-region" 16 | } 17 | 18 | module "aws-us-east-1" { 19 | aws_region = "us-east-1" 20 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 21 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 22 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 23 | source = "modules/hardening-region" 24 | } 25 | 26 | module "aws-us-west-1" { 27 | aws_region = "us-west-1" 28 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 29 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 30 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 31 | source = "modules/hardening-region" 32 | } 33 | 34 | module "aws-us-west-2" { 35 | aws_region = "us-west-2" 36 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 37 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 38 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 39 | source = "modules/hardening-region" 40 | } 41 | 42 | module "aws-ap-south-1" { 43 | aws_region = "ap-south-1" 44 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 45 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 46 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 47 | source = "modules/hardening-region" 48 | } 49 | 50 | module "aws-ap-northeast-2" { 51 | aws_region = "ap-northeast-2" 52 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 53 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 54 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 55 | source = "modules/hardening-region" 56 | } 57 | 58 | module "aws-ap-southeast-1" { 59 | aws_region = "ap-southeast-1" 60 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 61 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 62 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 63 | source = "modules/hardening-region" 64 | } 65 | 66 | module "aws-ap-southeast-2" { 67 | aws_region = "ap-southeast-2" 68 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 69 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 70 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 71 | source = "modules/hardening-region" 72 | } 73 | 74 | module "aws-ap-northeast-1" { 75 | aws_region = "ap-northeast-1" 76 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 77 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 78 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 79 | source = "modules/hardening-region" 80 | } 81 | 82 | module "aws-ca-central-1" { 83 | aws_region = "ca-central-1" 84 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 85 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 86 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 87 | source = "modules/hardening-region" 88 | } 89 | 90 | module "aws-eu-central-1" { 91 | aws_region = "eu-central-1" 92 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 93 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 94 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 95 | source = "modules/hardening-region" 96 | } 97 | 98 | module "aws-eu-west-1" { 99 | aws_region = "eu-west-1" 100 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 101 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 102 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 103 | source = "modules/hardening-region" 104 | } 105 | 106 | module "aws-eu-west-2" { 107 | aws_region = "eu-west-2" 108 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 109 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 110 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 111 | source = "modules/hardening-region" 112 | } 113 | 114 | module "aws-eu-west-3" { 115 | aws_region = "eu-west-3" 116 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 117 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 118 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 119 | source = "modules/hardening-region" 120 | } 121 | 122 | module "aws-eu-north-1" { 123 | aws_region = "eu-north-1" 124 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 125 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 126 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 127 | source = "modules/hardening-region" 128 | } 129 | 130 | module "aws-sa-east-1" { 131 | aws_region = "sa-east-1" 132 | aws_config_bucket = "${module.aws-global.aws_config_bucket}" 133 | aws_config_role_arn = "${module.aws-global.aws_config_role_arn}" 134 | aws_flowlogs_role_arn = "${module.aws-global.aws_flowlogs_role_arn}" 135 | source = "modules/hardening-region" 136 | } 137 | 138 | #module "aws-ap-northeast-3" { 139 | # aws_region = "ap-northeast-3" 140 | # source = "hardening-region" 141 | #} 142 | 143 | -------------------------------------------------------------------------------- /modules/hardening-global/cloudtrail.tf: -------------------------------------------------------------------------------- 1 | # Bucket for CloudTrail storage 2 | resource "aws_s3_bucket" "cloudtrail" { 3 | bucket = "cloudtrail-${data.aws_caller_identity.current.account_id}" 4 | 5 | logging { 6 | target_bucket = "${aws_s3_bucket.cloudtrail_logging.id}" 7 | target_prefix = "cloudtrail/" 8 | } 9 | 10 | versioning { 11 | enabled = true 12 | } 13 | } 14 | 15 | # Bucket for Cloudtrail storage logging 16 | resource "aws_s3_bucket" "cloudtrail_logging" { 17 | bucket = "cloudtrail-logging-${data.aws_caller_identity.current.account_id}" 18 | acl = "log-delivery-write" 19 | 20 | versioning { 21 | enabled = true 22 | } 23 | } 24 | 25 | # Cloudtrail bucket policy 26 | resource "aws_s3_bucket_policy" "cloudtrail" { 27 | bucket = "${aws_s3_bucket.cloudtrail.id}" 28 | 29 | policy = < CloudWatch 68 | resource "aws_iam_role" "cloudtrail_cloudwatch" { 69 | name = "cloudtrail_cloudwatch" 70 | 71 | assume_role_policy = <