Strato-Server mit Ubuntu 20.04

Aus Debacher-Wiki
Wechseln zu:Navigation, Suche

Für den bisherigen Netthelp-Server stand ein Update auf Ubuntu 20.04 an, weil mehrere Anwendungen neuere Software, vor allem bei PHP, erfordern. Eine Neuinstallation eines vorhandenen Gerätes ist mühsam, da es sehr lange dauert, bis die Webseiten und Mails zurück gespielt sind.

Zur Wahl standen Server von Strato oder von Hetzner. Bei Hetzner gibt es sehr günstige neuwertige Server bereits für ca. 40€, wenn man mit dem Serverstandort Finnland einverstanden ist. Beim Serverstandort Deutschland kommt man bei der gleichen Hardware auf 46€.

Letztendlich habe ich mich gegen Hetzner entschieden. In der letzten Zeit kam sehr viel Spam über Hetzner-Server und der Hetzner-Support hat auf meine Beschwerden nur sehr langsam und auch nur manchmal reagiert. Als dann auch mein eigener Hetzner-Server bei Hotmail blockiert war, blieb für mich nur noch Strato übrig.

Inhaltsverzeichnis

Root-Server C4-80 HDD

Intel® Xeon® E3-1230v6 Kaby Lake INTEL    

4 x 3,5 GHz (4C/8T) (max. Turbo: 3,9 GHz) Intel® Hyper-Threading (6999,8 Bogomips) 2 x 4.000 GB HDD (Software RAID 1) 32 GB DDR 4 ECC

Zum Preis von 45€ pro Monat. Die Festplatten sind 33 Monate in Betrieb, gegenüber inzwischen 84 Monaten beim Vorgänger.

Bei der Partionierung hat man nicht viel Auswahl. Ich habe die Version mit der ausführlichsten Partitionierung gewählt und Ubuntu 20.04. Die Partitionsgrößen kann man nicht weiter beeinflussen, das ist etwas schade, da /home in der Regel viel zu groß eingestellt ist. Aber bisher habe ich trotzdem noch nie Platzprobleme bekommen. Knapp eine Stunde später war das Gerät dann erreichbar, fertig partitioniert und mit der 20.04 Grundinstallation:

/dev/md1         20G   /
/dev/md0        968M   /boot
/dev/md2        2,2T   /home
/dev/md3        1,5T   /var


Installationsproblem

Nachdem ich die Konfiguration des Servers abgeschlossen hatte, habe ich den Rechner irgendwann rebootet. Nach diesem Reboot ist der Rechner immer nach relativ kurzer Zeit hängen geblieben. Wie sich später herausstellte immer nach etwa 20 Minuten.

Der Hardwaretest brachte keinen Fehler, auch wenn er deutlich länger lief, als vorher angegeben.

Mir fielen dann in den Logdateien Zeilen auf, die auf einen Suspend hinwiesen, dabei fiel mir dann auch auf, dass die Laufzeit immer gleich lang war.

Nov 12 09:01:00 h2954065 ModemManager[1320]: <info>  [sleep-monitor] system is about to suspend
Nov 12 09:01:00 h2954065 NetworkManager[1213]: <info>  [1636704060.5494] manager: sleep: sleep requested (sleeping: no  enabled: yes)
Nov 12 09:01:00 h2954065 NetworkManager[1213]: <info>  [1636704060.5497] manager: NetworkManager state is now ASLEEP
Nov 12 09:01:00 h2954065 gnome-shell[2214]: Screen lock is locked down, not locking
Nov 12 09:01:00 h2954065 systemd[1]: Reached target Sleep.
Nov 12 09:01:00 h2954065 systemd[1]: Starting Record successful boot for GRUB...
Nov 12 09:01:00 h2954065 systemd[1]: Starting Suspend...
Nov 12 09:01:00 h2954065 systemd-sleep[3037]: Suspending system...
Nov 12 09:01:00 h2954065 kernel: [ 1359.864041] PM: suspend entry (s2idle)

Da ich bei einem Reboot immer die laufenden Dienste vorher und hinterher vergleiche, war mir aufgefallen, dass gdm3 gestartet ist. Mir ist nicht klar, warum gdm3 installiert und gestartet ist, es wurde als Abhängigkeit zu imagemagick automatisch ausgewählt. Das verwundert mich etwas, weil ich imagemagick immer installiere, dieses Problem aber vorher nicht aufgetreten ist.

Ich habe dann, nachdem ich wieder einen Hardreset ausgelöst hatte, den Dienst gdm3 gestoppt

systemctl stop gdm3
systemctl disable gdm3

Zusätzlich habe ich noch das Schlafen des Rechners deaktiviert mittels

systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

Nun scheint der Server im Betrieb zu bleiben.

Bleibt natürlich die Frage, warum auf einem Server gdm3 installiert ist. Bei der Auswertung der Logdateien ist mir aufgefallen, dass das Paket als Abhängigkeit bei imagemagick auftaucht. Das ist auch bei anderen Installationen so. Bleibt nur die Farge, warum der Dienst aktiviert war??

Grundinstallation

Bevor man mit der eigentlichen Einrichtung beginnt, sollte man den Server unbedingt einmal neu starten.

Benötigt man einmal die Liste aller installierten Pakete, so bekommt man die mit

dpkg --get-selections

Leider steht dann immer noch install hinter dem Paketnamen, aber das lässt sich ja mit Suchen und Ersetzen beseitigen.

In den aktuellen Ubuntu-Versionen läuft das Starten und Stoppen der Netzwerkdienste systemctl, der klassische Runlevel-Editor funktioniert da teilweise auch noch, als Umleitung.

systemctl status|start|stop <daemon>.service
systemctl enable|disable <daemon>.service

Am Ende der Installation sollte man daran denken, die Namensauflösung für die Strato-Server IP auf einen eigenen Namen zu setzen und nicht die Voreinstellung zu belassen. Manche Provider lehnen Mails von Absenderadressen ab, wenn noch der Standardname gesetzt ist.

Firewall

Gleich nach der ersten Übersicht ist mir aufgefallen, dass keinerlei Firewall aktiv ist. Bei Ubuntu ist dafür ufw installiert und muss noch aktiviert werden. Bei ufw kann man relativ komfortabel Ports sperren oder freigeben, teilweise sogar über vorkonfigurierte Pakete. Wichtig ist, dass man Port 22 freigibt, bevor man die Firewall aktiviert (https://help.ubuntu.com/lts/serverguide/firewall.html, http://www.savvyadmin.com/ubuntus-ufw/):

ufw allow OpenSSH
ufw allow "Apache Full" 

Wer kommt auf die blöde Idee einen Bezeichner mit Leerzeichen zu wählen?? Nun kann man die Firewall aktivieren mittels:

ufw enable

Würde es die App OpenSSH nicht geben, so würde man Port 22 freigeben mittels:

ufw allow 22

oder

ufw allow ssh

Die Apache App steht erst zur Verfügung, wenn man den Webserver installiert hat. Die Liste der aktuell verfügbaren Apps kann man abrufen mittels:

ufw app list


Um nicht die syslog-Datei mit den ganzen Firewall-Meldungen zu füllen habe ich noch die Datei /etc/rsyslog.d/20-ufw.conf editiert.

# Log kernel generated UFW log messages to file
:msg,contains,"[UFW " /var/log/ufw.log

# Uncomment the following to stop logging anything that matches the last rule.
# Doing this will stop logging kernel generated UFW log messages to the file
# normally containing kern.* messages (eg, /var/log/kern.log)
& stop

Die letzte Zeile beginnt normalerweise mit einem Kommentarzeichen, dass nach Anweisung weg muss. Danach habe ich den rsyslog neu gestartet. An der Stelle ein kleiner Hinweis: möchte man wissen, welche Dienste verfügbar sind, bzw. wie sie genau heißen, so ist

service --status-all

sehr nützlich.

Inzwischen nutze ich ufw auch um lästige IP-Adressbereiche zu blockieren:

ufw deny from 185.222.xxx.0/24 to any

Die Liste aller aktuellen Regeln kann man abfragen mit:

ufw status

Interessant ist auch die Version

ufw status numbered

Hier wird jeder Regel eine Nummer vorangestellt

Status: Aktiv

     Zu                         Aktion      Von
     --                         ------      ---
[ 1] Apache Full                ALLOW IN    Anywhere                  
[ 2] OpenSSH                    ALLOW IN    Anywhere                  
[ 3] mosh                       ALLOW IN    Anywhere                  
[ 4] Postfix                    ALLOW IN    Anywhere                  
[ 5] Dovecot POP3               ALLOW IN    Anywhere                  
[ 6] 21/tcp                     ALLOW IN    Anywhere                  
[ 7] 143                        ALLOW IN    Anywhere                  
[ 8] 993                        ALLOW IN    Anywhere                  
[ 9] 53/udp                     ALLOW IN    Anywhere                  
[10] Dovecot Secure POP3        ALLOW IN    Anywhere                  
[11] 5349/tcp                   ALLOW IN    Anywhere                  
[12] 5349/udp                   ALLOW IN    Anywhere                  
[13] Anywhere                   DENY IN     185.222.xxx.0/24          
[14] Apache Full (v6)           ALLOW IN    Anywhere (v6)             
[15] OpenSSH (v6)               ALLOW IN    Anywhere (v6)             
[16] mosh (v6)                  ALLOW IN    Anywhere (v6)             
[17] Postfix (v6)               ALLOW IN    Anywhere (v6)             
[18] Dovecot POP3 (v6)          ALLOW IN    Anywhere (v6)             
[19] 21/tcp (v6)                ALLOW IN    Anywhere (v6)             
[20] 143 (v6)                   ALLOW IN    Anywhere (v6)             
[21] 993 (v6)                   ALLOW IN    Anywhere (v6)             
[22] 53/udp (v6)                ALLOW IN    Anywhere (v6)             
[23] Dovecot Secure POP3 (v6)   ALLOW IN    Anywhere (v6)             
[24] 5349/tcp (v6)              ALLOW IN    Anywhere (v6)             
[25] 5349/udp (v6)              ALLOW IN    Anywhere (v6)             

Über diese Nummer kann man sie auch wieder löschen

ufw delete 13

würde also obige Regel wieder löschen.

An der Liste sieht man schon, dass man die Reihenfolge berücksichtigen muss. Viele Dienste sind schon erlaubt, bevor die Sperregel kommt. Also besser immer an Position 1 schieben mittels:

ufw insert 1 deny from 185.222.xxx.0/24 to any

Ob sich etwas tut kann man abfragen mittels:

iptables  -v -n -L ufw-user-input

Bei mir sieht das Ergebnis folgendermaßen aus:

Chain ufw-user-input (1 references)
pkts bytes target     prot opt in     out     source               destination         
 100  6000 DROP       all  --  *      *       185.222.xxx.0/24     0.0.0.0/0           
 649 34606 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 /* 'dapp_Apache%20Full' */
  85  4980 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 /* 'dapp_OpenSSH' */
   0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            multiport dports 60000:61000 /* 'dapp_mosh' */
 157  9380 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:25 /* 'dapp_Postfix' */
...

Es wurden also 100 Pakete mit insgesamt 6000 Byte von der Regel blockiert.

Erste Installationen

Ich habe mir dann noch gleich ein paar nützliche Hilfsmittel installiert:

apt install synaptic
apt install apt-xapian-index
update-apt-xapian-index -vf

Installiert das grafische Tool zur Paketverwaltung (nicht wundern, da werden sehr viele Pakete mitinstalliert). Dann kann ich relativ leicht nach Paketen suchen.

Auch grafische Tools lassen sich per ssh aufrufen , wenn man beim Start der Verbindung den Parameter -X mit angibt:

ssh -X root@mein-server.domain

Da ich schon häufiger Probleme mit den Festplatten hatte, sind mir die Smartmontools wichtig:

apt install smartmontools

Der Aufruf von

smartctl -A /dev/sda

bzw.

smartctl -A /dev/sdb

Liefert mir die Information, dass die Festplatten schon 24030 Stunden gelaufen sind, also knapp drei Jahre. Es gibt aber keine Reallocierten Sektoren und beide Platten haben die gleiche Laufzeit.

Im Netzwerk benötigt man dann oft auch whois Informationen

apt install whois net-tools


Datenbank MariaDB

Hinsichtlich der Datenbank möchte ich ausprobieren, ob ich mit MariaDB genauso gut oder gar besser zurecht komme, als mit MySQL. Ich werde also für diese Installation auf MariaDB setzen, was bei Ubuntu 20.04 parallel vorhanden ist. Bei der Einrichtung habe ich mich an https://www.digitalocean.com/community/tutorials/how-to-install-mariadb-on-ubuntu-20-04-de orientiert.

Eigentlich sollte man im Betrieb keinen Unterschied zwischen MySQL und MariaDB feststellen könne, es werden nämlich die gleichen Konsolen-Befehle und die gleichen PHP-Module benutzt, es sollte alles nur ein bisschen performanter sein.

Zur Installation von Server und Client dient der folgende Aufruf:

apt install mariadb-client mariadb-server mariadb-common

Den Datenbankzugriff sollte man nicht über das Netz erlauben, also auch in der Firewall den entsprechenden Port nicht öffnen. Die Datenbank wird ja in der Regel nur über lokale Anwendungen genutzt und über PHPMyAdmin administriert.

Der Benutzer root hat auch bei der MariaDB-Installatio nur von der Konsole aus Zugriff und zwar ohne Passwort, also über die Socket:

MariaDB [(none)]> SELECT user,authentication_string,plugin,host FROM mysql.user;
+----------+-----------------------+-------------+-----------+
| user     | authentication_string | plugin      | host      |
+----------+-----------------------+-------------+-----------+
| root     |                       | unix_socket | localhost |
+----------+-----------------------+-------------+-----------+
1 rows in set (0.000 sec)

Einen zusätzlichen Admin-Benutzer mit Passwort kann man folgendermaßen anlegen

MariaDB [(none)]> GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
Query OK, 0 rows affected (0.001 sec)

Auch dessen Passwort wir bei MariaDB nicht mit angezeigt:

MariaDB [(none)]> SELECT user,authentication_string,plugin,host FROM mysql.user;
+----------+-----------------------+-------------+-----------+
| user     | authentication_string | plugin      | host      |
+----------+-----------------------+-------------+-----------+
| root     |                       | unix_socket | localhost |
| admin    |                       |             | localhost |
+----------+-----------------------+-------------+-----------+
2 rows in set (0.000 sec)

Ich habe dann die Konfigurationsdatei /etc/mysql/mariadb.conf.d/50-server.cnf ergänzt im Abschnitt [mariadb] (ab Zeile 128):

#ergänzt von U.D.
innodb_flush_log_at_trx_commit = 2

und die Datenbank neu gestartet. Den Datenbankzugriff sollte man nicht über das Netz erlauben, also auch in der Firewall den entsprechenden Port nicht öffnen. Die Datenbank wird ja in der Regel nur über lokale Anwendungen genutzt und über PHPMyAdmin administriert.


MariaDB im Zusammenspiel mit phpmyadmin

Bei der Installation von phpmyadmin wurde bei dieser Installation kein neuer Datenbank-User erstellt. Eventuell einfach, weil ich im vorherigen Schritt bereits einen passenden User eingerichtet habe. In der Datei /etc/mysql/debian.cnf sind jedenfalls nur folgende Zeilen zu finden:

# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = root
password = 
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host     = localhost
user     = root
password = 
socket   = /var/run/mysqld/mysqld.sock

Der von mir eingerichtete Benutzer kann sich aber per phpmyadmin anmelden. Man muss also nicht dem Benutzer root den Zugriff ermöglichen. Aus Sicherheitsgründen ist es sowieso sinnvoller einen individuellen Benutzer anzulegen, da oft genug versucht wird sich über phpmyadmin einzuloggen. Ich habe mich auch angewöhnt in der Datei /etc/apache2/conf-available/phpmyadmin.conf die Alias-Zeile auszukommentieren:

#Alias /phpmyadmin /usr/share/phpmyadmin

So kann ich erreichen, dass das Tool nicht allen virtuellen Systemen zur Verfügung steht, sondern die Berechtigung aktiv konfiguriert werden muss.

Apache-Webserver

Der Apache Webserver ist ein enorm umfangreiches Stück Software. Ich hoffe, dass meine Konfiguration einigermaßen sinnvoll ist.

Vorbemerkung

Diese Seite beschreibt die Einrichtung und Konfiguration von virtuellen Servern und beinhaltet:

  • MySQL bzw. MariaDB
  • Apache
  • letsencrypt

Apache-Vhosts.png


Installation

Die folgenden grundlegenden Pakete habe ich installiert

apt install apache2 apache2-bin apache2-data apache2-doc apache2-utils		

Dann ein paar Pakete für PHP

apt install libapache2-mod-php  php php-auth-sasl php-bz2 php-cli php-db php-gd php-geoip php-imap php-log php-mail php-curl php-imagick php-intl
apt install php-mbstring php-mdb2 php-mysql php-net-smtp php-phpseclib php-soap php-tcpdf php7.4-zip phpmyadmin 
apt install php-apcu php7.4-opcache

Da sind eventuell noch Doppelungen drin, sowohl das Paket, als auch das Metapaket. Es ist aber das, was mir

dpkg --get-selections | grep php

lieferte.

Nun noch ein paar Perl-Pakete:

apt install libapache2-mod-perl2 libapache2-reload-perl libapparmor-perl libarchive-zip-perl libauthen-sasl-perl libbsd-resource-perl libcairo-perl
apt install libcgi-fast-perl libfile-basedir-perl libfile-desktopentry-perl libfile-mimeinfo-perl libfont-afm-perl libgd-graph-perl libglib-perl libgtk2-perl
apt install libhtml-form-perl libhtml-format-perl libhtml-template-perl libhttp-daemon-perl libimage-magick-perl libmailtools-perl libnet-dbus-perl libtie-ixhash-perl
apt install libx11-protocol-perl libxml-xpathengine-perl libdbi-perl libdbd-mysql libclass-dbi-mysql-perl

Nach erfolgter Konfiguration (s.u.) darf man nicht vergessen die notwendigen Ports in der Firewall frei zu geben:

ufw allow "Apache Full"

Server-Module

Vorsichtshalber noch einmal ein paar Apache-Module aktivieren:

a2enmod perl
a2enmod cgi
a2enmod expires
a2enmod headers 
a2enmod rewrite
a2enmod ssl
a2dismod status
service apache2 restart

letsencrypt

Für die verschlüsselte Übertragung von Webseiten werden Zertifikate von letsencrypt benötigt. Auf https://certbot.eff.org finde ich für mein System die Anleitung ein Snap mit dem Certbot zu installieren. Da der snapd auf den Systemen bereits vorhanden ist geht die Installation relativ einfach in folgenden Schritten (als root):

snap install core 
snap refresh core
snap install --classic certbot

Dann noch einen Link setzen:

ln -s /snap/bin/certbot /usr/bin/certbot

Danach kann man dann ausprobieren, ob der Certbot mit den vorhandenen Zertifikaten funktioniert:

certbot renew --dry-run

Denkbar ist dann ein Cronjob der Art:

/usr/bin/certbot renew

Meine Crontab habe ich dann angepasst, obwohl mir nicht ganz klar ist, ob die Aktualisierung nicht schon automatisch geschieht.

Mit

systemctl list-timers

kann man sich die Timer anschauen, die nicht über Cron laufen, sondern über den Systemd-Timer. Es gibt dort einen snap.certbot.renew.timer, der im Verzeichnis /etc/systemd/system/ zu finden ist, aber wohl nur für die Aktualisierung des Snaps zuständig ist.

Sonstige Timer sind übrigens meist im Verzeichnis /lib/systemd/system/ zu finden.

Ein neues Zertifikat erzeugt man dann mit

/usr/bin/certbot certonly --config /var/www/vhosts/<domain>/letsencrypt.ini

Für jede meiner virtuellen Domains habe ich dann eine letsencrypt.ini erstellt, mit folgendem Inhalt:

 # Aufruf mit: /usr/bin/certbot certonly --config /var/www/vhosts/meine-maildomain.de/letsencrypt.ini
 # Wir nutzen 4096 bit RSA key statt 2048
 rsa-key-size = 4096
 
 # allgemeine Angaben
 email = uwe@meine-maildomain.de
 authenticator = webroot
 
 # Domains fuer die wir Zertifikate beantragen, die erste in
 # der liste legt den Hauptnamen fest. Alle Domains müssen beim
 # Aufruf erreichbar sein
 domains = meine-maildomain.de, www.meine-maildomain.de
 
 # Dies ist das Verzeichnis zur Domain, wo letsencrypt seinen Hash in
 # /.well-known/acme-challenge schreiben will. Der Pfad muss auf / enden
 # es muss in der vserver.conf stehen:   Alias /.well-known   /var/www/htdocs/.well-known
 webroot-path = /var/www/htdocs/

Die Reihenfolge der Domains spielt insofern eine Rolle, als die erste Domain als Bezeichner für die Verzeichnisstruktur innerhalb von /etc/letsencrypt benutzt wird. Es bietet sich also an mit einer kürzeren Angabe zu beginnen.

Zum Erzeugen der Zertifikate dient dann der Aufruf:

/usr/bin/certbot certonly --config /var/www/vhosts/meine-maildomain.de/letsencrypt.ini

Die täglichen Aktualisierungsversuche für die Zertifikate übernimmt ein Con-Job, der automatisch angelegt wird (/etc/cron.d/certbot). Leider bekommt man dann keine Mails mehr, sondern muss in die Logdateien schauen, ob alles geklappt hat. Falls mir das auf Dauer nicht gefällt, so mache ich das wieder über den eigenen Cron-Job.

Was ich jetzt erst entdeckt habe ist das Prinzip der Hooks. Man kann bei letsencript an mehreren Stellen Scripten hinterlegen, die bei Aktualisierung eines Zertifikates aufgerufen werden. Damit kann man z.B. den Webserver oder den Mailserver neu starten.


Zertifikate erweitern

Das Erweitern von letsencrypt Zertifikaten ist relativ einfach. Ich ergänze eine die Domains-Zeile in der Konfigurationsdatei und rufe den Erzeugungsprozess neu auf. Letsencrypt erkennt die Situation und fragt, ob ich das Zertifikat erweitern oder erneuern möchte. Wählt man hier erweitern, so wird das passende Zertifikat neu erzeugt.


Zertifikat reduzieren

Manchmal soll eine Domain aus einem Zertifikat entfernt werden, weil man eventuell sie an anderer Stelle benötigt, oder sie nicht mehr verfügbar ist. Ich habe bisher keinen direkten Weg gefunden, wenn man vorgeht wie beim Erweitern und einfach die Liste verkürzt, dann legt Letsenctypt ein zusätzliches Zertifikat an, mit der Ergänzung -0001 am Namen. Man muss also das Zertifikat löschen und neu erstellen.

certbot delete --cert-name MeineDomain

Es geht auch interaktiv mit

certbot delete

es erscheint eine Liste der vorhandenen Zertifikate und man gibt die Nummer des Zertifikates an, das man löschen möchte.

Anschließend legt man das Zertifikat mit der verkleinerten Liste neu an.

Man muss aber damit rechnen, dass man von Letsencrypt Hinweis-Mails bekommt, wenn sich das eigentlich gelöschte Zertifikat dem Ablauf-Zeitpunkt nähert. So weit geht die Lösung nämlich nicht.

Dienste neu starten

In der Regel müssen z.B. die Mail-Dienste neue gestartet werden, wenn das benutzte Zertifikat erneuert wurde. Sonst wird weiterhin das alte Zertifikat benutzt. Letsencrypt kennt Hooks, über die Aktionen ausgelöst werden.

Hier die Datei /etc/letsencrypt/renewal-hooks/deploy/postfix-dovecot-reload.sh

#!/bin/sh
# Dieses script liegt ausführbar in: /etc/letsencrypt/renewal-hooks/deploy

for domain in $RENEWED_DOMAINS
do
    if [ "$domain" = "mail.<meine Domain>.de" ]
    then
        systemctl reload postfix
        systemctl reload dovecot
        
    fi
done


für Typo3

Da ich viel mit Typo3 arbeite habe ich etwas an den PHP-Einstellungen gedreht in der /etc/php/7.4/apache2/php.ini:

post_max_size=12M
upload_max_filesize=12M
max_execution_time=240
max_input_vars = 1500

Typo3 benötigt unbedingt imagemagick (oder alternativ graphicsmagick)

apt install imagemagick imagemagick-doc
apt install graphicsmagick ghostscript webalizer

Reihenfolge-Probleme bei der Apache-Konfiguration

Bei der Apache-Konfiguration muss man sehr auf die Reihenfolge der einzelnen Einstellungen achten. Dabei spielt der Aufbau der Hauptkonfigurationsdatei /etc/apache2/apache2.conf eine wichtige Rolle. Hier werden der Reihe nach alle Dateien aus folgenden Unterverzeichnissen eingebunden:

  • mods-enabled
  • conf-enabled
  • sites-enabled

Die Dateien aus den einzelnen Verzeichnissen werden dann jeweils in alphabetischer Reihenfolge eingebunden, intern entsteht dabei eine einzige große Konfigurationsdatei. Innerhalb dieser virtuellen Datei spielen die Reihenfolgen eine Rolle.

Bei der Reihenfolge von Alias (auch SriptAlias) Anweisungen und Redirects, die in unterschiedlichen Kontexten auftreten, werden die Direktiven nach den üblichen Zusammenführungsregeln verarbeitet. Wenn jedoch mehrere Aliase oder Redirects im gleichen Kontext (z.B. im gleichen Abschnitt) auftreten, werden sie in einer bestimmten Reihenfolge verarbeitet.

Zunächst werden alle Umleitungen verarbeitet, bevor Aliase verarbeitet werden, und daher werden auf eine Anforderung, die mit einer Umleitung oder einer RedirectMatch übereinstimmt, niemals Aliase angewendet. Zweitens werden die Aliase und Redirects in der Reihenfolge verarbeitet, in der sie in den Konfigurationsdateien erscheinen, wobei die erste Übereinstimmung Vorrang hat.

Wenn also in der /etc/apache2/conf-available/postfixadmin.conf steht

Alias /postfixadmin /usr/share/postfixadmin/public

kann ich dies nicht im allgemeinen Teil der /etc/apache2/sites-available/000-default.conf überschreiben, weil die erst später inkludiert wird. Nur innerhalb einer Directory oder VirtualHost Struktur kann ich Veränderungen vornehmen, weil dies spezieller ist.

Aus dem gleichen Grund muss man, wenn zwei oder mehr dieser Direktiven auf denselben Unterpfad angewendet werden, den spezifischsten Pfad zuerst auflisten, damit alle Direktiven eine Wirkung haben. Zum Beispiel wird die folgende Konfiguration wie erwartet funktionieren:

ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin/"

Aber wenn die beiden oben genannten Richtlinien in umgekehrter Reihenfolge angewendet würden, würde der /cgi-bin vor dem /cgi-bin/mailman-Alias stehen, so dass die letztere Richtlinie ignoriert würde.

Es ist sinnvoll beim Start von Apache auf die Meldungen im Syslog zu achten. Habe ich z.B. den Alias /postfixadmin bei den conf-enabled nicht auskommentiert, so erscheint bei meiner Konfiguration (s.u.) die Meldung

The Alias directive in /etc/apache2/sites-enabled/000-default.conf at line 19 will probably never match because it overlaps an earlier Alias.

/etc/apache2/sites-available/000-default.conf

Diese Datei besteht bei mir aus drei Teilen:

  • Zuerst die allgemeinen Einstellungen, die dann für alle virtuellen Systeme gelten, sofern sie nicht überschrieben werden.
  • Dann der virtuelle Default-Server für Port 80. Er würde aufgerufen, wenn jemand über die IP-Adresse, den Namen beim Provider (static.w.x.y.z.clients.your-server.de) oder eine nicht vorgesehene Domain kommt.
  • Der entsprechende virtuelle Default-Server für Port 443.

Die virtuellen Standard-Server sind auch im Zusammenhang mit letsencrypt-Zertifikaten für den Mailserver ganz praktisch. Ich muss nicht für mail.meine-maildomain.de eine Webseite konfigurieren, wenn ich ein Zertifikat erstellen oder erweitern möchte. Letsencrypt landet im Zweifelsfall hier und kann den Zugriff verifizieren. Der Standard-Server auf Port 443 erleichtert dann die Kontrolle der erstellten Zertifikate für den Mailserver, wenn das hier angegebene Zertifikat auch im Mailsystem genutzt wird.

  1 ServerName default
  2 ServerAdmin Uwe@meine-maildomain.de
  3  
  4 ServerTokens Major
  5 ServerSignature off
  6 TraceEnable off
  7 ProxyRequests off
  8  
  9 UseCanonicalName Off
 10 DocumentRoot /var/www/vhosts/default/httpdocs
 11  
 12 ErrorLog ${APACHE_LOG_DIR}/error.log
 13 CustomLog ${APACHE_LOG_DIR}/access.log combined
 14  
 15 CustomLog /var/log/apache2/vhosts_access.log vhost_combined
 16  
 17 Alias /webmail       /var/www/htdocs/dummy
 18 Alias /phpmyadmin    /var/www/htdocs/dummy
 19 Alias /postfixadmin  /var/www/htdocs/dummy
 20  
 21 Alias /webstat       /var/www/htdocs/webalizer
 22 Alias /roundcube     /var/lib/roundcube
 23  
 24 <IfDefine MAILMAN>
 25   ScriptAlias     /mailman/       /var/www/htdocs/dummy
 26   Alias           /mailmanicons/  /var/www/htdocs/dummy
 27   Alias           /pipermail/     /var/www/htdocs/dummy
 28 </IfDefine>
 29  
 30 <IfModule mod_userdir.c>
 31   UserDir disabled
 32 </IfModule>
 33  
 34 <IfModule mod_expires.c>
 35   ExpiresActive On
 36   ExpiresDefault "access plus 1 month"
 37   ExpiresByType text/html "access plus 1 week"
 38   ExpiresByType image/gif "access plus 1 week"
 39   ExpiresByType image/jpeg "access plus 1 week"
 40   ExpiresByType image/png "access plus 1 week"
 41   ExpiresByType text/css "access plus 1 week"
 42   ExpiresByType text/javascript "access plus 1 week"
 43   ExpiresByType application/x-javascript "access plus 1 week"
 44   ExpiresByType text/xml "access plus 1 week"
 45 </IfModule>
 46  
 47 <IfModule mod_setenvif.c>
 48   # SEO
 49   BrowserMatchNoCase (mindUp|meanpathbot|seoscanners|AiHitBot|BLEXBot|DotBot|linkdexbot|MJ12bot|SEOkicks-Robot) ist_ein_bot
 50   # Sammeln Backlinks & Links
 51   BrowserMatchNoCase (exabot|Baidu|Haosou|Semrush|MegaIndex|AhrefsBot|BacklinkCrawler|dlcbot|spbot) ist_ein_bot
 52   # Performance Testing
 53   BrowserMatchNoCase (200PleaseBot|LoadTimeBot) ist_ein_bot
 54   # BilderSuche
 55   BrowserMatchNoCase (psbot|Yandex) ist_ein_bot
 56   # Harvester & Marketing
 57   BrowserMatchNoCase (MegaIndex|Applebot|XoviBot|CareerBot|GrapeshotCrawler|iCjobs|magpie-crawler|proximic) ist_ein_bot
 58   # Nutzlos, Schlecht bzw. unbekannt
 59   BrowserMatchNoCase (PetalBot|360Spider|AfD-Verbotsverfahren|Barkrowler|PeoplePal|ltx71|CalendarAgent|JobboerseBot|GarlikCrawler|Mail.RU_B
 60   # Per IP
 61   SetEnvIfNoCase Remote_Addr (62\.138\.0\.25) ist_ein_bot
 62   # kyivstar.net
 63   SetEnvIfNoCase Remote_Addr ^(5\.248|46\.118|37\.115|178\.137) ist_ein_bot
 64   # Einträge von 2020
 65   BrowserMatchNoCase (MaviBot|oBot|MetaJobBot|seocompany|coccocbot-image|SeznamBot) ist_ein_bot
 66 </IfModule>
 67  
 68 <IfModule mod_ssl.c>
 69   SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
 70   SSLHonorCipherOrder On
 71   SSLCompression off  
 72   SSLCipherSuite ALL:!aNULL:RC4+RSA:+HIGH:+MEDIUM:+LOW:+EXP:+eNULL
 73 </IfModule>
 74 
 75 <Directory "/var/www/vhosts">
 76   AllowOverride All
 77   Options +SymLinksIfOwnerMatch -Indexes
 78   Require all granted
 79  
 80   <IfModule mod_php7.c>
 81     php_value date.timezone "Europe/Berlin"    
 82     php_value open_basedir /var/www/:/tmp/
 83     php_value include_path /var/www/:/tmp/
 84   </IfModule>
 85  
 86 </Directory>
 87   
 88 <VirtualHost *:80>
 89   Alias /.well-known   /var/www/htdocs/.well-known
 90   <Directory /var/www/vhosts/default/httpdocs>
 91     AllowOverride All
 92     Options None
 93     Require all granted
 94   </Directory>
 95  
 96 </VirtualHost>
 97  
 98 <IfModule mod_ssl.c>
 99   <VirtualHost _default_:443 >
100     SSLEngine on
101    # Wenn das erste Zertifikat mit Letsencryp erstellt ist, dann die Zertifikate austauschen
102    # SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
103    # SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
104     SSLCertificateFile /etc/letsencrypt/live/<dummy>/fullchain.pem
105     SSLCertificateKeyFile /etc/letsencrypt/live/<dummy>/privkey.pem
106  
107     <FilesMatch "\.(cgi|shtml|phtml|php)$">
108       SSLOptions +StdEnvVars
109     </FilesMatch>
110  
111     <Directory /var/www/vhosts/default/httpdocs>
112       SSLRequireSSL
113       AllowOverride All
114       Options None
115       Require all granted
116     </Directory>
117  
118   </VirtualHost>
119  
120 </IfModule>


Die SSL-Einstellungen der Seite kann man unter https://www.cdn77.com/tls-test (Hinweis: deren Cache lässt sich anscheinend nicht löschen) und ganz ausführlich unter https://www.ssllabs.com/ssltest/ testen lassen. Es geht übrigens auch ohne externen Dienstleister mittels

nmap --script ssl-enum-ciphers -p 443 example.com

Ursprünglich hatte ich hier auch einen Alias für .well-known mir drin, das führt aber zu Problemen bei z.B. Nextcloud, hier wird mit Unterverzeichnissen von .well-known gearbeitet und dann treten Probleme mit der Reihenfolge auf.

Vserver-Konfiguration

Für jeden weiteren VServer erfolgt die Konfiguration in /etc/apache2/vhosts/sites-available/<dummy>.conf nach folgendem System, wobei <dummy> durch z.B. den Domainnamen ersetzt wird. Das zugehörige Verzeichnis wird dann mit diesem Namen unterhalb von /var/www/vhosts/ angelegt.

 1 <VirtualHost *:80>
 2   ServerName   www.<dummy>:80
 3   ServerAlias <dummy>
 4   DocumentRoot /var/www/vhosts/<dummy>/httpdocs
 5  
 6   CustomLog  /var/log/apache2/<dummy>_access.log combined
 7   ErrorLog   /var/log/apache2/<dummy>_error.log
 8  
 9   CustomLog /var/log/apache2/vhosts_access.log vhost_combined
10  
11   ScriptAlias  /cgi-bin/ /var/www/vhosts/<dummy>/cgi-bin/
12   Alias  /webstat       /var/www/vhosts/<dummy>/webstat
13   Alias /.well-known   /var/www/htdocs/.well-known
14  # Nur bei Bedarf aktivieren
15  # Alias /phpmyadmin /usr/share/phpmyadmin
16  # Alias /postfixadmin  /usr/share/postfixadmin/public
17  
18   <Directory /var/www/vhosts/<dummy>/httpdocs>
19  
20     <IfModule mod_setenvif.c>
21       <RequireAll> 
22         Require all granted
23         Require not env ist_ein_bot
24       </RequireAll> 
25     </IfModule>
26  
27     <IfModule mod_php7.c>
28       php_admin_flag engine on
29       php_admin_value include_path    "/var/www/vhosts/<dummy>/httpdocs:.:/tmp:./:/usr/share/php/PEAR/:/srv/www/typo3src:/usr/bin"
30       php_admin_value open_basedir    "/var/www/vhosts/<dummy>/httpdocs:/tmp:.:/usr/share/php/PEAR/:/srv/www/typo3src:/usr/bin"
31     </IfModule>
32  
33     Options -Includes +ExecCGI
34   </Directory>
35  
36   <Directory "/var/www/vhosts/<dummy>/cgi-bin">
37     AllowOverride None
38     Options +ExecCGI -Includes
39     Require all granted
40   </Directory>
41  
42 </VirtualHost>
43  
44 # soll SSL aktiviert werden das _no entfernen
45 <IfModule mod_ssl_no.c>
46  
47   <VirtualHost *:443>
48     ServerName   www.<dummy>:443
49     ServerAlias <dummy>
50     DocumentRoot /var/www/vhosts/<dummy>/httpdocs
51  
52     CustomLog  /var/log/apache2/<dummy>_access.log combined
53     ErrorLog   /var/log/apache2/<dummy>_error.log
54  
55     CustomLog /var/log/apache2/vhosts_access.log vhost_combined
56  
57     ScriptAlias  /cgi-bin/ /var/www/vhosts/<dummy>/cgi-bin/
58     Alias  /webstat /var/www/vhosts/<dummy>/webstat
59     Alias /.well-known   /var/www/htdocs/.well-known
60    # Nur bei Bedarf aktivieren
61    # Alias /phpmyadmin /usr/share/phpmyadmin
62    # Alias /postfixadmin  /usr/share/postfixadmin/public
63     SSLEngine on
64  
65     <Directory /var/www/vhosts/<dummy>/httpdocs>
66       <IfModule mod_setenvif.c>
67         <RequireAll> 
68           Require all granted
69           Require not env ist_ein_bot
70         </RequireAll> 
71       </IfModule>
72  
73       <IfModule mod_php7.c>
74         php_admin_flag engine on
75         php_admin_value include_path    "/var/www/vhosts/<dummy>/httpdocs:.:/tmp:./:/usr/share/php/PEAR/:/srv/www/typo3src:/usr/bin"
76         php_admin_value open_basedir    "/var/www/vhosts/<dummy>/httpdocs:/tmp:.:/usr/share/php/PEAR/:/srv/www/typo3src:/usr/bin"
77       </IfModule>
78  
79       Options -Includes +ExecCGI
80     </Directory>
81  
82     <Directory "/var/www/vhosts/<dummy>/cgi-bin">
83       AllowOverride None
84       Options +ExecCGI -Includes
85       Require all granted
86     </Directory>
87   
88     SSLCertificateFile /etc/letsencrypt/live/<dummy>/fullchain.pem
89     SSLCertificateKeyFile /etc/letsencrypt/live/<dummy>/privkey.pem
90   </VirtualHost>
91  
92 </IfModule>

Verwaltungs-Vserver

Für die Nutzung von

  • postfixadmin
  • phpmyadmin
  • rspamd
  • eigenen CGI Scripten

habe ich mir einen speziellen virtuellen Server erstellt, der keine eigenen Ordner bekommt, sondern auf dem Default-Server aufsetzt. /etc/apache2/vhosts/sites-available/verwaltung.conf

 1 <VirtualHost *:80>
 2   ServerName   verwaltung.<dummy>:80
 3   DocumentRoot /var/www/vhosts/default/httpdocs
 4  
 5   CustomLog  /var/log/apache2/verwaltung_access.log combined
 6   ErrorLog   /var/log/apache2/verwaltung_error.log
 7  
 8   CustomLog /var/log/apache2/vhosts_access.log vhost_combined
 9  
10   ScriptAlias  /cgi-bin/ /var/www/vhosts/default/cgi-bin/
11   Alias  /webstat        /var/www/vhosts/default/webstat
12   Alias /phpmyadmin      /usr/share/phpmyadmin
13   Alias /postfixadmin    /usr/share/postfixadmin/public
14   Alias /.well-known     /var/www/htdocs/.well-known
15   
16   <Directory /var/www/vhosts/default/httpdocs>
17  
18     <IfModule mod_setenvif.c>
19       <RequireAll> 
20         Require all granted
21         Require not env ist_ein_bot
22       </RequireAll> 
23     </IfModule>
24   
25     Options -Includes +ExecCGI
26   </Directory>
27  
28   <Directory "/var/www/vhosts/default/cgi-bin">
29     AllowOverride None
30     Options +ExecCGI -Includes
31     Require all granted
32   </Directory>
33  
34   RewriteEngine On
35   ProxyRequests Off
36   <Location /rspamd>
37     Order allow,deny
38     Allow from all
39   </Location>
40   RewriteRule ^/rspamd$ /rspamd/ [R,L]
41   RewriteRule ^/rspamd/(.*) http://localhost:11334/$1 [P,L]
42  
43 </VirtualHost>
44  
45 # soll SSL aktiviert werden das _no entfernen
46 <IfModule mod_ssl_no.c>
47  
48   <VirtualHost *:443>
49     ServerName   verwaltung.<dummy>:443
50     DocumentRoot /var/www/vhosts/default/httpdocs
51  
52     CustomLog  /var/log/apache2/verwaltung_access.log combined
53     ErrorLog   /var/log/apache2/verwaltung_error.log
54  
55     ScriptAlias  /cgi-bin/  /var/www/vhosts/default/cgi-bin/
56     Alias  /webstat         /var/www/vhosts/default/webstat
57     Alias /phpmyadmin       /usr/share/phpmyadmin
58     Alias /postfixadmin     /usr/share/postfixadmin/public
59     Alias /.well-known      /var/www/htdocs/.well-known
60  
61     SSLEngine on
62  
63     <Directory /var/www/vhosts/default/httpdocs>
64  
65       <IfModule mod_setenvif.c>
66         <RequireAll> 
67           Require all granted
68           Require not env ist_ein_bot
69         </RequireAll> 
70       </IfModule>
71  
72       Options -Includes +ExecCGI
73     </Directory>
74  
75     <Directory "/var/www/vhosts/default/cgi-bin">
76       AllowOverride None
77       Options +ExecCGI -Includes
78       Require all granted
79     </Directory>
80  
81     SSLCertificateFile /etc/letsencrypt/live/verwaltung.<dummy>/fullchain.pem
82     SSLCertificateKeyFile /etc/letsencrypt/live/verwaltung.<dummy>/privkey.pem
83  
84     RewriteEngine On
85     ProxyRequests Off
86     <Location /rspamd>
87       Order allow,deny
88       Allow from all
89     </Location>
90     RewriteRule ^/rspamd$ /rspamd/ [R,L]
91     RewriteRule ^/rspamd/(.*) http://localhost:11334/$1 [P,L]
92  
93   </VirtualHost>
94  
95 </IfModule>

Logdateien

Da ich die Logdateien für meine virtuellen Server mit Webalizer auswerten möchte, brauche ich eine Erweiterung für Logrotate:

/var/log/apache2/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        create 640 root adm
        sharedscripts
        postrotate
                if invoke-rc.d apache2 status > /dev/null 2>&1; then \
                    invoke-rc.d apache2 reload > /dev/null 2>&1; \
                fi;
        endscript
        prerotate
             /root/webalizer.sh
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi; \
        endscript
}

Zu finden in der Datei /etc/logrotate.d/apache2.


Mailsystem

Ziel der folgenden Konfiguration ist es ein Mailsystem einzurichten, welches ohne individuelle Systembenutzer auskommt. Alle Benutzerkonten befinden sich in einer Datenbank. Die Postfächer selber befinden sich dann im Verzeichnis /var/vmail, wobei danach erst einmal ein Verzeichnis für die Domain kommt und darunter dann ein Verzeichnis für den jeweiligen Benutzeraccount.

Vorbemerkung

Das Mail-System besitzt folgende Komponenten bzw. Funktionen:

  • Postfixadmin zur Verwaltung der virtuellen Benutzer in der Datenbank
  • Dovecot als POP und IMAP-Server
  • Postfix als Mailserver
  • rspamd über die Milter-Schnittstelle und
    • redis als leichte Datenbank für rspamd
    • spf und dkim sollen auch aktiviert sein
  • Roundcube als Mailclient

großes Bild zum Zusammenspiel von Postfix, Dovecot, rspamd und Postfixadmin

SPF, DKIM und DMARC gegen Spam

Mailserver sind von Natur aus vertrauensselig, was von Spammern ausgenutzt wird. Ich denke, dass mehr als 90% aller versendeten Nachrichten in die Kategorie Spam gehören. Der Mailserver Postfix beherrscht ein von Haus aus ein paar Möglichkeiten den Spam zu verringern:

  • Nutzung von RBL Listen in den smtpd_client_restrictions. Für jede eingehende Mail werden dann externe Server wie cbl.abuseat.org befragt, ob sie den sendenden Server kennen. Wenn ja, dann wird die Mail rejected. Damit dieses System effektiv wird, muss man aber mehrere derartige Server parallel nutzen.
  • Regeln über den formalen Aufbau einer Mail z.B. in den smtpd_helo_restrictions. Hier kann man mit reject_invalid_helo_hostname erreichen, dass nur Mails von Servern angenommen werden, bei denen der Name nicht korrekt ist.

Für weitergehende Untersuchungen, z.B. auf Viren oder Spam bindet man in der Regel zusätzliche Hilf-Programme ein, in der Regel SpamAssassin oder neuerdings auch rspamd. Diese Programme teilen dem Mailserver mit, wie mit der Mail weiter zu verfahren ist. Dabei taucht das Problem auf, dass eine Mail die der Mailserver angenommen hat auch zugestellt werden muss, sofern sich keinen direkten Schadcode beinhaltet. Normaler Spam muss also zugestellt werden, wenn die Mail nicht frühzeitig als Spam erkannt und daher nicht angenommen wird.

Die Spamfilter klassifizieren jede Mail anhand unterschiedlicher Regeln bzw. Kriterien und vergeben dabei einen Score. Je höher der Score, desto wahrscheinlicher handelt es sich um Spam.

Mit der Nutzung von SPF, DKIM und DMARC möchte ich erreichen, dass der Score meiner Mails auf anderen Servern gesenkt wird und sollte jemand mit einer meiner Domains illegal Mails verschicken, so soll sich deren Score erhöhen. Das ist die Idee hinter diesen Konzepten.

SPF

Das Sender Policy Framework (SPF) dient dazu, die Mail anhand der IP Adresse des sendenden Mailservers zu überprüfen. Dazu wird in einem speziellen Namesereintrag vom Typ TXT festgelegt, welche Server berechtigt sind Mails mit dieser Domain zu verschicken.

Im einfachsten Fall sieht der TXT Eintrag folgendermaßen aus:

v=spf1 a mx -all

Ein

dig -t txt meine-maildomain.de 

liefert dann

meine-maildomain.de.	3600	IN	TXT	"v=spf1 a mx -all"

Damit wird ausgesagt, dass nur Server, die einen A- oder einen MX-Eintrag in dieser Domain besitzen berechtigt sind Mails abzuliefern. Über das -all wird festgelegt, wie zu verfahren ist, wenn sich ein Server meldet, der der Regel nicht entspricht.

  • -all : Fail der Server darf nicht senden
  • ~all : SoftFail der Server darf eigentlich nicht senden, sei aber großzügig
  •  ?all : Neutral jeder Sender wird akzeptiert.

Ausführlichere Beschreibung der Syntax unter https://www.spf-record.de/syntax

Ein Vorteil von SPF ist, dass am sendenden Server keinerlei Konfiguration notwendig ist. Der größte Nachteil besteht darin, dass es bei Weiterleitungen Probleme gibt, da dann der sendende Server nicht mehr zum Absender passt.

Domainfactory generiert übrigens automatisch auch einen Catchall Eintrag für SPF.

DKIM

Auch das System Domain Keys Identified Mail (DKIM) dient auch dazu nur bestimmte Server zum Senden zu authentifizieren. Das geschieht, indem jede ausgehende Mail im Header digital signiert wird. Der öffentlichen Schlüssel hierzu ist im Nameserver hinterlegt. Der private Schlüssel muss natürlich geschützt auf dem sendenden Server liegen. Da mehrere Server sendeberechtigt sein können, kann es auch mehrere derartige Schlüssel geben. Deshalb wird zusätzlich ein sog. Selektor mit im Mail-Header hinterlegt, damit der Empfänger den richtigen Schlüssel wählen kann.

dig -t txt 2020._domainkey.meine-maildomain.de

liefert dann

2020._domainkey.meine-maildomain.de. 3600 IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClkpAaTMA5GcACTbSfJ1vSGwJyHnv1IVzR4GEu3PMAAIvuYCWPONypEAErZJciY8gK+z3FoPaMdsB+eii4rozlN6Jtfx+Fi/SM0FWV1PvPVI0tkoIXa82lKfJi4hqbWDHXxQYfROEKNFWZtoG/C951MwNOiq34Vk3X0nNoBWfyHwIDAQAB"

In diesem Beispiel ist 2020 der Selector. Damit der Mailclient den richtigen Schlüssel abfragen kann, findet sich in DKIM-Header ein Bereich s=2020.

Weitere Informationen unter https://de.wikipedia.org/wiki/DomainKeys

Ein großer Vorteil von DKIM gegenüber SPF ist, dass Weiterleitungen kein Problem sind. Der versendende Server spielt ja keine Rolle. Ein großer Nachteil ist, dass der Server entsprechend konfiguriert werden muss, damit er die richtigen Header-Zeilen erzeugen kann.

DMARC

Das Konzept Domain-based Message Authentication, Reporting and Conformance (DMARC) baut auf SPF und DKIM auf. Mit DMARC kann der Absender zusätzlich Empfehlungen geben, auf welche Art der Empfänger mit einer Mail umgeht, die in einem oder beiden Fällen nicht den Anforderungen entspricht.

Es gibt die folgenden Regeln

  • p=reject lehnt Mail ab, bei denen die Prüfung fehlgeschlagen ist
  • p=quarantine lässt Mail in den Spam-Ordner verschieben, bei denen die Überprüfung fehlgeschlagen ist
  • p=none weist den empfangenden Server an Berichte an die Mailadresse zu schicken, die per rua bzw. ruf hinterlegt wurde. Dazu ein Beispiel aus Wikipedia
v=DMARC1;p=quarantine;pct=100;rua=mailto:postmaster@example.org;ruf=mailto:forensik@example.org;adkim=s;aspf=r

Weitere Informationen unter: https://de.wikipedia.org/wiki/DMARC

dig -t any _dmarc.meine-maildomain.de

liefert hier

_dmarc.meine-maildomain.de. 3599 IN	TXT	"v=DMARC1; p=reject;"

Vorbereitungen

Für die folgende Beschreibung wird ein Benutzer vmail (uid 303) und eine Gruppe vmail (gid 303) benutzt. Der Benutzer hat als Homeverzeichnis /var/vmail, wo dann auch die eingehenden Mails liegen.

Da Gruppe und Benutzer nicht vorhanden sind, legt man sie neu an:

 groupadd -g 303 vmail
 mkdir /var/vmail
 chmod a+rxw /var/vmail
 useradd -d /var/vmail/ -s /bin/false -u 303 -g 303 vmail

postfixadmin

Mit dem Mail-Verwaltungstool geht es los. Das Tool selber interagiert in keinster Weise mit dem Mailsystem. Alle Änderungen erfolgen nur an und in der Datenbank. Von daher ist es sinnvoll dies als erste Komponente zu installieren.

apt install postfixadmin

Bei der Installation kann man gleich die notwendigen Datenbank-Einstellungen mit anlegen lassen. Bei einem ganz neuen System bietet sich das an. Seit einiger Zeit meckert das Programm beim Starten immer über ein fehlendes Verzeichnis, das können wir gleich korrigieren:

mkdir /usr/share/postfixadmin/templates_c
chown www-data.www-data /usr/share/postfixadmin/templates_c

Danach dann

DEBUG INFORMATION:
MySQL 3.x / 4.0 functions not available! (php5-mysql installed?)
database_type = 'mysql' in config.inc.php, are you using a different database?

Please check the documentation and website for more information. 

Dazu muss man dann in der Datei /etc/postfixadmin/dbconfig.inc.php die Zeile

$dbtype='mysql';

ändern in

$dbtype='mysqli';

Die Datei /etc/postfixadmin/dbconfig.inc.php hat dann folgenden Inhalt:

<?php
##
## database access settings in php format
## automatically generated from /etc/dbconfig-common/postfixadmin.conf
## by /usr/sbin/dbconfig-generate-include
##
## by default this file is managed via ucf, so you shouldn't have to
## worry about manual changes being silently discarded.  *however*,
## you'll probably also want to edit the configuration file mentioned
## above too.
##
$dbuser='postfix';
$dbpass='geheim';
$basepath='';
$dbname='postfix';
$dbserver='localhost';
$dbport='3306';
$dbtype='mysqli';

Aktuell ist bei der Version 3.2 eine kleine Änderung zu beachten, der Alias-Eintrag in der VServer-Konfiguration muss jetzt auf das Unterverzeichnis public zeigen:

Alias /postfixadmin  /usr/share/postfixadmin/public

Die entsprechende Einstellung ist zwar in der Datei /etc/apache2/conf-available/postfixadmin.conf schon vorhanden, dann ist PostfixAdmin aber in allen virtuellen Domains vorhanden. Ich habe diese Zeile also dort auskommentiert und füge die Zeile in den virtuellen Server ein, in dem ich sie haben möchte.

Beim Aufruf der setup.php gab es immer den Hinweis, dass die Datei config.local.php nicht gefunden werden könnte. Das scheint mir eine Falschinformation zu sein, was man schnell damit überprüfen kann, dass man in dieser Datei in der ersten Zeile

$CONF['configured'] = false;

setzt. Die Einstellung wird dann im setup.php kritisiert. Setzt man das wieder auf true, dann kann es weitergehen. Man muss im Webinterface zuerst ein Setup-Passwort erstellen und in die Konfigurationsdatei eintragen, danach muss man dann noch den Superadmin erstellen.

Beim Update einer vorhandenen (älteren) Datenbank kann man sich die letzten Schritte sparen. Man muss nur das Script setup.php aufrufen, dann wird die Datenbank aktualisiert. Den nächsten Schritt, das Erstellen des Installationspasswortes kann man machen, muss man aber nicht, wenn man eines in die Konfigurationsdatei (s.u.) eingetragen hat. Selbst wenn man den Schritt trotzdem vornimmt, dann kann man sich ggf. das Erstellen eines Superadmins sparen. Dazu ruft man einfach die ursprüngliche Postfixadmin-URL (ohne /setup.php) auf.

Die Datei config.inc.php ändert man nicht. Alle Änderungen werden in der Datei config.local.php vorgenommen

An der Datei /etc/postfixadmin/config.local.php habe ich nur wenig verändert und das auch hauptsächlich Kosmetik

 1  <?php
 2  $CONF['configured'] = true;
 3  
 4  // on submission it will be echoed out to you as a hashed value.
 5  $CONF['setup_password'] = '7caf48fdc954f6037dcc0a6438358db0:9fdf45d7c3dd759258e8af37d09b361218428af5';
 6  $CONF['default_language'] = 'de';
 7  
 8  $CONF['admin_email'] = 'postmaster@meine-maildomain.de';
 9  $CONF['page_size'] = '20';
10  
11  $CONF['default_aliases'] = array (
12    'abuse' => 'abuse@meine-maildomain.de',
13    'hostmaster' => 'hostmaster@meine-maildomain.de',
14    'postmaster' => 'postmaster@meine-maildomain.de',
15    'webmaster' => 'webmaster@meine-maildomain.de'
16  );
17  $CONF['domain_path'] = 'YES';
18  $CONF['domain_in_mailbox'] = 'NO';
19  $CONF['quota'] = 'YES';
20  $CONF['footer_text'] = 'Zurück zu Meine-Maildomain.de';
21  $CONF['footer_link'] = 'http://meine-maildomain.de';
22   
23  // Welcome Message
24  // This message is send to every newly created mailbox.
25  // Change the text between EOM.
26  $CONF['welcome_text'] = <<<EOM
27  Herzlich Willkommen, 
28  
29  zum neuen E-Mail Postfach.
30  
31  Bei Fragen oder Problemen bitte
32  Uwe Debacher ansprechen
33  EOM;
34  
35  $CONF['show_undeliverable_exceptions']=array("meine-maildomain.de");
36  $CONF['used_quotas'] = 'YES';
37  $CONF['maxquota'] = '2048';
38  $CONF['domain_quota_default'] = '0';
39  $CONF['aliases'] = '20';
40  $CONF['mailboxes'] = '20';

Dovecot

Eigentlich ist Dovecot nur ein Server für POP3 und Imap. Inzwischen kann dieses Programm deutlich mehr. Die Zugriffe auf die Datenbank mit den virtuellen Usern erfolgt in der Regel über Dovecot. Das geht bis zur Nachrichten-Zustellung in die Postfächer.

apt install dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql dovecot-sieve dovecot-managesieved dovecot-pop3d dovecot-antispam
service dovecot stop

Die Versionsnummer von dovecot kann man abfragen mittels

dovecot --version

/etc/dovecot/local.conf

Ich habe die Konfiguration in einer eigenen Datei /etc/dovecot/local.conf gesammelt: (hervorgehoben die Zeilen mit individueller Konfiguration)

  1  ##
  2  ## Protokollierung
  3  ##
  4  
  5  #mail_debug = yes
  6  #auth_debug = yes
  7  auth_debug_passwords = yes
  8  #auth_verbose = yes
  9  auth_verbose_passwords = sha1
 10  #verbose_proctitle = yes
 11  
 12  ##
 13  ## Grundeinstellungen
 14  ##
 15  
 16  first_valid_uid = 303
 17  mail_access_groups = postfix
 18  mail_privileged_group = vmail
 19  mail_uid = vmail
 20  mail_gid = vmail
 21  
 22  protocols = imap pop3 lmtp sieve
 23  
 24  # die folgenden Einstellungen sind nicht optimal, da die home-Verzeichnisse so nicht automatisch angelegt werden
 25  # geschickter wäre (dann auch dovecot-mysql.conf anpassen):
 26  #mail_home = /var/vmail/%d/%n
 27  #mail_location = maildir:/var/vmail/%d/%n/mail  oder  mail_location = maildir:~/Maildir
 28  
 29  mail_location = maildir:/var/vmail/%d/%n
 30  mail_home = /var/vmail/%d/%n/home
 31  
 32  mail_plugins = $mail_plugins quota
 33  
 34  disable_plaintext_auth = no
 35  auth_mechanisms = plain login
 36  
 37  ##
 38  ##TLS Konfiguration
 39  ##
 40  
 41  ssl = yes
 42  ssl_cert = </etc/letsencrypt/live/meine-maildomain.de/fullchain.pem
 43  ssl_key = </etc/letsencrypt/live/meine-maildomain.de/privkey.pem
 44  #ssl_cert = </etc/ssl/certs/ssl-cert-snakeoil.pem
 45  #ssl_key = </etc/ssl/private/ssl-cert-snakeoil.key
 46  
 47  # die beiden folgenden Parameter gibt es erst ab dc 2.3 (Ubuntu 20.04)
 48  ssl_dh = </etc/dovecot/dh4096.pem
 49  ssl_min_protocol = TLSv1.2
 50  #ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
 51  #ssl_prefer_server_ciphers = no
 52  
 53  ##
 54  ##Services
 55  ##
 56  
 57  service auth {
 58   unix_listener /var/spool/postfix/private/auth {
 59    group = postfix
 60    mode = 0660
 61    user = postfix
 62   }
 63  }
 64  
 65  service lmtp {
 66   unix_listener lmtp {
 67  #mode = 0666
 68   }
 69   unix_listener /var/spool/postfix/private/dovecot-lmtp {
 70   #mode = 0666
 71   user = postfix
 72   group = postfix
 73   }
 74  }
 75  
 76  service managesieve-login {
 77   inet_listener sieve {
 78   port = 4190 
 79   }
 80  }
 81  
 82  service quota-warning {
 83   executable = script /usr/local/bin/quota-warning.sh
 84   user = vmail
 85   unix_listener quota-warning {
 86         group = vmail
 87         mode = 0660
 88         user = vmail
 89   }
 90  }
 91  
 92  service quota-status {
 93   executable = quota-status -p postfix
 94   inet_listener {
 95  #    address = 127.0.0.1
 96     port = 12340
 97   }
 98   client_limit = 1
 99  }
100  # Hinweis in main.cf: smtpd_recipient_restrictions = check_policy_service inet:127.0.0.1:12340, ..
101  
102  service dict {
103    unix_listener dict {
104        mode = 0600
105        user = vmail
106        group = vmail
107    }
108  }
109  
110  dict {
111    sqluserquota = mysql:/etc/dovecot/dovecot-dict-sql-user.conf
112  }
113  
114  service imap {
115   executable = imap imap-postlogin
116  }
117  
118  service imap-postlogin {
119   executable = script-login /usr/local/bin/postlogin.sh
120   user = vmail
121   unix_listener imap-postlogin {
122   }
123  }
124  
125  
126  ##
127  ## Protokolle
128  ##
129  
130  protocol pop3 {
131   pop3_uidl_format = %08Xu%08Xv
132   mail_max_userip_connections = 5
133  }
134  
135  protocol imap {
136   mail_max_userip_connections = 8
137   mail_plugins = $mail_plugins imap_quota imap_sieve 
138  }
139  
140  protocol lmtp {
141   mail_plugins = $mail_plugins sieve 
142  }
143  
144  ##
145  ## Client Authentifizierung
146  ##
147  
148  passdb {
149   args = /etc/dovecot/dovecot-mysql.conf
150   driver = sql
151  }
152  
153  userdb {
154   args = /etc/dovecot/dovecot-mysql.conf
155   driver = sql
156  }
157  ## Hinweis: Datei 10-auth.conf editieren und !include auth-system.conf.ext deaktivieren
158  
159  ##
160  ## Postfächer
161  ##
162  
163  namespace inbox {
164   inbox = yes
165   mailbox Drafts {
166     auto = subscribe
167     special_use = \Drafts
168   }
169   mailbox Junk {
170     auto = subscribe
171     autoexpunge = 26w
172     special_use = \Junk
173   }  
174   mailbox "Junk-E-Mail" {
175     special_use = \Junk
176     autoexpunge = 26w
177   }
178   mailbox "Spam" {
179     special_use = \Junk
180     autoexpunge = 26w
181   }
182   mailbox Sent {
183     auto = subscribe
184     special_use = \Sent
185   }
186   mailbox "Sent Messages" {
187     special_use = \Sent
188   }
189   mailbox Trash {
190     auto = subscribe
191     autoexpunge = 26w
192     special_use = \Trash
193   }
194   mailbox "Gelöschte Elemente" {
195     autoexpunge = 26w
196     special_use = \Trash
197   }
198   mailbox "Papierkorb" {
199     autoexpunge = 26w
200     special_use = \Trash
201   }
202  }
203  
204  ##
205  ##Plugins
206  ##
207  
208  plugin {
209   sieve = ~/.dovecot.sieve
210   sieve_dir = ~/sieve
211  
212   sieve_plugins = sieve_imapsieve sieve_extprograms
213   sieve_before = /var/vmail/sieve/global/spam-global.sieve
214  
215   #quota_rule = *:storage=1G
216   #quota_rule2 = Trash:storage=+100M
217  
218   quota_grace = 10%%
219  
220   quota_warning = storage=66%% quota-warning 66 %u 
221   quota_warning2 = storage=80%% quota-warning 80 %u
222   quota_warning3 = storage=95%% quota-warning 95 %u
223   #  quota = dict:User Quota::noenforcing:proxy::sqluserquota
224   quota = dict:User Quota::proxy::sqluserquota
225  
226   quota_status_success = DUNNO
227   quota_status_nouser = DUNNO
228   quota_status_overquota = "552 5.2.2 Mailbox is over quota / Mailbox ist voll"
229  
230   ##Spam lernen
231  
232   # From elsewhere to Junk folder
233   imapsieve_mailbox1_name = Junk
234   imapsieve_mailbox1_causes = COPY
235   imapsieve_mailbox1_before = file:/var/vmail/sieve/global/learn-spam.sieve
236  
237   # From Junk folder to elsewhere
238   imapsieve_mailbox2_name = *
239   imapsieve_mailbox2_from = Junk
240   imapsieve_mailbox2_causes = COPY
241   imapsieve_mailbox2_before = file:/var/vmail/sieve/global/learn-ham.sieve
242  
243   sieve_pipe_bin_dir = /usr/bin
244  
245   sieve_global_extensions = +vnd.dovecot.pipe
246  }

Mit der auskommentierten Zeile

#  quota = dict:User Quota::noenforcing:proxy::sqluserquota

würde bei einem Postfach, das die Quotas überschritten hat, noch Mail angenommen werden (nonenforcing). Die aktive Zeile bewirkt, dass die Mail dann abgelehnt wird. Dann sollte man aber auch, wie angegeben, die main.cf von Postfix so erweitern, dass er von Dovecot über die Überfüllung des Postfaches informiert werden kann und dann die Annahme weiter Mails selber ablehnt.

Der Teil mit dem Lernen von SPAM und HAM hat mir etwas Probleme bereitet, mit fehlte anfangs das imap_sieve mail_plungin. Leider gibt es keine Fehlermeldungen in den Logdateien. Hilfreich war für mich https://doc.dovecot.org/configuration_manual/howto/antispam_with_sieve/. Wenn es funktioniert kann man das an einem erfolgreichen Aufruf von rspamc mittels

grep "pipe action" /var/log/mail.log

erkennen.


Zusätzlich habe ich die Authentisierung per pam deaktiviert, indem ich im Unterordner conf.d die Datei 10-auth.conf editiert und dort am Ende der Datei die einzig aktive Include-Zeile

!include auth-system.conf.ext

/etc/dovecot/dovecot-mysql.conf

Die in der Konfiguration erwähnte /etc/dovecot/dovecot-mysql.conf

 1  # Database driver: mysql, pgsql
 2  driver = mysql
 3  
 4  # Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT.
 5  default_pass_scheme = CRYPT
 6  
 7  # Database options
 8  #db_unix_socket = /var/lib/mysql/mysql.sock
 9  
10  connect = host=/var/run/mysqld/mysqld.sock dbname=postfix user=postfix password=assword
11  
12  password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1'
13  user_query = SELECT \
14     concat('/var/vmail/',maildir,'home/') as home, \    
15     concat('maildir:/var/vmail/',maildir) as mail, \ 
16     303 AS uid, \ 
17     303 AS gid, \ 
18     CONCAT('*:bytes=', IF(mailbox.quota = 0, domain.maxquota*1024000, mailbox.quota)) as quota_rule \
19     FROM mailbox, domain WHERE username = '%u' AND mailbox.active = '1' AND domain.domain = '%d' AND domain.active = '1'
20  
21  iterate_query = SELECT username as user FROM mailbox WHERE active ='1' ORDER BY domain, username

/usr/local/bin/quota-warning.sh

In der Konfiguration taucht das Plugins quota_warning auf. Dazu gehört das folgende Script:

 1  #!/bin/sh
 2  PERCENT=$1
 3  USER=$2
 4  ADMIN="uwe@maine-maildomain.de"
 5  FROM="postmaster@maine-maildomain.de"
 6  
 7  msg="From: $FROM
 8  To: $USER
 9  Bcc: $ADMIN
10  Subject: Quota-Warnung $PERCENT%  
11  
12  Lieber Nutzer,
13       
14  das Postfach $USER ist derzeit zu 
15  
16  $PERCENT% gefuellt.
17  
18  Bitte einige Mails loeschen und dann
19  den Papierkorb leeren!
20         
21  Herzlichen Dank
22  Das Mail-System."
23  
24  echo "$msg" | /usr/sbin/sendmail -f $FROM "$USER $ADMIN"
25  
26  exit 0

Die Auslastung der Quotas kann man sich im PostfixAdmin anschauen oder an der Konsole mittels

doveadm quota get -A

oder statt für alle auch für einen einzelnen Nutzer

doveadm quota get -u user@meine-maildomain.de

Man kann auch die Belegung neu berechnen lassen mittels

doveadm quota recalc -A

oder statt für alle auch für einen einzelnen Nutzer

doveadm quota recalc -u user@meine-maildomain.de

/usr/local/bin/postlogin.sh

Das folgende Script dient dazu, die Verzeichnisse home der einzelnen Mailbenutzer anzulegen. Da das mit dem welcome Plugin nicht funktioniert hat, bin ich auf das Postlogin von Dovecot ausgewichen. Dazu gehört dieses Script:

1  #!/bin/sh
2  local=${USER%%@*}
3  domain=${USER##*@}
4  
5  if [ ! -d "/var/vmail/$domain/$local/home" ]; then
6   mkdir  /var/vmail/$domain/$local/home
7  fi 
8  exec "$@"

Vor dem Dovecot-Neustart dann noch die Diffie-Hellman Parameter für Dovecot generieren

openssl dhparam -out /etc/dovecot/dh4096.pem 4096 

das dauert leider etwas.

/etc/dovecot/dovecot-dict-sql-user.conf

Diese Datei dient Dovecot dazu die jeweils aktuellen Quota-Werte an der richtigen Stelle in der MySQL-Datenbank zu speichern. Die erste Map für die Anzahl der Bytes, die zweite Map für die Anzahl der Nachrichten siehe https://wiki.dovecot.org/Quota/Dict .

 1  connect = host=/var/run/mysqld/mysqld.sock dbname=postfix user=postfix password=assword
 2   
 3  map {
 4     pattern = priv/quota/storage
 5     table = quota2
 6     username_field = username
 7     value_field = bytes
 8  }
 9   
10  map {
11     pattern = priv/quota/messages
12     table = quota2
13     username_field = username
14     value_field = messages
15 }

var/vmail/sieve/global/spam-global.sieve

1  require "fileinto";
2  
3  if header :contains "X-Spam-Flag" "YES" {
4     fileinto "Junk";
5  }
6  
7  if header :is "X-Spam" "Yes" {
8     fileinto "Junk";
9  }

/var/vmail/sieve/global/learn-ham.sieve

 1  require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
 2  
 3  if environment :matches "imap.mailbox" "*" {
 4     set "mailbox" "${1}";
 5  }
 6  
 7  if string "${mailbox}" "Trash" {
 8     stop;
 9  }
10  
11  pipe :copy "rspamc" ["learn_ham"];

/var/vmail/sieve/global/learn-spam.sieve

1  require ["vnd.dovecot.pipe", "copy", "imapsieve"];
2  pipe :copy "rspamc" ["learn_spam"];


Postfix

apt install postfix postfix-mysql postfix-policyd-spf-python alpine
service postfix stop

Für die Konfiguration ist es manchmal wichtig zu wissen, welche Versionsnummer das eigene Postfix-Programm hat. Diese Information liefert:

postconf -d mail_version

Die zentrale Konfigurationsdatei ist die master.cf. Hier werden die benötigten Dienste (z.B. smtp, uucp, ...) gestartet und zum Teil auch konfiguriert. Die eigentliche Konfiguration für den smtp Dienst befindet sich dann in der Datei main.cf. Weitere Dienste mit dem gleichen Programm, wie z.B. smtps oder submission übernehmen die dort gemachten Einstellungen, es sei den die Einstellung wird in einer Zeile, die mit " -o" beginnt überschrieben. Etwas ungewöhnlich ist eine Zeile wie -o smtpd_client_restrictions=$mua_client_restrictions, die so nicht fehlerfrei aktivierbar ist. Der rechte Teil ist nämlich quasi eine Variable, die in der main.cf mit Inhalt gefüllt werden muss.

/etc/postfix/master.cf

  1  #
  2  # Postfix master process configuration file.  For details on the format
  3  # of the file, see the master(5) manual page (command: "man 5 master" or
  4  # on-line: http://www.postfix.org/master.5.html).
  5  #
  6  # Do not forget to execute "postfix reload" after editing this file.
  7  #
  8  # ==========================================================================
  9  # service type  private unpriv  chroot  wakeup  maxproc command + args
 10  #               (yes)   (yes)   (no)    (never) (100)
 11  # ==========================================================================
 12  smtp      inet  n       -       y       -       -       smtpd
 13  #smtp      inet  n       -       y       -       1       postscreen
 14  #smtpd     pass  -       -       y       -       -       smtpd
 15  #dnsblog   unix  -       -       y       -       0       dnsblog
 16  #tlsproxy  unix  -       -       y       -       0       tlsproxy
 17  submission inet n       -       y       -       -       smtpd
 18    -o syslog_name=postfix/submission
 19    -o smtpd_tls_security_level=encrypt
 20    -o smtpd_sasl_auth_enable=yes
 21  #  -o smtpd_tls_auth_only=yes
 22  #  -o smtpd_reject_unlisted_recipient=no
 23    -o smtpd_client_restrictions=permit_sasl_authenticated,reject
 24  #  -o smtpd_helo_restrictions=$mua_helo_restrictions
 25    -o smtpd_sender_restrictions=reject_non_fqdn_sender,permit_sasl_authenticated,reject
 26  #  -o smtpd_recipient_restrictions=
 27    -o smtpd_relay_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
 28  #  -o milter_macro_daemon_name=ORIGINATING
 29  smtps     inet  n       -       y       -       -       smtpd
 30    -o syslog_name=postfix/smtps
 31    -o smtpd_tls_wrappermode=yes
 32    -o smtpd_sasl_auth_enable=yes
 33  #  -o smtpd_reject_unlisted_recipient=no
 34    -o smtpd_client_restrictions=permit_sasl_authenticated,reject
 35  #  -o smtpd_helo_restrictions=$mua_helo_restrictions
 36    -o smtpd_sender_restrictions=reject_non_fqdn_sender,permit_sasl_authenticated,reject
 37  #  -o smtpd_recipient_restrictions=
 38    -o smtpd_relay_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
 39  #  -o milter_macro_daemon_name=ORIGINATING
 40  #628       inet  n       -       y       -       -       qmqpd
 41  pickup    unix  n       -       y       60      1       pickup
 42  cleanup   unix  n       -       y       -       0       cleanup
 43  qmgr      unix  n       -       n       300     1       qmgr
 44  #qmgr     unix  n       -       n       300     1       oqmgr
 45  tlsmgr    unix  -       -       y       1000?   1       tlsmgr
 46  rewrite   unix  -       -       y       -       -       trivial-rewrite
 47  bounce    unix  -       -       y       -       0       bounce
 48  defer     unix  -       -       y       -       0       bounce
 49  trace     unix  -       -       y       -       0       bounce
 50  verify    unix  -       -       y       -       1       verify
 51  flush     unix  n       -       y       1000?   0       flush
 52  proxymap  unix  -       -       n       -       -       proxymap
 53  proxywrite unix -       -       n       -       1       proxymap
 54  smtp      unix  -       -       y       -       -       smtp
 55  relay     unix  -       -       y       -       -       smtp
 56         -o syslog_name=postfix/$service_name
 57  #       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
 58  showq     unix  n       -       y       -       -       showq
 59  error     unix  -       -       y       -       -       error
 60  retry     unix  -       -       y       -       -       error
 61  discard   unix  -       -       y       -       -       discard
 62  local     unix  -       n       n       -       -       local
 63  virtual   unix  -       n       n       -       -       virtual
 64  lmtp      unix  -       -       y       -       -       lmtp
 65  anvil     unix  -       -       y       -       1       anvil
 66  scache    unix  -       -       y       -       1       scache
 67  postlog   unix-dgram n  -       n       -       1       postlogd
 68  #
 69  # ====================================================================
 70  # Interfaces to non-Postfix software. Be sure to examine the manual
 71  # pages of the non-Postfix software to find out what options it wants.
 72  #
 73  # Many of the following services use the Postfix pipe(8) delivery
 74  # agent.  See the pipe(8) man page for information about ${recipient}
 75  # and other message envelope options.
 76  # ====================================================================
 77  #
 78  # maildrop. See the Postfix MAILDROP_README file for details.
 79  # Also specify in main.cf: maildrop_destination_recipient_limit=1
 80  #
 81  maildrop  unix  -       n       n       -       -       pipe
 82   flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
 83  #
 84  # ====================================================================
 85  #
 86  # Recent Cyrus versions can use the existing "lmtp" master.cf entry.
 87  #
 88  # Specify in cyrus.conf:
 89  #   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
 90  #
 91  # Specify in main.cf one or more of the following:
 92  #  mailbox_transport = lmtp:inet:localhost
 93  #  virtual_transport = lmtp:inet:localhost
 94  #
 95  # ====================================================================
 96  #
 97  # Cyrus 2.1.5 (Amos Gouaux)
 98  # Also specify in main.cf: cyrus_destination_recipient_limit=1
 99  #
100  #cyrus     unix  -       n       n       -       -       pipe
101  #  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
102  #
103  # ====================================================================
104  # Old example of delivery via Cyrus.
105  #
106  #old-cyrus unix  -       n       n       -       -       pipe
107  #  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
108  #
109  # ====================================================================
110  #
111  # See the Postfix UUCP_README file for configuration details.
112  #
113  #uucp      unix  -       n       n       -       -       pipe
114  # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
115  #
116  # Other external delivery methods.
117  #
118  ifmail    unix  -       n       n       -       -       pipe
119   flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
120  bsmtp     unix  -       n       n       -       -       pipe
121   flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
122  scalemail-backend unix	-	n	n	-	2	pipe
123   flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
124  #mailman   unix  -       n       n       -       -       pipe
125  # flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
126  # ${nexthop} ${user}
127  
128  # ==========================================================================
129  # service type  private unpriv  chroot  wakeup  maxproc command + args
130  #               (yes)   (yes)   (yes)   (never) (100)
131  # ==========================================================================
132  # Added using postfix-add-policy script:
133  policy-spf unix    -       n       n       -       0     spawn
134       user=nobody argv=/usr/bin/policyd-spf

/etc/postfix/main.cf

(die einzelnen Parameter sind in http://www.postfix.org/postconf.5.html dokumentiert)

  1  # See /usr/share/postfix/main.cf.dist for a commented, more complete version
  2  
  3  ##
  4  ##Netzwerk und Namen
  5  ##
  6  myorigin = /etc/mailname
  7  append_dot_mydomain = no
  8  masquerade_domains = meine-maildomain.de
  9  myhostname = mail.meine-maildomain.de
 10  mydestination = $myhostname, meine-maildomain.de, localhost, hetzner.meine-maildomain.de
 11  #mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
 12  
 13  smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
 14  inet_interfaces = all
 15  inet_protocols = ipv4
 16  
 17  
 18  ##
 19  # Einstellungen
 20  ##
 21  compatibility_level = 2
 22  smtputf8_enable = no 
 23  biff = no
 24  delay_warning_time = 4h
 25  readme_directory = no
 26  unknown_address_reject_code = 552
 27  unknown_client_reject_code = 551
 28  unknown_hostname_reject_code = 550
 29  #maps_rbl_reject_code = 451
 30  message_strip_characters = \0
 31  defer_transports = 
 32  mailbox_command = 
 33  mailbox_transport = 
 34  relayhost = 
 35  mailbox_size_limit = 0
 36  recipient_delimiter = +
 37  
 38  
 39  ##
 40  # TLS parameters
 41  ##
 42  
 43  ## ausgehende Verbindungen
 44  smtp_tls_security_level=may
 45  smtp_tls_mandatory_protocols=!SSLv2, !SSLv3
 46  #smtp_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
 47  #smtp_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
 48  smtp_tls_key_file = /etc/letsencrypt/live/meine-maildomain.de/privkey.pem
 49  smtp_tls_cert_file = /etc/letsencrypt/live/meine-maildomain.de/fullchain.pem
 50  smtp_tls_loglevel = 1
 51  smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
 52  
 53  smtp_tls_note_starttls_offer = yes
 54  
 55  ## eingehende Verbindungen
 56  smtpd_tls_security_level=may
 57  smtpd_tls_mandatory_protocols=!SSLv2, !SSLv3
 58  #smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
 59  #smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
 60  smtpd_tls_key_file=/etc/letsencrypt/live/meine-maildomain.de/privkey.pem
 61  smtpd_tls_cert_file=/etc/letsencrypt/live/meine-maildomain.de/fullchain.pem
 62  smtpd_tls_loglevel = 1
 63  smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
 64  
 65  smtpd_tls_received_header = yes
 66  smtpd_tls_session_cache_timeout = 3600s
 67  smtpd_tls_dh1024_param_file = /etc/postfix/dh2048.pem
 68  smtpd_delay_reject = yes
 69  smtpd_helo_required = yes
 70  
 71  ## SASL für eingehende Verbindungen
 72  broken_sasl_auth_clients = yes
 73  smtpd_sasl_local_domain = 
 74  smtpd_sasl_path = private/auth
 75  smtpd_sasl_security_options = noanonymous
 76  smtpd_sasl_type = dovecot
 77  # das kann/sollte hier auf no gesetzt werden, wenn smtps/submission aktiviert sind
 78  smtpd_sasl_auth_enable = yes
 79  smtp_sasl_auth_enable = no
 80  smtp_sasl_security_options = 
 81  smtp_sasl_password_maps = 
 82  
 83  
 84  ##
 85  # Maps und Tabellen
 86  ##
 87  alias_maps = hash:/etc/aliases
 88  alias_database = hash:/etc/aliases
 89  
 90  relay_domains = $mydestination, hash:/etc/postfix/relay
 91  #virtual_alias_domains = hash:/etc/postfix/virtual
 92  virtual_mailbox_base = /var/vmail/
 93  
 94  virtual_alias_maps = 
 95    proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf
 96    proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_maps.cf
 97    proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_catchall_maps.cf
 98  
 99  virtual_mailbox_domains =
100    proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf
101  
102  virtual_mailbox_maps =
103    proxy:mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
104    proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_mailbox_maps.cf
105  
106  virtual_transport = lmtp:unix:private/dovecot-lmtp
107  virtual_mailbox_limit = 0
108  virtual_minimum_uid = 303
109  virtual_uid_maps = static:303
110  virtual_gid_maps = static:303
111   
112  
113  ##
114  # Restrictions
115  ##
116  smtpd_client_restrictions = 
117    permit_sasl_authenticated
118    permit_mynetworks
119    reject_rbl_client cbl.abuseat.org
120    reject_rbl_client ix.dnsbl.manitu.net
121    reject_rbl_client bl.spamcop.net
122    reject_rbl_client dul.dnsbl.sorbs.net
123    reject_unknown_client
124  
125  smtpd_helo_restrictions = 
126    permit_mynetworks
127    reject_invalid_helo_hostname
128  
129  smtpd_sender_restrictions = 
130    hash:/etc/postfix/access
131    reject_unknown_sender_domain
132  
133  smtpd_recipient_restrictions = 
134    check_policy_service inet:127.0.0.1:12340
135    permit_sasl_authenticated
136    permit_mynetworks
137    reject_unauth_destination
138  
139  smtpd_relay_restrictions = 
140    permit_mynetworks
141    permit_sasl_authenticated
142    reject_unauth_destination
143  
144  # Block clients that speak too early.
145  smtpd_data_restrictions = reject_unauth_pipelining
146  
147  ##
148  # Spamfilter und DKIM-Signaturen via Rspamd
149  ##
150  
151  #content_filter = smtp-amavis:[localhost]:10024
152  
153  smtpd_milters = inet:localhost:11332
154  non_smtpd_milters = inet:localhost:11332
155  milter_protocol = 6
156  milter_mail_macros =  i {mail_addr} {client_addr} {client_name} {auth_authen}
157  milter_default_action = accept
158  
159  
160  ##
161  # Limits 
162  ##
163  mailbox_size_limit = 0
164  message_size_limit = 0
165  strict_8bitmime = no
166  strict_rfc821_envelopes = yes
167  
168  smtpd_client_message_rate_limit = 50
169  smtpd_client_connection_rate_limit = 10
170  smtpd_client_recipient_rate_limit = 50
171  smtpd_client_connection_count_limit = 25
172  bounce_queue_lifetime = 3d
173  maximal_queue_lifetime = 3d
174  
175  smtpd_recipient_limit = 50

Auch für Postfix Diffie-Hellman-Parameter generieren

Die DH-Parameter für Postfix sind hier nur 2048 Bit stark, um zu älteren Mailservern kompatibel zu bleiben.

openssl dhparam -out /etc/postfix/dh2048.pem 2048


Postfix und MySQL

Damit Postfix MySQL-Tabellen nutzen kann muss man folgende Dateien einrichten:

/etc/postfix/mysql_virtual_alias_maps.cf
/etc/postfix/mysql_virtual_domains_maps.cf
/etc/postfix/mysql_virtual_mailbox_limit_maps.cf  darum kümmert sich Dovecot
/etc/postfix/mysql_virtual_mailbox_maps.cf
/etc/postfix/mysql_virtual_alias_domain_maps.cf
/etc/postfix/mysql_virtual_alias_domain_catchall_maps.cf
/etc/postfix/mysql_virtual_alias_domain_mailbox_maps.cf

Diese mysql-Dateien im Verzeichnis /etc/postfix muss man anpassen an die eigenen Datenbankeinstellungen. Muster dazu finden sich in der Datei /usr/share/doc/postfixadmin/DOCUMENTS/POSTFIX_CONF.txt.gz. Im Prinzip wird Postfix hier mitgeteilt, wie die Datenbanktabellen abgefragt werden. Benutzername und Passwort müssen hier an die eigenen Einstellungen angepasst werden.

mysql_virtual_alias_maps.cf

 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query = SELECT goto FROM alias WHERE address='%s' AND active = '1'

mysql_virtual_domains_maps.cf

 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'

mysql_virtual_mailbox_limit_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'

mysql_virtual_mailbox_maps.cf

 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'

mysql_virtual_alias_domain_maps.cf

 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'

mysql_virtual_alias_domain_catchall_maps.cf

 # handles catch-all settings of target-domain
 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query  = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'

mysql_virtual_alias_domain_mailbox_maps.cf

 user = postfix
 password = password
 hosts = localhost
 dbname = postfix
 query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'

Firewall anpassen

 ufw allow Postfix
 ufw allow "Postfix SMTPS"
 ufw allow "Postfix Submission"
 ufw allow "Dovecot IMAP"
 ufw allow "Dovecot Secure IMAP"
 ufw allow "Dovecot POP3"
 ufw allow "Dovecot Secure POP3"

Vor dem Neustart der Dienste müssen die Datenbanken erzeugt werden:

postmap /etc/postfix/access
newaliases


Konfiguration der Clients

Thunderbird als Client-Programm lässt sich problemlos so konfigurieren, dass er mit den beiden verschlüsselten Protokollen arbeiten kann. Beispieleinstellungen ergeben sich aus dem folgenden Bildern.

Konfiguration von Thunderbird für die Nutzung von SMTPS

Konfiguration von Thunderbird für die Nutzung von Submission

Postfix und der Kampf gegen Spam

Heutzutage ist deutlich mehr als 90% aller verschickten Mails unerwünschter Spam. Die zentrale Stelle im Kampf gegen den Spam ist natürlich der Mailserver. Postfix hat eine Reihe von Mechanismen eingebaut, die versuchen Spam zu vermeiden:

  • RBL, ein System bei dem bestimmte Server gefragt werden, ob ihnen der abliefernde Server als Spamlieferant bekannt ist
  • Regeln wie invalid_hostname, hier überprüft Postfix, ob sich der Name, der zu der abliefernden IP-Adresse gehört zur gleichen IP wieder auflösen lässt
  • Access-Listen über die z.B. Mailadressen oder ganze Domains blockiert werden können

Wirklich Viren und Spam erkennen kann Postfix aber nicht. Dazu bedarf es eines externen Hilfsprogrammes, wie AmaVis oder rspamd. Diese Programme können allgemein nach zwei unterschiedlichen Konzepten eingebunden werden.

  • als Mail-Proxy
  • Über die Milter-Schnittstelle

AmaVis wird in der Regel als Proxy eingebunden. Dabei nimmt Postfix auf dem normalen Port z.B. 25 eine Mail an und gibt sie zur Untersuchung an AmaVis weiter. Wenn AmaVis mit der Mail fertig ist, dann gibt er die an einen zweiten Postfix-Prozess weiter, der z.B. auf Port 10025 lauscht. Dieses Verfahren kann man gut in den Header-Zeilen der Mail nachvollziehen, hier taucht der eigene Server nämlich doppelt auf. Nachteil bei dem Proxy-System ist, dass AmaVis die Mail erst sehen kann, wenn sie angenommen wurde. Auch, wenn sie als Spam klassifiziert wurde muss sie zugestellt werden. Konfiguriert wird der zweite Postfix-Daemon über die letzten Zeilen in der /etc/postfix/master.cf

127.0.0.1:10025 inet n    -       -       -       -     smtpd
    -o content_filter=
    -o smtpd_delay_reject=no
    ...

und der Zugriff auf AmaVis erolgt über eine Zeile in der main.cf

content_filter = smtp-amavis:[localhost]:10024

Über die Milter-Schnittstelle kann sich ein externes Programm wir rspamd in den Postfix-Prozess einklinken. Es wird also kein zweiter Postfix-Prozess benötigt. Außerdem ist das externe Programm so in der Lage eine Mail als Spam zu klassifizieren, ohne das sie letztendlich angenommen wurde. Postfix kann dann den Prozess abbrechen und dem sendenden Mailserver dabei eine entsprechende Fehlermeldung zukommen lassen. Damit sollten als Spam gekennzeichnete Mail überhaupt nicht mehr beim Empfänger auftauchen.

Für Amavis gibt es auch ein Milter-Programm, das ist aber schon einige Zeit nicht mehr aktualisiert worden, vor allem in den Ubuntu-Versionen nicht.


rspamd

Die Entwickler des rspamd möchten nicht, dass man die Pakete aus den jeweiligen Distributionen benutzt. Damit wollen sie einerseits erreichen, dass immer die aktuellste Version installiert ist und die Ubuntu-Paketentwickler nicht an der Konfiguration gedreht haben. Daher soll man das Programm immer von der Entwickler-Seite laden, dafür gibt es auch für Ubuntu die passenden Pakete.

apt install wget
wget -O- https://rspamd.com/apt-stable/gpg.key | apt-key add -
echo "deb [arch=amd64] http://rspamd.com/apt-stable/ $(lsb_release -c -s) main" > /etc/apt/sources.list.d/rspamd.list
echo "deb-src [arch=amd64] http://rspamd.com/apt-stable/ $(lsb_release -c -s) main" >> /etc/apt/sources.list.d/rspamd.list
apt update
apt install rspamd clamav clamav-daemon redis-server
service rspamd stop

Bei anderen Prozessor-Architekturen als amd64 muss man den Ausdruck [arch=amd64] weglassen oder passend ersetzen.

Für die Anpassung der Konfiguration haben die Entwickler sich ein interessantes System ausgedacht, wie man am Inhalt der Datei /etc/rspamd/rspamd.conf erkennen kann

# For specific modules or configuration you can also modify
# '$LOCAL_CONFDIR/local.d/file.conf' - to add your options or rewrite defaults
# '$LOCAL_CONFDIR/override.d/file.conf' - to override the defaults

Für jeden der vorhandenen Konfigurationsbereiche gibt es jeweils eine vorbereitete Konfigurationsdatei im Hauptverzeichnis der Konfiguration. Änderungsdateien legt man unter dem gleichen Namen entweder ins Unterverzeichnis:

  • local.d oder
  • override.d

Einstellungen die im Unterverzeichnis local.d gemacht werden ergänzen die vorhandenen Einstellungen, bei gleichem Bezeichner überschreiben sie auch. Einstellungen im Verzeichnis override.d löschen erst einmal alle Voreinstellungen, nur die neu gemachte Einstellung bleibt gültig.

Die Konfiguration des rspamd erfolgt erst einmal in relativ wenigen Schritten:

cd /etc/rspamd/

dann lässt man sich einen Passwort-Hash erzeugen

rspamadm pw 

das Passwort wird im Dialog abgefragt, der Hash, z.B. $2$piqhd3fcs6ncafzfw7qhjehjjwdmaoeo$nkdo6u8ot7ydibt6eipn7m97opzhcb3mcqpq9n64un7bec3mzhzy erscheint dann auf dem Bildschirm und muss in die Datei /etc/rspamd/local.d/worker-controller.inc übertragen werden.

/etc/rspamd/local.d/worker-controller.inc

password = "$2$piqhd3fcs6ncafzfw7qhjehjjwdmaoeo$nkdo6u8ot7ydibt6eipn7m97opzhcb3mcqpq9n64un7bec3mzhzy";
enable_password = "$2$piqhd3fcs6ncafzfw7qhjehjjwdmaoeo$nkdo6u8ot7ydibt6eipn7m97opzhcb3mcqpq9n64un7bec3mzhzy";

Im Proxy-Modus scannt rspamd_proxy die Nachrichten selbst und spricht direkt mit dem MTA unter Verwendung des Milter-Protokolls. Der Vorteil dieses Ansatzes ist seine Einfachheit.

/etc/rspamd/local.d/worker-proxy.inc

bind_socket = "localhost:11332";
milter = yes;
timeout = 120s;
upstream "local" {
 default = yes;
 self_scan = yes;
}


In der Regel wird man die Logmeldungen nicht in einer extra-Datei haben wollen, sondern im Syslog, dazu wieder eine kleine Erweiterung:

/etc/rspamd/local.d/local.d/logging.inc

type = "syslog";
level = "warning";


Die Header-Zeilen konfiguriert man

/etc/rspamd/local.d/milter_headers.conf

use = ["x-spamd-bar", "x-spam-level", "authentication-results"];
authenticated_headers = ["authentication-results"];

Das kleine Datenbank-Tool redis soll für Bayes-Filter genutzt werden und Auto-Learning soll aktiv sein:

/etc/rspamd/local.d/classifier-bayes.conf

backend = "redis";
autolearn = true;

Wo findet redis seine Partner

/etc/rspamd/local.d/redis.conf

servers = "127.0.0.1";

Clamav aktivieren, er muss natürlich installiert sein und auf der angegebenen Socket /var/run/clamav/clamd.ctl lauschen

/etc/rspamd/local.d/antivirus.conf

clamav {
 action = "reject";
 scan_mime_parts = true;
 log_clean = true;
 symbol = "CLAM_VIRUS";
 type = "clamav";
 servers = "/var/run/clamav/clamd.ctl";
}

Mail-Subject ändern (Vorsicht, das hat Seiteneffekte und erfordert eine längere Beschreibung).

/etc/rspamd/local.d/actions.conf

subject = "***SPAM*** %s";
rewrite_subject = 6;


Individuelle Black- und White-Listen

/etc/rspamd/local.d/multimap.conf

WHITELIST_IP {
   type = "ip";
   map = "/var/lib/rspamd/whitelist_ip.map";
   description = "Lokale ip whitelist -> accept";
   action = "accept";
}

WHITELIST_FROM {
   type = "from";
   map = "/var/lib/rspamd/whitelist_from.map";
   description = "Lokale from Whitelist -> accept";
   action = "accept";
}

WHITELIST_FROM_DOMAIN {
  type = "from";
  filter = "email:domain";
  map = "/var/lib/rspamd/whitelist_from_domain.map";
  description = "Lokale from Domain Whitelist -> -6.0";
  score = -6.0
}

BLACKLIST_IP {
  type = "ip";
  map = "/var/lib/rspamd/blacklist_ip.map";
  description = "Lokale ip Blacklist -> reject";
  action = "reject";
}

BLACKLIST_FROM {
  type = "from";
  map = "/var/lib/rspamd/blacklist_from.map";
  description = "Lokale from Blacklist -> reject";
  action = "reject";
}

Diese Listen muss man nicht anlegen, das passiert automatisch, wenn man z.B. in der GUI einen Eintrag vornimmt. Statt der action-Zeilen kann man auch score-Zeilen nutzen, um die Entscheidung abzuschwächen:

score=-6 (für die Whitelisten
score=6  (für die Blacklisten)

Hinweis: Man darf auf keinen Fall +6 schreiben, dann wird dies vom rspamd als String interpretiert und nicht als Zahl. Hat man eine Liste mit regexp=true, so muss die Map mit einem Zeilenumbruch enden, sonst wird die letzte Zeile nicht berücksichtigt.

Weitere Möglichkeiten und Beispiele finden sich unter https://rspamd.com/doc/modules/multimap.html

HAM/Spam lernen

Der rspamd lernt aus den eingehenden Mail. Man kann ihn aber auch vorab mit den vorhandenen Mails trainieren. Dazu bin ich in mein Postfachverzeichnis /var/vmail/<domain>/<user> gegangen und habe folgendes Kommandos abgesetzt:

find cur/* -type f  -exec /usr/bin/rspamc learn_ham {} \;
find .Junk/cur/* -type f  -exec /usr/bin/rspamc learn_spam {} \;

Falls sehr viele Mails vorhanden sind kann man die Suche einschränken, indem man vor dem Stern noch passende Ziffern anhand der Liste mit angibt. Also z.B.

find cur/16* -type f

DKIM-Signierung

Rspamd kann das Signieren der ausgehenden Mail übernehmen. Dazu muss zunächst ein entsprechender Schlüssel erzeugt und hinterlegt werden.

Hinweis: Meist wird empfohlen eine Schlüssellänge 2048 zu nutzen (-b 2048), der dann aber längere Schlüssel lässt sich bei manchen Nameservern aber nicht speichern.

mkdir /var/lib/rspamd/dkim/
rspamadm dkim_keygen -b 1024 -s 2020 -k /var/lib/rspamd/dkim/2020.key > /var/lib/rspamd/dkim/2020.txt
chown -R _rspamd:_rspamd /var/lib/rspamd/dkim
chmod 440 /var/lib/rspamd/dkim/*

Den erzeugten Key kann man sich ansehen mittels:

cat /var/lib/rspamd/dkim/2020.txt

Die Zeichenkette 2020 ist der sogenannte Selektor. Damit lässt sich der passende Schlüssel im Zonenfile des Nameservers wiederfinden.

Mich hat ursprünglich irritiert, dass sich die erzeugten Schlüssel an Anfang und Ende kaum unterschieden, ich hatte befürchtet, dass immer der gleiche Schlüssel erzeugt wird. Dazwischen unterscheiden sich die Schlüssel aber auf alle Fälle, so ist der Vergleich bzw. die Zuordnung aber schwierig.

/etc/rspamd/local.d/dkim_signing.conf

path = "/var/lib/rspamd/dkim/$selector.key";
selector = "2020";

### Erlaube DKIM signing auch für alias sender Adressen
allow_username_mismatch = true;

Die gleiche Information wird auch vom ARC-Modul benötigt.

ln -s /etc/rspamd/local.d/dkim_signing.conf /etc/rspamd/local.d/arc.conf

Das Webinterface von rspamd

Der rspamd stellt ein Webinterface zur Verfügung, welches auf dem Port 11334 des Servers auf Verbindungen wartet. Man hat jetzt drei Möglichkeiten auf dieses Interface zuzugreifen:

  • Man gibt Port 11334 in der Firewall frei und benötigt dann das Passwort, welches man bei der Konfiguration des rspamd gesetzt hat, zusätzlich muss man erreichen, dass der Worker auch auf der normalen IP-Adresse lauscht, dazu ergänzt man in der worker-controller.inc nach den Passwörtern noch
bind_socket = "*:11334";
  • Ein ssh-Tunnel zum Server ssh -L 8080:localhost:11334 root@meine-maildomain.de -N macht das Webinterface unter Port 8080 des lokalen Rechners verfügbar
  • Web-Proxy für den Apache, in einem virtuellen Server ergänzt man:
 RewriteEngine On
 ProxyRequests Off
 <Location /rspamd>
        Order allow,deny
        Allow from all
 </Location>
 RewriteRule ^/rspamd$ /rspamd/ [R,L]
 RewriteRule ^/rspamd/(.*) http://localhost:11334/$1 [P,L]

Das Proxy-Modul muss dazu installiert sein:

a2enmod proxy
a2enmod proxy_http

Damit ist dann nach einem Neustart des Webservers der rspamd über das Verzeichnis /rspamd/ erreichbar.

Warnung: Mit dem Proxy-Modul sollte man sehr vorsichtig sein. Es gibt immer wieder Leute, die versuchen den Server als Forward-Proxy zu nutzen, um verschleiert auf andere Server zugreifen zu können. Darstellung der Arbeitsweise eines Forward-Proxy

Das kann man mit der Einstellung ProxyRequests Off verhindern. Wir brauchen hier einen Reverse-Proxy, der einen internen Dienst (rspamd) über das Webinterface zur Verfügung stellt. Das ist weniger problematisch. Darstellung der Arbeitsweise eines Reverse-Proxy

Eine ausführliche Beschreibung habe ich gefunden unter: https://www.netnea.com/cms/apache-tutorial-9-reverse-proxy-einrichten/

Roundcube

Roundcube ist ein sehr beliebter und mächtiger Webmail-Client. Er lässt sich über eine Reihe von Plugins erweitern. Ich nutze gern die Plugins

  • sieve, zu Pflegen der entsprechenden Regeln
  • Password, zum Ändern des eigenen Passwortes in der Postfix-Datenbank.

Zuerst die Installation:

apt install roundcube roundcube-plugins roundcube-plugins-extra

Das Programm wird standardmäßig im Verzeichnis /var/lib/roundcube/ abgelegt (in Version 1.4.3). Hierbei werden einige Unterverzeichnisse und Dateien an andere Stellen verlinkt

  • config -> /etc/roundcube
  • program -> /usr/share/roundcube/program
  • .htaccess -> /etc/roundcube/htaccess
  • logs -> /var/log/roundcube
  • robots.txt -> /usr/share/roundcube/robots.txt

Die Plugins finden sich im Verzeichnis /usr/share/roundcube/plugins/, wobei die Konfigurationsdateien, die auf /etc/roundcube/plugins/ verlinkt wurden, jeweils nahezu leer sind. Ein Muster findet sich dann jeweils unter /usr/share/roundcube/plugins/ in der Datei config.inc.php.dist

Also

cp /usr/share/roundcube/plugins/password/config.inc.php.dist /etc/roundcube/plugins/password/config.inc.php
cp /usr/share/roundcube/plugins/managesieve/config.inc.php.dist /etc/roundcube/plugins/managesieve/config.inc.php
cp /usr/share/roundcube/plugins/zipdownload/config.inc.php.dist /etc/roundcube/plugins/zipdownload/config.inc.php
cp /usr/share/roundcube/plugins/jqueryui/config.inc.php.dist  /etc/roundcube/plugins/jqueryui/config.inc.php

Danach sind dann ein paar Dateien zu editieren.

/etc/roundcube/debian-db.php

 <?php
 ##
 ## database access settings in php format
 ## automatically generated from /etc/dbconfig-common/roundcube.conf
 ## by /usr/sbin/dbconfig-generate-include
 ##
 ## by default this file is managed via ucf, so you shouldn't have to
 ## worry about manual changes being silently discarded.  *however*,
 ## you'll probably also want to edit the configuration file mentioned
 ## above too.
 ##
 $dbuser='roundcube';
 $dbpass='assword';
 $basepath='';
 $dbname='roundcube';
 $dbserver='localhost';
 $dbport='';
 $dbtype='mysql';

/etc/roundcube/plugins/password/config.inc.php ab Zeile 95

 95  // SQL Driver options
 96  // ------------------
 97  // PEAR database DSN for performing the query. By default
 98  // Roundcube DB settings are used.
 99  $config['password_db_dsn'] = 'mysql://postfix:assword@localhost/postfix';
100  
101  // The SQL query used to change the password.
102  // The query can contain the following macros that will be expanded as follows:
103  //      %p is replaced with the plaintext new password
104  //      %P is replaced with the crypted/hashed new password
105  //         according to configured password_method
106  //      %o is replaced with the old (current) password
107  //      %O is replaced with the crypted/hashed old (current) password
108  //         according to configured password_method
109  //      %h is replaced with the imap host (from the session info)
110  //      %u is replaced with the username (from the session info)
111  //      %l is replaced with the local part of the username
112  //         (in case the username is an email address)
113  //      %d is replaced with the domain part of the username
114  //         (in case the username is an email address)
115  // Deprecated macros:
116  //      %c is replaced with the crypt version of the new password, MD5 if available
117  //         otherwise DES. More hash function can be enabled using the password_crypt_hash
118  //         configuration parameter.
119  //      %D is replaced with the dovecotpw-crypted version of the new password
120  //      %n is replaced with the hashed version of the new password
121  //      %q is replaced with the hashed password before the change
122  // Escaping of macros is handled by this module.
123  // Default: "SELECT update_passwd(%c, %u)"
124  $config['password_query'] = 'UPDATE mailbox SET password=%c WHERE username=%u';
125  
126  // By default the crypt() function which is used to create the %c
127  // parameter uses the md5 algorithm (deprecated, use %P).
128  // You can choose between: des, md5, blowfish, sha256, sha512.
129  $config['password_crypt_hash'] = 'md5';
130  ...

/etc/roundcube/plugins/managesieve/config.inc.php

 <?php
 
 // managesieve server port. When empty the port will be determined automatically
 // using getservbyname() function, with 4190 as a fallback.
 $config['managesieve_port'] = 4190;

/etc/roundcube/config.inc.php In der Hauptkonfigurationsdatei von Roundcube sind in zwei Abschnitten Änderungen notwendig.

Ab Zeile 25

25 // The IMAP host chosen to perform the log-in.
26 // Leave blank to show a textbox at login, give a list of hosts
27 // to display a pulldown menu or set one host as string.
28 // Enter hostname with prefix ssl:// to use Implicit TLS, or use
29 // prefix tls:// to use STARTTLS.
30 // Supported replacement variables:
31 // %n - hostname ($_SERVER['SERVER_NAME'])
32 // %t - hostname without the first part
33 // %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
34 // %s - domain name after the '@' from e-mail address provided at login screen
35 // For example %n = mail.domain.tld, %t = domain.tld
36 $config['default_host'] = 'localhost';
37 
38 // SMTP server host (for sending mails).
39 // Enter hostname with prefix ssl:// to use Implicit TLS, or use
40 // prefix tls:// to use STARTTLS.
41 // Supported replacement variables:
42 // %h - user's IMAP hostname
43 // %n - hostname ($_SERVER['SERVER_NAME'])
44 // %t - hostname without the first part
45 // %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
46 // %z - IMAP domain (IMAP hostname without the first part)
47 // For example %n = mail.domain.tld, %t = domain.tld
48 $config['smtp_server'] = 'localhost';
49 
50 // SMTP port. Use 25 for cleartext, 465 for Implicit TLS, or 587 for STARTTLS (default)
51 $config['smtp_port'] = 25;

Ab Zeile 74

74 // List of active plugins (in plugins/ directory)
75 // Debian: install roundcube-plugins first to have any
76 $config['plugins'] = array(
77   'archive',
78   'zipdownload',
79   'managesieve',
80   'password',
81 );

Die Datenbankkonfiguration findet sich dann in der Datei /usr/share/roundcube/SQL/mysql.initial.sql


ToDo

Was ich noch einmal ausprobieren muss ist die Möglichkeit User aus der Datenbank mit dem folgenden Script auch wieder zu löschen

/usr/share/roundcube/bin/deluser.sh

Der Hilfe-Text klingt vielversprechend

Usage: deluser.sh [--host=HOST][--age=DAYS][--dry-run] [username]
--host=HOST  The IMAP hostname or IP the given user is related to
--age=DAYS   Delete all users who have not logged in for more than X days
--dry-run    List users but do not delete them (for use with --age)

mailman

Mich hat überrascht, wie mühsam es ist eine Beschreibung zu finden, wie man mailman mit virtuellen Postfix-Mail verknüpft. Die hier beschriebene Konfiguration lässt einen Mailinglisten-Namen nur einmalig über alle Domains zu. Wenn es also eine Liste info@virtual1.tld gibt, dann ist es nicht möglich ebenfalls info@virtual2.tld einzurichten.

Weitere Informationen unter:

Zuerst muss man den Mailman installieren, per

apt install mailman

dann muss man ihn in der Apache-Konfiguration aktivieren:

Zuerst muss man die Konfigurationsdatei kopieren:

cp /etc/mailman/apache.conf  /etc/apache2/conf-available/mailman.conf

und dann aktivieren mittels:

a2enconf mailman

Danach den Apache neu starten.

service apache2 reload

Vor dem Neustart habe ich dann noch in jede vhost-Konfiguration die Zugriff auf Mailman bekommen soll die Zeilen

# Mailman-Pfade, ScriptAlias muss vor dem allgemeinen cgi-bin stehen
 ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
 Alias /pipermail/ /var/lib/mailman/archives/public/
 Alias /images/mailman/ /usr/share/images/mailman/

 ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin/"

eingefügt. Wichtig ist, dass die drei neuen Zeilen vor der üblichen ScriptAlias-Zeile für cgi-bin stehen, sonst funktioniert der SriptAlias für Mailman nicht.

Eine Änderung erfolgt in der Datei /etc/postfix/main.cf dort muss die Zeile

alias_maps = hash:/etc/aliases

erweitert werden zu

alias_maps = hash:/etc/aliases, hash:/var/lib/mailman/data/aliases

damit die Alias-Einträge vom Mailman berücksichtigt werden. Zusätzlich muss für die virtuellen Domains der Eintrag virtual_alias_maps erweitert werden zu

virtual_alias_maps = hash:/etc/postfix/virtual, hash:/var/lib/mailman/data/virtual-mailman, proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf, proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_catchall_maps.cf

Außerdem habe ich in der master.cf die mailan-spezifischen Zeilen auskommentiert:

#mailman   unix  -       n       n       -       -       pipe
#  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
#  ${nexthop} ${user}

Dahinter würde ein anderer Ansatz stecken.


Nun legt man das Site-Kennwort fest mit

   /usr/lib/mailman/bin/mmsitepass

In der Datei /usr/lib/mailman/Mailman/mm_cfg.py finden sich einige Grundeinstellungen für den Mailman, die man mit einem Texteditor vornehmen kann, weitere Standardvorgaben findet man in der Datei /usr/lib/mailman/Mailman/Defaults.py.

Also ergänzen wir ein paar Zeilen in der Datei /usr/lib/mailman/Mailman/mm_cfg.py:

##################################################
# Put YOUR site-specific settings below this line.
DEFAULT_SERVER_LANGUAGE = 'de'
DEFAULT_ARCHIVE = Off
DEFAULT_ARCHIVE_PRIVATE = 1
DEFAULT_URL_HOST = 'myserver.tld'
DEFAULT_EMAIL_HOST = 'myserver.tld'
add_virtualhost('virtual1.tld', 'virtual1.tld')
add_virtualhost('virtual2.tld', 'virtual2.tld')
MTA = 'Postfix'
POSTFIX_STYLE_VIRTUAL_DOMAINS = ['virtual1.tld', 'virtual2.tld']
DEFAULT_REQUIRE_EXPLICIT_DESTINATION = No

Jetzt legt man noch die notwendige Standard-Mailingliste "mailman" an mittels

/usr/lib/mailman/bin/newlist -l de mailman postmaster@myserver.tld passwort

Dann kann man den Dienst starten:

systemctl start mailman.service

und dann auch veranlassen, dass er zukünftig automatisch gestartet wird

systemctl enable mailman.service

Wichtig ist jetzt und beim Neuanlegen einer Mailingliste immer ein Blick in das Verzeichnis /var/lib/mailman/data, hier müssen vier Dateien mit aktuellem Zeitstempel auftauchen:

-rw-rw---- 1 root list  1843 Feb 19 15:35 aliases
-rw-r----- 1 root list 12288 Feb 19 15:35 aliases.db
-rw-rw---- 1 root list  1011 Feb 19 15:35 virtual-mailman
-rw-r----- 1 root list 12288 Feb 19 15:35 virtual-mailman.db

Diese Dateien werden automatisch erzeugt vom Programm /usr/lib/mailman/bin/genaliases . Manchmal dauert es etwas, bis die Dateien auftauchen, man kann das Programm auch von Hand aufrufen.

Hinweis: Die Dateien virtual-mailman und virtual-mailman.db werden natürlich erst angelegt, wenn für eine virtuelle Domain eine Mailingliste erzeugt wird. Solange bis die Dateien existieren ist die Postfix-Konfiguration nicht in Ordnung: Das hat mich mal wieder etwas Zeit gekostet.

Hier einmal ein Inhalt der Datei virtual-mailman

# This file is generated by Mailman, and is kept in sync with the binary hash
# file virtual-mailman.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you
# know what you're doing, and can keep the two files properly in sync.  If you
# screw it up, you're on your own.
#
# Note that you should already have this virtual domain set up properly in
# your Postfix installation.  See README.POSTFIX for details.

# LOOP ADDRESSES START
mailman-loop@apachebuch.de      mailman-loop
# LOOP ADDRESSES END 

# STANZA START: test
# CREATED: Sun Feb 19 15:35:19 2017
test@apachebuch.de              test
test-admin@apachebuch.de        test-admin
test-bounces@apachebuch.de      test-bounces
test-confirm@apachebuch.de      test-confirm
test-join@apachebuch.de         test-join
test-leave@apachebuch.de        test-leave
test-owner@apachebuch.de        test-owner
test-request@apachebuch.de      test-request
test-subscribe@apachebuch.de    test-subscribe
test-unsubscribe@apachebuch.de  test-unsubscribe
# STANZA END: test

Dazu gehört dann folgend aliases-Datei:

# This file is generated by Mailman, and is kept in sync with the
# binary hash file aliases.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE
# unless you know what you're doing, and can keep the two files properly
# in sync.  If you screw it up, you're on your own.

# The ultimate loop stopper address
mailman-loop: /var/lib/mailman/data/owner-bounces.mbox 

# STANZA START: mailman
# CREATED: Sun Feb 19 15:32:09 2017
mailman:             "|/var/lib/mailman/mail/mailman post mailman"
mailman-admin:       "|/var/lib/mailman/mail/mailman admin mailman"
mailman-bounces:     "|/var/lib/mailman/mail/mailman bounces mailman"
mailman-confirm:     "|/var/lib/mailman/mail/mailman confirm mailman"
mailman-join:        "|/var/lib/mailman/mail/mailman join mailman"
mailman-leave:       "|/var/lib/mailman/mail/mailman leave mailman"
mailman-owner:       "|/var/lib/mailman/mail/mailman owner mailman"
mailman-request:     "|/var/lib/mailman/mail/mailman request mailman"
mailman-subscribe:   "|/var/lib/mailman/mail/mailman subscribe mailman"
mailman-unsubscribe: "|/var/lib/mailman/mail/mailman unsubscribe mailman"
# STANZA END: mailman

# STANZA START: test
# CREATED: Sun Feb 19 15:35:19 2017
test:             "|/var/lib/mailman/mail/mailman post test"
test-admin:       "|/var/lib/mailman/mail/mailman admin test"
test-bounces:     "|/var/lib/mailman/mail/mailman bounces test"
test-confirm:     "|/var/lib/mailman/mail/mailman confirm test"
test-join:        "|/var/lib/mailman/mail/mailman join test"
test-leave:       "|/var/lib/mailman/mail/mailman leave test"
test-owner:       "|/var/lib/mailman/mail/mailman owner test"
test-request:     "|/var/lib/mailman/mail/mailman request test"
test-subscribe:   "|/var/lib/mailman/mail/mailman subscribe test"
test-unsubscribe: "|/var/lib/mailman/mail/mailman unsubscribe test"
# STANZA END: test

Die Dateien mit der Endung .db sind jeweils gehashte Versionen der Klartext-Dateien.

Will man eine neue Mailingliste erstellen, so ist das kein Problem, sofern es für die betreffende Domain schon eine Liste gibt. Dann kann man die einfach über http://server.tld/mailman/create anlegen. Wichtig ist aber, dass man anschließend noch einmal in die Konfiguration der Liste geht Allgemeine Optionen und dort unter Bevorzugter Hostname für E-Mail an diese Liste die zugehörige Domain angibt.

Gibt es für die Domain noch keine Mailingliste, so muss man darauf achten, dass die Domain per postfixadmin eingetragen ist, sonst nimmt das System keinerlei Mails für die Domain an. Dann mauss man in der Datei /usr/lib/mailman/Mailman/mm_cfg.py eine add_virtualhost() Zeile ergänzen und die Domain auch bei POSTFIX_STYLE_VIRTUAL_DOMAINS mit eintragen. Erst danach kann man dann die Liste sinnvoll anlegen.

Kontrollieren kann man noch, ob das Startscript legt die Datei /etc/cron.d/mailman anlegt. Beim Stoppen löscht es diese Datei auch wieder.

Migration vorhandener Listen

Bei einem Serverwechsel taucht immer wieder das Problem auf, dass man auch vorhandene Mailman-Listen umziehen möchte. Der Vorgang ist eigentlich ganz einfach. Die Beschreibung geht davon aus, dass Mailman auf dem Zielrechner bereits eingerichtet und funktionsfähig ist.

Nach meinen Erfahrungen besteht die einfachste Lösung darin die Listen auf dem neuen Server neu anzulegen

/usr/lib/mailman/bin/newlist -l de info@meine-maildomain.de uwe@meine-maildomain.de GeHeim

und dann mailman zu stoppen und die alten Dateien zu kopieren.

Im ersten Schritt müssen wir die Listen-Verzeichnisse aus den Ordnern

/var/lib/mailman/archives/private/
/var/lib/mailman/lists/

kopieren. Für jede Liste gibt es in den Ordnern ein gleichnamiges Verzeichnis und in dem Archivordner noch jeweils eines mit der Extension .mbox. Diese Verzeichnisse werden einfach auf den neuen Server kopiert und dem richtigen Eigentümer übereignet. Unter Ubuntu gehören die Dateien in der Regel lists.list und die übergeordneten Verzeichnisse root.list.

Die Eigentümer müssen jeweils noch richtig gesetzt werden.

Dann geht es noch an zwei Dateien im Verzeichnis data:

aliases
virtal-mailman

Hier finden sich für jede Liste mehrere Zeilen mit den Weiterleitungen. Die benötigten Zeilen sind durch das Neuanlegen der Liste entstanden, oder man holt sie sich vom alten Server und hängt sie an die Dateien auf dem neuen Server an. Am Ende müssen die entstandenen Dateien noch in das benutzte Datenbank-Format umgewandelt werden:

/usr/lib/mailman/bin/genaliases

oder man wartet bis mailman das selber macht.

Danach kann man dann mailman wieder starten.

Falls es Probleme mit der URL für die Liste gibt, so ist wichtig zu wissen, dass die Listenkonfiguration in der Datei:

/var/lib/mailman/lists/<listenname>/config.pck

steckt. Diese Datei kann man z.B. mittels

withlist -l -r fix_url <listname> -u mailman.yyy.com

bearbeiten (Listname ist hier ohne den Domainanteil).

Das setzt den host in web_page_url auf mailman.yyy.com and host_name auf xxx.com.

Testen

Nach Konfigurationsänderungen ist das Testen des Ergebnisses immer wichtig. Das betrifft einerseits die Viren und Spam Erkennung aber auch die ganzen Nameservereinträge.

Spam und Viren Testen kann man den Spamfilter mit GTUBE von Apache. Generic Test for Unsolicited Bulk Email (GTUBE) ist ein kurzer Mail-Text, der als Spam erkannt werden muss:

Herunterladen der Test-Datei:

wget http://spamassassin.apache.org/gtube/gtube.txt -P /tmp

und dann verschicken an einen User

sendmail uwe@meine-maildomain.de < /tmp/gtube.txt


Oder

https://www.heise.de/security/dienste/Emailcheck-2109.html


Die SSL-Parameter

openssl s_client -connect meine-maildomain.de:25 -starttls smtp
openssl s_client -connect meine-maildomain.de:993


Nameserver-Einträge SPF

dig -t txt meine-maildomain.de

bzw. mit übersichtlicherme Ergebnis

dig +short -t txt meine-maildomain.de


DKIM

dig -t txt 2020._domainkey.meine-maildomain.de

DMARC

dig -t txt _dmarc.meine-maildomain.de

Quotas

Das Testen der Quota-Einstellungen ist etwas mühsam. Die Warnungen werden z.B. immer dann erzeugt, wenn der Speicherplatz die angegebene Grenze übersteigt. Danach dann aber bis zur nächsten Grenze nicht mehr. Man muss also Mails löschen, um eine Grenze erneut auszutesten.

Bei Problemen kann man darauf achten, ob überhaupt Dovecot für die Mailzustellung genutzt wird:

Oct 18 11:21:19 mail dovecot: lmtp(name@meine-maildomain.de): msgid=<95fd0a2b46d4969a5ff2e514b.98ea0f958f.20201018090500.574acd086e.19474f00@mail111.beispiel.de>: saved mail to INBOX
Oct 18 11:21:19 mail postfix/lmtp[13873]: 7EBFD24006C: to=<name@meine-maildomain.de>, relay=meine-maildomain.de[private/dovecot-lmtp], delay=1.3, delays=1.1/0/0.01/0.12, dsn=2.0.0, status=sent (250 2.0.0 <name@meine-maildomain.de> UBNYEg8JjF8yNgAA37fa2w Saved)

Falls Postfix selber zustellt, wie bei der Mail an root

Oct 18 10:30:03 mail postfix/local[13154]: 61D1B240091: to=<root@meine-maildomain.de>, orig_to=<root>, relay=local, delay=0.28, delays=0.2/0/0/0.08, dsn=2.0.0, status=sent (delivered to mailbox)

greifen die Dovecot Einstellungen natürlich nicht.

Falls es probleme gibt kann man auch gut in der local.conf von Dovecot

mail_debug = yes

setzen. Beim Überschreiten einer Grenze sollte sich dann in der Mail.log ein Eintrag finden wie

Oct 18 10:31:22 mail dovecot: lmtp(name@meine-maildomain.de)<587134><ZL7WNlj9i19+9QgAjuSTOg>: Debug: quota: Executing warning: quota-warning 66 name@meine-maildomain.de (because bytes=235928217 -> 314565267 over limit 270336000)

fail2ban

Bei fail2ban handelt es sich um ein nützliches Tool, welches bestimmte Logdateien verfolgt und IP-Adressen blockt, die hier häufiger mit unerwünschten Zugriffen auftauchen. Diese IP-Adressen werden dann eine gewisse Zeit mittels Iptables-Regeln blockiert. Das Tool ist sehr konfigurierbar und nahezu beliebig erweiterbar, zumindest wenn man sich mit regulären Ausdrücken auskennt.

Installiert und gestartet wird das Tool mittels:

apt install fail2ban

Die Konfiguration befindet sich im Verzeichnis /etc/fail2ban

Hier findet sich für jeden zu überwachenden Dienst nein Bereich wie:

[sshd]

port    = ssh
logpath = %(sshd_log)s

Hier wird festgelegt, welcher Port eine Rolle spielt und welche Logdateien. Die Konfiguration ist so aber noch nicht vollständig. Weiter oben in der Datei finden sich einige Vorgaben, die hier auch überschrieben werden könnten.

# "bantime" is the number of seconds that a host is banned.
bantime  = 600

# A host is banned if it has generated "maxretry" during the last "findtime" seconds.
findtime  = 600

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

und vor allem

# "enabled" enables the jails.
#  By default all jails are disabled, and it should stay this way.
#  Enable only relevant to your setup jails in your .local or jail.d/*.conf
#
# true:  jail will be enabled and log files will get monitored for changes
# false: jail is not enabled
enabled = false

Durch diese Vorgabe wird kein einzige Jail gestartet. Änderungen nimmt man nicht hier vor, sondern in der Datei jail.d/defaults-debian.conf. Bei der Auslieferung hat diese Datei folgenden Inhalt:

[sshd]
enabled = true

Somit wird das Jail sshd gestartet. Wie genau es arbeitet wird über die Datei filter.d/sshd.conf festgelegt. Ihr Inhalt besteht aus einer Reihe von regulären Ausdrücken, die die Konfigurationsdatei untersuchen.

Die jail.d/defaults-debian.conf hat bei mir folgenden Inhalt:

[sshd]
enabled = true

[apache-badbots]
enabled = true

[vsftpd]
enabled = true

[postfix]
enabled = true

[postfix-rbl]
enabled = true

[dovecot]
enabled = true

Blacklist mit fail2ban

Auf eine interessante Anwendung von fail2ban hat mit Lukas Thiel aufmerksam gemacht. Man kann damit eine Sperrliste für IP-Adressen aufbauen.

Dazu ergänzt man die jail.d/defaults-debian.conf am Ende um folgende Zeilen:

[ip-blacklist]
enabled   = true
action    = iptables-allports[name=ip-blacklist]
filter    = ip-blacklist
logpath   = /etc/fail2ban/ip.blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

Nun noch den Filter definieren mit filter.d/ip-blacklist.conf

[Definition]

# Option:  failregex
# Notes :  Detection of blocked ip addresses.
# Values:  TEXT
#

failregex = ^<HOST> \[.*\]$ 

# Option:  ignoreregex
# Notes :  Regex to ignore.
# Values:  TEXT
#

ignoreregex =

Jetzt kann man IP-Adressen blocken über die Datei ip.blacklist:

192.168.84.188 [31/03/2018 16:34:05]
192.168.169.190 [16/04/2018 19:49:00]

Die Einträge bestehen jeweils aus einer Zeile, vorn die IP-Adresse, dahinter eine Zeitangabe für den Eintrag. Damit kann die Gültigkeit der Sperre begrenzt werden. Angegeben ist ja als findtime 15552000 Sekunden, was etwa einem halben Jahr entspricht.

Überprüfen kann man die Funktions mittls:

fail2ban-client status ip-blacklist

das ergibt eine Ausgabe wie:

Status for the jail: ip-blacklist
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	2
|  `- File list:	/etc/fail2ban/ip.blacklist
`- Actions
   |- Currently banned:	2
   |- Total banned:	2
   `- Banned IP list:	192.168.84.188 192.168.169.190


Mehr dazu

Unter https://sven.rojek.de/posts/fail2ban-iprange-mit-blackliste-blocken habe ich jetzt eine interessante Version gefunden, die auch das Blockieren von Netzblöcken zulässt.

Auch bei dieser Version habe ich den Effekt, dass gelegentlich Teile der IP-Adresse nicht berücksichtigt werden. Dadurch entstehen dann ganz andere Bereiche, die geblockt werden.

Geblockte Adressen kann man folgendermaßen per Hand freigeben:

fail2ban-client set ip-blacklist unbanip 116.255.167.53

entsprechend kann man sie mit banip auch von Hand blocken.

Ich hatte aber auch schon das Problem, dass sich fälschlich geblockte IP-Adressen nicht wieder löschen ließen. Hier hilft ein Neustart von fail2ban nicht weiter. Beim Stoppen werden zwar alle Blockaden entfernt, das Programm restauriert sie aber beim Neustart.

Die Datenbank mit diesen Einstellungen habe ich im Verzeichnis /var/lib/fal2ban gefunden. Dort gibt es die Datei fail2ban.sqlite3 mit den internen Daten. Ein Löschen dieser Datei führt dazu, dass beim Neustart von fail2ban keine Blockaden vorhanden sind. Die Datei ip-blacklist muss man nach dem Neustart einmal verändern, damit die Adressen neu eingelesen werden.

Ich meine, dass die erste Filterdatei filter ip-blacklist.conf nicht ganz korrekt ist, es fehlen die Leerzeichen nach der IP-Adresse (vor dem .*), dadurch ist das [^/] wirkungslos. Hinter dem [^/] muss auch ein Leerzeichen stehen, sonst werden auch für alle größeren Netzwerkblöcke zusätzlich /32er Regeln erzeugt. Generell sollte man daher zwischen IP-Adresse und Datumsbereich immer mindestens zwei Leerzeichen stehen haben.

[Definition]

# Option:  failregex
# Notes :  Detection of blocked ip addresses.
# Values:  TEXT
#

failregex = ^<HOST>(/32 .*|[^/] .*)?$

# Option:  ignoreregex
# Notes :  Regex to ignore.
# Values:  TEXT
#

ignoreregex =

Konsequenterweise sollte man das Leerzeichen wohl in allen der Filter setzen.


Die Funktion eines Filter kann man prüfen mit

fail2ban-regex ip-blacklist "^<HOST>(/32 .*|[^/] .*)?$"

bzw.

fail2ban-regex "10.10.10.10 [2018-09-18 16:16:00]" "^<HOST>(/32 .*|[^/] .*)?$"

Übrigens, bei dem Datumsformat ist fail2ban relativ flexibel, da sind mehrere Versionen hinterlegt. Die hier benutzen regulären Ausdrücke benötigen z.B. die eckigen Klammern um das Datum nicht.

Es geht (u.a.):

fail2ban-regex "10.10.10.10 19-09-2018 16:16:00" "^<HOST>(/32 .*|[^/] .*)?$"
fail2ban-regex "10.10.10.10 [19-09-2018 16:16:00]" "^<HOST>(/32 .*|[^/] .*)?$"
fail2ban-regex "10.10.10.10 19/09/2018 16:16:00" "^<HOST>(/32 .*|[^/] .*)?$"
fail2ban-regex "10.10.10.10 [2018-09-19 16:16:00]" "^<HOST>(/32 .*|[^/] .*)?$"
fail2ban-regex "10.10.10.10 [Sep 19 16:16:00]" "^<HOST>(/32 .*|[^/] .*)?$"
fail2ban-regex "10.10.10.10 [19/Sep/2018:16:16:00]" "^<HOST>(/32 .*|[^/] .*)?$"

Überarbeitete Lösung für IP-Blacklisten mit fail2ban

Die folgende Beschreibung baut auf dem Text unter https://sven.rojek.de/posts/fail2ban-iprange-mit-blackliste-blocken auf. Leider ist auf der Seite keine Kontaktmöglichkeit angegeben um Änderungsvorschläge zu machen. Auch finden sich dort für meinen Geschmack zu wenig Erläuterungen.

Die Sperrliste

Die Datei /etc/failban/ip-blacklist beinhaltet zeilenweise die IP-Adressen bzw. Bereich und den zugehörigen Zeitstempel.

########################################
#
# Single IP Example:
# 10.10.10.10       [2015/01/01 12:00:00]
# 10.10.10.10/32    [2015-0101 12:00:00]
#
#########################################
#
# IP Range Options:
# 10.10.10.10/24 = 10.10.10.*
# 10.10.10.10/16 = 10.10.*.*
# 10.10.10.10/8  = 10.*.*.*
#
#########################################
#
# IP Range Examples:
# 10.10.10.10/16    [2015-01-01 12:00:00]
# 10.10.10.10/24    [2015-01-01 12:00:00]
#
#########################################
10.2.245.209         [21/09/2018 16:01:00]
10.214.206.13/24     [21/09/2018 15:33:00]

Hier eine einzelne IP-Adresse und ein 24-er Block mit 256 Ip-Adressen. Zwischen der IP-Adresse und dem Zeitstempel müssen mindestens zwei Leerzeichen liegen. Es bietet sich an, alle Zeitstempel in Form einer Tabelle anzuordnen, damit man Tippfehler leichter erkennt. Gegenüber Fehlern in dem Zeitstempel (aber auch der IP-Adresse) ist fail2ban empfindlich.

Als Zeitstempel kommt alles in Frage, was fail2ban akzeptiert z.B.

[19-09-2018 16:16:00]
[2018-09-19 16:16:00]
[Sep 19 16:16:00]
[19/Sep/2018:16:16:00]

Die Konfigurationsdatei jail.local

Die Datei /etc/fail2ban/jail.local ist in der Standardinstallation von Ubuntu nicht vorhanden, kann also risikolos angelegt werden. Sie beinhaltet die Hauptkonfiguration für die flexible Blacklist.

[ip-blacklist]
enabled   = true
port      = anyport
action = action_ip-blacklist
filter    = filter_ip-blacklist
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist24]
enabled   = true
port      = anyport
action    = action_ip-blacklist[mask=24]
filter    = filter_ip-blacklist24
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist16]
enabled   = true
port      = anyport
action    = action_ip-blacklist[mask=16]
filter    = filter_ip-blacklist16
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist8]
enabled   = true
port      = anyport
action    = action_ip-blacklist[mask=8]
filter    = filter_ip-blacklist8
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

Die Datei definiert vier Regeln, für jede der vier Netzwerkklassen eine.

  • Alle Regeln sind aktiviert (enabled=true),
  • die Blockade betrifft alle Ports (port=anyport) ,
  • als Action wird action_ip-blacklist aufgerufen, die entsprechende Datei findet sich in /etc/fail2ban/action.d und bekommt den Parameter mask übergeben
  • als Filter dient die Datei filter_ip-blacklistnn aus dem Verzeichnis /etc/fail2ban/filter.d
  • die Quelle für das Sperren ist die Datei /etc/fail2ban/ip-blacklist
  • es gibt beliebig viele Versuche (maxretry=0)
  • der Zeitstempel darf maximal 180 Tage zurückliegen (findtime=15552000)
  • der Ban wird nie aufgehoben (bantime=-1)

Die Datei action_ip-blacklist.conf mit den Aktionen

Die Datei /etc/fail2ban/action.d/action_ip-blacklist.conf beschreibt, was zu tun ist z.B. beim Sperren einer IP-Adresse. Dieses Tool basiert sehr stark auf Iptables. Sie ähnelt der Datei iptables-common.conf, die von vielen anderen Jails aufgerufen wird.

[Definition]

# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart = iptables -N fail2ban-<name>
              iptables -A fail2ban-<name> -j RETURN
              iptables -I <chain> -p <protocol> -j fail2ban-<name>

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = iptables -D <chain> -p <protocol> -j fail2ban-<name>
             iptables -F fail2ban-<name>
             iptables -X fail2ban-<name>

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck = iptables -n -L <chain> | grep -q fail2ban-<name>

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          

Im Bereich Init werden einige Variable angelegt, die im oberen Bereich auch benutzt werden. Die Variablen können über Parameter beim Aufruf überschrieben werden.

  • (name = default) legt den Bezeichner für die eigene Chain fest, wird zu fail2ban-default
  • (protocol=all) es werden immer alle Ports blockiert
  • (chain=INPUT) die eigene Chain hängt sich in die Standard-Chain INPUT ein
  • (mask=32) wenn kein Parameter mask übergeben wird, dann gilt 32 als Standard-Maske

Im Bereich Definition werden die Aktionen festgelegt:

  • actionstart greift beim Start von fail2ban. Hier wird die eigene Chain (fail2ban-default) angelegt. Hier gibt es einen kleinen Schönheitsfehler, das wir vier Jails mit dem gleichen Namen anlegen wird auch die eigene Chain viermal angelegt. Alle IP-Blockaden arbeiten aber immer mit der ersten Chain (aufgrund des gleichen Namens).
  • actionstop beseitigt die eigene Chain und die darin noch vorhandenen Regeln
  • actioncheck testet vor dem Bannen, ob es die eigene Chain gibt
  • actionban blockiert die IP-Adresse bze. den IP-Bereich mittels Iptables. Als Aktion ist hier DROP angegeben und nicht wie sonst REJECT, der Bösewicht bekommt also keine Rückmeldung
  • actionunban hebt die Sperre für eine IP-Adresse bzw. einen IP-Block wieder auf.

Die Filterdateien

Im Verzeichnis /etc/fail2ban/filter.d/ finden sich die vier Filterdateien

  • filter_ip-blacklist24.conf
  • filter_ip-blacklist16.conf
  • filter_ip-blacklist8.conf
  • filter_ip-blacklist.conf

Die ersten drei ähneln sich sehr, hier ist nur die Netzwerkmaske zu berücksichtigen:

[Definition]
# Option:  failregex
# Notes :  Detection of blocked ip addresses.
# Values:  TEXT
#

failregex = ^<HOST>/24 .*$

# Option:  ignoreregex
# Notes :  Regex to ignore.
# Values:  TEXT
#

ignoreregex =

In der Zeile failregex wird das Muster festgelegt, das von fail2ban erwartet werden soll, hier:

^<HOST>/24 .*$

Dieses Muster besagt, dass am Anfang der Zeile (das ^ sagt am Anfang) eine IP-Adresse stehen soll. <HOST> ist so etwas wie ein fail2ban Makro, ein komplizierterer regulärer Ausdruck, der hier nur benutzt wird. Danach folgt die Netzwerkmaske und darauf ein Leerzeichen. Hinter dem Leerzeichen folgen beliebig (*) viele beliebige Zeichen (.) bis zum Ende der Zeile ($). Im diesem Teil sucht fail2ban dann einen Zeitstempel.

Lediglich die vierte Datei weicht etwas ab, da die 32 Maske gesetzt werden kann, oder auch nicht.

[Definition]

# Option:  failregex
# Notes :  Detection of blocked ip addresses.
# Values:  TEXT 
#

failregex = ^<HOST>(/32 .*|[^/] .*)?$

# Option:  ignoreregex
# Notes :  Regex to ignore.
# Values:  TEXT
#

ignoreregex =

Der Ausdruck beginnt wieder mit der IP-Adresse. Innerhalb der runden Klammer stehen zwei Möglichkeiten zur Wahl (getrennt durch |).

  • Die erste Möglichkeit ist Netzwerkmaske 32, ein Leerzeichen und beliebig viele beliebige Zeichen
  • Die zweite Möglichkeit, kein Schrägstrich ([^/]), dann ein Leerzeichen und dann beliebig viele beliebige Zeichen. Das Fragezeichen vor dem $ regelt die Gefräßigkeit der Regeln.


Blacklist mit fail2ban

Auf eine interessante Anwendung von fail2ban hat mit Lukas Thiel aufmerksam gemacht. Man kann damit eine Sperrliste für IP-Adressen aufbauen.

Dazu ergänzt man die jail.d/defaults-debian.conf am Ende um folgende Zeilen:

[ip-blacklist]
enabled   = true
action    = iptables-allports[name=ip-blacklist]
filter    = ip-blacklist
logpath   = /etc/fail2ban/ip.blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

Nun noch den Filter definieren mit filter.d/ip-blacklist.conf

[Definition]

# Option:  failregex
# Notes :  Detection of blocked ip addresses.
# Values:  TEXT
#

failregex = ^<HOST> \[.*\]$ 

# Option:  ignoreregex
# Notes :  Regex to ignore.
# Values:  TEXT
#

ignoreregex =

Jetzt kann man IP-Adressen blocken über die Datei ip.blacklist:

192.168.84.188 [31/03/2018 16:34:05]
192.168.169.190 [16/04/2018 19:49:00]

Die Einträge bestehen jeweils aus einer Zeile, vorn die IP-Adresse, dahinter eine Zeitangabe für den Eintrag. Damit kann die Gültigkeit der Sperre begrenzt werden. Angegeben ist ja als findtime 15552000 Sekunden, was etwa einem halben Jahr entspricht.

Überprüfen kann man die Funktions mittls:

fail2ban-client status ip-blacklist

das ergibt eine Ausgabe wie:

Status for the jail: ip-blacklist
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	2
|  `- File list:	/etc/fail2ban/ip.blacklist
`- Actions
   |- Currently banned:	2
   |- Total banned:	2
   `- Banned IP list:	192.168.84.188 192.168.169.190

vsftpd

Da wir auf dem System nur virtuelle Nutzer haben, muss auch der FTP-Server auf die Benutzerdatenbank zugreifen können. Dazu dienen die folgenden Konfigurationsschritte:

Die Grundinstallation von vsftpd ist relativ einfach:

apt install libpam-mysql vsftpd

direkt installieren.

Nun müssen noch am Anfang der Datei /etc/pam.d/vsftpd zwei Zeilen ergänzt werden:

auth sufficient pam_mysql.so user=postfix passwd=asswd host=localhost db=postfix table=mailbox usercolumn=username passwdcolumn=password crypt=1 where=active="1"
account sufficient pam_mysql.so user=postfix passwd=asswd host=localhost db=postfix table=mailbox usercolumn=username passwdcolumn=password crypt=1 where=active="1"

# Standard behaviour for ftpd(8).
auth    required        pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
...

Mit den beiden Zeilen wird dem Pam-System (bzw. dem Modul pam_mysql) mitgeteilt, dass es auf die MySQL-Datenbank zugreifen soll und wo es die notwendigen Daten findet. Statt der beiden langen Parameterlisten könnte man auch mit einem config_file arbeiten ( auth required pam_mysql.so config_file=/lib/security/pam_mysql.conf). Das hätte dann folgenden Aufbau:

users.host localhost
users.database postix
users.db_user postfix
users.db_passwd asswd
users.table mailbox
users.user_column username
users.password_column password
users.password_crypt 1
users.where_clause active="1"


Nun habe ich in der Datei /etc/vsftpd.conf noch ein paar Änderungen vorgenommen. Hier eine Liste aller aktiven Einstellungen in alphabetischer Reihenfolge:

allow_writeable_chroot=YES
anon_world_readable_only=YES
anonymous_enable=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd.chroot_list
chroot_local_user=YES
connect_from_port_20=YES
dirmessage_enable=YES
dual_log_enable=YES
guest_enable=YES
guest_username=ftp
listen=YES
listen_ipv6=NO
local_enable=YES
log_ftp_protocol=YES
nopriv_user=ftpsecure
pam_service_name=vsftpd
pasv_max_port=30100
pasv_min_port=30000
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
secure_chroot_dir=/var/run/vsftpd/empty
setproctitle_enable=YES
ssl_enable=NO
use_localtime=YES
user_config_dir=/etc/vsftpd/
vsftpd_log_file=/var/log/vsftpd.log
write_enable=YES
xferlog_enable=YES
xferlog_file=/var/log/xferlog
xferlog_std_format=YES


Der hier benutzte unterprivilegierte User ftpsecure muss noch angelegt werden:

adduser --system --home /var/run/vsftpd/empty --no-create-home --disabled-login ftpsecure

Nun muss noch für jeden Benutzer, der FTP nutzen können soll, eine Datei in /etc/vsftpd erstellt werden. Der Name der Datei ist der Benutzername, hier also die Mailadresse.

local_root=/var/www/vhosts/<verzeichnis>/httpdocs

anon_umask=022
anonymous_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_umask=022
anon_mkdir_write_enable=YES
anon_other_write_enable=YES

guest_username=www-data
connect_from_port_20=YES


Automatische Updates

Auch eine Server-Distribution muss gelegentlich aktualisiert werden. Dazu dient das Paket unattended-upgrades, das ggf. zuerst einmal installiert werden muss.

apt-get install unattended-upgrades

Es gibt dann ein paar Konfigurationdateien, die angepasst werden müssen/können. /etc/apt/apt.conf.d/50unattended-upgrades

// Automatically upgrade packages from these (origin:archive) pairs
//
// Note that in Ubuntu security updates may pull in new dependencies
// from non-security sources (e.g. chromium). By allowing the release
// pocket these get automatically pulled in.
Unattended-Upgrade::Allowed-Origins {
        "${distro_id}:${distro_codename}";
        "${distro_id}:${distro_codename}-security";
        // Extended Security Maintenance; doesn't necessarily exist for
        // every release and this system may not have it installed, but if
        // available, the policy for updates is such that unattended-upgrades
        // should also install from here by default.
        "${distro_id}ESMApps:${distro_codename}-apps-security";
        "${distro_id}ESM:${distro_codename}-infra-security";
        "${distro_id}:${distro_codename}-updates";
//      "${distro_id}:${distro_codename}-proposed";
//      "${distro_id}:${distro_codename}-backports";
};

// Python regular expressions, matching packages to exclude from upgrading
Unattended-Upgrade::Package-Blacklist {
    // The following matches all packages starting with linux-
//  "linux-";

    // Use $ to explicitely define the end of a package name. Without
    // the $, "libc6" would match all of them.
//  "libc6$";
//  "libc6-dev$";
//  "libc6-i686$";

    // Special characters need escaping
//  "libstdc\+\+6$";

    // The following matches packages like xen-system-amd64, xen-utils-4.1,
    // xenstore-utils and libxenstore3.0
//  "(lib)?xen(store)?";

    // For more information about Python regular expressions, see
    // https://docs.python.org/3/howto/regex.html
};

// This option controls whether the development release of Ubuntu will be
// upgraded automatically. Valid values are "true", "false", and "auto".
Unattended-Upgrade::DevRelease "auto";

// This option allows you to control if on a unclean dpkg exit
// unattended-upgrades will automatically run 
//   dpkg --force-confold --configure -a
// The default is true, to ensure updates keep getting installed
//Unattended-Upgrade::AutoFixInterruptedDpkg "true";


// Split the upgrade into the smallest possible chunks so that
// they can be interrupted with SIGTERM. This makes the upgrade
// a bit slower but it has the benefit that shutdown while a upgrade
// is running is possible (with a small delay)
//Unattended-Upgrade::MinimalSteps "true";

// Install all updates when the machine is shutting down
// instead of doing it in the background while the machine is running.
// This will (obviously) make shutdown slower.
// Unattended-upgrades increases logind's InhibitDelayMaxSec to 30s.
// This allows more time for unattended-upgrades to shut down gracefully
// or even install a few packages in InstallOnShutdown mode, but is still a
// big step back from the 30 minutes allowed for InstallOnShutdown previously.
// Users enabling InstallOnShutdown mode are advised to increase
// InhibitDelayMaxSec even further, possibly to 30 minutes.
Unattended-Upgrade::InstallOnShutdown "false";

// Send email to this address for problems or packages upgrades
// If empty or unset then no email is sent, make sure that you
// have a working mail setup on your system. A package that provides
// 'mailx' must be installed. E.g. "user@example.com"
Unattended-Upgrade::Mail "root";

...

In dem oberen Bereich legt man Fest, welche Art von Updates installiert werden. Ich habe zusätzlich "${distro_id}:${distro_codename}-updates" aktiviert. Außerdem lasse ich mir per Mail berichten.

Dann sollte man noch die Datei /etc/apt/apt.conf.d/10periodic anlegen:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Ausführlichere Informationen finden sich unter https://www.techgrube.de/tutorials/automatische-updates-auf-ubuntu-server-nutzen

Jetzt müssen noch die entsprechenden Ports in der Firewall freigegeben werden:

ufw allow 20:21/tcp
ufw allow 30000:30100/tcp
ufw allow 990/tcp