From d8880d36c726b70fad3f7e31f1d2b414c5cf4bfe Mon Sep 17 00:00:00 2001 From: Christoph Kuchenbuch Date: Wed, 16 Nov 2016 14:53:49 +0100 Subject: [PATCH] Initial commit --- README.md | 43 + install_dehydrated.sh | 2744 +++++++++++++++++++++ tools-bind/bind_get_domain_by_hostname.sh | 113 + tools-bind/bind_set_new_serial.sh | 281 +++ tools-bind/bind_set_renew_tlsa.sh | 351 +++ 5 files changed, 3532 insertions(+) create mode 100644 README.md create mode 100755 install_dehydrated.sh create mode 100755 tools-bind/bind_get_domain_by_hostname.sh create mode 100755 tools-bind/bind_set_new_serial.sh create mode 100755 tools-bind/bind_set_renew_tlsa.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac7f907 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +## dehydrated_cron + +### Das Skript _install_dehydrated.sh_ + +* installiert _dehydrated_ aus dem git Repository +* erstellt notwendige Verzeichnisse und Konfigurationen +* erstellt ein Cron Job Skript _dehydrated_cron.sh_ zur Generierung der Zertifikate +* erstellt die Skripte _create_domains_file.sh_, _change_ssl_directives.sh_ und _dh_tlsgen.sh_ im _tools_-Verzeichnis +* Konfiguriert einen bereits installierten Apache Webserver + +### Installation +Das Skript _install_dehydrated.sh_ auf den Zielrechner kopieren und (als root) ausführen. Die Konfigurationsdateien _config_ und __dehydrated_cron.conf_ im Konfigurationsverzeichnis (default: `/etc/dehydrated`) gegebenenfalls anpassen. **Voreingestellt ist der Testing Modus** + +Alle Verzeichnisse können während der Installation angegeben werden: + +* _DH Install Dir_: hierhin wird das Repository geclont +* _DH Conf Dir_: Hier werden die Konfigurationdateien für das dehydrated Skript (_config_) und für den Cron Job (_dehydrated_cron.conf_) hinterlegt. +* _DH Base Dir_: das Hauptverzeichnis für dehydrated bzw. Let's Encrypt. Dises Verzeichnis enthält die Accountdaten für den Zugang bei Let's Encrypt, die Zertifikate und Keys, das Skript _hook.sh_ und die Datei _domains.txt_, mit den zu erstellenden Zertifikatsnamen und -aliasen. Im Unterverzeichnis _cron_ liegt das Cronskript _dehydrated_cron.sh_, im Unterverzeichnis tools die Skripte _change_ssl_directives.sh_, _create_domains_file.sh_ und _dh_tlsgen.sh_. + +Während Installation kann angegeben werden, wohin der Cronjob installiert werden soll: in das Verzeichnis `/etc/cron.d/` oder als root cronjob angehängt an die datei `/var/spool/cron/crontabs/root`. + +**Das Installationsskript überschreibt keine vorhandenen Konfigurationen.** In einer Umgebung, in der _dehydrated_cron_ bereits installiert ist werden durch das erneute Ausführen vom _install_dehydrated.sh_ Skripte und das git Repository von _dehydrated_ gegebenenfalls aktualisiert. Haben sich Skripte oder das Repository geändert, so wird eine Sicherung (mit Zeitstempel) erstellt. + +### _dehydrated_cron.sh_ +Der Cronjob _dehydrated_cron.sh_ erstellt oder eneuert Zertikikate und stellt diese im Verzeichnis _DH_BASE/certs/\/_ zur Verfügung. Soll ein Host für einen bestimmten Service _DANE_ unterstützen, so erstellt der Cronob die notwendigen _TLSA_-Records und veröffentlicht diese im Nameserver. Hierfür ist der zuständige Nameserver entsprechend vorzubereiten/zu konfigurieren (SSH-Zugang, Bereitstellung von Skripten, die die generierten _TLSA_-records dem Zonefile hinzufügen, die Seriennumer erhöhen und die Zone reloaden (s.u. Verzeichnis bind)). + +Ausgeführt auf einer Konsole, ist das Skript informativ, ausgeführt als Cronjob werden nur dann Ausgaben produziert, falls Zertifikate ausgestellt/erneuert wurden oder Fehler aufgetreten sind. + +### Tools +Es werden drei Hilfs-Skripte im Verzeichnis _DH_BASE/tools_ zur Verfogung gestellt: +* _create_domains_file.sh_: sucht in den VHost Konfigurationen nach Hosts, für die ein Zertifikat erstellt werden kann/soll und trägt common name sowie eventuell alternative names in eine Zeile der Datei _DH_BASE/domains.txt_ ein. Diese Datei wird von _dehydrated_ eingelesen um die entsprechenden Zertifikate zu generieren (falls sie noch nicht existieren oder bald abgelaufen sind). +* _change_ssl_directives.sh_: Setzt in den Direktiven `SSLCertificateFile` und `SSLCertificateKeyFile` die entsprchenden Pfade zum Zertifikat/Key +* _dh_tlsgen.sh_ : Generiert _TLSA__Records für _hostname:port_. So kann beispielsweise überprüft werden, ob durch den Cronjob die korrekten Records im Nameserver eingetragen wurden. + +*** +### (Repository-)Verzeichnis tools-bind + + +In diesem Verzeichnis liegen drei Skripte, die für die Bereitstellung von TLSA Records benötigt werden. Diese Scripte müssen sich auf dem zuständigen Nameserver befinden und über SSH vom Cronjob _dehydrated_cron.sh_ ausgeführt werden können. Entsprechende Parameter können in der Konfigurationsdatei _dehydrated_cron.conf_ angepasst werden. Die ist nur dann notwendig, wenn für einen Service _DANE_ Unterstützung implementiert ist. + +* _bind_get_domain_by_hostname.sh_ +* _bind_set_renew_tlsa.sh_ +* _bind_set_new_serial.sh_ diff --git a/install_dehydrated.sh b/install_dehydrated.sh new file mode 100755 index 0000000..21f8b66 --- /dev/null +++ b/install_dehydrated.sh @@ -0,0 +1,2744 @@ +#!/usr/bin/env bash + +_DH_INSTALL_DIR=/usr/local/dehydrated +_DH_CONF_DIR=/etc/dehydrated +_DH_BASE_DIR=/var/lib/dehydrated +_DH_WELL_KNOWN_DIR=/var/www/dehydrated + +# ------------- +# --- Some functions +# ------------- +echononl(){ + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n "$*\\c" 1>&2 + else + echo -e -n "$*" 1>&2 + fi + rm /tmp/shprompt$$ +} + +fatal(){ + echo "" + echo -e "fataler Fehler: $*" + echo "" + echo -e "\t\033[31m\033[1mInstalllation wird abgebrochen\033[m\033[m" + echo "" + exit 1 +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*" + echo "" +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: $*" + echo "" +} + +echo_done() { + echo -e "\033[80G[ \033[32mdone\033[m ]" +} +echo_ok() { + echo -e "\033[80G[ \033[32mok\033[m ]" +} +echo_warning() { + echo -e "\033[80G[ \033[33m\033[1mwarn\033[m ]" +} +echo_failed(){ + echo -e "\033[80G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[80G[ \033[33m\033[1mskipped\033[m ]" +} + +## - Check if a given array (parameter 2) contains a given string (parameter 1) +## - +containsElement () { + local e + for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} + +clear +echo "" +echo -e "\033[21G\033[32mInstallation script for dehydrated\033[m" +echo "" + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH installation directory" +echo "" +echo "" +DH_INSTALL_DIR= +while [[ "X$DH_INSTALL_DIR" = "X" ]]; do + echononl "DH Installation Directory [$_DH_INSTALL_DIR]: " + read DH_INSTALL_DIR + if [[ "X$DH_INSTALL_DIR" = "X" ]]; then + DH_INSTALL_DIR=$_DH_INSTALL_DIR + fi +done +HOOK_EXAMPLE_FILE=${DH_INSTALL_DIR}/docs/examples/hook.sh + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH Configuration directory" +echo "" +echo "" +DH_CONF_DIR= +while [[ "X$DH_CONF_DIR" = "X" ]]; do + echononl "DH Configuration Directory [$_DH_CONF_DIR]: " + read DH_CONF_DIR + if [[ "X$DH_CONF_DIR" = "X" ]]; then + DH_CONF_DIR=$_DH_CONF_DIR + fi +done + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH Base directory" +echo "" +echo "" +DH_BASE_DIR= +while [[ "X$DH_BASE_DIR" = "X" ]]; do + echononl "DH Configuration Directory [$_DH_BASE_DIR]: " + read DH_BASE_DIR + if [[ "X$DH_BASE_DIR" = "X" ]]; then + DH_BASE_DIR=$_DH_BASE_DIR + fi +done +HOOK_OUT_FILE="${DH_BASE_DIR}/hook.sh" +DH_CRON_SCRIPT="${DH_BASE_DIR}/cron/dehydrated_cron.sh" + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH \"WELL KNOWN\" directory" +echo "" +echo "Output directory for challenge-tokens to be served by webserver or" +echo "deployed in HOOK (e.g.: /var/www/dehydrated)" +echo "" +DH_WELL_KNOWN_DIR= +while [[ "X$DH_WELL_KNOWN_DIR" = "X" ]]; do + echononl "DH \"WELL KNOWN\" Directory [$_DH_WELL_KNOWN_DIR]: " + read DH_WELL_KNOWN_DIR + if [[ "X$DH_WELL_KNOWN_DIR" = "X" ]]; then + DH_WELL_KNOWN_DIR=$_DH_WELL_KNOWN_DIR + fi +done + + +DH_CRON_TYPE="" +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "How to activate the dehydrated cronjob" +echo "" +echo "[1] System defined as cronjob file in /etc/cron.d" +echo "[2] User defined - added to root cronjobs" +echo "[3] Skip Cronjob Activation" +echo "" +echononl "Eingabe: " + + +while [ "$DH_CRON_TYPE" != "system" -a "$DH_CRON_TYPE" != "user" -a "$DH_CRON_TYPE" != "none" ];do + read OPTION + case $OPTION in + 1) + DH_CRON_TYPE="system" + ;; + 2) + DH_CRON_TYPE="user" + ;; + 3) + DH_CRON_TYPE="none" + ;; + + *) + echo "" + echo -e "\tFalsche Eingabe ! [ 1 = System defined ; 2 = User defined , 3 = Skip]" + echo "" + echononl "Eingabe:" + ;; + esac +done + +WEBSERVER_INSTALLATION="" +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Webserver Installation" +echo "" +echo "" +echo "[1] Apache2 from Debian Package System" +echo "[2] Apache2 installed from Sources" +echo "[3] Other Webserver Installation" + +echo "" +echononl "Eingabe: " + +while [ "$WEBSERVER_INSTALLATION" != "Apache2_Debian" \ + -a "$WEBSERVER_INSTALLATION" != "Apache2_Source" \ + -a "$WEBSERVER_INSTALLATION" != "Other_Webserver_Installation" ];do + read OPTION + case $OPTION in + 1) WEBSERVER_INSTALLATION="Apache2_Debian" + ;; + 2) WEBSERVER_INSTALLATION="Apache2_Source" + ;; + 3) WEBSERVER_INSTALLATION="Other_Webserver_Installation" + ;; + *) echo "" + echo -e "\tFalsche Eingabe ! [ 1 = Apache2 Debian ; 2 = Apache2 Sources ; 3 = Other ]" + echo "" + echononl "Eingabe:" + ;; + esac +done + +_set_apache_conf_symlink=false + +if [[ "$WEBSERVER_INSTALLATION" = "Apache2_Debian" ]] ;then + APACHE_VHOST_DIR=/etc/apache2/sites-enabled + APACHE_CONF_DIR=/etc/apache2/conf-available + _set_apache_conf_symlink=true +elif [[ "$WEBSERVER_INSTALLATION" = "Apache2_Source" ]] ;then + + + if [[ ! -d "$_APACHE_VHOST_DIR" ]]; then + if [[ -d "/usr/local/apache2/conf/vhosts" ]]; then + if [[ -d "/usr/local/apache2/conf/vhosts/0" ]]; then + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts/0 + else + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts + fi + fi + fi + + echo "" + echo -e "\033[32m--\033[m" + echo "" + echo "Insert VHost Configuration Directory" + echo "" + echo "Type:" + echo -e "\t\033[33mNone\033[m if not present or no webserver configuration should be done." + echo "" + APACHE_VHOST_DIR= + while [[ "X$APACHE_VHOST_DIR" = "X" ]]; do + echononl "VHost Configuration Directory [$_APACHE_VHOST_DIR]: " + read APACHE_VHOST_DIR + if [ "X$APACHE_VHOST_DIR" = "Xnone" -o "X$APACHE_VHOST_DIR" = "XNone" ]; then + warn "Webserver Configuration will be ommited" + APACHE_VHOST_DIR= + break + elif [[ "X$APACHE_VHOST_DIR" = "X" ]]; then + APACHE_VHOST_DIR=$_APACHE_VHOST_DIR + fi + done + + if [[ "`basename $APACHE_VHOST_DIR`" = "0" ]]; then + APACHE_CONF_DIR=`dirname $APACHE_VHOST_DIR` + else + APACHE_CONF_DIR=$APACHE_VHOST_DIR + fi + +else + APACHE_VHOST_DIR= + APACHE_CONF_DIR= + warn "This Type of Webserver Installation is not yet available" +fi + + +echo "" +echo "" + + +echo -e "#\033[32m --------------------\033[m" +echo -e "#\033[32m --- Start dehydrated (DH) installation with the following Parameters \033[m" +echo -e "#\033[32m --------------------\033[m" +echo "" +echo "DH Installation Directory.....: $DH_INSTALL_DIR" +echo "DH Configuration Directory....: $DH_CONF_DIR" +echo "DH Base Directory.............: $DH_BASE_DIR" +echo "DH \"WELL KNOWN\" Directory.....: $DH_WELL_KNOWN_DIR" +echo "" +if [[ "$DH_CRON_TYPE" = "system" ]]; then + echo "DH Cronjob Type...............: Installed as file in /etc/cron.d" +elif [[ "$DH_CRON_TYPE" = "user" ]]; then + echo "DH Cronjob Type...............: Added to /var/spool/cron/root" +else + echo -e "DH Cronjob Type...............: \033[33mSkip activation\033[m" +fi +echo "" +if [[ "$APACHE_CONF_DIR" != "$APACHE_VHOST_DIR" ]]; then + echo "Apache Config Directory.......: $APACHE_CONF_DIR" +fi +echo "Apache Vhost Directory........: $APACHE_VHOST_DIR" +echo "" + +echononl "Start with that configuration? [yes/no]: " +read OK +while [ "X$OK" != "Xyes" -a "X$OK" != "XYes" -a "X$OK" != "XNo" -a "X$OK" != "Xno" ] +do + echononl "wrong entry! [yes/no] :" + read OK +done +[ $OK = "Yes" -o $OK = "yes" ] || fatal "Change parameters and restart script: `basename $0`" + +echo +echo + +_date=`date +%Y-%m-%d-%H%M` + + +## - Configure Apache.. +## - +if [[ -n "$APACHE_CONF_DIR" ]]; then + + _apache_dh_conf_file=${APACHE_CONF_DIR}/000-dehydrated.conf + + echononl " Configure Apache Webserver: Create alias foe WLLKNOWN Directory.." + cat << EOF > $_apache_dh_conf_file +Alias /.well-known/acme-challenge ${DH_WELL_KNOWN_DIR}/ + + + Options +FollowSymLinks + AllowOverride None + Require all granted + +EOF + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + if $_set_apache_conf_symlink ; then + echononl " Activate \"000-dehydrated.conf\" .. " + if [[ -h /etc/apache2/conf-enabled/000-dehydrated.conf ]] ; then + echo_skipped + else + ln -s ../conf-available/000-dehydrated.conf /etc/apache2/conf-enabled/000-dehydrated.conf + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + fi + fi +fi + +if [[ -d "$DH_WELL_KNOWN_DIR" ]]; then + echononl " Backup \"$DH_WELL_KNOWN_DIR\" directory.." + mv $DH_WELL_KNOWN_DIR $DH_WELL_KNOWN_DIR.$_date + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi +echononl " Create directory \"$DH_WELL_KNOWN_DIR\" .." +mkdir -p $DH_WELL_KNOWN_DIR +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed +fi + +echononl " Create index.html file in directory \"$DH_WELL_KNOWN_DIR\" .." +cat << EOF > ${DH_WELL_KNOWN_DIR}/index.html + + + +HTTP Error 404 / Http Fehler 404 + + + + + + +
+ + + +
+ +

HTTP Error 404

+

The site you have requestet was not found on this Server

+

Please check your spelling and ry again.

+

Thank You very much!

+ +

HTTP Fehler 404

+

Die von Ihnen aufgerufene Seite gibt es leider nicht - Sorry

+

Bitte prüfen Sie die Adresse und versuchen es nochmals.

+

Vielen Dank für Ihr Verständnis!

+ +
+
+ + + +EOF +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed +fi + +if [[ -d "${DH_WELL_KNOWN_DIR}.$_date" ]] ; then + diff $DH_WELL_KNOWN_DIR ${DH_WELL_KNOWN_DIR}.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + info "$DH_WELL_KNOWN_DIR has not changed.\n\t Removing previos created backup.." + rm -rf ${DH_WELL_KNOWN_DIR}.$_date + fi +fi + + +apache_control_script=`which apachectl` +echononl " Restarting (graceful) apache webserver.." +if [[ -n "$apache_control_script" ]]; then + $apache_control_script graceful + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + warn "Graceful restart of apache webserver failed.." + fi +else + echo_skipped + warn "Apache Control Script (apachectl) not found." +fi + + + +## - dehydrated from git repository +## - +cd `dirname $DH_INSTALL_DIR` +echononl " Cloning repository \"dehydrated.git\".." +if [[ -d "$DH_INSTALL_DIR" ]]; then + echo_skipped + info "$DH_INSTALL_DIR already exists.\n\t Try to update (git pull) now.." + echononl " Backup existing repository" + cp -a $DH_INSTALL_DIR ${DH_INSTALL_DIR}.$_date + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + echononl " Update dehydrated repository" + cd $DH_INSTALL_DIR + git pull > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + warn "Maybe updating dehydrated repository was not successfully. Check it manually." + fi + diff -Nur $DH_INSTALL_DIR ${DH_INSTALL_DIR}.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + info "Repository has not changed.\n\t Removing previously created backup.." + rm -rf ${DH_INSTALL_DIR}.$_date + fi +else + git clone https://github.com/lukas2511/dehydrated.git $DH_INSTALL_DIR > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fatal "Cloning git repositors \"dehydrated.git\" failed!. Exciting install script.." + fi +fi + +## - Create needed directories +## - +## - Configuration directory +## - Base directory (for let's encrypt asccounts, generated certificates, +## - +echononl " Create Directory $DH_CONF_DIR if not exists.." +if [[ -d "$DH_CONF_DIR" ]]; then + echo_skipped + #echononl " Backup configuration directory to ${DH_CONF_DIR}.$_date.." + #cp -a $DH_CONF_DIR ${DH_CONF_DIR}.$_date + #if [[ $? -eq 0 ]] ; then + # echo_ok + #else + # echo_failed + #fi +else + mkdir -p $DH_CONF_DIR + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi + +echononl " Create Directory ${DH_BASE_DIR}.." +if [[ -d "$DH_BASE_DIR" ]]; then + echo_skipped +else + mkdir -p $DH_BASE_DIR + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi + + +## - Copy example configuration file to $DH_CONF_DIR +## - +echononl " Copy (example) configuration to ${DH_CONF_DIR}/config .." +if [[ -f "${DH_CONF_DIR}/config" ]]; then + echo_skipped + info "Configuration file already exists.\n\t So we let current configuration untouched." + echononl " Copy example Config file to ${DH_CONF_DIR}/config.example" + cp $DH_INSTALL_DIR/docs/examples/config ${DH_CONF_DIR}/config.example > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +else + cp $DH_INSTALL_DIR/docs/examples/config $DH_CONF_DIR/ > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + ## - Adapt configuration file + ## - + ## - CHALLENGETYPE="http-01" + ## - BASEDIR="$DH_BASE_DIR" + ## - WELLKNOWN="$DH_WELL_KNOWN_DIR" + ## - HOOK="${BASEDIR}/hook.sh" + ## - + echononl " Adjust configuration: CHALLENGETYPE=\"http-01\".." + perl -i -n -p -e 's/^(\s*#*\s*)(CHALLENGETYPE=.*)/## - \1\2\nCHALLENGETYPE="http-01"/' $DH_CONF_DIR/config + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + echononl " Adjust configuration: BASEDIR=\"$DH_BASE_DIR\".." + perl -i -n -p -e "s#^(\s*\#*\s*)(BASEDIR=.*)#\#\# - \1\2\nBASEDIR=\"$DH_BASE_DIR\"#" $DH_CONF_DIR/config + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + echononl " Adjust configuration: WELLKNOWN=\"$DH_WELL_KNOWN_DIR\".." + perl -i -n -p -e "s#^(\s*\#*\s*)(WELLKNOWN=.*)#\#\# - \1\2\nWELLKNOWN=\"$DH_WELL_KNOWN_DIR\"#" $DH_CONF_DIR/config + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + echononl " Adjust configuration: HOOK=\"\${BASEDIR}/hook.sh\".." + perl -i -n -p -e "s#^(\s*\#*\s*)(HOOK=.*)#\#\# - \1\2\nHOOK=\"\\\${BASEDIR}/hook.sh\"#" $DH_CONF_DIR/config + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi + + + ## - Let’s Encrypt has stringent rate limits in place. + ## - + ## - If you start testing using the production endpoint (which is the + ## - default), you will quickly hit these limits and find yourself locked out. + ## - + ## - To avoid this, please set the CA property to the Let’s Encrypt staging + ## - server URL in your config file: + ## - + echononl " Set CA property to the Let’s Encrypt staging server (for testing).." + perl -i -n -p -e 's#^(\s*\#*\s*)(CA=.*)#\#\# - \1\2\nCA="https://acme-staging.api.letsencrypt.org/directory"#' \ + $DH_CONF_DIR/config + if [[ $? -eq 0 ]] ; then + echo_ok + warn "Configuration is only for testing\n\t For production mode comment out line \"CA=..\"" + else + echo_failed + fi +fi + + +if [[ -f "$HOOK_OUT_FILE" ]] ; then + echononl " Backup existing hook-file to ${HOOK_OUT_FILE}.$_date" + cp -a $HOOK_OUT_FILE ${HOOK_OUT_FILE}.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi + +found=false +old_IFS=$IFS +IFS='' +> $HOOK_OUT_FILE +#regex_deploy="deploy_cert\s*()" +echononl " Writing file ${HOOK_OUT_FILE}.." +while read -r line || [[ -n "$line" ]]; do + if [[ $line =~ deploy_cert\s*() ]]; then + found=true + else + if $found ; then + if [[ $line =~ ^"}"$ ]]; then + + echo "" >> $HOOK_OUT_FILE + echo " #lynx --source https://www.identrust.com/certificates/trustid/root-download-x3.html \\" >> $HOOK_OUT_FILE + echo " # | grep -v \"\/textarea\" \\" >> $HOOK_OUT_FILE + echo " # | awk '/textarea/{x=NR+18;next}(NR<=x){print}' \\" >> $HOOK_OUT_FILE + echo " # | sed -e '1i-----BEGIN CERTIFICATE-----\' \\" >> $HOOK_OUT_FILE + echo " # | sed -e '\$a-----END CERTIFICATE-----\' \\" >> $HOOK_OUT_FILE + echo " # > \${BASEDIR}/certs/\${DOMAIN}/root-\${TIMESTAMP}.ca" >> $HOOK_OUT_FILE + echo " #ln -s root-\${TIMESTAMP}.ca \${BASEDIR}/certs/\${DOMAIN}/root.ca" >> $HOOK_OUT_FILE + echo " #cp -a \`realpath \$FULLCHAINFILE\` \`realpath \$FULLCHAINFILE\`.ORIG" >> $HOOK_OUT_FILE + echo " #cat \`realpath \$FULLCHAINFILE\`.ORIG \${BASEDIR}/certs/\${DOMAIN}/root-\${TIMESTAMP}.ca > \$FULLCHAINFILE" >> $HOOK_OUT_FILE + echo " cat \$KEYFILE \$FULLCHAINFILE > \${BASEDIR}/certs/\${DOMAIN}/privkey_cert_chain-\${TIMESTAMP}.pem" >> $HOOK_OUT_FILE + echo " ln -s privkey_cert_chain-\${TIMESTAMP}.pem \${BASEDIR}/certs/\${DOMAIN}/privkey_cert_chain.pem" >>$HOOK_OUT_FILE + echo " echo \" + Hook: \$DOMAIN - Certificate has been produced\"" >> $HOOK_OUT_FILE + echo "" >> $HOOK_OUT_FILE + found=false + fi + fi + fi + + echo $line >> $HOOK_OUT_FILE + +done < "$HOOK_EXAMPLE_FILE" +IFS=$old_IFS + +if [[ -f "$HOOK_OUT_FILE" ]] ; then + echo_ok +else + echo_failed +fi + +echononl " Make $HOOK_OUT_FILE executable.." +chmod 700 $HOOK_OUT_FILE +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed +fi + +diff $HOOK_OUT_FILE ${HOOK_OUT_FILE}.$_date > /dev/null 2>&1 +if [[ $? -eq 0 ]] ; then + info "$HOOK_OUT_FILE has not changed.\n\t Removing previously created backup" + rm -f ${HOOK_OUT_FILE}.$_date > /dev/null 2>&1 +fi + +if [[ ! -f "${DH_BASE_DIR}/domains.txt" ]]; then + echononl " Create empty \"domains.txt\" file.." + touch ${DH_BASE_DIR}/domains.txt + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi + +echononl " Create configuration file for Cron Job Script to \"$DH_CONF_DIR\".." +if [[ -f "${DH_CONF_DIR}/dehydrated_cron.conf" ]]; then + echo_skipped + info "${DH_CONF_DIR}/dehydrated_cron.conf already exists.\n\t So we let current configuration untouched." + echononl " Copy example Config file to ${DH_CONF_DIR}/ehydrated_cron.conf.example" + cat << EOF > ${DH_CONF_DIR}/dehydrated_cron.conf.example +## ------------------------------------------- +## - Configuration file for dehydrated cronjob +## ------------------------------------------- + +# - log_level: possible values are "0" for only error +# - messages or "1" for verbose +# - +# - If called from a noninteractive shell (as cron), log_level +# - will be set to "0" +# - +# - Defaults to "0" +# - +log_level=1 + +# - Where to find dehydrated script +# - +script_dir=$DH_INSTALL_DIR + +# - Where to find (common) names (and alternative names) for certificates +# - +domains_txt=${DH_BASE_DIR}/domains.txt + +# - Define hostnames from services other than webservices +# - Only one hostname is allowed, but you can define alternative +# - names (until to 99) for the certificate. this cab be done +# - by defining the concerning *_server_alt_names variable as a +# - space seperated list of hostnames +# - +# - example: +# - mail_server="mx.example.com" +# - mail_server_alt_names="mail.example.com smtp.example.com imap.example.com pop.example.com" +# - +mail_server="" +mail_server_alt_names="" + +ftp_server="" +ftp_server_alt_names="" + +# - Services installed on this mashine +# - +postfix_installed=false +dovecot_installed=false +pureftpd_installed=false +apache_installed=true +nginx_installed=false + + +# --- +# --- DANE +# --- + +# - Notice: +# - Supporting Dane is a little bit complex, because befor providing +# - the new certificates on a service, a TLSA record must be present. So +# - access to the nameserver within the permissions to generate the TLSA +# - records AND reload the concerning zone is required. + +# - Dane supported hostname:port +# - +# - Space seperated list of entries : +# - +# - example: +# - dane_records="\${mail_server}:25 \${mail_server}:587 webmail.so36.net:443" +# - +dane_records="" + +# - (Primary) Nameserver which supports Dane Records +# - +dane_nameserver="" + +# - How to access the namesever via ssh? +# - +# - Note: +# - Take care to provies an entry for nameservers dane_ssh_user in his +# - authorized_keys file. +# - +# - The ssh-key must not have a password! Otherwise this script will +# - not work! +# - +dane_ssh_user="manage-bind" +dane_ssh_port=22 +dane_ssh_key=/root/.ssh/id_rsa-dehydrated + + +# - Scripts used on nameserver. +# - +# - Note: +# - The scripts must be accessable by dane_ssh_user. This +# - ca be realised by adding a concerning entry into sudo file +# - +# - for example: +# - manage-bind ALL=(root)NOPASSWD:/usr/local/bin/bind_* +# - +set_new_serial_script=bind_set_new_serial.sh +renew_tlsa_record=bind_set_renew_tlsa.sh +get_domain_by_hostname=bind_get_domain_by_hostname.sh + + +# - TTL for Records "IN TLSA 3 1 1" and "IN TLSA 2 1 1" +# - +ttl_311=3600 +ttl_211=3600 + + +# - Logfile where the certificate generation process, called from dehydrated +# - script, will write down the results. This logfile will be evaluated +# - afterwords to do some post generation tasks (in case of success) or inform +# - about errors. +# - +# - Logging in that file and evaluatiog the results will happen in any case, +# - even if variable LOGGING is set to true +# - +_logfile="/tmp/dehydrated-`date +%Y-%m-%d-%H%M`.log" +EOF + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +else + cat << EOF > ${DH_CONF_DIR}/dehydrated_cron.conf +## ------------------------------------------- +## - Configuration file for dehydrated cronjob +## ------------------------------------------- + +# - log_level: possible values are "0" for only error +# - messages or "1" for verbose +# - +# - If called from a noninteractive shell (as cron), log_level +# - will be set to "0" +# - +# - Defaults to "0" +# - +log_level=1 + +# - Where to find dehydrated script +# - +script_dir=$DH_INSTALL_DIR + +# - Where to find (common) names (and alternative names) for certificates +# - +domains_txt=${DH_BASE_DIR}/domains.txt + +# - Define hostnames from services other than webservices +# - Only one hostname is allowed, but you can define alternative +# - names (until to 99) for the certificate. this cab be done +# - by defining the concerning *_server_alt_names variable as a +# - space seperated list of hostnames +# - +# - example: +# - mail_server="mx.example.com" +# - mail_server_alt_names="mail.example.com smtp.example.com imap.example.com pop.example.com" +# - +mail_server="" +mail_server_alt_names="" + +ftp_server="" +ftp_server_alt_names="" + +# - Services installed on this mashine +# - +postfix_installed=false +dovecot_installed=false +pureftpd_installed=false +apache_installed=true +nginx_installed=false + + +# --- +# --- DANE +# --- + +# - Notice: +# - Supporting Dane is a little bit complex, because befor providing +# - the new certificates on a service, a TLSA record must be present. So +# - access to the nameserver within the permissions to generate the TLSA +# - records AND reload the concerning zone is required. + +# - Dane supported hostname:port +# - +# - Space seperated list of entries : +# - +# - example: +# - dane_hosts_port="\${mail_server}:25 \${mail_server}:587 webmail.so36.net:443" +# - +dane_records="" + +# - (Primary) Nameserver which supports Dane Records +# - +dane_nameserver="" + +# - How to access the namesever via ssh? +# - +# - Note: +# - Take care to provies an entry for nameservers dane_ssh_user in his +# - authorized_keys file. +# - +# - The ssh-key must not have a password! Otherwise this script will +# - not work! +# - +dane_ssh_user="manage-bind" +dane_ssh_port=22 +dane_ssh_key=/root/.ssh/id_rsa-dehydrated + + +# - Scripts used on nameserver. +# - +# - Note: +# - The scripts must be accessable by dane_ssh_user. This +# - ca be realised by adding a concerning entry into sudo file +# - +# - for example: +# - manage-bind ALL=(root)NOPASSWD:/usr/local/bin/bind_* +# - +set_new_serial_script=bind_set_new_serial.sh +renew_tlsa_record=bind_set_renew_tlsa.sh +get_domain_by_hostname=bind_get_domain_by_hostname.sh + + +# - TTL for Records "IN TLSA 3 1 1" and "IN TLSA 2 1 1" +# - +ttl_311=360 +ttl_211=360 + + +# - Logfile where the certificate generation process, called from dehydrated +# - script, will write down the results. This logfile will be evaluated +# - afterwords to do some post generation tasks (in case of success) or inform +# - about errors. +# - +# - Logging in that file and evaluatiog the results will happen in any case, +# - even if variable LOGGING is set to true +# - +_logfile="/tmp/dehydrated-\`date +%Y-%m-%d-%H%M\`.log" +EOF + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + fi +fi + +echononl " Create directory \"${DH_BASE_DIR}/cron\".." +if [[ ! -d "${DH_BASE_DIR}/cron" ]] ; then + mkdir $DH_BASE_DIR/cron > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +else + echo_skipped +fi + +if [[ -f "$DH_CRON_SCRIPT" ]]; then + echononl " Backup cron script \"$DH_CRON_SCRIPT\".." + cp -a $DH_CRON_SCRIPT ${DH_CRON_SCRIPT}.$_date + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +fi + + +echononl " Install Cron Script \"$DH_CRON_SCRIPT\".." +cat << EOF > $DH_CRON_SCRIPT +#!/usr/bin/env bash + +## ------------------------------------------------------------------------------ +## --- All Configurations will be done in /etc/dehydrated/dehydrated_cronjob.conf +## ------------------------------------------------------------------------------ + +if [[ -f "/etc/dehydrated/dehydrated_cron.conf" ]]; then + source /etc/dehydrated/dehydrated_cron.conf +else + echo + echo -e " [ Error ]: No Configuration File found. Exiting now!" + echo + exit 1 +fi + +## --- some functions +## --- +echononl(){ + echo X\\\\c > /tmp/shprompt\$\$ + if [ \`wc -c /tmp/shprompt\$\$ | awk '{print \$1}'\` -eq 1 ]; then + echo -e -n "\$*\\\\c" 1>&2 + else + echo -e -n "\$*" 1>&2 + fi + rm /tmp/shprompt\$\$ +} + +fatal(){ + echo "" + echo -e "fataler Fehler: \$*" + echo "" + echo -e "\t\033[31m\033[1mInstalllation wird abgebrochen\033[m\033[m" + echo "" + exit 1 +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: \$*" + echo "" +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: \$*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: \$*" + echo "" +} + +echo_done() { + echo -e "\033[75G[ \033[32mdone\033[m ]" +} +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_warning() { + echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +## - Check if a given array (parameter 2) contains a given string (parameter 1) +## - +containsElement () { + local e + for e in "\${@:2}"; do [[ "\$e" == "\$1" ]] && return 0; done + return 1 +} + +## --- +## --- END: functions + + +# - Set log_level +# - +# - Test whether stdout (file descriptor 1) is a terminal or not (e.g. cron +# - or if you pipe the output to some other program) +# +if [[ -t 1 ]] ; then + case "\$log_level" in + 1) + verbose=true + ;; + *) + verbose=false + ;; + esac +else + verbose=false +fi + + +# --- +# --- Some checks +# --- + +if [[ ! -d "\$script_dir" ]] ; then + if \$verbose ; then + error "Dehydrated script directors \\"\$script_dir\\" not found!" + else + echo -e "\\n [ Error ]: Dehydrated script directors \\"\$script_dir\\" not found! \\n" + fi + exit 99 +fi + +dehydrated_script=\${script_dir}/dehydrated +if [[ ! -f "\$dehydrated_script" ]] ; then + if \$verbose ; then + error "Dehydrated script \"\$dehydrated_script\" not found!" + else + echo -e "\\n [ Error ]: Dehydrated script \"\$dehydrated_script\" not found! \\n" + fi + exit 99 +fi +if [[ ! -x "\$dehydrated_script" ]] ; then + if \$verbose ; then + error "Dehydrated script \"\$dehydrated_script\" is not executable!" + else + echo -e "\\n [ Error ]: Dehydrated script \"\$dehydrated_script\" is not executable! \\n" + fi + exit 99 +fi + +if [[ ! -f "\$domains_txt" ]] ; then + if [[ ! -d \`dirname \$domains_txt\` ]] ; then + if \$verbose ; then + error "File (domains.txt) with domain names not found!" + else + echo -e "\\n [ Error ]: File (domains.txt) with domain names not found! \\n" + fi + exit 99 + fi +fi + +if [[ -n "\$dane_records" ]] ; then + + if [[ -z \$set_new_serial_script ]];then + if \$verbose ; then + error "Script to set new serial not given! (var: set_new_serial_script)" + else + echo -e "\\n [ Error ]: Script to set new serial not given! (var: set_new_serial_script) \\n" + fi + exit 99 + fi + + if [[ -z \$renew_tlsa_record ]];then + if \$verbose ; then + error "Script to set/renew TLSA record not given! (var: renew_tlsa_record)" + else + echo -e "\\n [ Error ]: Script to set/renew TLSA record not given! (var: renew_tlsa_record) \\n" + fi + exit 99 + fi + + ssh -q -p \$dane_ssh_port \\ + -o BatchMode=yes \\ + -o StrictHostKeyChecking=no \\ + -i \$dane_ssh_key \\ + \$dane_ssh_user@\$dane_nameserver "ls" > /dev/null 2>&1 + if [[ \$? -gt 0 ]] ;then + if \$verbose ; then + error "Nameserver \"\$nameserver\" is not reachable vis ssh!" + else + echo -e "\\n [ Error ]: Nameserver \"\$nameserver\" is not reachable vis ssh! \\n" + fi + exit 99 + fi + ssh -q -p \$dane_ssh_port \\ + -o BatchMode=yes \\ + -o StrictHostKeyChecking=no \\ + -i \$dane_ssh_key \\ + \$dane_ssh_user@\$dane_nameserver "sudo \$set_new_serial_script check > /dev/null 2>&1" + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Failed to acces script \"\$set_new_serial_script\" on host \"\$dane_nameserver\"!" + else + echo -e "\\n Failed to acces script \"\$set_new_serial_script\" on host \"\$dane_nameserver\"! \\n" + fi + exit 99 + fi + ssh -q -p \$dane_ssh_port \\ + -o BatchMode=yes \\ + -o StrictHostKeyChecking=no \\ + -i \$dane_ssh_key \\ + \$dane_ssh_user@\$dane_nameserver "sudo \$renew_tlsa_record check > /dev/null 2>&1" + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Failed to acces script \"\$renew_tlsa_record\" on host \"\$dane_nameserver\"!" + else + echo -e "\\n Failed to acces script \"\$renew_tlsa_record\" on host \"\$dane_nameserver\"! \\n" + fi + exit 99 + fi +fi + + +\$verbose && echo "" + + +# --- +# --- Prepare dehydrated +# --- + +# - Add maill server name and, if present, concerning alternative names +# - to domains.txt +# - +if [[ -n "\$mail_server" ]]; then + \$verbose && echononl " Add mail server name (with alternative names) to domains.txt" + if grep -e "^\s*\$mail_server" \$domains_txt > /dev/null 2>&1 ; then + if \$verbose ; then + echo_skipped + fi + else + echo "\$mail_server \$mail_server_alt_names" >> \$domains_txt + if [[ \$? -eq 0 ]]; then + \$verbose && echo_ok + else + \$verbose && echo_failed + if \$verbose ; then + error "Adding mailserver name to \"domains.txt\" failed!" + else + echo -e "\\n [ Error ]: Adding mailserver name to \"domains.txt\" failed! \\n" + fi + fi + fi +fi + + +# - Add ftp server name and, if present, concerning alternative names +# - to domains.txt +# - +if [[ -n "\$ftp_server" ]]; then + \$verbose && echononl " Add ftpserver name (with alternative names) to domains.txt" + if grep -e "^\s*\$ftp_server" \$domains_txt > /dev/null 2>&1 ; then + if \$verbose ; then + echo_skipped + fi + else + echo "\$ftp_server \$ftp_server_alt_names" >> \$domains_txt + if [[ \$? -eq 0 ]]; then + \$verbose && echo_ok + else + \$verbose && echo_failed + if \$verbose ; then + error "Adding ftpserver name to \"domains.txt\" failed!" + else + echo -e "\\n [ Error ]: Adding ftpserver name to \"domains.txt\" failed! \\n" + fi + fi + fi +fi + + + + +# --- +# --- Certificate Generation +# --- + + +# - Invoke the dehydrated script to generate th let's encrypt certificates if +# - needed. +# - +\$verbose && echononl " Invoking the main dehydrated script for certificate generation.." +\$dehydrated_script -c -g > \$_logfile 2>&1 +ret_val=\$? +if [[ \$ret_val -eq 0 ]]; then + \$verbose && echo_ok +else + \$verbose && echo_failed + if \$verbose ; then + error "Dehydrated Script returns with error (\$ret_val)!" + else + echo -e "\\n [ Error ]: Dehydrated Script returns with error (\$ret_val)! \\n" + fi + echo "" + echo "Error message from dehydrated script:" + echo "=====================================" + echo "" + cat \$_logfile + echo "" + exit 10 +fi + +if grep -i "error:" \$_logfile > /dev/null 2>&1 ; then + if \$verbose ; then + warn "Maybe not all certificates are generated. See Output below." + else + echo -e "\\n [ Warn ]: Mayby not all certificates are generated! See Output below." + fi +fi + + + +# --- +# --- Postgeneration Tasks +# --- + +# - Set defaults +restart_postfix=false +restart_dovecot=false +restart_pureftpd=false +restart_apache=false +restart_nginx=false + +declare -a zone_to_reload_arr + +declare -a _tmp_arr +certs_updated=false +_dane_hosts="\$(cat \$_logfile | grep "Certificate has been produced" 2> /dev/null | awk '{print\$3}')" +# - Evaluate dehydrated's output - see if certificates where created +# - +if grep -i "Hook:" \$_logfile > /dev/null 2>&1 ; then + + certs_updated=true + + for hostname in \$_dane_hosts ; do + + # --- + # - Services to restart after changing/adding the certificate + # --- + if [ -n "\$mail_server" -a "\$hostname" = "\$mail_server" ]; then + \$postfix_installed && restart_postfix=true + \$dovecot_installed && restart_dovecot=true + fi + if [ -n "\$ftp_server" -a "\$hostname" = "\$ftp_server" ]; then + \$pureftpd_installed && restart_pureftpd=true + fi + if \$apache_installed ; then + restart_apache=true + elif \$nginx_installed ; then + restart_nginx=true + fi + + # --- + # - Dane + # --- + + # - See if dane is enabled for that certificate + [[ -n "\$dane_records" ]] || continue + + if [[ "\$dane_records" =~ "\$hostname" ]]; then + \$verbose && echo "" + \$verbose && info "Going to provide TLSA record for \$hostname" + fi + + CUR_IFS=\$IFS + for _entry in \$dane_records ; do + unset _tmp_arr + IFS=':' + _tmp_arr=(\$_entry) + IFS=\$CUR_IFS + if [[ "\$hostname" = "\${_tmp_arr[0]}" ]];then + + # - Get Zone (domain) for given host + # - + \$verbose && echononl " Get Zone containing \"\$hostname\\".." + domain=\$(ssh -q -p \$dane_ssh_port -i \$dane_ssh_key \${dane_ssh_user}@\${dane_nameserver} \\ + "sudo \$get_domain_by_hostname \$hostname" + ) + if [[ -n "\$domain" ]]; then + \$verbose && echo_ok + else + \$verbose && echo_failed + if \$verbose ; then + error "Getting Zone for \"\$hostname\\" failed!" + else + echo -e "\\n [ Error ]: Getting Zone for \"\$hostname\\" failed! \\n" + fi + continue + fi + + + # - Generate TLSA 3 1 1 record + # - + \$verbose && echononl " Generate \"TLSA 3 1 1\\" record from certificate (\${_tmp_arr[0]}).." + tlsa_record_311=\$( + printf "_%s._tcp.%s. \$ttl_311 IN TLSA 3 1 1 %s\\n" \\ + \${_tmp_arr[1]} \\ + \${_tmp_arr[0]} \\ + \$(openssl x509 -in /var/lib/dehydrated/certs/\${_tmp_arr[0]}/cert.pem -noout -pubkey | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + hexdump -ve '/1 "%02x"') + ) + if [[ \$? -eq 0 ]] ; then + \$verbose && echo_ok + else + \$verbose && echo_failed + if \$verbose ; then + error "Generating \"TLSA 3 1 1\\" record failed! " + else + echo -e "\\n [ Error ]: Generating \"TLSA 3 1 1\\" record failed! \\n" + fi + continue + fi + + # - Add/Renew Record in concerning zone file + # - + \$verbose && echononl " Add/Renew Record in concerning zone file.." + ssh -q -p \$dane_ssh_port -i \$dane_ssh_key \${dane_ssh_user}@\${dane_nameserver} \\ + "sudo \$renew_tlsa_record \$tlsa_record_311 > /dev/null 2>&1" + ret_val=\$? + case \$ret_val in + 0) + \$verbose && echo_skipped + if \$verbose ; then + info "TLSA 3 1 1 record for \\"\$hostname\\" is up to date." + else + echo -e "\\n [ Info ]: TLSA 3 1 1 record for \\"\$hostname\\" is up to date.\\n" + fi + ;; + 1) + \$verbose && echo_ok + if \$verbose ; then + info "TLSA 3 1 1 record for \\"\$hostname\\" replaced." + else + echo -e "\\n [ Info ]: TLSA 3 1 1 record for \\"\$hostname\\" replaced.\\n" + fi + ;; + 2) + \$verbose && echo_ok + if \$verbose ; then + info "New TLSA 3 1 1 record for \\"\$hostname\\" added." + else + echo -e "\\n [ Info ]: New TLSA 3 1 1 record for \\"\$hostname\\" added.\\n" + fi + ;; + 10) + \$verbose && echo_failed + if \$verbose ; then + error "Invalid TLSA record given!" + else + echo -e "\\n [ Error ]: Invalid TLSA record given! \\n" + fi + continue + ;; + 11) + \$verbose && echo_failed + if \$verbose ; then + error "No zonefile for host \\"\$hostname\\" found!" + else + echo -e "\\n [ Error ]: No zonefile for host \\"\$hostname\\" found! \\n" + fi + ;; + 20) + \$verbose && echo_failed + if \$verbose ; then + error "Replacing TLSA 3 1 1 record for host \\"\$hostname\\" failed!" + else + echo -e "\\n [ Error ]: Replacing TLSA 3 1 1 record for host \\"\$hostname\\" failed! \\n" + fi + continue + ;; + 21) + \$verbose && echo_failed + if \$verbose ; then + error "Adding TLSA 3 1 1 record for host \\"\$hostname\\" failed!" + else + echo -e "\\n [ Error ]: Adding TLSA 3 1 1 record for host \\"\$hostname\\" failed! \\n" + fi + continue + ;; + 99) + \$verbose && echo_failed + if \$verbose ; then + error "Fatal Error!" + else + echo -e "\\n [ Error ]: Fatal Error! \\n" + fi + continue + ;; + *) + \$verbose && echo_failed + if \$verbose ; then + error "Unknown exit code from remote script \\"\$renew_tlsa_record\"!" + else + echo -e "\\n [ Error ]: Unknown exit code from remote script \\"\$renew_tlsa_record\! \\n" + fi + continue + ;; + + esac + + # - Generate TLSA 2 1 1 record + # - + \$verbose && echononl " Generate \\"TLSA 2 1 1\\" record from root certificate (root.ca).." + tlsa_record_211=\$( + printf "_%s._tcp.%s. \$ttl_211 IN TLSA 2 1 1 %s\\n" \\ + \${_tmp_arr[1]} \\ + \${_tmp_arr[0]} \\ + \$(openssl x509 -in /var/lib/dehydrated/certs/\${_tmp_arr[0]}/chain.pem -noout -pubkey | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + hexdump -ve '/1 "%02x"') + ) + if [[ \$? -eq 0 ]] ; then + \$verbose && echo_ok + else + \$verbose && echo_failed + if \$verbose ; then + error "Generating \\"TLSA 2 1 1\\" record failed! " + else + echo -e "\\n [ Error ]: Generating \\"TLSA 2 1 1\\" record failed! \\n" + fi + continue + fi + + # - Add/Renew Record in concerning zone file + # - + \$verbose && echononl " Add/Renew Record in concerning zone file.." + ssh -q -p \$dane_ssh_port -i \$dane_ssh_key \${dane_ssh_user}@\${dane_nameserver} \\ + "sudo \$renew_tlsa_record \$tlsa_record_211 > /dev/null 2>&1" + ret_val=\$? + case \$ret_val in + 0) + \$verbose && echo_skipped + if \$verbose ; then + info "TLSA 2 1 1 record for \\"\$hostname\\" is up to date." + else + echo -e "\\n [ Info ]: TLSA 2 1 1 record for \\"\$hostname\\" is up to date.\\n" + fi + ;; + 1) + \$verbose && echo_ok + if \$verbose ; then + info "TLSA 2 1 1 record for \\"\$hostname\\" replaced." + else + echo -e "\\n [ Info ]: TLSA 2 1 1 record for \\"\$hostname\\" replaced.\\n" + fi + ;; + 2) + \$verbose && echo_ok + if \$verbose ; then + info "New TLSA 2 1 1 record for \\"\$hostname\\" added." + else + echo -e "\\n [ Info ]: New TLSA 2 1 1 record for \\"\$hostname\\" added.\\n" + fi + ;; + 10) + \$verbose && echo_failed + if \$verbose ; then + error "Invalid TLSA record given!" + else + echo -e "\\n [ Error ]: Invalid TLSA record given! \\n" + fi + continue + ;; + 11) + \$verbose && echo_failed + if \$verbose ; then + error "No zonefile for host \\"\$hostname\\" found!" + else + echo -e "\\n [ Error ]: No zonefile for host \\"\$hostname\\" found! \\n" + fi + continue + ;; + 20) + \$verbose && echo_failed + if \$verbose ; then + error "Replacing TLSA 2 1 1 record for host \\"\$hostname\\" failed!" + else + echo -e "\\n [ Error ]: Replacing TLSA 2 1 1 record for host \\"\$hostname\\" failed! \\n" + fi + continue + ;; + 21) + \$verbose && echo_failed + if \$verbose ; then + error "Adding TLSA 2 1 1 record for host \\"\$hostname\\" failed!" + else + echo -e "\\n [ Error ]: Adding TLSA 2 1 1 record for host \\"\$hostname\\" failed! \\n" + fi + continue + ;; + 99) + \$verbose && echo_failed + if \$verbose ; then + error "Fatal Error!" + else + echo -e "\\n [ Error ]: Fatal Error! \\n" + fi + continue + ;; + *) + \$verbose && echo_failed + if \$verbose ; then + error "Unknown exit code from remote script \\"\$renew_tlsa_record\"!" + else + echo -e "\\n [ Error ]: Unknown exit code from remote script \\"\$renew_tlsa_record\"! \\n" + fi + continue + ;; + + esac + + # - To avoid multiple reloading og one and the same zone, we only + # - collect the zones, having to reload, at this time and do the + # - reloading later. + if ! containsElement \$domain \${zone_to_reload_arr[@]} ; then + zone_to_reload_arr+=("\$domain") + fi + + fi + done + + done + +fi + + +# - Nothing to do if al is up tp date +# - +if ! \$certs_updated ; then + + if \$verbose ; then + info "All Certificates are up to date." + fi + + if \$verbose || grep -i "error:" \$_logfile > /dev/null 2>&1 ; then + echo "" + echo "" + echo "Output message from dehydrated script:" + echo "======================================" + echo "" + cat \$_logfile + echo "" + fi + + rm -f \$_logfile + + \$verbose && echo "" + exit 0 +fi + + +# - Reload zones if needed (updated certificate which supports +# - TLSA Record) +# - +if [[ \${#zone_to_reload_arr[@]} -gt 0 ]] ; then + + for _zone in \${zone_to_reload_arr[@]} ; do + + # - Increase serial in concerning zone file and reload zone + # - + \$verbose && echononl " Set new serial for zone \\"\$_zone\\" and also reload zone.." + ssh -q -p \$dane_ssh_port -i \$dane_ssh_key \${dane_ssh_user}@\${dane_nameserver} \\ + "sudo \$set_new_serial_script \$_zone > /dev/null 2>&1" + ret_val=\$? + case \$ret_val in + 0) + \$verbose && echo_ok + if \$verbose ; then + info "Serial is replaced and Zone is reloaded (\$_zone)." + else + echo -e "\\n [ Info ]: Serial is replaced and Zone is reloaded (\$_zone).\\n" + fi + ;; + 10) + \$verbose && echo_failed + if \$verbose ; then + error "Invalid Hostname/Domain given!" + else + echo -e "\\n [ Error ]: Invalid Hostname/Domain given! \\n" + fi + ;; + 11) + \$verbose && echo_failed + if \$verbose ; then + error "No zonefile found!" + else + echo -e "\\n [ Error ]: No zonefile found! \\n" + fi + ;; + 12) + \$verbose && echo_failed + if \$verbose ; then + error "Determin new Serial failed!" + else + echo -e "\\n [ Error ]: Determin new Serial failed! \\n" + fi + ;; + 13) + \$verbose && echo_failed + if \$verbose ; then + error "Increasing serial failed!" + else + echo -e "\\n [ Error ]: Increasing serial failed! \\n" + fi + ;; + 14) + \$verbose && echo_failed + if \$verbose ; then + error "Reloading Zone failed!" + else + echo -e "\\n [ Error ]: Reloading Zone failed! \\n" + fi + ;; + 15) + \$verbose && echo_failed + if \$verbose ; then + error "Hostname/Domain not supported!" + else + echo -e "\\n [ Error ]: Hostname/Domain not supported! \\n" + fi + ;; + 99) + \$verbose && echo_failed + if \$verbose ; then + error "Fatal Error!" + else + echo -e "\\n [ Error ]: Fatal Error! \\n" + fi + continue + ;; + *) + \$verbose && echo_failed + if \$verbose ; then + error "Unknown exit code from remote script \\"\$set_new_serial_script \$_zone\"!" + else + echo -e "\\n [ Error ]: Unknown exit code from remote script \\"\$set_new_serial_script \$_zone\"! \\n" + fi + continue + ;; + + esac + done +fi + + +# --- +# --- Restart Services +# --- + +if \$restart_apache ; then + if [[ -x "$apache_control_script" ]]; then + $apache_control_script graceful > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting Apache Webserver failed!" + else + echo -e "\\n [ Error ]: Restarting Apache Webserver failed! \\n" + fi + else + if \$verbose ; then + info "Apache Webserver restarted." + else + echo -e "\\n [ Info ]: Apache Webserver restarted.\\n" + fi + fi + else + if \$verbose ; then + warn "Apache Control Script (apachectl) not found. Take care to restart webservice manually" + else + echo -e "\\n [ Warn ]: Apache Control Script (apachectl) not found. Take care to restart webservice manually" + fi + fi +fi +if \$restart_postfix ; then + /etc/init.d/postfix reload > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting Postfix failed!" + else + echo -e "\\n [ Error ]: Restarting Postfix failed! \\n" + fi + else + if \$verbose ; then + info "Postfix service restarted." + else + echo -e "\\n [ Info ]: Postfix service restarted.\\n" + fi + fi +fi +if \$restart_dovecot ; then + /etc/init.d/dovecot restart > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting Dovecot Service failed!" + else + echo -e "\\n [ Error ]: Restarting Dovecot Service failed! \\n" + fi + else + if \$verbose ; then + info "Dovecot service restarted." + else + echo -e "\\n [ Info ]: Dovecot service restarted.\\n" + fi + fi +fi +if \$restart_pureftpd ; then + /etc/init.d/pure-ftpd restart > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting PureFTP Server failed!" + else + echo -e "\\n [ Error ]: Restarting PureFTP Server failed! \\n" + fi + else + if \$verbose ; then + info "PureFTP Server restarted." + else + echo -e "\\n [ Info ]: PureFTP Server restarted.\\n" + fi + fi +fi +if \$restart_nginx ; then + /etc/init.d/nginx restart > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting Nginx Webserver failed!" + else + echo -e "\\n [ Error ]: Restarting Nginx Webserver failed! \\n" + fi + else + if \$verbose ; then + info "Nginx Webserver restarted." + else + echo -e "\\n [ Info ]: Nginx Webserver restarted.\\n" + fi + fi +fi + + +if \$verbose || grep -i "error:" \$_logfile > /dev/null 2>&1 || grep -i "done\!" \$_logfile > /dev/null 2>&1; then + + echo "" + echo "" + echo "Output message from dehydrated script:" + echo "======================================" + echo "" + cat \$_logfile + echo "" +fi + +rm -f \$_logfile + +\$verbose && echo "" +exit 0 +EOF +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed +fi + +echononl " Make Cron Script \"$DH_CRON_SCRIPT executable.." +chmod 700 $DH_CRON_SCRIPT +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed +fi + +if [[ -f "${DH_CRON_SCRIPT}.$_date" ]] ; then + diff $DH_CRON_SCRIPT ${DH_CRON_SCRIPT}.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + echo_ok + info "Cronjob has not changed.\n\t Removing previously created backup.." + rm -f ${DH_CRON_SCRIPT}.$_date + fi +fi + + +if [[ "$DH_CRON_TYPE" = "user" ]]; then + echononl " Activate dehydrated cronjob for root user .." + _success=true + _cur_cron=`mktemp` + [[ $? -ne 0 ]] && _success=false + crontab -u root -l > $_cur_cron + [[ $? -ne 0 ]] && _success=false + if ! $success ; then + echo_failed + else + if grep `basename $DH_CRON_SCRIPT` $_cur_cron > /dev/null 2>&1 ; then + echo_skipped + info "Cronjob already activated." + else + _success=true + cat <> $_cur_cron + +# - Generate/Renew Let's Encrypt Certificates if needed (using dehydrated script) +# - +23 05 * * * $DH_CRON_SCRIPT +EOF + [[ $? -ne 0 ]] && _success=false + crontab -u root $_cur_cron + [[ $? -ne 0 ]] && _success=false + if $success ; then + echo_ok + else + echo_failed + fi + fi # if grep `basename $DH_CRON_SCRIPT` + fi # if ! $success ; then + rm -f $_cur_cron + + # - Check, whether a system based dehydrated cron exists in /etc/cron.d + # - + if grep -l `basename $DH_CRON_SCRIPT` /etc/cron.d/* > /dev/null 2>&1 ; then + warn "A system based dehydrated cronjob already exists in /etc/cron.d/\n\t Deleteing this one now.." + _success=true + echononl " Delete dehydrated cronjob in /etc/cron.d/.." + for _file in $(grep -l `basename $DH_CRON_SCRIPT` /etc/cron.d/*) ; do + rm -f $_file > /dev/null 2>&1 + [[ $? -ne 0 ]] && _success=false + done + if $success ; then + echo_ok + else + echo_failed + fi + + fi # if grep `basename $DH_CRON_SCRIPT` /etc/cron.d/* + +elif [[ "$DH_CRON_TYPE" = "system" ]]; then + echononl " Activate dehydrated cronjob in /etc/cron.d/" + if grep `basename $DH_CRON_SCRIPT` /etc/cron.d/* > /dev/null 2>&1 ; then + echo_skipped + info "Cronjob already activated." + else + cat < /etc/cron.d/dehydrated + +# - Generate/Renew Let's Encrypt Certificates if needed (using dehydrated script) +# - +21 05 * * * root $DH_CRON_SCRIPT +EOF + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_skipped + fi + fi # if grep `basename $DH_CRON_SCRIPT` + + # - Check, whether a dehydrated cron job exists for user root + # - + if crontab -l | grep `basename $DH_CRON_SCRIPT` > /dev/null 2>&1 ; then + warn "Dehydrated cronjob is also activated for root user.\n\t Deleting now.." + echononl " Delete dehydrated cronjob for user root.." + _success=true + _cur_cron=`mktemp` + [[ $? -ne 0 ]] && _success=false + crontab -u root -l > $_cur_cron + [[ $? -ne 0 ]] && _success=false + sed -i "/`basename $DH_CRON_SCRIPT`/d" $_cur_cron > /dev/null 2>&1 + [[ $? -ne 0 ]] && _success=false + crontab -u root $_cur_cron + [[ $? -ne 0 ]] && _success=false + if $success ; then + echo_ok + else + echo_failed + fi + + fi # if crontab -l | grep `basename $DH_CRON_SCRIPT` + +fi + + +echononl " Create directory ${DH_BASE_DIR}/tools (if not exists)." +if [[ ! -d "${DH_BASE_DIR}/tools" ]] ; then + mkdir ${DH_BASE_DIR}/tools > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +else + echo_skipped +fi + + +# - Sript create_domains_file.sh +# +if [[ -f "${DH_BASE_DIR}/tools/create_domains_file.sh" ]]; then + + # - Backup existing script create_domains_file.sh + # - + echononl " Backup ${DH_BASE_DIR}/tools/create_domains_file.sh.." + cp -a ${DH_BASE_DIR}/tools/create_domains_file.sh ${DH_BASE_DIR}/tools/create_domains_file.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +fi + + +echononl " Install script \"create_domains_file.sh\" into ${DH_BASE_DIR}/tools/" +cat < ${DH_BASE_DIR}/tools/create_domains_file.sh +#!/usr/bin/env bash + +# - Creates "domains.txt" die for dehydrated script (let's encrypt) +# - + +_DH_BASE_DIR=$DH_BASE_DIR +_APACHE_VHOST_DIR=$APACHE_VHOST_DIR + + +# ------------- +# --- Some functions +# ------------- +echononl(){ + echo X\\\\c > /tmp/shprompt\$\$ + if [ \`wc -c /tmp/shprompt\$\$ | awk '{print \$1}'\` -eq 1 ]; then + echo -e -n "\$*\\\\c" 1>&2 + else + echo -e -n "\$*" 1>&2 + fi + rm /tmp/shprompt\$\$ +} + +fatal(){ + echo "" + echo -e "fataler Fehler: \$*" + echo "" + echo -e "\t\033[31m\033[1mInstalllation wird abgebrochen\033[m\033[m" + echo "" + exit 1 +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: \$*" + echo "" +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: \$*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: \$*" + echo "" +} + +echo_done() { + echo -e "\033[75G[ \033[32mdone\033[m ]" +} +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_warning() { + echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +## - Check if a given array (parameter 2) contains a given string (parameter 1) +## - +containsElement () { + local e + for e in "\${@:2}"; do [[ "\$e" == "\$1" ]] && return 0; done + return 1 +} + + +clear +echo "" +echo -e "\033[21G\033[32mGets ServerName and ServerAlias(es) from Apache VHost Configuration\033[m" +echo -e "\033[21G\033[32mand adde them to dehydrated's \"domains.txt\" file\033[m" +echo "" + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH Base directory" +echo "" +echo "" +DH_BASE_DIR= +while [[ "X\$DH_BASE_DIR" = "X" ]]; do + echononl "DH Configuration Directory [\$_DH_BASE_DIR]: " + read DH_BASE_DIR + if [[ "X\$DH_BASE_DIR" = "X" ]]; then + DH_BASE_DIR=\$_DH_BASE_DIR + fi +done +SCRIPT_INSTALL_DIR=\${DH_BASE_DIR}/tools +DOMAINS_TXT_FILE=\${DH_BASE_DIR}/domains.txt + + +if [[ ! -d "\$_APACHE_VHOST_DIR" ]]; then + if [[ -d "/etc/apache2/sites-available" ]]; then + _APACHE_VHOST_DIR=/etc/apache2/conf-available + elif [[ -d "/usr/local/apache2/conf/vhosts" ]]; then + if [[ -d "/usr/local/apache2/conf/vhosts/0" ]]; then + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts/0 + else + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts + fi + fi +fi + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert VHost Directory" +echo "" +echo "" +APACHE_VHOST_DIR= +while [[ "X\$APACHE_VHOST_DIR" = "X" ]]; do + echononl "VHost Directory [\$_APACHE_VHOST_DIR]: " + read APACHE_VHOST_DIR + if [ "X\$APACHE_VHOST_DIR" = "Xnone" -o "X\$APACHE_VHOST_DIR" = "XNone" ]; then + warn "Webserver Configuration will be ommited" + APACHE_VHOST_DIR= + break + elif [[ "X\$APACHE_VHOST_DIR" = "X" ]]; then + APACHE_VHOST_DIR=\$_APACHE_VHOST_DIR + fi +done + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Sites without creating a certificate for" +echo "" +echo "Insert only siteames configured as \"ServerName\" (NOT ServerAlias) in VHost configuration" +echo "" +echo "Insert a blank seperated list of names or leave empty for none." +echo "" +SITES_WITHOUT_CERTS= +echononl "Sites without certificate: " +read SITES_WITHOUT_CERTS + +echo "" +echo "" + +echo -e "#\033[32m --------------------\033[m" +echo -e "#\033[32m --- Starts Script the following Parameters \033[m" +echo -e "#\033[32m --------------------\033[m" +echo "" +echo "DH Base Directory.............: \$DH_BASE_DIR" +echo "DH domains.txt file...........: \$DOMAINS_TXT_FILE" +echo "" +echo "Script Installation Directory.: \${DH_BASE_DIR}/tools" +echo "" +echo "Vhosts Directory..............: \$APACHE_VHOST_DIR" +echo "" +echo "Sites without Certificate.....: \$SITES_WITHOUT_CERTS" +echo "" + +echononl "Start with that configuration? [yes/no]: " +read OK +while [ "X\$OK" != "Xyes" -a "X\$OK" != "XYes" -a "X\$OK" != "XNo" -a "X\$OK" != "Xno" ] +do + echononl "wrong entry! [yes/no] :" + read OK +done +[ \$OK = "Yes" -o \$OK = "yes" ] || fatal "Change parameters and restart script: \`basename \$0\`" + + +declare -a SITE_WITHOUT_CERTS_arr +for _site in \$SITES_WITHOUT_CERTS ; do + SITE_WITHOUT_CERTS_arr+=("\${_site}") +done + +_date=\`date +%Y-%m-%d-%H%M\` +echo "" + + +echononl " Save existing file \`basename \$DOMAINS_TXT_FILE\`" +if [[ -f "\$DOMAINS_TXT_FILE" ]]; then + cp -a \$DOMAINS_TXT_FILE \$DOMAINS_TXT_FILE.\`date +%Y-%m-%d-%H%M\` + if [[ \$? = 0 ]]; then + echo_ok + else + echo_failed + fi +fi + +echo "" +declare -a vhost_file_arr +echononl " Create Array of VHost configuration files.." +while IFS='' read -r -d '' filename ; do + if [[ \`basename \$filename\` =~ ^00 ]] ; then + continue + elif ! grep SSLCertificate \$filename > /dev/null 2>&1 ; then + continue + + # - Internationalized domain names (starting with xn--) not yet supported + # - + elif grep -e "^\s*ServerName.*xn--" \$filename > /dev/null 2>&1 ; then + continue + elif grep -e "^\s*ServerAlias.*xn--" \$filename > /dev/null 2>&1 ; then + continue + + elif containsElement \`basename \${filename%.*}\` \${SITE_WITHOUT_CERTS_arr[@]} ; then + continue + else + vhost_file_arr+=("\`realpath \$filename\`") + fi + +done < <(find \$APACHE_VHOST_DIR -mindepth 1 -maxdepth 1 -name "*.conf" -print0) +if [[ \$? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + + +declare -i number_errors=0 + +for file in \${vhost_file_arr[@]} ; do + #echo \$file + _server_name="" + _server_name_failed=false + + # - Create empty array + _server_aliases_arr=() + _server_alias="" + + number_errors=0 + + echo "" + echononl " Get ServerName/ServerAlias from file \"\`basename \$file\`\"" + while read line ; do + + # - Get ServerName + if echo \$line | grep -e "^\s*ServerName" > /dev/null ; then + _server_name_tmp=\`echo \$line | awk '{print\$2}'\` + if [ -z "\$_server_name" ]; then + _server_name=\$_server_name_tmp + elif [ "\$_server_name" != "\$_server_name_tmp" ]; then + if [ \$number_errors -eq 0 ]; then + echo "[ Error ]: Misconfigured ServerName in file \"\$file\"" + fi + #error "Misconfigured ServerName" + _server_name_failed=true + let number_errors++ + fi + continue + fi + + containsElement \$_server_name \${SITE_WITHOUT_CERTS_arr[@]} && continue + + # - Get ServerAlias + if echo \$line | grep -e "^\s*ServerAlias" > /dev/null ; then + _server_alias_tmp=\`echo \$line | awk '{print\$2}'\` + if [ \${#_server_aliases_arr[@]} -eq 0 ] ; then + for _alias in \$_server_alias_tmp ; do + _server_aliases_arr+=("\$_alias") + done + else + for _alias in \$_server_alias_tmp ; do + containsElement "\$_alias" "\${_server_aliases_arr[@]}" && continue + _server_aliases_arr+=("\$_alias") + done + fi + continue + fi + + done < \$file + + if [[ \$number_errors -eq 0 ]]; then + echo_ok + else + echo_done + fi + + echononl " Add Servernames to \"\$DOMAINS_TXT_FILE\".." + if ! grep -e "^\$_server_name" \$DOMAINS_TXT_FILE > /dev/null 2>&1 ; then + echo "\$_server_name \${_server_aliases_arr[@]}" >> \$DOMAINS_TXT_FILE + if [[ \$? -eq 0 ]] ; then + echo_ok + else + echo_done + fi + else + echo_skipped + fi + +done + +if [[ -f "\${DOMAINS_TXT_FILE}.\$_date" ]]; then + diff \$DOMAINS_TXT_FILE \${DOMAINS_TXT_FILE}.\$_date > /dev/null 2>&1 + if [[ \$? -eq 0 ]] ; then + info "\$DOMAINS_TXT_FILE was already up to date.\n\t Deleting previously created backup.." + rm -f \${DOMAINS_TXT_FILE}.\$_date + else + echo "" + echononl "Sort \$DOMAINS_TXT_FILE.." + _success=true + _tmpfile=\`mktemp\` + if [[ \$? -eq 0 ]]; then + cat \$DOMAINS_TXT_FILE | sort -u > \$_tmpfile + [[ \$? -ne 0 ]] && _success=false + cp -a \$_tmpfile \$DOMAINS_TXT_FILE + [[ \$? -ne 0 ]] && _success=false + if \$_success ; then + echo_ok + else + echo_failed + fi + else + echo_skipped + warn "\$DOMAINS_TXT_FILE left unsorted.." + fi + fi +fi + +echo +exit 0 +EOF +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +echononl " Make \"${DH_BASE_DIR}/tools/create_domains_file.sh\" executable.." +chmod 755 ${DH_BASE_DIR}/tools/create_domains_file.sh +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +if [[ -f "${DH_BASE_DIR}/tools/create_domains_file.sh.$_date" ]]; then + diff ${DH_BASE_DIR}/tools/create_domains_file.sh ${DH_BASE_DIR}/tools/create_domains_file.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + info "Script create_domains_file.sh has not change.\n\t Removing previously created backup.." + echononl " Remove ${DH_BASE_DIR}/tools/create_domains_file.sh.$_date.." + rm -f ${DH_BASE_DIR}/tools/create_domains_file.sh.$_date + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi + fi +fi + + +# - Sript change_ssl_directives.sh +# +if [[ -f "${DH_BASE_DIR}/tools/change_ssl_directives.sh" ]]; then + + # - Backup existing script change_ssl_directives.sh + # - + echononl " Backup ${DH_BASE_DIR}/tools/change_ssl_directives.sh.." + cp -a ${DH_BASE_DIR}/tools/change_ssl_directives.sh ${DH_BASE_DIR}/tools/change_ssl_directives.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +fi + + +echononl " Install script \"change_ssl_directives.sh\" into ${DH_BASE_DIR}/tools/" +cat < ${DH_BASE_DIR}/tools/change_ssl_directives.sh +#!/usr/bin/env bash + +# - Changes "SSLCertificate.."-lines in vhost configuration + +_DH_BASE_DIR=$DH_BASE_DIR +_APACHE_VHOST_DIR=$APACHE_VHOST_DIR + +_apache_debian_install=false + +# ------------- +# --- Some functions +# ------------- +echononl(){ + echo X\\\\c > /tmp/shprompt\$\$ + if [ \`wc -c /tmp/shprompt\$\$ | awk '{print \$1}'\` -eq 1 ]; then + echo -e -n "\$*\\\\c" 1>&2 + else + echo -e -n "\$*" 1>&2 + fi + rm /tmp/shprompt\$\$ +} + +fatal(){ + echo "" + echo -e "fataler Fehler: \$*" + echo "" + echo -e "\t\033[31m\033[1mInstalllation wird abgebrochen\033[m\033[m" + echo "" + exit 1 +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: \$*" + echo "" +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: \$*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: \$*" + echo "" +} + +echo_done() { + echo -e "\033[75G[ \033[32mdone\033[m ]" +} +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_warning() { + echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +## - Check if a given array (parameter 2) contains a given string (parameter 1) +## - +containsElement () { + local e + for e in "\${@:2}"; do [[ "\$e" == "\$1" ]] && return 0; done + return 1 +} + + +clear +echo "" +echo -e "\033[21G\033[32mChanges SSLCertificate directives at apache vhost configurations\033[m" +echo -e "\033[21G\033[32mto their appropriate path to Let's Encrypt Certificate/Key.\033[m" +echo "" + + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert DH Base directory" +echo "" +echo "" +DH_BASE_DIR= +while [[ "X\$DH_BASE_DIR" = "X" ]]; do + echononl "DH Configuration Directory [\$_DH_BASE_DIR]: " + read DH_BASE_DIR + if [[ "X\$DH_BASE_DIR" = "X" ]]; then + DH_BASE_DIR=\$_DH_BASE_DIR + fi +done +SCRIPT_INSTALL_DIR=\${DH_BASE_DIR}/tools +DOMAINS_TXT_FILE=\${DH_BASE_DIR}/domains.txt + + +if [[ ! -f "\$DOMAINS_TXT_FILE" ]] ; then + fatal "Domain file \"\`basename \$DOMAINS_TXT_FILE\`\" not found!" +fi + + +if [[ ! -d "\$_APACHE_VHOST_DIR" ]]; then + if [[ -d "/etc/apache2/sites-available" ]]; then + _APACHE_VHOST_DIR=/etc/apache2/conf-available + elif [[ -d "/usr/local/apache2/conf/vhosts" ]]; then + if [[ -d "/usr/local/apache2/conf/vhosts/0" ]]; then + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts/0 + else + _APACHE_VHOST_DIR=/usr/local/apache2/conf/vhosts + fi + fi +fi + +echo "" + +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert VHost Directory" +echo "" +echo "" +APACHE_VHOST_DIR= +while [[ "X\$APACHE_VHOST_DIR" = "X" ]]; do + echononl "VHost Directory [\$_APACHE_VHOST_DIR]: " + read APACHE_VHOST_DIR + if [ "X\$APACHE_VHOST_DIR" = "Xnone" -o "X\$APACHE_VHOST_DIR" = "XNone" ]; then + warn "Webserver Configuration will be ommited" + APACHE_VHOST_DIR= + break + elif [[ "X\$APACHE_VHOST_DIR" = "X" ]]; then + APACHE_VHOST_DIR=\$_APACHE_VHOST_DIR + fi +done + +if [[ ! -d "\$APACHE_VHOST_DIR" ]]; then + fatal "No Apaqche VHost Configuration directory found!" +fi + +echo "" +echo "" + +echo -e "#\033[32m --------------------\033[m" +echo -e "#\033[32m --- Starts Script the following Parameters \033[m" +echo -e "#\033[32m --------------------\033[m" +echo "" +echo "DH Base Directory.............: \$DH_BASE_DIR" +echo "DH domains.txt file...........: \$DOMAINS_TXT_FILE" +echo "" +echo "Script Installation Directory.: \${DH_BASE_DIR}/tools" +echo "" +echo "Vhosts Directory..............: \$APACHE_VHOST_DIR" +echo "" + +echononl "Start with that configuration? [yes/no]: " +read OK +while [ "X\$OK" != "Xyes" -a "X\$OK" != "XYes" -a "X\$OK" != "XNo" -a "X\$OK" != "Xno" ] +do + echononl "wrong entry! [yes/no] :" + read OK +done +[ \$OK = "Yes" -o \$OK = "yes" ] || fatal "Change parameters and restart script: \`basename \$0\`" + +_date=\`date +%Y-%m-%d-%H%M\` + +if [[ "\$APACHE_VHOST_DIR" = "/etc/apache2/sites-enabled" ]] ; then + APACHE_VHOST_DIR="/etc/apache2/sites-available" +fi + +echononl " Backup existing VHost Directory.." +cp -a \$APACHE_VHOST_DIR \${APACHE_VHOST_DIR}.\$_date +if [[ \$? -eq 0 ]] ; then + echo_ok +else + echo_skipped +fi + +echo "" +declare -a vhost_file_arr +while IFS=' ' read -r site_server_name rest ; do + unset vhost_file_arr + while IFS=' ' read filename_site ; do + #if ! \$_apache_debian_install ; then + # [[ -h "\$filename_site" ]] && continue + #fi + if ! containsElement \`realpath \$filename_site\` \${vhost_file_arr[@]} ; then + vhost_file_arr+=("\`realpath \$filename_site\`") + fi + done < <(grep -s -l -E "ServerName\s+\${site_server_name}" \${APACHE_VHOST_DIR}/*) + + echononl " Adjust entries for \"\${site_server_name}\" .." + if [[ \${#vhost_file_arr[@]} -eq 0 ]]; then + echo_failed + warn "No vhost configuration found for \$site_server_name" + else + _cert_dir=\${DH_BASE_DIR}/certs/\${site_server_name} + if [[ ! -h "\${_cert_dir}/fullchain.pem" ]] ; then + echo_skipped + continue + fi + if [[ ! -h "\${_cert_dir}/privkey.pem" ]] ; then + echo_skipped + continue + fi + failed=false + for _name in \${vhost_file_arr[@]} ; do + perl -i -n -p -e s"#^(\s*)SSLCertificateFile.*#\1SSLCertificateFile \${_cert_dir}/fullchain.pem#" \$_name + [[ \$? -ne 0 ]] && failed=true + perl -i -n -p -e s"#^(\s*)SSLCertificateKeyFile.*#\1SSLCertificateKeyFile \${_cert_dir}/privkey.pem#" \$_name + [[ \$? -ne 0 ]] && failed=true + sed -i '/SSLCertificateChainFile/d' \$_name + [[ \$? -ne 0 ]] && failed=true + done + if \$failed ; then + echo_failed + else + echo_ok + fi + fi + +done < <(cat \$DOMAINS_TXT_FILE) + +if [[ -d "\${APACHE_VHOST_DIR}.\$_date" ]]; then + diff -Nur \$APACHE_VHOST_DIR \${APACHE_VHOST_DIR}.\$_date > /dev/null 2>&1 + if [[ \$? -eq 0 ]]; then + info "No VHosts configuration has changed.\n\t Removing previously created backup" + echononl "Delete \"\${APACHE_VHOST_DIR}.\$_date\".." + rm -rf \${APACHE_VHOST_DIR}.\$_date + if [[ \$? -eq 0 ]]; then + echo_ok + else + echo_failed + fi + else + if [[ -x "$apache_control_script" ]]; then + $apache_control_script graceful > /dev/null 2>&1 + if [[ \$? -gt 0 ]]; then + if \$verbose ; then + error "Restarting Apache Webserver failed!" + else + echo -e "\\n [ Error ]: Restarting Apache Webserver failed! \\n" + fi + else + if \$verbose ; then + info "Apache Webserver restarted." + else + echo -e "\\n [ Info ]: Apache Webserver restarted.\\n" + fi + fi + else + if \$verbose ; then + warn "Apache Control Script (apachectl) not found. Take care to restart webservice manually" + else + echo -e "\\n [ Warn ]: Apache Control Script (apachectl) not found. Take care to restart webservice manually" + fi + fi + + fi +fi + +echo +exit +EOF +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +echononl " Make \"${DH_BASE_DIR}/tools/change_ssl_directives.sh\" executable.." +chmod 755 ${DH_BASE_DIR}/tools/change_ssl_directives.sh +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +if [[ -f "${DH_BASE_DIR}/tools/change_ssl_directives.sh.$_date" ]]; then + diff ${DH_BASE_DIR}/tools/change_ssl_directives.sh ${DH_BASE_DIR}/tools/change_ssl_directives.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + info "Script change_ssl_directives.sh has not change.\n\t Removing previously created backup.." + echononl " Remove ${DH_BASE_DIR}/tools/change_ssl_directives.sh.$_date.." + rm -f ${DH_BASE_DIR}/tools/change_ssl_directives.sh.$_date + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi + fi +fi + + +# - Sript dh_tlsgen.sh +# +if [[ -f "${DH_BASE_DIR}/tools/dh_tlsgen.sh" ]]; then + + # - Backup existing script dh_tlsgen.sh + # - + echononl " Backup ${DH_BASE_DIR}/tools/dh_tlsgen.sh.." + cp -a ${DH_BASE_DIR}/tools/dh_tlsgen.sh ${DH_BASE_DIR}/tools/dh_tlsgen.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi +fi + +echononl " Install script \"dh_tlsgen.sh\" into ${DH_BASE_DIR}/tools/" +cat < ${DH_BASE_DIR}/tools/dh_tlsgen.sh +#!/usr/bin/env bash + +if [[ \$# -ne 1 ]] ; then + echo -e "\n usage: \`basename \$0\` \n" + exit +fi + +CUR_IFS=\$IFS +IFS=":" +_tmp_arr=(\$@) +IFS=\$CUR_IFS + +port=\${_tmp_arr[1]} +hostname=\${_tmp_arr[0]} + +cert=/var/lib/dehydrated/certs/\${hostname}/cert.pem +tlsa_record_311=\$( + printf '_%s._tcp.%s. IN TLSA 3 1 1 %s\n' \\ + \$port \\ + \$hostname \\ + \$(openssl x509 -in \$cert -noout -pubkey | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + hexdump -ve '/1 "%02x"') +) + +cert=/var/lib/dehydrated/certs/\${hostname}/chain.pem +tlsa_record_211_chain=\$( + printf '_%s._tcp.%s. IN TLSA 2 1 1 %s\n' \\ + \$port \\ + \$hostname \\ + \$(openssl x509 -in \$cert -noout -pubkey | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + hexdump -ve '/1 "%02x"') +) + +tlsa_record_211_root="" +cert=/var/lib/dehydrated/certs/\${hostname}/root.ca +if [[ -f "\$cert" ]]; then + tlsa_record_211_root=\$( + printf '_%s._tcp.%s. IN TLSA 2 1 1 %s\n' \\ + \$port \\ + \$hostname \\ + \$(openssl x509 -in \$cert -noout -pubkey | + openssl pkey -pubin -outform DER | + openssl dgst -sha256 -binary | + hexdump -ve '/1 "%02x"') + ) +fi + +echo "" +echo "TLSA 3 1 1 certificate" +echo "======================" +echo \$tlsa_record_311 +echo "" +echo "TLSA 2 1 1 chain" +echo "================" +echo \$tlsa_record_211_chain +echo "" +if [[ -n "\$tlsa_record_211_root" ]]; then + echo "TLSA 2 1 1 root" + echo "===============" + echo \$tlsa_record_211_root + echo "" +fi + +exit 0 +EOF +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +echononl " Make \"${DH_BASE_DIR}/tools/dh_tlsgen.sh\" executable.." +chmod 755 ${DH_BASE_DIR}/tools/dh_tlsgen.sh +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed +fi + +if [[ -f "${DH_BASE_DIR}/tools/dh_tlsgen.sh.$_date" ]]; then + diff ${DH_BASE_DIR}/tools/dh_tlsgen.sh ${DH_BASE_DIR}/tools/dh_tlsgen.sh.$_date > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + info "Script dh_tlsgen.sh has not change.\n\t Removing previously created backup.." + echononl " Remove ${DH_BASE_DIR}/tools/dh_tlsgen.sh.$_date.." + rm -f ${DH_BASE_DIR}/tools/dh_tlsgen.sh.$_date + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + fi + fi +fi + +echo "" +exit 0 diff --git a/tools-bind/bind_get_domain_by_hostname.sh b/tools-bind/bind_get_domain_by_hostname.sh new file mode 100755 index 0000000..cb5ef14 --- /dev/null +++ b/tools-bind/bind_get_domain_by_hostname.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +# - Bind configuration file containing zone definitions +# - +ZONE_CONF_FILE=/etc/bind/named.conf.local + +## -- +## -- End: Variable definitions + +## *** +## *** Don't make changes after this line *** +## *** + +## --- some functions +## --- +echononl(){ + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n "$*\\c" 1>&2 + else + echo -e -n "$*" 1>&2 + fi + rm /tmp/shprompt$$ +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: $*" + echo "" +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*" + echo "" +} + +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +## --- +## --- END: functions + +hostname=$1 + +# - Test whether stdout (file descriptor 1) is a terminal or not (e.g. cron +# - or if you pipe the output to some other program) +# +if [[ -t 1 ]] ; then + verbose=true +else + verbose=false +fi + +$verbose && echo "" + + +# - Validate Syntax of given domain +# - +valid_domain_regex="^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$" +$verbose && echononl "\tValidate syntax of given hostname/domain.." +if [[ $hostname =~ $valid_domain_regex ]]; then + if [[ ! $hostname =~ \. ]]; then + $verbose && echo_failed + $verbose && error "Invalid hostname/domain given!" + exit 10 + else + $verbose && echo_ok + fi +else + $verbose && echo_failed + $verbose && error "Invalid hostname/domain given!" + exit 10 +fi + +_failed=false +_hostname=$(echo ${hostname//\./\\.}) +while ! grep -e "$_hostname" $ZONE_CONF_FILE > /dev/null 2>&1 ; do + hostname=${hostname#*.} + _hostname=$(echo ${hostname//\./\\.}) + if [[ ! $_hostname =~ \. ]]; then + _failed=true + break + fi +done + +if $_failed ; then + $verbose && error "hostname \"$1\" not supported by this nameserver!" +else + domain=$hostname + if $verbose ; then + info "Domain: $domain" + else + echo "$domain" + fi +fi + +$verbose && echo +exit 0 + diff --git a/tools-bind/bind_set_new_serial.sh b/tools-bind/bind_set_new_serial.sh new file mode 100755 index 0000000..ffe9838 --- /dev/null +++ b/tools-bind/bind_set_new_serial.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash + +# - Sets new serial and reloads zone +# - +# - Return (Exit) Codes: +# - success: +# - 0: Serial is replaced and Zone is reloaded +# - error: +# - 10: Invalid Hostname/Domain given +# - 15: Hostname/Domain not supported +# - 11: No zonefile found +# - 12: Determin new Serial failed +# - 13: Increasing Serial failed +# - 14: Reloading Zone failed +# - 99: Fatal error +# - +# - usage: ./nd_set_new_serial.sh +# - +# - example: ./nd_set_new_serial.sh a.mx.open.de + +# - + +## -- Variable definitions +## -- + +# - Bind configuration file containing zone definitions +# - +ZONE_CONF_FILE=/etc/bind/named.conf.local + +BIND_USER=bind +BIND_GROUP=bind + +## -- +## -- End: Variable definitions + +## *** +## *** Don't make changes after this line *** +## *** + +## --- some functions +## --- +echononl(){ + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n "$*\\c" 1>&2 + else + echo -e -n "$*" 1>&2 + fi + rm /tmp/shprompt$$ +} + +fatal(){ + echo "" + echo -e "[ \033[31m\033[1mError\033[m ]: $*" + echo "" + echo -e "\t\033[31m\033[1mScript is canceled\033[m\033[m" + echo "" + exit 1 +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[32m\033[1mInfo\033[m ]: $*" + echo "" +} + +ok (){ + echo "" + echo -e "\t[ \033[36m\033[1mOk\033[m ]: $*" + echo "" +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*" + echo "" +} + +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +containsElement () { + local e + for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} + +## --- +## --- END: functions + +# - Parameter "check" can be used, to test whether this script +# - is accessable (e.g. from a script on a remote host) +# - +if [[ "$1" = "check" ]]; then + echo "\$1: $1" + exit 0 +fi + +host_name=$1 + + +echo "" + + +# - Validate Syntax of given domain +# - +valid_domain_regex="^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$" +echononl "\tValidate syntax of given domain.." +if [[ $host_name =~ $valid_domain_regex ]]; then + if [[ ! $host_name =~ \. ]]; then + echo_failed + error "Invalid hostname/domain \"$1\" given!" + exit 10 + else + echo_ok + fi +else + echo_failed + error "Invalid hostname/domain ($1) given!" + exit 10 +fi + + +# - Determin zone (domain) +# - +_failed=false +_host_name=$host_name +_tmp_host_name=$(echo ${_host_name//\./\\.}) +while ! grep -e "$_tmp_host_name" $ZONE_CONF_FILE > /dev/null 2>&1 ; do + _host_name=${_host_name#*.} + _tmp_host_name=$(echo ${_host_name//\./\\.}) + if [[ ! $_tmp_host_name =~ \. ]]; then + _failed=true + break + fi +done + +if $_failed ; then + error "Given hostname/domain \"$1\" not supported by this nameserver!" +else + domain=$_host_name +fi + + +# - Determine zonefile (by reading bind configuration) +# - +_found=false +declare -i _number=0 +regex_zone="^[[:space:]]*zone[[:space:]]+\"$_tmp_host_name\"" +regex_file="^[[:space:]]*file" +while IFS='' read -r line || [[ -n "$line" ]] ; do + if [[ $line =~ $regex_zone ]]; then + _found=true + fi + if $_found ; then + if [[ $line =~ $regex_file ]]; then + zone_file=`echo $line | awk '{print$2}'` + shopt -s extglob + if [[ $zone_file =~ \; ]]; then + zone_file=${zone_file%%*(\;)} + fi + if [[ $zone_file =~ ^\" ]]; then + zone_file=${zone_file##*(\")} + zone_file=${zone_file%%*(\")} + fi + shopt -u extglob + let number++ + break + fi + fi +done < $ZONE_CONF_FILE + +zone_file_dir=`dirname $zone_file` + +if [[ $number -eq 0 ]] ; then + error "No Zonefile (master) found for domain \"$domain\" ." + exit 11 +fi + + +echononl "\tBackup existing directory containg zonefiles.." +if [[ -d "$zone_file_dir" ]] ; then + cp -a $zone_file_dir ${zone_file_dir}.BAK.`date +%Y-%m-%d-%H%M` + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + echo "" + exit 99 + fi +else + echo_failed + error "Zonefile directory not found for domain \"$domain\" ." + exit 99 +fi + + +# - Determin new serial +# - +echononl "\tDetermin new serial.." +_failed=false +declare -i serial_new=`date +%Y%m%d01` +serial_cur=`grep -e "^\s*[0-9]\{10\}" $zone_file | grep serial | awk '{print$1}'` +if [[ $? -gt 0 ]] ; then + _failed=true +fi +while [ ! $serial_new -gt $serial_cur ]; do + let serial_new++ +done +if [[ $? -gt 0 ]] ; then + _failed=true +fi +if $_failed ; then + echo_failed + error "Determin Serial failed!" + exit 12 +else + echo_ok +fi + + +# - Replace serial with the new one +# - +echononl "\tIncrease serial for zone file \"`basename $zone_file`\".." +perl -i -n -p -e "s#^(\s*)\s$serial_cur(.*)#\1 $serial_new\2#" $zone_file > /dev/null 2>&1 +if [[ $? -eq 0 ]]; then + echo_ok +else + echo_failed + error "Increasing Serial failed!" + exit 13 +fi + +echo "" +echononl "\tCorrect Owner for $zone_file .." +chown $BIND_USER:$BIND_GROUP $zone_file +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed + exit 99 +fi +echononl "\tCorrect permissions on $zone_file .." +chmod 644 $zone_file +if [[ $? -eq 0 ]] ; then + echo_ok +else + echo_failed + exit 99 +fi + + +# - Reload Zone +# - +echononl "\tReloading zone \"$domain\".." +rndc reload $domain > /dev/null 2>&1 +if [[ $? -eq 0 ]]; then + echo_ok + info "Serial increased and zone reloaded ($domain)" + exit 0 +else + echo_failed + error "Increasing Serial failed!" + exit 13 +fi + +echo +exit 99 diff --git a/tools-bind/bind_set_renew_tlsa.sh b/tools-bind/bind_set_renew_tlsa.sh new file mode 100755 index 0000000..ad22371 --- /dev/null +++ b/tools-bind/bind_set_renew_tlsa.sh @@ -0,0 +1,351 @@ +#!/usr/bin/env bash + +# - Replaces a TLSA Record +# - +# - Return (Exit) Codes: +# - success: +# - 0: TLSA record is up to date +# - 1: TLSA record replaced +# - 2: New TLSA record written +# - error: +# - 10: Invalid TLSA record given +# - 11: No zonefile for TLSA record found +# - 15: Hostname/Domain not supported +# - 20: Replacing record failed +# 21: Adding Record failed +# - 99: Fatal error +# - +# - example: +# - ./replace_dns_tlsa.sh _25._tcp.mail.initiativenserver.de. IN TLSA 3 1 1 aab3a46b387dd543ed8d... + + +## -- Variable definitions +## -- + +# - Bind configuration file containing zone definitions +# - +ZONE_CONF_FILE=/etc/bind/named.conf.local + +BIND_USER=bind +BIND_GROUP=bind + +## -- +## -- End: Variable definitions + + +## *** +## *** Don't make changes after this line *** +## *** + + +## --- some functions +## --- +echononl(){ + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n "$*\\c" 1>&2 + else + echo -e -n "$*" 1>&2 + fi + rm /tmp/shprompt$$ +} + +warn (){ + echo "" + echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" + echo "" +} + +info (){ + echo "" + echo -e "\t[ \033[33m\033[1mInfo\033[m ]: $*" + echo "" +} + +error(){ + echo "" + echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*" + echo "" +} + +echo_ok() { + echo -e "\033[75G[ \033[32mok\033[m ]" +} +echo_failed(){ + echo -e "\033[75G[ \033[1;31mfailed\033[m ]" +} +echo_skipped() { + echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" +} + +backup_dir () { + dir_to_backup=$1 + echononl "\tBackup existing directory \"$dir_to_backup\" .." + if [[ -d "$dir_to_backup" ]] ; then + cp -a $dir_to_backup ${dir_to_backup}.BAK.`date +%Y-%m-%d-%H%M` + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + echo "" + exit 99 + fi + else + echo_failed + error "Directory \"$dir_to_backup\" not found. No Backup written!" + exit 99 + fi + +} + +## --- +## --- END: functions + +# - Parameter "check" can be used, to test whether this script +# - is accessable (e.g. from a script on a remote host) +# - +if [[ "$1" = "check" ]]; then + echo "\$1: $1" + exit 0 +fi + + +# - Split given Record into an array +# - +declare -a record_arr=($@); +if [[ ${#record_arr[@]} -eq 7 ]]; then + + record_name=${record_arr[0]} + record_ttl="" + record_type="${record_arr[1]} ${record_arr[2]} ${record_arr[3]} ${record_arr[4]} ${record_arr[5]}" + record_hash=${record_arr[6]} + +elif [[ ${#record_arr[@]} -eq 8 ]]; then + + record_name=${record_arr[0]} + record_ttl=${record_arr[1]} + record_type="${record_arr[2]} ${record_arr[3]} ${record_arr[4]} ${record_arr[5]} ${record_arr[6]}" + record_hash=${record_arr[7]} + +else + + error "Invalid TLSA record given!" + exit 10 + +fi + + +# - Split record_name, to get port,protocol,hostnaem,domain +# - +CUR_IFS=$IFS +IFS='\.' +declare -a split_record_name_arr=($record_name) +IFS=$CUR_IFS + +_port=${split_record_name_arr[0]} +port=${_port##*_} +_protocol=${split_record_name_arr[1]} +protocol=${_protocol##*_} + +hostname="${split_record_name_arr[2]}" +declare -i _index=3 +while [[ $_index -lt ${#split_record_name_arr[@]} ]] ; do + hostname="${hostname}.${split_record_name_arr[$_index]}" + let _index++ +done + + +# - Determin zone (domain) +# - +_failed=false +_hostname=$hostname +_tmp_hostname=$(echo ${_hostname//\./\\.}) +while ! grep -e "$_tmp_hostname" $ZONE_CONF_FILE > /dev/null 2>&1 ; do + _hostname=${_hostname#*.} + _tmp_hostname=$(echo ${_hostname//\./\\.}) + if [[ ! $_tmp_hostname =~ \. ]]; then + _failed=true + break + fi +done + +if $_failed ; then + error "Given hostname/domain \"$hostname\" not supported by this nameserver!" + exit 15 +else + domain=$_hostname +fi + + +# - Determine zonefile (by reading bind configuration) +# - +_found=false +declare -i _number=0 +regex_zone="^[[:space:]]*zone[[:space:]]+\"$_tmp_hostname\"" +regex_file="^[[:space:]]*file" +while IFS='' read -r line || [[ -n "$line" ]] ; do + if [[ $line =~ $regex_zone ]]; then + _found=true + fi + if $_found ; then + if [[ $line =~ $regex_file ]]; then + zone_file=`echo $line | awk '{print$2}'` + shopt -s extglob + if [[ $zone_file =~ \; ]]; then + zone_file=${zone_file%%*(\;)} + fi + if [[ $zone_file =~ ^\" ]]; then + zone_file=${zone_file##*(\")} + zone_file=${zone_file%%*(\")} + fi + shopt -u extglob + let number++ + break + fi + fi +done < $ZONE_CONF_FILE + +if [[ $number -eq 0 ]] ; then + error "No Zonefile (master) found for domain \"$domain\" ." + exit 11 +fi + +zone_file_dir=`dirname $zone_file` + +# - Backup existing zone file directory +# - + + +# - Update/Add TLSA recotd if needed +# - +if grep -E "^$record_name.+$record_type" $zone_file > /dev/null 2>&1 ; then + if [[ -n "$record_ttl" ]]; then + search_string="^$record_name\\s+$record_ttl\\s+$record_type" + else + search_string="^$record_name\\s+$record_type" + fi + if grep -E "$search_string" $zone_file | grep $record_hash > /dev/null 2>&1 ; then + info "TLSA record is already up to date.." + echo "" + exit 0 + else + _replac_string=${record_arr[@]} + # - Backup Zone directory + backup_dir $zone_file_dir + + # - Replace TLSA Record + echononl "\tGoing to replace TLSA Record.." + perl -i -n -p -e "s#^${record_name}.+${record_type}.*#$_replac_string#" $zone_file + if [[ $? -eq 0 ]] ; then + echo_ok + echo "" + exit 1 + else + echo_failed + echo "" + exit 20 + fi + fi +else + warn "No Record for replacing fount in zonefile \"`basename $zone_file`\"!" + + declare -i _count + + search_string="^_${port}\._(tcp|udp)\.$hostname" + _count=`grep -Eo "$search_string" $zone_file | wc -l` + + _tlsa_record_found=true + if [[ $_count -eq 0 ]]; then + + search_string="^_[0-9]{1,4}\._(tcp|udp)\.$hostname" + _count=`grep -Eo "$search_string" $zone_file | wc -l` + + if [[ $_count -eq 0 ]]; then + + search_string="^_[0-9]{1,4}\._(tcp|udp).*TLSA" + _count=`grep -Eo "$search_string" $zone_file | wc -l` + + if [[ $_count -eq 0 ]]; then + + _tlsa_record_found=false + + search_string="^[^;].+\s+IN\s+MX" + _count=`grep -Eo "$search_string" $zone_file | wc -l` + + if [[ $_count -eq 0 ]]; then + search_string="^[^;].+\s+IN\s+NS" + _count=`grep -Eo "$search_string" $zone_file | wc -l` + if [[ $_count -eq 0 ]]; then + error "No place for adding a new TLSA record found. Check manually!" + exit 99 + fi + fi + fi + fi + fi + + CUR_IFS=$IFS + IFS='' + _tmpfile=`mktemp` + > $_tmpfile + + # - backup zone directory + backup_dir $zone_file_dir + + # - Add new TLSA record + echononl "\tAdd new TLSA record to zonefile \"\".." + while read -r line || [[ -n "$line" ]]; do + + echo $line >> $_tmpfile + + if echo "$line" | grep -E "$search_string" > /dev/null 2>&1 ; then + let _count-- + fi + if [[ $_count -eq 0 ]]; then + echo "" >> $_tmpfile + if ! $_tlsa_record_found ; then + echo ";" >> $_tmpfile + echo "; DANE" >> $_tmpfile + echo ";" >> $_tmpfile + fi + echo "${record_arr[@]}" >> $_tmpfile + echo "" >> $_tmpfile + _count=-1 + fi + done < "$zone_file" + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + rm $_tmpfile + exit 21 + fi + + IFS=$CUR_IFS + mv $_tmpfile $zone_file + + # - Set Coorect Owner/Permission + echo "" + echononl "\tCorrect Owner for $zone_file .." + chown $BIND_USER:$BIND_GROUP $zone_file + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + exit 99 + fi + echononl "\tCorrect permissions on $zone_file .." + chmod 644 $zone_file + if [[ $? -eq 0 ]] ; then + echo_ok + else + echo_failed + exit 99 + fi + + echo "" + exit 2 +fi + +echo +exit 99