0 ? 1 : 0
15 | key_id = aws_kms_key.terraform-state[0].id
16 | policy = data.aws_iam_policy_document.terraform-state.json
17 | }
18 |
19 | data "aws_iam_policy_document" "terraform-state" {
20 | statement {
21 | sid = "Enable IAM User Permissions"
22 | effect = "Allow"
23 | actions = ["kms:*"]
24 | resources = ["*"]
25 |
26 | dynamic "principals" {
27 | for_each = var.principals
28 | content {
29 | type = principals.value.type
30 | identifiers = principals.value.identifiers
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/modules/terraform-backend/output.tf:
--------------------------------------------------------------------------------
1 | output "kms-key-id" {
2 | value = var.kms-encryption ? aws_kms_key.terraform-state[0].id : null
3 | }
4 |
5 | output "kms-key-arn" {
6 | value = var.kms-encryption ? aws_kms_key.terraform-state[0].arn : null
7 | }
8 |
9 | output "s3-bucket" {
10 | value = aws_s3_bucket.infrastructure.bucket
11 | }
12 |
13 | output "lock-table-name" {
14 | value = var.lock_table_enabled ? aws_dynamodb_table.terraform-state-lock[0].name : null
15 | }
16 |
--------------------------------------------------------------------------------
/modules/terraform-backend/s3.tf:
--------------------------------------------------------------------------------
1 | resource "aws_s3_bucket" "infrastructure" {
2 | bucket = "${var.project}-terraform-${var.env}"
3 |
4 | tags = {
5 | Name = "${var.project} ${var.env} infrastructure bucket"
6 | Project = var.project
7 | Environment = var.env
8 | }
9 | }
10 |
11 | data "aws_iam_policy_document" "terraform-state-storage" {
12 | policy_id = "terraform-s3-${var.env}-state-policy"
13 | statement {
14 | sid = "AllowSSLRequestsOnly"
15 | principals {
16 | type = "*"
17 | identifiers = ["*"]
18 | }
19 | effect = "Deny"
20 | actions = ["s3:*"]
21 | resources = [
22 | "arn:aws:s3:::${var.project}-terraform-${var.env}/*",
23 | "arn:aws:s3:::${var.project}-terraform-${var.env}"
24 | ]
25 | condition {
26 | test = "Bool"
27 | variable = "aws:SecureTransport"
28 | values = ["false"]
29 | }
30 | }
31 | }
32 |
33 | resource "aws_s3_bucket_policy" "infrastructure" {
34 | bucket = aws_s3_bucket.infrastructure.id
35 | policy = data.aws_iam_policy_document.terraform-state-storage.json
36 | }
37 |
38 | resource "aws_s3_bucket_versioning" "infrastructure" {
39 | bucket = aws_s3_bucket.infrastructure.id
40 | versioning_configuration {
41 | status = "Enabled"
42 | }
43 | }
44 |
45 | resource "aws_s3_bucket_public_access_block" "infrastructure" {
46 | bucket = aws_s3_bucket.infrastructure.id
47 |
48 | block_public_acls = true
49 | block_public_policy = true
50 | ignore_public_acls = true
51 | restrict_public_buckets = true
52 | }
53 |
54 | resource "aws_s3_bucket_server_side_encryption_configuration" "infrastructure" {
55 | bucket = aws_s3_bucket.infrastructure.id
56 | rule {
57 | apply_server_side_encryption_by_default {
58 | sse_algorithm = length(aws_kms_key.terraform-state) > 0 ? "aws:kms" : "AES256"
59 | kms_master_key_id = length(aws_kms_key.terraform-state) > 0 ? aws_kms_key.terraform-state[0].id : null
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/modules/terraform-backend/vars.tf:
--------------------------------------------------------------------------------
1 | variable "env" {
2 | default = "dev"
3 | description = "The environment for the terraform backend"
4 | }
5 |
6 | variable "project" {
7 | type = string
8 | description = "The name of the project/product"
9 | }
10 |
11 | variable "lock_table_enabled" {
12 | default = true
13 | description = "True - to enable locking of the state file with DynamoDB"
14 | }
15 |
16 | variable "kms-encryption" {
17 | default = true
18 | description = "True - to Create KMS key to use for encrypting the S3 bucket. Otherwise - AES256 will be used"
19 | }
20 |
21 | variable "kms-deletion-window" {
22 | default = 30
23 | description = "The duration in days after which the key is deleted after destruction of the resource, must be between 7 and 30 days"
24 | validation {
25 | condition = var.kms-deletion-window >= 7 && var.kms-deletion-window <= 30
26 | error_message = "kms-deletion-window must be between 7 and 30 days"
27 | }
28 | }
29 |
30 | variable "principals" {
31 | description = "List of principals allowed to use kms key"
32 | type = list(object({
33 | type = string
34 | identifiers = list(string)
35 | }))
36 | default = []
37 | }
38 |
--------------------------------------------------------------------------------
/modules/tgw-routes/route.tf:
--------------------------------------------------------------------------------
1 | resource "aws_route" "source-route" {
2 | for_each = {for route-table in var.source_route_tables: route-table => true }
3 | route_table_id = each.key
4 | destination_cidr_block = var.destination_cidr
5 | transit_gateway_id = var.transit_gateway_id
6 | }
7 |
8 | resource "aws_route" "destination-route" {
9 | for_each = {for route-table in var.destination_route_tables: route-table => true }
10 | route_table_id = each.key
11 | destination_cidr_block = var.source_cidr
12 | transit_gateway_id = var.transit_gateway_id
13 | }
14 |
--------------------------------------------------------------------------------
/modules/tgw-routes/variables.tf:
--------------------------------------------------------------------------------
1 | variable "source_route_tables" {
2 | description = "source vpc route tables"
3 | }
4 |
5 | variable "destination_route_tables" {
6 | description = "destination vpc route tables"
7 | }
8 |
9 | variable "source_cidr" {
10 |
11 | }
12 | variable "destination_cidr" {
13 |
14 | }
15 |
16 | variable "transit_gateway_id" {
17 |
18 | }
--------------------------------------------------------------------------------
/modules/waf/README.md:
--------------------------------------------------------------------------------
1 | ## Requirements
2 |
3 | No requirements.
4 |
5 | ## Providers
6 |
7 | | Name | Version |
8 | |------|---------|
9 | | [aws](#provider\_aws) | n/a |
10 |
11 | ## Modules
12 |
13 | No modules.
14 |
15 | ## Resources
16 |
17 | | Name | Type |
18 | |------|------|
19 | | [aws_wafv2_ip_set.ratelimit_ipset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
20 | | [aws_wafv2_web_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource |
21 | | [aws_wafv2_web_acl_association.alb-waf-association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association) | resource |
22 |
23 | ## Inputs
24 |
25 | | Name | Description | Type | Default | Required |
26 | |------|-------------|------|---------|:--------:|
27 | | [env](#input\_env) | optional environment | `string` | `""` | no |
28 | | [lb\_arns](#input\_lb\_arns) | ARN of ALBs to associate with WAF | `set(string)` | `[]` | no |
29 | | [managed\_rules](#input\_managed\_rules) | managed rules | list(object({
name = string
priority = number
managed_rule_name = string
managed_rule_vendor_name = string
block = bool
blocking_rules = optional(list(string))
allowing_rules = optional(list(string))
counting_rules = optional(list(string))
}))
| `[]` | no |
30 | | [name](#input\_name) | name of WAF | `string` | `"alb-waf"` | no |
31 | | [ratelimit\_rules](#input\_ratelimit\_rules) | ratelimiting rules | list(object({
name = string
limit = number
priority = number
exclude_ip_ranges = list(string)
block = bool
}))
| `[]` | no |
32 | | [regex\_match\_rules](#input\_regex\_match\_rules) | n/a | list(object({
name = string
priority = number
action = string # "count" or "block"
statement = any
rule_label = optional(list(string), null)
}))
| `[]` | no |
33 | | [scope](#input\_scope) | scope of WAF, use 'CLOUDFRONT' for CloudFront distributions | `string` | `"REGIONAL"` | no |
34 |
35 | ## Outputs
36 |
37 | No outputs.
38 |
--------------------------------------------------------------------------------
/modules/waf/associations.tf:
--------------------------------------------------------------------------------
1 | resource "aws_wafv2_web_acl_association" "alb-waf-association" {
2 | for_each = var.lb_arns
3 | resource_arn = each.value
4 | web_acl_arn = aws_wafv2_web_acl.this.arn
5 | }
6 |
--------------------------------------------------------------------------------
/modules/waf/vars.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | description = "name of WAF"
3 | default = "alb-waf"
4 | }
5 | variable "scope" {
6 | description = "scope of WAF, use 'CLOUDFRONT' for CloudFront distributions"
7 | default = "REGIONAL"
8 | validation {
9 | condition = var.scope == "REGIONAL" || var.scope == "CLOUDFRONT"
10 | error_message = "scope must be REGIONAL or CLOUDFRONT"
11 | }
12 | }
13 | variable "lb_arns" {
14 | type = set(string)
15 | description = "ARN of ALBs to associate with WAF"
16 | default = []
17 | }
18 |
19 | variable "env" {
20 | description = "optional environment"
21 | default = ""
22 | }
23 |
24 | variable "ratelimit_rules" {
25 | description = "ratelimiting rules"
26 | type = list(object({
27 | name = string
28 | limit = number
29 | priority = number
30 | exclude_ip_ranges = list(string)
31 | block = bool
32 | }))
33 | default = []
34 | }
35 | variable "managed_rules" {
36 | description = "managed rules"
37 | type = list(object({
38 | name = string
39 | priority = number
40 | managed_rule_name = string
41 | managed_rule_vendor_name = string
42 | block = bool
43 | blocking_rules = optional(list(string))
44 | allowing_rules = optional(list(string))
45 | counting_rules = optional(list(string))
46 | }))
47 | default = []
48 | }
49 | variable "regex_match_rules" {
50 | type = list(object({
51 | name = string
52 | priority = number
53 | action = string # "count" or "block"
54 | statement = any
55 | rule_label = optional(list(string), null)
56 | }))
57 | default = []
58 | }
59 |
--------------------------------------------------------------------------------
/modules/wireguard-legacy/config-files.tf:
--------------------------------------------------------------------------------
1 | resource "aws_s3_object" "docker-compose" {
2 | bucket = aws_s3_bucket.configuration-bucket.id
3 | key = "firezone/docker-compose.yml"
4 | source = "${path.module}/templates/docker-compose.yml"
5 | etag = filemd5("${path.module}/templates/docker-compose.yml")
6 | }
--------------------------------------------------------------------------------
/modules/wireguard-legacy/efs.tf:
--------------------------------------------------------------------------------
1 | resource "aws_efs_file_system" "vpn" {
2 | performance_mode = "generalPurpose"
3 |
4 | tags = {
5 | Name = "vpn"
6 | }
7 | }
8 |
9 | resource "aws_efs_mount_target" "vpn" {
10 | count = 2
11 |
12 | file_system_id = aws_efs_file_system.vpn.id
13 | subnet_id = var.efs_subnet_ids[count.index]
14 | security_groups = [aws_security_group.vpn-efs.id]
15 | }
16 |
17 | resource "aws_security_group" "vpn-efs" {
18 | name = "vpn-efs"
19 | description = "vpn-efs"
20 | vpc_id = var.vpc_id
21 |
22 | ingress {
23 | from_port = 2049
24 | protocol = "tcp"
25 | to_port = 2049
26 | security_groups = [aws_security_group.vpn-instance.id]
27 | description = "Allow traffic from vpn to efs"
28 | }
29 |
30 | egress {
31 | from_port = 0
32 | protocol = "-1"
33 | to_port = 0
34 | cidr_blocks = ["0.0.0.0/0"]
35 | }
36 |
37 | tags = {
38 | Name = "vpn-efs"
39 | }
40 | }
--------------------------------------------------------------------------------
/modules/wireguard-legacy/output.tf:
--------------------------------------------------------------------------------
1 | output "vpn-ip" {
2 | value = aws_eip.vpn_ip.public_ip
3 | }
4 |
--------------------------------------------------------------------------------
/modules/wireguard-legacy/rds.tf:
--------------------------------------------------------------------------------
1 | module "vpn-rds" {
2 | source = "github.com/in4it/terraform-modules//modules/rds"
3 | name = "vpn"
4 | storage = "20"
5 | storage_type = "gp2"
6 | engine = "postgres"
7 | engine_version = "15.2"
8 | username = "vpn"
9 | database_name = "vpn"
10 | vpc_id = var.vpc_id
11 | instance_type = var.db_instance_type
12 | subnet_group = ""
13 | subnet_ids = var.db_subnet_ids
14 |
15 | ingress_security_groups = [aws_security_group.vpn-instance.id]
16 | }
17 |
--------------------------------------------------------------------------------
/modules/wireguard-legacy/s3.tf:
--------------------------------------------------------------------------------
1 |
2 | resource "aws_s3_bucket" "configuration-bucket" {
3 | bucket = "vpn-configuration-${data.aws_caller_identity.current.account_id}-${var.env}"
4 |
5 | lifecycle {
6 | prevent_destroy = true
7 | }
8 | }
9 |
10 | resource "aws_s3_bucket_public_access_block" "configuration-bucket" {
11 | bucket = aws_s3_bucket.configuration-bucket.id
12 |
13 | block_public_acls = true
14 | block_public_policy = true
15 | ignore_public_acls = true
16 | restrict_public_buckets = true
17 | }
18 |
19 | resource "aws_s3_bucket_versioning" "configuration-bucket" {
20 | bucket = aws_s3_bucket.configuration-bucket.id
21 | versioning_configuration {
22 | status = "Enabled"
23 | }
24 | }
25 |
26 | resource "aws_s3_bucket_server_side_encryption_configuration" "configuration-bucket" {
27 | bucket = aws_s3_bucket.configuration-bucket.id
28 |
29 | rule {
30 | apply_server_side_encryption_by_default {
31 | sse_algorithm = "AES256"
32 | }
33 | }
34 | }
35 |
36 | resource "aws_s3_bucket_policy" "configuration-bucket" {
37 | bucket = aws_s3_bucket.configuration-bucket.id
38 | policy = < /etc/caddy/Caddyfile && caddy run --config /etc/caddy/Caddyfile
31 |
32 | ${EXTERNAL_URL:-https://} {
33 | log
34 | reverse_proxy * 172.25.0.100:${PHOENIX_PORT:-13000}
35 | ${TLS_OPTS:-}
36 | }
37 | EOF
38 | network_mode: "host"
39 | deploy:
40 | <<: *default-deploy
41 |
42 | firezone:
43 | image: firezone/firezone:${VERSION:-latest}
44 | ports:
45 | - ${WIREGUARD_PORT:-51820}:${WIREGUARD_PORT:-51820}/udp
46 | env_file:
47 | # This should contain a list of env vars for configuring Firezone.
48 | # See https://www.firezone.dev/docs/reference/env-vars for more info.
49 | - ${FZ_INSTALL_DIR:-.}/.env
50 | volumes:
51 | # IMPORTANT: Persists WireGuard private key and other data. If
52 | # /var/firezone/private_key exists when Firezone starts, it is
53 | # used as the WireGuard private. Otherwise, one is generated.
54 | - ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone
55 | cap_add:
56 | # Needed for WireGuard and firewall support.
57 | - NET_ADMIN
58 | - SYS_MODULE
59 | sysctls:
60 | # Needed for masquerading and NAT.
61 | - net.ipv6.conf.all.disable_ipv6=0
62 | - net.ipv4.ip_forward=1
63 | - net.ipv6.conf.all.forwarding=1
64 | networks:
65 | firezone-network:
66 | ipv4_address: 172.25.0.100
67 | ipv6_address: 2001:3990:3990::99
68 |
69 | deploy:
70 | <<: *default-deploy
71 |
72 | networks:
73 | firezone-network:
74 | enable_ipv6: true
75 | driver: bridge
76 | ipam:
77 | config:
78 | - subnet: 172.25.0.0/16
79 | - subnet: 2001:3990:3990::/64
80 | gateway: 2001:3990:3990::1
--------------------------------------------------------------------------------
/modules/wireguard-legacy/templates/userdata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | apt-get update
4 | apt-get install ca-certificates curl gnupg awscli git binutils -y
5 | install -m 0755 -d /etc/apt/keyrings
6 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
7 | chmod a+r /etc/apt/keyrings/docker.gpg
8 |
9 | echo \
10 | "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
11 | "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
12 | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
13 |
14 | apt-get update
15 | apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
16 |
17 | # efs helper
18 | cd /root
19 | git clone https://github.com/aws/efs-utils
20 | cd efs-utils
21 | ./build-deb.sh
22 | mv ./build/amazon-efs-utils*deb /
23 | apt-get -y install /amazon-efs-utils*deb
24 | rm /amazon-efs-utils*deb
25 | mkdir /efs
26 | echo -e "${efs_fs_id}\t/efs\tefs\t_netdev,noresvport,tls" >> /etc/fstab
27 | mount /efs
28 |
29 | mkdir /efs/firezone
30 | aws s3 cp s3://${s3_bucket}/firezone/docker-compose.yml /efs/firezone/
31 |
32 | curl -L -o /bin/aws-env https://github.com/in4it/aws-env/releases/download/v0.7/aws-env-linux-amd64
33 | chmod +x /bin/aws-env
34 |
35 | cd /efs/firezone
36 | AWS_ENV_PATH=${aws_env_path} AWS_REGION=${aws_region} /bin/aws-env --format=dotenv | sed 's#\$#\\$#g' > .env
37 |
38 | echo "# The ability to change the IPv4 and IPv6 address pool will be removed
39 | # in a future Firezone release in order to reduce the possible combinations
40 | # of network configurations we need to handle.
41 | #
42 | # Due to the above, we recommend not changing these unless absolutely
43 | # necessary.
44 | WIREGUARD_IPV4_NETWORK=100.64.0.0/10
45 | WIREGUARD_IPV4_ADDRESS=100.64.0.1
46 | WIREGUARD_IPV6_NETWORK=fd00::/106
47 | WIREGUARD_IPV6_ADDRESS=fd00::1" >> .env
48 |
49 | if [ ! -e .setup-completed ] ; then
50 | docker compose run --rm firezone bin/migrate
51 | docker compose run --rm firezone bin/create-or-reset-admin
52 | fi
53 |
54 |
55 | touch .setup-completed
56 | docker compose up -d
--------------------------------------------------------------------------------
/modules/wireguard-legacy/variables.tf:
--------------------------------------------------------------------------------
1 | variable "instance_type" {
2 | default = "t3.micro"
3 | }
4 | variable "db_instance_type" {
5 | default = "db.t4g.micro"
6 | }
7 | variable "vpc_id" {
8 |
9 | }
10 | variable "instance_subnet_id" {
11 | description = "subnet to launch the EC2 in"
12 | }
13 | variable "db_subnet_ids" {
14 | description = "subnets to launch the DB in"
15 | }
16 | variable "efs_subnet_ids" {
17 | description = "subnets to create the efs mountpoints in"
18 | }
19 | variable "external_url" {
20 | description = "external url the VPN is going to be reachable over. Starts with https://"
21 | }
22 | variable "admin_email" {
23 | description = "email of administrator"
24 | }
25 | variable "log_retention_days" {
26 | default = 30
27 | }
28 |
29 | variable "listeners" {
30 | type = list(object({
31 | port = string
32 | protocol = string
33 | cidr_blocks = list(string)
34 | }))
35 | default = [{
36 | port = "51820"
37 | protocol = "udp"
38 | cidr_blocks = ["0.0.0.0/0"]
39 | },
40 | {
41 | port = "80"
42 | protocol = "tcp"
43 | cidr_blocks = ["0.0.0.0/0"]
44 | },
45 | {
46 | port = "443"
47 | protocol = "tcp"
48 | cidr_blocks = ["0.0.0.0/0"]
49 | }]
50 | }
51 |
52 | variable "env" {
53 | default = "prod"
54 | }
55 |
56 | variable "tags" {
57 | default = {}
58 | type = map(string)
59 | }
60 |
--------------------------------------------------------------------------------
/modules/wireguard-legacy/vpn-instance-iam.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_instance_profile" "vpn_iam_instance_profile" {
2 | name = "vpn-iam-instance-profile-${var.env}"
3 | role = aws_iam_role.vpn-iam-role.name
4 | }
5 |
6 | resource "aws_iam_role" "vpn-iam-role" {
7 | name = "vpn-iam-role-${var.env}"
8 | path = "/"
9 |
10 | assume_role_policy = < /etc/wireguard/site2site.key
22 | fi
23 |
24 | if [ -z "$PUBLIC_KEY" ] ; then
25 | cat /etc/wireguard/site2site.key | wg pubkey | tee /etc/wireguard/site2site.pub
26 | aws ssm put-parameter --region ${aws_region} --name ${aws_env_path}PUBLIC_KEY --value $(cat /etc/wireguard/site2site.pub) --key-id $KMS_ID --type SecureString
27 | else
28 | echo $PUBLIC_KEY > /etc/wireguard/site2site.pub
29 | fi
30 |
31 |
32 |
33 | echo "[Interface]
34 | PostUp = wg set %i private-key /etc/wireguard/%i.key
35 | Address = $VPN_INTERNAL_CIDR
36 | ListenPort = 51820
37 |
38 | [Peer]
39 | PublicKey = ${vpn_destination_pubkey}
40 | AllowedIPs = $VPN_DESTINATION_ALLOWED_IPS,$VPN_INTERNAL_CIDR
41 | Endpoint = ${vpn_destination_public_ip}:51820" > /etc/wireguard/site2site.conf
42 |
43 | sysctl -w net.ipv4.ip_forward=1
44 |
45 | echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
46 |
47 | systemctl enable --now wg-quick@site2site
--------------------------------------------------------------------------------
/modules/wireguard-site2site/variables.tf:
--------------------------------------------------------------------------------
1 | variable "identifier" {
2 | default = "site2site"
3 | }
4 | variable "instance_type" {
5 | default = "t3.micro"
6 | }
7 |
8 | variable "source_dest_check" {
9 | default = false
10 | }
11 |
12 | variable "vpc_id" {
13 |
14 | }
15 | variable "instance_subnet_id" {
16 | description = "subnet to launch the EC2 in"
17 | }
18 | variable "log_retention_days" {
19 | default = 30
20 | }
21 |
22 | variable "listeners" {
23 | type = list(object({
24 | port = string
25 | protocol = string
26 | cidr_blocks = list(string)
27 | }))
28 | default = [{
29 | port = "51820"
30 | protocol = "udp"
31 | cidr_blocks = ["0.0.0.0/0"]
32 | }]
33 | }
34 |
35 | variable "env" {
36 | default = "prod"
37 | }
38 |
39 | variable "vpn_internal_cidr" {
40 |
41 | }
42 | variable "vpn_destination_allowed_ips" {
43 |
44 | }
45 | variable "vpn_destination_pubkey" {
46 |
47 | }
48 | variable "vpn_destination_public_ip" {
49 |
50 | }
51 |
52 | variable "tags" {
53 | default = {}
54 | type = map(string)
55 | }
56 |
--------------------------------------------------------------------------------
/modules/wireguard-site2site/vpn-instance-iam.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_instance_profile" "vpn_iam_instance_profile" {
2 | name = "${var.identifier}-vpn-iam-instance-profile-${var.env}"
3 | role = aws_iam_role.vpn-iam-role.name
4 | }
5 |
6 | resource "aws_iam_role" "vpn-iam-role" {
7 | name = "${var.identifier}-vpn-iam-role-${var.env}"
8 | path = "/"
9 |
10 | assume_role_policy = < /root/rustup.sh && chmod +x /root/rustup.sh && /root/rustup.sh -y
7 | . "$HOME/.cargo/env"
8 |
9 | # reinitialize vpn
10 | systemctl stop vpn-rest-server
11 | systemctl stop vpn-configmanager
12 | wg-quick down vpn
13 | rm -rf /vpn/config
14 | rm -rf /vpn/secrets
15 | rm -rf /vpn/tls-certs
16 |
17 | # efs helper
18 | cd /root
19 | git clone https://github.com/aws/efs-utils
20 | cd efs-utils
21 | git checkout v2.0.4
22 | ./build-deb.sh
23 | chmod 755 ~/efs-utils/build/amazon-efs-utils*deb
24 | mv ~/efs-utils//build/amazon-efs-utils*deb /
25 | apt-get -y install /amazon-efs-utils*deb
26 | rm /amazon-efs-utils*deb
27 |
28 | # Clean up packages after efs installation
29 | apt-get remove --purge -y binutils build-essential git rustc cargo pkg-config libssl-dev
30 | apt-get autoremove -y
31 | apt-get clean
32 |
33 | # set mounts in fstab
34 | echo -e "${efs_fs_id}\t/efs\tefs\t_netdev,noresvport,tls" >> /etc/fstab
35 | echo -e "${efs_fs_id}:/config\t/vpn/config\tefs\t_netdev,noresvport,tls" >> /etc/fstab
36 | echo -e "${efs_fs_id}:/secrets\t/vpn/secrets\tefs\t_netdev,noresvport,tls" >> /etc/fstab
37 | echo -e "${efs_fs_id}:/tls-certs\t/vpn/tls-certs\tefs\t_netdev,noresvport,tls" >> /etc/fstab
38 | echo -e "${efs_fs_id}:/stats\t/vpn/stats\tefs\t_netdev,noresvport,tls" >> /etc/fstab
39 |
40 | # require mounts before starting the vpn server
41 | sed -i 's#\[Unit\]#[Unit]\nRequires=vpn-config.mount vpn-secrets.mount\nAfter=vpn-config.mount vpn-secrets.mount#' /etc/systemd/system/vpn-configmanager.service
42 | sed -i 's#\[Unit\]#[Unit]\nRequires=vpn-config.mount vpn-secrets.mount\nAfter=vpn-config.mount vpn-secrets.mount#' /etc/systemd/system/vpn-rest-server.service
43 |
44 | # reload systemd
45 | systemctl daemon-reload
46 |
47 | # create directories and mount
48 | mkdir -p /efs
49 | mount /efs
50 | mkdir -p /efs/config
51 | mkdir -p /efs/secrets
52 | mkdir -p /efs/tls-certs
53 | mkdir -p /efs/stats
54 | chown vpn:vpn /efs/config
55 | chown vpn:vpn /efs/tls-certs
56 | chown vpn:vpn /efs/stats
57 | chmod 700 /efs
58 | chmod 700 /efs/config
59 | chmod 700 /efs/tls-certs
60 | chmod 700 /efs/secrets
61 | chmod 700 /efs/stats
62 |
63 | mkdir /vpn/config
64 | mount /vpn/config
65 | mkdir /vpn/secrets
66 | mount /vpn/secrets
67 | mkdir /vpn/tls-certs
68 | mount /vpn/tls-certs
69 | mkdir /vpn/stats
70 | mount /vpn/stats
71 |
72 | # restart vpn
73 | systemctl start vpn-rest-server
74 | systemctl start vpn-configmanager
75 |
--------------------------------------------------------------------------------
/modules/wireguard-vpn-server/variables.tf:
--------------------------------------------------------------------------------
1 | variable "instance_type" {
2 | default = "t3.small"
3 | }
4 |
5 | variable "vpc_id" {
6 | description = "VPC id to launch the VPN Server in"
7 |
8 | }
9 | variable "instance_subnet_id" {
10 | description = "subnet to launch the VPN Server in"
11 | }
12 |
13 | variable "instance_profile_name" {
14 | default = ""
15 | description = "use a custom instance profile"
16 | }
17 |
18 | variable "efs_subnet_ids" {
19 | description = "subnets to create the efs mountpoints in"
20 | }
21 |
22 | variable "efs_encrypted" {
23 | description = "Enable EFS encryption"
24 | default = false
25 | }
26 |
27 | variable "efs_kms_key_id" {
28 | description = "EFS CMK ID for encryption"
29 | default = ""
30 | }
31 |
32 | variable "efs_backup_enabled" {
33 | description = "Enable EFS backups"
34 | default = false
35 | }
36 |
37 | variable "env" {
38 | default = "prod"
39 | }
40 |
41 | variable "listeners" {
42 | type = list(object({
43 | port = string
44 | protocol = string
45 | cidr_blocks = list(string)
46 | }))
47 | default = [{
48 | port = "51820"
49 | protocol = "udp"
50 | cidr_blocks = ["0.0.0.0/0"]
51 | },
52 | {
53 | port = "80"
54 | protocol = "tcp"
55 | cidr_blocks = ["0.0.0.0/0"]
56 | },
57 | {
58 | port = "443"
59 | protocol = "tcp"
60 | cidr_blocks = ["0.0.0.0/0"]
61 | }]
62 | }
63 |
64 | variable "tags" {
65 | default = {}
66 | type = map(string)
67 | }
68 |
69 | variable "ami_owner" {
70 | default = "aws-marketplace"
71 | }
72 |
73 | variable "license" {
74 | default = "marketplace"
75 | }
76 |
--------------------------------------------------------------------------------
/modules/wireguard-vpn-server/vpn-server.tf:
--------------------------------------------------------------------------------
1 | data "aws_region" "current" {}
2 | data "aws_caller_identity" "current" {}
3 |
4 | resource "aws_instance" "vpn-server" {
5 | depends_on = [
6 | aws_efs_mount_target.vpn-server-config
7 | ]
8 | ami = data.aws_ami.vpn-server.id
9 | instance_type = var.instance_type
10 | subnet_id = var.instance_subnet_id
11 | vpc_security_group_ids = [aws_security_group.vpn-server.id]
12 | iam_instance_profile = var.instance_profile_name != "" ? var.instance_profile_name : aws_iam_instance_profile.vpn-server.name
13 |
14 | user_data_base64 = base64encode(templatefile("${path.module}/templates/userdata.sh", {
15 | aws_region = data.aws_region.current.name
16 | efs_fs_id = aws_efs_file_system.vpn-server-config.id
17 | }))
18 |
19 | root_block_device {
20 | encrypted = true
21 | }
22 |
23 | metadata_options {
24 | http_endpoint = "enabled"
25 | http_tokens = "required"
26 | instance_metadata_tags = "enabled"
27 | }
28 |
29 | tags = merge({ Name = "vpn-server-${var.env}", env = var.env, license = var.license }, var.tags)
30 | }
31 |
32 | resource "aws_eip" "vpn-server" {
33 | instance = aws_instance.vpn-server.id
34 |
35 | lifecycle {
36 | prevent_destroy = true
37 | }
38 | }
39 |
40 | data "aws_ami" "vpn-server" {
41 | owners = [var.ami_owner]
42 | most_recent = true
43 |
44 | filter {
45 | name = "name"
46 | values = [var.license == "marketplace" ? "in4it-vpn-server-licensed-*" : "in4it-vpn-server-byol-*"]
47 | }
48 | }
49 |
50 | resource "aws_security_group" "vpn-server" {
51 | name = "vpn-server-${var.env}"
52 | vpc_id = var.vpc_id
53 |
54 | dynamic "ingress" {
55 | for_each = var.listeners
56 | content {
57 | from_port = ingress.value.port
58 | protocol = ingress.value.protocol
59 | to_port = ingress.value.port
60 | cidr_blocks = ingress.value.cidr_blocks
61 | }
62 | }
63 | egress {
64 | from_port = 0
65 | protocol = "-1"
66 | to_port = 0
67 | cidr_blocks = ["0.0.0.0/0"]
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/modules/wireguard/rds.tf:
--------------------------------------------------------------------------------
1 | module "vpn-rds" {
2 | source = "github.com/in4it/terraform-modules//modules/rds"
3 | name = "vpn"
4 | storage = "20"
5 | storage_type = "gp3"
6 | engine = "postgres"
7 | engine_version = var.db_engine_version
8 | username = "vpn"
9 | database_name = "vpn"
10 | vpc_id = var.vpc_id
11 | instance_type = var.db_instance_type
12 | subnet_group = ""
13 | subnet_ids = var.db_subnet_ids
14 |
15 | ingress_security_groups = [aws_security_group.vpn-instance.id]
16 | }
17 |
--------------------------------------------------------------------------------
/modules/wireguard/variables.tf:
--------------------------------------------------------------------------------
1 | variable "instance_type" {
2 | default = "t3.micro"
3 | }
4 | variable "db_instance_type" {
5 | default = "db.t4g.micro"
6 | }
7 | variable "vpc_id" {
8 |
9 | }
10 | variable "instance_subnet_id" {
11 | description = "subnet to launch the EC2 in"
12 | }
13 | variable "db_subnet_ids" {
14 | description = "subnets to launch the DB in"
15 | }
16 | variable "efs_subnet_ids" {
17 | description = "subnets to create the efs mountpoints in"
18 | }
19 | variable "external_url" {
20 | description = "external url the VPN is going to be reachable over. Starts with https://"
21 | }
22 | variable "admin_email" {
23 | description = "email of administrator"
24 | }
25 | variable "log_retention_days" {
26 | default = 30
27 | }
28 |
29 | variable "listeners" {
30 | type = list(object({
31 | port = string
32 | protocol = string
33 | cidr_blocks = list(string)
34 | }))
35 | default = [{
36 | port = "51820"
37 | protocol = "udp"
38 | cidr_blocks = ["0.0.0.0/0"]
39 | },
40 | {
41 | port = "80"
42 | protocol = "tcp"
43 | cidr_blocks = ["0.0.0.0/0"]
44 | },
45 | {
46 | port = "443"
47 | protocol = "tcp"
48 | cidr_blocks = ["0.0.0.0/0"]
49 | }]
50 | }
51 |
52 | variable "env" {
53 | default = "prod"
54 | }
55 |
56 | variable "tags" {
57 | default = {}
58 | type = map(string)
59 | }
60 | variable "db_engine_version" {
61 | default = "15.5"
62 | }
--------------------------------------------------------------------------------