setup-ipsec-vpn/aws/cloudformation-template-ips...

742 lines
29 KiB
JSON

{
"Metadata": {
"README": {
"Fn::Join": [
"\n",
[
"",
"AWS Cloudformation Template for deploying IPSec VPN Servers on AWS EC2,",
"based on the work of Lin Song <linsongui@gmail.com> : https://github.com/hwdsl2/setup-ipsec-vpn",
"The latest version of this template can be found at : https://github.com/hwdsl2/setup-ipsec-vpn/aws",
"",
"Copyright (C) 2020-2024 Scott X. L. <wtanglef@pm.me>",
"",
"This work is licensed under the Creative Commons Attribution-ShareAlike 3.0",
"Unported License: http://creativecommons.org/licenses/by-sa/3.0/",
"",
"Attribution required: Please include my name in any derivative and let me",
"know how you have improved it!",
""
]
]
}
},
"AWSTemplateFormatVersion": "2010-09-09",
"Mappings": {
"OS": {
"Ubuntu2004": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\napt-get -yq update\napt-get -yq install python3-pip zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"Ubuntu2204": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\napt-get -yq update\napt-get -yq install python3-pip zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"Ubuntu2404": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\nrm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED\nadd-apt-repository ppa:deadsnakes/ppa\napt-get -yq update\napt-get -yq install python3.11 python3-pip zip\npython3 -m pip install awscli\npython3.11 -m pip install -U pip setuptools\npython3.11 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"Debian10": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\napt-get -yq update\napt-get -yq install python3-pip zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"Debian11": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\napt-get -yq update\napt-get -yq install python3-pip zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"Debian12": {
"HelperInstallationCommands": "export DEBIAN_FRONTEND=noninteractive\nrm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED\napt-get -yq update\napt-get -yq install python3-pip zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"CentOS7": {
"HelperInstallationCommands": "yum -y install python3 wget zip awscli\npython3 -m pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz"
},
"AmazonLinux2": {
"HelperInstallationCommands": "export PATH=\"$PATH:/opt/aws/bin\""
}
}
},
"Resources": {
"IAMInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"InstanceProfileName": {
"Ref": "KeyPair"
},
"Path": "/setup-ipsec-vpn/",
"Roles": [
{
"Ref": "S3ExecutionRole"
}
]
},
"DependsOn": [
"S3ExecutionRole",
"KeyPair"
]
},
"Ikev2S3Bucket": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain",
"Properties": {
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": false,
"BlockPublicPolicy": false,
"IgnorePublicAcls": false,
"RestrictPublicBuckets": false
},
"LifecycleConfiguration": {
"Rules": [
{
"Id": "DeletionAfterOneDay",
"Status": "Enabled",
"ExpirationInDays": 1
}
]
},
"BucketName": {
"Fn::GetAtt": [
"KeyPairDisplayFunctionInfo",
"Combination"
]
}
},
"Metadata": {},
"DependsOn": [
"KeyPair"
]
},
"OpenBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "Ikev2S3Bucket"
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "Ikev2S3Bucket"
},
"/*"
]
]
}
}
]
}
}
},
"VpnVpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/24"
},
"Metadata": {}
},
"VpnSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "VpnVpc"
},
"CidrBlock": "10.0.0.0/24",
"MapPublicIpOnLaunch": true,
"AvailabilityZone": {
"Fn::Select": [
"0",
{
"Fn::GetAZs": ""
}
]
}
},
"Metadata": {},
"DependsOn": [
"VpnVpc",
"VpcInternetGateway"
]
},
"VpnRouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "VpnVpc"
}
},
"Metadata": {},
"DependsOn": [
"VpnSubnet"
]
},
"PublicInternetRoute": {
"Type": "AWS::EC2::Route",
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": {
"Ref": "VpnRouteTable"
},
"GatewayId": {
"Ref": "VpcInternetGateway"
}
},
"Metadata": {},
"DependsOn": [
"VpnRouteTable",
"VpcInternetGateway",
"InternetGatewayAttachment"
]
},
"VpnInstance": {
"Type": "AWS::EC2::Instance",
"CreationPolicy": {
"ResourceSignal": {
"Timeout": "PT15M"
}
},
"Properties": {
"IamInstanceProfile": {
"Ref": "IAMInstanceProfile"
},
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"\n",
[
"#!/bin/bash -xe",
{ "Fn::Sub": "trap 'cfn-signal -e 1 --resource VpnInstance --stack ${AWS::StackName} --region ${AWS::Region}' ERR" },
"sleep 60",
{
"Fn::FindInMap": [
"OS",
{
"Ref": "OS"
},
"HelperInstallationCommands"
]
},
{ "Fn::Sub": "export VPN_IPSEC_PSK='${VpnIpsecPsk}'" },
{ "Fn::Sub": "export VPN_USER='${VpnUser}'" },
{ "Fn::Sub": "export VPN_PASSWORD='${VpnPassword}'" },
"wget -t 3 -T 30 -nv -O vpn.sh https://github.com/hwdsl2/setup-ipsec-vpn/raw/master/vpnsetup.sh",
"sh vpn.sh",
"mkdir /root/profiles",
"cp /root/vpnclient* /root/profiles",
{ "Fn::Sub": "cd /root/ && zip -er --password '${VpnPassword}' profiles.zip ./profiles" },
{ "Fn::Sub": "aws s3 cp /root/profiles.zip s3://${Ikev2S3Bucket}/" },
{ "Fn::Sub": "cfn-signal -e 0 --stack ${AWS::StackName} --resource VpnInstance --region ${AWS::Region}" }
]
]
}
},
"SecurityGroupIds": [
{
"Fn::GetAtt": [
"VpnSecurityGroup",
"GroupId"
]
}
],
"SubnetId": {
"Ref": "VpnSubnet"
},
"AvailabilityZone": {
"Fn::Select": [
"0",
{
"Fn::GetAZs": ""
}
]
},
"InstanceType": {
"Ref": "InstanceType"
},
"KeyName": {
"Ref": "KeyPair"
},
"ImageId": {
"Fn::GetAtt": [
"AMIInfo",
"AMIId"
]
}
},
"Metadata": {},
"DependsOn": [
"VpnRouteTable",
"KeyPair",
"AMIInfoFunction",
"VpnSecurityGroup",
"Ikev2S3Bucket",
"IAMInstanceProfile"
]
},
"KeyPair": {
"Type": "AWS::EC2::KeyPair",
"Properties": {
"KeyName": {
"Fn::Join": [
"-",
[
"setup-ipsec-vpn",
{
"Ref": "AWS::StackName"
}
]
]
}
}
},
"VpnSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "The VPN Security Group, allowing ingress UDP traffic at port 4500 and 500.",
"GroupName": "VpnSecurityGroup",
"VpcId": {
"Ref": "VpnVpc"
},
"SecurityGroupIngress": [
{
"CidrIp": "0.0.0.0/0",
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22
},
{
"CidrIp": "0.0.0.0/0",
"IpProtocol": "udp",
"FromPort": 500,
"ToPort": 500
},
{
"CidrIp": "0.0.0.0/0",
"IpProtocol": "udp",
"FromPort": 4500,
"ToPort": 4500
}
],
"SecurityGroupEgress": [
{
"CidrIp": "0.0.0.0/0",
"IpProtocol": -1
}
]
},
"Metadata": {}
},
"VpcInternetGateway": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {},
"Metadata": {},
"DependsOn": [
"VpnVpc"
]
},
"SubnetRouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": {
"Ref": "VpnRouteTable"
},
"SubnetId": {
"Ref": "VpnSubnet"
}
},
"Metadata": {}
},
"KeyPairDisplayFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Runtime": "python3.12",
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"import boto3",
"import cfnresponse",
"import string",
"import random",
"import traceback",
"'''",
"This python program should be embedded into its designated cloudformation",
"template as the inline code of one of the lambda functions.",
"Its function is to create a random combination of 20 characters for the naming of the Ikev2S3Bucket, and",
"to retrieve the private key material for display under the Outputs tab.",
"'''",
"def handler(event, context):",
" try:",
" if event['RequestType'] == 'Delete':",
" cfnresponse.send(event, context, cfnresponse.SUCCESS, {})",
" elif event['RequestType'] == 'Create':",
" rCombination = 'setup-ipsec-vpn-' + ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(20)).lower()",
" region = event['ResourceProperties']['Region']",
" ssm = boto3.client('ssm',region)",
" response = ssm.get_parameter(",
{
"Fn::Join": [
"",
[
" Name='/ec2/keypair/",
{
"Fn::GetAtt": [
"KeyPair",
"KeyPairId"
]
},
"',"
]
]
},
" WithDecryption=True",
" )",
" keyMaterial = response['Parameter']['Value']",
" cfnresponse.send(event, context, cfnresponse.SUCCESS, {'KeyMaterial':keyMaterial, 'Combination':rCombination}, 'KeyPairDisplayFunctionInfo')",
" except Exception as e:",
" cfnresponse.send(event, context, cfnresponse.FAILED, {'ErrorMsg':traceback.format_exc()})"
]
]
}
},
"Timeout": 30
},
"Metadata": {},
"DependsOn": [
"LambdaExecutionRole",
"KeyPair"
]
},
"KeyPairDisplayFunctionInfo": {
"Type": "Custom::KeyPairDisplayFunctionInfo",
"Properties": {
"Region": {
"Ref": "AWS::Region"
},
"ServiceToken": {
"Fn::GetAtt": [
"KeyPairDisplayFunction",
"Arn"
]
}
},
"Metadata": {},
"DependsOn": [
"KeyPairDisplayFunction",
"KeyPair"
]
},
"AMIInfo": {
"Type": "Custom::AMIInfo",
"Properties": {
"Region": {
"Ref": "AWS::Region"
},
"ServiceToken": {
"Fn::GetAtt": [
"AMIInfoFunction",
"Arn"
]
},
"Distribution": {
"Ref": "OS"
}
},
"Metadata": {},
"DependsOn": [
"AMIInfoFunction"
]
},
"AMIInfoFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Runtime": "python3.12",
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"import boto3",
"import cfnresponse",
"import traceback",
"'''",
"This python script should be embeded into its designated cloudformation template.",
"Its function is to sort out the correct AMI image to use for each of the distribution options available.",
"'''",
"def creation_date(e):",
" return e['CreationDate']",
"",
"def handler(event, context):",
" try:",
" regionName = event['ResourceProperties']['Region']",
" distribution = event['ResourceProperties']['Distribution']",
" ec2 = boto3.client('ec2',regionName)",
" AMIName = {",
" 'Ubuntu2004': 'ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*',",
" 'Ubuntu2204': 'ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*',",
" 'Ubuntu2404': 'ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*',",
" 'Debian10': 'debian-10-amd64-*',",
" 'Debian11': 'debian-11-amd64-*',",
" 'Debian12': 'debian-12-amd64-*',",
" 'CentOS7': 'CentOS Linux 7 x86_64 - *',",
" 'AmazonLinux2': 'amzn2-ami-hvm-*.*-x86_64-gp2',",
" }[distribution]",
" response = ec2.describe_images(Filters=[{'Name':'name', 'Values':[AMIName]}], Owners=['099720109477', '136693071363', '125523088429', 'amazon'])",
" images = response['Images']",
" images.sort(key=creation_date,reverse=True)",
" AMIId = images[0]['ImageId']",
" cfnresponse.send(event, context, cfnresponse.SUCCESS, {'AMIId':AMIId}, 'AMIInfo')",
" except Exception:",
" cfnresponse.send(event, context, cfnresponse.FAILED, {'ErrorMsg':traceback.format_exc()})"
]
]
}
},
"Timeout": 30
},
"Metadata": {},
"DependsOn": [
"LambdaExecutionRole"
]
},
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
}
]
},
"Metadata": {}
},
"S3ExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "s3-bucket-specific-policy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": [
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"Ikev2S3Bucket",
"Arn"
]
},
"/*"
]
]
}
]
}
]
}
}
]
},
"Metadata": {}
},
"InternetGatewayAttachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"InternetGatewayId": {
"Ref": "VpcInternetGateway"
},
"VpcId": {
"Ref": "VpnVpc"
}
},
"Metadata": {}
}
},
"Parameters": {
"VpnUser": {
"Type": "String",
"Description": "Your VPN username"
},
"VpnIpsecPsk": {
"Type": "String",
"Description": "Your VPN IPsec PSK (pre-shared key)"
},
"VpnPassword": {
"Type": "String",
"Description": "Your VPN password"
},
"OS": {
"Type": "String",
"Description": "The OS of your VPN server. Default: Ubuntu 22.04",
"Default": "Ubuntu2204",
"AllowedValues": [
"Ubuntu2404",
"Ubuntu2204",
"Ubuntu2004",
"Debian12",
"Debian11",
"Debian10",
"CentOS7",
"AmazonLinux2"
]
},
"InstanceType": {
"Type": "String",
"Description": "The instance type of your VPN server. Using t2.micro or t3.micro may qualify for the AWS Free Tier.",
"AllowedValues": [
"t2.micro",
"t3.nano",
"m5.large",
"t3.micro",
"t3.small",
"t2.nano",
"t2.small",
"t3a.nano",
"t3a.micro",
"t3a.small",
"m5a.large",
"t1.micro"
],
"Default": "t2.micro"
}
},
"Outputs": {
"1VPNAddress": {
"Description": "This is the public IP of your newly-launched VPN server.",
"Value": {
"Fn::GetAtt": [
"VpnInstance",
"PublicIp"
]
}
},
"2VPNUsername": {
"Description": "Your VPN username",
"Value": {
"Ref": "VpnUser"
}
},
"3VPNPassword": {
"Description": "Your VPN password",
"Value": {
"Ref": "VpnPassword"
}
},
"4VPNKey": {
"Description": "Your VPN IPsec PSK (pre-shared key)",
"Value": {
"Ref": "VpnIpsecPsk"
}
},
"5EC2PrivateKeyId": {
"Description": "The ID of the key pair created. For more information regarding how to retrieve the private key for authentication, please refer to: https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/aws/README.md#faqs",
"Value": {
"Fn::GetAtt": [
"KeyPair",
"KeyPairId"
]
}
},
"6EC2PrivateKeyMaterial": {
"Description": "The content of your private key for accessing the VPN server via SSH. Save it as a file for use when connecting.",
"Value": {
"Fn::GetAtt": [
"KeyPairDisplayFunctionInfo",
"KeyMaterial"
]
}
},
"7NextStep": {
"Description": "Learn how to configure VPN clients.",
"Value": "https://github.com/hwdsl2/setup-ipsec-vpn#next-steps"
},
"8WarningForDebianUsers": {
"Description": "Please be noted that due to Debian images on AWS EC2 using cloud kernels, you are unable to use IPSec/L2TP mode if your server is running Debian. For more information, please refer to the link to the left.",
"Value": "https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#debian-kernel"
},
"9RetrieveYourIkev2Credentials": {
"Description": "Please use the following link to download your IKEv2 connection credentials. The password to the ZIP file that stores the credentials, is the same password used to connect to your VPN server. The download link for the credentials will expire in ONE day.",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Fn::GetAtt": [
"Ikev2S3Bucket",
"RegionalDomainName"
]
},
"/profiles.zip"
]
]
}
}
}
}