Skip to content

sqlxpert/10-minute-aws-client-vpn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

10-Minute AWS Client VPN

Goals

This CloudFormation template (+ optional Terraform module) helps you set up an AWS-managed VPN in about 10 minutes and operate it for as little as $1.41 per work day!

How this template minimizes costs:

  1. Split-tunneling. Only AWS private network (VPC) traffic uses the VPN.

  2. Single Availability Zone, by default. VPN clients can access VPC resources in any Availability Zone in the same region at no extra charge.

  3. Optional on/off scheduling with github.com/sqlxpert/lights-off-aws .

    Cost savings...
    VPN usage Price (1 hour) Hours (7 days) Hours (365 days) Cost (365 days)
    Always on:
    Endpoint associated 10¢ 168 8,760 $876
    1 client connected 40 2,080 $104
    Total $980
    Work hours only:
    Endpoint associated 10¢ 50 2,607 $261
    1 client connected 40 2,080 $104
    Total $365

    $365 per year divided by 260 work days gives $1.41 per work day.

    AWS Client VPN prices in the us-east-1 region were checked in October, 2025 but can change at any time. NAT gateway, data transfer, and other charges may also apply.

Rationale for connecting to AWS with a VPN...

Experts discourage relying on the strength of the perimeter around your private network, but sometimes, perimeter security is the available defense, and a virtual private network connection is necessary. For example, to access an AWS Elastic File System (EFS) volume from your local computer, you must use a VPN, so that the Network File System (NFS) client connection originates inside your AWS Virtual Private Cloud (VPC). NFS server software was not designed for exposure to the public Internet.

Quick Installation

Before you begin, take a deep breath! Certificate setup is shorter than it looks. To avoid errors, read each step completely before doing it. You will have to switch between this ReadMe file and AWS's documentation.

AWS CloudShell might be useful for setup and maintenance, but be aware of limitations on persistent storage if you use your free CloudShell home directory to store your certificate authority (and Terraform state, if you are using Terraform).

  1. Create the VPN certificate(s) by following AWS's mutual authentication steps.

    • Copy the individual Linux/macOS commands and execute them verbatim.

    • Copy and edit the block of commands before executing those. Not replacing custom_folder is actually fine for now (if only AWS's technical writers had picked a meaningful folder name instead of a placeholder!), but after the mkdir line, please insert:

      chmod go= ~/custom_folder
    • After uploading the first (server) certificate, copy the ARN returned by AWS Certificate Manager.

    • Uploading the second (client) certificate is completely optional.

  2. Tag the VPN certificate(s) if you are using Terraform. If you are not using a separate client certificate, apply both tags to the server certificate.

    aws acm add-tags-to-certificate --tags 'Key=CVpnServer,Value=' --certificate-arn 'SERVER_CERT_ARN'
    aws acm add-tags-to-certificate --tags 'Key=CVpnClientRootChain,Value=' --certificate-arn 'CLIENT_CERT_ARN'
  3. Install the Client VPN CloudFormation stack using CloudFormation or Terraform.

    • CloudFormation
      Easy

      Create a stack "With new resources (standard)" from a locally-saved copy of cloudformation/10-minute-aws-client-vpn.yaml [right-click to save as...].

      • Name the stack CVpn .

      • The parameters are thoroughly documented. Set only the "Essential" ones.

      • Under "Additional settings" → "Stack policy - optional", you can "Upload a file" and select a locally-saved copy of cloudformation/10-minute-aws-client-vpn-policy.json [right-click to save as...]. The stack policy prevents replacement or deletion of certain resources during stack updates, producing an error if you attempt parameter updates that are not possible.

    • Terraform

      Check that you have at least:

      Add the following child module to your existing root module:

      module "cvpn" {
        source = "git::https://github.com/sqlxpert/10-minute-aws-client-vpn.git//terraform?ref=v4.1.1"
          # Reference a specific version from github.com/sqlxpert/10-minute-aws-client-vpn/releases
      
        cvpn_params = {
          TargetSubnetId = "subnet-10123456789abcdef"
        }
      }

      Edit the subnet ID to match the ID of a subnet in the VPN's primary (or sole) Availability Zone.

      Have Terraform download the module's source code. Review the plan before typing yes to allow Terraform to proceed with applying the changes.

      terraform init
      terraform apply

      Turn on the VPN by changing the Enable parameter of the CVpn stack to true in CloudFormation. The Terraform module leaves the VPN off at first and then deliberately ignores changes to cvpn_params["Enable"] so that CloudFormation can manage that parameter.

  4. Follow Step 7 of AWS's Getting Started document.

    • Find your VPN in the list of Client VPN endpoints in the AWS Console and download the configuration file from there.

    • cd to the directory where you downloaded the file and:

      chmod go= downloaded-client-config.ovpn
    • Open the file in your preferred editor, copy the skeleton from AWS's instructions and paste it at the end of the file, then replace the text between the tags with the contents of the ~/custom_folder/client1.domain.tld.crt certificate file and the ~/custom_folder/client1.domain.tld.key key file.

    • Rename ~/custom_folder and note that you must also continue to protect easy-rsa/easyrsa3/pki and downloaded-client-config.ovpn . All three contain copies of your key.

  5. Download either the latest OpenVPN client (Resources → Connect Client → Download) or AWS client.

  6. Import your edited configuration file to the client.

  7. Use the client to connect to the VPN.

  8. Add FromClientSampleSecGrp to an EC2 instance or, if you do not use SSH, create and add a security group that accepts traffic from VPN clients on the port of your choice.

  9. Test. On your local computer, run:

    ssh -i PRIVATE_KEY_FILE ec2-user@IP_ADDRESS

    where PRIVATE_KEY_FILE is the path to the private key for the instance's SSH key pair, and IP_ADDRESS is the private address of the instance.

    Different operating system images have different default usernames; ec2-user is not always correct!

    If you do not use SSH, run a different command to test VPN connectivity.

  10. Remove FromClientSampleSecGrp (or equivalent) from you EC2 instance.

Automatic Scheduling

To turn the VPN on and off on a schedule...
  1. If you used Terraform above, skip to Automatic Scheduling Step 2.

    If you used CloudFormation...

    • Create a stack "With new resources (standard)" from a locally-saved copy of cloudformation/10-minute-aws-client-vpn-prereq.yaml [right-click to save as...].

    • Name this stack CVpnPrereq .

    • Under "Additional settings" → "Stack policy - optional", you can "Upload a file" and select a locally-saved copy of 10-minute-aws-client-vpn-prereq-policy.json [right-click to save as...]. The stack policy prevents inadvertent replacement or deletion of the deployment role during stack updates, but it cannot prevent deletion of the entire CVpnPrereq stack.

    • Update your initial CVpn stack, changing nothing until the "Configure stack options" page, on which you will set "IAM role - optional" to CVpnPrereq-DeploymentRole . You are using a CloudFormation service role to delegate update privileges.

      If your own privileges are limited, you might need explicit permission to pass the role to CloudFormation. See the CVpnPrereq-SampleDeploymentRolePassRolePol IAM policy for an example.

  2. Install Lights Off.

  3. Update your CVpn CloudFormation stack, adding the following stack-level tags:

    • sched-set-Enable-true : u=1 u=2 u=3 u=4 u=5 H:M=11:00
    • sched-set-Enable-false : u=2 u=3 u=4 u=5 u=6 H:M=01:00

    In Terraform, set the following variable inside your module block:

        cvpn_schedule_tags = {
          sched-set-Enable-true  = "u=1 u=2 u=3 u=4 u=5 H:M=11:00"
          sched-set-Enable-false = "u=2 u=3 u=4 u=5 u=6 H:M=01:00"
        }

    Adjust the weekdays and the times based on your work schedule. This example is suitable for the mainland portions of the United States and Canada.

    • u=1 is Monday and u=7 is Sunday, per ISO 8601.
    • Times are in Universal Coordinated Time (UTC). This converter may be helpful: www.timeanddate.com .
    • UTC has no provision for Daylight Saving Time/Summer Time. Leave a buffer at the end of your work day to avoid having to change schedules.
  4. Find your VPN in the list of Client VPN endpoints in the AWS Console and check that its "Target network associations" are being created and deleted as scheduled. Check actual costs after a few days, and set up alerts with AWS Budgets.

Parameter Updates

You can toggle the Enable parameter (always in CloudFormation, never from Terraform).

You can add or remove a backup subnet (in a second Availability Zone), but you must do it while the VPN is enabled, or the change won't register.

You can also switch between generic and custom VPN client security groups.

Do not try to change the VPC, the IP address ranges, or the path parameters after the CVpn stack has been created. Instead, create a CVpn2 stack (in Terraform, create a new module instance with cvpn_stack_name_suffix = "2" ), then update the remote line of your client configuration file and re-import the configuration file to your VPN client utility.

Terraform Details

Terraform Module Outputs

Output Original Resource and Attribute
Matching Data Source and Argument
module.cvpn.cvpn_endpoint_id aws_ec2_client_vpn_endpoint.id
data.aws_ec2_client_vpn_endpoint.client_vpn_endpoint_id
module.cvpn.cvpn_client_sec_grp_id aws_security_group.id
data.aws_security_group.id

To accept traffic from VPN clients, reference module.cvpn.cvpn_client_sec_grp_id in:

of server or listener security groups. ⚠ The security group output is not available if cvpn_params["CustomClientSecGrpIds"] was set.

Creating Certificates in Terraform

To automate certificate creation, consider third-party modules such as:

Terraform Permissions

If you run Terraform with least-privilege permissions...

If you do not give Terraform full AWS administrative permissions, you must give it permission to:

  • List, describe, get tags for, create, tag, update, untag and delete IAM roles, update the "assume role" (role trust or "resource-based") policy, and put and delete in-line policies

  • List, describe, create, tag, update, untag, and delete CloudFormation stacks

  • Set and get CloudFormation stack policies

  • Pass CVpnPrereq-DeploymentRole-* to CloudFormation

  • List, describe, and get tags for, all data sources. For a list, run:

    grep 'data "' terraform*/*.tf | cut --delimiter=' ' --fields='1,2'

Open the AWS Service Authorization Reference, go through the list of services on the left, and consult the "Actions" table for each of:

  • AWS Identity and Access Management (IAM)
  • CloudFormation
  • AWS Security Token Service
  • Amazon EC2
  • AWS Certificate Manager
  • AWS Systems Manager
  • AWS Key Management Service (if you encrypt the CloudWatch log group with a KMS key)

In most cases, you can scope Terraform's permissions to one workload by regulating resource naming and tagging, and then by using:

Check Service and Resource Control Policies (SCPs and RCPs), as well as resource policies (such as KMS key policies).

The deployment role defined in the CVpnPrereq stack gives CloudFormation the permissions it needs to create the CVpn stack. Terraform itself does not need the deployment role's permissions.

Feedback

To help improve the 10-minute AWS Client VPN template, please report bugs and propose changes.

Licenses

Scope Link Included Copy
Source code files, and source code embedded in documentation files GNU General Public License (GPL) 3.0 LICENSE-CODE.md
Documentation files (including this readme file) GNU Free Documentation License (FDL) 1.3 LICENSE-DOC.md

Copyright Paul Marcelin

Contact: marcelin at cmu.edu (replace "at" with @)

About

Set up AWS Client VPN in 10 minutes, run it for as little as $1.41 per work day

Topics

Resources

License

GPL-3.0, GFDL-1.3 licenses found

Licenses found

GPL-3.0
LICENSE-CODE.md
GFDL-1.3
LICENSE-DOC.md

Stars

Watchers

Forks

Contributors

Languages