View on GitHub

aws-cfn-vpn

AWS CloudFormation Stack for VPN server.

GitHub tag GitHub GitHub last commit

GitHub issues GitHub pull requests

aws-cfn-vpn

AWS CloudFormation Stack for VPN services.

This Repo use AWS CloudFormation to automate the deployment of Shadowsocks and L2TPD, and is trying to make the deployment as easy as possible.

Additionally, it’s also deploying shadowsocks-manager which is a web-based Shadowsocks management tool for multi-user and traffic statistics, support multi-node, creating DNS records, and syncing IPs to name.com.

stack list

Services List

Features

Shadowsocks-libev:

L2TPD:

Overview

This stack leverages several other repos to achieve the work, below gives an overview of the inside dependency structure. All the internal dependencies will be installed automatically except aws-ec2-ses.

aws-cfn-vpn (github)
├── aws-cfn-vpc (github)
├── aws-cfn-vpc-peer-acceptor (github)
├── aws-cfn-vpc-peer-requester (github)
├── aws-cfn-config-provider (github)
├── aws-cfn-vpn-lexbot (github)
├── aws-cfn-acm (github)
├── shadowsocks-libev-v2ray (dockerhub)
│   ├── shadowsocks-libev (dockerhub)
│   ├── v2ray-plugin (github)
|   └── acme.sh (github)
├── shadowsocks-manager (dockerhub)
│   ├── django (pip)
│   └── [aws-ec2-ses (github)] - Manually setup involved
├── aws-ec2-xl2tpd (github)
│   ├── openswan (yum)
│   └── xl2tpd (yum)
└── chap-manager (github)

Insight

stack.json

This repo contains a standard AWS CloudFormation template stack.json which can be deployed with AWS web console, AWS CLI, or any other AWS CloudFormation compatible tool.

This template will create an AWS CloudFormation stack, including following resources:

sample-*.conf

sample-*.conf are config files used by aws/cfn/vpn/deploy to automate AWS CloudFormation template deployment.

aws/cfn/vpn/deploy can be installed from repo xsh-lib/aws.

Classic Deployment Scenarios

There are 2 classic deployment scenarios:

  1. Deploy a single stack with everything inside, including shadowsocks-manager, Shadowsocks node, and L2TPD. This method is not recommended, the shadowsocks-manager will be unreachable once the node’s network goes wrong. There’s 1 sample config file for this.

    • sample-00-sb.conf
  2. Deploy at least 2 stacks, one for shadowsocks-manager and L2TPD, one or more for Shadowsocks nodes. Each one needs to be deployed in a different AWS account. That allows you to balance network traffic between AWS accounts. There are 3 sample config files for this.

    • sample-0-sb.conf
    • sample-1-sb.conf
    • sample-2-sb.conf

Domain Name Design

There are 3 DNS hostnames needed for your services:

  1. The domain name pointing to shadowsocks-manager service, such as admin.ss.yourdomain.com.

  2. The domain name pointing to L2TPD services, such as vpn.yourdomain.com.

  3. The domain name pointing to Shadowsocks nodes, such as ss.yourdomain.com, or v2ray.ss.yourdomain.com for v2ray plugin enabled nodes.

Deploy

The sample deployment is deploying 3 stacks, one for shadowsocks-manager and L2TPD, two for Shadowsocks nodes.

Prepare at local

Several tools are needed in the deployment, below shows how to get them ready.

  1. awscli: Install it from here.

  2. xsh: xsh is a bash library framework.

     $ git clone https://github.com/alexzhangs/xsh
     $ bash xsh/install.sh
    
  3. xsh-lib/core and xsh-lib/aws

     $ xsh load xsh-lib/core
     $ xsh load xsh-lib/aws
    

Note: If you are proceeding without the tools, then you will have to manually edit config files and upload templates and Lambda functions to S3, and handle the parameters for each nested template, which is most people want to avoid.

Prepare AWS Accounts

  1. Sign up AWS accounts if you don’t have one.

    You will need more than one account if planning to deploy multi-node stacks.

  2. Create an IAM user and give it admin permissions in each AWS account.

    This can be done with AWS CLI if you already have the access key configured for the account:

     $ aws iam create-user --user-name admin
     $ aws iam attach-user-policy --user-name admin --policy-arn "arn:aws:iam::aws:policy/AdministratorAccess"
    

    Otherwise, just use the AWS web console.

    NOTE: You must create an AWS IAM user or role to deploy the stacks, you can not use AWS root user or its access key to do the deployment. Because there is IAM assume role inside the template, which assumes an action ec2:AcceptVpcPeeringConnection and AWS restricts it’s can’t be assumed by the root user.

  3. Create an access key for each IAM user created in the last step.

    This can be done with AWS CLI if you already have the access key configured for the account:

     $ aws iam create-access-key --user-name admin
    

    Otherwise, just use the AWS web console.

  4. Create a profile for each access key created in the last step.

    Following commands will create three profiles with names: vpn-0, vpn-1, and vpn-2 which will be used in the rest of this document.

    A region is needed to be set in this step.

     $ aws configure --profile=vpn-0
     $ aws configure --profile=vpn-1
     $ aws configure --profile=vpn-2
    

Get the code

In the same directory:

$ git clone https://github.com/alexzhangs/aws-cfn-vpn
$ git clone https://github.com/alexzhangs/aws-cfn-vpc
$ git clone https://github.com/alexzhangs/aws-cfn-vpc-peer-acceptor
$ git clone https://github.com/alexzhangs/aws-cfn-vpc-peer-requester
$ git clone https://github.com/alexzhangs/aws-cfn-config-provider
$ git clone https://github.com/alexzhangs/aws-cfn-vpn-lexbot
$ git clone https://github.com/alexzhangs/aws-cfn-acm

Create the manager stack and the node stacks

Simplest Way

The simplest way to create the stacks is to use the high-level wrapper command aws/cfn/vpn/cluster provided by the xsh-lib/aws library.

# Set the environment variables
XSH_AWS_CFN_VPN_ENV=sb  # sb stands for sandbox
XSH_AWS_CFN_VPN_DOMAIN=Example.com  # replace with your domain
XSH_AWS_CFN_VPN_DNS=name.com
XSH_AWS_CFN_VPN_DNS_USERNAME=DomainNameServerUsername  # replace with your name.com API username
XSH_AWS_CFN_VPN_DNS_CREDENTIAL=DomainNameServerCredential  # replace with your name.com API credential
#XSH_AWS_CFN_VPN_PLUGINS=v2ray  # uncomment this if want to enable v2ray-plugin

# Create the config files and deploy the stacks at once
xsh aws/cfn/vpn/cluster -x 0-2 -c vpn -C aws-cfn-vpn

The options listed above is the best practice for the deployment. It minimizes the manual work and the risk of errors, also provides the best security.

However, it requires you own a domain in name.com and have the name.com API enabled.

DomainNameServerUsername and DomainNameServerCredential are the API credentials of name.com. They are used to create and update the DNS records for the domains of the web console, L2TP, and Shadowsocks nodes. The TLS certificate (for web console) provision process also depends on it to be fully automated.

The API credentials can be generated at your name.com API settings.

The command takes around 30 minutes to complete. If everything goes smoothly, you will get 1 manager stack with the L2TPD enabled, and 2 Shadowsocks node stacks with traffic balanced by DNS. You will be able to log in to your manager stack web console with the domain name without any additional setting.

3 config files are created in the directory aws-cfn-vpn along with the deployment:

The Way without API Credentials

# Set the environment variables
XSH_AWS_CFN_VPN_ENV=sb  # sb stands for sandbox
XSH_AWS_CFN_VPN_DOMAIN=Example.com  # replace with your domain

# Create the config files and deploy the stacks at once
xsh aws/cfn/vpn/cluster -x 0-2 -c vpn -C aws-cfn-vpn

If the domain is enabled without API credentials, you need to manually create a DNS record to validate the newly created ACM certificate. Visit AWS ACM service console for the manager stack AWS account, to obtain the DNS record info. Once the ACM certificate is validated successfully, the creation will proceed.

The Way without Domain

# Set the environment variables
XSH_AWS_CFN_VPN_ENV=sb  # sb stands for sandbox

# Create the config files and deploy the stacks at once
xsh aws/cfn/vpn/cluster -x 0-2 -c vpn -C aws-cfn-vpn

# See the help document of the command for the details
xsh help aws/cfn/vpn/cluster

If the domain is not enabled at all, the manager stack web console is not HTTPS secured. Therefore, the user and password of web console are sent in plain text. The L2TPD service and the Shadowsocks nodes are not accessible with a domain name, only with the public IP of the EC2 instance.

Verify the manager stack deployment.

Open your browser, visit http://<PUBLIC_IP>/admin, a login screen should show up.

Or visit https://admin.ss.yourdomain.com/admin. Note that you must use the HTTPS protocol with using the domain, the HTTP protocol won’t work with it.

Log in with the default username and password defined within vpn-0-sb.conf:

"SSMAdminUsername=admin"
"SSMAdminPassword=passw0rd"

Maintain DNS Records

If the DNS service API is enabled, then you can skip the following steps, shadowsocks-manager should have taken care of the DNS records.

If you are not in the case above, proceed with the following steps:

  1. Create a DNS CNAME record, such as admin.ss.yourdomain.com, pointing to the public DNS name of the ELB of the manager stack.

    Use this domain to access the shadowsocks-manager.

  2. Create a DNS A record, such as vpn.yourdomain.com, pointing to the public IP of EC2 Instance of manager stack.

    Use this domain to access the L2TP service.

  3. Create a DNS A record, such as ss.yourdomain.com pointing to the public IP of EC2 Instance of node stack.

    Use this domain to access the Shadowsocks service.

Configure shadowsocks-manager

  1. Log in to the shadowsocks-manager web console at https://admin.ss.yourdomain.com/admin after the DNS records get effective.

  2. Go to Home › Shadowsocks › Shadowsocks Nodes, to check the node list, all node stacks you created should have been registered as nodes automatically.

    Note: The registration relies on the AWS Config, SNS, and Lambda services, it takes up to around 5 minutes to capture and deliver the config changes.

  3. Now you are ready to create Shadowsocks accounts on the web console, or import the previously exported accounts back.

Verify L2TPD services

Use your L2TPD client to connect to the service.

With macOS High Sierra, you can choose the built-in L2TPD client:

Interface: VPN
VPN Type: L2TP over IPSec

The default credential defined within vpn-0-sb.conf is:

"L2TPUsername=vpnuser"
"L2TPPassword=passw0rd"
"L2TPSharedKey=SharedSecret"

v2ray-plugin

V2ray-plugin is optionally supported for the Shadowsocks nodes in Websocket (HTTPS) mode.

This feature is experimental and is disabled by default. It requires several options to be set properly in the node stack config file.

"EnableV2ray=1"
"SSDomain=<v2ray.ss.yourdomain.com>"
"DomainNameServer=name.com"
"DomainNameServerUsername=<YourDomainNameServerUsername>"
"DomainNameServerCredential=<YourDomainNameServerCredential>"

Use below command to deploy v2ray-plugin enabled nodes:

XSH_AWS_CFN_VPN_PLUGINS=v2ray xsh aws/cfn/vpn/cluster -x {0..2} -c vpn -C aws-cfn-vpn

acme.sh is internally used to provision additional TLS certificate for v2ray-plugin automatically. This certificate is used for the domain v2ray.ss.yourdomain.com.

The corresponding client settings are:

plugin: v2ray-plugin
plugin_opts: tls;host=v2ray.ss.yourdomain.com

NOTE: The v2ray-plugin is set on node level, all accounts creating on this node are going to be v2ray enabled.

Customize the Deployment

The deployment can be customized by editing the config files, or their templates at aws-cfn-vpn/config-templates before to generate config files.

Also the deployment can be customized by using the low-level wrapper command aws/cfn/vpn/config and aws/cfn/vpn/deploy provided by the xsh-lib/aws library.

See help document of the commands for the details.

xsh list aws/cfn/vpn
xsh help aws/cfn/vpn/config
xsh help aws/cfn/vpn/deploy

Tips

  1. How to change the IP address of the EC2 instance of the Manager stack or the Node stack?

    Update the stack with a new value of parameter EipDomain, switch the the value between vpc and an empty string ``, this will change the EIP of the EC2 instance.

    DO NOT operate on the EIP directly, such as allocate a new EIP and associate it, then release the old. This will cause an error in locating the original EIP resource when operating on the stack level.

    For the EC2 instance of the Node stacks, the following methods are recommended:

    • Use the admin web console at Home › Shadowsocks › Shadowsocks Nodes.
    • Use the Lex chatbot.
  2. How to enable the HTTPS(SSL certificate) for the Manager stack?

    HTTPS will be enabled by default if you specify a domain for the template parameter SSMDomain.

    The TLS certificate is issued for the domain SSMDomain with AWS ACM service, the service is free, there’s no charge for the certificates.

Development

Re-generate the sample config files

# Unset the environment variables if they are set, otherwise the command will use the values in the environment.
unset XSH_AWS_CFN_VPN_ENV \
    XSH_AWS_CFN_VPN_DOMAIN \
    XSH_AWS_CFN_VPN_DNS \
    XSH_AWS_CFN_VPN_DNS_USERNAME \
    XSH_AWS_CFN_VPN_DNS_CREDENTIAL \
    XSH_AWS_CFN_VPN_PLUGINS

# Generate the sample config file(s): sample-00-sb.conf
xsh aws/cfn/vpn/config -x 00 -p vpn-0 -b sample -e sb

# Generate the sample config file(s): sample-0-sb.conf, sample-1-sb.conf, sample-2-sb.conf
xsh aws/cfn/vpn/config -x 0-2 -p vpn-{0..2} -b sample -e sb

Create the Lambda Layer Packages

  1. requests

     cd lambdas/layers
     mkdir -p python
     pip install requests -t python
     # find and delete all .pyc files and __pycache__ directories
     find python -name '__pycache__' -type d -exec rm -r {} +
     find python -name '*.pyc' -type f -delete
     zip -r9 LambdaLayerRequests.zip python
     rm -rf python
    
  2. tldextract

     cd lambdas/layers
     mkdir -p python
     pip install tldextract -t python
     # find and delete all .pyc files and __pycache__ directories
     find python -name '__pycache__' -type d -exec rm -r {} +
     find python -name '*.pyc' -type f -delete
     zip -r9 LambdaLayerTldExtract.zip python
     rm -rf python
    

TODO

Troubleshooting

  1. The stack ends up at ‘CREATE_FAILED’ status.

    Log in to the AWS web console, go to CloudFormation, check the event list of the stack, found the failed events to locate the root reason, check the event list of the nested stack if necessary.

  2. For any problem related to the repos that aws-cfn-vpn depends on, check with the depended repos, here is the quick dial of star gates.

    1. aws-cfn-vpc
    2. aws-cfn-vpc-peer-acceptor
    3. aws-cfn-vpc-peer-requester
    4. aws-cfn-config-provider
    5. aws-cfn-vpn-lexbot
    6. aws-cfn-acm
    7. aws-ec2-shadowsocks-libev
    8. shadowsocks-libev-v2ray
    9. shadowsocks-manager
    10. aws-ec2-ses
    11. aws-ec2-xl2tpd
    12. chap-manager
  3. Failed to delete the manager stack.

    If VPC peer connections exist in the manager stacks, deleting the stacks will fail.

    Solution:

    1. Delete all the node stacks before deleting the manager stack.

    2. Manually delete all existing peer connections belong to that stack first. This can be done with AWS web console, or the CLI:

        $ aws ec2 describe-vpc-peering-connections
        $ aws ec2 delete-vpc-peering-connection --vpc-peering-connection-id <peering-connection-id>
      
  4. Encountering errors while executing EC2 userdata.

    This might be caused by using the untested AWS AMI. The EC2 userdata is tested only with the following AMIs:

    • Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type
    • Amazon Linux 2 AMI (HVM), SSD Volume Type - This AMI is RECOMMENDED for aws-cfn-vpn

    Feel free to open pull requests for the verified compatible AMIs.