From 49e32287765832155208b5454dc4dff9dfb1ffc4 Mon Sep 17 00:00:00 2001 From: xpk Date: Tue, 22 Oct 2024 08:21:45 +0800 Subject: [PATCH] UPD: back ported changes from upstream. vpc-subnet-manual module now supports s3 flow log --- .../networking/vpc-subnet-manual/README.md | 163 ++++++++++-------- modules/networking/vpc-subnet-manual/main.tf | 46 ++--- .../networking/vpc-subnet-manual/variables.tf | 56 +++--- .../vpc-subnet-manual/vpc-flowlog.tf | 92 +++++----- 4 files changed, 195 insertions(+), 162 deletions(-) diff --git a/modules/networking/vpc-subnet-manual/README.md b/modules/networking/vpc-subnet-manual/README.md index 52fb060..c4598aa 100644 --- a/modules/networking/vpc-subnet-manual/README.md +++ b/modules/networking/vpc-subnet-manual/README.md @@ -1,91 +1,108 @@ - # Overview This module performs the following tasks: - Create VPC, vpcflow log -- Create subnets in multiple AZ +- Create subnets in every AZ - Create IGW, NGW - Create s3 and ddb endpoints which are free -## Requirements +## Subnet addressing -| Name | Version | -|------|---------| -| terraform | >= 1.3.0 | -| aws | >= 5.0 | +Subnet cidrs needs to be specified manually -## Providers +## Inputs: -| Name | Version | -|------|---------| -| aws | >= 5.0 | -| random | n/a | +| Name | Description | Type | Default | Required | +|---------------------------------|---------------------------------------------------|---------------|---------|----------| +| private-subnet-cidrs | private subnets | list | [] | yes | +| public-subnet-cidrs | public subnets | list | [] | yes | +| create-nat-gateway | whether to deploy NAT gateway for private subnets | bool | true | yes | +| vpc-cidr | VPC cidr | string | none | yes | +| enable-flowlog | whether to enable vpc flowlog | bool | true | yes | +| vpcflowlog-retain-days | number of days to retain vpc cloudwatch log | number | 90 | yes | +| vpcflowlog-cwl-loggroup-key-arn | kms key alias arn for log group encryption | string | none | yes | +| secondary_cidr_blocks | Additional CIDR blocks to be associated with VPC | list(string) | none | no | +| resource-prefix | Prefix of resource name | string | "" | yes | -## Modules -| Name | Source | Version | -|------|--------|---------| -| vpc-ep | ../vpc-endpoints | n/a | +## Outputs: -## Resources +| Name | Description | Type | +|-----------------------|-------------------------|---------| +| vpc_id | vpc id | string | +| public_subnets | list of cidr blocks | list | +| private_subnets | list of cidr blocks | list | +| secondary_cidr_blocks | list of secondary cidrs | list | -| Name | Type | -|------|------| -| [aws_cloudwatch_log_group.vpcflowlog-loggroup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_default_security_group.default-sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_security_group) | resource | -| [aws_eip.ngw-eip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | -| [aws_flow_log.vpc-flowlog](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource | -| [aws_iam_role.vpcflowlog-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy.vpcflowlog-role-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | -| [aws_internet_gateway.igw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | -| [aws_nat_gateway.ngw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | -| [aws_route.private-routes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | -| [aws_route.public-routes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | -| [aws_route_table.private-route-table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | -| [aws_route_table.public-route-table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | -| [aws_route_table_association.private_route_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | -| [aws_route_table_association.public_route_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | -| [aws_subnet.private-subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | -| [aws_subnet.public-subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | -| [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource | -| [aws_vpc_ipv4_cidr_block_association.additional_cidr](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipv4_cidr_block_association) | resource | -| [random_id.rid](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | -| [aws_availability_zones.available-az](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | -| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_default_tags.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source | +## Using s3 bucket for flowlog +Make sure the bucket policy allows access from delivery.logs. If the bucket is encrypted with CMK, +make sure the key policy allows the aws service delivery.logs.amazonaws.com. -## Inputs +### Sample s3 bucket policy +```json +{ + "Id" : "policy01", + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "AWSLogDeliveryWrite", + "Effect" : "Allow", + "Principal" : { + "Service" : "delivery.logs.amazonaws.com" + }, + "Action" : "s3:PutObject", + "Resource" : "arn:aws:s3:::BUCKET_NAME/*" + }, + { + "Sid" : "AWSLogDeliveryCheck", + "Effect" : "Allow", + "Principal" : { + "Service" : "delivery.logs.amazonaws.com" + }, + "Action" : "s3:GetBucketAcl", + "Resource" : "arn:aws:s3:::BUCKET_NAME" + } + ] + } +``` -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| create-free-vpc-endpoints | Set true to deploy s3 and dynamodb endpoints, which are free | `bool` | `true` | no | -| create-nat-gateway | Set true to deploy NAT gateway | `bool` | `false` | no | -| enable-flow-log | Set true to deploy vpc flowlog | `bool` | `true` | no | -| log-group-class | Log group class for flowlog cloudwatch log group, which can be STANDARD or INFREQUENT\_ACCESS | `string` | `"STANDARD"` | no | -| num\_azs | By default, use 2 AZs for redundancy | `number` | `2` | no | -| private-subnet-cidrs | List of private subnet cidrs | `list(string)` | n/a | yes | -| public-subnet-cidrs | List of public subnet cidrs | `list(string)` | n/a | yes | -| resource-prefix | n/a | `any` | n/a | yes | -| secondary\_cidr\_blocks | Additional cidr blocks | `list(string)` | `[]` | no | -| vpc-cidr | CIDR of VPC to be created | `string` | n/a | yes | -| vpcflowlog-cwl-loggroup-key-arn | CWL log group encryption key arn | `string` | n/a | yes | -| vpcflowlog-retain-days | Days to retain flowlog | `number` | `90` | no | +### Sample CMK policy +```json + { + "Sid": "Allow AWS Service to use the key", + "Effect": "Allow", + "Principal": { + "Service": [ + "delivery.logs.amazonaws.com", + "cloudtrail.amazonaws.com", + "s3.amazonaws.com" + ] + }, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ], + "Resource": "*" + } +``` -## Outputs -| Name | Description | -|------|-------------| -| private-route-table-id | n/a | -| private-subnet-ids | n/a | -| private\_subnets | n/a | -| public-route-table-id | n/a | -| public-subnet-ids | n/a | -| public\_subnets | n/a | -| route\_tables\_for\_gateway\_endpoints | n/a | -| secondary\_cidr\_blocks | n/a | -| vpc-cidr | n/a | -| vpc\_id | n/a | - ---- -## Authorship -This module was developed by UPDATE_THIS. \ No newline at end of file +## Example: + +```hcl +module "vpc-subnets" { + source = "../../modules/networking/vpc-subnet-manual" + + resource-prefix = local.resource-prefix + private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"] + public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"] + vpc-cidr = "172.17.0.0/16" + enable-flow-log = false + vpcflowlog-cwl-loggroup-key-arn = "" + create-nat-gateway = true + create-free-vpc-endpoints = true +} +``` \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/main.tf b/modules/networking/vpc-subnet-manual/main.tf index 5e08c13..0e92bb3 100644 --- a/modules/networking/vpc-subnet-manual/main.tf +++ b/modules/networking/vpc-subnet-manual/main.tf @@ -1,3 +1,9 @@ +data "aws_caller_identity" "this" {} + +data "aws_availability_zones" "available-az" { + state = "available" +} + data "aws_default_tags" "this" { lifecycle { postcondition { @@ -7,36 +13,29 @@ data "aws_default_tags" "this" { } } -data "aws_caller_identity" "this" {} - -data "aws_availability_zones" "available-az" { - state = "available" -} - - locals { - vpc-cidr = var.vpc-cidr - enabled-az-names = slice(data.aws_availability_zones.available-az.names, 0, var.num_azs) + no-az = 2 # hard-coding to 2AZ + vpc-cidr = var.vpc-cidr } resource "aws_subnet" "private-subnets" { count = length(var.private-subnet-cidrs) vpc_id = aws_vpc.vpc.id - availability_zone = element(local.enabled-az-names, count.index) + availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2) cidr_block = var.private-subnet-cidrs[count.index] - tags = { - Name = "${var.resource-prefix}-private-${split("-", element(local.enabled-az-names, count.index))[2]}-${count.index + 1}" - } + tags = merge(data.aws_default_tags.this.tags, { + Name = "${var.resource-prefix}-private-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}" + }) } resource "aws_subnet" "public-subnets" { count = length(var.public-subnet-cidrs) vpc_id = aws_vpc.vpc.id - availability_zone = element(local.enabled-az-names, count.index) + availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2) cidr_block = var.public-subnet-cidrs[count.index] - tags = { - Name = "${var.resource-prefix}-public-${split("-", element(local.enabled-az-names, count.index))[2]}-${count.index + 1}" - } + tags = merge(data.aws_default_tags.this.tags, { + Name = "${var.resource-prefix}-public-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}" + }) } resource "aws_vpc" "vpc" { @@ -133,21 +132,22 @@ resource "aws_route_table_association" "private_route_association" { /* harden default security group. the default sg created by aws allows all egress. this resource limits ingress and egress from and to itself +and allow icmp only */ resource "aws_default_security_group" "default-sg" { vpc_id = aws_vpc.vpc.id ingress { - protocol = -1 + protocol = "icmp" self = true - from_port = 0 - to_port = 0 + from_port = -1 + to_port = -1 description = "Allow traffic coming from this SG" } egress { - from_port = 0 - protocol = -1 - to_port = 0 + from_port = -1 + protocol = "icmp" + to_port = -1 self = true description = "Allow traffic going to this SG" } diff --git a/modules/networking/vpc-subnet-manual/variables.tf b/modules/networking/vpc-subnet-manual/variables.tf index 47e2827..8fe2703 100644 --- a/modules/networking/vpc-subnet-manual/variables.tf +++ b/modules/networking/vpc-subnet-manual/variables.tf @@ -1,45 +1,59 @@ -variable "resource-prefix" {} +variable "resource-prefix" { + type = string + description = "Prefix of resource" +} # VPC variables variable "vpc-cidr" { - type = string - description = "CIDR of VPC to be created" + type = string + description = "VPC primary CIDR" } variable "private-subnet-cidrs" { - description = "List of private subnet cidrs" - type = list(string) + type = list(string) + description = "Private subnet CIDRs" } variable "public-subnet-cidrs" { - description = "List of public subnet cidrs" - type = list(string) + type = list(string) + description = "Public subnet CIDRs" + default = null } variable "create-nat-gateway" { - description = "Set true to deploy NAT gateway" type = bool default = false } +variable "flow-log-destination" { + type = string + description = "Destination of flowlog. Valid destinations are s3 or cwlog" + default = null +} + +variable "flow-log-bucket-arn" { + type = string + default = null + description = "Arn of S3 bucket to be used for flow logging" +} + variable "enable-flow-log" { - description = "Set true to deploy vpc flowlog" type = bool default = true } variable "vpcflowlog-retain-days" { - description = "Days to retain flowlog" - type = number - default = 90 + type = number + default = 90 + description = "Log retention period for CWlogs" } + variable "vpcflowlog-cwl-loggroup-key-arn" { - description = "CWL log group encryption key arn" - type = string + type = string + description = "KMS key arn for cwlog encryption" } variable "create-free-vpc-endpoints" { - description = "Set true to deploy s3 and dynamodb endpoints, which are free" type = bool default = true } @@ -48,16 +62,4 @@ variable "secondary_cidr_blocks" { type = list(string) description = "Additional cidr blocks" default = [] -} - -variable "num_azs" { - description = "By default, use 2 AZs for redundancy" - type = number - default = 2 -} - -variable "log-group-class" { - description = "Log group class for flowlog cloudwatch log group, which can be STANDARD or INFREQUENT_ACCESS" - type = string - default = "STANDARD" } \ No newline at end of file diff --git a/modules/networking/vpc-subnet-manual/vpc-flowlog.tf b/modules/networking/vpc-subnet-manual/vpc-flowlog.tf index 361f922..2f10904 100644 --- a/modules/networking/vpc-subnet-manual/vpc-flowlog.tf +++ b/modules/networking/vpc-subnet-manual/vpc-flowlog.tf @@ -1,6 +1,6 @@ resource "aws_flow_log" "vpc-flowlog" { - count = var.enable-flow-log ? 1 : 0 - iam_role_arn = aws_iam_role.vpcflowlog-role.arn + count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0 + iam_role_arn = aws_iam_role.vpcflowlog-role[0].arn log_destination = aws_cloudwatch_log_group.vpcflowlog-loggroup[0].arn traffic_type = "ALL" vpc_id = aws_vpc.vpc.id @@ -9,13 +9,22 @@ resource "aws_flow_log" "vpc-flowlog" { } } -resource "aws_cloudwatch_log_group" "vpcflowlog-loggroup" { - count = var.enable-flow-log ? 1 : 0 +resource "aws_flow_log" "vpc-flowlog-s3" { + count = var.enable-flow-log && var.flow-log-destination == "s3" ? 1 : 0 + log_destination_type = "s3" + log_destination = var.flow-log-bucket-arn + traffic_type = "ALL" + vpc_id = aws_vpc.vpc.id + tags = { + Name = "${var.resource-prefix}-vpcflowlog" + } +} - name_prefix = "/aws/vpcflowlog/" +resource "aws_cloudwatch_log_group" "vpcflowlog-loggroup" { + count = var.enable-flow-log && var.flow-log-destination == "cwlog" ? 1 : 0 + name_prefix = "vpcflowlog/${aws_vpc.vpc.id}/" kms_key_id = var.vpcflowlog-cwl-loggroup-key-arn retention_in_days = var.vpcflowlog-retain-days - log_group_class = var.log-group-class } resource "random_id" "rid" { @@ -23,45 +32,50 @@ resource "random_id" "rid" { } resource "aws_iam_role" "vpcflowlog-role" { - name = "VpcFlowlogRole-${random_id.rid.dec}" - path = "/service/" - assume_role_policy = <