Exchange ne veut plus recevoir de mails

J’ai eu le cas d’un serveur Exchange 2010 qui s’est mis à ne plus recevoir de messages de l’extérieur.
Tout semblait fonctionner :

  • les services étaient tous au vert
  • aucune erreurs ou alertes dans les journaux d’événements
  • les clients pouvaient consulter leurs mails, leurs contacts et leurs taches
  • aucune mise à jour n’a été installé récemment
  • aucun changement de configuration faite récemment

Et pourtant impossible de recevoir et/ou envoyer.
J’ai pu trouver la solution, faute de mieux, en loguant une conversation SMTP avec le serveur Exchange afin de voir ce qu’il se passait réellement, et la bingo : le serveur a répondu 4.3.1 Insufficient System Resources.

Après une courte recherche, il s’est avéré qu’Exchange estimait ne plus avoir suffisamment d’espace disque pour fonctionner convenablement, c’est à dire moins de 4~5 Go de libre. C’est par design, une mesure de sécurité permettant d’avoir un système online en interdisant de dévorer le peu d’espace libre restant.

J’ai ensuite récupéré un peu d’espace disque là où je pouvais, notamment en supprimant des archives laissées par l’installation des cumulative updates Exchange (soit quelques 2Go quand même), ce qui m’a permis de constater le retour à la normal d’Exchange, me laissant ainsi le temps de planifier une petite maintenance pour étendre le disque dur devenu trop petit (facile dans un environnement virtuel ;)).

Voir aussi

Backup par mail

Le pipe (|), opérateur exclusivement Unix, et trop rarement utilisé (tout du moins par moi), m’a permis de réaliser en une seule ligne, une sauvegarde de base de données, compressée, et envoyé par mail.

Voyons la commande finale :

mysqldump --hex-blob --opt -pyourVerySecretPassword -u backup -B my_database | bzip2 -9 | uuencode bdd_mabase_prod_`date +%Y%m%d-%H%M%S`.sql.bz2 | mail -s "Backup BDD" storage_mail@gmail.com

Détaillons cette chaîne :

  1. on dump la ou les bases de données, pour se faire, je conseille fortement l’utilisation d’un utilisateur dédié qui n’aura qu’un accès limité à la lecture et éventuellement à l’utilisation des verrous pour assurer l’intégrité de la sauvegarde
  2. le flux de sortie, un script SQL, est envoyé à bzip pour être compressé
  3. le flux compressé est envoyé à uuencode. Cette opération va encoder avec des caractères ASCII afin d’être pouvoir transmis en pièce jointe et en lui donnant un nom
  4. enfin on expédie le mail avec un sujet et un destinataire

Et si on chiffrait la sauvegarde ?

Il y a plusieurs intérêt à chiffrer la sauvegarde :

  • la sauvegarde peut être placé dans un endroit non sécurisé ou faiblement
  • toute altération sur la sauvegarde sera impossible ou visible

Encore faut-il s’y prendre convenablement. En effet, on a immédiatement 2 façons naïves de chiffrer :

  • soit en utilisant un chiffrage symétrique
  • soit en utilisant un chiffrage asymétrique

Il se trouve que ces deux approches sont après coup débiles. En effet prenons d’abord le chiffrage symétrique ; que se passe-t-il si le serveur qu’on sauvegarde est compromis ? On peut estimer que le contenu des sauvegardes, y compris celles faites AVANT le piratage du serveur ne sont plus sûres, puisque le serveur compromis a non seulement un accès en écriture au site de sauvegarde, mais également la clé de déchiffrage des sauvegardes.

Examinons maintenant le chiffrage asymétrique, qui corrige bien le problème précédent mais présente deux problèmes :

  • le premier est un problème purement pratique : openssl, le couteau suisse du chiffrage pète en rsa lorsque le flux dépasse une certaine taille (très inférieure à 1Mo)
  • le second est d’ordre plus général : les chiffrages asymétriques coûtent très cher en temps processeur (autrement dit un chiffrage asymétrique prendre beaucoup plus de temps qu’un chiffrage symétrique sur le même flux), pour peu que la quantité de données soit « assez » conséquente, le chiffrage pourrait dégrader de façon significative et pendant un temps estimé trop long le serveur sur lequel les calculs s’effectuent.

La méthode que je qualifierais de bonne, (merci à Denis Altudov pour l’astuce), consiste à utiliser se qui se fait déjà dans les mails avec S/MIME : le contenu à protéger est chiffré avec un algorithme symétrique, dont la clé, générée à la demande et donc unique, est chiffrée par un algorithme asymétrique en utilisant la clé privée du destinataire ; ainsi seul le destinataire du mail est capable de lire le contenu protégé.

C’est exactement ce qu’on va faire, en utilisant openssl, et plus spécifiquement la sous-commande smime de openssl.
Il faudra au préalable un certificat (avec sa clé privée), au format PEM pour simplifier l’exemple. Reprenons l’exemple initial en ajoutant du chiffrage :

mysqldump --hex-blob --opt -pyourVerySecretPassword -u backup -B my_database | bzip2 -9 | openssl smime -encrypt -binary -aes128 -outform DER yourPrivateKey.crt | uuencode bdd_mabase_prod_`date +%Y%m%d-%H%M%S`.sql.bz2.ssl | mail -s "Backup BDD" storage_mail@gmail.com

# version openssl smime autonome (ou presque)
mysqldump --hex-blob --opt -pyourVerySecretPassword -u backup -B my_database | bzip2 -9 | openssl smime -encrypt -binary -aes128 -from sender@kveer.com -to storage_mail@gmail.com -subject "Bacup BDD" yourPrivateKey.crt | sendmail storage_mail@gmail.com

Note : openssl smime est capable de générer un mail lui-même comme on peut le voir dans la seconde commande, mais cela aurait donné un mail avec une pièce jointe nommée smime.p7m, ce qui n’est pas terrible pour identifier ce qui était chiffré, en particulier le type de fichier, c’est pourquoi j’ai préféré rester à utiliser uuencode.

Et voilà, plus besoin d’un script dédié pour des plans de sauvegarde simple, il n’y a plus qu’à cronifier.

Déchiffrage

Par rapport à l’exemple donné, voici la commande miroir pour déchiffrer les pièces jointes :

openssl smime -decrypt -binary -aes128 -inform DER yourPrivateKey.crt -in your_encrypted_file.sql.bz2.ssl -out your_decrypted_file.sql.bz2

Les arguments -aes128  et -binary  sont peut-être superflus, je n’ai pas testé précisément ces points là.

Plusieurs pièces jointes dans un mail

Toujours avec le même exemple, voici comment on peut joindre plusieurs pièces jointes, et même un corps de message à son mail :

(
echo -e $YOUR_MESSAGE;
mysqldump --hex-blob --opt -pyourVerySecretPassword -u backup -B my_database | bzip2 -9 | openssl smime -encrypt -binary -aes128 -outform DER yourPrivateKey.crt | uuencode bdd_mabase_prod_`date +%Y%m%d-%H%M%S`.sql.bz2.ssl;
mysqldump --hex-blob --opt -pyourVerySecretPassword -u backup -B another_database | bzip2 -9 | openssl smime -encrypt -binary -aes128 -outform DER yourPrivateKey.crt | uuencode bdd_another_base_prod_`date +%Y%m%d-%H%M%S`.sql.bz2.ssl
)
| mail -s "Backup BDD" storage_mail@gmail.com

Où sauvegarder

Où vous le souhaitez. Pour ma part, j’ai choisi Google, car il propose 8Go par boîte mail gratuitement et accepte des pièces jointes allant jusqu’à 25Mo et parce que je suis pauvre. Pour une sauvegarde à pas chère, les mails via Google constituent une excellente opportunité.

Les paquets à installer

  • uuencode se trouve dans le paquet app-arch/sharutils
  • mail se trouve dans le paquet mail-client/mailx

Exemple de script

A peu de chose près, voici ce que j’utilise comme script de sauvegarde. Ce script se trouve dans @@/etc/cron.daily@@, ce qui lui permet d’être automatiquement exécuté avec les bons privilèges tous les jours.

#!/bin/bash
MYSQL_PASS=verySecretPassword
MYSQL_USER=backup_user
DATE=`date +%Y%m%d-%H%M%S`
HOSTNAME=`hostname`
RECIPIENT=backup@gmail.com
CERT=/root/backup.gmail.com.crt
WWW_WEB1='/var/www/web1/htdocs/ /var/www/web1/reset_acl.sh'
WWW_WEB2=/var/www/web2/htdocs
WWW_WEB3='/var/www/web3/forum /var/www/web3/www'

MYSQL_CMD="mysqldump --hex-blob --opt -u ${MYSQL_USER} -p${MYSQL_PASS} -B"
OPENSSL_CMD="openssl smime -encrypt -binary -aes128 -outform DER $CERT"

CONF='/etc/bash/bashrc
/etc/ahbot.conf
/etc/bind
/etc/clam*.conf
/etc/conf.d
/etc/courier*
/etc/cron*
/etc/dhcp
/etc/env.d
/etc/fail2ban
/etc/freshclam.conf
/etc/group
/etc/passwd
/etc/shadow
/etc/hosts
/etc/init.d
/etc/ipsec*
/etc/kernels
/etc/layman
/etc/locale.gen
/etc/make.conf
/etc/munin
/etc/mysql
/etc/nginx
/etc/ntp.conf
/etc/opendkim
/etc/openvpn
/etc/pam.d
/etc/php
/etc/postfix
/etc/pureftpd.*
/etc/realmd.conf
/etc/revdep-rebuild
/etc/screenrc
/etc/scriptdev2.conf
/etc/security
/etc/selinux
/etc/sysctl.conf
/etc/syslog-ng
/etc/teamspeak3-server
/etc/unrealircd'

T=$( cat <<EOF
Type your texte here\n
with \n if you want your mail to be readable.
EOF
)

( echo -e $T;
uuencode $CERT my_cert.crt;
$MYSQL_CMD base1 | bzip2 -9 | $OPENSSL_CMD | uuencode ${HOSTNAME}_bdd_base1_${DATE}.sql.bz2.ssl;
$MYSQL_CMD base2 | bzip2 -9 | $OPENSSL_CMD | uuencode ${HOSTNAME}_bdd_base2_${DATE}.sql.bz2.ssl;
$MYSQL_CMD base3 | bzip2 -9 | $OPENSSL_CMD | uuencode ${HOSTNAME}_bdd_base3_${DATE}.sql.bz2.ssl;
tar cj ${WWW_WEB1} | $OPENSSL_CMD | uuencode ${HOSTNAME}_www_web1_${DATE}.tar.bz2.ssl;
tar cj ${WWW_WEB2} | $OPENSSL_CMD | uuencode ${HOSTNAME}_www_web2_${DATE}.tar.bz2.ssl;
tar cj ${WWW_WEB3} | $OPENSSL_CMD | uuencode ${HOSTNAME}_www_web3_${DATE}.tar.bz2.ssl;
tar cj ${CONF} | $OPENSSL_CMD | uuencode ${HOSTNAME}_etc_${DATE}.tar.bz2.ssl
) | mail -s "Backup $HOSTNAME bdd $DATE" $RECIPIENT

Voir aussi