---
heat_template_version: 2015-04-30

parameters:
  public_network:
    description: name of the public network to get floating ips from
    type: string
    default: ext-floating1
  key_name:
    type: string
    label: Key Name
    description: Name of key-pair to be used for compute instance
    default: yubikey-taylor
  dmz_flavor:
    type: string
    default:  a1-ram2-disk20-perf1
  nodes_flavor:
    type: string
    default:  a2-ram4-disk50-perf1
  nodes_image:
    type: string
    default:  Debian 11.3 bullseye

resources:
  # random password for Windows VMs
  random_password:
    type: OS::Heat::RandomString
    properties:
      length: 8

  dmz_net:
    type: OS::Neutron::Net
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'dmz']]}
      value_specs:
        mtu: 1500

  dmz_subnet:
    type: OS::Neutron::Subnet
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'dmz']]}
      network_id: {get_resource: dmz_net}
      cidr: "192.168.0.0/24"
      enable_dhcp: true
      allocation_pools: [{"start": 192.168.0.3, "end": 192.168.0.20 }]
      gateway_ip: "192.168.0.1"
      dns_nameservers:
        - "84.16.67.69"
        - "84.16.67.70"
      ip_version: 4

  nodes_net:
    type: OS::Neutron::Net
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'nodes']]}
      value_specs:
        mtu: 1500

  nodes_subnet:
    type: OS::Neutron::Subnet
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'nodes']]}
      network_id: {get_resource: nodes_net}
      cidr: "10.0.0.0/24"
      dns_nameservers:
        - "84.16.67.69"
        - "84.16.67.70"
      ip_version: 4

  dmz_router:
    type: OS::Neutron::Router
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'router']]}
      external_gateway_info:
        network: {get_param: public_network}

  dmz_router_dmz_patch:
    type: OS::Neutron::RouterInterface
    properties:
      router_id: {get_resource: dmz_router}
      subnet: {get_resource: dmz_subnet}

  dmz_router_nodes_patch:
    type: OS::Neutron::RouterInterface
    properties:
      router_id: {get_resource: dmz_router}
      subnet: {get_resource: nodes_subnet}

  dmz_security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      description: Security group for dmz
      name: {list_join: ['-', [{get_param: OS::stack_name}, 'dmz']]}
      rules:
        - protocol: "icmp"
          remote_ip_prefix: 0.0.0.0/0
        - protocol: "tcp"
          remote_ip_prefix: 0.0.0.0/0
          port_range_min: 22
          port_range_max: 22
        - protocol: "esp"
          remote_ip_prefix: 0.0.0.0/0
        - protocol: "udp"
          remote_ip_prefix: 0.0.0.0/0
          port_range_min: 500
          port_range_max: 500
        - protocol: "udp"
          remote_ip_prefix: 0.0.0.0/0
          port_range_min: 4500
          port_range_max: 4500

  sg_dmz_rule_self:
    type: OS::Neutron::SecurityGroupRule
    properties:
      security_group: {get_resource: dmz_security_group}
      protocol: "tcp"
      remote_ip_prefix: {list_join: ['', [{get_attr: [dmzhost_port_floating_ip, floating_ip_address]}, '/32']]}

  sg_nodes:
    type: OS::Neutron::SecurityGroup
    properties:
      description: Security group for nodes
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'nodes']]}
      rules:
        - protocol: tcp
          remote_ip_prefix: 0.0.0.0/0
          port_range_min: 22
          port_range_max: 22
        - protocol: tcp
          remote_ip_prefix: 0.0.0.0/0
          port_range_min: 3389
          port_range_max: 3389
  sg_nodes_rule_dmz:
    type: OS::Neutron::SecurityGroupRule
    depends_on: sg_nodes
    properties:
      security_group: {get_resource: sg_nodes}
      protocol: "tcp"
      remote_group: {get_resource: dmz_security_group}
  sg_nodes_rule_tcp:
    type: OS::Neutron::SecurityGroupRule
    depends_on: sg_nodes
    properties:
      security_group: {get_resource: sg_nodes}
      protocol: "tcp"
      remote_group: {get_resource: sg_nodes}
  sg_nodes_rule_udp:
    type: OS::Neutron::SecurityGroupRule
    depends_on: sg_nodes
    properties:
      security_group: {get_resource: sg_nodes}
      protocol: "udp"
      remote_group: {get_resource: sg_nodes}

  # DMZ HOST
  dmzhost_port_floating_ip:
    type: OS::Neutron::FloatingIP
    properties:
      floating_network: {get_param: public_network}
      port_id: { get_resource: dmzhost_port }

  dmzhost_port:
    type: OS::Neutron::Port
    depends_on: dmz_router_dmz_patch
    properties:
      network: { get_resource: dmz_net }
      fixed_ips:
        - ip_address: 192.168.0.2
          subnet: { get_resource: dmz_subnet }
      allowed_address_pairs:
        - ip_address: 10.0.0.0/24
      security_groups: [{get_resource: dmz_security_group}]

  dmzhost:
    type: OS::Nova::Server
    depends_on: dmzhost_port_floating_ip
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'dmz']]}
      image: 'Debian 11.3 bullseye'
      flavor: {get_param: dmz_flavor}
      key_name: {get_param: key_name}
      networks:
        - port: { get_resource: dmzhost_port }
      user_data:
        str_replace:
          template: |
            #!/bin/bash -v
            sudo apt update
            sudo apt upgrade -y
            sudo apt install strongswan strongswan-pki libcharon-extra-plugins -y
            sudo tee -a /etc/sysctl.d/10-ik.conf<<EOF
            net.ipv4.ip_forward=1
            net.ipv4.conf.all.send_redirects=1
            EOF
            sudo sysctl -p /etc/sysctl.d/10-ik.conf
            sudo mkdir -p /root/strongswan-pki/{cacerts,certs,private}
            sudo chmod 600 /root/strongswan-pki/
            sudo sh -c 'pki --gen --type rsa --size 4096 --outform pem > /root/strongswan-pki/private/ca-key.pem'
            sudo sh -c 'pki --self --ca --lifetime 3650 --in /root/strongswan-pki/private/ca-key.pem --type rsa --dn "CN=StrongSwan VPN root CA" --outform pem > /root/strongswan-pki/cacerts/ca-cert.pem'
            sudo sh -c 'pki --gen --type rsa --size 4096 --outform pem >  /root/strongswan-pki/private/server-key.pem'
            sudo sh -c 'pki --pub --in  /root/strongswan-pki/private/server-key.pem --type rsa | pki --issue --lifetime 1825 \
            --cacert  /root/strongswan-pki/cacerts/ca-cert.pem --cakey  /root/strongswan-pki/private/ca-key.pem \
            --dn "CN=$DMZ_FLOATING_IP" --san $DMZ_FLOATING_IP --san @$DMZ_FLOATING_IP \
            --flag serverAuth --flag ikeIntermediate --outform pem > /root/strongswan-pki/certs/server-cert.pem'
            sudo sh -c 'cp -r  /root/strongswan-pki/* /etc/ipsec.d/'
            sudo tee -a /etc/ipsec.secrets<<EOF
            # This file holds shared secrets or RSA private keys for authentication.
            # RSA private key for this host, authenticating it to any other host
            # which knows the public part.
            infomaniak-endpoint external-endpoint : PSK "Replace This with A very strong and very long string!"
            
            : RSA "server-key.pem"
            taylor : EAP "My_veRy_Str0NG_paSsword!"

            EOF
            sudo tee -a /etc/ipsec.conf<<EOF
            # ipsec.conf - strongSwan IPsec configuration file

            config setup

            conn %default
                    keylife=20m
                    rekeymargin=3m
                    keyingtries=1
                    mobike=no

            # basic configuration
            conn infomaniak_VPN
                keyexchange=ikev2
                leftfirewall=no
                left=$DMZ_FLOATING_IP
                leftsubnet=10.0.0.0/24
                leftid=external-endpoint
                right=%any
                rightsubnet=%any
                rightid=infomaniak-endpoint
                auto=route
                dpdaction=hold
                dpddelay=30s
                dpdtimeout=120s
                ike=aes128-sha256-modp1536
                ikelifetime=3600s
                esp=aes128-sha256-modp1536
                lifetime=3600s
                type=tunnel
                authby=psk

            conn ikev2
                auto=add
                compress=no
                type=tunnel
                keyexchange=ikev2
                fragmentation=yes
                forceencaps=yes
                dpdaction=clear
                dpddelay=300s
                rekey=no
                left=%any
                leftid=$DMZ_FLOATING_IP
                leftcert=server-cert.pem
                leftsendcert=always
                leftsubnet=0.0.0.0/0
                right=%any
                rightid=%any
                rightauth=eap-mschapv2
                rightsourceip=10.0.0.0/24
                rightdns=83.166.143.51,83.166.143.52
                rightsendcert=never
                eap_identity=%identity
            EOF
            iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o ens3 -m policy --pol ipsec --dir out -j ACCEPT
            iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o ens3 -j MASQUERADE
            sudo debconf-set-selections <<EOF
            iptables-persistent iptables-persistent/autosave_v4 boolean true
            iptables-persistent iptables-persistent/autosave_v6 boolean true
            EOF
            sudo apt install -y iptables-persistent
            #sudo sh -c '/sbin/iptables-save > /etc/iptables/rules.v4'
            ipsec restart

          params:
            $DMZ_FLOATING_IP: {get_attr: [dmzhost_port_floating_ip, floating_ip_address]}

 #dmzhost:
    # type: dmz.yaml
    #depends_on: dmz_router
    #properties:
      #public_network: {get_param: public_network}
      #private_network: {get_resource: private_net}
      #name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'dmz']]}
      #key_name: {get_param: key_name}
      #security_groups: {get_resource: dmz_security_group}

  nodes_group:
    type: OS::Nova::ServerGroup
    properties:
      name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'group-nodes']]}
      policies: ["anti-affinity"]

  nodes:
    type: OS::Heat::ResourceGroup
    properties:
      count: 2
      resource_def:
        type: node.yaml
        properties:
          flavorname: {get_param: nodes_flavor}
          private_network: {get_resource: nodes_net}
          name: {list_join: ['-', [{get_param: "OS::stack_name"}, 'node', '%index%']]}
          servergroup: {get_resource: nodes_group}
          key_name: {get_param: key_name}
          image: {get_param: nodes_image}
          # security_groups: {list_join: [',', ['default', {list_join: ['-', [{get_param: "OS::stack_name"}, 'nodes']]}]]}
          security_groups:
            - default
            - {get_resource: sg_nodes}

outputs:
  dmz_public_ip:
    description: Floating IP of the VPN host
    value: {get_attr: [dmzhost_port_floating_ip, floating_ip_address]}
  nodes_ips:
    description: Nodes ip addresses
    value: {get_attr: [nodes, internal_ip]}
  admin_pass:
    description: For Windows servers only, login password
    value: {get_attr: [nodes, admin_pass]}
