first pass at a wireguard-as vpn module

ctalarms-whitelist
lalanza808 4 years ago
parent 6cf51ee982
commit d6a0a96841

@ -0,0 +1,7 @@
# WireGuard Access Server
TODO
## Helpful links
https://fabianlee.org/2017/05/21/golang-running-a-go-binary-as-a-systemd-service-on-ubuntu-16-04/

@ -0,0 +1,35 @@
module "vpn_asg" {
source = "terraform-aws-modules/autoscaling/aws"
name = "${var.prefix}-vpn"
image_id = data.aws_ami.ubuntu.image_id
instance_type = var.instance_type
security_groups = [aws_security_group.vpn.id]
iam_instance_profile = aws_iam_instance_profile.vpn.name
asg_name = "${var.prefix}-vpn"
lc_name = "${var.prefix}-vpn"
health_check_type = "EC2"
vpc_zone_identifier = var.public_subnets
min_size = 1
max_size = 1
desired_capacity = 1
wait_for_capacity_timeout = 0
default_cooldown = 120
health_check_grace_period = 120
key_name = var.key_name
user_data = templatefile("${path.module}/files/vpn_user_data.sh", {
EIP_ID = aws_eip.vpn.id
REGION = data.aws_region.current.name
CONFIG_BUCKET = aws_s3_bucket.configs.id
WIREGUARD_INTERFACE = var.wireguard_interface
WIREGUARD_PORT = var.wireguard_vpn_port
})
tags_as_map = {
"Name" = "${var.prefix}-vpn"
"App" = "WireGuard"
"Terraform" = "true"
}
}

@ -0,0 +1,87 @@
#!/bin/bash
set -x
# Elastic IP attachment
INSTANCE_ID=$(curl -s 169.254.169.254/latest/meta-data/instance-id)
aws ec2 associate-address --allocation-id ${EIP_ID} --instance-id $INSTANCE_ID --region ${REGION}
# Install WireGuard and other dependencies
apt-get install -y software-properties-common
add-apt-repository -y ppa:wireguard/wireguard
apt-get update
apt-get install -y "linux-headers-$(uname -r)"
apt-get install -y wireguard iptables resolvconf awscli git sudo
# Initialization WireGuard configs
aws s3api head-object --bucket ${CONFIG_BUCKET} --key wg0.conf
if [[ "$?" -eq 0 ]]; then
echo "[+] Copying existing WireGuard config to system from s3://${CONFIG_BUCKET}"
aws s3 cp s3://${CONFIG_BUCKET}/wg0.conf /etc/wireguard/wg0.conf
else
echo "[+] Generating new WireGuard config"
wg genkey | tee /opt/privkey | wg pubkey > /opt/pubkey
cat << EOF > /etc/wireguard/wg0.conf
[Interface]
Address = ${WIREGUARD_INTERFACE}
ListenPort = ${WIREGUARD_PORT}
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;
PrivateKey = $(cat /opt/privkey)
SaveConfig = true
EOF
aws s3 cp /etc/wireguard/wg0.conf s3://${CONFIG_BUCKET}/wg0.conf
fi
# Install Rust and app as a systemd service
sudo apt install build-essential -y
cat << EOF > /opt/install_app.sh
#!/bin/bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | RUSTUP_HOME=~/.rustup sh -s -- -y
source ~/.cargo/env
git clone https://github.com/lalanza808/wgas-rs ~/wgas-rs
cd ~/wgas-rs
rustup override set nightly
cargo build --release
EOF
chmod +x /opt/install_app.sh
sudo -u ubuntu /opt/install_app.sh
useradd wgas-rs -s /sbin/nologin -M
cat << EOF > /lib/systemd/system/wgas-rs.service
[Unit]
Description=WireGuard Access Server Service
ConditionPathExists=/home/ubuntu/wgas-rs/target/release/wgas-rs
After=network.target
[Service]
Type=simple
User=wgas-rs
Group=wgas-rs
LimitNOFILE=1024
Restart=on-failure
RestartSec=10
startLimitIntervalSec=60
WorkingDirectory=/home/ubuntu/wgas-rs
ExecStart=/home/ubuntu/wgas-rs/target/release/wgas-rs
# make sure log directory exists and owned by syslog
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/log/wgas-rs
ExecStartPre=/bin/chown syslog:adm /var/log/wgas-rs
ExecStartPre=/bin/chmod 755 /var/log/wgas-rs
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=wgas-rs
[Install]
WantedBy=multi-user.target
EOF
chmod 755 /lib/systemd/system/wgas-rs.service
systemctl daemon-reload
systemctl enable wgas-rs
systemctl start wgas-rs

@ -0,0 +1,77 @@
data "aws_iam_policy_document" "vpn_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [
"ec2.amazonaws.com"
]
}
}
}
data "aws_iam_policy_document" "vpn" {
statement {
actions = [
"route53:ListHostedZones",
"route53:GetChange"
]
resources = [
"*"
]
}
statement {
actions = [
"route53:ChangeResourceRecordSets"
]
resources = [
"arn:aws:route53:::hostedzone/${data.aws_route53_zone.domain.zone_id}"
]
}
statement {
actions = [
"ec2:AssociateAddress",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces"
]
resources = [
"*"
]
}
statement {
actions = [
"s3:Put*",
"s3:Get*"
]
resources = [
"${aws_s3_bucket.configs.arn}/*"
]
}
}
resource "aws_iam_policy" "vpn" {
name_prefix = aws_iam_role.vpn.name
description = "WireGuard VPN server policy for managing resources on AWS"
policy = data.aws_iam_policy_document.vpn.json
}
resource "aws_iam_role" "vpn" {
name_prefix = "${var.prefix}-vpn-"
assume_role_policy = data.aws_iam_policy_document.vpn_assume_role.json
}
resource "aws_iam_role_policy_attachment" "vpn" {
role = aws_iam_role.vpn.name
policy_arn = aws_iam_policy.vpn.arn
}
resource "aws_iam_role_policy_attachment" "ssm" {
role = aws_iam_role.vpn.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "vpn" {
name = aws_iam_role.vpn.name
role = aws_iam_role.vpn.name
}

@ -0,0 +1,23 @@
data "aws_route53_zone" "domain" {
name = var.domain_name
}
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}

@ -0,0 +1,63 @@
resource "aws_security_group" "vpn" {
name_prefix = "${var.prefix}-vpn-"
description = "Allow connectivity to and from the WireGuard VPN instance."
vpc_id = var.vpc_id
}
resource "aws_security_group_rule" "vpn_access" {
type = "ingress"
from_port = var.wireguard_vpn_port
to_port = var.wireguard_vpn_port
protocol = "udp"
cidr_blocks = var.vpn_access_cidrs
security_group_id = aws_security_group.vpn.id
}
resource "aws_security_group_rule" "management_access_80" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.management_access_cidrs
security_group_id = aws_security_group.vpn.id
}
resource "aws_security_group_rule" "management_access_8000" {
type = "ingress"
from_port = 8000
to_port = 8000
protocol = "tcp"
cidr_blocks = var.management_access_cidrs
security_group_id = aws_security_group.vpn.id
}
resource "aws_security_group_rule" "management_access_443" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.management_access_cidrs
security_group_id = aws_security_group.vpn.id
}
resource "aws_security_group_rule" "management_access_22" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.management_access_cidrs
security_group_id = aws_security_group.vpn.id
}
resource "aws_security_group_rule" "vpn_egress" {
type = "egress"
from_port = 0
to_port = 65535
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.vpn.id
}
resource "aws_eip" "vpn" {
vpc = true
}

@ -0,0 +1,26 @@
resource "aws_s3_bucket" "configs" {
bucket_prefix = "${var.prefix}-config-"
acl = "private"
}
data "aws_iam_policy_document" "config_policy" {
statement {
actions = [
"s3:*"
]
principals {
type = "AWS"
identifiers = [
aws_iam_role.vpn.arn
]
}
resources = [
"${aws_s3_bucket.configs.arn}/*"
]
}
}
resource "aws_s3_bucket_policy" "configs" {
bucket = aws_s3_bucket.configs.id
policy = data.aws_iam_policy_document.config_policy.json
}

@ -0,0 +1,47 @@
variable "prefix" {
default = "wireguard"
description = "String to use as a prefix when naming resources"
}
variable "domain_name" {
description = "Domain name to use for setting up Route 53 records"
}
variable "instance_type" {
default = "t3.small"
description = "EC2 instance type to provision for VPN server"
}
variable "key_name" {
description = "Key pair to use for provisioning EC2 instance"
}
variable "vpn_access_cidrs" {
default = ["0.0.0.0/0"]
description = "IP addresses which should be able to connect to the VPN"
type = list
}
variable "management_access_cidrs" {
default = []
description = "IP addresses which should be able to reach the administrative interfaces (web/ssh)"
type = list
}
variable "wireguard_vpn_port" {
default = 51820
description = "Port to use for WireGuard VPN (udp)"
}
variable "vpc_id" {
description = "ID of the VPC to deploy network resources into"
}
variable "public_subnets" {
description = "List of subnets for deploying WireGuard VPN servers into"
}
variable "wireguard_interface" {
default = "10.66.66.1/24"
description = "VPN tunnel interface IP and CIDR"
}
Loading…
Cancel
Save