Hetzner-Server mit Ubuntu 20.04
Hinweis: Momentan bin ich dabei die Konfiguration vollkommen von Grund auf neu zu machen. Für das Mailsystem sind die aktuellen Schritte unter Überarbeitung der Mail-Konfiguration für Ubuntu 20.04 zu finden. Das neue System wird nicht wesentlich anders sein, die Konfigurationen sollen aber wesentlich übersichtlicher und strukturierter werden.
Neuer Hetzner-Server
Nach meiner Einschätzung hat mein bisheriger Hetzner-Server ein Festplattenproblem. Zumindest synchronisiert sich das Raid ziemlich oft und Smartctl liefert Fehler
SMART Attributes Data Structure revision number: 10 Vendor Specific SMART Attributes with Thresholds: ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE 1 Raw_Read_Error_Rate 0x000f 116 099 006 Pre-fail Always - 101230976 3 Spin_Up_Time 0x0003 095 094 000 Pre-fail Always - 0 4 Start_Stop_Count 0x0032 100 100 020 Old_age Always - 16 5 Reallocated_Sector_Ct 0x0033 100 100 010 Pre-fail Always - 0 7 Seek_Error_Rate 0x000f 089 060 030 Pre-fail Always - 839780273 9 Power_On_Hours 0x0032 062 062 000 Old_age Always - 33544 10 Spin_Retry_Count 0x0013 100 100 097 Pre-fail Always - 0 12 Power_Cycle_Count 0x0032 100 100 020 Old_age Always - 16 ...
Als ich das Problem vor einiger Zeit beim Hetzner-Support gemeldet habe, bin ich leider nicht weiter gekommen, da die Platte ansonsten keine Fehler meldet. Aktuelle steht aber eine Aktualisierung des Betriebssystems an, Ubuntu 16.04 bietet eine zu alte PHP-Version. Die Mühe mit der Aktualisierung wollte ich mir aber nicht mit einer defekten Festplatte antun. Also habe ich heute einen neuen Hetzner-Server geordert, auch wenn er mit 27€ etwas teurer ist als der bisherige Server. Dafür habe ich darauf geachtet, dass der Server über Enterprise HDs verfügt. Hoffentlich ist hier die Haltbarkeit höher.
Das Gerät verfügt über folgende Harware:
- Prozessor Intel Core i7-4770 CPU @ 3.40GHz (CPU-B 7048)
- dazu Festplatten 2x2 TB Ent. HDD und
- 32 GB Hauptspeicher.
- 8 x 6799.56 Bogomips
Nach der Bestellung hat es nicht einmal eine halbe Stunde gedauert, bis der Server zur Grundinstallation im Rettungssystem zur Verfügung stand.
Festplatten-Tausch
Leider musste ich feststellen, dass auch diese Platten nicht in Ordnung sind, ich werde also erst einmal keine Installation vornehmen. Wenn der Support wieder sagt, dass alles in Ordnung ist, dann werde ich den Server zurückgeben. Platte /dev/sda hat eine hohe Rate an Raw_Read_Error_Rate, das ist aber bei manchen Platten wohl Ok, wenn der Wert mit Hardware_ECC_Recovered übereinstimmt. Die Platte /dev/sdb hat aber einen Reallocated_Sector_Ct von 17, nach meiner Kenntnis muss man dann zeitnah mit einem Ausfall der Platte rechnen. Mal sehen, wie gut der Service von Hetzner ist.
Ergebnis: Der Service ist gut. Mein Problem mit dem Server trat am Samstag auf und war je kein Notfall. Am Montag um 08.39 Uhr hatte ich eine Antwort vom Support. Das Angebot war die Platte mit den defekten Sektoren auszuwechseln. Dazu musste ich mein Einverständnis erklären, dass der Server heruntergefahren wird. Eine halbe Stunde danach kam die Mitteilung, dass die Platte gewechselt ist.
/dev/sda 56173 Stunden (etwas mehr als 6 Jahre) bei 27 Start/Stopps (Seagate Constellation ES.3, ST2000NM0033-9ZM175)
/dev/sdb 43372 Stunden (knapp 5 Jahre) bei 15 Start/Stopps (Hitachi/HGST Ultrastar 7K4000, HGST HUS724020ALA640)
Natürlich war die Platte nicht ins System eingebunden, sie war unpartitioniert. Ich fand es dann einfacher die gesamte Installation neu zu starten. Dazu muss man im Hetzner-Robot für diesen Server die Funktion Rescue aktivieren. Das ging erst einmal nicht, das System beschwerte sich, dass VNC aktiviert wäre. Nachdem ich das deaktiviert hatte, konnte ich das Rescue-System starten.
Danach konnte ich dann wieder die ganz normale Erstinstallation nach dem Hetzner-System durchgehen.
Grundinstallation
Startet man im Rescue-System das Programm installimage, so muss man zuerst das Betriebssystem auswählen, welches man installieren möchte. Ich habe mich für Ubuntu 20.04 entschieden. Nach dieser Auswahl landet man in einem Editor, indem man vor allem die Partitionierung der Festplatten festlegt. Die vorgegebene Datei ist gut dokumentiert, sodass hier eigentlich keine Probleme auftauchen sollten. Ich habe mich für folgende Aufteilung entschieden:
/dev/md1 1G /boot /dev/md2 50G / /dev/md3 5G /tmp /dev/md4 800G /var /dev/md5 980G /home (der gesamte Rest)
Schwierig kann es sein den Editor ordnungsgemäß zu verlassen. Die Funktionstaste F10 wirkt bei mir nicht auf den Hetzner-Editor, sondern auf das Terminal. Hetzner schlägt im Wiki vor ESC und dann 0 zu drücken.
Im nächsten Schritt dann
apt update apt upgrade
Am Ende des Upgrade-Prozesses tauchen Fenster auf mit Fragen, die beantwortet werden müssen. Bei der Frage nach der Aktualisierung der OpenSSH-Konfiguration habe ich mich für das Belassen der vorhandenen Version entschieden. Schwieriger war die Frage zu GRUB:
Fortsetzen ohne Installation von GRUB yes/no?
Bei no kam das Fenster immer wieder, man muss also yes antworten.
Ich habe mir dann noch gleich ein paar nützliche Hilfsmittel installiert:
apt install synaptic apt-xapian-index update-apt-xapian-index -vf
Installiert das grafische Tool zur Paketverwaltung. 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 viele Stunden gelaufen sind.
Im Netzwerk benötigt man dann oft auch whois Informationen
apt install whois
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://ubuntu.com/server/docs/security-firewall, https://help.ubuntu.com/community/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.
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 10 Pakete mit insgesamt 6000 Byte von der Regel blockiert.
Anpassungen
Die Spracheinstellungen anpassen
update-locale LANG=de_DE.UTF-8
Danach neu anmelden, um den Effekt zu sehen.
~ # locale LANG=de_DE.UTF-8 LANGUAGE= LC_CTYPE="de_DE.UTF-8" LC_NUMERIC="de_DE.UTF-8" LC_TIME="de_DE.UTF-8" LC_COLLATE="de_DE.UTF-8" LC_MONETARY="de_DE.UTF-8" LC_MESSAGES="de_DE.UTF-8" LC_PAPER="de_DE.UTF-8" LC_NAME="de_DE.UTF-8" LC_ADDRESS="de_DE.UTF-8" LC_TELEPHONE="de_DE.UTF-8" LC_MEASUREMENT="de_DE.UTF-8" LC_IDENTIFICATION="de_DE.UTF-8" LC_ALL=
Noch ein paar zusätzliche Pakete:
apt install bind9-host whois nmap lsof man-db apt-get install arj bzip2 cabextract cpio file gzip nomarch pax rar unrar unzip zip lhasa p7zip ssl-cert lrzip lzop rpm2cpio unrar-free p7zip p7zip-rar apt install gnupg logwatch
Danach habe ich dann einen Reboot veranlasst.
Verzeichnisse
Ein paar Verzeichnsse anlegen:
mkdir /var/log/ustat mkdir /srv/www mkdir /srv/www/typo3src mkdir /etc/webalizer.d mkdir /etc/logrotate.d/apache2-vhosts.d mkdir /etc/vhosts-sicherung.d mkdir /var/log/apache2-vhosts.d mkdir /var/vmail mkdir /var/log/vhosts-sicherung mkdir /home/tmp mkdir /home/server2-sicherungen mkdir /var/www mkdir /var/www/vhosts mkdir /var/www/htdocs mkdir /var/www/htdocs/dummy mkdir /var/www/htdocs/webalizer
Webserver
Achtung: Dieser ganze Abschnitt hat nur noch historische Bedeutung. Eine aktuellere Überarbeitung ist zu finden unter Überarbeitung der Apache-Konfiguration für Ubuntu 20.04
Viele Apache-Anwendungen benötigen eine Datenbank. Deshalb im ersten Schritt die Installation von MySQL.
Datenbank-MySQL
Zur Installation von Server und Client dient der folgende Aufruf:
apt install mysql-client mysql-server mysql-common
Ich habe dann die Konfigurationsdatei /etc/mysql/mysql.conf.d/mysqld.cnf ergänzt
#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.
MySQL-Passwort
In aktuellen Datenbank-Versionen hat der Benutzer root keinen Zugriff mittels Passwort und kann daher z.B. auch nicht per phpmyadmin arbeiten. Der folgende MySQL-Befehl zeigt die Einstellung
mysql> SELECT user,authentication_string,plugin,host FROM mysql.user; +---------------------+-------------------------------------------+-----------------------+-----------+ | user | authentication_string | plugin | host | +---------------------+-------------------------------------------+-----------------------+-----------+ | root | | auth_socket | localhost | | mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost | | mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost | ...
Ändern lässt sich diese Einstellung mittels:
use mysql; ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; flush privileges;
Dann ergibt sich:
mysql> select user,authentication_string,plugin,host FROM mysql.user; +---------------------+-------------------------------------------+-----------------------+-----------+ | user | authentication_string | plugin | host | +---------------------+-------------------------------------------+-----------------------+-----------+ | root | *A9172BC315E0BF4D14D201C4CD16374ED187B2B3 | mysql_native_password | localhost | | mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost | | mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | mysql_native_password | localhost |
Damit ist das Passwort gesetzt. Will man es später einmal ändern, so muss man berücksichtigen, dass sich das Passwort für MySQL nicht mehr mit den altbekannten Kommandos setzen lässt, weil das Passwort nicht mehr im Feld password, sondern im Feld authentication_string zu finden ist:
use mysql; UPDATE user SET authentication_string= password('password') WHERE User = 'root'; flush privileges;
und
https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04-de
Nachdem das Passwort gesetzt ist, kann sich der Benutzer root auch per phpmyadmin anmelden.
MySQL im Zusammenspiel mit phpmyadmin
Eine etwas einfachere Lösung das Passwort-Problem zu lösen gibt es, wenn mit phpmyadmin gearbeitet wird. In der Datei /etc/mysql/debian.cnf sind die Zugangsdaten für einen bei der Installation angelegten User zu finden:
# Automatically generated for Debian scripts. DO NOT TOUCH! [client] host = localhost user = debian-sys-maint password = BmhrjzFR0D0521NX socket = /var/run/mysqld/mysqld.sock [mysql_upgrade] host = localhost user = debian-sys-maint password = BmhrjzFR01D021NX socket = /var/run/mysqld/mysqld.sock
Mit dem hier befindlichen Benutzer und dem hier befindlichen zufällig generiertem Passwort kann man per phpmyadmin auf die Datenbank zugreifen. Hier kann man dann bequem einen Benutzer mit allen gewünschten Rechten anlegen.
Apache
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.
Nicht vorhanden waren die Pakete php-gettext und php-recode, die Ursache muss ich noch recherchieren.
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"
letsencrypt
Für die verschlüsselte Übertragung von Webseiten werden Zertifikate von letsencrypt benötigt. Das notwendige Paket ist bei Ubuntu dabei:
apt install certbot
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/
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.
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
/etc/apache2/sites-available/000-default.conf
Im ersten Schritt ändere ich einen Teil der Standardvorgaben in der Datei 000-default.conf. Der Name resultiert aus der Tatsache, dass der Apache die Dateien in alphabetischer Reihenfolge einliest. Diese Datei muss als erste eingelesen werden.
ServerAdmin Webmaster@<dummy>.de
ServerTokens Major
DocumentRoot /var/www/htdocs
Alias /groupoffice /var/www/htdocs/dummy
Alias /squirrelmail /var/www/htdocs/dummy
Alias /webmail /var/www/dummy
Alias /webstat /var/www/htdocs/webalizer
<Directory "/var/www/vhosts">
AllowOverride All
Options +FollowSymLinks
Require all granted
</Directory>
<IfModule mod_userdir.c>
UserDir disabled
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
ExpiresByType text/html "access plus 1 week"
ExpiresByType image/gif "access plus 1 week"
ExpiresByType image/jpeg "access plus 1 week"
ExpiresByType image/png "access plus 1 week"
ExpiresByType text/css "access plus 1 week"
ExpiresByType text/javascript "access plus 1 week"
ExpiresByType application/x-javascript "access plus 1 week"
ExpiresByType text/xml "access plus 1 week"
</IfModule>
<IfModule mod_setenvif.c>
# SEO
BrowserMatchNoCase (mindUp|meanpathbot|seoscanners|AiHitBot|BLEXBot|DotBot|linkdexbot|MJ12bot|SEOkicks-Robot) ist_ein_bot
# Sammeln Backlinks & Links
BrowserMatchNoCase (exabot|Baidu|Haosou|Semrush|MegaIndex|AhrefsBot|BacklinkCrawler|dlcbot|spbot) ist_ein_bot
# Performance Testing
BrowserMatchNoCase (200PleaseBot|LoadTimeBot) ist_ein_bot
# BilderSuche
BrowserMatchNoCase (psbot|Yandex) ist_ein_bot
# Harvester & Marketing
BrowserMatchNoCase (XoviBot|CareerBot|GrapeshotCrawler|iCjobs|magpie-crawler|proximic) ist_ein_bot
# Nutzlos, Schlecht bzw. unbekannt
BrowserMatchNoCase (Ezooms|updown_tester|^Java) ist_ein_bot
# Per IP
SetEnvIfNoCase Remote_Addr (62\.138\.0\.25) ist_ein_bot
</IfModule>
<VirtualHost *:80>
ServerName default
UseCanonicalName Off
DocumentRoot /var/www/vhosts/default/httpdocs
Alias /groupoffice /var/www/htdocs/dummy
Alias /squirrelmail /var/www/htdocs/dummy
Alias /webmail /var/www/htdocs/dummy
Alias /postfixadmin /var/www/htdocs/dummy
Alias /phpMyAdmin /var/www/htdocs/dummy
Alias /roundcube /var/www/htdocs/dummy
Alias /.well-known /var/www/htdocs/.well-known
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<IfModule mod_ssl.c>
SSLEngine off
</IfModule>
<Directory /var/www/vhosts/default/httpdocs>
AllowOverride All
Options None
Require all granted
<IfModule mod_php7.c>
php_admin_flag engine on
php_admin_flag safe_mode off
</IfModule>
</Directory>
</VirtualHost>
Der Ordner /var/www/htdocs/dummy ist hier ein leerer Dummy-Ordner, damit Zugriffe auf die entsprechenden Anwendungen ins Leere laufen. Natürlich müssen die in der Konfiguration genannten Pfade eingerichtet sein, also z.B. /var/www/vhosts/default/httpdocs und /var/www/vhosts/default/httpsdocs. Idealerweise sollte in den Ordnern auch eine Datei index.html liegen, um Fehlermeldungen zu vermeiden.
/etc/apache2/sites-available/default-ssl.conf
Damit die Konfiguration funktionieren kann muss, sofern das Modul SSL aktiviert wurde, an der angegebenen Stelle ein Zertifikat vorliegen. Es kann ruhig ein selbst erstelltes Zertifikat sein. Zur Vereinfachung ist bei Ubuntu gleich ein Zertifikat erstellt, liegt im Paket ssl-cert.
Dann habe ich mir die vorhandene Datei default-ssl.conf angepasst:
<IfModule mod_ssl.c>
<VirtualHost _default_:443 >
ServerAdmin webmaster@<dummy>.de
# ServerName default
UseCanonicalName Off
DocumentRoot /var/www/vhosts/default/httpsdocs
ScriptAlias /cgi-bin/ "/usr/lib/cgi-bin/"
Alias /groupoffice /var/www/htdocs/dummy
Alias /squirrelmail /var/www/htdocs/dummy
Alias /webmail /var/www/htdocs/dummy
Alias /postfixadmin /var/www/htdocs/dummy
Alias /phpmyadmin /var/www/htdocs/dummy
Alias /roundcube /var/www/htdocs/dummy
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNUL
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/lib/cgi-bin/">
AllowOverride None
Options None
Require all denied
SSLOptions +StdEnvVars
</Directory>
<Directory /var/www/vhosts/default/httpsdocs>
SSLRequireSSL
AllowOverride None
Options None
Require all granted
</Directory>
</VirtualHost>
</IfModule>
Im Prinzip hätte ich auch beides in eine Datei packen können, aber es waren ja schon die beiden vorhanden.
Für alle weiteren https Seiten benutze ich Zertifikat von letsencrypt, aber für Stratoserver-Adressen erstellt letsencrypt keine Zertifikate mehr, weil es schon zuviele für xxx.stratoserver.net gibt.
/etc/apache2/sites-available/server.net.conf
Ich will einen Unterschied haben zwischen der Seite die bei einem Aufruf über die IP-Adresse oder eine nicht konfigurierte Domain erfolgt und der Seite, die über den korrekten Server-Namen geliefert wird. Daher ein virtueller Server für legale Namen.
<VirtualHost *:80>
ServerName www.<dummy>
ServerAlias <dummy>
UseCanonicalName Off
DocumentRoot /var/www/vhosts/default/httpdocs
CustomLog /var/log/apache2/server_access_log combined
ErrorLog /var/log/apache2/server_error_log
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin/"
Alias /phpmyadmin /usr/share/phpmyadmin
Alias /postfixadmin /var/www/htdocs/postfixadmin/public
Alias /webmail /var/www/roundcubemail
Alias /webstat /var/www/vhosts/default/webstat
Alias /roundcube /var/lib/roundcube
Alias /roundcubemail /var/lib/roundcube
Alias /.well-known /var/www/htdocs/.well-known
<IfModule mod_ssl.c>
SSLEngine off
</IfModule>
<Directory "/var/www/vhosts/cgi-bin/cgi-bin/">
AllowOverride None
Options None
Require all granted
</Directory>
<Directory /var/www/vhosts/default/httpdocs>
AllowOverride All
Options None
Require all granted
<IfModule mod_php7.c>
php_admin_flag engine on
php_admin_flag safe_mode off
</IfModule>
</Directory>
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443 >
ServerName www.<dummy>
ServerAlias <dummy>
UseCanonicalName Off
DocumentRoot /var/www/vhosts/default/httpsdocs
CustomLog /var/log/apache2/server_access_log combined
ErrorLog /var/log/apache2/server_error_log
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin/"
Alias /phpmyadmin /usr/share/phpmyadmin
Alias /postfixadmin /var/www/htdocs/postfixadmin/public
Alias /webmail /var/www/roundcubemail
Alias /webstat /var/www/vhosts/default/webstat
Alias /roundcube /var/lib/roundcube
Alias /roundcubemail /var/lib/roundcube
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNUL
SSLCertificateFile /etc/letsencrypt/live/server3.netthelp.de/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/server3.netthelp.de/privkey.pem
# SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
# SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
SSLProtocol All -SSLv2 -SSLv3
<Directory "/var/www/vhosts/default/cgi-bin/">
AllowOverride None
Options None
Require all granted
</Directory>
<Directory /var/www/vhosts/default/httpsdocs>
SSLRequireSSL
AllowOverride None
Options None
Require all granted
</Directory>
</VirtualHost>
</IfModule>
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.
<VirtualHost *:80>
ServerName www.<dummy>:80
ServerAlias <dummy>
UseCanonicalName Off
DocumentRoot /var/www/vhosts/<dummy>/httpdocs
CustomLog /var/log/apache2-vhosts.d/<dummy>_access.log combined
ErrorLog /var/log/apache2-vhosts.d/<dummy>_error.log
CustomLog /var/log/apache2/vhosts_access.log vhost_combined
ScriptAlias /cgi-bin/ /var/www/vhosts/<dummy>/cgi-bin/
Alias /webstat /var/www/vhosts/<dummy>/webstat
Alias /.well-known /var/www/htdocs/.well-known
<IfModule mod_ssl.c>
SSLEngine off
</IfModule>
<Directory /var/www/vhosts/<dummy>/httpdocs>
<IfModule mod_php7.c>
php_admin_flag engine on
php_admin_flag safe_mode off
php_admin_value include_path "/var/www/vhosts/<dummy>/httpdocs:.:/tmp:./:/usr/share/php/PEAR/:/var/www/typo3src"
php_admin_value open_basedir "/var/www/vhosts/<dummy>/httpdocs:/tmp:.:/usr/share/php/PEAR/:/var/www/typo3src"
php_value date.timezone "Europe/Berlin"
</IfModule>
<IfModule mod_python.c>
<Files ~ (\.py$)>
SetHandler python-program
PythonHandler mod_python.cgihandler
</Files>
</IfModule>
Options -Includes +ExecCGI
</Directory>
<Directory "/var/www/vhosts/<dummy>/cgi-bin">
AllowOverride None
Options +ExecCGI -Includes
Require all granted
</Directory>
</VirtualHost>
# soll SSL aktiviert werden das _no entfernen
<IfModule mod_ssl_no.c>
<VirtualHost *:443>
ServerName www.<dummy>:443
ServerAlias <dummy>
UseCanonicalName Off
DocumentRoot /var/www/vhosts/<dummy>/httpdocs
CustomLog /var/log/apache2-vhosts.d/<dummy>_access.log combined
ErrorLog /var/log/apache2-vhosts.d/<dummy>_error.log
ScriptAlias /cgi-bin/ /var/www/vhosts/<dummy>/cgi-bin/
Alias /webstat /var/www/vhosts/<dummy>/webstat
SSLEngine on
<Directory /var/www/vhosts/<dummy>/httpdocs>
<IfModule mod_php7.c>
php_admin_flag engine on
php_admin_flag safe_mode off
php_admin_value include_path "/var/www/vhosts/<dummy>/httpdocs:.:/tmp:./:/usr/share/php/PEAR/:/var/www/typo3src"
php_admin_value open_basedir "/var/www/vhosts/<dummy>/httpdocs:/tmp:.:/usr/share/php/PEAR/:/var/www/typo3src"
php_value date.timezone "Europe/Berlin"
</IfModule>
<IfModule mod_python.c>
<Files ~ (\.py$)>
SetHandler python-program
PythonHandler mod_python.cgihandler
</Files>
</IfModule>
Options -Includes +ExecCGI
</Directory>
<Directory "/var/www/vhosts/<dummy>/cgi-bin">
AllowOverride None
Options +ExecCGI -Includes
Require all granted
</Directory>
SSLCertificateFile /etc/letsencrypt/live/<dummy>/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/<dummy>/privkey.pem
SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder On
SSLCompression off
# Add six earth month HSTS header for all users...
# Header add Strict-Transport-Security "max-age=15768000"
SSLCipherSuite EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
CustomLog /var/log/apache2-vhosts.d/<dummy>_ssl.log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
</IfModule>
Server-Module
Vorsichtshalber noch einmal ein paar Apache-Module aktivieren:
a2enmod perl a2enmod cgi a2enmod expires a2enmod headers a2enmod rewrite a2enmod ssl service apache2 restart
NextCloud Update
Auf dem bisherigen Server war Nextcloud 15.0.x die höchste Version, danach war die PHP-Version nicht mehr für eine Aktualisierung geeignet. Auf dem neuen Server läuft php7.4, was erst mit NextCloud 18.x.y unterstützt wird. Ein Update ohne Fehlermeldungen ist also nicht möglich.
Es gibt aber einen einfachen Trick mittels:
joe lib/versioncheck.php
wird die Versionsnummer manipuliert. Wenn man dort als Grenze 7.5 einsetzt, dann läuft das Update mittels
sudo -u www-data php occ upgrade
durch.
Ich hatte dann im Frontend bei Version 16.0.11 die Fehlermeldung
Das Setzen der Umgebungslokale auf en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8 ist fehlgeschlagen
Das lässt sich lösen mittels
joe /etc/apache2/envvars
Hier in Zeile 28 das Kommentarzeichen entfernen
## Uncomment the following line to use the system default locale instead: . /etc/default/locale
und den Apache neu starten.
Neuere Versionen wünschen sich noch die folgenden PHP-Module
apt install php-gmp php-bcmath
Irgendwann stand bei mir in Verwaltung->Übersicht:
Ihr Datenverzeichnis ist ungültig. Stellen Sie sicher, dass eine Datei ".ocdata" im Wurzelverzeichnis des data-Verzeichnisses existiert.
bzw.
Your data directory is invalid ensure there is a file called “.ocdata” in the root of the data directory.
Trotzdem hat Nextcloud ganz normal funktioniert. Dieses Problem scheint recht oft aufzutauchen, es gibt aber wenig zielführende Hinweise zur Problemlösung. Auch die Empfehlung für
sudo -u www-data php occ files:scan --all
hat keinerlei Fehlermeldung geliefert.
Bei mir hat es geholfen bei Verwaltung->Grundeinstellungen->Hintergrundaufgaben von Ajax auf Webcron umzustellen und dann wieder zurück. Danach war die Fehlermeldung beseitigt.
Die Aktualistät und teilweise auch die Sicherheit des installierten Systems kann man unter https://scan.nextcloud.com/ testen lassen.
Talk mit eigenem TURN-Server
Meistens sitzt man ja heutzutage hinter irgendeiner Art von Firewall. Von daher ist die Nutzung von Nextcloud Talk auf der Serverseite nicht trivial. Ähnlich wie bei der VOIP-Telefonier braucht man einen Hilfsdienst für die Kontaktaufnahme, einen TURN-Server. Ohne diesen Server funktionieren Telefonie und Videotelefonie nicht.
Mehr Informationen unter https://adminforge.de/nextcloud/nextcloud-talk-mit-eigenem-turn-server/ und https://decatec.de/home-server/nextcloud-talk-mit-eigenem-turn-server-coturn/ und etwas zum Hintergrund in meinem Blog unter https://www.debacher.de/ublog/2020/11/nextcloud-talk-webrtc-stun-und-turn/ .
Zum Glück ist die benötigte Software bei Ubuntu in den Paketen verfügbar:
apt install coturn
Zur Aktivierung muss man die Datei /etc/default/coturn editieren und die letzte Zeile aktivieren:
# # Uncomment it if you want to have the turnserver running as # an automatic system service daemon # TURNSERVER_ENABLED=1
Dann die Konfiguration in der Datei /etc/turnserver.conf anpassen. Hier sind alles Zeilen schon vorhanden, aber zum Teil auskommentiert bzw. müssen angepasst werden. Das folgende Listing zeigt nur die bei mir aktiven Zeilen (grep -v "#" /etc/turnserver.conf | grep "\S"):
tls-listening-port=5349 fingerprint lt-cred-mech use-auth-secret static-auth-secret=EinGrossesGeheimnis realm=nextcloud.debacher.de total-quota=100 bps-capacity=0 stale-nonce=600 cert=/etc/coturn/certs/nextcloud.<meine Domain>.de.cert pkey=/etc/coturn/certs/nextcloud.<meine Domain.de.key cipher-list="ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384" dh-file=/etc/dovecot/dh4096.pem no-stdout-log syslog no-multicast-peers no-tlsv1 no-tlsv1_1
Das Geheimnis static-auth-secret kann man auch gut erzeugen mittels:
openssl rand -hex 32
Das dh-file habe ich schon für Dovecot erzeugt. Wenn nicht, dann kann man das nachholen mittels:
openssl dhparam -out /etc/dovecot/dh4096.pem 4096
Dann
service coturn restart
Jetzt noch die Ports freigeben
ufw allow 5349/tcp ufw allow 5349/udp
In der Konfiguration benutzte ich die Zertifikate, die auch die Nextcloud-Instanz für https benutzt. Leider ist das nicht ganz trivial, weil die für den Apache genutzten Letsencrypt-Dateien nur Links sind. Und mit diesen Links mag coturn anscheinend nicht arbeiten. Daher muss man zu einem kleinen Trick greifen, den ich auf der Seite https://serverfault.com/questions/849683/how-to-setup-coturn-with-letsencrypt gefunden habe.
mkdir -p /etc/coturn/certs
chown -R turnserver:turnserver /etc/coturn/
chmod -R 700 /etc/coturn/
touch /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh
chmod 700 /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh
Der Inhalt der Datei /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy sieht dann folgendermaßen aus
#!/bin/sh
for domain in $RENEWED_DOMAINS; do
if [ "$domain" = "owncloud.<meine Domain>.de" ]
then
daemon_cert_root=/etc/coturn/certs
# Sicherstellen, dass certificate und private key files nie allgemein lesbar sind,
# auch nicht während des Kopierens nach daemon_cert_root.
umask 077
cp "$RENEWED_LINEAGE/fullchain.pem" "$daemon_cert_root/$domain.cert"
cp "$RENEWED_LINEAGE/privkey.pem" "$daemon_cert_root/$domain.key"
# Passenden Eigentümer und Rechte für den Dämon setzen, zum Lesen von certificate und key.
chown turnserver "$daemon_cert_root/$domain.cert" "$daemon_cert_root/$domain.key"
chmod 400 "$daemon_cert_root/$domain.cert" "$daemon_cert_root/$domain.key"
service coturn restart >/dev/null
fi
done
Auch wenn die Syntax an manchen Stellen etwas komisch ist, es liegt kein Tippfehler vor. Die runde Klammer muss nicht geöffnet sein, die schließende runde Klammer beendet den Vergleichs-Fall. Die beiden Semikolons beenden den Vergleich, es werden keine weiteren Untersuchungen vorgenommen.
Inhaltlich wird hier ein Letsencrypt-Hook genutzt, der nach einer Aktualisierung des Nextcloud-Zertifikates das neue Zertifikat in ein Verzeichnis für Coturn kopiert und die Rechte auch passend setzt.
Den eben installierten Server muss man dann noch in Nextcloud in den Einstellungen bekannt machen:
Mediawiki-Update
Bei mediawiki 1.31.8 tauchten PHP-Warnungen in der Logdatei auf, unter PHP7.4:
PHP Notice: Trying to access array offset on value of type null in /var/www/vhosts/debacher.de/httpdocs/uwiki/includes/profiler/SectionProfiler.php on line xx
Wobei xx=99, 100 bzw. 101
Der Patch dazu ist relativ einfach, es werden einfach die drei Zeilen ersetzt:
# $totalCpu = max( $this->end['cpu'] - $this->start['cpu'], 0 );
# $totalReal = max( $this->end['real'] - $this->start['real'], 0 );
# $totalMem = max( $this->end['memory'] - $this->start['memory'], 0 );
if ( is_array( $this->start ) ) {
$totalCpu = max( $this->end['cpu'] - $this->start['cpu'], 0 );
$totalReal = max( $this->end['real'] - $this->start['real'], 0 );
$totalMem = max( $this->end['memory'] - $this->start['memory'], 0 );
} else {
$totalCpu = 0;
$totalReal = 0;
$totalMem = 0;
}
Mailsystem
Achtung: Dieser ganze Abschnitt hat nur noch historische Bedeutung. Eine aktuellere Überarbeitung ist zu finden unter Überarbeitung der Mail-Konfiguration für Ubuntu 20.04
Auf diesem Server soll es möglichst wenig Systembenutzer geben. Alle Mailadressen sollen also virtuell sein. Das System besteht aus folgenden Komponenten:
- Postfix
- Dovecot
- MySQL
- PostfixAdmin
Eine weitere wichtige Rolle hierbei spielt
- Roundcube
damit kann der virtuelle Benutzer Mailfilter bearbeiten und sein eigenes Passwort ändern.
Vorweg noch ein paar Informationen, die für das Testen ganz nützlich sind, man kann Mails in der Warteschlange (Abfrage mittels mailq) einzeln oder alle auf einen Schlag löschen.
postsuper -d ALL postsuper -d 52DBF19F51 (die komische Zeichenkette ist die Queue-ID die mailq mit ausgibt, hier wird diese Mail gelöscht)
Den Inhalt der Mail findet man in
/var/spool/postfix/deferred/5/52DBF19F51
Zustellinformationen bzw. Fehlermeldungen in
/var/spool/postfix/defer/5/52DBF19F51
oder per
postcat -vq 52DBF19F51 (zeigt die Mail mit der angegebenen ID)
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
Zunächst müssen einige Pakete installiert werden
apt install amavisd-new amavisd-new-postfix postfix postfix-mysql postfixadmin dovecot-antispam spamass-milter roundcube roundcube-plugins roundcube-plugins-extra mailutils apt install dovecot-lmtpd dovecot-managesieved dovecot-mysql dovecot-pop3d clamav clamav-daemon
Postfixadmin
Die Beschreibung geht aus von dem Programmpaket PostfixAdmin, welches die Verwaltung der virtuellen Mail-Adressen über ein kleines nettes Webfrontend erlaubt. Die Homepage dieses Programms ist http://postfixadmin.sourceforge.net/. Das Programmpaket greift nur auf eine MySQL-Datenbank zu und nicht direkt in das Mailsystem ein.
Das Programm lässt sich ganz einfach über die Paketverwaltung installieren und liegt in der Version 3.2 vor.
Das Programm wird im Verzeichnis /usr/share/postfixadmin eingerichtet und die Konfigurationsdateien sind auf /etc/postfixadmin verlinkt.
Datenbanken
Für das Tool wird eine Datenbank mit folgenden Tabellen benötigt (So legt sie Postfixadmin über http://<server>/postfixadmin/setup.php an, bzw. aktualisiert sie beim Aufruf der Seite, das kann dann übrigens etwas dauern).
CREATE TABLE `admin` (
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`superadmin` tinyint(1) NOT NULL DEFAULT '0',
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
`phone` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`email_other` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`token_validity` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
PRIMARY KEY (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Virtual Admins';
CREATE TABLE `alias` (
`address` varchar(255) NOT NULL,
`goto` text NOT NULL,
`domain` varchar(255) NOT NULL,
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`address`),
KEY `domain` (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Virtual Aliases';
CREATE TABLE `alias_domain` (
`alias_domain` varchar(255) NOT NULL DEFAULT '',
`target_domain` varchar(255) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`alias_domain`),
KEY `active` (`active`),
KEY `target_domain` (`target_domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Domain Aliases';
CREATE TABLE `config` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '',
`value` varchar(20) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=ucs2 COMMENT='PostfixAdmin settings';
CREATE TABLE `domain` (
`domain` varchar(255) NOT NULL,
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`aliases` int NOT NULL DEFAULT '0',
`mailboxes` int NOT NULL DEFAULT '0',
`maxquota` bigint NOT NULL DEFAULT '0',
`quota` bigint NOT NULL DEFAULT '0',
`transport` varchar(255) NOT NULL,
`backupmx` tinyint(1) NOT NULL DEFAULT '0',
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Virtual Domains';
CREATE TABLE `domain_admins` (
`username` varchar(255) NOT NULL,
`domain` varchar(255) NOT NULL,
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Domain Admins';
CREATE TABLE `fetchmail` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`domain` varchar(255) DEFAULT '',
`mailbox` varchar(255) NOT NULL,
`src_server` varchar(255) NOT NULL,
`src_auth` enum('password','kerberos_v5','kerberos','kerberos_v4','gssapi','cram-md5','otp','ntlm','msn','ssh','any') CHARACTER SET ucs2 C
`src_user` varchar(255) NOT NULL,
`src_password` varchar(255) NOT NULL,
`src_folder` varchar(255) NOT NULL,
`poll_time` int unsigned NOT NULL DEFAULT '10',
`fetchall` tinyint unsigned NOT NULL DEFAULT '0',
`keep` tinyint unsigned NOT NULL DEFAULT '0',
`protocol` enum('POP3','IMAP','POP2','ETRN','AUTO') CHARACTER SET ucs2 COLLATE ucs2_general_ci DEFAULT NULL,
`usessl` tinyint unsigned NOT NULL DEFAULT '0',
`sslcertck` tinyint(1) NOT NULL DEFAULT '0',
`sslcertpath` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
`sslfingerprint` varchar(255) DEFAULT '',
`extra_options` text,
`returned_text` text,
`mda` varchar(255) NOT NULL,
`date` timestamp NOT NULL DEFAULT '1999-12-31 23:00:00',
`created` timestamp NOT NULL DEFAULT '1999-12-31 23:00:00',
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `log` (
`timestamp` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`username` varchar(255) NOT NULL,
`domain` varchar(255) NOT NULL,
`action` varchar(255) NOT NULL,
`data` text NOT NULL,
`id` int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
KEY `timestamp` (`timestamp`),
KEY `domain_timestamp` (`domain`,`timestamp`)
) ENGINE=MyISAM AUTO_INCREMENT=455 DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Log';
CREATE TABLE `mailbox` (
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`maildir` varchar(255) NOT NULL,
`quota` bigint NOT NULL DEFAULT '0',
`local_part` varchar(255) NOT NULL,
`domain` varchar(255) NOT NULL,
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`active` tinyint(1) NOT NULL DEFAULT '1',
`phone` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`email_other` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`token_validity` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
PRIMARY KEY (`username`),
KEY `domain` (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Virtual Mailboxes';
CREATE TABLE `quota` (
`username` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`path` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`current` bigint DEFAULT NULL,
PRIMARY KEY (`username`,`path`)
) ENGINE=MyISAM DEFAULT CHARSET=ucs2;
CREATE TABLE `quota2` (
`username` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`bytes` bigint NOT NULL DEFAULT '0',
`messages` int NOT NULL DEFAULT '0',
PRIMARY KEY (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=ucs2;
CREATE TABLE `vacation` (
`email` varchar(255) NOT NULL,
`subject` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`body` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`activefrom` timestamp NOT NULL DEFAULT '1999-12-31 23:00:00',
`activeuntil` timestamp NOT NULL DEFAULT '2038-01-17 23:00:00',
`cache` text NOT NULL,
`domain` varchar(255) NOT NULL,
`interval_time` int NOT NULL DEFAULT '0',
`created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`email`),
KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Postfix Admin - Virtual Vacation';
CREATE TABLE `vacation_notification` (
`on_vacation` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`notified` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '',
`notified_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`on_vacation`,`notified`),
CONSTRAINT `vacation_notification_pkey` FOREIGN KEY (`on_vacation`) REFERENCES `vacation` (`email`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Postfix Admin - Virtual Vacation Notifications';
Nicht alle Tabellen werden in meiner Konfiguration wirklich benutzt, aber es ist einfacher alle anzulegen.
/etc/postfixadmin/dbconfig.inc.php
Die Datenbankanbindung wird in der Ubuntu-Version über diese Datei konfiguriert.
<?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='assword';
$basepath='';
$dbname='postfix';
$dbserver='localhost';
$dbport='';
$dbtype='mysqli';
/etc/postfixadmin/config.local.php
Die momentan aktuelle Version besitzt eine Reihe von neuen Funktionen. Schöne bei dieser Version ist die Konfiguration, die sich in einer Datei config.local.php sammeln lässt. Außerdem sind die Voreinstellungen deutlich dichter an meinen Wünschen. Es bleibt dann hier:
<?php $CONF['configured'] = true; // on submission it will be echoed out to you as a hashed value. $CONF['setup_password'] = '53a0f62bf8dcf073112eb0a0ed8929a:d85f0fe1081dccf9738b01fa7b377005b2ee8d9e'; $CONF['default_language'] = 'de'; $CONF['admin_email'] = 'postmaster@netthelp.de'; $CONF['page_size'] = '20'; $CONF['default_aliases'] = array ( 'abuse' => 'abuse@netthelp.de', 'hostmaster' => 'hostmaster@netthelp.de', 'postmaster' => 'postmaster@netthelp.de', 'webmaster' => 'webmaster@netthelp.de' ); $CONF['quota'] = 'YES'; $CONF['footer_text'] = 'Zurück zu Netthelp.de'; $CONF['footer_link'] = 'http://netthelp.de'; // Welcome Message // This message is send to every newly created mailbox. // Change the text between EOM. $CONF['welcome_text'] = <<<EOM Herzlich Willkommen, zum Netthelp E-Mail Postfach. Bei Fragen oder Problemen bitte Uwe Debacher ansprechen EOM; $CONF['show_undeliverable_exceptions']=array("netthelp.de","debacher.de"); $CONF['used_quotas'] = 'YES'; $CONF['maxquota'] = '2048'; $CONF['domain_quota_default'] = '0';
Probleme mit PostfixAdmin
Beim ersten Start tauchte eine Fehlermeldung auf:
ERROR: the templates_c directory doesn't exist or isn't writeable for the webserver
Ich habe dann dieses Verzeichnis angelegt und dem Webserver übereignet.
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 gleichen Einstellungen finden sich auch noch einmal in der Datei /etc/dbconfig-common/postfixadmin.conf
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
AmaVis
Diese Programm ist dazu da sich um Spam und Viren in EMails zu kümmern. Dazu arbeitet es mit Clamav und Spamassassin zusammen.
Für die Zusammenarbeit ist etwas Vorarbeit zu leisten, ich orientiere mich dabei an https://www.exratione.com/2016/05/a-mailserver-on-ubuntu-16-04-postfix-dovecot-mysql/
Zuerst werden die Programme gegenseitig in ihre Gruppen aufgenommen:
adduser clamav amavis adduser amavis clamav
Dann wird die Datei /etc/clamav/clamd.conf etwas angepasst
# Needed to allow things to work with Amavis, when both amavis and clamav # users are added to one another's groups. AllowSupplementaryGroups true
In der Datei /etc/amavis/conf.d/15-content_filter_mode müssen die Checks überhaupt erst einmal aktiviert werden, indem man die Kommentarzeichen vor den Zeilen entfernt.
use strict; # You can modify this file to re-enable SPAM checking through spamassassin # and to re-enable antivirus checking. # # Default antivirus checking mode # Please note, that anti-virus checking is DISABLED by # default. # If You wish to enable it, please uncomment the following lines: @bypass_virus_checks_maps = ( %bypass_virus_checks, @bypass_virus_checks_acl, $bypass_virus_checks_re); # # Default SPAM checking mode # Please note, that anti-spam checking is DISABLED by # default. # If You wish to enable it, please uncomment the following lines: @bypass_spam_checks_maps = ( %bypass_spam_checks, @bypass_spam_checks_acl, $bypass_spam_checks_re); 1; # ensure a defined return
Eine Anpassung in /etc/default/spamassassin
# Cronjob # Set to anything but 0 to enable the cron job to automatically update # spamassassin's rules on a nightly basis CRON=1
Der Schalter ENABLED=1 in dieser Datei wird nicht mehr unterstützt, stattdessen aktiviert man den Dienst mittels:
systemctl enable spamassassin
Nun noch /etc/amavis/conf.d/50-user anpassen, damit die lokalen Domains aus der Datenbank genommen werden
use strict; # # Place your configuration directives here. They will override those in # earlier files. # # See /usr/share/doc/amavisd-new/ for documentation and examples of # the directives you can use in this file # # Three concurrent processes. This should fit into the RAM available on an # AWS micro instance. This has to match the number of processes specified # for Amavis in /etc/postfix/master.cf. $max_servers = 3; # Add spam info headers if at or above that level - this ensures they # are always added. $sa_tag_level_deflt = -9999; # Check the database to see if mail is for local delivery, and thus # should be spam checked. @lookup_sql_dsn = (['DBI:mysql:database=postfix;host=127.0.0.1;port=3306', 'postfix', 'assword']); $sql_select_policy = 'SELECT domain from domain WHERE CONCAT("@",domain) IN (%k)'; $sql_select_white_black_list = undef; # Uncomment to bump up the log level when testing. # $log_level = 2; $sa_tag_level_deflt = -999; # add spam info headers if at, or above that level $sa_tag2_level_deflt = 5; # add 'spam detected' headers at that level $sa_kill_level_deflt = 12; # triggers spam evasive actions $final_virus_destiny = D_DISCARD; # (data not lost, see virus quarantine) $final_banned_destiny = D_PASS; # D_REJECT when front-end MTA $final_spam_destiny = D_PASS; $final_bad_header_destiny = D_PASS; # False-positive prone (for spam) $virus_admin = "root\@meine-maildomain.de"; $spam_admin = "root\@maine-maildomain.de";
#------------ Do not modify anything below this line ------------- 1; # ensure a defined return
Zum Abschluss die Dienste neu starten:
service clamav-freshclam restart service clamav-daemon restart service amavis restart service spamassassin restart
Spamfilterung mit Razor und Pyzor
Razor und Pyzor sind Erweiterungen, deren Daten von einer Community gefplegt werden und dadurch innerhalb recht kurzer Zeit auf Spamwellen reagieren können.
apt install pyzor razor
Und dann zur Einbindung
su - amavis -s /bin/bash razor-admin -register razor-admin -create razor-admin -discover
Dovecot
Unter http://www.debacher.de/wiki/Root-Server_mit_OpenSuSE_13.2#Dovecot habe ich die schrittweise Entwicklung meiner Dovecot-Konfiguration beschrieben. Diese Konfiguration passt weiterhin und befindet sich in der Datei /etc/dovecot/local.conf
disable_plaintext_auth = no first_valid_uid = 303 mail_access_groups = postfix mail_privileged_group = postfix mail_location = maildir:/var/vmail/%d/%n mail_home = /var/vmail/%d/%n/home mail_plugins = $mail_plugins quota passdb { args = /etc/dovecot/dovecot-mysql.conf driver = sql } plugin { sieve = ~/.dovecot.sieve sieve_dir = ~/sieve } dict { sqluserquota = mysql:/etc/dovecot/dovecot-dict-sql-user.conf } protocols = imap pop3 lmtp sieve ssl = yes #ssl_cert = </etc/letsencrypt/live/server2.netthelp.de/fullchain.pem #ssl_key = </etc/letsencrypt/live/server2.netthelp.de/privkey.pem ssl_cert = </etc/ssl/certs/ssl-cert-snakeoil.pem ssl_key = </etc/ssl/private/ssl-cert-snakeoil.key userdb { args = /etc/dovecot/dovecot-mysql.conf driver = sql } verbose_proctitle = yes protocol pop3 { pop3_uidl_format = %08Xu%08Xv mail_max_userip_connections = 5 } protocol imap { mail_max_userip_connections = 8 mail_plugins = $mail_plugins imap_quota } service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0660 user = postfix } } protocol lmtp { mail_plugins = $mail_plugins sieve } service lmtp { unix_listener lmtp { #mode = 0666 } unix_listener /var/spool/postfix/private/dovecot-lmtp { #mode = 0666 user = postfix group = postfix } } service managesieve-login { inet_listener sieve { port = 4190 } } plugin { #quota_rule = *:storage=1G #quota_rule2 = Trash:storage=+100M quota_grace = 10%% quota_warning = storage=66%% quota-warning 66 %u quota_warning2 = storage=80%% quota-warning 80 %u quota_warning3 = storage=95%% quota-warning 95 %u # quota = maildir:User quota:noenforcing quota = dict:User Quota::noenforcing:proxy::sqluserquota } service quota-warning { executable = script /usr/local/bin/quota-warning.sh user = vmail unix_listener quota-warning { group = vmail mode = 0660 user = vmail } } service dict { unix_listener dict { mode = 0600 user = vmail } } auth_mechanisms = plain login #verbose_proctitle = no #mail_debug = yes #auth_debug = yes auth_debug_passwords = yes #auth_verbose = yes auth_verbose_passwords = sha1
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
durch Voranstellen einer Raute deaktiviert habe. Das spart viel Zeit bei allen Authentifizierungen.
/etc/dovecot/dovecot-mysql.conf
Diese Datei musste ich etwas anpassen, da bei Ubuntu die Mysql-Socket Adresse etwas anders ist.
# Database driver: mysql, pgsql driver = mysql # Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT. default_pass_scheme = CRYPT # Database options connect = host=/var/run/mysqld/mysqld.sock dbname=postfix user=postfix password=assword password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1' user_query = SELECT concat('maildir:/var/vmail/',maildir) as mail, \ 303 AS uid, \ 303 AS gid, \ CONCAT('*:bytes=', \ IF(mailbox.quota = 0, domain.maxquota*1024000, mailbox.quota)) \ as quota_rule \ FROM mailbox, domain \ WHERE username = '%u' AND mailbox.active = '1' AND \ domain.domain = '%d' AND domain.active = '1' iterate_query = SELECT username as user FROM mailbox WHERE active ='1'
/etc/dovecot/dovecot-dict-sql-user.conf
Auch in dieser Datei muss ich der veränderten Socket Rechnung tragen
connect = host=/var/run/mysqld/mysqld.sock dbname=postfix user=postfix password=assword map { pattern = priv/quota/storage table = quota2 username_field = username value_field = bytes } map { pattern = priv/quota/messages table = quota2 username_field = username value_field = messages }
Erweiterungen
Manchmal taucht das Problem auf, dass auf dem Server mehrere Ordner für den gleichen Zwecke auftauchen, also z.B.
- .Junk
- .Spam
für unerwünschte Mails und
- .Trash
- .Papierkorb
für Müll
- .Gesendet
- .Sent
für verschickte Mails
Das kann man verhindern, indem man die Konfiguration der /etc/dovecot/conf.d/15-mailboxes.conf erweitert
namespace inbox { mailbox Drafts { special_use = \Drafts } mailbox Junk { special_use = \Junk } mailbox Spam { special_use = \Junk } mailbox Trash { special_use = \Trash } mailbox Papierkorb { special_use = \Trash } .... }
Eventuell muss man hier auf die Eigenarten weiterer Client-Programme reagieren.
Expunge
Die neueren Dovecot-Versionen bieten neue Möglichkeiten für die Mailordner:
namespace inbox { mailbox Drafts { special_use = \Drafts } mailbox Junk { special_use = \Junk auto = create autoexpunge = 26w } mailbox Trash { special_use = \Trash auto = create autoexpunge = 26w } .... }
Über auto = create erreicht man, dass dieses Postfach automatisch mit angelegt wird. Mittels auto = subscribe kann man einstellen, dass das Postfach angelegt und abonniert wird. Mit autoexpunge = 26w erreicht man, dass Mails in dem jeweiligen Ordner gelöscht werden, wenn sie älter als hier 26 Wochen sind. Das macht natürlich nur bei Spam und Papierkorb wirklich Sinn.
Die autoexpunge Funktionalität hat aber einen Seiteneffekt. In meiner Konfiguration ist mail_home = /var/vmail/%d/%n/home eingestellt. Dieser Ordner home wird aber nicht automatisch angelegt aber bei jeder Anmeldung des Benutzers für das autoexpunge benötigt. Es gibt also jedes mal eine Fehlermeldung in der Log-Datei, wenn man diesen Ordner nicht händisch anlegt.
Ich sehe hier drei Lösungsansätze:
- Verzicht auf autoexpunge
- Änderung von mail_location = maildir:/var/vmail/%d/%n und mail_home = /var/vmail/%d/%n/home, so dass mail_location ein Unterverzeichnis von mail_home wird
- das Plugin welcome, darüber könnte man das Verzeichnis automatisch anlegen lassen
Die mittlere Lösung kommt für mich nicht infrage, dazu habe ich schon zu viele Postfächer auf den Servern.
Roundcube
Dieses beliebte Webmail-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
// SQL Driver options // ------------------ // PEAR database DSN for performing the query. By default // Roundcube DB settings are used. $config['password_db_dsn'] = 'mysql://postfix:assword@localhost/postfix'; // The SQL query used to change the password. // The query can contain the following macros that will be expanded as follows: // %p is replaced with the plaintext new password // %P is replaced with the crypted/hashed new password // according to configured password_method // %o is replaced with the old (current) password // %O is replaced with the crypted/hashed old (current) password // according to configured password_method // %h is replaced with the imap host (from the session info) // %u is replaced with the username (from the session info) // %l is replaced with the local part of the username // (in case the username is an email address) // %d is replaced with the domain part of the username // (in case the username is an email address) // Deprecated macros: // %c is replaced with the crypt version of the new password, MD5 if available // otherwise DES. More hash function can be enabled using the password_crypt_hash // configuration parameter. // %D is replaced with the dovecotpw-crypted version of the new password // %n is replaced with the hashed version of the new password // %q is replaced with the hashed password before the change // Escaping of macros is handled by this module. // Default: "SELECT update_passwd(%c, %u)" $config['password_query'] = 'UPDATE mailbox SET password=%c WHERE username=%u'; // By default the crypt() function which is used to create the %c // parameter uses the md5 algorithm (deprecated, use %P). // You can choose between: des, md5, blowfish, sha256, sha512. $config['password_crypt_hash'] = 'md5'; ...
/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 zwei Änderungen notwendig.
Ab Zeile 36
// The mail host chosen to perform the log-in. // Leave blank to show a textbox at login, give a list of hosts // to display a pulldown menu or set one host as string. // To use SSL/TLS connection, enter hostname with prefix ssl:// or tls:// // Supported replacement variables: // %n - hostname ($_SERVER['SERVER_NAME']) // %t - hostname without the first part // %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part) // %s - domain name after the '@' from e-mail address provided at login screen // For example %n = mail.domain.tld, %t = domain.tld $config['default_host'] = 'localhost';
Ab Zeile 74
// List of active plugins (in plugins/ directory) $config['plugins'] = array( 'archive', 'zipdownload', 'managesieve', 'password', );
Die Datenbankkonfiguration findet sich dann in der Datei /usr/share/roundcube/SQL/mysql.initial.sql
Postfix
Das zentrale Mailprogramm wird über die Dateien /etc/postfix/master.cf und /etc/postfix/main.cf konfiguriert. An der master.cf habe ich keinerlei Änderungen vorgenommen. Lediglich die main.cf habe ich nach meinen Anforderungen erweitert.
/etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. #myorigin = /etc/mailname smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) biff = no # appending .domain is the MUA's job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings #delay_warning_time = 4h readme_directory = no # TLS parameters smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_use_tls=yes smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = server3.netthelp.de alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname mydestination = $myhostname, server3.netthelp.de, h2656233.stratoserver.net, localhost.stratoserver.net, localhost relayhost = #mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all inet_protocols = ipv4 content_filter = smtp-amavis:[localhost]:10024 policy-spf_time_limit = 3600s relay_domains = $mydestination, hash:/etc/postfix/relay #virtual_alias_domains = hash:/etc/postfix/virtual #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 virtual_alias_maps = 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 broken_sasl_auth_clients = yes smtpd_sasl_local_domain = smtpd_sasl_path = private/auth smtpd_sasl_security_options = noanonymous smtpd_sasl_type = dovecot smtpd_sasl_auth_enable = yes smtp_sasl_auth_enable = no smtp_sasl_security_options = smtp_sasl_password_maps = unknown_address_reject_code = 552 unknown_client_reject_code = 551 unknown_hostname_reject_code = 550 virtual_gid_maps = static:303 virtual_mailbox_base = /var/vmail/ virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf virtual_mailbox_limit = 0 virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf, proxy:mysql:/etc/postfix/mysql_virtual_alias_domain_mailbox_maps.cf virtual_minimum_uid = 303 virtual_transport = lmtp:unix:private/dovecot-lmtp virtual_uid_maps = static:303 #maps_rbl_reject_code = 451 #canonical_maps = hash:/etc/postfix/canonical #relocated_maps = hash:/etc/postfix/relocated #sender_canonical_maps = hash:/etc/postfix/sender_canonical #transport_maps = hash:/etc/postfix/transport #mail_spool_directory = /var/mail message_strip_characters = \0 defer_transports = mailbox_command = mailbox_transport = mailbox_size_limit = 0 message_size_limit = 0 strict_8bitmime = no strict_rfc821_envelopes = yes smtpd_delay_reject = yes smtpd_helo_required = yes smtpd_client_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_rbl_client cbl.abuseat.org, reject_rbl_client ix.dnsbl.manitu.net, reject_rbl_client bl.spamcop.net, reject_rbl_client dul.dnsb smtpd_helo_restrictions = permit_mynetworks, reject_invalid_hostname smtpd_sender_restrictions = hash:/etc/postfix/access, reject_unknown_sender_domain #smtpd_recipient_restrictions = , check_policy_service unix:private/policy-spf smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination # tls config # eingehende Verbindungen smtpd_use_tls = yes smtpd_enforce_tls = no # Obiges kann zusammengefasst werden zu smtpd_tls_security_level=may smtpd_tls_loglevel = 1 smtpd_tls_received_header = yes smtpd_tls_session_cache_timeout = 3600s smtpd_tls_mandatory_protocols=!SSLv2, !SSLv3 #tls_random_source = dev:/dev/urandom #tls_random_prng_update_period = 3600s # ausgehende Verbindungen smtp_use_tls = yes smtp_enforce_tls = no # Obiges kann zusammengefasst werden zu smtp_tls_security_level=may smtp_tls_note_starttls_offer = yes smtp_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtp_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key #smtp_tls_key_file = /etc/letsencrypt/live/server3.netthelp.de/privkey.pem #smtp_tls_cert_file = /etc/letsencrypt/live/server3.netthelp.de/fullchain.pem smtp_tls_loglevel = 1 smtp_tls_mandatory_protocols=!SSLv2, !SSLv3 # Limits - neu ab 17.1.17 gegen Spam smtpd_client_message_rate_limit = 50 smtpd_client_connection_rate_limit = 10 smtpd_client_recipient_rate_limit = 50 smtpd_client_connection_count_limit = 25 bounce_queue_lifetime = 3d maximal_queue_lifetime = 3d
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 /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.
Für die Dateien kenne ich jeweils zwei unterschiedliche Formulierungen, hier am Beispiel der mysql_virtual_domains_maps.cf
SuSE-Version (deprecated seit Postfix 2.2.)
user = postfix password = password hosts = localhost #hosts = 127.0.0.1 dbname = postfix table = domain select_field = domain where_field = domain additional_conditions = and backupmx = '0' and active = '1'
Übliche Version.
user = postfix password = password hosts = localhost dbname = postfix query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
Bei Ubuntu findet man jeweils die übliche Form.
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"
master.cf kontrollieren
Hier ist erst einmal nur eine Einstellung wichtig, in Zeile 12 muss das Kommentarzeichen am Anfang entfernt sein:
smtp inet n - y - - smtpd
Dienste neu starten
Vor dem Neustart der Dienste müssen die Datenbanken erzeugt werden:
postmap /etc/postfix/access newaliases
und dann
service postfix restart service dovecot restart
Anstehende Erweiterungen
In der nächsten Zeit muss ich für mich die Nutzung von SPF und DKIM klären. Eine nette Beschreibung habe ich unter https://www.linuxbabe.com/mail-server/setting-up-dkim-and-spf gefunden.
Eine Hilfe bei den Tests kann dann die folgende Seite bieten:
Die entsprechenden Test (also für andere Server) sind in der aktuellen Konfiguration schon aktiv
SPF in der master.cf von Postfix (ab Zeile 132)
# Added using postfix-add-policy script: policy-spf unix - n n - 0 spawn user=nobody argv=/usr/bin/policyd-spf
DKIM in der amavis 21-ubuntu_defaults
# DOMAIN KEYS IDENTIFIED MAIL (DKIM) $enable_dkim_verification = 1;
Es ginge also nur darum die passenden Einträge für ausgehende Mails zu erzeugen.
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:
Bei 20.04 scheint das Paket libpam-mysql in Ordnung zu sein. Die Installation von vsftpd ist dadurch 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 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 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}ESM:${distro_codename}"; "${distro_id}:${distro_codename}-updates"; // "${distro_id}:${distro_codename}-proposed"; // "${distro_id}:${distro_codename}-backports"; }; // List of packages to not update (regexp are supported) Unattended-Upgrade::Package-Blacklist { // "vim"; // "libc6"; // "libc6-dev"; // "libc6-i686"; }; // 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 "false"; // Split the upgrade into the smallest possible chunks so that // they can be interrupted with SIGUSR1. 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 unattended-upgrades when the machine is shuting down // instead of doing it in the background while the machine is running // This will (obviously) make shutdown slower 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.
Nützliche Links
- https://yannici.de/server/linux/debian/debian-8-mailserver-installation/
- https://stackoverflow.com/questions/42300579/letsencrypt-certbot-multiple-renew-hooks
- https://www.exratione.com/2019/02/a-mailserver-on-ubuntu-18-04-postfix-dovecot-mysql/
- https://dokuwiki.tachtler.net/doku.php?id=tachtler:dovecot_plugins
- https://doc.dovecot.org/configuration_manual/config_file/config_variables/
- https://workaround.org/ispmail/jessie/postfix-dovecot