diff --git a/scripts/21-le-deploy-hook.sh b/scripts/21-le-deploy-hook.sh index 013f2b9..ee5091b 100644 --- a/scripts/21-le-deploy-hook.sh +++ b/scripts/21-le-deploy-hook.sh @@ -4,9 +4,11 @@ source ./lib.sh install -d /etc/letsencrypt/renewal-hooks/deploy -cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK' +# --- 50: Symlink-Hook (setzt stabile /etc/ssl/{ui,webmail,mail}) --- +cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh < ${le_base}" - fi + local host="\$1" target_dir="\$2" + [[ -z "\$host" ]] && return 0 + local le="/etc/letsencrypt/live/\${host}" + local cert="\${le}/fullchain.pem" + local key="\${le}/privkey.pem" + [[ -f "\$cert" && -f "\$key" ]] || return 0 + install -d -m 0755 "\$target_dir" + ln -sf "\$cert" "\${target_dir}/fullchain.pem" + ln -sf "\$key" "\${target_dir}/privkey.pem" + echo "[+] Linked \${target_dir} -> \${le}" } -link_if "$UI_LE" "$UI_SSL_DIR" -link_if "$WEBMAIL_LE" "$WEBMAIL_SSL_DIR" -link_if "$MX_LE" "$MAIL_SSL_DIR" -# Dienste neu laden +link_if "${UI_HOST}" "\${UI_SSL_DIR}" +link_if "${WEBMAIL_HOST}" "\${WEBMAIL_SSL_DIR}" +link_if "${MX_HOST}" "\${MAIL_SSL_DIR}" + systemctl reload nginx || true systemctl reload postfix dovecot || true HOOK - chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh -##!/usr/bin/env bash -#set -euo pipefail -#source ./lib.sh -# -#UI_SSL_DIR="/etc/ssl/ui" -#WEBMAIL_SSL_DIR="/etc/ssl/webmail" -#MAIL_SSL_DIR="/etc/ssl/mail" -# -#UI_HOST="${UI_HOST:-}" -#WEBMAIL_HOST="${WEBMAIL_HOST:-}" -#MX_HOST="${MAIL_HOSTNAME:-}" -# -#install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy -# -## Hook-Datei, die Certbot nach jeder Erneuerung ausführt -#cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK' -##!/usr/bin/env bash -#set -euo pipefail -# -#UI_SSL_DIR="/etc/ssl/ui" -#WEBMAIL_SSL_DIR="/etc/ssl/webmail" -#MAIL_SSL_DIR="/etc/ssl/mail" -# -#UI_HOST="${UI_HOST}" -#WEBMAIL_HOST="${WEBMAIL_HOST}" -#MX_HOST="${MAIL_HOSTNAME}" -# -#link_if() { -# local host="$1" target_dir="$2" -# [[ -z "$host" ]] && return 0 -# local le="/etc/letsencrypt/live/${host}" -# local cert="${le}/fullchain.pem" -# local key="${le}/privkey.pem" -# [[ -f "$cert" && -f "$key" ]] || return 0 -# install -d -m 0755 "$target_dir" -# ln -sf "$cert" "${target_dir}/fullchain.pem" -# ln -sf "$key" "${target_dir}/privkey.pem" -# echo "[+] Linked ${target_dir} -> ${le}" -#} -# -#link_if "$UI_HOST" "$UI_SSL_DIR" -#link_if "$WEBMAIL_HOST" "$WEBMAIL_SSL_DIR" -#link_if "$MX_HOST" "$MAIL_SSL_DIR" -# -#systemctl reload nginx || true -#systemctl reload postfix || true -#systemctl reload dovecot || true -#HOOK -#chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh \ No newline at end of file +# --- 60: TLSA-Hook (bei jedem Renew für MX neu berechnen – falls Key doch rotiert) --- +cat >/etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh < "/etc/mailwolt/dns/\${MX_HOST}.tlsa.txt" + echo "[TLSA] \${TLSA_LINE}" +fi +HOOK +chmod +x /etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh \ No newline at end of file diff --git a/scripts/70-nginx.sh b/scripts/70-nginx.sh index 716209d..72424cc 100644 --- a/scripts/70-nginx.sh +++ b/scripts/70-nginx.sh @@ -201,10 +201,35 @@ CONF echo "}" >> "$outfile" } +build_site_acme_only(){ + local host="$1" outfile="$2" + + cat > "$outfile" <&1 | grep -q http_v2; then -# NGINX_HTTP2_SUFFIX=" http2" -#fi -# -## PHP-FPM Socket oder TCP ermitteln und fastcgi_pass bauen -#detect_php_fpm_sock(){ -# for v in 8.3 8.2 8.1 8.0 7.4; do -# s="/run/php/php${v}-fpm.sock" -# [[ -S "$s" ]] && { echo "unix:${s}"; return; } -# done -# [[ -S "/run/php/php-fpm.sock" ]] && { echo "unix:/run/php/php-fpm.sock"; return; } -# echo "127.0.0.1:9000" -#} -#PHP_FPM_TARGET="$(detect_php_fpm_sock)" -#if [[ "$PHP_FPM_TARGET" == unix:* ]]; then -# FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" -#else -# FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" -#fi -# -## Helper zum Bauen einer Site -## $1=host, $2=cert_dir (/etc/ssl/ui oder /etc/ssl/webmail), $3=outfile -#build_site(){ -# local host="$1" cert_dir="$2" outfile="$3" -# local cert="${cert_dir}/fullchain.pem" -# local key="${cert_dir}/privkey.pem" -# -# cat > "$outfile" <> "$outfile" <<'CONF' -# # DEV: Vite-Proxy -# location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } -# location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } -# location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } -#CONF -# fi -# -# echo "}" >> "$outfile" -#} -# -## Sites erzeugen -#UI_SITE="/etc/nginx/sites-available/ui-mailwolt.conf" -#WEBMAIL_SITE="/etc/nginx/sites-available/webmail-mailwolt.conf" -# -#build_site "$UI_HOST" "/etc/ssl/ui" "$UI_SITE" -#build_site "$WEBMAIL_HOST" "/etc/ssl/webmail" "$WEBMAIL_SITE" -# -#ln -sf "$UI_SITE" "/etc/nginx/sites-enabled/ui-mailwolt.conf" -#ln -sf "$WEBMAIL_SITE" "/etc/nginx/sites-enabled/webmail-mailwolt.conf" -# -## Real-IP nur, wenn Proxy davor -#if [[ "$PROXY_MODE" -eq 1 && -n "$NPM_IP" ]]; then -# cat > /etc/nginx/conf.d/realip.conf </dev/null 2>&1 || true -# systemctl reload nginx || true -#else -# die "nginx -t fehlgeschlagen – siehe /var/log/nginx/*.log" -#fi - -#--- - -##!/usr/bin/env bash -#set -euo pipefail -#source ./lib.sh -# -#log "Nginx konfigurieren …" -# -#ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# -#NGINX_SITE="/etc/nginx/sites-available/${APP_USER}.conf" -#NGINX_SITE_LINK="/etc/nginx/sites-enabled/${APP_USER}.conf" -#ACME_ROOT="/var/www/letsencrypt" -#install -d -m 0755 "$ACME_ROOT" -# -## Default-Sites konsequent entfernen (verhindert doppelten default_server) -#rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true -# -## HTTP/2 prüfen -#NGINX_HTTP2_SUFFIX="" -#if nginx -V 2>&1 | grep -q http_v2; then -# NGINX_HTTP2_SUFFIX=" http2" -#fi -# -## PHP-FPM Socket oder TCP ermitteln und fastcgi_pass bauen -#detect_php_fpm_sock(){ -# for v in 8.3 8.2 8.1 8.0 7.4; do -# s="/run/php/php${v}-fpm.sock" -# [[ -S "$s" ]] && { echo "unix:${s}"; return; } -# done -# [[ -S "/run/php/php-fpm.sock" ]] && { echo "unix:/run/php/php-fpm.sock"; return; } -# echo "127.0.0.1:9000" -#} -#PHP_FPM_TARGET="$(detect_php_fpm_sock)" -#if [[ "$PHP_FPM_TARGET" == unix:* ]]; then -# FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" # << keep the unix: prefix! -#else -# FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" -#fi -# -## Prüfen, ob UI-Zert vorhanden ist -#UI_CERT="/etc/ssl/ui/fullchain.pem" -#UI_KEY="/etc/ssl/ui/privkey.pem" -#SSL_ENABLED=0 -#[[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SSL_ENABLED=1 -# -#TPL="${ROOT_DIR}/config/nginx/site.conf.tmpl" -#[[ -f "$TPL" ]] || die "Nginx-Template fehlt: $TPL" -#render="$(cat "$TPL")" -# -## --------- Bausteine, die in das Template eingesetzt werden --------- -# -## (A) HTTP-Body, wenn KEIN SSL → App direkt über Port 80 -#HTTP_BODY_APP="$(cat <<'HTTP' -# root ${APP_DIR}/public; -# index index.php index.html; -# -# access_log /var/log/nginx/${APP_USER}_access.log; -# error_log /var/log/nginx/${APP_USER}_error.log; -# -# client_max_body_size 25m; -# -# location / { try_files $uri $uri/ /index.php?$query_string; } -# location ~ \.php$ { -# include snippets/fastcgi-php.conf; -# __FASTCGI_PASS__ -# } -# location ^~ /livewire/ { try_files $uri /index.php?$query_string; } -# location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ { expires 30d; access_log off; } -#HTTP -#)" -# -## (B) HTTP-Body, wenn SSL → nur Redirect auf 443 -#HTTP_BODY_REDIRECT='return 301 https://$host$request_uri;' -# -## (C) kompletter SSL-Serverblock (wird nur eingefügt, wenn SSL aktiv) -#SSL_BLOCK="$(cat <<'SSL' -#server { -# listen 443 ssl${NGINX_HTTP2_SUFFIX}; -# listen [::]:443 ssl${NGINX_HTTP2_SUFFIX}; -# server_name _; -# -# ssl_certificate ${UI_CERT}; -# ssl_certificate_key ${UI_KEY}; -# ssl_protocols TLSv1.2 TLSv1.3; -# -# root ${APP_DIR}/public; -# index index.php index.html; -# -# access_log /var/log/nginx/${APP_USER}_ssl_access.log; -# error_log /var/log/nginx/${APP_USER}_ssl_error.log; -# -# client_max_body_size 25m; -# -# location / { try_files $uri $uri/ /index.php?$query_string; } -# location ~ \.php$ { -# include snippets/fastcgi-php.conf; -# __FASTCGI_PASS__ -# } -# location ^~ /livewire/ { try_files $uri /index.php?$query_string; } -# location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ { expires 30d; access_log off; } -# -# # WebSocket: Laravel Reverb -# location /ws/ { -# proxy_http_version 1.1; -# proxy_set_header Upgrade $http_upgrade; -# proxy_set_header Connection "Upgrade"; -# proxy_set_header Host $host; -# proxy_read_timeout 60s; -# proxy_send_timeout 60s; -# proxy_pass http://127.0.0.1:8080/; -# } -# -# # Reverb HTTP API -# location /apps/ { -# proxy_http_version 1.1; -# proxy_set_header Host $host; -# proxy_read_timeout 60s; -# proxy_send_timeout 60s; -# proxy_pass http://127.0.0.1:8080/apps/; -# } -#} -#SSL -#)" -# -## --------- Platzhalter ersetzen --------- -#if [[ $SSL_ENABLED -eq 1 ]]; then -# render="${render/__HTTP_BODY__/$HTTP_BODY_REDIRECT}" -# render="${render/__SSL_SERVER_BLOCK__/$SSL_BLOCK}" -#else -# render="${render/__HTTP_BODY__/$HTTP_BODY_APP}" -# # HTTPS-Block komplett entfernen -# render="${render/__SSL_SERVER_BLOCK__/}" -#fi -# -## Variablen & __FASTCGI_PASS__ im fertigen Render ersetzen -#render="$(echo "$render" \ -# | sed "s|\${APP_DIR}|${APP_DIR}|g; s|\${APP_USER}|${APP_USER}|g; \ -# s|\${UI_CERT}|${UI_CERT}|g; s|\${UI_KEY}|${UI_KEY}|g; \ -# s|\${NGINX_HTTP2_SUFFIX}|${NGINX_HTTP2_SUFFIX}|g; \ -# s|__FASTCGI_PASS__|${FASTCGI_PASS}|g")" -# -## Schreiben/aktivieren -#echo "$render" > "$NGINX_SITE" -#ln -sf "$NGINX_SITE" "$NGINX_SITE_LINK" -# -## Test & reload -#if nginx -t; then -# systemctl enable --now nginx >/dev/null 2>&1 || true -# systemctl reload nginx || true -#else -# die "nginx -t fehlgeschlagen – siehe /var/log/nginx/*.log" -#fi \ No newline at end of file diff --git a/scripts/75-le-issue.sh b/scripts/75-le-issue.sh index 7345210..a2c2a85 100644 --- a/scripts/75-le-issue.sh +++ b/scripts/75-le-issue.sh @@ -16,61 +16,37 @@ issue() { echo "[!] DNS zeigt (noch) nicht auf diese IP – überspringe: ${host}" return 0 fi + + # Für MX den Schlüssel beibehalten, damit TLSA (3 1 1) stabil bleibt + EXTRA_ARGS=() + if [[ "$host" == "$MAIL_HOSTNAME" ]]; then + EXTRA_ARGS+=(--reuse-key) + fi + certbot certonly --agree-tos -m "${LE_EMAIL:-admin@${BASE_DOMAIN}}" \ - --non-interactive --webroot -w "$ACME_WEBROOT" -d "$host" || true + --non-interactive --webroot -w "$ACME_WEBROOT" -d "$host" \ + "${EXTRA_ARGS[@]}" || true } if [[ "$BASE_DOMAIN" != "example.com" ]]; then issue "$UI_HOST" issue "$WEBMAIL_HOST" issue "$MAIL_HOSTNAME" - # Hook verlinkt automatisch; reload nginx: + + # Nginx neu laden (Symlink-Hook verlinkt die neuen Zerts) systemctl reload nginx || true + + # Direkt nach Erst-Ausstellung TLSA für MX einmal erzeugen + MX_CERT="/etc/letsencrypt/live/${MAIL_HOSTNAME}/fullchain.pem" + if [[ -s "$MX_CERT" ]]; then + HASH="$(openssl x509 -in "$MX_CERT" -noout -pubkey \ + | openssl pkey -pubin -outform DER \ + | openssl dgst -sha256 | sed 's/^.*= //')" + TLSA_LINE="_25._tcp.${MAIL_HOSTNAME}. IN TLSA 3 1 1 ${HASH}" + install -d -m 0755 /etc/mailwolt/dns + echo "${TLSA_LINE}" > "/etc/mailwolt/dns/${MAIL_HOSTNAME}.tlsa.txt" + echo "[TLSA] ${TLSA_LINE}" + fi else echo "[i] BASE_DOMAIN=example.com – LE-Ausstellung wird übersprungen." -fi - -##!/usr/bin/env bash -#set -euo pipefail -#source ./lib.sh -# -## Falls du auch UI/Webmail am Backend ausstellen willst, setz diese Flags vor dem Installer: -## ISSUE_UI_CERT=1 ISSUE_WEBMAIL_CERT=1 ./install.sh -#ISSUE_UI_CERT="${ISSUE_UI_CERT:-0}" -#ISSUE_WEBMAIL_CERT="${ISSUE_WEBMAIL_CERT:-0}" -# -#ACME_WEBROOT="/var/www/letsencrypt" -#install -d -m 0755 "$ACME_WEBROOT" -# -## nginx muss bereits laufen (Step 70), und die Location für /.well-known muss existieren. -# -#issue_if_points_here() { -# local host="$1" -# [[ -z "$host" ]] && return 0 -# # prüfe, ob A/AAAA auf unsere erkannte Public IP zeigen -# local want_ip="${SERVER_PUBLIC_IPV4:-$(hostname -I | awk '{print $1}')}" -# local has_ip; has_ip="$(getent ahosts "$host" | awk '{print $1}' | sort -u | head -n1 || true)" -# if [[ "$has_ip" != "$want_ip" ]]; then -# log "DNS von $host zeigt auf $has_ip (nicht $want_ip) – überspringe HTTP-01 hier." -# return 0 -# fi -# -# log "Fordere LE-Zertifikat an für ${host} …" -# certbot certonly --agree-tos \ -# -m "${LE_EMAIL:-admin@${BASE_DOMAIN}}" \ -# --non-interactive \ -# --webroot -w "$ACME_WEBROOT" \ -# -d "$host" || true -#} -# -## In deiner Topologie holt NPM die UI/Webmail-Zerts → hier nur MX -#if [[ "${BASE_DOMAIN}" != "example.com" ]]; then -# issue_if_points_here "${MAIL_HOSTNAME:-}" -# -# # Optional – nur wenn explizit freigegeben: -# [[ "$ISSUE_UI_CERT" = "1" ]] && issue_if_points_here "${UI_HOST:-}" -# [[ "$ISSUE_WEBMAIL_CERT" = "1" ]] && issue_if_points_here "${WEBMAIL_HOST:-}" -#fi -# -## Nach erfolgreicher Ausstellung sofort die stabilen Pfade verlinken (Deploy-Hook nutzen) -#bash /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh || true \ No newline at end of file +fi \ No newline at end of file