Skip to content

Instantly share code, notes, and snippets.

@wparad
Last active January 10, 2022 17:23
Show Gist options
  • Save wparad/db1359046989dfaeba9e6aa2216fe9ff to your computer and use it in GitHub Desktop.
Save wparad/db1359046989dfaeba9e6aa2216fe9ff to your computer and use it in GitHub Desktop.
Creates an AWS Client VPN with security groups given a VPC
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
}
#!/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();
@wparad
Copy link
Author

wparad commented Jan 10, 2022

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment