AWS Client VPN endpoint
- Open AWS SSO service page. Select Applications from the sidebar
- Choose Add a new application
- Select Add a custom SAML 2.0 application
- Fill Display name and Description
- Set session duration (VPN session duration) - 12h
- Select "If you don't have a metadata file, you can manually type your metadata values."
- Application ACS URL: http://127.0.0.1:35001
- Application SAML audience: urn:amazon:webservices:clientvpn
- Save changes
- Download AWS SSO SAML metadata file (file for vpn secret)
- Select tab "Attribute mappings":
- Subject -> ${user:subject} -> emailAddress
- NameID -> ${user:email} -> basic
- memberOf -> ${user:groups} -> unspecified
- Select tab "Assigned users"
- Assign users or groups created on previous step
# Get metadata.xml file from AWS SSO/Applications
# Encode file with base64 `base64 -w 0` (linux only) or `openssl base64 -A`
# Create secret `saml_metadata` with key `saml_metadata_xml` and value base64
data "aws_secretsmanager_secret" "saml" {
name = "saml_metadata"
}
data "aws_secretsmanager_secret_version" "saml" {
secret_id = data.aws_secretsmanager_secret.saml.id
version_stage = "AWSCURRENT"
}
resource "aws_iam_saml_provider" "vpn" {
name = var.vpn_saml_provider_name # could be anything that satisfy regular expression pattern: [\w._-]+
saml_metadata_document = base64decode(jsondecode(data.aws_secretsmanager_secret_version.saml.secret_string)["saml_metadata_xml"]) # saml_metadata_xml
tags = var.tags
}
module "vpn" {
source = "fivexl/client-vpn-endpoint/aws"
endpoint_name = "myvpn"
endpoint_client_cidr_block = "10.100.0.0/16"
endpoint_subnets = [module.vpc.intra_subnets[0]] # Attach VPN to single subnet. Reduce cost
endpoint_vpc_id = module.vpc.vpc_id
tls_subject_common_name = "int.example.com"
saml_provider_arn = aws_iam_saml_provider.vpn.arn
authorization_rules = {}
additional_routes = {
"${module.vpc.intra_subnets[0]}" = "172.16.0.0/24"
}
authorization_rules_all_groups = {
full_access_private_subnet_0 = module.vpc.private_subnets_cidr_blocks[0]
}
tags = var.tags
}
variable "vpn_access_public" {
description = "List of SSO Group IDs for accessing public subnets"
type = list(string)
default = []
}
variable "vpn_access_private" {
description = "List of SSO Group IDs for accessing private subnets"
type = list(string)
default = []
}
variable "vpn_access_intra" {
description = "List of SSO Group IDs for accessing intra subnets"
type = list(string)
default = []
}
variable "vpn_access_db" {
description = "List of SSO Group IDs for accessing db subnets"
type = list(string)
default = []
}
variable "vpn_access_elasticache" {
description = "List of SSO Group IDs for accessing elasticache subnets"
type = list(string)
default = []
}
variable "vpn_access_all" {
description = "List of SSO Group IDs for accessing all subnets"
type = list(string)
default = []
}
# https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/limits.html
# Authorization rules per Client VPN endpoint defaul quota is 50
# https://console.aws.amazon.com/servicequotas/home/services/ec2/quotas/L-9A1BC94B
locals {
vpn_authorization_rules_public = { for item in setproduct(module.vpc.public_subnets_cidr_blocks, var.vpn_access_public) : "public_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules_private = { for item in setproduct(module.vpc.private_subnets_cidr_blocks, var.vpn_access_private) : "private_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules_intra = { for item in setproduct(module.vpc.intra_subnets_cidr_blocks, var.vpn_access_intra) : "intra_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules_db = { for item in setproduct(module.vpc.database_subnets_cidr_blocks, var.vpn_access_db) : "db_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules_elasticache = { for item in setproduct(module.vpc.elasticache_subnets_cidr_blocks, var.vpn_access_elasticache) : "elasticache_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules_all = { for item in setproduct([module.vpc.vpc_cidr_block], var.vpn_access_all) : "all_${item[0]}_${item[1]}" => "${item[0]},${item[1]}" }
vpn_authorization_rules = merge(
local.vpn_authorization_rules_public,
local.vpn_authorization_rules_private,
local.vpn_authorization_rules_intra,
local.vpn_authorization_rules_db,
local.vpn_authorization_rules_elasticache,
local.vpn_authorization_rules_all
)
}
module "vpn" {
source = "fivexl/client-vpn-endpoint/aws"
endpoint_name = "myvpn"
endpoint_client_cidr_block = "10.100.0.0/16"
endpoint_subnets = [module.vpc.intra_subnets[0]] # Attach VPN to single subnet. Reduce cost
endpoint_vpc_id = module.vpc.vpc_id
tls_subject_common_name = "int.example.com"
saml_provider_arn = data.aws_ssm_parameter.iam_vpn_saml_provider_arn.value
authorization_rules = local.vpn_authorization_rules
authorization_rules_all_groups = {}
tags = var.tags
}
Name | Version |
---|---|
terraform | >= 0.15 |
aws | >= 3.33 |
Name | Version |
---|---|
aws | >= 3.33 |
tls | n/a |
No modules.
Name | Type |
---|---|
aws_acm_certificate.this | resource |
aws_cloudwatch_log_group.this | resource |
aws_cloudwatch_log_stream.this | resource |
aws_ec2_client_vpn_authorization_rule.this | resource |
aws_ec2_client_vpn_authorization_rule.this_all_groups | resource |
aws_ec2_client_vpn_authorization_rule.this_sso_to_dns | resource |
aws_ec2_client_vpn_endpoint.this_sso | resource |
aws_ec2_client_vpn_network_association.this_sso | resource |
aws_ec2_client_vpn_route.this_sso | resource |
aws_security_group.this | resource |
tls_private_key.this | resource |
tls_self_signed_cert.this | resource |
aws_vpc.this | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
authorization_rules | Map containing authorization rule configuration. rule_name = "target_network_cidr, access_group_id" . | map(string) |
{} |
no |
authorization_rules_all_groups | Map containing authorization rule configuration with authorize_all_groups=true. rule_name = "target_network_cidr" . | map(string) |
{} |
no |
cloudwatch_log_group_name_prefix | Specifies the name prefix of CloudWatch Log Group for VPC flow logs. | string |
"/aws/client-vpn-endpoint/" |
no |
cloudwatch_log_group_retention_in_days | Specifies the number of days you want to retain log events in the specified log group for VPN connection logs. | number |
30 |
no |
endpoint_client_cidr_block | The IPv4 address range, in CIDR notation, from which to assign client IP addresses. The address range cannot overlap with the local CIDR of the VPC in which the associated subnet is located, or the routes that you add manually. The address range cannot be changed after the Client VPN endpoint has been created. The CIDR block should be /22 or greater. | string |
"10.100.100.0/24" |
no |
endpoint_name | Name to be used on the Client VPN Endpoint | string |
n/a | yes |
endpoint_subnets | List of IDs of endpoint subnets for network association | list(string) |
n/a | yes |
endpoint_vpc_id | VPC where the VPN will be connected. | string |
n/a | yes |
saml_provider_arn | The ARN of the IAM SAML identity provider. | string |
n/a | yes |
tags | A map of tags to add to all resources | map(string) |
{} |
no |
tls_subject_common_name | The common_name for subject for which a certificate is being requested. RFC5280. | string |
n/a | yes |
tls_validity_period_hours | Specifies the number of hours after initial issuing that the certificate will become invalid. | number |
47400 |
no |
Name | Description |
---|---|
security_group_description | n/a |
security_group_id | n/a |
security_group_name | n/a |
security_group_vpc_id | n/a |