diff --git a/deploy/compose/unbound.yaml b/deploy/compose/unbound.yaml index ba3a84d3..9df63e3e 100644 --- a/deploy/compose/unbound.yaml +++ b/deploy/compose/unbound.yaml @@ -7,6 +7,8 @@ services: cache_from: - type=registry,ref=ghcr.io/jeboehm/mailserver-unbound:buildcache restart: on-failure:5 + cap_add: + - NET_BIND_SERVICE security_opt: - no-new-privileges read_only: false diff --git a/deploy/kustomize/unbound/deployment.yaml b/deploy/kustomize/unbound/deployment.yaml index d6e9f238..b543a7ca 100644 --- a/deploy/kustomize/unbound/deployment.yaml +++ b/deploy/kustomize/unbound/deployment.yaml @@ -32,18 +32,20 @@ spec: - secretRef: name: secret-config-env securityContext: - allowPrivilegeEscalation: false - runAsUser: 100 - runAsGroup: 101 capabilities: drop: - ALL + add: + - NET_BIND_SERVICE + - CHOWN + - SETUID + - SETGID ports: - name: dns - containerPort: 5353 + containerPort: 53 protocol: UDP - name: dns-tcp - containerPort: 5353 + containerPort: 53 protocol: TCP livenessProbe: exec: @@ -67,8 +69,5 @@ spec: cpu: 50m memory: 32Mi securityContext: - fsGroup: 101 - fsGroupChangePolicy: OnRootMismatch - runAsNonRoot: true seccompProfile: type: RuntimeDefault diff --git a/deploy/kustomize/unbound/network-policy.yaml b/deploy/kustomize/unbound/network-policy.yaml index 51aac11f..0643225f 100644 --- a/deploy/kustomize/unbound/network-policy.yaml +++ b/deploy/kustomize/unbound/network-policy.yaml @@ -20,6 +20,6 @@ spec: - podSelector: {} ports: - protocol: UDP - port: 5353 # DNS + port: 53 # DNS - protocol: TCP - port: 5353 # DNS TCP + port: 53 # DNS TCP diff --git a/docs/UPGRADE.md b/docs/UPGRADE.md index b2f8744b..997fd3f4 100644 --- a/docs/UPGRADE.md +++ b/docs/UPGRADE.md @@ -4,6 +4,14 @@ Upgrade guide for docker-mailserver. Before upgrading, ensure you have updated `docker-compose.yml` and `docker-compose.production.yml` files. +## v6.0 to v6.1 + +- **Unbound port change and capability requirement (breaking)**: Unbound now listens on port `53` (UDP/TCP) instead of `5353`. + - Compose: the `unbound` service now requires `cap_add: [NET_BIND_SERVICE]` to bind <1024 as non-root. + - Kubernetes: the `unbound` deployment exposes containerPorts `53/TCP` and `53/UDP` and adds the `NET_BIND_SERVICE` capability. + - Rspamd and internal components should use `unbound:53`. Any hardcoded `:5353` must be updated. + - If you previously customized Postfix to use `127.0.0.1:5353`, remove that customization. Postfix and other services should resolve via standard port 53. + ## v5.x to v6.0 Deployment on Kubernetes is now a first class citizen. You can use the `kustomization.yaml` file to deploy the mailserver to your Kubernetes cluster. @@ -21,7 +29,6 @@ The Helm chart has been deprecated and archived. - **Mail Submission**: Mail submission is now only possible on port 587. ### MDA (Mail Delivery Agent) - - **Base Image**: Changed to `dovecot/dovecot`. This image is no longer based on Alpine Linux. - **TLS Certificate Paths**: Updated to `/etc/dovecot/tls/tls.crt` and `/etc/dovecot/tls/tls.key`. A Diffie-Hellman file is no longer required. - **Mail Storage**: Now mounted to `/srv/vmail` instead of `/var/vmail`. diff --git a/target/mta/Dockerfile b/target/mta/Dockerfile index 7de61dbf..f5082420 100644 --- a/target/mta/Dockerfile +++ b/target/mta/Dockerfile @@ -65,6 +65,9 @@ RUN apk --no-cache add \ postconf smtpd_error_sleep_time=10s && \ postconf smtpd_soft_error_limit=3 && \ postconf smtpd_hard_error_limit=5 && \ + postconf postscreen_dnsbl_sites='bl.spamcop.net*2' && \ + postconf postscreen_dnsbl_threshold=2 && \ + postconf postscreen_dnsbl_action=enforce && \ newaliases COPY --from=dockerize /bin/dockerize /usr/local/bin/dockerize COPY rootfs/ / diff --git a/target/mta/rootfs/etc/postfix/master.cf b/target/mta/rootfs/etc/postfix/master.cf index b390f449..2ebe8a77 100644 --- a/target/mta/rootfs/etc/postfix/master.cf +++ b/target/mta/rootfs/etc/postfix/master.cf @@ -1,5 +1,7 @@ -smtp inet n - n - - smtpd -#smtp inet n - n - 1 postscreen +smtp inet n - y - 1 postscreen +smtpd pass - - y - - smtpd +dnsblog unix - - y - 0 dnsblog +tlsproxy unix - - y - 0 tlsproxy pickup unix n - n 60 1 pickup cleanup unix n - n - 0 cleanup qmgr unix n - n 300 1 qmgr diff --git a/target/mta/rootfs/usr/local/bin/init.sh b/target/mta/rootfs/usr/local/bin/init.sh index 905c144e..4c76a352 100755 --- a/target/mta/rootfs/usr/local/bin/init.sh +++ b/target/mta/rootfs/usr/local/bin/init.sh @@ -55,3 +55,23 @@ dockerize \ -template /etc/postfix/mysql-recipient-access.cf.templ:/etc/postfix/mysql-recipient-access.cf \ -template /etc/postfix/mysql-email-submission.cf.templ:/etc/postfix/mysql-email-submission.cf \ /bin/true + +# Configure resolver for Postfix to use $UNBOUND_DNS_ADDRESS +# Accept formats like "host:port" or "ip:port"; default port 53 if omitted +if [ -n "${UNBOUND_DNS_ADDRESS}" ]; then + UNBOUND_DNS_HOST=$(echo "${UNBOUND_DNS_ADDRESS}" | cut -d: -f1) + UNBOUND_DNS_PORT=$(echo "${UNBOUND_DNS_ADDRESS}" | cut -s -d: -f2) + if [ -z "${UNBOUND_DNS_PORT}" ]; then + UNBOUND_DNS_PORT=53 + fi + + # Resolve hostname to IP if necessary + UNBOUND_DNS_IP=$(getent hosts "${UNBOUND_DNS_HOST}" | awk '{print $1}' | head -n1) + if [ -z "${UNBOUND_DNS_IP}" ]; then + UNBOUND_DNS_IP=${UNBOUND_DNS_HOST} + fi + + mkdir -p /var/spool/postfix/etc + echo "nameserver ${UNBOUND_DNS_IP}" > /var/spool/postfix/etc/resolv.conf + # glibc resolv.conf does not support custom port; rely on Unbound standard port 53 +fi diff --git a/target/unbound/Dockerfile b/target/unbound/Dockerfile index c5a9e229..653e7f56 100644 --- a/target/unbound/Dockerfile +++ b/target/unbound/Dockerfile @@ -4,10 +4,8 @@ LABEL maintainer="https://github.com/jeboehm/docker-mailserver" LABEL vendor="https://github.com/jeboehm/docker-mailserver" LABEL de.ressourcenkonflikt.docker-mailserver.autoheal="true" -RUN chown -R unbound:unbound /etc/unbound && \ - apk add --no-cache bind-tools -COPY --chown=unbound:unbound rootfs/ / -USER unbound +RUN apk add --no-cache bind-tools +COPY rootfs/ / -EXPOSE 5353/tcp 5353/udp +EXPOSE 53/tcp 53/udp HEALTHCHECK CMD /usr/local/bin/healthcheck.sh diff --git a/target/unbound/rootfs/etc/unbound/unbound.conf b/target/unbound/rootfs/etc/unbound/unbound.conf index ad94098c..a412aee6 100644 --- a/target/unbound/rootfs/etc/unbound/unbound.conf +++ b/target/unbound/rootfs/etc/unbound/unbound.conf @@ -1,7 +1,7 @@ server: chroot: "" username: "" - port: 5353 + port: 53 trust-anchor-file: "/etc/unbound/root.key" do-ip6: no diff --git a/target/unbound/rootfs/usr/local/bin/healthcheck.sh b/target/unbound/rootfs/usr/local/bin/healthcheck.sh index 469c8b20..e2d54052 100755 --- a/target/unbound/rootfs/usr/local/bin/healthcheck.sh +++ b/target/unbound/rootfs/usr/local/bin/healthcheck.sh @@ -1,18 +1,17 @@ #!/bin/sh set -e -# Test DNS resolution using dig -if ! dig @127.0.0.1 -p 5353 github.com >/dev/null 2>&1; then - echo "Healthcheck failed: DNS resolution test failed" - exit 1 +# UDP check +if ! dig @127.0.0.1 -p 53 github.com +time=2 +tries=1 +short >/dev/null 2>&1; then + echo "Healthcheck failed: dig UDP to 127.0.0.1:53" + exit 1 fi -# Test UDP connectivity (unbound typically uses UDP for DNS) -if ! dig @127.0.0.1 -p 5353 +tcp github.com >/dev/null 2>&1; then - echo "Healthcheck failed: TCP DNS resolution test failed" - exit 1 +# TCP check (no nc dependency) +if ! dig +tcp @127.0.0.1 -p 53 github.com +time=2 +tries=1 +short >/dev/null 2>&1; then + echo "Healthcheck failed: dig TCP to 127.0.0.1:53" + exit 1 fi echo "Healthcheck passed" - exit 0 diff --git a/test/bats/Dockerfile b/test/bats/Dockerfile index 114028f4..ad822714 100644 --- a/test/bats/Dockerfile +++ b/test/bats/Dockerfile @@ -17,8 +17,7 @@ ENV MYSQL_HOST=db \ MDA_IMAPS_ADDRESS=mda:31993 \ MTA_SMTP_ADDRESS=mta:25 \ MTA_SMTP_SUBMISSION_ADDRESS=mta:587 \ - RELAYHOST=false \ - UNBOUND_DNS_ADDRESS=unbound:5353 \ + UNBOUND_DNS_ADDRESS=unbound:53 \ WAITSTART_TIMEOUT=1m \ WEB_HTTP_ADDRESS=web:8080