Browse the Repo


Browse the Repo



Deploy an OpenVPN server. Supports auto healing, public key infrastructure (PKI), cert backup, and managing user accounts using IAM groups.

Code Preview

Preview the Code

mobile file icon


Terraform Version

Open VPN Package Infrastructure Package

This repo contains modules for running a production-ready OpenVPN server and managing OpenVPN user accounts. The modules are:

  • init-openvpn - initializes the public key infrastructure (PKI) via OpenSSL for use by OpenVPN. Designed to be run via user-data on boot
  • install-openvpn - Scripts to install the OpenVPN image in a packer-generated AMI
  • openvpn-admin - A command-line utility to request and revoke certificates and to process those requests
  • openvpn-server - Terraform templates that deploy OpenVPN
  • start-openvpn-admin - Scripts to start openvpn-admin on the openvpn-server in order to process certificate requests and revocations

Note: The OpenVPN tools offer partial Ubuntu 20.04 support, but have not been tested fully for compatibility with other Gruntwork modules. For the release notes, checkout this PR that adds support for Ubuntu 20.04. For issues or contributions, please follow the Contributions section of this README, or simply raise a Github issue.

Architecture Overview


The openvpn-server module will deploy a single server into an Auto Scaling Group (ASG) for redundancy purposes. Should the server fail, a new server will be automatically provisioned by the ASG. Upon initial boot, the init-openvpn module will establish a new public key infrastructure (PKI), including a Certificate Authority (CA), server certificate and a certificate revocation list. These assets will then be backed up
to an S3 bucket and encrypted for protection should a server failure occur.

In a failure scenario, when a replacement server is started by the ASG, the PKI will be automatically restored from the S3 bucket ensuring the previously-issued client certificates will continue to function.

Client Certificate Requests

Users who are members of the proper IAM group will use the openvpn-admin utility to request a new certificate. Behind the scenes, this certificate request will be sent to the server via an SQS queue, will be signed by the server and an OpenVPN client configuration file (.ovpn) with the certificates embedded will be written to disk on the requestor's workstation. This .ovpn file can then be imported into any number of popular OpenVPN clients.

Client Certificate Revocations

Users who are members of the proper IAM group will be allowed to use the same openvpn-admin utility to revoke an existing user's certificate. Behind the scenes, the revocation requests will be sent to the server via an SQS queue, the certificate will be revoked and a confirmation will be sent to back to the requestor's workstation.

Usage with multiple AWS accounts

If your IAM users are defined in one AWS account (e.g., security account) and the OpenVPN server is deployed in another account (e.g., the dev or prod account), then in order for users to be able to request or revoke OpenVPN certificates, they will need access to the SQS queues in the account with the OpenVPN server. When deploying the openvpn-server module, you can specify the ARNs of the AWS account where IAM users are defined using the external_account_arns parameter, and the module will create two IAM roles—one for users and one for admins—that can be assumed by users in those accounts to get access to the SQS queues. See the how to switch between accounts documentation for instructions on assuming IAM roles in other AWS accounts.

Connecting to multiple VPNs

Your VPN users may have problems connecting to multiple VPNs at the same time, unless you follow these best practices:

  1. Make sure that your VPC CIDRs do not overlap.
  2. Make sure that your VPN subnet CIDRs do not overlap. For example, use for Dev, for Prod, and so on.
  3. Make sure that your openvpn-server module's user-data/ includes --search-domain "${search_domain}" in its arguments to init-openvpn, and pass the appropriate search domain in to the user data template object. See the example here for details.
  4. Use Viscosity rather than Tunnelblick. Tunnelblick does not support setting DNS nameservers when connecting to multiple VPNs at the same time, and suffers from a bug that clobbers the state of your internet connection if you ever connect to more than one VPN at a time.

How to use this repo (for learning and testing)?

⚠️ The following steps are recommended only for learning and testing purposes. If you need to setup OpenVPN and use this module for a production environment, or anything other than learning and testing purposes, please refer to our Service Catalog and the Production use section below.

  1. Build the AMI image using Packer
  2. Configure the module & apply using Terraform
  3. Add users & admins to the relevant IAM groups created by the module
  4. Try requesting a certificate
    aws-vault exec <Non-admin-openvpn-user> -- openvpn-admin request --username <Non-admin-openvpn-user> --aws-region <region>
  5. Try using that certficiate to connect to the OpenVPN server and route your local traffic through there
    • you could use Tunnelblick for MacOS based machines, or OpenVPN Connect for Windows-based machines
    • On Tunnelblick, and most OpenVPN clients, you should be able to choose full or split tunnel mode (see what this is here).
    • This module should be configured to use split tunnel mode in favour of directing only the relevant traffic to your OpenVPN Server and hence VPC. This is the default option that might help you save on the costs of running the OpenVPN server.
    • In short, split tunnel mode will enable you to only direct certain traffic to your OpenVPN server, and leave the rest as is through the public Internet, and full tunnel mode means all your traffic is getting routed through the VPN server. If you've opted in for the latter, your machine will show you a different than the usual public IP address. You can Check what your IP is here..
  6. Try revoking a certificate as an OpenVPN admin
    aws-vault exec <Admin-openvpn-user> -- openvpn-admin revoke --username <Non-admin-openvpn-user> --aws-region <region>


See the examples folder for sample code to build the openvpn-admin binary, a packer template to build an AMI and Terraform code to launch everything necessary to run OpenVPN in your AWS environment.

You can follow the openvpn-host or openvpn-host-duo example to deploy a ready-made OpenVPN host into your AWS infrastructure. These examples are best for learning and experimenting on non-production environments.

How to use this repo (for production)?

We recommend that for a production environment you use the example from our Gruntwork AWS Service Catalog: examples/for-production/[...]/networking/openvpn-server.

If you'd prefer to use the module code from the Service catalog instead of the example, please see terraform-aws-service-catalog/.../modules/mgmt/openvpn-server.

What is a module?

At Gruntwork, we've taken the thousands of hours we spent building infrastructure on AWS and condensed all that experience and code into pre-built packages or modules. Each module is a battle-tested, best-practices definition of a piece of infrastructure, such as a VPC, ECS cluster, or an Auto Scaling Group. Modules are versioned using Semantic Versioning to allow Gruntwork clients to keep up to date with the latest infrastructure best practices in a systematic way.

How do you use a module?

Most of our modules contain either:

  1. Terraform code
  2. Scripts & binaries

Using a Terraform Module

To use a module in your Terraform templates, create a module resource and set its source field to the Git URL of this repo. You should also set the ref parameter so you're fixed to a specific version of this repo, as the master branch may have backwards incompatible changes (see module sources).

For example, to use v1.0.0 of the openvpn module, you would add the following:

module "openvpn-server" {
  source = ""

  // set the parameters for the OpenVPN module

Note: the double slash (//) is intentional and required. It's part of Terraform's Git syntax (see module sources).

See the module's documentation and file for all the parameters you can set. Run terraform get -update to pull the latest version of this module from this repo before runnin gthe standard terraform plan and terraform apply commands.

Using scripts & binaries

You can install the scripts and binaries in the modules folder of any repo using the Gruntwork Installer. For example, if the scripts you want to install are in the modules/mongodb-scripts folder of the repo, you could install them as follows:

gruntwork-install --module-name "init-openvpn" --repo "" --tag "0.0.1"

See the docs for each script & binary for detailed instructions on how to use them.

Developing a module


We are following the principles of Semantic Versioning. During initial development, the major version is to 0 (e.g., 0.x.y), which indicates the code does not yet have a stable API. Once we hit 1.0.0, we will follow these rules:

  1. Increment the patch version for backwards-compatible bug fixes (e.g., v1.0.8 -> v1.0.9).
  2. Increment the minor version for new features that are backwards-compatible (e.g., v1.0.8 -> v1.1.0).
  3. Increment the major version for any backwards-incompatible changes (e.g. v1.0.8 -> v2.0.0).

The version is defined using Git tags. Use GitHub to create a release, which will have the effect of adding a git tag.


See the test folder for details.


Adding support for a new OS distribution

Checkout this PR that adds support for Ubuntu 20.04

  1. Update the conditional logic in the init-vpn & install-vpn bin/ scripts to allow the required OS distro. Other modules may also need updating.
  2. Update the packer example to have a configuration for the new OS build
  3. Use Packer to create a new AMI.
  4. Apply some of the example terraform code (e.g. openvpn-host).
  5. SSH into the EC2 instance and see if there's any error logs - e.g. in the user-data log.
  6. Test by requesting a new certificate from the server.


Please see LICENSE.txt for details on how the code in this repo is licensed.


  1. Convert to CIDR format for parameters

Questions? Ask away.

We're here to talk about our services, answer any questions, give advice, or just to chat.

Ready to hand off the Gruntwork?