Guide Complet : Sécurisation et Optimisation d'un Serveur Mail Autohébergé avec Stalwart

Guide Complet : Sécurisation et Optimisation d'un Serveur Mail Autohébergé avec Stalwart
Photo by Austin Paskerian / Unsplash

Ce guide technique détaille la mise en place, la sécurisation granulaire et l'automatisation de la maintenance d'un serveur de messagerie moderne propulsé par Stalwart sur un système Debian.

L'approche adoptée s'appuie sur le principe de moindre privilège, le durcissement de l'accès distant, l'automatisation défensive et la gestion maîtrisée des cycles de vie applicatifs.

Introduction

Déployer un serveur de messagerie auto-hébergé (MTA/IMAP/JMAP) requiert un niveau de rigueur élevé en matière de sécurité système. Entre les tentatives de connexions automatisées sur les ports d'administration et la nécessité d'assurer des mises à jour applicatives sans interruption de service, chaque composant doit être verrouillé.

Ce tutoriel synthétise les bonnes pratiques indispensables pour transformer une installation Debian de base en un bastion prêt pour la production.

Étape 1 : Durcissement des accès SSH et cloisonnement des utilisateurs

Par défaut, l'accès direct à l'utilisateur root par mot de passe constitue la première vulnérabilité exploitée par les bots. La première action consiste à créer un compte administrateur dédié et à restreindre strictement les entrées.

Restriction de l'accès SSH au seul compte autorisé et interdiction de root : Créez un fichier de configuration dédié dans /etc/ssh/sshd_config.d/ :

sudo tee /etc/ssh/sshd_config.d/99-security.conf > /dev/null << 'EOF'
PermitRootLogin no
PasswordAuthentication yes
AllowUsers mail
EOF

Vérifiez la syntaxe et rechargez le démon SSH :

sudo sshd -t && sudo systemctl reload ssh

Création de l'administrateur technique et attribution des privilèges :

sudo apt update && sudo apt install -y sudo
sudo adduser mail
sudo usermod -aG sudo mail

3. Étape 2 : Déplacement du port SSH et isolation réseau

Modifier le port d'écoute par défaut (22) réduit drastiquement le bruit généré par les scanners de vulnérabilités automatisés.

Application et validation :

sudo sshd -t && sudo systemctl restart ssh

Note : Gardez toujours une session active ouverte lors de ces manipulations pour valider la connexion depuis un second terminal avant de fermer la première.

Mise à jour du pare-feu UFW :

sudo ufw allow 22000/tcp
sudo ufw reload

Modification du port d'écoute (exemple sur le port 22000) :

echo "Port 22000" | sudo tee /etc/ssh/sshd_config.d/port.conf

Étape 3 : Automatisation de la protection contre le force brute (Fail2ban)

Les versions récentes de Debian (et d'OpenSSH) s'appuient sur l'unité systemd sshd-session. Il est donc crucial d'adapter Fail2ban pour qu'il analyse correctement les journaux sans générer de faux positifs.

Redémarrage du service de protection :

sudo systemctl restart fail2ban

Configuration de la prison dans /etc/fail2ban/jail.local :

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 <VOTRE_IP_PERSONNELLE>
backend = systemd

[sshd]
enabled = true
port = 22000
maxretry = 3
bantime = 24h

Création d'un filtre personnalisé /etc/fail2ban/filter.d/sshd.local :

[Definition]
_daemon = sshd(?:-session)?

failregex = ^%(__prefix_line__)s(?:error: PAM: )?Authentication failure for(?: invalid user)? <F-USER>\S+</F-USER> from <HOST>(?: port \d+)?(?: ssh\d*)?[:.,]?\s*$
            ^%(__prefix_line__)s(?:Failed password for(?: invalid user)?|<F-USER>invalid user</F-USER> \S+) <HOST>(?: port \d+)?(?: ssh\d*)?[:.,]?\s*$
            ^%(__prefix_line__)s ROOT LOGIN REFUSED FROM <HOST>\s*$
            ^%(__prefix_line__)s Connection reset by authenticating user <F-USER>\S+</F-USER> <HOST> port \d+ \[preauth\]$

Étape 4 : Supervision des sessions et alternatives modernes à la commande last

Sur les distributions récentes, l'outil historique last est parfois absent en raison d'évolutions de formats binaires (Y2038). L'inspection des connexions s'effectue directement via journald :

Afficher l'historique des connexions réussies :

journalctl -u ssh | grep -E "Accepted|session opened"

Étape 5 : Industrialisation des mises à jour de Stalwart (Scripts d'Upgrade & Rollback)

Pour s'affranchir des manipulations manuelles lors des publications de correctifs de Stalwart, deux scripts d'administration sont installés dans /usr/local/sbin/.

A. Le script de mise à jour sécurisée (/usr/local/sbin/stalwart-upgrade)

Ce script télécharge la version cible, sauvegarde le binaire et la configuration, stoppe le service, applique la mise à jour et effectue un rollback automatique si le service ne parvient pas à démarrer.

sudo tee /usr/local/sbin/stalwart-upgrade > /dev/null << 'EOF'
#!/usr/bin/env bash
set -euo pipefail

BIN_PATH="/usr/local/bin/stalwart"
BACKUP_BIN="/usr/local/bin/stalwart.bak"
CONFIG_DIR="/etc/stalwart"
BACKUP_CONFIG="/etc/stalwart.bak.tar.gz"
SERVICE="stalwart"
ARCH="x86_64-unknown-linux-gnu"

if [[ $EUID -ne 0 ]]; then
   echo "❌ Erreur : Ce script doit être exécuté avec sudo ou en tant que root."
   exit 1
fi

CURRENT_VER=$("$BIN_PATH" --version | awk '{print $NF}' | tr -d 'v\r')
echo "ℹ️  Version actuelle en production : $CURRENT_VER"

TMP_DIR=$(mktemp -d /tmp/stalwart-upgrade.XXXXXX)
trap 'rm -rf "$TMP_DIR"' EXIT

if [[ $# -ge 1 ]]; then
    TARGET_TAG="$1"
    [[ "$TARGET_TAG" != v* ]] && TARGET_TAG="v${TARGET_TAG}"
    URL="https://github.com/stalwartlabs/stalwart/releases/download/${TARGET_TAG}/stalwart-${ARCH}.tar.gz"
    echo "⬇️  Téléchargement de la version demandée ($TARGET_TAG)..."
else
    URL="https://github.com/stalwartlabs/stalwart/releases/latest/download/stalwart-${ARCH}.tar.gz"
    echo "⬇️  Téléchargement de la dernière version disponible (latest)..."
fi

curl -fsSL "$URL" -o "$TMP_DIR/stalwart.tar.gz"
tar -xzf "$TMP_DIR/stalwart.tar.gz" -C "$TMP_DIR"
chmod +x "$TMP_DIR/stalwart"

NEW_VER=$("$TMP_DIR/stalwart" --version | awk '{print $NF}' | tr -d 'v\r')
echo "ℹ️  Version téléchargée : $NEW_VER"

if [[ "$CURRENT_VER" == "$NEW_VER" ]]; then
    echo "✅ Stalwart est déjà à jour en version $CURRENT_VER."
    exit 0
fi

echo "📦 Sauvegarde du binaire et de la configuration..."
cp -af "$BIN_PATH" "$BACKUP_BIN"
[[ -d "$CONFIG_DIR" ]] && tar -czf "$BACKUP_CONFIG" -C / etc/stalwart

echo "⏹️  Arrêt du service $SERVICE..."
systemctl stop "$SERVICE"

echo "🚀 Installation de la version $NEW_VER..."
install -m 0755 -o root -g root "$TMP_DIR/stalwart" "$BIN_PATH"

echo "▶️  Démarrage du service..."
systemctl start "$SERVICE"
sleep 3

if systemctl is-active --quiet "$SERVICE"; then
    echo "✅ Mise à jour réussie : $CURRENT_VER ➔ $NEW_VER !"
else
    echo "⚠️  Échec du démarrage. Rollback automatique vers $CURRENT_VER..."
    cp -af "$BACKUP_BIN" "$BIN_PATH"
    systemctl start "$SERVICE"
    exit 1
fi
EOF

B. Le script de retour arrière manuel (/usr/local/sbin/stalwart-rollback)

Permet de restaurer instantanément la version précédente en cas de dysfonctionnement applicatif :

sudo tee /usr/local/sbin/stalwart-rollback > /dev/null << 'EOF'
#!/usr/bin/env bash
set -euo pipefail

BIN_PATH="/usr/local/bin/stalwart"
BACKUP_BIN="/usr/local/bin/stalwart.bak"
BACKUP_CONFIG="/etc/stalwart.bak.tar.gz"
SERVICE="stalwart"

if [[ ! -f "$BACKUP_BIN" ]]; then
   echo "❌ Aucune sauvegarde binaire trouvée."
   exit 1
fi

echo "⏹️  Arrêt du service..."
systemctl stop "$SERVICE"

echo "🔄 Restauration de l'ancien binaire..."
cp -af "$BACKUP_BIN" "$BIN_PATH"

if [[ "${1:-}" == "--with-config" ]] && [[ -f "$BACKUP_CONFIG" ]]; then
    echo "🔄 Restauration de la configuration..."
    tar -xzf "$BACKUP_CONFIG" -C /
fi

echo "▶️  Redémarrage du service..."
systemctl start "$SERVICE"
echo "✅ Rollback exécuté avec succès !"
EOF

Rendez les scripts exécutables :

sudo chmod 0750 /usr/local/sbin/stalwart-upgrade /usr/local/sbin/stalwart-rollback

Conclusion

En combinant le verrouillage des accès distants (isolation d'utilisateurs, interdiction de root, port non standard), l'automatisation du filtrage des attaques par force brute via Fail2ban et l'industrialisation du cycle de vie de Stalwart à l'aide de scripts de bascule sécurisés, vous disposez d'une infrastructure de messagerie robuste, pérenne et hautement administrable. Cette architecture garantit à la fois une protection optimale contre les menaces extérieures et une sérénité opérationnelle lors des opérations de maintenance.