Skip to content

Commit

Permalink
Merge pull request #12 from dxw/add-infrastructure-vpc-with-flow-logs
Browse files Browse the repository at this point in the history
Add infrastructure VPC with flow logs
  • Loading branch information
Stretch96 authored Nov 20, 2023
2 parents eb6da43 + 9c43f85 commit 4f7149c
Show file tree
Hide file tree
Showing 17 changed files with 527 additions and 6 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,23 @@ This project creates and manages resources within an AWS account for infrastruct

| Name | Type |
|------|------|
| [aws_athena_workgroup.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_workgroup) | resource |
| [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_glue_catalog_database.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/glue_catalog_database) | resource |
| [aws_glue_catalog_table.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/glue_catalog_table) | resource |
| [aws_iam_role.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.infrastructure_vpc_flow_logs_allow_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_kms_alias.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_key.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_s3_bucket.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_lifecycle_configuration.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
| [aws_s3_bucket_policy.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_vpc.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |

## Inputs
Expand All @@ -34,7 +49,20 @@ This project creates and manages resources within an AWS account for infrastruct
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | AWS region in which to launch resources | `string` | n/a | yes |
| <a name="input_environment"></a> [environment](#input\_environment) | The environment name to be used as part of the resource prefix | `string` | n/a | yes |
| <a name="input_infrastructure_kms_encryption"></a> [infrastructure\_kms\_encryption](#input\_infrastructure\_kms\_encryption) | Enable infrastructure KMS encryption. This will create a single KMS key to be used across all resources that support KMS encryption. | `bool` | n/a | yes |
| <a name="input_infrastructure_logging_bucket_retention"></a> [infrastructure\_logging\_bucket\_retention](#input\_infrastructure\_logging\_bucket\_retention) | Retention in days for the infrasrtucture S3 logs. This is for the default S3 logs bucket, where all AWS service logs will be delivered | `number` | n/a | yes |
| <a name="input_infrastructure_name"></a> [infrastructure\_name](#input\_infrastructure\_name) | The infrastructure name to be used as part of the resource prefix | `string` | n/a | yes |
| <a name="input_infrastructure_vpc"></a> [infrastructure\_vpc](#input\_infrastructure\_vpc) | Enable infrastructure VPC | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_assign_generated_ipv6_cidr_block"></a> [infrastructure\_vpc\_assign\_generated\_ipv6\_cidr\_block](#input\_infrastructure\_vpc\_assign\_generated\_ipv6\_cidr\_block) | Assign generated IPv6 CIDR block on infrastructure VPC | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_cidr_block"></a> [infrastructure\_vpc\_cidr\_block](#input\_infrastructure\_vpc\_cidr\_block) | Infrastructure VPC CIDR block | `string` | n/a | yes |
| <a name="input_infrastructure_vpc_enable_dns_hostnames"></a> [infrastructure\_vpc\_enable\_dns\_hostnames](#input\_infrastructure\_vpc\_enable\_dns\_hostnames) | Enable DNS hostnames on infrastructure VPC | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_enable_dns_support"></a> [infrastructure\_vpc\_enable\_dns\_support](#input\_infrastructure\_vpc\_enable\_dns\_support) | Enable DNS support on infrastructure VPC | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_enable_network_address_usage_metrics"></a> [infrastructure\_vpc\_enable\_network\_address\_usage\_metrics](#input\_infrastructure\_vpc\_enable\_network\_address\_usage\_metrics) | Enable network address usage metrics on infrastructure VPC | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_cloudwatch_logs"></a> [infrastructure\_vpc\_flow\_logs\_cloudwatch\_logs](#input\_infrastructure\_vpc\_flow\_logs\_cloudwatch\_logs) | Enable VPC logs on infrastructure VPC to CloudWatch Logs | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_retention"></a> [infrastructure\_vpc\_flow\_logs\_retention](#input\_infrastructure\_vpc\_flow\_logs\_retention) | VPC flow logs retention in days | `number` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_s3_key_prefix"></a> [infrastructure\_vpc\_flow\_logs\_s3\_key\_prefix](#input\_infrastructure\_vpc\_flow\_logs\_s3\_key\_prefix) | Flow Logs by default will go into the infrastructure S3 logs bucket. This is the key prefix used to isolate them from other logs | `string` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_s3_with_athena"></a> [infrastructure\_vpc\_flow\_logs\_s3\_with\_athena](#input\_infrastructure\_vpc\_flow\_logs\_s3\_with\_athena) | Enable VPC flow logs in infrastructure VPC to the S3 logs bucket. A compatible Glue table/database and Athena workgroup will also be created to allow querying the logs. | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_traffic_type"></a> [infrastructure\_vpc\_flow\_logs\_traffic\_type](#input\_infrastructure\_vpc\_flow\_logs\_traffic\_type) | Infrastructure VPC flow logs traffic type | `string` | n/a | yes |
| <a name="input_infrastructure_vpc_instance_tenancy"></a> [infrastructure\_vpc\_instance\_tenancy](#input\_infrastructure\_vpc\_instance\_tenancy) | Infrastructure VPC instance tenancy | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | Project name to be used as a prefix for all resources | `string` | n/a | yes |

## Outputs
Expand Down
11 changes: 11 additions & 0 deletions kms-infrastructure.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ resource "aws_kms_key" "infrastructure" {
{
aws_account_id = local.aws_account_id
}
)}${local.infrastructure_vpc_flow_logs_cloudwatch_logs && local.infrastructure_kms_encryption ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/cloudwatch-logs-allow.json.tpl",
{
log_group_arn = local.infrastructure_vpc_flow_logs_cloudwatch_logs && local.infrastructure_kms_encryption ? "arn:aws:logs:${local.aws_region}:${local.aws_account_id}:log-group:${local.resource_prefix}-infrastructure-vpc-flow-logs" : ""
}
)}${local.infrastructure_vpc_flow_logs_s3_with_athena && local.infrastructure_kms_encryption ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/log-delivery-allow.json.tpl",
{
account_id = local.infrastructure_vpc_flow_logs_s3_with_athena && local.infrastructure_kms_encryption ? local.aws_account_id : ""
region = local.aws_region
}
)}
]
EOT
Expand Down
70 changes: 64 additions & 6 deletions locals.tf
Original file line number Diff line number Diff line change
@@ -1,13 +1,71 @@
locals {
project_name = var.project_name
infrastructure_name = var.infrastructure_name
environment = var.environment
aws_region = var.aws_region
aws_account_id = data.aws_caller_identity.current.account_id
resource_prefix = "${var.project_name}-${var.infrastructure_name}-${var.environment}"
project_name = var.project_name
infrastructure_name = var.infrastructure_name
environment = var.environment
aws_region = var.aws_region
aws_account_id = data.aws_caller_identity.current.account_id
resource_prefix = "${var.project_name}-${var.infrastructure_name}-${var.environment}"
resource_prefix_hash = format("%.8s", sha512(local.resource_prefix))

infrastructure_kms_encryption = var.infrastructure_kms_encryption

infrastructure_logging_bucket_retention = var.infrastructure_logging_bucket_retention

enable_infrastructure_logs_bucket = (
local.infrastructure_vpc_flow_logs_s3_with_athena
)
logs_bucket_source_arns = concat(
local.infrastructure_vpc_flow_logs_s3_with_athena ? ["arn:aws:logs:${local.aws_region}:${local.aws_account_id}:*"] : [],
)

infrastructure_vpc = var.infrastructure_vpc
infrastructure_vpc_cidr_block = var.infrastructure_vpc_cidr_block
infrastructure_vpc_enable_dns_support = var.infrastructure_vpc_enable_dns_support
infrastructure_vpc_enable_dns_hostnames = var.infrastructure_vpc_enable_dns_hostnames
infrastructure_vpc_instance_tenancy = var.infrastructure_vpc_instance_tenancy
infrastructure_vpc_enable_network_address_usage_metrics = var.infrastructure_vpc_enable_network_address_usage_metrics
infrastructure_vpc_assign_generated_ipv6_cidr_block = var.infrastructure_vpc_assign_generated_ipv6_cidr_block
infrastructure_vpc_flow_logs_cloudwatch_logs = var.infrastructure_vpc_flow_logs_cloudwatch_logs && local.infrastructure_vpc
infrastructure_vpc_flow_logs_s3_with_athena = var.infrastructure_vpc_flow_logs_s3_with_athena && local.infrastructure_vpc
infrastructure_vpc_flow_logs_s3_key_prefix = trim(var.infrastructure_vpc_flow_logs_s3_key_prefix, "/")
infrastructure_vpc_flow_logs_retention = var.infrastructure_vpc_flow_logs_retention
infrastructure_vpc_flow_logs_traffic_type = var.infrastructure_vpc_flow_logs_traffic_type
infrastructure_vpc_flow_logs_glue_table_columns = {
version = "int",
account_id = "string",
interface_id = "string",
srcaddr = "string",
dstaddr = "string",
srcport = "int",
dstport = "int",
protocol = "bigint",
packets = "bigint",
bytes = "bigint",
start = "bigint",
"`end`" = "bigint",
action = "string",
log_status = "string",
vpc_id = "string",
subnet_id = "string",
instance_id = "string",
tcp_flags = "int",
type = "string",
pkt_srcaddr = "string",
pkt_dstaddr = "string",
az_id = "string",
sublocation_type = "string",
sublocation_id = "string",
pkt_src_aws_service = "string",
pkt_dst_aws_service = "string",
flow_direction = "string",
traffic_path = "int",
}
infrastructure_vpc_flow_logs_glue_table_partition_keys = {
region = "string",
date = "string",
hour = "string"
}

default_tags = {
Project = local.project_name,
Infrastructure = local.infrastructure_name,
Expand Down
13 changes: 13 additions & 0 deletions policies/assume-roles/service-principle-standard.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": ${services}
},
"Action": "sts:AssumeRole"
}
]
}
16 changes: 16 additions & 0 deletions policies/cloudwatch-logs-rw.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
19 changes: 19 additions & 0 deletions policies/kms-key-policy-statements/cloudwatch-logs-allow.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
%{if log_group_arn != ""}{
"Effect": "Allow",
"Principal": {
"Service": "logs.amazonaws.com"
},
"Action": [
"kms:Encrypt*",
"kms:Decrypt*",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"kms:EncryptionContext:aws:logs:arn": "${log_group_arn}"
}
}
}%{endif}
22 changes: 22 additions & 0 deletions policies/kms-key-policy-statements/log-delivery-allow.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
%{if account_id != ""}{
"Effect": "Allow",
"Principal": {
"Service": [ "delivery.logs.amazonaws.com" ]
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ["${account_id}"]
},
"ArnLike": {
"aws:SourceArn": ["arn:aws:logs:${region}:${account_id}:*"]
}
}
}%{endif}
17 changes: 17 additions & 0 deletions policies/s3-bucket-policy-statements/enforce-tls.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Principal": "*",
"Action": "s3:*",
"Effect": "Deny",
"Resource": [
"${bucket_arn}",
"${bucket_arn}/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
},
"NumericLessThan": {
"s3:TlsVersion": "1.2"
}
}
}
37 changes: 37 additions & 0 deletions policies/s3-bucket-policy-statements/log-delivery-access.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"Principal": {
"Service": "logging.s3.amazonaws.com"
},
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "${log_bucket_arn}/*",
"Condition": {
"ArnLike": {
"aws:SourceArn": ${source_arns}
},
"StringEquals": {
"aws:SourceAccount": "${account_id}"
}
}
},
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"s3:GetBucketAcl",
"s3:ListBucket"
],
"Resource": "${log_bucket_arn}",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "${account_id}"
},
"ArnLike": {
"aws:SourceArn": ${source_arns}
}
}
}
4 changes: 4 additions & 0 deletions policies/s3-bucket-policy.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"Version": "2012-10-17",
"Statement": ${statement}
}
97 changes: 97 additions & 0 deletions s3-infrastructure-logs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# https://github.com/aquasecurity/tfsec/issues/2081
# tfsec:ignore:aws-s3-enable-bucket-logging
resource "aws_s3_bucket" "infrastructure_logs" {
count = local.enable_infrastructure_logs_bucket ? 1 : 0

bucket = "${local.resource_prefix_hash}-logs"
}

resource "aws_s3_bucket_policy" "infrastructure_logs" {
count = local.enable_infrastructure_logs_bucket ? 1 : 0

bucket = aws_s3_bucket.infrastructure_logs[0].id
policy = templatefile(
"${path.module}/policies/s3-bucket-policy.json.tpl",
{
statement = <<EOT
[
${templatefile("${path.root}/policies/s3-bucket-policy-statements/enforce-tls.json.tpl", { bucket_arn = aws_s3_bucket.infrastructure_logs[0].arn })},
${templatefile("${path.root}/policies/s3-bucket-policy-statements/log-delivery-access.json.tpl", {
log_bucket_arn = aws_s3_bucket.infrastructure_logs[0].arn
source_arns = jsonencode(local.logs_bucket_source_arns)
account_id = local.aws_account_id
})}
]
EOT
}
)
}

resource "aws_s3_bucket_public_access_block" "infrastructure_logs" {
count = local.enable_infrastructure_logs_bucket ? 1 : 0

bucket = aws_s3_bucket.infrastructure_logs[0].id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "infrastructure_logs" {
count = local.enable_infrastructure_logs_bucket ? 1 : 0

bucket = aws_s3_bucket.infrastructure_logs[0].id

versioning_configuration {
status = "Enabled"
}
}

# because infrastructure_kms_encryption is only true when multiple other
# vars are true, tfsec can't figure out that this will actually have kms encryption when
# enabled
#tfsec:ignore:aws-s3-encryption-customer-key tfsec:ignore:aws-s3-enable-bucket-encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "infrastructure_logs" {
count = local.enable_infrastructure_logs_bucket ? 1 : 0

bucket = aws_s3_bucket.infrastructure_logs[0].id

dynamic "rule" {
for_each = local.infrastructure_kms_encryption ? [1] : []
content {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.infrastructure[0].arn
sse_algorithm = "aws:kms"
}
}
}

dynamic "rule" {
for_each = local.infrastructure_kms_encryption ? [] : [1]
content {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
}

resource "aws_s3_bucket_lifecycle_configuration" "logs" {
count = local.enable_infrastructure_logs_bucket && local.infrastructure_logging_bucket_retention != 0 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_logs[0].id

rule {
id = "all_expire"

filter {
prefix = ""
}

expiration {
days = local.infrastructure_logging_bucket_retention
}

status = "Enabled"
}
}
Loading

0 comments on commit 4f7149c

Please sign in to comment.