mailcow / mailcow-dockerized

mailcow: dockerized - 🐮 + 🐋 = 💕
https://mailcow.email
GNU General Public License v3.0
8.58k stars 1.15k forks source link

Single domain results in empty sni.map and postfix warnings #5816

Closed SwissOS closed 1 month ago

SwissOS commented 5 months ago

Contribution guidelines

I've found a bug and checked that ...

Description

When using mailcow with a single domain, the postfix.sh script creates an empty sni.map file in the postfix conf directory. This is not the correct solution, as later on, Postfix complains when a TLS connection happens (see log below).
The correct sni.map should contain:
mail.domain.com(=$hostname) /etc/ssl/mail/key.pem /etc/ssl/mail/cert.pem

Logs:

postfix-mailcow-1  | Apr  3 07:30:47 349d44507649 postfix/smtpd[702]: TLS SNI domain.com from e2i340.smtp2go.com[103.2.141.84] not matched, using default chain

Steps to reproduce:

1. Setup mailcow with a single domain/hostname
2. Launch mailcow
3. Wait for a TLS SNI connection and see postfix logs

Which branch are you using?

master

Which architecture are you using?

ARM64 (aarch64)

Operating System:

Ubuntu 22.04 LTS

Server/VM specifications:

16 GB/4 cores

Is Apparmor, SELinux or similar active?

no

Virtualization technology:

none

Docker version:

26.0.0

docker-compose version or docker compose version:

2.25.0

mailcow version:

2024-02

Reverse proxy:

Nginx

Logs of git diff:

not relevant

Logs of iptables -L -vn:

not relevant

Logs of ip6tables -L -vn:

not relevant

Logs of iptables -L -vn -t nat:

not relevant

Logs of ip6tables -L -vn -t nat:

not relevant

DNS check:

172.64.155.249
104.18.32.7
SwissOS commented 5 months ago

I think I have found the problem inside postfix.sh. The logic to write sni.map is incorrect. Here are the original lines:

# create sni configuration
if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
  echo -n "" > /opt/postfix/conf/sni.map
else
  echo -n "" > /opt/postfix/conf/sni.map;
  for cert_dir in /etc/ssl/mail/*/ ; do
    if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
      continue;
    fi
    IFS=" " read -r -a domains <<< "$(cat "${cert_dir}domains")"
    for domain in "${domains[@]}"; do
      echo -n "${domain} ${cert_dir}key.pem ${cert_dir}cert.pem" >> /opt/postfix/conf/sni.map;
      echo "" >> /opt/postfix/conf/sni.map;
    done
  done
fi
postmap -F hash:/opt/postfix/conf/sni.map;

In fact, the logic should be to check first if the files cert.pem and key.pem are present in /etc/ssl (which they should if you follow the documentation to copy those files there in the first place), in which case the sni.map should be a single line consisting of: ${MAILCOW_HOSTNAME} ${cert_dir}key.pem ${cert_dir}cert.pem So, do not check if SKIP_LETS_ENCRYPT is true, but check if those 2 files are there first. The rest for multiple domains should be kept the same.

I am unfortunately not a programmer, and cannot make a PR for those changes, even if they are probably very trivial to most here (sorry!). If someone can write the changes, I can test them. Thank you in advance!

cjwalsh commented 3 months ago

+1 for this issue, it affects my setup where I have mailcow behind a reverse proxy which is responsible for the SSL cert management and the relevant cert files are being mapped into /etc/ssl/mail via the volumes: section for postfix-mailcow in my docker-compose.override.yml.

Note that I have multiple domains in mailcow, but one cert includes them all using the Subject Alternative Name approach, so I don't think this is related to having a single domain for mailcow, I think it's just about SKIP_LETS_ENCRYPT = y assuming that there won't be any cert files for postfix.

milkmaker commented 1 month ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.