#!/bin/bash
#
# ALTE VERSION: Ausstellen des Cert _auf_ der 3CX, Upload _in_ die pfSense wg. SSL offload
#
# bash Skript zur Erneuerung eines LetsEncrypt Zertifikates lokal auf einem Client (hier: unter debian mit certbot, z.B. )
# und anschl. Upload auf eine pfSense Firewall und Speichern in deren Konfiguration für z.B. squid rp oder haproxy
#
# Hinweis: es erfolgt keine Überprüfung der automatischen Verlängerung und Benachrichtigung, schlägt die Verlängerung fehl dann
# läuft zwar das Skript aber es werden keine Zertifikate verlängert und ersetzt, das läuft halt aus
#
# Problem:
# - nur root kann auf der Firewall die Konfigurationsänderungen speichern
# - nur root kann auf der Firewall Dienste neu starten lassen
# - daher muss auf der Firewall ein separater cron Job als root laufen der das macht
#
# die Ausführung ist daher leider zweigeteilt
# Teil 1:
# - auf dem Client wird die Zertifikatverlängerung angestoßen und durchgeführt falls erforderlich
# - es wird ein php Skript für die Firewall generiert
# - das Skript und die zuvor generierten LE Zertifikate werden auf die Firewall hochgeladen
# Teil 2:
# - auf der Firewall muss ein _extra_ cron Job später laufen der das hochgeladene PHP Skript ausführt
# - dieses PHP Skript pflegt die Zertifikate ein, startet haproxy neu und löscht anschl. sich selbst
# und die Zertifikate
#
#
# es wird auf dem Client Computer (debian) benötigt:
# - certbot, sollte schon 1x gelaufen sein, d.h. auch Port 80 ankommend und Verlängerung möglich ...
# - sshpass und es sollte schon mind. 1x eine ssh verbindung zur pfSense aufgebaut gewesen sein wg. dem ssh hostkey!
# - ein cron job: (crontab -l ; echo "0 1 * * * /root/letsencrypt-renew-and-upload_pbx.domain.de-pfsense.sh >/dev/null 2>&1") | crontab -
#
# es wird auf der pfSense benötigt:
# - aktiviertes ssh
# - installiertes Paket cron
# - aktivierter cron Job zur (einmal täglichen) Ausführung des Aktualisierungsskripts auf der Firewall:
# /usr/local/bin/php -f /home/<certuser>/<fqdn>/<fqdn>.php <- certuser name und fqdn ersetzen!
# - ein extra eingerichteter Benutzer der Gruppe der Administratoren auf der pfSense (ja, geht wohl leider nicht anders, aktuell gibt es dafür ein anderes Skript)
# - ein vorab manuell eingespieltes Zertifikat welches als Bezeichnung den Hostnamen des Zertifikates beinhaltet,
# das wird dann nach Bedarf (wenn eine LE Verlängerung ansteht) immer mal ausgetauscht
#
# Suchstichworte: pfsense haproxy letsencrypt upload automatisch skript script shell bash debian sshpass 3cx pbx
#
# (temp.?) Anpassen der 3CX v18 Debian 10 Sourcen für die Installation von certbot und sshpass unter /etc/apt/sources.list:
# deb http://deb.debian.org/debian buster main
# deb-src http://deb.debian.org/debian buster main
#
# deb http://security.debian.org/debian-security buster/updates main
# deb-src http://security.debian.org/debian-security buster/updates main
#
# apt -y update && apt -y install certbot sshpass
#
# Suchstichworte: pfsense haproxy letsencrypt upload automatisch skript script shell bash debian
#
# Portweiterleitung vorab evtl. testen mit simplen http server:
# python3 -m http.server 80
#
_pfsenseip="192.168.10.10" # die IP der pfSense mit haproxy
_pfsenseport="61322" # der SSH Port der pfSense mit haproxy
_pfusername="sshuser" # der auf der pfSense extra angelegte Benutzer
_pfpassword="passwortsuperstrenggeheim" # das Passwort des auf der pfSense extra angelegten Benutzers
_fqdn=$(hostname -f) # der FQDN des Letsenrypt Zertifikates, ist auch genau so unter diesem Namen in der pfSense drin
_certemail="[email protected]" # das zur Verlängerung des Leytsencrypt Zertifikates benutzte E-Mail Konto
_doupdate=$false
_oldcerthash=$(cat /etc/letsencrypt/live/$_fqdn/fullchain.pem | md5sum)
_oldkeyhash=$(cat /etc/letsencrypt/live/$_fqdn/privkey.pem | md5sum)
# ab hier erfolgt die Zertifikatverlängerung mittels certbot, standalone, ohne Webserver Integration
#
# wichtig: der Port 80 muss frei sein und es muss eine Port- und Domainweiterleitung zu diesem Host existieren
#
certbot certonly --standalone -d "$_fqdn" --rsa-key-size 4096 --email "$_certemail" --agree-tos -n
_actcerthash=$(cat /etc/letsencrypt/live/$_fqdn/fullchain.pem | md5sum)
_actkeyhash=$(cat /etc/letsencrypt/live/$_fqdn/privkey.pem | md5sum)
_tcxcerthash=$(cat /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-crt.pem | md5sum)
_tcxkeyhash=$(cat /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-key.pem | md5sum)
_sipcerthash=$(cat /var/lib/3cxpbx/Instance1/Bin/Cert/domain_cert_$_fqdn.pem | md5sum)
_sipkeyhash=$(cat /var/lib/3cxpbx/Instance1/Bin/Cert/domain_key_$_fqdn.pem | md5sum)
if [[ ($_oldcerthash != $_actcerthash) || ($_tcxcerthash != $_actcerthash) || ($_sipcerthash != $_actcerthash) || ($_oldkeyhash != $_actkeyhash) || ($_tcxkeyhash != $_actkeyhash) || ($_sipkeyhash != $_actkeyhash) ]]; then
rm /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-crt.pem
rm /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-key.pem
cp /etc/letsencrypt/live/$_fqdn/fullchain.pem /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-crt.pem # nicht cert.pem ?!?
cp /etc/letsencrypt/live/$_fqdn/privkey.pem /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-key.pem
chown phonesystem:phonesystem /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-crt.pem
chown phonesystem:phonesystem /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-key.pem
chmod 644 /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-crt.pem
chmod 644 /var/lib/3cxpbx/Bin/nginx/conf/Instance1/$_fqdn-key.pem
/etc/init.d/nginx reload
rm /var/lib/3cxpbx/Instance1/Bin/Cert/domain_cert_$_fqdn.pem
rm /var/lib/3cxpbx/Instance1/Bin/Cert/domain_key_$_fqdn.pem
cp /etc/letsencrypt/live/$_fqdn/fullchain.pem /var/lib/3cxpbx/Instance1/Bin/Cert/domain_cert_$_fqdn.pem
cp /etc/letsencrypt/live/$_fqdn/privkey.pem /var/lib/3cxpbx/Instance1/Bin/Cert/domain_key_$_fqdn.pem
chown phonesystem:phonesystem /var/lib/3cxpbx/Instance1/Bin/Cert/domain_cert_$_fqdn.pem
chown phonesystem:phonesystem /var/lib/3cxpbx/Instance1/Bin/Cert/domain_key_$_fqdn.pem
chmod 644 /var/lib/3cxpbx/Instance1/Bin/Cert/domain_cert_$_fqdn.pem
chmod 644 /var/lib/3cxpbx/Instance1/Bin/Cert/domain_key_$_fqdn.pem
systemctl stop 3CXPhoneSystem01
_services=$(systemctl list-unit-files --state=enabled |grep -i 3cx |awk '{printf "%s\n", $1}')
for _service in $_services; do systemctl is-active --quiet $_service || systemctl start $_service; done
_doupdate=$true
cat << EOF > ~/$_fqdn.php
<?php
require_once("certs.inc");
require_once("pfsense-utils.inc");
\$a_cert = &\$config['cert'];
\$fname=pathinfo(__FILE__, PATHINFO_FILENAME);
for (\$ii=0; \$ii < count(\$a_cert); \$ii++) {
if (\$a_cert[\$ii]['descr'] == \$fname) {
\$fcinh=file_get_contents("/home/$_pfusername/\$fname/\$fname.pem");
\$fkinh=file_get_contents("/home/$_pfusername/\$fname/\$fname.key");
\$a_cert[\$ii]['crt']=base64_encode(\$fcinh);
\$a_cert[\$ii]['prv']=base64_encode(\$fkinh);
}
}
write_config("Zertifikat \$fname wurde aktualisiert");
parse_config(true);
shell_exec("/usr/local/etc/rc.d/haproxy.sh restart");
?>
EOF
# evtl. noch div. lokale Services auf dem Client mit dem neuen Zertifikat versorgen und diese anschl. neu starten laasen
# ssh-keygen -f ~/.ssh/known_hosts -R $_pfsenseip
# ssh-keyscan -H $_pfsenseip >> ~/.ssh/known_hosts
sshpass -p $_pfpassword ssh -p $_pfsenseport -o StrictHostKeyChecking=no $_pfusername@$_pfsenseip rm -r -f /home/$_pfusername/$_fqdn
sshpass -p $_pfpassword ssh -p $_pfsenseport -o StrictHostKeyChecking=no $_pfusername@$_pfsenseip mkdir /home/$_pfusername/$_fqdn
sshpass -p $_pfpassword ssh -p $_pfsenseport -o StrictHostKeyChecking=no $_pfusername@$_pfsenseip chmod -R 700 /home/$_pfusername/$_fqdn
sshpass -p $_pfpassword scp -P $_pfsenseport /etc/letsencrypt/live/$_fqdn/fullchain.pem $_pfusername@$_pfsenseip:/home/$_pfusername/$_fqdn/$_fqdn.pem
sshpass -p $_pfpassword scp -P $_pfsenseport /etc/letsencrypt/live/$_fqdn/privkey.pem $_pfusername@$_pfsenseip:/home/$_pfusername/$_fqdn/$_fqdn.key
sshpass -p $_pfpassword scp -P $_pfsenseport ~/$_fqdn.php $_pfusername@$_pfsenseip:/home/$_pfusername/$_fqdn/$_fqdn.php
# sshpass -p $_pfpassword scp -P $_pfsenseport ~/$_fqdn.sh $_pfusername@$_pfsenseip:/home/$_pfusername/$_fqdn/$_fqdn.sh
fi
rm -f ~/$_fqdn.php
exit 1
# Reste:
# zugriff unter php / psSsh auf die config
print_r($config['cert']['4']['crt']);
exec;