# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

AWSTemplateFormatVersion: '2010-09-09'

Description: strongSwan VPN Gateway as an EC2 Instance

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: System Classification
      Parameters:
      - pOrg
      - pSystem
      - pApp
    - Label:
        default: System Environment
      Parameters:
      - pEnvPurpose
    - Label:
        default: Authentication Type
      Parameters:
      - pAuthType
    - Label:
        default: Common Parameters for Certificate-Based Authentication
      Parameters:
      - pCertBucket
      - pCgwCert
      - pCgwPrivateKey
      - pCgwPrivateKeyPassphraseSecretName
      - pRootCaCert
      - pSubordinateCaCert
    - Label:
        default: VPN Tunnel 1
      Parameters:
      - pTunnel1PskSecretName
      - pTunnel1VgwCertDomainName
      - pTunnel1VgwOutsideIpAddress
      - pTunnel1CgwInsideIpAddress
      - pTunnel1VgwInsideIpAddress
      - pTunnel1VgwBgpAsn
      - pTunnel1BgpNeighborIpAddress
    - Label:
        default: VPN Tunnel 2
      Parameters:
      - pTunnel2PskSecretName
      - pTunnel2VgwCertDomainName
      - pTunnel2VgwOutsideIpAddress
      - pTunnel2CgwInsideIpAddress
      - pTunnel2VgwInsideIpAddress
      - pTunnel2VgwBgpAsn
      - pTunnel2BgpNeighborIpAddress
    - Label:
        default: Local Network Configuration
      Parameters:
      - pVpcId
      - pVpcCidr
      - pSubnetId
      - pUseElasticIp
      - pEipAllocationId
      - pLocalBgpAsn
    - Label:
        default: EC2  
      Parameters:
      - pAmiId
      - pInstanceType

    ParameterLabels:
      pOrg:
        default: Organization Identifier
      pSystem:
        default: System Identifier
      pApp:
        default: Application Identifier
      pEnvPurpose:
        default: Environment Purpose

      pAuthType:
        default: "Authentication type: 'psk' - Pre-shared key or 'pubkey' - Certificate"
      pCertBucket:
        default: S3 Bucket Containing Certificates and Client Private Key
      pCgwCert:
        default: Customer Gateway Certificate (S3 key)
      pCgwPrivateKey:
        default: Customer Gateway Private Key (S3 key)
      pCgwPrivateKeyPassphraseSecretName:
        default: Name of secret in AWS Secrets Manager for Customer Gateway Private Key Passphrase
      pRootCaCert:
        default: Root CA Certificate (S3 key)
      pSubordinateCaCert:
        default: Subordinate CA Certificate (S3 key)

      pTunnel1PskSecretName:
        default: Name of secret in AWS Secrets Manager for VPN Tunnel 1 Pre-Shared Key
      pTunnel1VgwCertDomainName:
        default: VPN Tunnel 1 Virtual Private Gateway Domain Name - Certificate Authentication. e.g vpn-07..78.endpoint-0
      pTunnel1VgwOutsideIpAddress:
        default: VPN Tunnel 1 Virtual Private Gateway Outside IP Address
      pTunnel1CgwInsideIpAddress:
        default: VPN Tunnel 1 Customer Gateway Inside IP Address
      pTunnel1VgwInsideIpAddress:
        default: VPN Tunnel 1 Virtual Private Gateway Inside IP Address
      pTunnel1VgwBgpAsn:
        default: VPN Tunnel 1 Virtual Private Gateway BGP ASN
      pTunnel1BgpNeighborIpAddress:
        default: VPN Tunnel 1 BGP Neighbor IP Address

      pTunnel2PskSecretName:
        default: Name of secret in AWS Secrets Manager for VPN Tunnel 2 Pre-Shared Key
      pTunnel2VgwCertDomainName:
        default: VPN Tunnel 2 Virtual Private Gateway Domain Name - Certificate Authentication. e.g vpn-07..78.endpoint-1
      pTunnel2VgwOutsideIpAddress:
        default: VPN Tunnel 2 Virtual Private Gateway Outside IP Address
      pTunnel2CgwInsideIpAddress:
        default: VPN Tunnel 2 Customer Gateway Inside IP Address
      pTunnel2VgwInsideIpAddress:
        default: VPN Tunnel 2 Virtual Private Gateway Inside IP Address
      pTunnel2VgwBgpAsn:
        default: VPN Tunnel 2 Virtual Private Gateway BGP ASN
      pTunnel2BgpNeighborIpAddress:
        default: VPN Tunnel 2 BGP Neighbor IP Address

      pUseElasticIp:
        default: Use Elastic IP Address? (true/false)
      pEipAllocationId:
        default: Elastic IP Address Allocation ID
      pLocalBgpAsn:
        default: Local VPN Gateway's BGP ASN
      pVpcId:
        default: VPC ID
      pVpcCidr:
        default: VPC CIDR Block
      pSubnetId:
        default: Subnet ID for VPN Gateway

      pInstanceType:
        default: EC2 Instance Type
      pAmiId:
        default: EC2 AMI ID

Parameters:
  pOrg:
    Type: String
    Description: Used to qualify resource names
    Default: example

  pSystem:
    Type: String
    Description: Used to qualify resource names
    Default: infra

  pApp:
    Type: String
    Description: Used to qualify resource names
    Default: vpngw

  pEnvPurpose:
    Type: String
    Description: Used to qualify resource names. 10 characters max.
    AllowedPattern: '^[a-zA-Z0-9-_]{1,10}$'
    Default: test

  pAuthType:
    Type: String
    Description: "Authentication type: 'psk' - Pre-shared key or 'pubkey' - Certificate"
    Default: psk
    AllowedValues:
      - psk
      - pubkey

  pCertBucket:
    Description: S3 Bucket Containing Certificates and Customer Gateway Private Key. Required for certificate-based authentication.
    Type: String
    Default: ''

  pCgwCert:
    Description: Customer Gateway Certificate (S3 key). Required for certificate-based authentication.
    Type: String
    Default: ''
  
  pCgwPrivateKey:
    Description: Customer Gateway Private Key (S3 key). Required for certificate-based authentication.
    Type: String
    Default: ''

  pCgwPrivateKeyPassphraseSecretName:
    Description: Name of secret in AWS Secrets Manager for Customer Gateway Private Key Passphrase. Required for certificate-based authentication.
    Type: String
    Default: ''

  pRootCaCert:
    Description: Root CA Certificate (S3 key). Required for certificate-based authentication.
    Type: String
    Default: ''

  pSubordinateCaCert:
    Description: Subordinate CA Certificate (S3 key). Required for certificate-based authentication.
    Type: String
    Default: ''

  pTunnel1PskSecretName:
    Description: Name of secret in AWS Secrets Manager for VPN Tunnel 1 Pre-Shared Key. Required for PSK-based authentication.
    Type: String
    Default: ''

  pTunnel1VgwCertDomainName:
    Description: VPN Tunnel 1 Virtual Private Gateway Domain Name. Required for certificate-based authentication. e.g vpn-07..78.endpoint-1
    Type: String
    Default: ''

  pTunnel1VgwOutsideIpAddress:
    Description: VPN Tunnel 1 Virtual Private Gateway Outside IP Address
    Type: String

  pTunnel1CgwInsideIpAddress:
    Description:  VPN Tunnel 1 Customer Gateway Inside IP Address
    Type: String
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|3[0-8]))$ 

  pTunnel1VgwInsideIpAddress:
    Description: VPN Tunnel 1 Virtual Private Gateway Inside IP Address
    Type: String
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|3[0-8]))$ 

  pTunnel1VgwBgpAsn:
    Description: VPN Tunnel 1 Virtual Private Gateway BGP ASN
    Type: Number
    Default: 64512

  pTunnel1BgpNeighborIpAddress:
    Description: VPN Tunnel 1 BGP Neighbor IP Address
    Type: String
  
  pTunnel2PskSecretName:
    Description: Name of secret in AWS Secrets Manager for VPN Tunnel 2 Pre-Shared Key. Required for PSK-based authentication.
    Type: String
    Default: ''

  pTunnel2VgwCertDomainName:
    Description: VPN Tunnel 2 Virtual Private Gateway Domain Name. Required for certificate-based authentication. e.g vpn-07..78.endpoint-1
    Type: String
    Default: ''

  pTunnel2VgwOutsideIpAddress:
    Description: VPN Tunnel 2 Virtual Private Gateway Outside IP Address
    Type: String

  pTunnel2CgwInsideIpAddress:
    Description:  VPN Tunnel 2 Customer Gateway Inside IP Address
    Type: String
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|3[0-8]))$ 

  pTunnel2VgwInsideIpAddress:
    Description: VPN Tunnel 2 Virtual Private Gateway Inside IP Address
    Type: String
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|3[0-8]))$ 
  
  pTunnel2VgwBgpAsn:
    Description: VPN Tunnel 2 Virtual Private Gateway BGP ASN
    Type: Number
    Default: 64512

  pUseElasticIp:
    Type: String
    Description: Whether Elastic IP address is to be used.
    Default: true
    AllowedValues: [true, false]

  pEipAllocationId:
    Description: Elastic IP Address Allocation ID
    Type: String
    Default: ''

  pLocalBgpAsn:
    Description: Local VPN Gateway's BGP ASN
    Type: Number
    Default: 65000

  pTunnel2BgpNeighborIpAddress:
    Description: VPN Tunnel 2 BGP Neighbor IP Address
    Type: String

  pVpcId:
    Description: VPC ID
    Type: AWS::EC2::VPC::Id

  pVpcCidr:
    Description: VPC CIDR Block
    Type: String
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$

  pSubnetId:
    Description: Subnet ID for VPN Gateway
    Type: AWS::EC2::Subnet::Id

  pInstanceType:
    Description: EC2 Instance Type
    Type: String
    Default: t3a.micro
    AllowedValues:
      - t3a.micro
      - t3a.small
      - t3a.medium
    ConstraintDescription: must be a valid EC2 instance type.

  pAmiId:
    Description: EC2 AMI ID
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs'

Rules:
  SubnetsInVPC:
    Assertions:
      - Assert:
          'Fn::EachMemberIn':
            - 'Fn::ValueOfAll':
                - 'AWS::EC2::Subnet::Id'
                - VpcId
            - 'Fn::RefAll': 'AWS::EC2::VPC::Id'
        AssertDescription: All subnets must in the VPC

Conditions:
  cUseCertAuth:  !Equals [ !Ref 'pAuthType', 'pubkey' ]
  cUsePskAuth:   !Equals [ !Ref 'pAuthType', 'psk' ]
  cUseElasticIp: !Equals [ !Ref 'pUseElasticIp', true ]

Resources:
  rInstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${pSystem}-${pApp}-ec2-${pEnvPurpose}'
      VpcId: !Ref pVpcId
      GroupDescription: Allow traffic from other VPN gateway and all locally sourced traffic
      SecurityGroupIngress:
        - IpProtocol: udp
          FromPort: 500
          ToPort: 500
          CidrIp: !Sub '${pTunnel1VgwOutsideIpAddress}/32'
        - IpProtocol: udp
          FromPort: 500
          ToPort: 500
          CidrIp: !Sub '${pTunnel2VgwOutsideIpAddress}/32'
        - IpProtocol: udp
          FromPort: 4500
          ToPort: 4500
          CidrIp: !Sub '${pTunnel1VgwOutsideIpAddress}/32'
        - IpProtocol: udp
          FromPort: 4500
          ToPort: 4500
          CidrIp: !Sub '${pTunnel2VgwOutsideIpAddress}/32'
        - IpProtocol: '50'
          CidrIp: !Sub '${pTunnel1VgwOutsideIpAddress}/32'
        - IpProtocol: '50'
          CidrIp: !Sub '${pTunnel2VgwOutsideIpAddress}/32'
        - IpProtocol: '51'
          CidrIp: !Sub '${pTunnel1VgwOutsideIpAddress}/32'
        - IpProtocol: '51'
          CidrIp: !Sub '${pTunnel2VgwOutsideIpAddress}/32'
        - IpProtocol: '-1'
          FromPort: 0
          ToPort: 65535
          CidrIp: !Ref pVpcCidr

  rLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties: 
      LaunchTemplateName: !Sub '${pSystem}-${pApp}-${pEnvPurpose}'
      LaunchTemplateData: 
        InstanceType: !Ref pInstanceType
        ImageId: !Ref pAmiId
        IamInstanceProfile: 
          Arn: !GetAtt rInstanceProfile.Arn
        NetworkInterfaces:
          - DeviceIndex: 0
            DeleteOnTermination: true
            Description: !Sub '${pSystem}-${pApp}-${pEnvPurpose}'
            Groups:
              - !Ref rInstanceSecurityGroup
            AssociatePublicIpAddress: !Ref pUseElasticIp
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash -x

            yum install -y amazon-cloudwatch-agent

            /opt/aws/bin/cfn-init \
                --verbose \
                --stack ${AWS::StackName} \
                --resource rLaunchTemplate \
                --configsets ${pAuthType} \
                --region ${AWS::Region}

            /opt/aws/bin/cfn-signal \
                --exit-code $? \
                '${rVpnGatewayWaitHandle}'
    Metadata:
      AWS::CloudFormation::Authentication:
        S3BucketAccessCredential:
          type: "S3"
          roleName: !Ref rRole
      AWS::CloudFormation::Init:
        configSets:
          psk:
            - 01-config-cloudwatch-agent
            - 02-restart-cloudwatch-agent
            - 03-install-epel
            - 04-config-vpn-gateway-config
            - 05-config-vpn-gateway-secrets-psk
            - 06-config-vpn-gateway-commands
          pubkey:
            - 01-config-cloudwatch-agent
            - 02-restart-cloudwatch-agent
            - 03-install-epel
            - 04-config-vpn-gateway-config
            - 05-config-vpn-gateway-cert-files
            - 06-config-vpn-gateway-commands
        01-config-cloudwatch-agent:
          files:
            /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
              content: !Sub |
                {
                  "metrics": {
                    "metrics_collected": {
                      "cpu": {
                        "resources": [
                          "*"
                        ],
                        "measurement": [
                          "usage_idle",
                          "usage_nice",
                          "usage_guest"
                        ],
                        "totalcpu": false,
                        "metrics_collection_interval": 10
                      },
                      "mem": {
                        "measurement": [
                          "total",
                          "used",
                          "used_percent"
                        ]
                      },
                      "swap": {
                        "measurement": [
                          "free",
                          "used",
                          "used_percent"
                        ]
                      },
                      "netstat": {
                        "measurement": [
                          "tcp_established",
                          "tcp_syn_sent",
                          "tcp_close"
                        ],
                        "metrics_collection_interval": 60
                      },
                      "disk": {
                        "measurement": [
                          "total",
                          "free",
                          "used",
                          "used_percent"
                        ],
                        "resources": [
                          "*"
                        ],
                        "drop_device": true
                      },  
                      "processes": {
                        "measurement": [
                          "running",
                          "sleeping",
                          "dead"
                        ]
                      }
                    },
                    "append_dimensions": {
                      "ImageId": "${!aws:ImageId}",
                      "InstanceId": "${!aws:InstanceId}",
                      "InstanceType": "${!aws:InstanceType}"
                    },
                    "aggregation_dimensions" : [["InstanceId", "InstanceType"],[]]
                  },
                  "logs": {
                    "logs_collected": {
                      "files": {
                        "collect_list": [
                          {
                            "file_path": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log",
                            "log_group_name": "amazon-cloudwatch-agent.log",
                            "log_stream_name": "amazon-cloudwatch-agent.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/cloud-init.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/cloud-init.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/cloud-init-output.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/cloud-init-output.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/cfn-init.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/cfn-init.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/cfn-wire.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/cfn-wire.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/charon.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/charon.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/quagga/zebra.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/zebra.log",
                            "timezone": "UTC"
                          },
                          {
                            "file_path": "/var/log/quagga/bgpd.log",
                            "log_group_name": "${rCloudWatchLogsAgentGroup}",
                            "log_stream_name": "{instance_id}/bgpd.log",
                            "timezone": "UTC"
                          }
                        ]
                      }
                    },
                    "log_stream_name": "${rCloudWatchLogsAgentGroup}",
                    "force_flush_interval" : 15
                  }
                }
              mode: '000444'
              owner: root
              group: root
        02-restart-cloudwatch-agent:
          commands:
            01-stop-service:
              command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop
            02-start-service:
              command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s
        03-install-epel:
          commands:
            01-install-epel:
              command: amazon-linux-extras install epel -y
        04-config-vpn-gateway-config:
          packages:
            yum:
              strongswan: []
              quagga: []
              jq: []
          files:
            /etc/strongswan/strongswan.conf:
              content: |
                # strongswan.conf - strongSwan configuration file
                #
                # Refer to the strongswan.conf(5) manpage for details
                #
                # Configuration changes should be made in the included files
                charon {
                  plugins {
                    include strongswan.d/charon/*.conf
                  }
                  load_modular = yes
                  filelog {
                    charon {
                      path = /var/log/charon.log
                      time_format = %b %e %T
                      ike_name = yes
                      append = yes
                    }
                  }
                }
              mode: '000600'
              owner: root
              group: root
            /etc/strongswan/ipsec.conf:
              content: !Join
                - ''
                - 
                  - !Sub |
                    conn %default
                      leftauth=${pAuthType}
                      rightauth=${pAuthType}
                      ike=aes256-sha256-modp2048s256,aes128-sha1-modp1024!
                      ikelifetime=28800s
                      aggressive=no
                      esp=aes128-sha256-modp2048s256,aes128-sha1-modp1024!
                      lifetime=3600s
                      type=tunnel
                      dpddelay=10s
                      dpdtimeout=30s
                      keyexchange=ikev1
                      rekey=yes
                      reauth=no
                      dpdaction=restart
                      closeaction=restart
                      left=%defaultroute
                      leftsubnet=0.0.0.0/0,::/0
                      rightsubnet=0.0.0.0/0,::/0
                      leftupdown=/etc/strongswan/ipsec-vti.sh
                      installpolicy=yes
                      compress=no
                      mobike=no

                    conn AWS-VPC-TUNNEL-1
                      left=%any
                      right=${pTunnel1VgwOutsideIpAddress}
                      auto=start
                      mark=100
                  - !If
                    - cUseCertAuth
                    - !Sub |2
                        leftcert=client-public-cert.pem
                        rightid="CN=${pTunnel1VgwCertDomainName}"
                    - ''
                  - !Sub |

                    conn AWS-VPC-TUNNEL-2
                      left=%any
                      right=${pTunnel2VgwOutsideIpAddress}
                      auto=start
                      mark=200
                  - !If
                    - cUseCertAuth
                    - !Sub |2
                        leftcert=client-public-cert.pem
                        rightid="CN=${pTunnel2VgwCertDomainName}"
                    - ''
              mode: '000600'
              owner: root
              group: root
            /etc/strongswan/ipsec-vti.sh:
              content: !Sub |
                #!/bin/bash
                
                #@ /etc/strongswan/ipsec-vti.sh (Centos) or /etc/strongswan.d/ipsec-vti.sh (Ubuntu)
                
                # AWS VPC Hardware VPN Strongswan updown Script
                
                # Usage Instructions:
                # Add "install_routes = no" to /etc/strongswan/strongswan.d/charon.conf or /etc/strongswan.d/charon.conf
                # Add "install_virtual_ip = no" to /etc/strongswan/strongswan.d/charon.conf or /etc/strongswan.d/charon.conf
                # For Ubuntu: Add "leftupdown=/etc/strongswan.d/ipsec-vti.sh" to /etc/ipsec.conf
                # For RHEL/Centos: Add "leftupdown=/etc/strongswan/ipsec-vti.sh" to /etc/strongswan/ipsec.conf
                # For RHEL/Centos 6 and below: git clone git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git && cd iproute2 && make && cp ./ip/ip /usr/local/sbin/ip
                
                # Adjust the below according to the Generic Gateway Configuration file provided to you by AWS.
                # Sample: http://docs.aws.amazon.com/AmazonVPC/latest/NetworkAdminGuide/GenericConfig.html
                
                IP=$(which ip)
                IPTABLES=$(which iptables)
                
                PLUTO_MARK_OUT_ARR=(${!PLUTO_MARK_OUT//// })
                PLUTO_MARK_IN_ARR=(${!PLUTO_MARK_IN//// })
                case "$PLUTO_CONNECTION" in
                  AWS-VPC-TUNNEL-1)
                    VTI_INTERFACE=vti1
                    VTI_LOCALADDR=${pTunnel1CgwInsideIpAddress}
                    VTI_REMOTEADDR=${pTunnel1VgwInsideIpAddress}
                    ;;
                  AWS-VPC-TUNNEL-2)
                    VTI_INTERFACE=vti2
                    VTI_LOCALADDR=${pTunnel2CgwInsideIpAddress}
                    VTI_REMOTEADDR=${pTunnel2VgwInsideIpAddress}
                    ;;
                esac
                
                case "${!PLUTO_VERB}" in
                    up-client)
                        #$IP tunnel add ${!VTI_INTERFACE} mode vti local ${!PLUTO_ME} remote ${!PLUTO_PEER} okey ${!PLUTO_MARK_OUT_ARR[0]} ikey ${!PLUTO_MARK_IN_ARR[0]}
                        $IP link add ${!VTI_INTERFACE} type vti local ${!PLUTO_ME} remote ${!PLUTO_PEER} okey ${!PLUTO_MARK_OUT_ARR[0]} ikey ${!PLUTO_MARK_IN_ARR[0]}
                        sysctl -w net.ipv4.conf.${!VTI_INTERFACE}.disable_policy=1
                        sysctl -w net.ipv4.conf.${!VTI_INTERFACE}.rp_filter=2 || sysctl -w net.ipv4.conf.${!VTI_INTERFACE}.rp_filter=0
                        $IP addr add ${!VTI_LOCALADDR} remote ${!VTI_REMOTEADDR} dev ${!VTI_INTERFACE}
                        $IP link set ${!VTI_INTERFACE} up mtu 1436
                	      $IPTABLES -t mangle -I FORWARD -o ${!VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
                        $IPTABLES -t mangle -I INPUT -p esp -s ${!PLUTO_PEER} -d ${!PLUTO_ME} -j MARK --set-xmark ${!PLUTO_MARK_IN}
                        $IP route flush table 220
                        #/etc/init.d/bgpd reload || /etc/init.d/quagga force-reload bgpd
                        ;;
                    down-client)
                        #$IP tunnel del ${!VTI_INTERFACE}
                        $IP link del ${!VTI_INTERFACE}
                	      $IPTABLES -t mangle -D FORWARD -o ${!VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
                        $IPTABLES -t mangle -D INPUT -p esp -s ${!PLUTO_PEER} -d ${!PLUTO_ME} -j MARK --set-xmark ${!PLUTO_MARK_IN}
                        ;;
                esac
              mode: '000700'
              owner: root
              group: root
            /etc/quagga/zebra.conf:
              content: |
                hostname {HOSTNAME}
                password zebra
                enable password zebra
                !
                log file /var/log/quagga/zebra.log
                !
                ! Configure interfaces
                interface lo
                ! Change preferred source ip address of received routes
                route-map RM_SET_SRC permit 10
                  set src {PRIVATE_IP}
                ip protocol bgp route-map RM_SET_SRC
                !
                line vty
              mode: '000600'
              owner: quagga
              group: quagga
            /etc/quagga/bgpd.conf:
              content: !Sub |
                hostname bgpd
                password zebra
                enable password zebra
                !
                log file /var/log/quagga/bgpd.log
                !
                debug bgp events
                debug bgp filters
                debug bgp fsm
                debug bgp keepalives
                debug bgp updates
                !
                router bgp ${pLocalBgpAsn}
                  bgp router-id {PRIVATE_IP} 
                  network ${pVpcCidr}
                  neighbor ${pTunnel1BgpNeighborIpAddress} remote-as ${pTunnel1VgwBgpAsn}
                  neighbor ${pTunnel2BgpNeighborIpAddress} remote-as ${pTunnel2VgwBgpAsn}
                  neighbor ${pTunnel2BgpNeighborIpAddress} route-map RM_LOWER_PRIORITY out
                !
                route-map RM_LOWER_PRIORITY permit 10
                  set as-path prepend ${pLocalBgpAsn} ${pLocalBgpAsn} ${pLocalBgpAsn}
                !
                line vty
              mode: '000600'
              owner: quagga
              group: quagga
            /etc/sysctl.conf:
              content: |
                # sysctl settings are defined through files in
                # /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
                #
                # Vendors settings live in /usr/lib/sysctl.d/.
                # To override a whole file, create a new file with the same in
                # /etc/sysctl.d/ and put new settings there. To override
                # only specific settings, add a file with a lexically later
                # name in /etc/sysctl.d/ and put new settings there.
                #
                # For more information, see sysctl.conf(5) and sysctl.d(5).
                
                net.ipv4.ip_forward = 1
                net.ipv4.conf.all.send_redirects = 0
                net.ipv4.conf.default.send_redirects = 0
                net.ipv4.tcp_max_syn_backlog = 1280
                net.ipv4.icmp_echo_ignore_broadcasts = 1
                net.ipv4.conf.all.accept_source_route = 0
                net.ipv4.conf.all.accept_redirects = 0
                net.ipv4.conf.all.secure_redirects = 0
                net.ipv4.conf.all.log_martians = 1
                net.ipv4.conf.default.accept_source_route = 0
                net.ipv4.conf.default.accept_redirects = 0
                net.ipv4.conf.default.secure_redirects = 0
                net.ipv4.icmp_echo_ignore_broadcasts = 1
                net.ipv4.icmp_ignore_bogus_error_responses = 1
                net.ipv4.tcp_syncookies = 1
                net.ipv4.conf.all.rp_filter = 1
                net.ipv4.conf.default.rp_filter = 1
                net.ipv4.tcp_mtu_probing = 1
              mode: '000600'
              owner: root
              group: root
        05-config-vpn-gateway-secrets-psk:
          files:
            /etc/strongswan/ipsec.secrets:
              content: !Sub |
                  ${pTunnel1VgwOutsideIpAddress} : PSK "{TUNNEL_1_PSK}"
                  ${pTunnel2VgwOutsideIpAddress} : PSK "{TUNNEL_2_PSK}"
              mode: '000600'
              owner: root
              group: root
            /tmp/set-psk.sh:
              content: !Sub |
                #!/bin/bash

                for (( i=0; i<2; ++i)); do
                  TUNNEL_NUM=$((${!i} + 1))
                  
                  if (( $TUNNEL_NUM == 1 )) ; then
                    SECRET_NAME=${pTunnel1PskSecretName}
                  else
                    SECRET_NAME=${pTunnel2PskSecretName}
                  fi

                  PSK=$(aws secretsmanager get-secret-value --secret-id ${!SECRET_NAME} --region ${AWS::Region} | jq -r '.SecretString' | jq -r '.psk') &&
                  
                  if test -z "$PSK"
                  then
                    echo "\$PSK is empty"
                    exit 1
                  else
                    echo "\$PSK is NOT empty"
                    sed -i -e "s/{TUNNEL_${!TUNNEL_NUM}_PSK}/${!PSK}/" /etc/strongswan/ipsec.secrets
                  fi
                done
              mode: '000700'
              owner: root
              group: root
          commands:
            00-set-psk:
              command: >- 
                /tmp/set-psk.sh
        05-config-vpn-gateway-cert-files:
          files:
            /etc/strongswan/ipsec.d/cacerts/root-ca.pem: 
              source: !Sub "https://${pCertBucket}.s3.${AWS::Region}.amazonaws.com/${pRootCaCert}"
              mode: '000600'
              owner: root 
              group: root
              authentication: "S3BucketAccessCredential"
            /etc/strongswan/ipsec.d/cacerts/subordinate-ca.pem:
              source: !Sub "https://${pCertBucket}.s3.${AWS::Region}.amazonaws.com/${pSubordinateCaCert}"
              mode: '000600' 
              owner: root 
              group: root
              authentication: "S3BucketAccessCredential"
            /etc/strongswan/ipsec.d/certs/client-public-cert.pem:
              source: !Sub "https://${pCertBucket}.s3.${AWS::Region}.amazonaws.com/${pCgwCert}"
              mode: '000600'
              owner: root 
              group: root
              authentication: "S3BucketAccessCredential"
            /etc/strongswan/ipsec.d/private/client-private-key.pem:
              source: !Sub "https://${pCertBucket}.s3.${AWS::Region}.amazonaws.com/${pCgwPrivateKey}"
              mode: '000600' 
              owner: root 
              group: root
              authentication: "S3BucketAccessCredential"
            /etc/strongswan/ipsec.secrets:
              content: |
                  : RSA client-private-key.pem {PASSPHRASE}
              mode: '000600'
              owner: root
              group: root
            /tmp/set-passphrase.sh:
              content: !Sub |
                #!/bin/bash
                
                PASSPHRASE=$(aws secretsmanager get-secret-value --secret-id ${pCgwPrivateKeyPassphraseSecretName} --region ${AWS::Region} | jq -r '.SecretString' | jq -r '.passphrase') &&
                
                if test -z "$PASSPHRASE"
                then
                  echo "\$PASSPHRASE is empty"
                  exit 1
                else
                  echo "\$PASSPHRASE is NOT empty"
                  sed -i -e "s/{PASSPHRASE}/${!PASSPHRASE}/" /etc/strongswan/ipsec.secrets
                fi
              mode: '000700'
              owner: root
              group: root
          commands:
            00-set-private-key-passphrase:
              command: >- 
                /tmp/set-passphrase.sh
        06-config-vpn-gateway-commands:
          commands:
            00-sed-instance-specific-settings:
              command: >- 
                ipaddr=$(curl 169.254.169.254/latest/meta-data/local-ipv4) &&
                sed -i -e "s/{PRIVATE_IP}/${ipaddr}/" /etc/quagga/zebra.conf && 
                sed -i -e "s/{PRIVATE_IP}/${ipaddr}/" /etc/quagga/bgpd.conf && 
                hostname=$(curl 169.254.169.254/latest/meta-data/local-hostname) &&
                sed -i -e "s/{HOSTNAME}/${hostname}/" /etc/quagga/zebra.conf
            01-load-sysctl-changes:
              command: sysctl -p /etc/sysctl.conf
            02-enable-ip-forwarding:
              command: >- 
                sysctl -w net.ipv4.ip_forward=1 && 
                sysctl -w net.ipv4.conf.eth0.disable_xfrm=1 && 
                sysctl -w net.ipv4.conf.eth0.disable_policy=1
            03-enable-start-strongswan:
              command: >- 
                systemctl enable strongswan && 
                systemctl start  strongswan
            04-enable-start-zebra:
              command: >- 
                systemctl enable zebra && 
                systemctl start  zebra
            05-enable-start-bgpd:
              command: >- 
                systemctl enable bgpd && 
                systemctl start  bgpd

  rVpnGatewayEipAssociation:
    Type: AWS::EC2::EIPAssociation
    Condition: cUseElasticIp
    Properties:
      AllocationId: !Ref pEipAllocationId
      InstanceId: !Ref rVpnGateway

  rVpnGateway:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId:
          Ref: rLaunchTemplate
        Version:
          Fn::GetAtt:
            [ rLaunchTemplate, LatestVersionNumber ]
      NetworkInterfaces:
        - DeviceIndex: '0'
          SubnetId: !Ref pSubnetId
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub '${pSystem}-${pApp}-${pEnvPurpose}'

  rVpnGatewayWaitHandle: 
   Type: AWS::CloudFormation::WaitConditionHandle

  rVpnGatewayWaitCondition1: 
    Type: AWS::CloudFormation::WaitCondition
    DependsOn: rVpnGateway
    Properties: 
      Handle: 
        Ref: rVpnGatewayWaitHandle
      Timeout: '600'
      Count: 1

  rRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${pOrg}-${pSystem}-${pApp}-${pEnvPurpose}-${AWS::Region}-svc-cloud-watch-ssm'
      Path: !Sub '/${pOrg}/${pSystem}/${pApp}/'
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

  rPolicyS3:
    Type: AWS::IAM::Policy
    Condition: cUseCertAuth
    Properties:
      PolicyName: !Sub '${pOrg}-${pSystem}-${pApp}-${pEnvPurpose}-s3-read-only'
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: 
              - s3:GetObject
            Resource: !Sub 'arn:aws:s3:::${pCertBucket}/*'
      Roles:
        - !Ref rRole

  rPolicySecretsManagerCertAuth:
    Type: AWS::IAM::Policy
    Condition: cUseCertAuth
    Properties:
      PolicyName: !Sub '${pOrg}-${pSystem}-${pApp}-${pEnvPurpose}-secrets-manager-read-only'
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
              - secretsmanager:DescribeSecret
            Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${pCgwPrivateKeyPassphraseSecretName}-*'
      Roles:
        - !Ref rRole

  rPolicySecretsManagerPskAuth:
    Type: AWS::IAM::Policy
    Condition: cUsePskAuth
    Properties:
      PolicyName: !Sub '${pOrg}-${pSystem}-${pApp}-${pEnvPurpose}-secrets-manager-read-only'
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
              - secretsmanager:DescribeSecret
            Resource: 
              - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${pTunnel1PskSecretName}-*'
              - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${pTunnel2PskSecretName}-*'
      Roles:
        - !Ref rRole

  rInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub '${pSystem}-${pApp}-${pEnvPurpose}-${AWS::Region}'
      Path: !Sub '/${pOrg}/${pSystem}/${pApp}/'
      Roles:
        - !Ref rRole

  rCloudWatchLogsAgentGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/${pSystem}/${pApp}/ec2/${pEnvPurpose}'
      RetentionInDays: 30