Skip to content

Commit

Permalink
Infrastructure VPC Flow Logs
Browse files Browse the repository at this point in the history
* This allows setting up Flow Logs on the Infrastructure VPC, with
  either/both S3 and CloudWatch destinations. If the S3 destination is
  deployed, it will also deploy the compatible Glue table/database for
  AThena, so that we can easily query the logs. An Athena workgroup has
  also been created, where we can create useful stored queries (The
  athena output goes to the logging bucket).
  • Loading branch information
Stretch96 committed Nov 20, 2023
1 parent bef1b0f commit 9c3caf8
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 0 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ 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 |
Expand All @@ -49,6 +57,11 @@ This project creates and manages resources within an AWS account for infrastruct
| <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 |

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
56 changes: 56 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,62 @@ locals {

infrastructure_logging_bucket_retention = var.infrastructure_logging_bucket_retention

enable_infrastructure_logs_bucket = (
local.infrastructure_vpc_flow_logs_cloudwatch_logs ||
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}
25 changes: 25 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,28 @@ variable "infrastructure_vpc_assign_generated_ipv6_cidr_block" {
description = "Assign generated IPv6 CIDR block on infrastructure VPC"
type = bool
}

variable "infrastructure_vpc_flow_logs_cloudwatch_logs" {
description = "Enable VPC logs on infrastructure VPC to CloudWatch Logs"
type = bool
}

variable "infrastructure_vpc_flow_logs_s3_with_athena" {
description = "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."
type = bool
}

variable "infrastructure_vpc_flow_logs_retention" {
description = "VPC flow logs retention in days"
type = number
}

variable "infrastructure_vpc_flow_logs_traffic_type" {
description = "Infrastructure VPC flow logs traffic type"
type = string
}

variable "infrastructure_vpc_flow_logs_s3_key_prefix" {
description = "Flow Logs by default will go into the infrastructure S3 logs bucket. This is the key prefix used to isolate them from other logs"
type = string
}
19 changes: 19 additions & 0 deletions vpc-infrastructure-flow-logs-athena.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "aws_athena_workgroup" "infrastructure_vpc_flow_logs" {
count = local.infrastructure_vpc_flow_logs_s3_with_athena ? 1 : 0

name = "${local.resource_prefix}-infrastructure-vpc-flow-logs"

configuration {
enforce_workgroup_configuration = true
publish_cloudwatch_metrics_enabled = true

result_configuration {
output_location = "s3://${aws_s3_bucket.infrastructure_logs[0].bucket}/${local.infrastructure_vpc_flow_logs_s3_key_prefix}-athena-output"

encryption_configuration {
encryption_option = local.infrastructure_kms_encryption ? "SSE_KMS" : "SSE_S3"
kms_key_arn = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
}
}
}
}
34 changes: 34 additions & 0 deletions vpc-infrastructure-flow-logs-cloudwatch.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
resource "aws_cloudwatch_log_group" "infrastructure_vpc_flow_logs" {
count = local.infrastructure_vpc_flow_logs_cloudwatch_logs ? 1 : 0

name = "${local.resource_prefix}-infrastructure-vpc-flow-logs"
retention_in_days = local.infrastructure_vpc_flow_logs_retention
kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
skip_destroy = true
}

resource "aws_iam_role" "infrastructure_vpc_flow_logs" {
count = local.infrastructure_vpc_flow_logs_cloudwatch_logs ? 1 : 0

name = "${local.resource_prefix}-infrastructure-vpc-flow-logs"
assume_role_policy = templatefile("${path.root}/policies/assume-roles/service-principle-standard.json.tpl", {
services = jsonencode(["vpc-flow-logs.amazonaws.com"])
})
}

resource "aws_iam_role_policy" "infrastructure_vpc_flow_logs_allow_cloudwatch_rw" {
count = local.infrastructure_vpc_flow_logs_cloudwatch_logs ? 1 : 0

name = "${local.resource_prefix}-ecs-vpc-flow-logs-cloudwatch-logs-rw"
role = aws_iam_role.infrastructure_vpc_flow_logs[0].id
policy = templatefile("${path.root}/policies/cloudwatch-logs-rw.json.tpl", {})
}

resource "aws_flow_log" "infrastructure_vpc_flow_logs_cloudwatch" {
count = local.infrastructure_vpc_flow_logs_cloudwatch_logs ? 1 : 0

iam_role_arn = aws_iam_role.infrastructure_vpc_flow_logs[0].id
log_destination = aws_cloudwatch_log_group.infrastructure_vpc_flow_logs[0].arn
traffic_type = local.infrastructure_vpc_flow_logs_traffic_type
vpc_id = aws_vpc.infrastructure[0].id
}
58 changes: 58 additions & 0 deletions vpc-infrastructure-flow-logs-glue-tables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
resource "aws_glue_catalog_database" "infrastructure_vpc_flow_logs" {
count = local.infrastructure_vpc_flow_logs_s3_with_athena ? 1 : 0

name = "${replace(local.resource_prefix, "-", "_")}_infrastructure_vpc_logs"
description = "Database for ${local.resource_prefix} VPC flow log tables to be queried with Athena"
}

resource "aws_glue_catalog_table" "infrastructure_vpc_flow_logs" {
count = local.infrastructure_vpc_flow_logs_s3_with_athena ? 1 : 0

name = "${replace(local.resource_prefix, "-", "_")}_infrastructure_vpc_logs"
database_name = aws_glue_catalog_database.infrastructure_vpc_flow_logs[0].name

dynamic "partition_keys" {
for_each = local.infrastructure_vpc_flow_logs_glue_table_partition_keys
content {
name = partition_keys.key
type = partition_keys.value
}
}

parameters = {
comment = "VPC Flow logs table for ${local.resource_prefix} infrastructure VPC"
EXTERNAL = "TRUE"
"skip.header.line.count" = "1"
"projection.enabled" = "true"
"projection.region.type" = "enum"
"projection.region.values" = local.aws_region
"projection.day.type" = "date"
"projection.day.range" = "2023/01/01,NOW"
"projection.day.format" = "yyyy/MM/dd"
"projection.hour.type" = "integer"
"projection.hour.range" = "00,23"
"projection.hour.digits" = "2"
"storage.location.template" = "s3://${aws_s3_bucket.infrastructure_logs[0].id}/${local.infrastructure_vpc_flow_logs_s3_key_prefix}/AWSLogs/${local.aws_account_id}/vpcflowlogs/$${region}/$${day}/$${hour}"
}

storage_descriptor {
input_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat"
location = "s3://${aws_s3_bucket.infrastructure_logs[0].id}/${local.infrastructure_vpc_flow_logs_s3_key_prefix}/AWSLogs/${local.aws_account_id}/vpcflowlogs"

ser_de_info {
parameters = {
"serialization.format" = "1"
}
serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe"
}

dynamic "columns" {
for_each = local.infrastructure_vpc_flow_logs_glue_table_columns
content {
name = columns.key
type = columns.value
}
}
}
}
13 changes: 13 additions & 0 deletions vpc-infrastructure-flow-logs-s3.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
resource "aws_flow_log" "infrastructure_vpc_flow_logs_s3" {
count = local.infrastructure_vpc_flow_logs_s3_with_athena ? 1 : 0

log_destination_type = "s3"
log_destination = "${aws_s3_bucket.infrastructure_logs[0].arn}/${local.infrastructure_vpc_flow_logs_s3_key_prefix}"
traffic_type = local.infrastructure_vpc_flow_logs_traffic_type
vpc_id = aws_vpc.infrastructure[0].id

destination_options {
file_format = "parquet"
per_hour_partition = true
}
}

0 comments on commit 9c3caf8

Please sign in to comment.