diff --git a/chatmaild/src/chatmaild/config.py b/chatmaild/src/chatmaild/config.py index e6a8886a..8b4aab14 100644 --- a/chatmaild/src/chatmaild/config.py +++ b/chatmaild/src/chatmaild/config.py @@ -44,7 +44,6 @@ class Config: ) self.mtail_address = params.get("mtail_address") self.disable_ipv6 = params.get("disable_ipv6", "false").lower() == "true" - self.noacme = os.environ.get("CHATMAIL_NOACME", "false").lower() == "true" self.addr_v4 = os.environ.get("CHATMAIL_ADDR_V4", "") self.addr_v6 = os.environ.get("CHATMAIL_ADDR_V6", "") self.acme_email = params.get("acme_email", "") diff --git a/docker-compose.override.yaml.example b/docker-compose.override.yaml.example index 6503dc3b..8f612645 100644 --- a/docker-compose.override.yaml.example +++ b/docker-compose.override.yaml.example @@ -5,6 +5,22 @@ # # Volumes listed here are APPENDED to the base file's volumes. # Scalar values (environment, image, etc.) are REPLACED. +# +# --- External TLS certificates --- +# Use when certs are managed outside the container (certbot, acmetool, Traefik, ...). +# Mount the cert files and set TLS_EXTERNAL_CERT_AND_KEY to the in-container paths. +# Changed certs are picked up automatically (inotify via tls-cert-reload.path). +# +# Host acmetool (bare-metal migration): +# volumes: - /var/lib/acme/live:/var/lib/acme/live:ro +# environment: +# TLS_EXTERNAL_CERT_AND_KEY: "/var/lib/acme/live/${MAIL_DOMAIN}/fullchain /var/lib/acme/live/${MAIL_DOMAIN}/privkey" +# +# Traefik certs-dumper (see docker/docker-compose-traefik.yaml): +# volumes: - traefik-certs:/certs:ro +# environment: +# TLS_EXTERNAL_CERT_AND_KEY: "/certs/${MAIL_DOMAIN}/certificate.crt /certs/${MAIL_DOMAIN}/privatekey.key" + services: chatmail: volumes: diff --git a/docker-compose.yaml b/docker-compose.yaml index a53e00fd..6829c621 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -35,7 +35,7 @@ services: CMDEPLOY_STAGES: ${CMDEPLOY_STAGES:-} CHATMAIL_NOSYSCTL: ${CHATMAIL_NOSYSCTL:-True} CHATMAIL_NOPORTCHECK: ${CHATMAIL_NOPORTCHECK:-True} - CHATMAIL_NOACME: ${CHATMAIL_NOACME:-} + TLS_EXTERNAL_CERT_AND_KEY: ${TLS_EXTERNAL_CERT_AND_KEY:-} network_mode: "host" volumes: ## system (required) diff --git a/docker/chatmail_relay.dockerfile b/docker/chatmail_relay.dockerfile index 6a6debe7..4204b5b5 100644 --- a/docker/chatmail_relay.dockerfile +++ b/docker/chatmail_relay.dockerfile @@ -87,12 +87,6 @@ RUN rm -f /etc/nginx/sites-enabled/default COPY --chmod=555 ./docker/files/setup_chatmail_docker.sh /setup_chatmail_docker.sh COPY --chmod=555 ./docker/files/entrypoint.sh /entrypoint.sh -# Certificate monitoring as a proper systemd timer (not a background process) -COPY --chmod=555 ./docker/files/chatmail-certmon.sh /chatmail-certmon.sh -COPY ./docker/files/chatmail-certmon.service /lib/systemd/system/chatmail-certmon.service -COPY ./docker/files/chatmail-certmon.timer /lib/systemd/system/chatmail-certmon.timer -RUN ln -sf /lib/systemd/system/chatmail-certmon.timer /etc/systemd/system/timers.target.wants/chatmail-certmon.timer - HEALTHCHECK --interval=60s --timeout=10s --retries=3 \ CMD systemctl is-active dovecot postfix nginx unbound opendkim filtermail doveauth chatmail-metadata || exit 1 diff --git a/docker/files/chatmail-certmon.service b/docker/files/chatmail-certmon.service deleted file mode 100644 index f89b950f..00000000 --- a/docker/files/chatmail-certmon.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Check TLS certificate changes and reload services -After=setup_chatmail.service - -[Service] -Type=oneshot -ExecStart=/bin/bash /chatmail-certmon.sh -PassEnvironment=MAIL_DOMAIN PATH_TO_SSL diff --git a/docker/files/chatmail-certmon.sh b/docker/files/chatmail-certmon.sh deleted file mode 100644 index 107c169d..00000000 --- a/docker/files/chatmail-certmon.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Check if TLS certificates have changed and reload services if so. -# Called by chatmail-certmon.timer (systemd timer, default every 60s). -set -eo pipefail - -PATH_TO_SSL="${PATH_TO_SSL:-/var/lib/acme/live/${MAIL_DOMAIN}}" -HASH_FILE="/run/chatmail-certmon.hash" - -if [ ! -d "$PATH_TO_SSL" ]; then - exit 0 -fi - -current_hash=$(find "$PATH_TO_SSL" -type f -exec sha1sum {} \; | sort | sha1sum | awk '{print $1}') -previous_hash="" -if [ -f "$HASH_FILE" ]; then - previous_hash=$(cat "$HASH_FILE") -fi - -if [ -n "$current_hash" ] && [ "$current_hash" != "$previous_hash" ]; then - echo "[INFO] Certificate hash changed, reloading nginx, dovecot and postfix." - echo "$current_hash" > "$HASH_FILE" - # On first run (no previous hash), don't reload — services may not be up yet - if [ -n "$previous_hash" ]; then - systemctl reload nginx.service - systemctl reload dovecot.service - systemctl reload postfix.service - fi -fi diff --git a/docker/files/chatmail-certmon.timer b/docker/files/chatmail-certmon.timer deleted file mode 100644 index 8dc5aa8d..00000000 --- a/docker/files/chatmail-certmon.timer +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Periodically check TLS certificate changes - -[Timer] -OnBootSec=120 -OnUnitActiveSec=60 - -[Install] -WantedBy=timers.target diff --git a/docker/files/entrypoint.sh b/docker/files/entrypoint.sh index f6a3fb1f..a1fdddd4 100755 --- a/docker/files/entrypoint.sh +++ b/docker/files/entrypoint.sh @@ -6,7 +6,7 @@ SETUP_CHATMAIL_SERVICE_PATH="${SETUP_CHATMAIL_SERVICE_PATH:-/lib/systemd/system/ # Whitelist only the env vars needed by setup_chatmail_docker.sh. # Forwarding all env vars (via printenv) would leak Docker internals, # orchestrator secrets, and other unrelated variables into systemd. -env_vars="MAIL_DOMAIN CMDEPLOY_STAGES CHATMAIL_INI CHATMAIL_NOSYSCTL CHATMAIL_NOPORTCHECK CHATMAIL_NOACME PATH_TO_SSL PATH" +env_vars="MAIL_DOMAIN CMDEPLOY_STAGES CHATMAIL_INI CHATMAIL_NOSYSCTL CHATMAIL_NOPORTCHECK TLS_EXTERNAL_CERT_AND_KEY PATH" sed -i "s||$env_vars|g" "$SETUP_CHATMAIL_SERVICE_PATH" exec /lib/systemd/systemd "$@" diff --git a/docker/files/setup_chatmail_docker.sh b/docker/files/setup_chatmail_docker.sh index a1f9c82a..54226454 100755 --- a/docker/files/setup_chatmail_docker.sh +++ b/docker/files/setup_chatmail_docker.sh @@ -29,6 +29,13 @@ if [ ! -f "$CHATMAIL_INI" ]; then $CMDEPLOY init --config "$CHATMAIL_INI" "$MAIL_DOMAIN" fi +# Inject external TLS paths from env var (unless user mounted their own ini) +if [ -n "${TLS_EXTERNAL_CERT_AND_KEY:-}" ]; then + if ! grep -q '^tls_external_cert_and_key' "$CHATMAIL_INI"; then + echo "tls_external_cert_and_key = $TLS_EXTERNAL_CERT_AND_KEY" >> "$CHATMAIL_INI" + fi +fi + # --- Deploy fingerprint: skip cmdeploy run if nothing changed --- # On restart with identical image+config, systemd already brings up all # enabled services — the full cmdeploy run is redundant (~30s saved). diff --git a/env.example b/env.example index ba62c93c..1cd99cd5 100644 --- a/env.example +++ b/env.example @@ -3,5 +3,7 @@ MAIL_DOMAIN="chat.example.com" # CMDEPLOY_STAGES - default: "configure,activate". Set to "install,configure,activate" to force full reinstall. # CMDEPLOY_STAGES="configure,activate" -# Skip acmetool when using an external certificate manager (e.g. Traefik, Caddy). -# CHATMAIL_NOACME="True" +# External TLS certificates (e.g. from Traefik, Caddy, certbot on the host). +# Paths refer to locations *inside* the container. +# Mount the cert files via a volume (see docker-compose.override.yaml.example). +# TLS_EXTERNAL_CERT_AND_KEY="/certs/fullchain.pem /certs/privkey.pem"