Skip to content
Vignesh Rao edited this page Jun 19, 2024 · 17 revisions

VPN Server

vpn-server is a light weight package, that provides a fully automated, and highly scalable solution to create your own on-demand VPN server. This package is portable, and platform independent offering the ability to scale up and scale down instances on the go.

Why vpn-server?

  • You need a VPN but don't want to pay for it?
  • OpenVPN is the solution, but configuring it manually can be a lengthy process.
  • Once configured, keeping the instance up all the time costs $$.
  • Scaling up/down a VPN server on demand can make that lengthy process an absolute nightmare.
  • This module allows you to create your own on demand VPN server with a single call. ETA: ~2 minutes.
  • Done using your own VPN server? Simply delete all the AWS resources with a single call. ETA: ~10 seconds.
  • The solution is fully automated and runs with OpenVPN using AWS EC2.

How it works

  • Creates an AWS EC2 instance using a pre-built OpenVPN AMI.
  • Creates a security group with the necessary ports allowed.
  • Configures the VPN server using SSH.
  • Download the OpenVPN client and connect using the public DNS of the ec2 instance.
  • All set! Now the internet traffic will be routed through the VPN. Verify it using an IP Lookup

To take it a step further, if you have a registered domain in AWS, vpn-server can be accessed with an alias record in route53 pointing to the public IP of the ec2 instance.

  • All the above steps are performed automatically when creating a new VPN server.
  • This module can also be used to clean up all the AWS resources spun up for creating a vpn server.

ENV Variables

Environment variables can be loaded from any env file.

Mandatory

  • VPN_USERNAME - Username to access OpenVPN Connect client.
  • VPN_PASSWORD - Password to access OpenVPN Connect client.

Optional

  • VPN_PORT - Port number for web interfaces. Defaults to 943
  • IMAGE_ID - AMI ID to be used. Defaults to a pre-built AMI from SSM parameter for OpenVPN Access Server AMI Alias
  • INSTANCE_TYPE - Instance type to use for the VPN server. Defaults to t2.micro (minimum memory requirement is 1 GiB)
  • KEY_PAIR - Name of the key pair file to connect to ec2. Defaults to OpenVPN
  • SECURITY_GROUP - Name of the security group. Defaults to OpenVPN Access Server
  • VPN_INFO - Name of the JSON file to dump the server information. Defaults to vpn_info.json
  • HOSTED_ZONE - Domain name for the hosted zone.
  • SUBDOMAIN - Alias record name using which the VPN server has to be accessed.
  • AWS_PROFILE_NAME - AWS profile name. Uses [default] profile.
  • AWS_ACCESS_KEY - AWS Access Key. Defaults to ~/.aws/credentials
  • AWS_SECRET_KEY - AWS Secret Key. Defaults to ~/.aws/credentials
  • AWS_REGION_NAME - AWS Region Name. Defaults to us-east-2

Runtime

By default, vpn-server loads all the environment variables from a .env file and validates using pydantic

Use case 1

To use a custom .env file, set the filename to the env var env_file before importing vpn module.

import os

os.environ['env_file'] = 'custom.env'

import vpn

vpn_server = vpn.VPNServer()
vpn_server.create_vpn_server()

Use case 2

To override your .env file or to get away from environment variables simply use kwargs during instantiation.

import vpn

kwargs = dict(
    vpn_username="myusername",
    vpn_password="Mypassword23$",
    aws_region_name="eu-west-2",
    hosted_zone="myzone.com",
    subdomain="open.vpn",
    instance_type="t2.small",
    key_pair="OpenVPN_sept2623",
    security_group="Open VPN Sept 26 2023",
    vpn_info="vpn_info_sept2623.json"
)

vpn_server = vpn.VPNServer(**kwargs)
vpn_server.create_vpn_server()

From the example above, an output file named vpn_info_sept2623.json will be stored in the current working directory. This can be used to delete the VPN server, that deletes all the AWS resources acquired during creation.

vpn_server.delete_vpn_server()

In the example provided above,

  • open.vpn.myzone.com will be server hostname
  • an instance_type of at least t2.micro is required
  • any string values can be used for the args key_pair, security_group, and vpn_info
    • 💡 use a suffix str(int(time.time())) to avoid overwrites when spinning up multiple VPN servers
  • key_pair and vpn_info will be used as filenames, so use appropriate naming convention

Test

To test an existing VPN server

import vpn

vpn_server = vpn.VPNServer()
vpn_server.test_vpn()

Running a test will confirm three things

  1. GET connection to public IP address, public DNS name and the alias record (if hosted_zone and subdomain are provided)
  2. SSH connectivity on port 22 to the public DNS
  3. Service availability on VPN server

Unknowns

  • vpn-server uses built-in waiter to await instance warmup and SSH connectivity
  • These waiters may potentially trigger a WaiterError (never ran into this during testing)
  • There are exception handlers in place to handle them, so retry if connectivity is lost

Further reading

Refer the runbook for implementation details.