Deployment
Deploying openvpn-aws
As the name implies, openvpn-aws is intended to be deployed on AWS. To set it up, you’ll use the following services:
- EC2
- ECS
- S3
- CloudFront
- Route53
- CloudWatch
- IAM
- ACM
Note Currently, deploying openvpn-aws requires CloudFront for the UI. An upcoming release will eliminate this requirement.
Create the S3 bucket
First, create an S3 bucket, or select an existing bucket in which you want to store the VPN configuration and UI files. You may choose to store the UI and configuration in separate buckets. You block all public access to the bucket.
Bucket Policy
You’ll need to restrict access to the vpn.conf
file in the bucket, to ensure only authorized users are able to change the
VPN configuration. Below is an example policy.
{
"Id": "Policy1576352535498",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1576352524198",
"Action": [
"s3:DeleteBucket",
"s3:DeleteBucketPolicy",
"s3:DeleteObject",
"s3:PutBucketPolicy",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::example-vpn/conf/vpn.conf",
"arn:aws:s3:::example-vpn"
],
"Condition": {
"StringNotLike": {
"aws:username": ["user1", "user2", "user3"]
}
},
"Principal": "*"
}
]
}
The bucket name used in the rest of this document will be example-vpn
. It is assumed the configuration will be stored in example-vpn/conf/vpn.conf
.
IAM Role
Next, create the task role for the ECS service. This is the role the VPN server will execute as, you will need to grant the following permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::example-vpn/vpn/*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"iam:ListSSHPublicKeys",
"iam:ListGroupsForUser",
"iam:GetSSHPublicKey",
"route53:ChangeResourceRecordSets",
"ec2:DescribeSubnets",
"ec2:DescribeRouteTables",
"iam:GetGroup"
],
"Resource": "*"
}
]
}
ECS Task Definition
Below is an example ECS task definition in JSON. The most important points are:
- Task Role: the role you created above
- Port mappings: forward port 1194 UDP
- Command:
--s3, s3://example-vpn/conf, --loglevel, info
- Image:
amadigan/openvpn-aws
- Auto-configure CloudWatch Logs
"linuxParameters": {"capabilities": ["NET_ADMIN"]},
which can only be added via JSON, or set privileged to true
{
"ipcMode": null,
"executionRoleArn": null,
"containerDefinitions": [
{
"dnsSearchDomains": null,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/vpn-test",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "ecs"
}
},
"entryPoint": null,
"portMappings": [
{
"hostPort": 1194,
"protocol": "udp",
"containerPort": 1194
}
],
"command": [
"--s3",
"s3://example-vpn/conf",
"--loglevel",
"info"
],
"linuxParameters": {"capabilities": ["NET_ADMIN"]},
"cpu": 0,
"environment": null,
"resourceRequirements": null,
"ulimits": null,
"dnsServers": null,
"mountPoints": null,
"workingDirectory": null,
"secrets": null,
"dockerSecurityOptions": null,
"memory": null,
"memoryReservation": null,
"volumesFrom": null,
"stopTimeout": null,
"image": "amadigan/openvpn-aws",
"startTimeout": null,
"firelensConfiguration": null,
"dependsOn": null,
"disableNetworking": null,
"interactive": null,
"healthCheck": null,
"essential": true,
"links": null,
"hostname": null,
"extraHosts": null,
"pseudoTerminal": null,
"user": null,
"readonlyRootFilesystem": null,
"dockerLabels": null,
"systemControls": null,
"privileged": false,
"name": "vpn",
"repositoryCredentials": {
"credentialsParameter": ""
}
}
],
"memory": "256",
"taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/vpn",
"family": "vpn-test",
"pidMode": null,
"requiresCompatibilities": [
"EC2"
],
"networkMode": null,
"cpu": null,
"inferenceAccelerators": [],
"proxyConfiguration": null,
"volumes": [],
"placementConstraints": [],
"tags": []
}
Notes:
- AWS Fargate is not currently supported
- openvpn-aws needs minimal memory, even 64MB should be plenty
VPC Subnet
You will most likely want to create a new subnet for your VPN server. It should have the following properties:
- Connected to an internet gateway, not a NAT
- Auto-assign public IP address
- Explicit routes to a NAT gateway for any public IP address ranges that you want routed over the VPN (
nat
option in vpn.conf) - Explicit routes to any VPC peering connections you want visible over the VPN
Security Group
Create new security group in the EC2 console for your VPN server. You should open UDP port 1194 to the world. Clients
on your VPN will appear to be connecting to other resources in your VPC from your VPN server, so you can reference your vpn
security group from other security groups to control what your users can access on each server.
Write your configuration file
Before you can start your VPN server, you need to write a vpn.conf
. More information can be found on the Configuration page. Once you have written this file, upload it to your S3 bucket as example-vpn/conf/vpn.conf
.
You should enable encryption for this file using either AES256 or KMS. When the server starts, it will generate and upload the
server’s private key, which will inherit the encryption you select for vpn.conf
.
ECS Cluster
AWS can automatically create an ECS cluster. You will most likely want to create a separate cluster (of 1 EC2 instance) for your
VPN. A t3.nano
EC2 instance is more than sufficient. Make sure you select the VPC, subnet, and security groups you used above.
ECS Service
Once you have created your ECS Cluster, create a service to run the EC2 task definition from above. Note that only one instance of openvpn-aws can run on a given EC2 instance (because it needs to bind port 1194). You should either select the DAEMON service type, or select the “One Task Per Host” placement template. You cannot connect openvpn-aws to a load balancer.
Setting up the UI
Once the server starts, it will automatically generate 3 files next to the vpn.conf
in your S3 bucket:
- server.key
- server.crt
- serverca.crt
The VPN will also register itself in Route53, using the zone and name from the route53
option in your configuration file.
The UI requires serverca.crt in order to generate VPN configurations for your users. Note that the only file here that needs
to be kept secret is server.key
.
- Create a new S3 bucket, or a new directory in your existing bucket. This guide will assume you are storing the UI in
example-vpn/ui
. - Download the latest tar of the UI from the releases page
- In
config.json
, edit theremote
property so that it reflects the domain name of your VPN server - Upload the contents of the openvpn-aws directory to
example-vpn/ui
. - Copy the
serverca.crt
file from theconf
directory in S3 to theui
directory.
AWS Certificate Manager
You will need an SSL certificate host the UI. Go to the AWS Certificate Manager, and change your region to us-east-1. Create a
new certificate for the hostname you want to run the UI at. Note that this cannot be the same as the name used by the VPN server.
This guide will assume the name is vpn-ui.example.com
.
CloudFront
Create a new CloudFront distribution which points to your S3 bucket/directory.
- Set the origin to example-vpn.s3.us-west-2.amazonaws.com, be sure to substitute the region that you created the S3 bucket in
- Set the origin path to
/ui
- Enable “Restrict Bucket Access”
- Enable “Grant Read Permissions on Bucket”
- Either select an existing identity or create a new one
- Select the SSL certificate you created in the previous step
- Set the CNAME value to
vpn-ui.example.com
- Set the default root object to index.html
Route53
Once your CloudFront distribution has been created, create a new A record in Route53, aliased to the CloudFront distribution, with
the name vpn-ui.example.com
.
Once the CloudFront distribution finishes deploying (typically about 15 minutes) you should be able to see the UI. This will allow you to generate VPN configurations and register keys in IAM.