Cheap custom VPN on AWS EC2 and virtualization
Contents
Introduction
I was wondering lately if hosting my OpenVPN server as an AWS EC2 instance would be
cheaper than the cheap ~6$ permanent hosting solution i was using
since then.
Turns out that yes it seems way cheaper to host it on a small
AWS ARM t4g.nano instance (varying from as low as ~0.46$ a
month to ~2$ a month) with some small work and being careful on the
options. (gp3 storage with very scaled down storage space, no
static IP, no snapshots)
05/08/2023 : From Feb 2024, AWS users with public IPv4
addresses will be charged $0.005 per IP per hour. So the monthly
expense above may have changed, the solution is to perhaps go for
IPV6.
I am generally not a proponent of AWS services for personal
projects but it is well adapted for my use case of a shared multi
regions VPN server with very infrequent access, the instance is
small, cheap and still have more bandwidth / power than the
solution i was using so far... it's also way more flexible with
huge amount of options if scaling is ever needed. It is also easy
to setup as most of the process can be done through the web
UI.
VPN setup
There is nothing much to say on the VPN installation as i use
an automated
setup script, it is trivial to use and provide everything
needed to setup a simple VPN server on Linux with good defaults,
the script also allow to add VPN users. (it will generate a .ovpn
client file)
An AWS EC2 security group must be created for the VPN server
to allow inbound access, my security group allow inbound UDP on
port 1194 and any outbound connections. I also allow ICMP requests
so i am able to check when the instance is up.
The last step was to add some sort of automated script to
provide an updated .ovpn file in case of an IP change. (i don't use
a static IP to reduce cost) A good and easy automated alternative
would be to use a free dynamic DNS service
likes no-ip or just use spare
domains with some API capabilities.
To solve DNS
leak i basically use the same DNS on my client / server. I
don't use IPV6.
Some streaming website detect Amazon instances and block them,
they can do so through reverse DNS
lookup (with a default setup a ping to your instance gives you
something like
ec2-35-178-79-47-eu-west-2.compute.amazonaws.com
), i
am not sure there is solutions for the default dynamic IP given by
AWS but it could be done (with some fees) with a
static Elastic IP.Some website detect Amazon instances through CIDR
IP range, there is no solutions to bypass this as far as i know
?
To reduce the VPN cost further i also added a CloudWatch alarm
which terminate the instance automatically when inbound activity
drop under a threshold for some hours. (instances are started
through the API in my case so i can select which region i want to
be on at any times)
Virtualizing
I first tried to virtualize my existing VPN server so i could
import it as an image (AMI) and
spawn EC2 instances from different regions with the AWS
API.
The process of virtualizing
(aka cloning) a machine is quite simple and can be useful for
backup / archival reasons, you may want to turn any machines and
all their content into a "small" portable file which can be
deployed again later on.
The steps below are done on Ubuntu but most of it should works
on any Linux distributions.
Image creation over SSH
Warning: This step clone the entire machine disk so
probably needs plenty storage space; just make sure you have
enough.
The command below produce a RAW
server.img
file
which can be converted later on to smaller formats.ssh -v -i /your/ssh/private/key -p 20000
root@your.server.ip.address "dd if=/dev/sda" | dd
of=server.img
There is arguments to select the SSH private key (-i ...) and
port (-p ...) but this is not mandatory if the default SSH key /
port is ok for you.
Replace
/dev/sda
by the disk you want to clone;
it can be identified by running fdisk
-l
The
.img
is generally not usable directly (and
may also takes too much disk space as-is) so a conversion step is
needed.Image conversion
This step uses qemu-img to convert the
RAW image file to any of the tool supported formats eg. qcow2,
qcow, cow, vdi, vmdk, vpc, cloop.
sudo apt-get install qemu-utils
qemu-img convert -pO vpc server.img
server.vhd
VHD format will generally be compatible with Amazon EC2 import
utility, the utility also supports OVA, VMDK and RAW format but
some of them may fail due to different options.
Alternative
There is also the VBoxManage command (need Virtual
Box) which can convert between several formats, below is an example
of VMDK to VHD conversion:
VBoxManage clonemedium disk server.vmdk
server.vhd --format VHD
AMI import
This step will import the image as an AMI image,
it require AWS CLI utilities. (install
and
quick-start guide)
Some policy setup may be required on your AWS account to allow
the commands below to work. (can be done on the web UI)
A S3 bucket must be created first but you can also use an
existing one, once created the image file must be uploaded to the
bucket. (can be done easily through the S3 web. UI)
Then a file
containers.json
must be created with
this content (makes sure to replace bucket name and eventually
description / key to match yours):[
{
"Description": "your description",
"Format": "vhd",
"UserBucket": {
"S3Bucket":
"your-s3-bucket",
"S3Key":
"server.vhd"
}
}
]
An AMI (Amazon Image) can then be created with AWS CLI (makes
sure to change the containers.json path and the region to your
preference):
aws ec2 import-image --description "VPN Server"
--disk-containers "file:///local/path/to/containers.json" --region
eu-west-3
The creation is not immediate, the process can be monitored
with a second command (replace import-ami-xx by the id given by the
previous command output) :
aws ec2 describe-import-image-tasks --import-task-ids
import-ami-0bc0651071016d2 --region eu-west-3
And that is it, the image should appear in the AMI
list on the web UI once the import is done, EC2 instances can be
created with this image.
The image can also be copied to other regions through
the web UI for a multi-regions VPN.
Conclusion
In the end i didn't use the virtualized image at all :-)
because the cheapest instances are ARM instances... and my original
server was x86 so i reinstalled the VPN on an ARM Amazon Linux
instance instead and created an image from this instance once done,
the process was faster than virtualizing the original
server.
So now i have a cheap on-demand multi-regions VPN and i am
quite satisfied of AWS for the performance / cost / flexibility
over the hosting solution i was using before.
No leaks ! (DNS are all UK / OpenDNS)
UPDATE: I also did a small web
interface (see below) to switch VPN region at will from
anywhere, this use Pug to render
the page, Node.js with
Express.js and the AWS API, it
probe regions instances to detect which one is up and spawn
an instance from a
launch template when a region is selected, it also terminate
all VPN instances when it switch and setup a
CloudWatch alarm to terminate the instance automatically when
inactive. It is also able to tell if the client IP is connected to
the VPN by using NGINX module ngx_http_sub_module
to replace a specific string by the client IP and client side
JavaScript logic check if it match with the VPN IP.
back to top