Docker Mailserver selbst betreiben | ein Erfahrungsbericht
Mithilfe eines geeigneten Docker-Images ist es relativ einfach einen Mailserver selbst zu betreiben. Ursprünglich habe ich den integrierten Mailserver des Host Europe vServers (Plesk) verwendet und bin bei der Suche nach einem Ersatz auf einen sehr einfachen Docker-Container gestoßen. Der schlanke Container liefert einen Mailserver ohne grafische Verwaltungsoberfläche, kann aber mit wenigen einfachen Befehlen verwaltet werden. Für das Senden und Empfangen der Mails kann ein beliebiger E-Mail-Client verwendet werden, dazu wird POP3 oder IMAP für das Empfangen und SMTP für das Versenden angeboten. Ob die Mails des eigenen Servers empfangen und zugestellt werden können, hängt neben dem Setup, von den richtigenDNS-Einstellungenab.
Docker ermöglicht es, Services oder Applikationen per Befehl in einem sogenannten Container zu starten.
Ein Container ist eine vom Betriebssystem (OS) unabhängige isolierte Umgebung:
Beim ersten Start eines Containers, lädt Docker selbstständig alle notwendigen Quellen
aus dem Internet.
Docker kann unter Windows, macOS oder einer Linux-Distribution installiert werden,
siehe auch: Docker
Voraussetzung
Vorab habe ich Docker installiert, siehe: topic/docker und für die SSL-Zertifikate einen Reverse-Proxy im Einsatz: Traefik-Reverse-Proxy.
Mailserver Inbetriebnahme
Als Mailserver habe ich das folgende Docker-Image verwendet: "ghcr.io/docker-mailserver/docker-mailserver:latest".
Für den Start sind 3 Dateien erforderlich:
docker-compose.yml
.env (Die Variablen der .env-Datei könnten auch direkt in der docker-compose.yml-Datei ersetzt werden)
.mailserver.env (Variablen für die Mailserversettings)
Die docker-compose-Datei habe ich wie folgt angelegt:
Zudem verwende ich für die Variablen in der docker-compose.yml-Datei eine eigene Datei: .env, diese enthält die Werte für den Host-, Domain- und Container-Namen:
Damit wäre der Server grundsätzlich einsatzbereit und könnte bereits gestartet werden. Könnte, denn für zusätzliche Einstellungen sollte auch noch kurz die zuletzt heruntergeladene Datei mailserver.env durchgesehen werden und darin benötigte Features aktiviert oder deaktiviert werden. Die Datei ist relativ gut kommentiert und dadurch weitestgehend selbsterklärend. Damit das Zustellen und Empfangen der Mails klappt, müssen zudem entsprechende DNS-Einträge erstellt werden.
Der Start des Containers erfolgt dann mit dem Befehl „docker compose up“.
root@l2:~/mailserver# docker compose up -d
...
Status: Downloaded newer image for mailserver/docker-mailserver:latest
Creating mailserver ... done
Attaching to mailserver
mailserver.env
Folgende Einstellungen habe ich in der mailserver.env – Datei angepasst:
Per Standardeinstellung ist die Größe der Mails auf 10 MB limitiert, durch das Setzen der Variablen „POSTFIX_MESSAGE_SIZE_LIMIT“ kann das Limit erhöht werden.
Administration
Neue E-Mail-Adressen oder Weiterleitungen können mithilfe des Setup-Skripts erstellt werden. Seit Version 10.2 befindet sich das Skript im Docker-Container, entsprechend erfolgt der Aufruf über „docker exec“. Die Hilfe kann wie folgt angezeigt werden:
docker exec mailserver setup help
„mailserver“ steht in dem Befehl für den Namen des Docker-Containers, hier die angezeigte Hilfe für Version 11.2
root@ubuntu:/var/mailserver#docker exec mailserver setup help
SETUP(1)
NAME
setup - 'docker-mailserver' Administration & Configuration script
SYNOPSIS
./setup [ OPTIONS... ] COMMAND [ help | ARGUMENTS... ]
COMMAND := { email | alias | quota | dovecot-master | config | relay | debug } SUBCOMMAND
DESCRIPTION
This is the main administration script that you use for all your interactions with
'docker-mailserver'. Setup, configuration and much more is done with this script.
Please note that this script executes most of its commands inside the container itself.
If it cannot find a running 'docker-mailserver' container, it will attempt to run one using
any available tags which include 'label=org.opencontainers.image.title="docker-mailserver"'
and then run the necessary commands. If the tag for the container is not found, this script
will pull the ':latest' tag of 'docker.io/mailserver/docker-mailserver'.
This tag refers to the latest release, see the tagging convention in the README under:
https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md
You will be able to see detailed information about the script you're invoking and their
arguments by appending 'help' after your command. Currently, this does not work with all scripts.
[SUB]COMMANDS
COMMAND email :=
/usr/local/bin/setup email add <EMAIL ADDRESS> [<PASSWORD>]
/usr/local/bin/setup email update <EMAIL ADDRESS> [<PASSWORD>]
/usr/local/bin/setup email del [ OPTIONS... ] <EMAIL ADDRESS> [ <EMAIL ADDRESS>... ]
/usr/local/bin/setup email restrict <add|del|list> <send|receive> [<EMAIL ADDRESS>]
/usr/local/bin/setup email list
COMMAND alias :=
/usr/local/bin/setup alias add <EMAIL ADDRESS> <RECIPIENT>
/usr/local/bin/setup alias del <EMAIL ADDRESS> <RECIPIENT>
/usr/local/bin/setup alias list
COMMAND quota :=
/usr/local/bin/setup quota set <EMAIL ADDRESS> [<QUOTA>]
/usr/local/bin/setup quota del <EMAIL ADDRESS>
COMMAND dovecot-master :=
/usr/local/bin/setup dovecot-master add <USERNAME> [<PASSWORD>]
/usr/local/bin/setup dovecot-master update <USERNAME> [<PASSWORD>]
/usr/local/bin/setup dovecot-master del [ OPTIONS... ] <USERNAME> [ <USERNAME>... ]
/usr/local/bin/setup dovecot-master list
COMMAND config :=
/usr/local/bin/setup config dkim [ ARGUMENTS... ]
COMMAND relay :=
/usr/local/bin/setup relay add-auth <DOMAIN> <USERNAME> [<PASSWORD>]
/usr/local/bin/setup relay add-domain <DOMAIN> <HOST> [<PORT>]
/usr/local/bin/setup relay exclude-domain <DOMAIN>
COMMAND fail2ban :=
/usr/local/bin/setup fail2ban
/usr/local/bin/setup fail2ban ban <IP>
/usr/local/bin/setup fail2ban unban <IP>
COMMAND debug :=
/usr/local/bin/setup debug fetchmail
/usr/local/bin/setup debug login <COMMANDS>
/usr/local/bin/setup debug show-mail-logs
EXAMPLES
./setup.sh email add test@example.com
Add the email account test@example.com. You will be prompted
to input a password afterwards since no password was supplied.
./setup.sh config dkim keysize 2048 domain 'example.com,not-example.com'
Creates keys of length 2048 but in an LDAP setup where domains are not known to
Postfix by default, so you need to provide them yourself in a comma-separated list.
./setup.sh config dkim help
This will provide you with a detailed explanation on how to use the
config dkim command, showing what arguments can be passed and what they do.
Nach dem ersten Anlegen einer E-Mail-Adresse für eine neue Domain, kann der DKIM-Key für den DNS-Eintrag erstellt werden, siehe DKIM.
Alias anlegen (Weiterleitung)
docker exec mailserver setup alias add email@domain.tld weiterleitung@mail-adresse.ok
DKIM
Nachdem die ersten Mail-Adressen oder Weiterleitungen am System angelegt wurden, können mit folgendem Befehl DKIM Keys erzeugt werden:
docker exec mailserver setup config dkim
Die Keys werden für alle Domänen für die eine E-Mail-Adresse oder Weiterleitung angelegt wurde in folgendem Ordner abgelegt: config/opendkim/keys/domain.tld/mail.txt und sollten für den DNS-Eintrag verwendet werden:
DNS-Einträge
Für den Betrieb eines Mailservers sollte neben dem A- und MX-Record der DKIM-Key, ein SPF-Eintrag und eine DMARC-Policy im DNS erstellt werden. Der Domänenname: domain.tld steht in folgenden Beispielen repräsentativ für die eigene Domäne. Die folgenden Einträge müssen in die DNS-Zone für domain.tld hinzugefügt werden:
A-Record
Type
Name
Value
A
mail.domain.tld
IP-Adresse des Webservers
Als Servername sollte, in der Reverse-Lookup-Zone zudem mail.domain.tld hinterlegt werden, damit die IP-Adresse des Webservers auch auf mail.domain.tld auflöst. Bei einem vServer oder Cloud-Server eines Providers kann der Servername in der Admin-Oberfläche des Providers festgelegt werden.
MX
Type
Name
Value
Priorität
MX
mail.domain.tld
IP-Adresse des Webservers
Der MX-Record ist für das Zustellen der Mails zuständig. Alle Mails die @domain.tld als Domäne haben, werden an diesen Server zugestellt. Bei mehreren Mailservern wird der Server mit der niedrigsten Priorität verwendet.
Der zuvor erstellte DKIM Key:
Type
Name
Value
TXT
mail._domainkey.domain.tld
v=DKIM1; h=sha256; k=rsa; p=Inhalt-von-config/opendkim/keys/domain.tld/mail.txt .... bei mir ohne "
DKIM (DomainKeys Identified Mail) ist eine Signatur, die den Mails hinzugefügt wird, um das Fälschen des Absenders zu erschweren. DKIM wird für das Versenden von Mails verwendet. Sollte der Provider nur 512 Zeichen für dessen DNS-Einträge unterstützen, kann mit folgendem Aufruf ein kürzerer Key erzeugt werden: "docker exec mailserver setup config dkim keysize 2048". Damit die Keys neu ausgestellt werden, müssen die alten Keys im Ordner ..config/opendkim/keys/domain.tld vorher gelöscht und der Mailserver neugestartet werden.
SPF-Eintrag
Type
Name
Value
TXT
@
„v=spf1 ip4:IP-Adresse-des-Webservers -all"
Um die ip4-Einstellung einer anderen Domäne zu verwenden, kann der SPF-Eintrag wie folgt umgeleitet werden. Wichtig ist dabei, dass der Parameter -all nicht verwendet wird:
Type
Name
Value
TXT
@
„v=spf1 redirect=server.domain.tld"
Auch der SPF-Eintrag (Sender Policy Framework) ist eine Maßnahme um das Fälschen von E-Mail-Adressen zu erschweren. Der SPF-Eintrag gibt vor welche Server für das Senden der Emails berechtigt sind, in dem Beispiel die IP-Adresse unseres Mailservers.
Als Setup für SSL-Zertifikate verwende ich Let's Encrypt und einen Reverse Proxy, siehe: Traefik-Reverse-Proxy. Damit die Zertifikatsverwaltung vom Reverse Proxy verwendet werden kann, habe ich das docker-compose.yml-File wie folgt angepasst:
Der Pfad zum nginx-data-Ordner des Webproxy unter „Volumes“ muss natürlich entsprechend angepasst werden. Zudem habe ich, anders als beim originalen docker-compose-File, die anderen Volumes in das Dateisystem umgeleitet. Siehe: Docker Daten speichern: Docker Volumes vs. Host-Ordner
SpamAssassin Spamfilter learn
Um der Flut an Spam-Mails Herr zu werden, habe ich SpamAssassin mit dem sa-learn-Befehl trainiert: SpamAssassin kann neben Blacklists und anderen Spamprüfungen von bestehenden Mails aus der Inbox und dem Junk-Ordner lernen. Der Befehl sa-learn untersucht dazu beide Ordner und merkt sich bestimmte Muster von Spam und Nicht-Spam Mails (Ham). Wird also ein Mail falsch erkannt und dann über den Mail-Client in den Spam-Ordner verschoben, kann SpamAssassin daraus lernen und ein ähnliches Mail beim nächsten Mal ev. gleich in den Spam-Ordner geben.
Der sa-learn-Befehl kann wie folgt im Docker-Container gestartet werden:
Mit Hilfe von Sieve-Filtern ist es möglich bestimmte Regeln auf die Inbox anzuwenden, z. B. bestimmte Absender in einen Ordner zu verschieben. Hier ein Beispiel, meiner in NextCloud verwendeten Filterregeln:
Damit Sieve über die Filterregeln über den Mail-Client geändert werden können, muss der Port 4190 in docker-compose hinterlegt werden.
FetchMail – Mails von anderen Konten holen
Um regelmäßig E-Mails von einem anderen Konto abzuholen, kann in der Datei mailserver.env „ENABLE_FETCHMAIL“ aktiviert werden:
# –––––––––––––––––––––––––––––––––––––––––––––––# ––– Fetchmail Section –––––––––––––––––––––––––# –––––––––––––––––––––––––––––––––––––––––––––––
ENABLE_FETCHMAIL=1
# The interval to fetch mail in seconds
FETCHMAIL_POLL=300
Der Standardwert für das Abholen ist alle 5 Minuten.
Die Kontodaten für das Abholen können dann in folgender Config-Datei hinterlegt werden: docker-data/dms/config/fetchmail.cf
## Example configuration: IMAP
poll imap.domain.tld with proto IMAP
user 'ccc@gmx.at' there with
password 'pwd'
is 'mailboxOnDockerMailserver@domain.tld'
here ssl
Debug - Fail2ban
Sollte der Service Fail2ban aktiviert sein, werden fehlgeschlagene Anmeldeversuche bestraft, in dem der Absender für einige Zeit blockiert wird. Die blockierten IP-Adressen können wie folgt angezeigt und wieder freigegeben werden:
docker exec mailserver setup fail2ban
Banned in dovecot: ???.???.???.???
Banned in postfix-sasl: 60.171.17.185, 61.182.227.245, 117.50.65.112, 116.196.112.146, 119.62.142.225, 113.31.104.89
root@ubuntu-4gb-nbg1-2:/var/web/mailserver# ./setup.sh debug fail2ban unban ???.???.???.???
Unbanned IP from dovecot: ???.???.???.???
root@ubuntu:/var/web/mailserver#
Log-Ausgabe
Wie auch für anderen Docker-Container kann mit „docker logs“ angezeigt werden, was der Container gerade so treibt:
docker logs -f mailserver
Troubleshooting: DNS-Einträge
Policy Reasons
Mail Delivery Subsystem
Returned mail: see transcript for details
This message was created automatically by mail delivery software.
Deny to deliver the message you sent to one or more recipients.
Reasons for deny are as follows:
REASONS: Policy Reasons
RECIPIENTS:
???@???.xx
Mögliche Ursache: fehlender SPF-Record
Domain unbekannt
Ein E-Mail an eine unbekannte Domain wird ca. so vom Mail Delivery Subsystem präsentiert:
Mail Delivery Subsystem
Returned mail: see transcript for details
Undelivered Mail Returned to Sender
This is the mail system at host mail.domain.tld.
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.
For further assistance, please send mail to postmaster.
If you do so, please include this problem report. You can
delete your own text from the attached returned message.
The mail system
<gibts-nicht@ungültige.domain
Zitierter Text
>: Host or domain name not found. Name service error
for name=ungültige.domain type=AAAA: Host not found
Delivery report
postmaster@???.???.???
Delivery report
Hello, this is the mail server on ???.???.???
I am sending you this message to inform you on the delivery status of a
message you previously sent. Immediately below you will find a list of
the affected recipients; also attached is a Delivery Status Notification
(DSN) report in standard format, as well as the headers of the original
message.
<user@???.???.???
Zitierter Text
> delivery failed; will not continue trying
Upgrade
Wie für Docker üblich, können neue Versionen mit einem pull geladen werden:
docker-compose pull
docker compose up -d
Fazit
Zugegeben, die Verwaltung über Befehle im Terminal ist sicher nicht jedermanns Sache. Der Vorteil dieses Setups ist aber, dass sich die Konfiguration und die Daten des kompletten Mailservers dank Docker in einem Ordner befinden und der Server somit ganz einfach auf einen anderen Server kopiert und dort gestartet werden kann, siehe auch: topic/docker
Hey,
leider habe ich ein ähnlichen Problem Tobi.
Ich erhalte Cannot find a valid DOMAIN for '/etc/letsencrypt/live/<DOMAIN>/', ...
Ich habe mich ausschließlich an deine Anleitung gehalten.
Hast du eine Idee?
B
Bernhard
Stimmt der Pfad zu Traefik und dessen Json-File?
- ../traefik/letsencrypt/acme.json:/etc/letsencrypt/acme.json:ro
und existiert die Datei: traefik/letsencrypt/acme.json ? enthält diese die gewünschte Domain?
A
anonym
Die Pfade stimmen, aber die letsencrypt/acme.json is leer.
Wie muss der Inhalt denn aussehen? Das habe ich im Tutorial wohl überlesen
A
anonym
Wobei, es sieht so aus, als ob es keine Datei, sondern ein Ordner ist? (Also die acme.json)?
B
Bernhard
Nein, ist eine Textdatei:
Konnte diese sogar über die Bash des Mailservers auslesen:
root@dockerhost:/var/web/mailserver#docker exec -it mailserver /bin/bash
root@server:/# cat /etc/letsencrypt/acme.json
{
"myresolver": {
"Account": {
...
},
"PrivateKey": ???
...
"Certificates": [
{
"domain": {
"main": "deineDomain.tld"
},
A
anonym
Okay, und die wird automatisch angelegt, wenn ich Traefik starte?
Wenn ich den Mailserver starte gibt es eine Datei in /traefik/letsencrypt/acme.json/mail.domain.tld
Aber die ist leider leer
B
Bernhard
Enthält das docker-compose.yml File für Traefik unter "command:", neben der anderen beschriebenen Zeilen, auch folgende Zeile?
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
B
Bernhard
Bitte den Ordner löschen und eine Datei mit Namen acme.json anlegen ...
A
anonym
Ja genau, die Zeile ist da.
Das Problem ist, dass acme.json irgendwie als Ordner angelegt wird und nicht als Datei.
Und wenn ich dann bspw. den Mailserver starte, wird eine neue Datei angelegt angelegt im besagten Ordner, sprich /letsencrypt/acme.json/mail.Domain.tld
B
Bernhard
· geändert
Ich habe gerade nochmal die Konfig von Traefik kontrolliert: Ich verwende für den Ordner letsencrypt aktuell Bind Mounts und nicht, wie ursprünglich im Beitrag, Volumes.
Die Docker-compose.yaml-Datei für Traefik mountet den Ordner bei mir wie folgt:
...
volumes:
- "./letsencrypt:/letsencrypt"
...
Ich habe die Datei auch in dem Beitrag angepasst: https://www.libe.net/traefik
A
anonym
Okay, also mit der Config wird ja automatisch auf die Domain mail.domain.tld ein Zertfikat gepackt, welches ich auch mit https://crt.sh/ sehen kann -> mail.domain.tld hat also ein gültiges Zertifikat.
Das wird genauso im Treafik-Dashboard angezeigt.
Beim Starten des Mailservers erhalte ich jedoch weiterhin:
[ INF ] Welcome to docker-mailserver 12.1.0
mailserver | [ INF ] Checking configuration
mailserver | [ INF ] Configuring mail server
mailserver | [ ERROR ] Cannot find a valid DOMAIN for '/etc/letsencrypt/live/<DOMAIN>/', tried: 'mail.domain', 'domain.de', 'domain.de' (Hab meine DOmain entfernt)
mailserver | [ ERROR ] _find_letsencrypt_domain | LETSENCRYPT_DOMAIN appears to be misconfigured, please verify.
mailserver | [ ERROR ] Shutting down
mailserver | 2023-08-22 14:13:53,125 WARN received SIGTERM indicating exit request
B
Bernhard
Ich glaube da passt mit dem Pfad zur acme.json was nicht und vermute, dass die Datei im Mailservercontainer nicht verfügbar ist ...
T
Tobi
Hi!
Leider funktioniert das mit Traefik nicht richtig, mir ist aber ehrlich gesagt nicht ganz klar wie bzw. wo ich den Pfad für nginx in der o.g. docker-compose-yml für Traefik ändern soll. Folgende Meldung bekomme ich:
WARN[0000] The "SELINUX_LABEL" variable is not set. Defaulting to a blank string.
network webproxy declared as external, but could not be found
B
Bernhard
Hallo Tobi,
danke für deinen Kommentar:
SELINUX_LABEL war ursprünglich in der .env Datei gesetzt und leer und wird daher nicht benötigt und sollte in der Docker-Compose-Datei auch entfernt werden. Ich habe diesen Beitrag entsprechend angepasst. Sorry für Irreführung.
Bezüglich des Netzwerks "webproxy": Ich verwende für das Let`s Encrypt Setup ein Netzwerk mit Namen webproxy: Wie in dem entsprechenden Artikel auf dieser Seite erwähnt, kann dieses mit dem Befehl: "docker network create webproxy" angelegt werden.
Ich hoffe, die Antwort bringt dich ein Stück weiter ...
T
Tobi
HI Bernhard,
vielen dank für deine Anwort. Zumindest kann ich jetzt den Container starten, allerdings scheitert es nach wie vor an Treafik bzw. den Zertifikat, im Log bekomme ich diese Meldung:
[ ERROR ] Cannot find a valid DOMAIN for '/etc/letsencrypt/live/<DOMAIN>/', tried: '', 'meinedomain.de', 'meinedomain.de'
[ ERROR ] _find_letsencrypt_domain | LETSENCRYPT_DOMAIN appears to be misconfigured, please verify.
[ ERROR ] Shutting down
Ich habe Treafik nach dieser Anleitung konfiguriert (https://goneuland.de/traefik-v2-3-reverse-proxy-mit-crowdsec-im-stack-einrichten/), mit Nextloud, Bitwarden usw. klappt das alles ohne Probleme. Wäre super wenn du mir da helfen könntest, da ich mit den anderen Lösungen nicht klar komme und eine einfachen Mailserver brauche der nicht aufgebläht ist und Resourcen frisst.
Danke schonmal im voraus!
Lg Tobi
B
Bernhard
Auf die Schnelle kann ich nur sehen, dass die beiden Anleitungen etwas voneinander abweichen: goneuland verwendet z.B. "proxy" als Netzwerkname und nicht wie in dem zugehörigen Artikel zu diesem Beitrag: "webproxy" ..., vielleicht versuchst du mal das zugehörige Traefik-Setup zu diesem Artikel: https://www.libe.net/traefik
Fragen / Kommentare