diff --git a/CHANGELOG.md b/CHANGELOG.md index 52633fa..507b9a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,3 +7,6 @@ * v1.1 -- 20210223 - add iam policy to terraform-state + +* v1.2 -- 20210223 + - module: access-logging diff --git a/README.md b/README.md index dc81bdb..9e235c9 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ This has no other dependencies, since it has to be created first. Only one is n ### splunk-user -### access-logging-bucket +### [access-logging](access-logging) This sets up the S3 bucket used for access logs. One is needed per region, and the region and account are included -in the bucket names: `inf-log-{account_id}-{region}`. +in the bucket names: `inf-logs-{account_id}-{region}`. ### object-logging ### cloudtrail diff --git a/access-logging/README.md b/access-logging/README.md new file mode 100644 index 0000000..11f4239 --- /dev/null +++ b/access-logging/README.md @@ -0,0 +1,84 @@ +# aws-inf-setup :: access-logging + +This set up the needed components for S3 access log bucket. An access log must exist in each region +where there are components wishing to use access logs (S3, ALB, etc.). + +* S3 bucket +* S3 bucket objects (key prefixes, aka "directories") +* S3 bucket policy + +# Usage +Here is a simple example, the one most commonly expected to be used. + +```hcl +module "logs" { + source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//access-logging" +} +``` + +This one can be used if you need to customize stuff, though really, the defaults are all built +for a reason, and deployment code (i.e., Ansible) will expect these defaults to be used in +variable file generation. + +```hcl +module "logs_full" { + source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" + + # optional + account_alias = "do2-govcloud" + bucket_name = "inf-logs-123456789012" + + # logs is generally not needed and not recommended + component_tags = { + "s3" = { + "SpecialTag1" = "something" + "SpecialTag2" = "somethingElse" + } + } +} +``` + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Modules + +No Modules. + +## Resources + +| Name | +|------| +| [aws_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | +| [aws_caller_identity](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | +| [aws_iam_policy_document](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | +| [aws_region](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | +| [aws_s3_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | +| [aws_s3_bucket_object](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object) | +| [aws_s3_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | +| [aws_s3_bucket_public_access_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| account\_alias | AWS Account Alias (required) | `string` | `""` | no | +| account\_id | AWS Account ID (default will pull from current user) | `string` | `""` | no | +| bucket\_name | Logging S3 bucket name | `string` | `""` | no | +| bucket\_name\_prefix | Logging S3 bucket prefix, prepended to the AWS account ID and region to make the bucket name. | `string` | `"inf-logs"` | no | +| component\_tags | Additional tags for Components (s3, kms, ddb) | `map(map(string))` |
{
"ddb": {},
"kms": {},
"s3": {}
}
| no | +| override\_prefixes | Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things | `map(string)` | `{}` | no | +| tags | AWS Tags to apply to appropriate resources (S3, KMS). Do not include safeguard tags here, use the data\_safeguard field for such things. | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| logs\_bucket\_arn | Logging S3 bucket ARN | diff --git a/access-logging/data.tf b/access-logging/data.tf new file mode 120000 index 0000000..995624d --- /dev/null +++ b/access-logging/data.tf @@ -0,0 +1 @@ +../common/data.tf \ No newline at end of file diff --git a/access-logging/defaults.tf b/access-logging/defaults.tf new file mode 120000 index 0000000..a5556ac --- /dev/null +++ b/access-logging/defaults.tf @@ -0,0 +1 @@ +../common/defaults.tf \ No newline at end of file diff --git a/access-logging/main.tf b/access-logging/main.tf new file mode 100644 index 0000000..81f8584 --- /dev/null +++ b/access-logging/main.tf @@ -0,0 +1,123 @@ +/* +* # aws-inf-setup :: access-logging +* +* This set up the needed components for S3 access log bucket. An access log must exist in each region +* where there are components wishing to use access logs (S3, ALB, etc.). +* +* * S3 bucket +* * S3 bucket objects (key prefixes, aka "directories") +* * S3 bucket policy +* +* # Usage +* Here is a simple example, the one most commonly expected to be used. +* +* ```hcl +* module "logs" { +* source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//access-logging" +* } +* ``` +* +* This one can be used if you need to customize stuff, though really, the defaults are all built +* for a reason, and deployment code (i.e., Ansible) will expect these defaults to be used in +* variable file generation. +* +* ```hcl +* module "logs_full" { +* source = "git@github.e.it.census.gov:terraform-modules/aws-inf-setup.git//terraform-state" +* +* # optional +* account_alias = "do2-govcloud" +* bucket_name = "inf-logs-123456789012" +* +* # logs is generally not needed and not recommended +* component_tags = { +* "s3" = { +* "SpecialTag1" = "something" +* "SpecialTag2" = "somethingElse" +* } +* } +* } +* ``` +*/ + +locals { + account_id = var.account_id != "" ? var.account_id : data.aws_caller_identity.current.account_id + logs_region = data.aws_region.current.name + account_environment = data.aws_arn.current.partiion == "aws-us-gov" ? "gov" : "ew" + logs_alb_accounts = lookup(local._defaults["load-balancer"], local.account_environment, [local.account_id]) + logs_alb_account = lookup(local._defaults["load-balancer"], local.logs_region, local.account_id) + + bucket_name = var.bucket_name != "" ? var.bucket_name : format("%v-%v-%v", var.bucket_name_prefix, local.account_id, local.logs_region) + logs_policy_name = format("%v%v", lookup(local._prefixes, "policy", ""), var.bucket_name_prefix) + logs_folders = ["s3", "elasticmapreduce", "alb-logs", "nlb-logs", "inventory"] + + base_tags = { + "Organization" = "census:aditcio:csvd" + "boc:tf_module_version" = local._module_version + "boc:created_by" = "terraform" + } +} + + +#--- +# s3 +#--- +resource "aws_s3_bucket" "logs" { + bucket = local.bucket_name + acl = "log-delivery-write" + + # uses aws/kms key so log delivery works properly + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "aws:kms" + } + } + } + + versioning { + enabled = false + } + + lifecycle { + prevent_destroy = true + } + + # probably want some migration of old data to some other location + # like glacier + + tags = merge( + var.tags, + local.base_tags, + lookup(var.component_tags, "s3", {}), + map("Name", local.bucket_name), + ) + + provisioner "local-exec" { + command = "sleep 30" + } +} + +resource "aws_s3_bucket_public_access_block" "logs" { + bucket = aws_s3_bucket.logs.id + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +#--- +# create "directories" +#--- +resource "aws_s3_bucket_object" "logs" { + for_each = toset(local.logs_folders) + bucket = aws_s3_bucket.logs.id + key = format("%v/", each.key) + source = "/dev/null" +} + +resource "aws_s3_bucket_policy" "logs" { + bucket = aws_s3_bucket.logs.id + policy = data.aws_iam_policy_document.logs_s3.json +} + diff --git a/access-logging/outputs.tf b/access-logging/outputs.tf new file mode 100644 index 0000000..7661e2f --- /dev/null +++ b/access-logging/outputs.tf @@ -0,0 +1,9 @@ +output "logs_bucket_id" { + description = "Logging S3 bucket ID" + value = aws_s3_bucket.logs.id +} + +output "logs_bucket_arn" { + description = "Logging S3 bucket ARN" + value = aws_s3_bucket.logs.arn +} diff --git a/access-logging/policy_data.tf b/access-logging/policy_data.tf new file mode 100644 index 0000000..93f4466 --- /dev/null +++ b/access-logging/policy_data.tf @@ -0,0 +1,50 @@ +data "aws_iam_policy_document" "logs_s3" { + statement { + sid = "AWSLogWrite" + effect = "Allow" + actions = ["s3:PutObject"] + resources = ["${aws_s3_bucket.logs.arn}/*"] + principals { + type = "AWS" + identifiers = [format("arn:%v:iam::%v:root", data.aws_arn.current.partition, local.account_id)] + } + } + statement { + sid = "AWSLogDeliveryWrite" + effect = "Allow" + actions = ["s3:PutObject"] + resources = ["${aws_s3_bucket.logs.arn}/*"] + principals { + type = "Service" + identifiers = ["delivery.logs.amazonaws.com"] + } + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } + statement { + sid = "AWSLogDeliveryAclCheck" + effect = "Allow" + actions = ["s3:GetBucketAcl"] + resources = [aws_s3_bucket.logs.arn] + principals { + type = "Service" + identifiers = ["delivery.logs.amazonaws.com"] + } + } + + # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-logging-bucket-permissions + statement { + sid = "AWSALBAccessLog" + effect = "Allow" + actions = ["s3:PutObject"] + resources = ["${aws_s3_bucket.logs.arn}/alb-logs/*"] + principals { + type = "AWS" + # identifiers = [ formatlist("arn:%v:iam::%v:root",data.aws_arn.current.partition,local.logs_alb_accounts) ] + identifiers = [format("arn:%v:iam::%v:root", data.aws_arn.current.partition, local.logs_alb_account)] + } + } +} diff --git a/access-logging/prefixes.tf b/access-logging/prefixes.tf new file mode 120000 index 0000000..7e265d5 --- /dev/null +++ b/access-logging/prefixes.tf @@ -0,0 +1 @@ +../common/prefixes.tf \ No newline at end of file diff --git a/access-logging/variables.common.tf b/access-logging/variables.common.tf new file mode 120000 index 0000000..7439ed8 --- /dev/null +++ b/access-logging/variables.common.tf @@ -0,0 +1 @@ +../common/variables.common.tf \ No newline at end of file diff --git a/access-logging/variables.tf b/access-logging/variables.tf new file mode 100644 index 0000000..2589730 --- /dev/null +++ b/access-logging/variables.tf @@ -0,0 +1,18 @@ +variable "bucket_name" { + description = "Logging S3 bucket name" + type = string + # default = "inf-logs-{{ tf_account }}-{{ region }}" + default = "" +} + +variable "bucket_name_prefix" { + description = "Logging S3 bucket prefix, prepended to the AWS account ID and region to make the bucket name." + type = string + default = "inf-logs" +} + +variable "component_tags" { + description = "Additional tags for Components (s3, kms, ddb)" + type = map(map(string)) + default = { "s3" = {}, "kms" = {}, "ddb" = {} } +} diff --git a/access-logging-bucket/version.tf b/access-logging/version.tf similarity index 100% rename from access-logging-bucket/version.tf rename to access-logging/version.tf diff --git a/common/defaults.tf b/common/defaults.tf index c67478b..bc32b74 100644 --- a/common/defaults.tf +++ b/common/defaults.tf @@ -1,5 +1,19 @@ +# for the accesss logs for load balancers +# https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-logging-bucket-permissions locals { _defaults = { + "load-balancer" = { + "gov" = ["190560391635", "048591011584"] + "us-gov-east-1" = "190560391635" + "us-gov-west-1" = "048591011584" + + "ew" = ["127311923021", "033677994240", "027434742980", "797873946194"] + "us-east-1" = "127311923021" + "us-east-2" = "033677994240" + "us-west-1" = "027434742980" + "us-west-2" = "797873946194" + } } } + diff --git a/common/variables.common.tf b/common/variables.common.tf index 2bc450a..2870df4 100644 --- a/common/variables.common.tf +++ b/common/variables.common.tf @@ -10,6 +10,7 @@ variable "account_id" { variable "account_alias" { description = "AWS Account Alias (required)" type = string + default = "" } variable "override_prefixes" { @@ -23,64 +24,3 @@ variable "tags" { type = map(string) default = {} } - -## # s3 -## variable "bucket_name" { -## description = "AWS Bucket Name. Standard prefix will be applied here, do not include here." -## type = string -## } -## -## variable "bucket_folders" { -## description = "List of folders (keys) to create after creation of bucket. They will have object metadata provided based on metadata_tags and data_safeguard labels." -## type = list(string) -## default = [] -## } -## -## variable "kms_key_id" { -## description = "AWS KMS Key ID (one per bucket). This is currently ignored." -## type = string -## default = "" -## } -## -## variable "metadata_tags" { -## description = "AWS S3 Custom metadata (prefix x-amzn-meta- automatically included, not needed here). If data_safeguard labels are applied, they will be incorporated on any bucket objects created." -## type = map(string) -## default = {} -## } -## -## variable "access_log_bucket_prefix" { -## description = "Access log bucket prefix, to which the bucket name will be appended to make the target_prefix" -## type = string -## default = "s3" -## } -## -## variable "access_log_bucket" { -## description = "Server Access Logging Bucket ID" -## type = string -## # default = null -## } -## -## variable "allowed_cidr" { -## description = "List of allowed source IPs (NOT from within the VPC). If empty, there will be no restrictions on source IP. If provided, you must also use allowed_endpoints for access within a VPC." -## type = list(string) -## default = [] -## } -## -## variable "allowed_endpoints" { -## description = "List of allowed VPC endpoint IDs. If used, it will enable access to the bucket from the specific VPC endpoints." -## type = list(string) -## default = [] -## } -## -## variable "force_destroy" { -## description = "Sets force_destroy to allow the bucket and contents to be deleted. The deletion may take a very long time based on the number of objects. You normally want to update this to true, apply, and then destroy the resource." -## type = bool -## default = false -## } -## -## # variable "lifecycle_rules" { -## # description = "Setup lifecycle rules (in-progress, not working)" -## # type = map() -## # default = {} -## # } -## diff --git a/common/version.tf b/common/version.tf index 5190b69..2f4174c 100644 --- a/common/version.tf +++ b/common/version.tf @@ -1,3 +1,3 @@ locals { - _module_version = "1.1" + _module_version = "1.2" }