Contabo – VPS Server Setup
Server Setup für ecoDMS + Mailserver (Dovecot) + Nameserver
Start-Config
- Wähle ein Server Passwort ohne Sonderzeichen – denn das Tastaturlayout stimmt meist nicht + daher wird es schwierig mit dem einloggen.
- Einloggen per VNC (Passwort vorher über Contabo Control Panel setzen)
- Tastaturlayout Anpassen:
sudo dpkg-reconfigure keyboard-configuration
Dann durch den Dialog:
- Keyboard model → Generic 105-key (oder dein Modell)
- Country → Switzerland
- Layout → German (Switzerland)
- danach:
sudo service keyboard-setup restart
sudo locale-gen de_CH.UTF-8
sudo update-locale LC_ALL=de_CH.UTF-8 LANG=de_CH.UTF-8 - SSH aktivieren (ist bereits installiert)
sudo systemctl enable ssh
sudo systemctl start ssh
Prüfen:sudo systemctl status ssh - Jetzt per SSH connecten für das weitere Setup
ssh benutzer@deine-ip
Partitionieren
1. Im Contabo Panel:
- „Manage“ → „Rescue System“
- Passwort setzen
- Server bootet ins Rescue System
2. Via VNC einloggen als root mit dem gesetzten Passwort
3. GUI starten:
startxfce44. GParted öffnen: Applications → Run Program → gparted
Partitionsschema
| Partition | Grösse | Filesystem | Mountpoint |
|---|---|---|---|
/dev/sda1 | 105 MB | FAT32 | /boot/efi (behalten) |
/dev/sda2 | 20 GB | ext4 | / |
/dev/sda3 | 5 GB | ext4 | /tmp |
/dev/sda4 | 15 GB | ext4 | /var |
/dev/sda5 | 5 GB | ext4 | /opt |
/dev/sda6 | 50 GB | ext4 | /opt/ecodms/data |
/dev/sda7 | 50 GB | ext4 | /opt/ecodms_database |
/dev/sda8 | 15 GB | ext4 | /mail |
/dev/sda9 | 140 GB | ext4 | /backup |
Schritt 1 — /dev/sda1 verkleinern
- Rechtsklick auf
/dev/sda1→ Resize/Move - Neue Grösse eingeben: 20480 MB (= 20 GB)
- Resize klicken
Schritt 2 — Neuen freien Speicher aufteilen
Im nun freien Bereich nacheinander neue Partitionen erstellen. Rechtsklick auf „unallocated“ → New:
| Partition | Grösse (MB) | Filesystem | Label |
|---|---|---|---|
| neue sda | 5120 | ext4 | tmp |
| neue sda | 15360 | ext4 | var |
| neue sda | 5120 | ext4 | opt |
| neue sda | 51200 | ext4 | ecodms-data |
| neue sda | 51200 | ext4 | ecodms-db |
| neue sda | 15360 | ext4 | |
| Rest (~143 GB) | alles übrige | ext4 | backup |
„Partition Name“ entweder leer lassen oder das gleiche wie bei Label rein schreiben.
Schritt 3 — Änderungen anwenden
Oben auf den grünen Haken ✓ klicken → „Apply All Operations“
⚠️ Erst danach werden die Änderungen wirklich geschrieben!
Jetzt rebooten, um Server wieder normal zu starten
reboot
/etc/fstab einrichten
UUIDs raus suchen:
# blkid | grep sda
/dev/sda4: LABEL="opt" UUID="1a2caeb7-7780-4bf6-8583-7b94115ea0c2" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="opt" PARTUUID="5ee847e0-9b04-4102-9c80-03b3768f3644"
/dev/sda16: LABEL="BOOT" UUID="02ae2efa-0a43-47c2-94dd-0cab2e898ba6" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="251d947f-e890-4fc9-aab5-32d0584ca3f8"
/dev/sda2: LABEL="tmp" UUID="fba4d803-9f68-4cbb-926b-51b6b8df4aad" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="tmp" PARTUUID="06d853b8-13de-47ce-9006-e568173a602b"
/dev/sda7: LABEL="mail" UUID="00bce287-db88-44a3-9853-506724f5988e" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="mail" PARTUUID="dd2fd908-62f8-4f91-8856-2b18b6174626"
/dev/sda5: LABEL="ecodms-data" UUID="f67995f6-5ae3-4d50-8b07-781a015e802e" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ecodms-data" PARTUUID="05e98365-6cf4-46d3-be0b-969689bcf13d"
/dev/sda3: LABEL="var" UUID="1253a41f-6a53-41c3-90b3-2ab3b2aa3c87" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="var" PARTUUID="f964a19d-ec81-439a-976a-3f39fcb88880"
/dev/sda15: LABEL_FATBOOT="UEFI" LABEL="UEFI" UUID="A732-B748" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="c2d8823d-c32e-42b0-aeea-9025bfa72093"
/dev/sda1: LABEL="cloudimg-rootfs" UUID="6d085073-3c5a-49aa-9133-d0c30d62315b" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="33145b69-de24-47b8-9114-e691b1972e92"
/dev/sda8: LABEL="backup" UUID="c8f32f9a-a456-4e73-9178-4ef4617c38be" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="backup" PARTUUID="8ae003e0-8ca6-4de9-b73e-842938e9f2f0"
/dev/sda6: LABEL="ecodms-db" UUID="56956818-db52-4410-b7d9-4c899e6664da" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ecodms-db" PARTUUID="c533b2e2-2438-471a-8c64-5340c5190119"
/dev/sda14: PARTUUID="c0485e87-9979-4be9-8211-edaf8aae81b1"
Und daraus das fstab generieren:
vi /etc/fstab
folgendes einfügen:
# Neue Partitionen
UUID=fba4d803-9f68-4cbb-926b-51b6b8df4aad /tmp ext4 defaults 0 2
UUID=1253a41f-6a53-41c3-90b3-2ab3b2aa3c87 /var ext4 defaults 0 2
UUID=1a2caeb7-7780-4bf6-8583-7b94115ea0c2 /opt ext4 defaults 0 2
UUID=f67995f6-5ae3-4d50-8b07-781a015e802e /opt/ecodms/data ext4 defaults 0 2
UUID=56956818-db52-4410-b7d9-4c899e6664da /opt/ecodms_database ext4 defaults 0 2
UUID=00bce287-db88-44a3-9853-506724f5988e /mail ext4 defaults 0 2
UUID=c8f32f9a-a456-4e73-9178-4ef4617c38be /backup ext4 defaults 0 2
Und jetzt noch die Mountpunkte erstellen:
mkdir -p /opt/ecodms/data
mkdir -p /opt/ecodms_database
mkdir /mail
mkdir /backup
und jetzt mounten
mount -a
Und per
df -h
mounts checken
Filesystem Size Used Avail Use% Mounted on
tmpfs 795M 976K 794M 1% /run
/dev/sda1 20G 2.3G 18G 12% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda16 881M 117M 703M 15% /boot
/dev/sda15 105M 6.2M 99M 6% /boot/efi
tmpfs 795M 12K 795M 1% /run/user/0
/dev/sda2 4.9G 24K 4.6G 1% /tmp
/dev/sda3 15G 24K 14G 1% /var
/dev/sda4 4.9G 24K 4.6G 1% /opt
/dev/sda7 15G 24K 14G 1% /mail
/dev/sda8 136G 28K 129G 1% /backup
Ah… die EcoDMS Verzeichnisse FEHLEN werden erst nach dem Mounten von opt erstellt. Daher jetzt nach dem mounten nochmals ausführen:
mkdir -p /opt/ecodms/datamkdir -p /opt/ecodms_databasesystemctl daemon-reloadmount -a
Und jetzt ist alles, wie es sein soll:
# df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 795M 1004K 794M 1% /run
/dev/sda1 20G 2.3G 18G 12% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda16 881M 117M 703M 15% /boot
/dev/sda15 105M 6.2M 99M 6% /boot/efi
tmpfs 795M 12K 795M 1% /run/user/0
/dev/sda2 4.9G 24K 4.6G 1% /tmp
/dev/sda3 15G 24K 14G 1% /var
/dev/sda4 4.9G 36K 4.6G 1% /opt
/dev/sda7 15G 24K 14G 1% /mail
/dev/sda8 136G 28K 129G 1% /backup
/dev/sda5 49G 24K 47G 1% /opt/ecodms/data
/dev/sda6 49G 24K 47G 1% /opt/ecodms_database
Grundconfig
Grundeinstellungen für Bash/Automatische Updates etc. aus den Ubuntu Basics übernehmen.
Login per SSH Key aktivieren
Zusätzlich zum neuen User Login, kann man auch noch das Login per SSH Key aktivieren:
Auf lokaler Maschine: (sollte noch kein id_rsa.pub exisisteren, kann man es mit
ssh-keygen
generieren. Dann:
cat /home/<meinusername>/.ssh/id_rsa.pub
Inhalt beim Server beim lokalen Nutzer in die authorized_keys einfügen, das sich im HOME Verzeichnis befindet. Sollte das File/Folder noch nicht existieren, erstellen:
teslina@corky:~$ cd
teslina@corky:~$ mkdir .ssh
teslina@corky:~$ touch .ssh/authorized_keys
teslina@corky:~$ chmod 700 .ssh/
teslina@corky:~$ chmod 600 .ssh/authorized_keys
teslina@corky:~$ vi .ssh/authorized_keys # --> hier den Inhalt von id_rsa.pub einfügen
Ab jetzt kann man sich ohne Passwort einloggen.
SSH Root Login deaktivieren
Nachdem wir in den Basics einen neuen Server Login User hinzugefügt haben, das SSHD Root Login deaktivieren.
Ausserdem habe keine Lust, dass SSH Connection automatisch nach N Minuten getrennt wird. Wenn Du diese Einstellung beibehalten möchtest, kannst Du diesen Schritt überspringen. Ansonsten die Config wie folgt anpassen:
sudo vi /etc/ssh/sshd_config
Folgendes anpassen:
ClientAliveInterval 30 ClientAliveCountMax 5 PermitRootLogin no
SSHD neu starten:
sudo service sshd restart
Installation Software
Kleines Problem noch: dass /var beim ersten Boot leer war und Ubuntu nur das Nötigste neu erstellt hat.
Daher jetzt noch kurz fehlende Verzeichnisse erstellen:
mkdir /var/lib/dpkgmkdir -p /var/backupsmkdir -p /var/localmkdir -p /var/mailmkdir -p /var/snap
mkdir -p /var/lib/python3
rm /usr/share/python3/runtime.d/byobu.rtupdate
Dann dpkg reparieren:
dpkg --configure -aapt update
Und nun weiter mit:
- System updaten (
apt update && apt upgrade) - Grundsicherheit (fail2ban, ufw Firewall)
- ecoDMS
- Dovecot (Mail)
- DNS
System updaten
apt update
apt upgrade -y
Grundsicherheit
Firewall (ufw):
apt install -y ufw fail2ban
ufw allow 22/tcp
ufw enable
Aktivieren und Status prüfen:
systemctl enable fail2bansystemctl start fail2banufw allow 22/tcpufw enableufw statussystemctl status fail2ban
Jetzt nochmal upgrade:
apt upgrade -y
ecoDMS installieren
Port freigeben
ufw allow 17001 comment ecoDMS
ecoDMS Repository hinzufügen
echo "deb http://www.ecodms.de/ecodms_260164/noble /" > /etc/apt/sources.list.d/ecodms.list
wget -qO /etc/apt/trusted.gpg.d/ecodms.asc http://www.ecodms.de/gpg/ecodms.key
apt update
ecoDMS installieren
apt install ecodmsserver
Postgres DB in eigene Partition verschieben
Zuerst alles stoppen (ecodms startet nach installation selber)
service ecodms stop
service postgresql stop
mv /var/lib/postgresql /opt/ecodms_database/postgresql
vi /etc/postgresql/16/main/postgresql.conf
Zeile ändern:
data_directory = '/opt/ecodms_database/postgresql/16/main'
service postgresql
start
pg_lsclusters
Bind installieren
apt install bind9 bind9utils -y
systemctl status bind9
Verzeichnisstruktur vorbereiten
mkdir -p /etc/bind/working
mkdir -p /etc/bind/master
mkdir -p /etc/bind/zones/
mkdir -p /etc/bind/secondary/autogenerated
mkdir -p /etc/bind/config
mkdir -p /var/log/bind
chown bind:bind /var/log/bind
chown bind:bind /etc/bind/working
Die Zonen vom bisherigen Server auf den neuen kopieren:
# Auf dem alten Server ausführen:
scp -r /etc/bind/zones root@[IP_NEW_SERVER]:/etc/bind/
scp -r /etc/bind/master root@[IP_NEW_SERVER]:/etc/bind/
scp -r /etc/bind/config root@[IP_NEW_SERVER]:/etc/bind/
scp /etc/bind/named.root root@[IP_NEW_SERVER]:/etc/bind/
Berechtigungen anpassen
chown -R bind:bind /etc/bind/working
chmod 775 /etc/bind/working
chown -R bind:bind /var/log/bind
chmod 755 /var/log/bind
Firewall Port ergänzen:
ufw allow 53/tcp
ufw allow 53/udp
Dann AppArmor-Profil für bind anpassen:
vi /etc/apparmor.d/usr.sbin.named
Diese Zeilen hinzufügen (nach den bestehenden /var/log Einträgen):
/var/log/bind/ rw, /var/log/bind/** rw,
Dann auch noch folgendes hinzufügen (einfach unterhalb /etc/bind)
/etc/bind/working/ rw,
/etc/bind/working/** rw,
Dann:
apparmor_parser -r /etc/apparmor.d/usr.sbin.named systemctl start named systemctl status named
MySQL Client installieren
apt install mysql-client -y
Certbot installieren
apt install certbot -y
Damit Let’s Encrypt den Domain Challence machen kann, Port öffnen:
ufw allow 80/tcp
Dann Zertifikat anfordern:
certbot certonly --standalone -d mail.meinmailserver.com
Dovecot installieren
Ich migriere hier die Daten vom alten Server. Daher mach ich noch einen Zwischenschritt bei den Configs:
Zuerst UID auf vorherigem Server prüfen:
id vmail
Dann auf dem neuen Server mit gleicher UID hinzufügen:
groupadd -g [GID_VON_VORHERIGEM_SERVER] vmail
useradd -u [UID_VON_VORHERIGEM_SERVER] -g vmail -d /mail -s /usr/sbin/nologin vmail
apt install dovecot-core dovecot-imapd dovecot-mysql
Verzeichnisse erstellen:
mkdir -p /mail/dovecot
chown -R vmail:vmail /mail
chmod -R 700 /mail
Damit wir dann per MySQL auf den Server zugreifen können, noch die IP Adresse vom Dovecot Server auf dem Mysql Server in der Firewall eintragen:
ufw allow from <DOVECOT_SERVER_IP> to any port 3306
Und im Mysql den dovecot SQL User eröffnen:
CREATE USER '<USERNAME>'@'<DOVECOT_SERVER_IP>' IDENTIFIED BY '<DEIN_PASSWORT>';
GRANT ALL PRIVILEGES ON <DB_NAME>.* TO '<USERNAME>'@'<DOVECOT_SERVER_IP>';
FLUSH PRIVILEGES;
Config Files anpassen (siehe Dovecot Install)
10-mail.conf
vi /etc/dovecot/conf.d/10-mail.conf
Diese Werte setzen/ändern:
mail_home = /mail/dovecot/%n
mail_location = maildir:~/Maildir:LAYOUT=fs mail_uid = vmail mail_gid = vmail namespace inbox { separator = . prefix = INBOX. inbox = yes }
10-auth.conf
vi /etc/dovecot/conf.d/10-auth.conf
Ändern:
disable_plaintext_auth = yes auth_mechanisms = plain login
Und sicherstellen dass nur diese Zeile aktiv ist:
!include auth-sql.conf.extAlle anderen !include auskommentieren.
auth-sql.conf.ext
vi /etc/dovecot/conf.d/auth-sql.conf.ext
Inhalt:
passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext }
userdb {
driver = static
args = uid=vmail gid=vmail home=/mail/dovecot/%n
}
dovecot-sql.conf.ext
vi /etc/dovecot/dovecot-sql.conf.ext
Inhalt:
driver = mysql connect = host=localhost dbname=<DB_NAME> user=<USERNAME> password=<DEIN_PASSWORT> default_pass_scheme = SHA512-CRYPT password_query = \ SELECT \ CONCAT(username, '@', domain) AS user, \ password \ FROM mail_entries \ WHERE ( username = '%Lu' \ OR ( username = '%Ln' AND domain = '%Ld' ) \ ) AND is_active = 1 AND is_mainaccount = 1
Berechtigungen setzen:
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
10-ssl.conf
vi /etc/dovecot/conf.d/10-ssl.conf
Vorerst nur:
ssl = yes ssl_cert = </etc/letsencrypt/live/mail.meinserver.com/fullchain.pem ssl_key = </etc/letsencrypt/live/mail.meinserver.com/privkey.pem
SSL-Zertifikate kommen später wenn Let’s Encrypt eingerichtet ist — vorerst können wir selbstsignierte Zertifikate verwenden zum Testen.
Danach Dovecot starten:
systemctl restart dovecot systemctl status dovecot
MySQL Testen:
mysql -h <MYSQL_SERVER> -u <DOVECOT_USER> -p
Jetzt IMAP Port in der Firewall öffnen:
ufw allow 143/tcp ufw allow 993/tcp
Dann einen echten IMAP-Login testen:
openssl s_client -connect corky:993
Nach der Verbindung eintippen:
a LOGIN user@domain.com PASSWORT
Mailboxen von altem Server migrieren:
Phase 1: Erster grosser Sync:
Zwei Varianten – entweder
Von ServerNew pullen:
nohup rsync -avz --progress root@ServerOld:/mail/dovecot/ /mail/dovecot/ > /var/log/rsync-mail.log 2>&1&Von ServerOld pushen:
nohup rsync -avz --progress /mail/dovecot/ root@ServerNew:/mail/dovecot/ > /var/log/rsync-mail.log 2>&1&Phase 2 — Kurz vor Umschaltung (mit –delete für sauberen Abschluss):
rsync -avz --delete --progress root@ServerOld:/mail/dovecot/ /mail/dovecot/
Was passiert, wenn ich auf einem der Server neue Mails erhalte oder lösche…?
| Aktion | Mit --delete | Ohne --delete |
|---|---|---|
| Mail auf ServerOld gelöscht | wird auch auf ServerNew gelöscht | bleibt auf ServerNew erhalten |
| Mail auf ServerOld neu | wird auf ServerNew kopiert | wird auf ServerNew kopiert |
| Mail auf ServerNew gelöscht | wird von ServerOld wiederhergestellt | bleibt gelöscht |
Postfix installieren
apt install postfix postfix-mysql -y
apt install postsrsd -y
Während der Installation fragt er nach dem Setup-Typ — wähle „Internet Site“ und als Systemname mail.deinmailservername.com.
Dann noch mailutilities
apt install mailutils -y
Firewall öffnen:
ufw allow 25/tcp
ufw allow 587/tcp
Jetzt die MySQL Config Files erstellen:
mkdir -p /etc/postfix vi /etc/postfix/mysql-virtual-domains.cf
Inhalt:
user = dbuser password = dbpass hosts = dbhost dbname = dbname query = SELECT DISTINCT domain FROM mail_entries WHERE domain='%s' UNION SELECT DISTINCT source_domain FROM mail_domain_forwarding WHERE source_domain='%s'
vi /etc/postfix/mysql-virtual-mailboxes.cf
user = dbuser
password = dbpass
hosts = dbhost
dbname = dbname
query = SELECT CONCAT(username, '/Maildir/') FROM mail_entries WHERE username='%u' AND domain='%d' AND is_active=1 AND is_mainaccount=1
vi /etc/postfix/mysql-virtual-aliases.cf
user = dbuser
password = dbpass
hosts = dbhost
dbname = dbname query = SELECT alias FROM mail_alias_entries WHERE username='%s' UNION SELECT CONCAT(username,'@',domain) FROM mail_entries WHERE alias='%s' AND is_active=1
vi /etc/postfix/mysql-virtual-alias-domains.cf
user = dbuser
password = dbpass
hosts = dbhost
dbname = dbname
query = SELECT alias FROM mail_alias_entries WHERE username='%s' UNION SELECT CONCAT(username,'@',domain) FROM mail_entries WHERE alias='%u' AND domain='%d' AND is_active=1
vi /etc/postfix/mysql-relay-domains.cf
user = dbuser
password = dbpass
hosts = dbhost
dbname = dbname query = SELECT domain FROM mail_mx_entries WHERE domain='%s'
Berechtigungen setzen:
chmod 640 /etc/postfix/mysql-*.cf chown root:postfix /etc/postfix/mysql-*.cf
Jetzt main.cf anpassen für Virtual Mailboxes mit MySQL:
vi /etc/postfix/main.cf
Diese Zeilen ändern/hinzufügen:
myhostname = mail.meinmailserver.com mydomain = meinmailserver.com myorigin = /etc/mailname # Nur localhost empfangen - virtuelle Domains übernehmen den Rest mydestination = localhost # Virtual Mailbox Setup virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-domains.cf virtual_mailbox_base = /mail/dovecot virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailboxes.cf virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-aliases.cf, mysql:/etc/postfix/mysql-virtual-alias-domains.cf relay_domains = mysql:/etc/postfix/mysql-relay-domains.cf # Berechtigungen für Mailboxen (vmail user) virtual_uid_maps = static:1003 virtual_gid_maps = static:1003 virtual_minimum_uid = 1003 # SMTP Banner smtpd_banner = $myhostname ESMTP # TLS - vorerst selbstsigniert smtpd_tls_cert_file = /etc/ssl/mail/mail.crt smtpd_tls_key_file = /etc/ssl/mail/mail.key smtpd_tls_security_level = may
Dann Postfix neu starten und testen:
systemctl restart postfix systemctl status postfix
Testen
Und MySQL-Verbindung testen (verwende eine testmaildomain.com, welche in deinen virtual domains gespeichert ist):
postmap -q testmaildomain.com mysql:/etc/postfix/mysql-virtual-domains.cfOutput vom Test müsste dann
testmaildomain.com
sein.
Weitere Tests:
# postmap -q mail@mainmail.com mysql:/etc/postfix/mysql-virtual-mailboxes.cf
Output:mainmail.com/mainmail/
Aliases Testen:
postmap -q meine@email.com mysql:/etc/postfix/mysql-virtual-aliases.cf
emailcomuser@email.com
Domain-Forwarding und Relay-Domain testen:
postmap -q testdomain.com mysql:/etc/postfix/mysql-relay-domains.cf
Echten Mailversand Testen:
echo "Test Mail von neuem Mailserver" | mail -s "Test" meine@email.com
Und Postfix Log prüfen:
tail -20 /var/log/mail.log
-> Die Mail müsste nun direkt ausgeliefert worden sein (falls das Dovecot-User-Dir in der Verzeichnisstruktur noch nicht vorhanden war, müsste es jetzt erstellt worden sein)
more /mail/dovecot/email.com/emailcomuser/new/1779800598.V807I80005M472890.myhostname
Return-Path: <root@myhostname>
X-Original-To: meine@email.com
Delivered-To: emailcomuser@email.com
Received: by mail.mainmailserver.com (Postfix, from userid 0)
id C4C4B4017E; Tue, 26 May 2026 15:03:17 +0200 (CEST)
Subject: Test
To: <meine@email.com>
User-Agent: mail (GNU Mailutils 3.17)
Date: Tue, 26 May 2026 15:03:17 +0200
Message-Id: <20260526130317.C4C4B4017E@mail.mainmailserver.com>
From: root <root@myhostname>
Test Mail von neuem Mailserver
Dann noch testen ob Dovecot die Mail via IMAP abrufen kann:
openssl s_client -connect myhostname:993
Nach Verbindung:
a LOGIN meine@email.com PASSWORT b SELECT INBOX c FETCH 1 BODY[]
SMTP Auth
Postfix muss Dovecot für die SASL-Authentifizierung nutzen.
apt install libsasl2-modules -y
Dann main.cf anpassen:
vi /etc/postfix/main.cf
Hinzufügen:
# SASL Auth via Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
# Nur authentifizierte User dürfen senden
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destinationDann Dovecot SASL Socket einrichten:
vi /etc/dovecot/conf.d/10-master.conf
Den service auth Block suchen und anpassen — den unix_listener für Postfix aktivieren (bestehende unix_listener wie auth-userdb unverändert lassen):
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}vi /etc/postfix/master.cf
Diese Zeile suchen und auskommentieren (das # entfernen):
submission inet n - y - - smtpd
Und darunter diese Optionen hinzufügen / auskommentieren:
-o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject
Dann:
ufw allow 587/tcp
systemctl restart dovecot
postfix reload
Nochmals testen:
openssl s_client -connect myhostname:587 -starttls smtp
Nach Verbindung kurz Testen mit swaks:
apt install swaks -y swaks --to user@email.com --from user@email.com --server myhostname:587 --auth LOGIN --auth-user user@email.com --auth-password PASSWORD --tls
-> Wenn es funktioniert, müsste nun ein Testmail angezeigt werden.
Let’s Encrypt Mail-Zertifikat
Let’s Encrypt Zertifikat holen:
apt install certbot -y certbot certonly --standalone -d mail.meinmailserver.com
Jetzt Dovecot und Postfix auf das neue Zertifikat umstellen:
Dovecot:
vi /etc/dovecot/conf.d/10-ssl.conf
ssl = yes ssl_cert = </etc/letsencrypt/live/mail.meinmailserver.com/fullchain.pem ssl_key = </etc/letsencrypt/live/mail.meinmailserver.com/privkey.pem
Postfix:
vi /etc/postfix/main.cf
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.meinmailserver.com/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/mail.meinmailserver.com/privkey.pem
Dann:
systemctl restart dovecot postfix reload
PTR Record aktualisieren
Wichtig beim Mailserver / Mailversand. PTR Record sollte auf mail.meinmailserver.com zeigen. Bei Contabo kann man das in der DNS-Verwaltung / Reverse DNS Management machen:
Danach testen:
dig -x <ServerIP>
Müsste jetzt
;; ANSWER SECTION:
<ServerIP>.in-addr.arpa. 86400 IN PTR mail.meinmailserver.com.
zurückgeben
DKIM
apt install opendkim opendkim-tools -y
Dann einen DKIM-Key für meinmailserver.com generieren:
mkdir -p /etc/opendkim/keys/meinmailserver.com opendkim-genkey -t -s myhost -d meinmailserver.com -D /etc/opendkim/keys/meinmailserver.com/
Den public Key anzeigen:
cat /etc/opendkim/keys/meinmailserver.com/myhost.txt
Inhalt in den DNS Eintrag von meinmailserver.com hinzufügen.
Konfigurieren:
Werden mehrere Domains über den Mailserver verwaltet, mit KeyTable/SigningTable arbeiten.
vi /etc/opendkim.conf
Diese Zeilen hinzufügen/ändern:
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable Socket local:/var/spool/postfix/opendkim/opendkim.sock InternalHosts 127.0.0.0/8, <meineServerIP>
Dann KeyTable erstellen:
vi /etc/opendkim/KeyTable
Inhalt:
myhost._domainkey.domain1.com meinmailserver.com:myhost:/etc/opendkim/keys/meinmailserver.com/myhost.private myhost._domainkey.domain2.com meinmailserver.com:myhost:/etc/opendkim/keys/meinmailserver.com/myhost.private myhost._domainkey.domain3.com meinmailserver.com:myhost:/etc/opendkim/keys/meinmailserver.com/myhost.private myhost._domainkey.domain4.com meinmailserver.com:myhost:/etc/opendkim/keys/meinmailserver.com/myhost.private
Dann SigningTable:
vi /etc/opendkim/SigningTable
Inhalt:
*@domain1.com myhost._domainkey.domain1.com *@domain2.com myhost._domainkey.domain2.com *@domain3.com myhost._domainkey.domain3.com *@domain4.com myhost._domainkey.domain4.com
Socket-Verzeichnis erstellen:
mkdir -p /var/spool/postfix/opendkim chown opendkim:postfix /var/spool/postfix/opendkim chmod 750 /var/spool/postfix/opendkim usermod -aG opendkim postfix
Postfix mit opendkim verbinden:
vi /etc/postfix/main.cf
Hinzufügen:
# Milter
milter_protocol = 6 milter_default_action = accept smtpd_milters = local:/opendkim/opendkim.sock non_smtpd_milters = local:/opendkim/opendkim.sock
Dann:
systemctl restart opendkim postfix reload systemctl status opendkim
Testen:
# swaks --to user@email.com --from user@mymailserver.com --server myhostname:587 --auth LOGIN --auth-user user@email.com --auth-password PASSWORD --tls
-> müsste nun wieder Testmail generieren/anzeigen.
Dann die zugestellte Mail prüfen ob DKIM-Header vorhanden ist:
ls -t /mail/dovecot/mailuser/Maildir/new/ | head -1 | xargs -I{} cat /mail/dovecot/mailuser/Maildir/new/{} | grep -i dkimDies müsste nun die DKIM Signatur zurückgeben:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mymailserver.com;
Jetzt einen echten Test mit mail-tester.com machen — das gibt einen Score 1-10 und zeigt ob SPF, DKIM und DMARC korrekt konfiguriert sind:
- Geh auf mail-tester.com
- Kopiere die angezeigte Test-Adresse
- Schicke eine Mail dorthin:
swaks --to TESTADRESSE@srv1.mail-tester.com --from my@mymailserver.com --server myhost:587 --auth LOGIN --auth-user my@email.com --auth-password PASSWORT --tls --header "Subject: Test Mail"
Und Ergebnis prüfen.
DMARC
DMARC-Eintrag in der DNS Zonendatei hinzufügen:
Hinzufügen:
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:hostmaster@mymailserver.com; ruf=mailto:hostmaster@mymailserver.com; pct=100"
Danach nochmals mail-tester.com testen.
Server 2: Web, MySQL + DNS
Partitionierung
| Partition | Label | Grösse |
|---|---|---|
| /dev/sda1 | cloudimg-rootfs | 20 GB |
| /dev/sda2 | tmp | 5 GB |
| /dev/sda3 | var | 20 GB |
| /dev/sda4 | opt | 5 GB |
| /dev/sda5 | www | 60 GB |
| /dev/sda6 | gitea | 10 GB |
| /dev/sda7 | database | 50 GB |
| /dev/sda8 | logs-www | 10 GB |
| /dev/sda9 | backup | 219 GB |
Software
Mysql
apt install mysql-server -y
prüfen
systemctl status mysql
MySQL absichern:
mysql_secure_installation
Bei den Fragen:
- Validate Password Plugin → n
- Remove anonymous users → y
- Disallow root login remotely → y
- Remove test database → y
- Reload privilege tables → y
Datenverzeichnis verschieben:
systemctl stop mysql mv /var/lib/mysql /database/mysql
MySQL Config anpassen:
vi /etc/mysql/mysql.conf.d/mysqld.cnf
Zeile ändern:
datadir = /database/mysql
AppArmor anpassen (Ubuntu-spezifisch, sonst startet MySQL nicht):
vi /etc/apparmor.d/usr.sbin.mysqld
Diese Zeilen hinzufügen:
/database/mysql/ r, /database/mysql/** rwk,
Dann:
systemctl restart apparmor systemctl start mysql systemctl status mysql
Kurz testen:
# mysql -e "SELECT @@datadir;"
+------------------+
| @@datadir |
+------------------+
| /database/mysql/ |
+------------------+
Bind (NS2)
apt install bind9 bind9utils bind9-doc -y
systemctl status bind9
Firewall Port ergänzen:
ufw allow 53/tcp
ufw allow 53/udp
Verzeichnisse erstellen
mkdir -p /etc/bind/working
mkdir -p /etc/bind/working/secondary
mkdir -p /etc/bind/config
mkdir -p /var/log/bind
chown bind:bind /var/log/bind
chown bind:bind /etc/bind/working
chmod 775 /etc/bind/working
chown bind:bind /etc/bind/config
chmod 775 /etc/bind/config
Dann vom (alten) NS1 Server die Config rüberkopieren:
# Auf dem alten NS1 Server:
scp -r /etc/bind/config root@<NEW_SERVER_IP>:/etc/bind/ scp /etc/bind/named.root root@<NEW_SERVER_IP>:/etc/bind/ scp -r /etc/bind/master root@<NEW_SERVER_IP>:/etc/bind/
Dann auf dem neuen NS2 den Symlink erstellen zum auto-gernerated named.conf
cd /etc/bind
mv named.conf named.conf.dist
ln -s config/secondary.file named.confFür die Transfers noch das Login für bind einrichten (auf beiden Servern NS1+NS2):
# Home-Verzeichnis erstellen und Shell setzen:
systemctl stop named
usermod -d /var/lib/bind -s /bin/bash bind
systemctl start named
mkdir -p /var/lib/bind/.ssh
chown bind:bind /var/lib/bind/.ssh
chown -R bind:bind /var/lib/bind
chmod 700 /var/lib/bind/.ssh
chown -R bind:bind /etc/bind/working
chmod 775 /etc/bind/working
Dann den SSH Public Key vom NS1 holen — falls noch kein Key vorhanden ist, wird dieser erst generiert:
# Falls noch kein Key vorhanden:
sudo -u bind ssh-keygen -t ed25519 -f /var/lib/bind/.ssh/id_ed25519 -N ""
cat /var/lib/bind/.ssh/id_ed25519.pub
-> jetzt Inhalt auf NS2 in die authorized_keys kopieren.
vi /var/lib/bind/.ssh/authorized_keys # -> inhalt einfügen
chown bind:bind /var/lib/bind/.ssh/authorized_keys
chmod 600 /var/lib/bind/.ssh/authorized_keys
Und jetzt von NS1 aus die Verbindung testen:
sudo -u bind ssh bind@ns2_server_ip
Apparmor Settings anpassen:
vi /etc/apparmor.d/usr.sbin.named
Diese Zeile suchen:
/etc/bind/** r,
Und ergänzen:
/etc/bind/** r, /etc/bind/config/ rw, /etc/bind/config/** rw,
/etc/bind/working/ rw,
/etc/bind/working/** rw,
/etc/bind/secondary/ rw,
/etc/bind/secondary/** rw,
Zeile suchen:
/var/log/named/** rw,
Und ergänzen:
/var/log/bind/ rw, /var/log/bind/** rw,
Dann:
apparmor_parser -r /etc/apparmor.d/usr.sbin.named
Dann noch das Root-Hints File updaten:
sudo curl -o /etc/bind/db.root https://www.internic.net/domain/named.root
NGINX + PHP
apt install nginx -y
apt install php-fpm php-mysql php-gd php-curl php-mbstring php-xml php-zip php-intl -y
Verzeichnis für globale NGINX Configs erstellen
mkdir -p /etc/nginx/global
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.dist
vi /etc/nginx/nginx.conf
Und die gewünschten Anpassungen machen.
Firewall freigeben:
ufw allow 80/tcp
ufw allow 443/tcp
Let’s Encrypt
apt install certbot python3-certbot-nginx -y
FTP Server
apt install proftpd-mod-mysql proftpd-mod-cryptoRelevante Settings in /etc/proftpd/proftpd.conf :
RequireValidShell off
Include /etc/proftpd/sql.conf
Relevante Module in /etc/proftpd/modules.conf:
LoadModule mod_sql.c LoadModule mod_sql_mysql.c LoadModule mod_sql_passwd.c
#
SQL-Konfiguration (z.B. in /etc/proftpd/sql.conf):
<IfModule mod_sql.c> SQLBackend mysql SQLConnectInfo datenbankname@localhost dbuser dbpassword SQLAuthTypes Crypt SQLUserInfo ftp_login username password uid gid homedir shell #SQLGroupInfo ftp_groups groupname gid members
SQLAuthenticate users
SQLMinUserUID 79 SQLMinUserGID 79
# Additional SQL Statements
SQLNamedQuery getcount SELECT "count, username from ftp_login where username='%u'"
SQLNamedQuery updatedbinfo UPDATE "count=count+1,last_login=NOW(),last_ip='%a',last_host='%h' WHERE username='%u'" ftp_login
SQLShowInfo PASS "230" "Hello %u, you were already %{getcount} times logged in"
SQLLog PASS updatedbinfo
# limit only to active logins
SQLUserWhereClause "active = '1'" # Zusaetzlich zur MySQL Query hinzufuegen
</IfModule>
SQL Login Table:
CREATE TABLE IF NOT EXISTS `ftp_login` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(60) NOT NULL DEFAULT '', -- war 30, Usernames können länger sein
`password` varchar(128) NOT NULL DEFAULT '', -- für alle Hash-Typen ausreichend
`uid` int(11) NOT NULL DEFAULT '80',
`gid` int(11) NOT NULL DEFAULT '80',
`homedir` varchar(255) NOT NULL DEFAULT '', -- war 60, Pfade können länger sein
`count` int(11) NOT NULL DEFAULT '0',
`last_host` varchar(255) NOT NULL DEFAULT '', -- war 50, für lange Hostnamen/FQDNs
`last_ip` varchar(45) NOT NULL DEFAULT '', -- war 20, IPv6 braucht bis zu 39 Zeichen
`last_login` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`active` tinyint(1) NOT NULL DEFAULT '1', -- tinyint(4)→(1), semantisch ein Boolean
`shell` varchar(255) NOT NULL DEFAULT '/sbin/nologin',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`), -- war normaler KEY, sollte UNIQUE sein
KEY `homedir` (`homedir`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 -- MyISAM→InnoDB, latin1→utf8mb4
COMMENT='FTP Login Informations' AUTO_INCREMENT=1;
Crypt (SHA-512) Passwort generieren (auf dem Server) für FTP Login
mkpasswd -m sha-512 deinPasswortBeispielausgabe (dein Hash wird anders aussehen wegen zufälligem Salt):
$6$rounds=656000$iR3uAqNFZpMkFb3x$abc123...
Beispiel-Insert
INSERT INTO `ftp_login`(`username`, `password`, `uid`, `gid`, `homedir`, `shell`, `active`)VALUES('test', '$6$rounds=656000$iR3uAqNFZpMkFb3x$abc123...', 80, 80, '/home/ftp/test', '/sbin/nologin', 1);Ports definieren (passives FTP + Explicit FTPS (STARTTLS))
21/tcp– Steuerkanal- Datenkanal-Portrange nach Wahl, z.B.
60000-65000/tcp - 990/tcp – Implicit FTPS
vi /etc/proftpd/proftpd.conf
PassivePorts 60000 65000
UFW / Firewall Ports öffnen:
ufw allow 21/tcp ufw allow 60000:65000/tcp
Für das FTPS (Let’s Encrypt oder self-signed)
# Self-signed für den Anfang:
openssl req -x509 -newkey rsa:2048 -keyout /etc/ssl/private/proftpd.key -out /etc/ssl/certs/proftpd.crt -days 3650 -nodes
Dann fragt dich OpenSSL interaktiv nach Land, Organisation usw. – bei self-signed kannst du alles mit Enter überspringen, nur Common Name sollte die IP oder der Hostname des Servers sein.
ProFTPD-Config (/etc/proftpd/tls.conf):
<IfModule mod_tls.c> TLSEngine on TLSLog /var/log/proftpd/tls.log TLSProtocol TLSv1.2 TLSv1.3 TLSCipherSuite HIGH:!aNULL:!MD5 TLSRSACertificateFile /etc/ssl/certs/proftpd.crt TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key TLSVerifyClient off TLSRequired on # ← erzwingt TLS, reines FTP wird abgelehnt </IfModule>
In proftpd.conf einbinden:
Include /etc/proftpd/tls.conf
Modul aktivieren in modules.conf:
LoadModule mod_tls.c
ProFTP neu laden:
service proftpd restart
Status prüfen:
service proftpd status
.


