From e83dddb5bf5d38849d9b3bf56fc2aeea55877998 Mon Sep 17 00:00:00 2001 From: Jim Enright Date: Wed, 16 Aug 2023 12:14:22 +0100 Subject: [PATCH] Add support for VPC Endpoints in AWS pre-reqs (#24) Signed-off-by: Jim Enright --- modules/terraform-cdp-aws-pre-reqs/README.md | 14 +++ modules/terraform-cdp-aws-pre-reqs/data.tf | 16 +++ .../terraform-cdp-aws-pre-reqs/defaults.tf | 2 + modules/terraform-cdp-aws-pre-reqs/main.tf | 104 ++++++++++++++++++ .../terraform-cdp-aws-pre-reqs/variables.tf | 52 +++++++++ 5 files changed, 188 insertions(+) diff --git a/modules/terraform-cdp-aws-pre-reqs/README.md b/modules/terraform-cdp-aws-pre-reqs/README.md index 4c28b26..fec5333 100644 --- a/modules/terraform-cdp-aws-pre-reqs/README.md +++ b/modules/terraform-cdp-aws-pre-reqs/README.md @@ -79,13 +79,20 @@ In each directory an example `terraform.tfvars.sample` values file is included t | [aws_s3_object.cdp_backup_storage_object](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/s3_object) | resource | | [aws_s3_object.cdp_log_storage_object](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/s3_object) | resource | | [aws_security_group.cdp_default_sg](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group) | resource | +| [aws_security_group.cdp_endpoint_sg](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group) | resource | | [aws_security_group.cdp_knox_sg](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group) | resource | | [aws_security_group_rule.cdp_default_sg_egress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.cdp_default_sg_ingress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.cdp_default_sg_ingress_self](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.cdp_endpoint_ingress_self](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.cdp_endpoint_sg_egress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.cdp_endpoint_sg_ingress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.cdp_knox_sg_egress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.cdp_knox_sg_ingress](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.cdp_knox_sg_ingress_self](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/security_group_rule) | resource | +| [aws_vpc_endpoint.gateway_endpoints](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/vpc_endpoint) | resource | +| [aws_vpc_endpoint.interface_endpoints](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/vpc_endpoint) | resource | +| [aws_vpc_endpoint.s3_global_interface_endpoint](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/resources/vpc_endpoint) | resource | | [random_id.bucket_suffix](https://registry.terraform.io/providers/hashicorp/random/3.4.3/docs/resources/id) | resource | | [time_sleep.iam_propagation](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/sleep) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/caller_identity) | data source | @@ -97,6 +104,8 @@ In each directory an example `terraform.tfvars.sample` values file is included t | [aws_iam_policy_document.cdp_xaccount_role_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/iam_policy_document) | data source | | [aws_subnets.vpc_subnets](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/subnets) | data source | | [aws_vpc.cdp_vpc](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/vpc) | data source | +| [aws_vpc_endpoint_service.gateway_endpoints](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/vpc_endpoint_service) | data source | +| [aws_vpc_endpoint_service.interface_endpoints](https://registry.terraform.io/providers/hashicorp/aws/4.67.0/docs/data-sources/vpc_endpoint_service) | data source | | [http_http.bucket_access_policy_doc](https://registry.terraform.io/providers/hashicorp/http/3.2.1/docs/data-sources/http) | data source | | [http_http.datalake_admin_s3_policy_doc](https://registry.terraform.io/providers/hashicorp/http/3.2.1/docs/data-sources/http) | data source | | [http_http.datalake_backup_policy_doc](https://registry.terraform.io/providers/hashicorp/http/3.2.1/docs/data-sources/http) | data source | @@ -119,11 +128,13 @@ In each directory an example `terraform.tfvars.sample` values file is included t | [bucket\_access\_policy\_doc](#input\_bucket\_access\_policy\_doc) | Bucket Access Data Access Policy | `string` | `null` | no | | [bucket\_access\_policy\_name](#input\_bucket\_access\_policy\_name) | Bucket Access Data Access Policy Name | `string` | `null` | no | | [cdp\_default\_sg\_egress\_cidrs](#input\_cdp\_default\_sg\_egress\_cidrs) | List of egress CIDR blocks for CDP Default Security Group Egress rule | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| [cdp\_endpoint\_sg\_egress\_cidrs](#input\_cdp\_endpoint\_sg\_egress\_cidrs) | List of egress CIDR blocks for VPC Endpoint Security Group Egress rule | `list(string)` |
[
"0.0.0.0/0"
]
| no | | [cdp\_knox\_sg\_egress\_cidrs](#input\_cdp\_knox\_sg\_egress\_cidrs) | List of egress CIDR blocks for CDP Knox Security Group Egress rule | `list(string)` |
[
"0.0.0.0/0"
]
| no | | [cdp\_private\_subnet\_ids](#input\_cdp\_private\_subnet\_ids) | List of private subnet ids. Required if create\_vpc is false. | `list(any)` | `null` | no | | [cdp\_public\_subnet\_ids](#input\_cdp\_public\_subnet\_ids) | List of public subnet ids. Required if create\_vpc is false. | `list(any)` | `null` | no | | [cdp\_vpc\_id](#input\_cdp\_vpc\_id) | VPC ID for CDP environment. Required if create\_vpc is false. | `string` | `null` | no | | [create\_vpc](#input\_create\_vpc) | Flag to specify if the VPC should be created | `bool` | `true` | no | +| [create\_vpc\_endpoints](#input\_create\_vpc\_endpoints) | Flag to specify if VPC Endpoints should be created | `bool` | `true` | no | | [data\_storage](#input\_data\_storage) | Data storage locations for CDP environment |
object({
data_storage_bucket = string
data_storage_object = string
})
| `null` | no | | [datalake\_admin\_role\_name](#input\_datalake\_admin\_role\_name) | Datalake Admin role Name | `string` | `null` | no | | [datalake\_admin\_s3\_policy\_doc](#input\_datalake\_admin\_s3\_policy\_doc) | Location or Contents of Datalake Admin S3 Data Access Policy | `string` | `null` | no | @@ -147,8 +158,11 @@ In each directory an example `terraform.tfvars.sample` values file is included t | [ranger\_audit\_s3\_policy\_doc](#input\_ranger\_audit\_s3\_policy\_doc) | Location or Contents of Ranger S3 Audit Data Access Policy | `string` | `null` | no | | [ranger\_audit\_s3\_policy\_name](#input\_ranger\_audit\_s3\_policy\_name) | Ranger S3 Audit Data Access Policy Name | `string` | `null` | no | | [security\_group\_default\_name](#input\_security\_group\_default\_name) | Default Security Group for CDP environment | `string` | `null` | no | +| [security\_group\_endpoint\_name](#input\_security\_group\_endpoint\_name) | Security Group for VPC Endpoints | `string` | `null` | no | | [security\_group\_knox\_name](#input\_security\_group\_knox\_name) | Knox Security Group for CDP environment | `string` | `null` | no | | [vpc\_cidr](#input\_vpc\_cidr) | VPC CIDR Block | `string` | `"10.10.0.0/16"` | no | +| [vpc\_endpoint\_gateway\_services](#input\_vpc\_endpoint\_gateway\_services) | List of AWS services used for VPC Gateway Endpoints | `list(string)` |
[
"s3"
]
| no | +| [vpc\_endpoint\_interface\_services](#input\_vpc\_endpoint\_interface\_services) | List of AWS services used for VPC Interface Endpoints | `list(string)` |
[
"sts",
"rds",
"elasticloadbalancing",
"elasticfilesystem",
"eks",
"ecr.dkr",
"ecr.api",
"ec2",
"cloudformation",
"autoscaling"
]
| no | | [xaccount\_account\_policy\_doc](#input\_xaccount\_account\_policy\_doc) | Location of cross acount policy document | `string` | `null` | no | | [xaccount\_policy\_name](#input\_xaccount\_policy\_name) | Cross Account Policy name | `string` | `null` | no | | [xaccount\_role\_name](#input\_xaccount\_role\_name) | Cross account Assume role Name | `string` | `null` | no | diff --git a/modules/terraform-cdp-aws-pre-reqs/data.tf b/modules/terraform-cdp-aws-pre-reqs/data.tf index 5378414..834783b 100644 --- a/modules/terraform-cdp-aws-pre-reqs/data.tf +++ b/modules/terraform-cdp-aws-pre-reqs/data.tf @@ -35,6 +35,22 @@ data "aws_subnets" "vpc_subnets" { } } +# Find details about S3 Gateway endpoint services +data "aws_vpc_endpoint_service" "gateway_endpoints" { + for_each = toset(var.vpc_endpoint_gateway_services) + + service = each.key + service_type = "Gateway" +} + +# Find details about S3 Gateway endpoint services +data "aws_vpc_endpoint_service" "interface_endpoints" { + for_each = toset(var.vpc_endpoint_interface_services) + + service = each.key + service_type = "Interface" +} + # HTTP get request to download policy documents # ..Cross Account Policy data "http" "xaccount_account_policy_doc" { diff --git a/modules/terraform-cdp-aws-pre-reqs/defaults.tf b/modules/terraform-cdp-aws-pre-reqs/defaults.tf index 3426b55..0bae797 100644 --- a/modules/terraform-cdp-aws-pre-reqs/defaults.tf +++ b/modules/terraform-cdp-aws-pre-reqs/defaults.tf @@ -43,6 +43,8 @@ locals { security_group_knox_name = coalesce(var.security_group_knox_name, "${var.env_prefix}-knox-sg") + security_group_endpoint_name = coalesce(var.security_group_endpoint_name, "${var.env_prefix}-endpoint-sg") + security_group_rules_ingress = [ { # CIDR ingress diff --git a/modules/terraform-cdp-aws-pre-reqs/main.tf b/modules/terraform-cdp-aws-pre-reqs/main.tf index 043e494..5cf57d3 100644 --- a/modules/terraform-cdp-aws-pre-reqs/main.tf +++ b/modules/terraform-cdp-aws-pre-reqs/main.tf @@ -117,6 +117,110 @@ resource "aws_security_group_rule" "cdp_knox_sg_egress" { protocol = "all" } +# VPC Endpoint SG +resource "aws_security_group" "cdp_endpoint_sg" { + + count = var.create_vpc_endpoints ? 1 : 0 + + vpc_id = local.vpc_id + name = local.security_group_endpoint_name + description = local.security_group_endpoint_name + tags = merge(local.env_tags, { Name = local.security_group_endpoint_name }) +} + +# Create self reference ingress rule to allow communication within the security group +resource "aws_security_group_rule" "cdp_endpoint_ingress_self" { + + count = var.create_vpc_endpoints ? 1 : 0 + + security_group_id = aws_security_group.cdp_endpoint_sg[0].id + type = "ingress" + from_port = 0 + to_port = 0 + description = "Self-reference ingress rule" + protocol = "all" + self = true +} + +# Create security group rules from combining the default and extra list of ingress rules +resource "aws_security_group_rule" "cdp_endpoint_sg_ingress" { + count = var.create_vpc_endpoints ? length(concat(local.security_group_rules_ingress, local.security_group_rules_extra_ingress)) : 0 + + description = "Ingress rules for Endpoint Security Group" + security_group_id = aws_security_group.cdp_endpoint_sg[0].id + type = "ingress" + cidr_blocks = tolist(concat(local.security_group_rules_ingress, local.security_group_rules_extra_ingress))[count.index].cidr + from_port = tolist(concat(local.security_group_rules_ingress, local.security_group_rules_extra_ingress))[count.index].port + to_port = tolist(concat(local.security_group_rules_ingress, local.security_group_rules_extra_ingress))[count.index].port + protocol = tolist(concat(local.security_group_rules_ingress, local.security_group_rules_extra_ingress))[count.index].protocol +} + +# Terraform removes the default ALLOW ALL egress. Let's recreate this +resource "aws_security_group_rule" "cdp_endpoint_sg_egress" { + + count = var.create_vpc_endpoints ? 1 : 0 + + description = "Egress rule for Endpoint CDP Security Group" + security_group_id = aws_security_group.cdp_endpoint_sg[0].id + type = "egress" + cidr_blocks = var.cdp_endpoint_sg_egress_cidrs + from_port = 0 + to_port = 0 + protocol = "all" +} + +# ------- VPC Endpoints ------- +# S3 Gateway endpoint +resource "aws_vpc_endpoint" "gateway_endpoints" { + + for_each = { + for k, v in toset(var.vpc_endpoint_gateway_services) : k => v + if var.create_vpc_endpoints == true + } + + vpc_id = local.vpc_id + service_name = data.aws_vpc_endpoint_service.gateway_endpoints[each.key].service_name + vpc_endpoint_type = "Gateway" + route_table_ids = concat([local.default_route_table_id], local.public_route_table_ids, local.private_route_table_ids) + + tags = merge(local.env_tags, { Name = "${var.env_prefix}-${each.key}-gateway-endpoint" }) +} + +# Interface endpoints +# From list in vpc_endpoint_interface_services +resource "aws_vpc_endpoint" "interface_endpoints" { + + for_each = { + for k, v in toset(var.vpc_endpoint_interface_services) : k => v + if var.create_vpc_endpoints == true + } + + vpc_id = local.vpc_id + service_name = data.aws_vpc_endpoint_service.interface_endpoints[each.key].service_name + vpc_endpoint_type = "Interface" + private_dns_enabled = true + + subnet_ids = concat(local.public_subnet_ids, local.private_subnet_ids) + security_group_ids = [aws_security_group.cdp_endpoint_sg[0].id] + + tags = merge(local.env_tags, { Name = "${var.env_prefix}-${each.key}-interface-endpoint" }) +} +# S3-Global Interface endpoint +resource "aws_vpc_endpoint" "s3_global_interface_endpoint" { + + count = var.create_vpc_endpoints ? 1 : 0 + + vpc_id = local.vpc_id + service_name = "com.amazonaws.s3-global.accesspoint" + vpc_endpoint_type = "Interface" + private_dns_enabled = true + + subnet_ids = concat(local.public_subnet_ids, local.private_subnet_ids) + security_group_ids = [aws_security_group.cdp_endpoint_sg[0].id] + + tags = merge(local.env_tags, { Name = "${var.env_prefix}-s3-global-interface-endpoint" }) +} + # ------- S3 Buckets ------- resource "random_id" "bucket_suffix" { count = var.random_id_for_bucket ? 1 : 0 diff --git a/modules/terraform-cdp-aws-pre-reqs/variables.tf b/modules/terraform-cdp-aws-pre-reqs/variables.tf index 5676875..9fb30c5 100644 --- a/modules/terraform-cdp-aws-pre-reqs/variables.tf +++ b/modules/terraform-cdp-aws-pre-reqs/variables.tf @@ -141,6 +141,14 @@ variable "security_group_knox_name" { default = null } +variable "security_group_endpoint_name" { + type = string + + description = "Security Group for VPC Endpoints" + + default = null +} + variable "ingress_extra_cidrs_and_ports" { type = object({ cidrs = list(string) @@ -170,6 +178,50 @@ variable "cdp_knox_sg_egress_cidrs" { default = ["0.0.0.0/0"] } +variable "cdp_endpoint_sg_egress_cidrs" { + type = list(string) + + description = "List of egress CIDR blocks for VPC Endpoint Security Group Egress rule" + + default = ["0.0.0.0/0"] +} + +variable "create_vpc_endpoints" { + type = bool + + description = "Flag to specify if VPC Endpoints should be created" + + default = true +} + +variable "vpc_endpoint_gateway_services" { + type = list(string) + + description = "List of AWS services used for VPC Gateway Endpoints" + + default = ["s3"] + +} + +variable "vpc_endpoint_interface_services" { + type = list(string) + + description = "List of AWS services used for VPC Interface Endpoints" + + default = [ + "sts", + "rds", + "elasticloadbalancing", + "elasticfilesystem", + "eks", + "ecr.dkr", + "ecr.api", + "ec2", + "cloudformation", + "autoscaling", + ] +} + # ------- Storage Resources ------- variable "random_id_for_bucket" { type = bool