Guide Complet : Sécurisation et Optimisation d'un Serveur Mail Autohébergé avec Stalwart
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.