diff --git a/apps/infra/production/network/README.md b/apps/infra/production/network/README.md index 52f40a990b..d8b2e30e26 100644 --- a/apps/infra/production/network/README.md +++ b/apps/infra/production/network/README.md @@ -1,3 +1,79 @@ # Network Project for Network + +코드당 인프라의 네트워크의 설정을 담은 프로젝트로 route53, cloudfront, subnet, nat 등을 관리합니다. +public network의 경우에는 본 프로젝트에서 관리하고, private network의 경우에는 각 리소스를 사용하는 서비스(프로젝트)에서 생성 및 사용하고, 본 프로젝트에서는 route table만 관리합니다. + + + +## Requirements + +| Name | Version | +|------|---------| +| [aws](#requirement\_aws) | ~> 5.52 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.52.0 | +| [aws.us-east-1](#provider\_aws.us-east-1) | 5.52.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_acm_certificate.codedang](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource | +| [aws_acm_certificate_validation.for_all_domains](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource | +| [aws_cloudfront_distribution.codedang](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource | +| [aws_eip.for_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | +| [aws_internet_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | +| [aws_nat_gateway.public_subnet1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | +| [aws_route53_record.certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.codedang](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_zone.codedang](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource | +| [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | +| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | +| [aws_route_table_association.admin_api1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.admin_api2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.client_api1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.client_api2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.iris1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.iris2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.public_subnet1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.public_subnet2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_subnet.public_subnet1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | +| [aws_subnet.public_subnet2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | +| [aws_vpc.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource | +| [aws_cloudfront_cache_policy.disable](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_cache_policy) | data source | +| [aws_cloudfront_origin_request_policy.allow_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_origin_request_policy) | data source | +| [aws_cloudfront_origin_request_policy.exclude_host_header](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_origin_request_policy) | data source | +| [aws_lb.admin_api](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/lb) | data source | +| [aws_lb.client_api](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/lb) | data source | +| [aws_subnet.private_admin_api1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnet.private_admin_api2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnet.private_client_api1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnet.private_client_api2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnet.private_iris1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | +| [aws_subnet.private_iris2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [private\_admin\_api1\_id](#input\_private\_admin\_api1\_id) | n/a | `any` | n/a | yes | +| [private\_admin\_api2\_id](#input\_private\_admin\_api2\_id) | n/a | `any` | n/a | yes | +| [private\_client\_api1\_id](#input\_private\_client\_api1\_id) | n/a | `any` | n/a | yes | +| [private\_client\_api2\_id](#input\_private\_client\_api2\_id) | n/a | `any` | n/a | yes | +| [private\_iris1\_id](#input\_private\_iris1\_id) | n/a | `any` | n/a | yes | +| [private\_iris2\_id](#input\_private\_iris2\_id) | n/a | `any` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/apps/infra/production/network/cloudfront.tf b/apps/infra/production/network/cloudfront.tf new file mode 100644 index 0000000000..37951c2b3a --- /dev/null +++ b/apps/infra/production/network/cloudfront.tf @@ -0,0 +1,104 @@ +data "aws_lb" "client_api" { + name = "Codedang-Client-Api-LB" +} + +data "aws_lb" "admin_api" { + name = "Codedang-Admin-Api-LB" +} + +data "aws_cloudfront_cache_policy" "disable" { + name = "Managed-CachingDisabled" +} + +data "aws_cloudfront_origin_request_policy" "allow_all" { + name = "Managed-AllViewer" +} + +data "aws_cloudfront_origin_request_policy" "exclude_host_header" { + name = "Managed-AllViewerExceptHostHeader" +} + +resource "aws_cloudfront_distribution" "codedang" { + origin { + domain_name = "amplify.codedang.com" + origin_id = "frontend" # TODO: Add unique ID of Amplify + + custom_origin_config { + http_port = 80 + https_port = 443 + origin_protocol_policy = "https-only" + origin_ssl_protocols = ["TLSv1.2"] + } + } + + origin { + domain_name = data.aws_lb.client_api.dns_name + origin_id = data.aws_lb.client_api.id + + custom_origin_config { + http_port = 80 + https_port = 443 + origin_protocol_policy = "http-only" # TODO: allow HTTPS only + origin_ssl_protocols = ["TLSv1.2"] + } + } + + origin { + domain_name = data.aws_lb.admin_api.dns_name + origin_id = data.aws_lb.admin_api.id + + custom_origin_config { + http_port = 80 + https_port = 443 + origin_protocol_policy = "http-only" + origin_ssl_protocols = ["TLSv1.2"] + } + } + + enabled = true + comment = "Codedang" + http_version = "http2and3" + + aliases = ["codedang.com"] + + default_cache_behavior { + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD", "OPTIONS"] + target_origin_id = "frontend" # TODO: do not hard-code origin_id + viewer_protocol_policy = "redirect-to-https" + cache_policy_id = data.aws_cloudfront_cache_policy.disable.id + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.exclude_host_header.id + } + + ordered_cache_behavior { + path_pattern = "/api/*" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD", "OPTIONS"] + target_origin_id = data.aws_lb.client_api.id + viewer_protocol_policy = "redirect-to-https" + cache_policy_id = data.aws_cloudfront_cache_policy.disable.id + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.allow_all.id + } + + ordered_cache_behavior { + path_pattern = "/graphql" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD", "OPTIONS"] + target_origin_id = data.aws_lb.admin_api.id + viewer_protocol_policy = "redirect-to-https" + cache_policy_id = data.aws_cloudfront_cache_policy.disable.id + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.allow_all.id + } + + restrictions { + geo_restriction { + restriction_type = "none" + } + } + + viewer_certificate { + acm_certificate_arn = aws_acm_certificate_validation.for_all_domains.certificate_arn + ssl_support_method = "sni-only" + minimum_protocol_version = "TLSv1.2_2021" + } +} diff --git a/apps/infra/production/network/main.tf b/apps/infra/production/network/main.tf index 111fb80883..be1016d9f6 100644 --- a/apps/infra/production/network/main.tf +++ b/apps/infra/production/network/main.tf @@ -16,5 +16,5 @@ terraform { } provider "aws" { - region = var.region + region = "ap-northeast-2" } diff --git a/apps/infra/production/network/private_network.tf b/apps/infra/production/network/private_network.tf new file mode 100644 index 0000000000..f188b0a1c4 --- /dev/null +++ b/apps/infra/production/network/private_network.tf @@ -0,0 +1,42 @@ +resource "aws_route_table" "private" { + vpc_id = aws_vpc.main.id + + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.public_subnet1.id + } + + tags = { + Name = "Codedang-Private-RT" + } +} + +resource "aws_route_table_association" "admin_api1" { + subnet_id = var.private_admin_api1_subnet_id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "admin_api2" { + subnet_id = var.private_admin_api2_subnet_id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "iris1" { + subnet_id = var.private_iris1_subnet_id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "iris2" { + subnet_id = var.private_iris2_subnet_id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "client_api1" { + subnet_id = var.private_client_api1_subnet_id + route_table_id = aws_route_table.private.id +} + +resource "aws_route_table_association" "client_api2" { + subnet_id = var.private_client_api2_subnet_id + route_table_id = aws_route_table.private.id +} diff --git a/apps/infra/production/network/public_network.tf b/apps/infra/production/network/public_network.tf new file mode 100644 index 0000000000..f4b16b7a6d --- /dev/null +++ b/apps/infra/production/network/public_network.tf @@ -0,0 +1,79 @@ +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "Codedang-InternetFacing" + } +} + +resource "aws_eip" "for_nat" { + domain = "vpc" + + lifecycle { + create_before_destroy = true + } + + depends_on = [aws_internet_gateway.main] + + tags = { + Name = "Codedang-NatEIP" + } +} + +# TODO: public_subnet2에도 할당 필요 (-> vpc endpoint로 수정하는 방향으로 진행하기) +resource "aws_nat_gateway" "public_subnet1" { + allocation_id = aws_eip.for_nat.id + subnet_id = aws_subnet.public_subnet1.id + + tags = { + Name = "Codedang-NatGateway-1" + } +} + +resource "aws_route_table" "public" { + vpc_id = aws_vpc.main.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.main.id + } + + route { + ipv6_cidr_block = "::/0" + gateway_id = aws_internet_gateway.main.id + } + + tags = { + Name = "Codedang-Public-RT" + } +} + +resource "aws_subnet" "public_subnet1" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.90.0/24" + availability_zone = "ap-northeast-2a" + + tags = { + Name = "Codedang-Public-Nat-Subnet1" + } +} + +resource "aws_subnet" "public_subnet2" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.91.0/24" + availability_zone = "ap-northeast-2c" + + tags = { + Name = "Codedang-Public-Nat-Subnet2" + } +} + +resource "aws_route_table_association" "public_subnet1" { + subnet_id = aws_subnet.public_subnet1.id + route_table_id = aws_route_table.public.id +} + +resource "aws_route_table_association" "public_subnet2" { + subnet_id = aws_subnet.public_subnet2.id + route_table_id = aws_route_table.public.id +} diff --git a/apps/infra/production/network/route53.tf b/apps/infra/production/network/route53.tf new file mode 100644 index 0000000000..4a4df005af --- /dev/null +++ b/apps/infra/production/network/route53.tf @@ -0,0 +1,52 @@ +provider "aws" { + alias = "us_east_1" + region = "us-east-1" +} + +resource "aws_route53_zone" "codedang" { + name = "codedang.com" +} + +resource "aws_route53_record" "codedang" { + name = "codedang.com" + type = "A" + zone_id = aws_route53_zone.codedang.zone_id + + alias { + name = aws_cloudfront_distribution.codedang.domain_name + zone_id = aws_cloudfront_distribution.codedang.hosted_zone_id + evaluate_target_health = false + } +} + +resource "aws_acm_certificate" "codedang" { + domain_name = "codedang.com" + validation_method = "DNS" + provider = aws.us_east_1 +} + +resource "aws_route53_record" "certificate" { + for_each = { + for dvo in aws_acm_certificate.codedang.domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + zone_id = aws_route53_zone.codedang.zone_id + type = dvo.resource_record_type + value = dvo.resource_record_value + } + } + + allow_overwrite = true + name = each.value.name + records = [each.value.value] + ttl = 60 + type = each.value.type + zone_id = each.value.zone_id +} + +resource "aws_acm_certificate_validation" "for_all_domains" { + provider = aws.us_east_1 + certificate_arn = aws_acm_certificate.codedang.arn + validation_record_fqdns = [ + for record in aws_route53_record.certificate : record.fqdn + ] +} diff --git a/apps/infra/production/network/variables.tf b/apps/infra/production/network/variables.tf index 5f8881bb65..4fc1834f13 100644 --- a/apps/infra/production/network/variables.tf +++ b/apps/infra/production/network/variables.tf @@ -1,5 +1,6 @@ -variable "region" { - type = string - description = "The region for provider" - default = "ap-northeast-2" -} +variable "private_admin_api1_subnet_id" {} +variable "private_admin_api2_subnet_id" {} +variable "private_iris1_subnet_id" {} +variable "private_iris2_subnet_id" {} +variable "private_client_api1_subnet_id" {} +variable "private_client_api2_subnet_id" {} diff --git a/apps/infra/production/network/vpc.tf b/apps/infra/production/network/vpc.tf new file mode 100644 index 0000000000..14f130ec07 --- /dev/null +++ b/apps/infra/production/network/vpc.tf @@ -0,0 +1,7 @@ +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = "Codedang-VPC" + } +}