#!/bin/bash
#
# DNS Service

PATH=$HOME/.docker/cli-plugins:/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

config_dns() {
  echo -ne "\n* Configuring /federated/apps/dns container.."
  spin &
  SPINPID=$!
  EXTERNAL_IP_LASTDIGIT=`echo $EXTERNALIP | awk -F . '{ print $4 }'`
  EXTERNALIP_INADDR=`echo $EXTERNALIP | awk -F . '{ print $3"."$2"."$1".in-addr.arpa"}'`
  EXTERNALIP_INADDR_CAPS=`echo $EXTERNALIP | awk -F . '{ print $3"."$2"."$1".IN-ADDR.ARPA"}'`
  mkdir -p /federated/apps/dns/data/root
  mkdir -p /federated/apps/dns/data/etc/bind/zones
  mkdir -p /federated/apps/dns/data/var/log/letsencrypt

cat > /federated/apps/dns/docker-compose.yml <<EOF
services:
  dns:
    image: alpine:\${IMAGE_VERSION}
    container_name: dns
    hostname: dns
    restart: always
    working_dir: /root
    networks:
      core:
        ipv4_address: 192.168.0.10
    volumes:
      - ./data/etc/bind:/etc/bind
      - ./data/etc/letsencrypt:/etc/letsencrypt
      - ./data/var/log/letsencrypt:/var/log/letsencrypt
      - ./data/root:/root
    ports:
      - "53:53/udp"
      - "53:53/tcp"
    command: [ "/root/dns-cert.sh" ]

networks:
  core:
    external: true
EOF

cat > /federated/apps/dns/data/root/dns-cert.sh <<'EOF'
#!/bin/sh -x

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

if [ ! -e /etc/bind/.firstdone ]; then
  # Install bind and certbot packages
  apk add --no-cache bind
  apk add --no-cache certbot

  # Install ACME DNS script
  if [ ! -e /root/acme-dns-auth.py ]; then
    wget https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py
    chmod +x acme-dns-auth.py
  fi
  [ ! -e /usr/bin/python ] && ln -s /usr/bin/python3 /usr/bin/python 

  # Run rndc to create bind keys for rndc to run
  rndc-confgen -a
  cat /etc/bind/rndc.key >> /etc/bind/named.conf

  # Run Certbot and insert CNAME record into bind configuration
  DOMAIN=`ls /etc/bind/zones | head -1`
  echo "Trying to certbot for DOMAIN $DOMAIN"
  CNAME_RECORD=`certbot certonly --manual --manual-auth-hook /root/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d \*.$DOMAIN -d $DOMAIN --agree-tos --email hostmaster@$DOMAIN -n 2>1 | grep acme-dns | awk '{ print $3 }'`
  echo "Got CNAME record: $CNAME_RECORD"
  echo "$CNAME_RECORD" > /etc/bind/.cnamerecord
  echo -e "_acme-challenge.customer2\tIN\tCNAME\t$CNAME_RECORD" >> /etc/bind/zones/$DOMAIN

  # Reload Bind configuration without restarting the container or process
  named -f -g &
  crond &
#  rndc reload $DOMAIN
#  rndc reload
  sleep 7

  # Run Certbot again to generate the certificate
  certbot certonly --manual --manual-auth-hook /root/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d \*.$DOMAIN -d $DOMAIN --agree-tos --email hostmaster@$DOMAIN -n &> /dev/null
  if [ $? -eq 0 ]; then
    echo "[federated]: SUCCESS generating certificates for $DOMAIN!"
    echo "[federated]: Certificates are at /etc/letsencrypt/live/$DOMAIN"
    echo -ne "#!/bin/sh\n\n/usr/bin/certbot renew -q" > /etc/periodic/15min/certbot-renew.sh
    chmod +x /etc/periodic/15min/certbot-renew.sh
  else
    touch /etc/bind/.failedcert
    touch /etc/bind/.firstdone
    echo "[federated]: FAILED generating certificates for $DOMAIN"
    echo "[federated]: Check that you have DNS setup properly"
    wait -n
    exit 2;
  fi

  touch /etc/bind/.firstdone
  wait -n
elif [ -e /etc/bind/.firstdone ] && [ -e /etc/bind/.failedcert ]; then
  # Install bind and certbot packages
  apk add --no-cache bind
  apk add --no-cache certbot

  DOMAIN=`ls /etc/bind/zones | head -1`
  named -f -g &
  crond &
  sleep 7

  # Run Certbot again to generate the certificate
  certbot certonly --manual --manual-auth-hook /root/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d \*.$DOMAIN -d $DOMAIN --agree-tos --email hostmaster@$DOMAIN -n &> /dev/null 
  if [ $? -eq 0 ]; then
    rm /etc/bind/.failedcert
    echo "[federated]: SUCCESS generating certificates for $DOMAIN!"
    echo "[federated]: Certificates are at /etc/letsencrypt/live/$DOMAIN"
  else
    touch /etc/bind/.failedcert
    echo "[federated]: FAILED generating certificates for $DOMAIN"
    echo "[federated]: Check that you have DNS setup properly"
    wait -n
    exit 2;
  fi
  wait -n
else
  # Install bind and certbot packages
  apk add --no-cache bind
  apk add --no-cache certbot
  [ ! `pgrep -x named` ] && named -f -g && crond
fi
EOF

  chmod +x /federated/apps/dns/data/root/dns-cert.sh

cat > /federated/apps/dns/.env <<EOF
IMAGE_VERSION="3.17.1"
EOF
chmod 600 /federated/apps/dns/.env

cat > /federated/apps/dns/data/etc/bind/named.conf <<EOF
options {
    directory "/var/bind";

    listen-on { any; };
    listen-on-v6 { none; };
    allow-transfer {  none; };
    allow-recursion { none; };
    recursion no;
    dnssec-validation auto;
};
zone "$DOMAIN" {
    type master;
    file "/etc/bind/zones/$DOMAIN";
};
zone "$EXTERNALIP_INADDR" {
    type master;
    file "/etc/bind/zones/$DOMAIN.rev";
};
controls {
    inet 127.0.0.1 port 953
    allow { 127.0.0.1; } keys { "rndc-key"; };
};
EOF

cat > /federated/apps/dns/data/etc/bind/zones/$DOMAIN <<EOF
\$TTL	2d
\$ORIGIN $DOMAIN.
$DOMAIN.	IN      SOA     ns1.$DOMAIN. hostmaster.$DOMAIN. (
	                18	; Serial
       	                12h	; Refresh
              		15m     ; Retry
	            	3w	; Expire
	             	3h	; Negative Cache TTL
			)
	IN	NS	ns1.$DOMAIN.
	IN	NS	ns2.$DOMAIN.
	MX	10 	mail.$DOMAIN.
	IN	TXT	"v=spf1 mx a:mail.$DOMAIN ~all"
ns1	IN	A	$EXTERNALIP
ns2	IN      A       $EXTERNALIP
mail	IN      A       $EXTERNALIP
www	IN      A       $EXTERNALIP
EOF

cat > /federated/apps/dns/data/etc/bind/zones/$DOMAIN.rev <<EOF
\$TTL    2d
\$ORIGIN $EXTERNALIP_INADDR_CAPS.
@       IN      SOA     ns1.$DOMAIN. hostmaster.$DOMAIN. (
                12	; Serial
                12h	; Refresh
                15m     ; Retry
                3w	; Expire
                3h	; Negative Cache TTL
		)
	IN	NS	ns1.$DOMAIN.
	IN	NS	ns2.$DOMAIN.
$EXTERNAL_IP_LASTDIGIT       IN      PTR     computer.$DOMAIN.
EOF

echo -ne "done."
kill $SPINPID &> /dev/null
}

start_dns() {
  echo -ne "\n* Starting /federated/apps/dns service.."
  spin &
  SPINPID=$!

  if [ $DEBUG ]; then
    # Start /federated/apps/dns with output to console for debug
    docker compose -f /federated/apps/dns/docker-compose.yml -p dns up
    [ $? -eq 0 ] && echo -ne "done.\n" || fail "There was a problem starting service /federated/apps/dns"
  else
    # Start /federated/apps/dns with output to /dev/null
    docker compose -f /federated/apps/dns/docker-compose.yml -p dns up -d &> /dev/null

    # Keep trying to see that certificates are generated
    RETRY="18"
    while [ $RETRY -gt 0 ]; do
    # Check if we have hit limit for requesting certificate too many times / limits
    docker exec -it dns grep "too many certificates" /var/log/letsencrypt/letsencrypt.log &> /dev/null
      if [ $? -eq 0 ]; then
        fail "There was a problem starting /federated/apps/dns\nYou have requested this certificate too many times.\nWait 24 hours before requesting it again.\n"
      fi

      # Check if certs are generated
      ls /federated/apps/dns/data/etc/letsencrypt/live/$DOMAIN/*.pem &> /dev/null
      if [ $? -eq 0 ]; then
        kill -9 $SPINPID &> /dev/null
        echo -ne "done."
#      echo -ne "* Certificates at /federated/apps/dns/data/etc/letsencrypt/live/$DOMAIN\n"
        break
      else
        if [ "$RETRY" == 1 ]; then
          docker compose -f /federated/apps/dns/docker-compose.yml -p dns down &> /dev/null
          fail "There was a problem starting service /federated/apps/dns\nCheck the output of 'docker logs dns' or turn on\ndebug with -d"
        fi
        ((RETRY--))
        sleep 9 
      fi
    done
  fi
}
