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:


startxfce4

4. GParted öffnen: Applications → Run Program → gparted


Partitionsschema

PartitionGrösseFilesystemMountpoint
/dev/sda1105 MBFAT32/boot/efi (behalten)
/dev/sda220 GBext4/
/dev/sda35 GBext4/tmp
/dev/sda415 GBext4/var
/dev/sda55 GBext4/opt
/dev/sda650 GBext4/opt/ecodms/data
/dev/sda750 GBext4/opt/ecodms_database
/dev/sda815 GBext4/mail
/dev/sda9140 GBext4/backup

Schritt 1 — /dev/sda1 verkleinern

  1. Rechtsklick auf /dev/sda1Resize/Move
  2. Neue Grösse eingeben: 20480 MB (= 20 GB)
  3. Resize klicken

Schritt 2 — Neuen freien Speicher aufteilen

Im nun freien Bereich nacheinander neue Partitionen erstellen. Rechtsklick auf „unallocated“ → New:

PartitionGrösse (MB)FilesystemLabel
neue sda5120ext4tmp
neue sda15360ext4var
neue sda5120ext4opt
neue sda51200ext4ecodms-data
neue sda51200ext4ecodms-db
neue sda15360ext4mail
Rest (~143 GB)alles übrigeext4backup

„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/data
mkdir -p /opt/ecodms_database
systemctl daemon-reload
mount -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/dpkg
mkdir -p /var/backups
mkdir -p /var/local
mkdir -p /var/mail
mkdir -p /var/snap
mkdir -p /var/lib/python3
rm /usr/share/python3/runtime.d/byobu.rtupdate

 

Dann dpkg reparieren:

dpkg --configure -a
apt 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 fail2ban
systemctl start fail2ban
ufw allow 22/tcp
ufw enable
ufw status
systemctl 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

verschieben:

mv /var/lib/postgresql /opt/ecodms_database/postgresql

Config anpassen:

vi /etc/postgresql/16/main/postgresql.conf

Zeile ändern:

data_directory = '/opt/ecodms_database/postgresql/16/main'

 

starten + prüfen:

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


Installieren:

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.ext

Alle 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…?

AktionMit --deleteOhne --delete
Mail auf ServerOld gelöschtwird auch auf ServerNew gelöschtbleibt auf ServerNew erhalten
Mail auf ServerOld neuwird auf ServerNew kopiertwird auf ServerNew kopiert
Mail auf ServerNew gelöschtwird von ServerOld wiederhergestelltbleibt 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.cf

Output 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_destination

Dann 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
  }
}
587 Port aktivieren:
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:

bash
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
Updaten:
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
Updaten:
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 dkim

Dies 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:

  1. Geh auf mail-tester.com
  2. Kopiere die angezeigte Test-Adresse
  3. 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

PartitionLabelGrösse
/dev/sda1cloudimg-rootfs20 GB 
/dev/sda2tmp5 GB 
/dev/sda3var20 GB 
/dev/sda4opt5 GB 
/dev/sda5www60 GB 
/dev/sda6gitea10 GB 
/dev/sda7database50 GB 
/dev/sda8logs-www10 GB 
/dev/sda9backup219 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.conf

Fü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-crypto

Relevante 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 deinPasswort

Beispielausgabe (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

.

nach oben