Skip to content

Commit

Permalink
Merge pull request #1 from hazelops/feature/initial
Browse files Browse the repository at this point in the history
Terraform AWS Wireguard module
  • Loading branch information
kobrikx authored Jul 30, 2021
2 parents fd0ce73 + 3aeccc0 commit b7a87c7
Show file tree
Hide file tree
Showing 13 changed files with 1,205 additions and 22 deletions.
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Local .terraform directories
**/.terraform/*

# Terraform lockfile
.terraform.lock.hcl

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log

# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore CLI configuration files
.terraformrc
terraform.rc
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

674 changes: 674 additions & 0 deletions LICENSE.md

Large diffs are not rendered by default.

89 changes: 88 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,88 @@
# terraform-aws-wireguard
# Terraform AWS WireGuard

A Terraform module to deploy a WireGuard VPN server on AWS.

### Prerequisites
Before using this module, you'll need to:

1. Install the [WireGuard tools](https://www.wireguard.com/install/) for your OS.

2. Generate a key pair for each client:
```bash
wg genkey | tee client1-privatekey | wg pubkey > client1-publickey
```

3. Generate a key pair for the server
```bash
wg genkey | tee server-privatekey | wg pubkey > server-publickey
```
4. Add each client's public key, along with the next available IP address to the `wg_clients` list.

5. Add server's private key to the `wg_server_private_key` variable. You can use AWS SSM Parameter store to store and read server private key.


<!-- BEGINNING OF GENERATED BY TERRAFORM-DOCS -->

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.5 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | ~> 3.0 |
| <a name="provider_template"></a> [template](#provider\_template) | n/a |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_autoscaling_group.wireguard_asg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) | resource |
| [aws_eip.wireguard](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_iam_instance_profile.wireguard_profile](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_policy.wireguard_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.wireguard_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.wireguard_roleattach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_launch_configuration.wireguard_launch_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_configuration) | resource |
| [aws_route53_record.wireguard](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_security_group.sg_wireguard](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_ami.ubuntu](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.ec2_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.wireguard_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [template_file.wg_client_data_json](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_env"></a> [env](#input\_env) | The name of environment for WireGuard. Used to differentiate multiple deployments. | `any` | n/a | yes |
| <a name="input_instance_type"></a> [instance\_type](#input\_instance\_type) | The machine type to launch, some machines may offer higher throughput for higher use cases. | `string` | `"t3.nano"` | no |
| <a name="input_region"></a> [region](#input\_region) | n/a | `string` | n/a | yes |
| <a name="input_route53_hosted_zone_id"></a> [route53\_hosted\_zone\_id](#input\_route53\_hosted\_zone\_id) | Route53 Hosted zone ID. | `string` | `null` | no |
| <a name="input_route53_record_name"></a> [route53\_record\_name](#input\_route53\_record\_name) | Route53 Record name. | `string` | `null` | no |
| <a name="input_ssh_key_id"></a> [ssh\_key\_id](#input\_ssh\_key\_id) | A SSH public key ID to add to the VPN instance. | `any` | n/a | yes |
| <a name="input_subnet_ids"></a> [subnet\_ids](#input\_subnet\_ids) | A list of subnets for the Autoscaling Group to use for launching instances. May be a single subnet, but it must be an element in a list. | `list(string)` | n/a | yes |
| <a name="input_target_group_arns"></a> [target\_group\_arns](#input\_target\_group\_arns) | Running a scaling group behind an LB requires this variable, default null means it won't be included if not set. | `list(string)` | `null` | no |
| <a name="input_use_eip"></a> [use\_eip](#input\_use\_eip) | Whether to enable Elastic IP switching code in user-data on wg server startup. If true, eip\_id must also be set to the ID of the Elastic IP. | `bool` | `false` | no |
| <a name="input_use_route53"></a> [use\_route53](#input\_use\_route53) | Whether to use Route53 | `bool` | `false` | no |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | The VPC ID in which Terraform will launch the resources. | `any` | n/a | yes |
| <a name="input_wg_clients"></a> [wg\_clients](#input\_wg\_clients) | List of client objects with IP and public key. See Usage in README for details. | `list(object({ client_friendly_name = string, client_public_key = string, client_allowed_cidr = string }))` | n/a | yes |
| <a name="input_wg_persistent_keepalive"></a> [wg\_persistent\_keepalive](#input\_wg\_persistent\_keepalive) | Persistent Keepalive - useful for helping connection stability over NATs. | `number` | `25` | no |
| <a name="input_wg_server_interface"></a> [wg\_server\_interface](#input\_wg\_server\_interface) | The default interface to forward network traffic to. | `string` | `"eth0"` | no |
| <a name="input_wg_server_net"></a> [wg\_server\_net](#input\_wg\_server\_net) | IP range for vpn server - make sure your Client ips are in this range but not the specific ip i.e. not .1 | `string` | `"10.0.0.1/24"` | no |
| <a name="input_wg_server_port"></a> [wg\_server\_port](#input\_wg\_server\_port) | Port for the vpn server. | `number` | `51820` | no |
| <a name="input_wg_server_private_key"></a> [wg\_server\_private\_key](#input\_wg\_server\_private\_key) | WG server private key. | `string` | n/a | yes |

## Outputs

No outputs.
<!-- END OF GENERATED BY TERRAFORM-DOCS -->
29 changes: 29 additions & 0 deletions examples/complete.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module "wireguard" {
source = "hazelops/wireguard/aws"
version = "~>1.0"
env = your-env
region = your-aws-region
use_eip = true
ssh_key_id = "your-ec2-key-id"
instance_type = "t3.nano"
wg_server_net = "10.0.0.1/24"
wg_server_port = 51820
wg_persistent_keepalive = 25
wg_server_interface = "eth0"
use_route53 = false
subnet_ids = ["your-vpc-subnets"]
vpc_id = "id-of-your-vpc"
wg_server_private_key = data.aws_ssm_parameter.wireguard_server_private_key.value
wg_clients = [
{ client_friendly_name = "user1"
client_allowed_cidr = "10.0.0.3/24"
client_public_key = "user1-public-key" },
{ client_friendly_name = "user2"
client_allowed_cidr = "10.0.0.4/24"
client_public_key = "user2-public-key" }
]
}

data "aws_ssm_parameter" "wireguard_server_private_key" {
name = "/mgmt/wg-server-private-key"
}
23 changes: 23 additions & 0 deletions examples/minimal.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module "wireguard" {
source = "hazelops/wireguard/aws"
version = "~>1.0"
env = your-env
region = your-aws-region
use_eip = true
ssh_key_id = "your-ec2-key-id"
subnet_ids = ["vpc-subnets"]
vpc_id = "id-of-your-vpc"
wg_server_private_key = data.aws_ssm_parameter.wireguard_server_private_key.value
wg_clients = [
{ client_friendly_name = "user1"
client_allowed_cidr = "10.0.0.3/24"
client_public_key = "user1-public-key" },
{ client_friendly_name = "user2"
client_allowed_cidr = "10.0.0.4/24"
client_public_key = "user2-public-key" }
]
}

data "aws_ssm_parameter" "wireguard_server_private_key" {
name = "/mgmt/wg-server-private-key"
}
52 changes: 52 additions & 0 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
data "aws_iam_policy_document" "ec2_assume_role" {
statement {
actions = [
"sts:AssumeRole"
]

principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}

data "aws_iam_policy_document" "wireguard_policy_doc" {
statement {
actions = [
"ec2:AssociateAddress",
"ssm:GetParameter"
]

resources = ["*"]
}
}

data "aws_caller_identity" "current" {}

resource "aws_iam_policy" "wireguard_policy" {
name = "${var.env}-${var.region}-tf-wireguard"
description = "Terraform Managed. Allows Wireguard instance to attach EIP."
policy = data.aws_iam_policy_document.wireguard_policy_doc.json
count = (var.use_eip ? 1 : 0) # only used for EIP mode
}

resource "aws_iam_role" "wireguard_role" {
name = "${var.env}-${var.region}-tf-wireguard"
description = "Terraform Managed. Role to allow Wireguard instance to attach EIP."
path = "/"
assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
count = (var.use_eip ? 1 : 0) # only used for EIP mode
}

resource "aws_iam_role_policy_attachment" "wireguard_roleattach" {
role = aws_iam_role.wireguard_role[0].name
policy_arn = aws_iam_policy.wireguard_policy[0].arn
count = (var.use_eip ? 1 : 0) # only used for EIP mode
}

resource "aws_iam_instance_profile" "wireguard_profile" {
name = "${var.env}-${var.region}-tf-wireguard"
role = aws_iam_role.wireguard_role[0].name
count = (var.use_eip ? 1 : 0) # only used for EIP mode
}
104 changes: 104 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
resource "aws_eip" "wireguard" {
vpc = true
tags = {
Name = "${var.env}-wireguard"
}
}

resource "aws_route53_record" "wireguard" {
count = var.use_route53 ? 1 : 0
allow_overwrite = true
set_identifier = "${var.env}-${var.region}-wireguard"
zone_id = var.route53_hosted_zone_id
name = var.route53_record_name
type = "A"
ttl = "60"
records = [aws_eip.wireguard.public_ip]
}

data "template_file" "wg_client_data_json" {
template = file("${path.module}/templates/client-data.tpl")
count = length(var.wg_clients)

vars = {
client_friendly_name = var.wg_clients[count.index].client_friendly_name
client_public_key = var.wg_clients[count.index].client_public_key
client_allowed_cidr = var.wg_clients[count.index].client_allowed_cidr
persistent_keepalive = var.wg_persistent_keepalive
}
}

data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}

resource "aws_launch_configuration" "wireguard_launch_config" {
name_prefix = "${var.env}-wireguard-"
image_id = data.aws_ami.ubuntu.id
instance_type = var.instance_type
key_name = var.ssh_key_id
iam_instance_profile = (var.use_eip ? aws_iam_instance_profile.wireguard_profile[0].name : null)
user_data = templatefile("${path.module}/templates/user-data.txt", {
wg_server_private_key = var.wg_server_private_key,
wg_server_net = var.wg_server_net,
wg_server_port = var.wg_server_port,
peers = join("\n", data.template_file.wg_client_data_json.*.rendered),
use_eip = var.use_eip ? "enabled" : "disabled",
eip_id = aws_eip.wireguard.id,
wg_server_interface = var.wg_server_interface
})
security_groups = [aws_security_group.sg_wireguard.id]
associate_public_ip_address = var.use_eip

lifecycle {
create_before_destroy = true
}
}

resource "aws_autoscaling_group" "wireguard_asg" {
name = aws_launch_configuration.wireguard_launch_config.name
launch_configuration = aws_launch_configuration.wireguard_launch_config.name
min_size = 1
desired_capacity = 1
max_size = 1
vpc_zone_identifier = var.subnet_ids
health_check_type = "EC2"
termination_policies = ["OldestLaunchConfiguration", "OldestInstance"]
target_group_arns = var.target_group_arns

lifecycle {
create_before_destroy = true
}

tags = [
{
key = "Name"
value = aws_launch_configuration.wireguard_launch_config.name
propagate_at_launch = true
},
{
key = "Project"
value = "wireguard"
propagate_at_launch = true
},
{
key = "env"
value = var.env
propagate_at_launch = true
},
{
key = "tf-managed"
value = "True"
propagate_at_launch = true
},
]
}
33 changes: 33 additions & 0 deletions sg.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
resource "aws_security_group" "sg_wireguard" {
name = "${var.env}-${var.region}-wireguard"
description = "Terraform Managed. Allow Wireguard client traffic from internet."
vpc_id = var.vpc_id

tags = {
Name = "${var.env}-${var.region}-wireguard"
Project = "wireguard"
tf-managed = "True"
env = var.env
}

ingress {
from_port = var.wg_server_port
to_port = var.wg_server_port
protocol = "udp"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
5 changes: 5 additions & 0 deletions templates/client-data.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[Peer]
# client_friendly_name = ${client_friendly_name}
PublicKey = ${client_public_key}
AllowedIPs = ${client_allowed_cidr}
PersistentKeepalive = ${persistent_keepalive}
Loading

0 comments on commit b7a87c7

Please sign in to comment.