Last active
January 10, 2022 17:23
-
-
Save wparad/db1359046989dfaeba9e6aa2216fe9ff to your computer and use it in GitHub Desktop.
Creates an AWS Client VPN with security groups given a VPC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
locals { | |
environment = "dev", | |
vpc = { | |
vpc_id = "VPC_ID", | |
private_subnets = ["SUBNET_ID_1"] | |
} | |
} | |
resource "aws_cloudwatch_log_group" "vpn_cloudwatch_log_group" { | |
name = "/aws/vpn" | |
retention_in_days = "30" | |
tags = { | |
Environment = local.environment | |
Terraform = "true" | |
} | |
} | |
resource "aws_cloudwatch_log_stream" "vpn_cloudwatch_log_stream" { | |
name = "vpn-usage" | |
log_group_name = aws_cloudwatch_log_group.vpn_cloudwatch_log_group.name | |
} | |
resource "tls_private_key" "ca" { | |
algorithm = "RSA" | |
} | |
resource "tls_self_signed_cert" "ca" { | |
key_algorithm = "RSA" | |
private_key_pem = tls_private_key.ca.private_key_pem | |
subject { | |
common_name = "vpn-ca.${local.environment}.rhosys.ch" | |
organization = "Rhosys" | |
} | |
validity_period_hours = 87600 | |
is_ca_certificate = true | |
allowed_uses = [ | |
"cert_signing", | |
"crl_signing", | |
] | |
} | |
resource "tls_private_key" "vpn_server" { | |
algorithm = "RSA" | |
} | |
resource "tls_cert_request" "vpn_server" { | |
key_algorithm = "RSA" | |
private_key_pem = tls_private_key.vpn_server.private_key_pem | |
subject { | |
common_name = "vpn-server.${local.environment}.rhosys.ch" | |
organization = "Rhosys" | |
} | |
} | |
resource "tls_locally_signed_cert" "vpn_server" { | |
cert_request_pem = tls_cert_request.vpn_server.cert_request_pem | |
ca_key_algorithm = "RSA" | |
ca_private_key_pem = tls_private_key.ca.private_key_pem | |
ca_cert_pem = tls_self_signed_cert.ca.cert_pem | |
validity_period_hours = 87600 | |
allowed_uses = [ | |
"key_encipherment", | |
"digital_signature", | |
"server_auth", | |
] | |
} | |
resource "aws_acm_certificate" "vpn_server" { | |
private_key = tls_private_key.vpn_server.private_key_pem | |
certificate_body = tls_locally_signed_cert.vpn_server.cert_pem | |
certificate_chain = tls_self_signed_cert.ca.cert_pem | |
} | |
resource "aws_ec2_client_vpn_endpoint" "developers" { | |
description = "developers-clientvpn" | |
server_certificate_arn = aws_acm_certificate.vpn_server.arn | |
client_cidr_block = "10.254.0.0/16" | |
split_tunnel = true | |
authentication_options { | |
type = "certificate-authentication" | |
root_certificate_chain_arn = aws_acm_certificate.vpn_server.arn | |
} | |
connection_log_options { | |
enabled = true | |
cloudwatch_log_group = aws_cloudwatch_log_group.vpn_cloudwatch_log_group.name | |
cloudwatch_log_stream = aws_cloudwatch_log_stream.vpn_cloudwatch_log_stream.name | |
} | |
tags = { | |
Name = "vpn.${aws_route53_zone.aws_dns.name}" | |
Environment = local.environment | |
} | |
} | |
resource aws_security_group "vpn_client_access" { | |
name = "Developer VPN" | |
description = "Allow developer vpn access to all internal net" | |
vpc_id = local.vpc.vpc_id | |
ingress { | |
from_port = 0 | |
to_port = 0 | |
protocol = "-1" | |
cidr_blocks = ["0.0.0.0/0"] | |
} | |
egress { | |
from_port = 0 | |
to_port = 0 | |
protocol = "-1" | |
cidr_blocks = ["0.0.0.0/0"] | |
} | |
} | |
resource "aws_ec2_client_vpn_network_association" "vpn_linked_subnets" { | |
for_each = toset(local.vpc.private_subnets) | |
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.developers.id | |
subnet_id = each.value | |
security_groups = [ | |
aws_security_group.vpn_client_access.id | |
] | |
lifecycle { | |
ignore_changes = [subnet_id] # This is a hack to fix a bug: https://github.com/terraform-providers/terraform-provider-aws/issues/7597 | |
} | |
} | |
resource "aws_ec2_client_vpn_authorization_rule" "authorize_traffic_from_vpn_to_subnets" { | |
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.developers.id | |
target_network_cidr = aws_ec2_client_vpn_endpoint.developers.client_cidr_block | |
authorize_all_groups = true | |
} | |
# This is exported to secrets manager so that the script `generate-user-certs.js` can grab the certs and pull them down. | |
resource "aws_secretsmanager_secret" "vpn_root_cert_authority" { | |
name = "vpn-client-dev-cert-authority" | |
} | |
resource "aws_secretsmanager_secret_version" "vpn_root_cert_authority" { | |
secret_id = aws_secretsmanager_secret.vpn_root_cert_authority.id | |
secret_string = jsonencode({ | |
cert = tls_self_signed_cert.ca.cert_pem | |
key = tls_private_key.ca.private_key_pem | |
}) | |
} | |
output "vpn_security_group_id" { | |
description = "Security group for the VPN to allow connections by VPN clients" | |
value = aws_security_group.vpn_client_access.id | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
// This file generates a user client certificate and certificate key used for the developer VPN | |
const path = require('path'); | |
const util = require('util'); | |
const exec = util.promisify(require('child_process').exec); | |
const writeFile = util.promisify(require('fs').writeFile); | |
const deleteFile = util.promisify(require('fs').unlink); | |
async function run() { | |
try { | |
let { stdout, stderr } = await exec('aws secretsmanager get-secret-value --secret-id vpn-client-dev-cert-authority --region eu-central-1 --query SecretString'); | |
const secret = JSON.parse(JSON.parse(stdout)); | |
const rootCertFile = path.join(__dirname, 'rootCA.crt'); | |
const rootKeyFile = path.join(__dirname, 'rootCA.key'); | |
await writeFile(rootCertFile, secret.cert); | |
await writeFile(rootKeyFile, secret.key); | |
// Allow generating the ca and ca key in place for testing | |
// await exec(`openssl req -x509 -newkey rsa:2048 -keyout "${rootKeyFile}" -out "${rootCertFile}" -subj "/CN=localhost" -nodes -days 365`); | |
await exec('openssl genrsa -out user-cert.key 2048'); | |
await exec('openssl req -new -sha256 -key user-cert.key -subj "/C=CH/O=Rhosys/CN=`users`@`hostname`" -out certificate-request.csr'); | |
await exec(`openssl x509 -req -in certificate-request.csr -CA "${rootCertFile}" -CAkey "${rootKeyFile}" -set_serial 0x01 -out user-cert.crt -days 3650 -sha256`); | |
await deleteFile(rootCertFile); | |
await deleteFile(rootKeyFile); | |
console.log('Created User key and cert: "user-cert.crt" and "user-cert.key"'); | |
} catch (error) { | |
console.log('Error generating certificates', error); | |
} | |
} | |
run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Might want to also deploy some of these other resources mentioned here: https://aws.amazon.com/blogs/security/authenticate-aws-client-vpn-users-with-aws-single-sign-on/