# Configure a SUNET CDN CA server
class cdn::cache(
  Hash[String, Integer] $customers = {
    customer1 => 1000000000,
  },
  String $sunet_cdnp_version = '0.0.1',
  Hash[String, String] $acme_url = {
    test => 'https://internal-sto3-test-ca-1.cdn.sunet.se:9000/acme/acme/directory'
  }
)
{
  include sunet::packages::certbot
  include cdn::ca_trust

  $cache_secrets = lookup({ 'name' => 'cdn::cache-secrets', 'default_value' => undef })

  file { '/opt/sunet-cdn':
    ensure => directory,
    owner  => 'root',
    group  => 'root',
    mode   => '0755',
  }

  file { '/opt/sunet-cdn/customers':
    ensure => directory,
    owner  => 'root',
    group  => 'root',
    mode   => '0755',
  }

  file { '/opt/sunet-cdn/conf':
    ensure => directory,
    owner  => 'root',
    group  => 'root',
    mode   => '0755',
  }

  file { '/opt/sunet-cdn/conf/varnish-slash-seccomp.json':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/varnish-slash-seccomp.json.erb'),
  }

  file { '/etc/systemd/network/10-cdn-dummy.netdev':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-dummy.netdev.erb'),
  }

  file { '/etc/systemd/network/10-cdn-dummy.network':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-dummy.network.erb'),
  }

  file { '/etc/systemd/network/10-cdn-ipip.netdev':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-ipip.netdev.erb'),
  }

  file { '/etc/systemd/network/10-cdn-ipip.network':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-ipip.network.erb'),
  }

  file { '/etc/systemd/network/10-cdn-ip6tunl.netdev.erb':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-ip6tunl.netdev.erb'),
  }

  file { '/etc/systemd/network/10-cdn-ip6tunl.network.erb':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/10-cdn-ip6tunl.network.erb'),
  }

  # Reload the network config if it has changed
  exec { 'networkctl reload':
    subscribe   => [File['/etc/systemd/network/10-cdn-dummy.network'], File['/etc/systemd/network/10-cdn-ipip.network']],
    refreshonly => true,
  }

  $sysctl_file = '/etc/sysctl.d/99-cdn-cache.conf'
  file { $sysctl_file:
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('cdn/cache/sysctl.erb'),
  }
  # Load the sysctl file if it has changed
  exec { "sysctl -p ${sysctl_file}":
    subscribe   => File[$sysctl_file],
    refreshonly => true,
  }

  # Allow IPv4 tunnel packets arriving from l4lb nodes
  sunet::nftables::rule { 'sunet_cdn_tunnel4':
    rule => 'add rule inet filter input ip saddr { 130.242.64.233, 130.242.64.235 } ip protocol ipencap counter accept comment "sunet-cdn-tunnel4"'
  }

  # Allow IPv6 tunnel packets arriving from l4lb nodes
  sunet::nftables::rule { 'sunet_cdn_tunnel6':
    rule => 'add rule inet filter input ip6 saddr { 2001:6b0:2006:74::1, 2001:6b0:2006:75::1 } ip6 nexthdr ipv6 counter accept comment "sunet-cdn-tunnel6"'
  }

  # Allow decapsulated tunnel packets targeting the service IP range to reach
  # local service ports
  sunet::nftables::rule { 'sunet_cdn_service4':
    rule => 'add rule inet filter input meta iifname tunl0 ip daddr 188.240.152.0/24 tcp dport { 80, 443 } counter accept comment "sunet-cdn-service4"'
  }
  sunet::nftables::rule { 'sunet_cdn_service6':
    rule => 'add rule inet filter input meta iifname ip6tnl0 ip6 daddr 2001:6b0:2100::/48 tcp dport { 80, 443 } counter accept comment "sunet-cdn-service6"'
  }

  # From https://wiki.sunet.se/display/sunetops/Platform+naming+standards
  $my_fqdn = $facts['networking']['fqdn']
  $dot_split = split($my_fqdn, '[.]')
  $my_hostname = $dot_split[0]
  $dash_split = split($my_hostname,'[-]')
  $environment = $dash_split[2]

  sunet::nftables::allow { 'allow-step-ca-acme':
    from  => '89.45.237.248', # internal-sto3-test-ca-1.cdn.sunet.se
    port  => 80,
    proto => 'tcp',
  }

  # Get client cert for connecting to MQTT bus
  exec { "certbot certonly -n --email patlu@sunet.se --no-eff-email --agree-tos --standalone -d ${my_fqdn} --server ${acme_url[$environment]} --http-01-address ${facts['networking']['ip']}":
    creates => "/etc/letsencrypt/live/${my_fqdn}/fullchain.pem"
  }

  $sunet_cdnp_dir = '/var/lib/sunet-cdnp'
  $sunet_cdnp_file = "sunet-cdnp_${sunet_cdnp_version}_linux_${facts[os][architecture]}.tar.gz"
  $sunet_cdnp_url = "https://github.com/SUNET/sunet-cdnp/releases/download/v${sunet_cdnp_version}/${sunet_cdnp_file}"
  # Create directory for managing CDP purger
  file { $sunet_cdnp_dir:
    ensure => directory,
    owner  => 'root',
    group  => 'root',
    mode   => '0755',
  }

  exec { "curl -LO ${sunet_cdnp_url}":
    creates => "${sunet_cdnp_dir}/${sunet_cdnp_file}",
    cwd     => $sunet_cdnp_dir,
    notify  => Exec['extract sunet-cdnp'],
  }

  exec { 'extract sunet-cdnp':
    command     => "tar -xzf ${sunet_cdnp_file} sunet-cdnp",
    cwd         => $sunet_cdnp_dir,
    refreshonly => true,
  }

  file { "${sunet_cdnp_dir}/sunet-cdnp":
    owner => 'root',
    group => 'root',
    mode  => '0755',
  }

  file { '/usr/local/bin/sunet-cdnp':
    ensure => link,
    target => "${sunet_cdnp_dir}/sunet-cdnp",
  }

  if $cache_secrets {
    $customers.each |String $customer, Integer $customer_uid| {
      if $cache_secrets['customers'][$customer] {
        file { "/opt/sunet-cdn/customers/${customer}":
          ensure => directory,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0750',
        }

        file { "/opt/sunet-cdn/customers/${customer}/conf":
          ensure => directory,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0750',
        }

        file { "/opt/sunet-cdn/customers/${customer}/shared":
          ensure => directory,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0750',
        }

        file { "/opt/sunet-cdn/customers/${customer}/cache":
          ensure => directory,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0750',
        }

        file { "/opt/sunet-cdn/customers/${customer}/certs-private":
          ensure => directory,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0750',
        }

        $combined_pem = "/opt/sunet-cdn/customers/${customer}/certs-private/combined.pem"

        concat { $combined_pem:
          ensure => present,
          owner  => $customer_uid,
          group  => $customer_uid,
          mode   => '0640',
        }

        concat::fragment { "${customer}-fullchain-${cache_secrets['customers'][$customer]['host']}":
          target => $combined_pem,
          source => "/opt/certbot-sync/letsencrypt/live/${cache_secrets['customers'][$customer]['host']}/fullchain.pem",
          order  => '01',
        }

        concat::fragment { "${customer}-privkey-${cache_secrets['customers'][$customer]['host']}":
          target => $combined_pem,
          source => "/opt/certbot-sync/letsencrypt/live/${cache_secrets['customers'][$customer]['host']}/privkey.pem",
          order  => '02',
        }

        file { "/opt/sunet-cdn/customers/${customer}/conf/haproxy.cfg":
          ensure  => file,
          owner   => $customer_uid,
          group   => $customer_uid,
          mode    => '0440',
          content => template('cdn/cache/haproxy.cfg.erb'),
        }

        file { "/opt/sunet-cdn/customers/${customer}/conf/varnish.vcl":
          ensure  => file,
          owner   => $customer_uid,
          group   => $customer_uid,
          mode    => '0440',
          content => template('cdn/cache/varnish.vcl.erb'),
        }

        sunet::docker_compose { "sunet-cdn-cache-${customer}":
          content          => template('cdn/cache/docker-compose.yml.erb'),
          service_name     => "cdn-cache-${customer}",
          compose_dir      => "/opt/sunet-cdn/compose/${customer}",
          compose_filename => 'docker-compose.yml',
          description      => "SUNET CDN CA ${customer}",
        }
      }
    }
  }
}