- Create a new CloudFormation template and add the following Mappings. This is a list of all the latest OpenVPN AMIs in each region. We're adding these to maximize region portability for our template—you can omit the regions you have no intention of using:
Mappings:
AWSRegion2AMI: # Latest OpenVPN AMI at time of publishing: 2.1.4
us-east-1:
AMI: ami-bc3566ab
us-east-2:
AMI: ami-10306a75
us-west-2:
AMI: ami-d3e743b3
us-west-1:
AMI: ami-4a02492a
eu-west-1:
AMI: ami-f53d7386
eu-central-1:
AMI: ami-ad1fe6c2
ap-southeast-1:
AMI: ami-a859ffcb
ap-northeast-1:
AMI: ami-e9da7c88
ap-southeast-2:
AMI: ami-89477aea
sa-east-1:
AMI: ami-0c069b60
- We now need to define some Parameters. Firstly we'll need to know which VPC and subnet to deploy our VPN instance to. Note that you need to specify a public subnet here, otherwise you won't be able to access your OpenVPN server:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where load balancer and instance will launch
SubnetId:
Type: List<AWS::EC2::Subnet::Id>
Description: Subnet where OpenVPN server will launch
(pick at least 1)
- We also need to define InstanceType and KeyName. These are the EC2 instance class and SSH key pair to use to launch our OpenVPN server:
InstanceType:
Type: String
Description: OpenVPN server instance type
Default: m3.medium
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: EC2 KeyPair for SSH access
- We need a parameter for AdminPassword. This is the temporary password which will be given to the openvpn user (administrator) when the server starts up:
AdminPassword:
Type: String
Description: Password for 'openvpn' user
Default: openvpn
NoEcho: true
- The last parameter is the CIDR block, which we wish to allow to connect to our VPN server. You may wish to lock this down to the public IP range of your corporate network, for example:
AllowAccessFromCIDR:
Type: String
Description: IP range/address to allow VPN connections from
Default: "0.0.0.0/0"
- The first Resource we need to define is the security group our OpenVPN server will live in. You'll also use this security group to allow access to other resources in your network. Add it to your template as follows:
VPNSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Inbound access to OpenVPN server
VpcId: !Ref VpcId
SecurityGroupIngress:
- CidrIp: !Ref AllowAccessFromCIDR
FromPort: 443
IpProtocol: tcp
ToPort: 443
- CidrIp: !Ref AllowAccessFromCIDR
FromPort: 22
IpProtocol: tcp
ToPort: 22
- CidrIp: !Ref AllowAccessFromCIDR
FromPort: 1194
IpProtocol: udp
ToPort: 1194
- We can now define the actual OpenVPN instance itself. You'll notice that we are explicitly configuring the network interface. This is required, because we want to declare that this instance must get a public IP address (otherwise you won't be able to access it). In the UserData, we declare some variables that the OpenVPN software will pick up when it starts so that it can configure itself:
OpenVPNInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap [ AWSRegion2AMI, !Ref "AWS::Region", AMI ]
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: "0"
GroupSet:
- !Ref VPNSecurityGroup
SubnetId: !Select [ 0, Ref: SubnetId ]
Tags:
- Key: Name
Value: example-openvpn-server
UserData:
Fn::Base64: !Sub
- |
public_hostname=openvpn
admin_user=openvpn
admin_pw=${admin_pw}
reroute_gw=1
reroute_dns=1
- admin_pw: !Ref AdminPassword
- Finally, we add some helpful Outputs:
Outputs:
OpenVPNAdministration:
Value:
Fn::Join:
- ""
- - https://
- !GetAtt OpenVPNInstance.PublicIp
- /admin/
Description: Admin URL for OpenVPN server
OpenVPNClientLogin:
Value:
Fn::Join:
- ""
- - https://
- !GetAtt OpenVPNInstance.PublicIp
- /
Description: Client login URL for OpenVPN server
OpenVPNServerIPAddress:
Value: !GetAtt OpenVPNInstance.PublicIp
Description: IP address for OpenVPN server
- Go ahead and launch this stack in the CloudFormation web console, or via the CLI, with the following command:
aws cloudformation create-stack \
--template-body file://04-securely-access-private-instances.yaml \
--stack-name example-vpn \
--parameters \
ParameterKey=KeyName,ParameterValue=<key-pair-name> \
ParameterKey=VpcId,ParameterValue=<your-vpc-id> \
ParameterKey=SubnetId,ParameterValue=<your-public-subnet-id>