OpenVPNWalkthrough-Part2.yaml
AWSTemplateFormatVersion: '2010-09-09' | |
Description: OpenVPN Stack | |
Parameters: | |
OpenVPNPort: | |
Type: Number | |
Default: 1194 | |
Description: OpenVPN UDP port | |
SSHKeyName: | |
Type: AWS::EC2::KeyPair::KeyName | |
Description: SSH Key for the OpenVPN Instance | |
ConstraintDescription: Must be the name of an existing EC2 KeyPair. | |
ClientIPCIDR: | |
Type: String | |
Default: 0.0.0.0/0 | |
Description: CIDR IP to be granted access by the SG, use 0.0.0.0/0 to accept all IPs | |
Mappings: | |
RegionMap: | |
us-east-1: | |
"AMAZONLINUXAMI" : "ami-8c1be5f6" # Amazon Linux AMI 2017.09 | |
us-east-2: | |
"AMAZONLINUXAMI" : "ami-c5062ba0" # Amazon Linux AMI 2017.09 | |
us-west-1: | |
"AMAZONLINUXAMI" : "ami-02eada62" # Amazon Linux AMI 2017.09 | |
us-west-2: | |
"AMAZONLINUXAMI" : "ami-e689729e" # Amazon Linux AMI 2017.09 | |
ca-central-1: | |
"AMAZONLINUXAMI" : "ami-fd55ec99" # Amazon Linux AMI 2017.09 | |
eu-west-1: | |
"AMAZONLINUXAMI" : "ami-acd005d5" # Amazon Linux AMI 2017.09 | |
eu-central-1: | |
"AMAZONLINUXAMI" : "ami-c7ee5ca8" # Amazon Linux AMI 2017.09 | |
eu-west-2: | |
"AMAZONLINUXAMI" : "ami-1a7f6d7e" # Amazon Linux AMI 2017.09 | |
ap-southeast-1: | |
"AMAZONLINUXAMI" : "ami-0797ea64" # Amazon Linux AMI 2017.09 | |
ap-southeast-2: | |
"AMAZONLINUXAMI" : "ami-8536d6e7" # Amazon Linux AMI 2017.09 | |
ap-northeast-2: | |
"AMAZONLINUXAMI" : "ami-9bec36f5" # Amazon Linux AMI 2017.09 | |
ap-northeast-1: | |
"AMAZONLINUXAMI" : "ami-2a69be4c" # Amazon Linux AMI 2017.09 | |
ap-south-1: | |
"AMAZONLINUXAMI" : "ami-4fc58420" # Amazon Linux AMI 2017.09 | |
sa-east-1: | |
"AMAZONLINUXAMI" : "ami-f1344b9d" # Amazon Linux AMI 2017.09 | |
Resources: | |
# Our VPC, most of our resources will be provisioned within | |
myVPC: | |
Type: AWS::EC2::VPC | |
Properties: | |
CidrBlock: 10.0.0.0/22 # We only need 1 IPaddress for our OpenVPN server, I just like even numbers and 8-bit subnets | |
Tags: | |
- Key: Name | |
Value: personal-OpenVPN-vpc | |
# The only subnet we will create within our VPC, our OpenVPN server will be provisioned within | |
# This subnet will be assigned a default route out to the internet, hence the name. | |
MyPublicSubnet: | |
Type: AWS::EC2::Subnet | |
Properties: | |
VpcId: !Ref myVPC | |
CidrBlock: 10.0.0.0/24 # 8-bit subnet provides 256 addresses, 251 of which are usable | |
Tags: | |
- Key: Name | |
Value: personal-OpenVPN-publicSubnet | |
# We will need our VPC to have access to the internet | |
myInternetGateway: | |
Type: "AWS::EC2::InternetGateway" | |
Properties: | |
Tags: | |
- Key: Name | |
Value: personal-OpenVPN-myIGW | |
# The VPC route table | |
myRouteTablePublic: | |
Type: "AWS::EC2::RouteTable" | |
Properties: | |
VpcId: !Ref myVPC | |
Tags: | |
- Key: Name | |
Value: personal-OpenVPN-myRouteTablePublic | |
# Attach the Internet Gateway to myVPC | |
AttachInternetGateway: | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
VpcId: !Ref myVPC | |
InternetGatewayId: !Ref myInternetGateway | |
# Add a default route to our VPCs internet gateway | |
RouteDefaultPublic: | |
Type: "AWS::EC2::Route" | |
DependsOn: myInternetGateway | |
Properties: | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: !Ref myInternetGateway | |
RouteTableId: !Ref myRouteTablePublic | |
# Associate our route table to our subnet | |
MyPublicSubnetRouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
SubnetId: !Ref MyPublicSubnet | |
RouteTableId: !Ref myRouteTablePublic | |
# Request a new Elastic IP Address | |
myEIP: | |
Type: "AWS::EC2::EIP" | |
Properties: | |
Domain: vpc | |
# Bind our Elastic IP Address to an Elastic Network Interface | |
AssociateManagementAccessPort: | |
Type: AWS::EC2::EIPAssociation | |
Properties: | |
AllocationId: !GetAtt myEIP.AllocationId | |
NetworkInterfaceId: !Ref myNetworkInterface | |
# Create a security group for the ENI that will be attached to our OpenVPN server | |
# OpenVPN and SSH port access | |
OpenVPNInstanceSG: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: SG for OpenVPN Server | |
VpcId: !Ref myVPC | |
SecurityGroupIngress: | |
- IpProtocol: udp | |
FromPort: !Ref OpenVPNPort | |
ToPort: !Ref OpenVPNPort | |
CidrIp: !Ref ClientIPCIDR | |
- IpProtocol: tcp | |
FromPort: 22 | |
ToPort: 22 | |
CidrIp: !Ref ClientIPCIDR | |
# This is the IAM role which will be associated with our EC2 instance | |
myEC2InstanceRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- ec2.amazonaws.com | |
Action: | |
- sts:AssumeRole | |
Path: "/" | |
# This is the IAM policy which will be attached to our EC2 instance role | |
myAccessPolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyName: myAccessPolicy | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- s3:* | |
Effect: Allow | |
Resource: "*" | |
Roles: | |
- !Ref myEC2InstanceRole | |
# Binding profile for our myEC2InstanceRole to the actual EC2 instance | |
ec2InstanceProfile: | |
Type: AWS::IAM::InstanceProfile | |
Properties: | |
Path: "/" | |
Roles: | |
- !Ref myEC2InstanceRole | |
# The Elastic Network Interface which will be attached to our EC2 instance | |
# Our security group, OpenVPNInstanceSG is also associated with this interface | |
myNetworkInterface: | |
Type: AWS::EC2::NetworkInterface | |
Properties: | |
SubnetId: !Ref MyPublicSubnet | |
Description: Public Interface | |
GroupSet: | |
- !Ref OpenVPNInstanceSG | |
SourceDestCheck: false | |
Tags: | |
- | |
Key: Name | |
Value: Public ENI | |
# This is the S3 bucket where our client profile and secrets will be stored | |
myS3Bucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
AccessControl: Private | |
# The EC2 instance which will host OpenVPN | |
EC2OpenVPNInstance: | |
Type: "AWS::EC2::Instance" | |
Properties: | |
ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMAZONLINUXAMI] | |
InstanceType: t2.micro | |
SourceDestCheck: false | |
KeyName: !Ref SSHKeyName | |
NetworkInterfaces: | |
- | |
NetworkInterfaceId: !Ref myNetworkInterface | |
DeviceIndex: 0 | |
IamInstanceProfile: !Ref ec2InstanceProfile | |
Tags: | |
- | |
Key: Name | |
Value: OpenVPN Server | |
# User data is passed into the instance, executed as a shell script, and run only once on first boot | |
# Here we invoke cfn-init on our configSet myCfnConfigSet | |
# The last command emits a cfn-signal to the CloudFormation stack which completes the associated CreationPolicy timer | |
UserData: | |
"Fn::Base64": | |
!Sub | | |
#!/bin/bash | |
yum update -y aws-cfn-bootstrap | |
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2OpenVPNInstance --configsets myCfnConfigSet --region ${AWS::Region} | |
yum -y update | |
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2OpenVPNInstance --region ${AWS::Region} | |
# The CloudFormation stack will wait to mark the EC2OpenVPNInstance as CREATE_COMPLETE until we recieve a signal from the instance, or 10 minutes elapses. | |
CreationPolicy: | |
ResourceSignal: | |
Count: "1" | |
Timeout: PT10M | |
Metadata: | |
AWS::CloudFormation::Init: | |
# Our cfn-init config set rules, divided into logical sections to make reading it easier, hopefully :) | |
configSets: | |
myCfnConfigSet: | |
- "configure_cfn" | |
- "install_software" | |
- "generate_secrets" | |
- "generate_client" | |
- "configure_server" | |
- "upload_files" | |
# Configure and start cfn-hup | |
# cfn-hup will poll the stack for changes, and if possible, apply instance changes in place on the instance | |
configure_cfn: | |
files: | |
/etc/cfn/hooks.d/cfn-auto-reloader.conf: | |
content: !Sub | | |
[cfn-auto-reloader-hook] | |
triggers=post.update | |
path=Resources.EC2OpenVPNInstance.Metadata.AWS::CloudFormation::Init | |
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2OpenVPNInstance --configsets myCfnConfigSet --region ${AWS::Region} | |
mode: "000400" | |
owner: root | |
group: root | |
/etc/cfn/cfn-hup.conf: | |
content: !Sub | | |
[main] | |
stack=${AWS::StackId} | |
region=${AWS::Region} | |
verbose=true | |
interval=1 | |
mode: "000400" | |
owner: root | |
group: root | |
services: | |
sysvinit: | |
cfn-hup: | |
enabled: "true" | |
ensureRunning: "true" | |
files: | |
- "/etc/cfn/cfn-hup.conf" | |
- "/etc/cfn/hooks.d/cfn-auto-reloader.conf" | |
# Install the latest version of openvpn via the yum package manager | |
# Install easy-rsa via the EPEL repo | |
# Make a copy of the installed files to /opt/easy-rsa as our working directory | |
install_software: | |
packages: | |
yum: | |
openvpn: [] | |
commands: | |
01_install_software_install_easyrsa: | |
command: wget -qO- https://github.com/OpenVPN/easy-rsa/releases/download/2.2.2/EasyRSA-2.2.2.tgz | tar xvz -C /opt/ | |
02_install_software_copy_easyrsa: | |
command: cp -R /opt/EasyRSA-2.2.2 /opt/easy-rsa | |
# Use easy-rsa to generate our certificate authority (CA) and encryption keys | |
# I'm not sure if it's possible to source files into the cfn-init environment, so I am just doing it inline with each command | |
# The easy-rsa scripts use an interactive mode flag which is what the sed command is removing | |
# Use openssl to generate a static TLS client cert, this is what the client will use authenticate with the the OpenVPN server | |
generate_secrets: | |
commands: | |
01_generate_secrets_clean_keysdir: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/clean-all" | |
command: "source /opt/easy-rsa/vars;/opt/easy-rsa/clean-all" | |
02_generate_secrets_update_build-ca: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-ca" | |
command: !Sub | | |
sed -i 's/--interact//g' /opt/easy-rsa/build-ca | |
03_generate_secrets_run_build-ca: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-ca" | |
command: "source /opt/easy-rsa/vars;/opt/easy-rsa/build-ca" | |
04_generate_secrets_run_build-dh: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-dh" | |
command: "source /opt/easy-rsa/vars;/opt/easy-rsa/build-dh" | |
05_generate_secrets_update_build-key-server: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-key-server" | |
command: !Sub | | |
sed -i 's/--interact//g' /opt/easy-rsa/build-key-server | |
06_generate_secrets_run_build-key-server: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-key-server" | |
command: "source /opt/easy-rsa/vars;/opt/easy-rsa/build-key-server server" | |
07_generate_secrets_statictlssecret: | |
cwd: "/opt/easy-rsa/keys" | |
command: "openvpn --genkey --secret statictlssecret.key" | |
# Generate the openvpn client configuration files | |
# Generate a script which will concatinate the client configuration with the cert and encryption key to generate the ovpn profile | |
generate_client: | |
files: | |
/opt/easy-rsa/openvpn_client.conf: | |
content: !Sub | | |
client | |
dev tun | |
proto udp | |
remote ${myEIP} ${OpenVPNPort} | |
ca ca.crt | |
cert clientuser.crt | |
key clientuser.key | |
tls-client | |
tls-auth statictlssecret.key 1 | |
tls-version-min 1.2 | |
tls-cipher TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 | |
cipher AES-256-CBC | |
auth SHA512 | |
resolv-retry infinite | |
auth-retry none | |
nobind | |
persist-key | |
persist-tun | |
ns-cert-type server | |
comp-lzo | |
verb 3 | |
mode: "000700" | |
owner: root | |
group: root | |
/opt/easy-rsa/gen_ovpn_profile.sh: | |
content: !Sub | | |
(cat /opt/easy-rsa/openvpn_client.conf | |
echo '<key>' | |
cat keys/clientuser.key | |
echo '</key>' | |
echo '<cert>' | |
cat keys/clientuser.crt | |
echo '</cert>' | |
echo '<ca>' | |
cat keys/ca.crt | |
echo '</ca>' | |
) > /opt/easy-rsa/keys/openvpn_clientuser.ovpn | |
mode: "000700" | |
owner: root | |
group: root | |
commands: | |
01_generate_client_update_build-key: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-key" | |
command: !Sub | | |
sed -i 's/--interact//g' /opt/easy-rsa/build-key | |
02_generate_client_run_build-key: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/build-key" | |
command: "source /opt/easy-rsa/vars;/opt/easy-rsa/build-key clientuser" | |
03_generate_client_generate_ovpn_profile: | |
cwd: "/opt/easy-rsa" | |
test: "test -e /opt/easy-rsa/gen_ovpn_profile.sh" | |
command: "/opt/easy-rsa/gen_ovpn_profile.sh" | |
# Generate configuration file for the OpenVPN server | |
# Enable IP forwarding in Linux | |
# Start OpenVPN | |
configure_server: | |
files: | |
/opt/openvpn/server.conf: | |
content: !Sub | | |
port ${OpenVPNPort} | |
proto udp | |
dev tun | |
server 172.16.0.0 255.255.252.0 | |
push "redirect-gateway def1" | |
ca /opt/easy-rsa/keys/ca.crt | |
cert /opt/easy-rsa/keys/server.crt | |
key /opt/easy-rsa/keys/server.key | |
dh /opt/easy-rsa/keys/dh2048.pem | |
tls-server | |
tls-auth /opt/easy-rsa/keys/statictlssecret.key 0 | |
tls-version-min 1.2 | |
tls-cipher TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 | |
cipher AES-256-CBC | |
auth SHA512 | |
ifconfig-pool-persist ipp.txt | |
keepalive 10 120 | |
ping-timer-rem | |
comp-lzo | |
persist-key | |
persist-tun | |
status openvpn-status.log | |
log-append /var/log/openvpn.log | |
verb 3 | |
max-clients 100 | |
user nobody | |
group nobody | |
mode: "000644" | |
owner: "root" | |
group: "root" | |
commands: | |
01_configure_server_sysctl_ipforward: | |
command: echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf | |
02_configure_server_sysctl_reload: | |
command: "sysctl -p" | |
03_configure_server_iptables_nat: | |
command: "iptables -t nat -A POSTROUTING -s 172.16.0.0/22 -o eth0 -j MASQUERADE" | |
04_configure_server_update_config: | |
command: "cp -rf /opt/openvpn/server.conf /etc/openvpn/server.conf" | |
05_configure_server_openvpn_start: | |
command: "service openvpn start" | |
# Zip the client files | |
# Upload the client file archive and cfn-init log to S3 | |
upload_files: | |
commands: | |
01_upload_files_zipfiles: | |
cwd: "/opt/easy-rsa/keys" | |
command: "zip openVPNClientFiles.zip ca.crt statictlssecret.key clientuser.key clientuser.crt openvpn_clientuser.ovpn" | |
02_upload_files_s3cp_openVPNClientFiles: | |
cwd: "/opt/easy-rsa/keys" | |
command: !Sub | | |
aws s3 cp openVPNClientFiles.zip s3://${myS3Bucket}/client/openVPNClientFiles.zip | |
03_upload_files_s3cp_cfn_init_log: | |
cwd: "/var/log" | |
test: "test -e /var/log/cfn-init.log" | |
command: !Sub | | |
aws s3 cp /var/log/cfn-init.log s3://${myS3Bucket}/log/genSecrets_cfn-init.log | |
Outputs: | |
myS3BucketOut: | |
Description: S3 bucket name | |
Value: !Ref myS3Bucket | |
myEIPOut: | |
Description: Instance EIP | |
Value: !Ref myEIP | |
EC2OpenVPNInstanceOut: | |
Description: EC2 Instance | |
Value: !Ref EC2OpenVPNInstance |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment