Si vous utilisez Postfix vous avez peut-être mis en place un deuxième serveur de mails Postfix qui joue le rôle de MX Secondaire.
Pour éviter que ce serveur MX secondaire soit un maillon faible dans votre lutte contre le SPAM, une des pratiques est de mettre en place les mêmes filtres que sur le serveur initial (greylisting, filtre anti-spam) et de mettre en place des tables d’adresses relayées.
Si vous utilisez Mailman sur le serveur de mail maître, vous aimeriez sans doute que les adresses utilisées par Mailman soient également relayées par votre serveur MX Secondaire.
J’ai écrit pour cela un outil qui est destiné à alimenter un serveur MX secondaire avec les adresses utilisées par l’outil Mailman en configuration avec domaines virtuels (cf. GNU Mailman Virtual domains).
Il va exporter la totalité de cette table, ou la filtrer en n’exportant que les entrées correspondant à un domaine particulier puis les déposer avec la commande scp sur le serveur MX secondaire et enfin lancer en SSH la commande postmap sur ce serveur secondaire.
Bug report de mailmanSecondaryMX.sh
Si vous voulez me faire remonter un bug : ouvrir un bug.
Pré-requis avant installation et utilisation (configuration Postfix et SSH)
1. Sur le serveur primaire sur lequel est exécuté l’outil mailmanSecondaryMX.sh
- doit utiliser Mailman en configuration avec domaines vrituels (cf.GNU Mailman Virtual domains)
- doit disposer d’un fichier
virtual_alias_mapsourelay_recipient_mapspour Mailman correctement déclaré dans la configuration de Postfix (cf./etc/postfix/main.cf), par exemplerelay_recipient_maps = hash:/var/lib/mailman/data/virtual-mailman - la table
virtual_alias_mapsourelay_recipient_mapspour Mailman doit être alimentée (fichier non vide) - le serveur MX primaire doit pouvoir accéder au serveur MX secondaire en SSH avec authentification par clé (voir ici comment configurer les clés SSH)
2. Sur le serveur MX secondaire :
- le ou les domaines relayés doivent être déclarés dans la configuration de Postfix dans la table transport (par défaut la table transport
/etc/postfix/transport), par exemple :domaine_relaye.com smtp:url_serveur_mx_primaire.domaine_relaye.com - le ou les domaines relayés doivent être déclarés dans la configuration de Postfix dans les
relay_domains(par exemple :relay_domains = domaine_relaye.com) - table spécifique contenant les adresses relayées doit être déclarée en tant que
relay_recipient_maps(par défaut, l’outil utilise la table/etc/postfix/mailmanSecondaryMXmais vous pouvez utiliser un autre fichier si besoin – voir paramètres du script). Par exemple :relay_recipient_maps = hash:/etc/postfix/mailmanSecondaryMX
Installation
Pour installer cet outil, téléchargez le script dans le dossier où vous voulez l’installer :
wget --no-check-certificate https://raw.github.com/yvangodard/mailmanSecondaryMX/master/mailmanSecondaryMX.sh sudo chmod 755 mailmanSecondaryMX.sh
Pour une aide complète, installez le script et lancez le :
./mailmanSecondaryMX.sh help
Paramètres d’utilisation mailmanSecondaryMX.sh
mailmanSecondaryMX.sh doit être exécuté sur le serveur primaire où sont hébergées les listes de discussion de Mailman avec les paramètres suivantes :
./mailmanSecondaryMX.sh [-h] | -s <secondary server address>
[-r <relay recipient map>] [-u <remote user>]
[-R <remote postfix map>] [-c <remote postmap command>]
[-d <domains to extract>]
[-e <email report option>] [-E <email address>] [-j <log file>]
Paramètre obligatoire :
-s <secondary server address>: adresse du serveur MX secondaire de backup (IP ou URL, par exemple :-s mysecondarymx.server.com)
Paramètres facultatifs :
-r <relay recipient map>: chemin complet de la table des adresses de Mailman sur le serveur primaire (par défaut, le chemin/var/lib/mailman/data/virtual-mailmanest utilisé)-u <remote user>: utilisateur autorisé à se connecter en SHH par clé au serveur secondaire (par défaut, le compterootest utilisé)-R <remote postfix map>: chemin complet, sur le serveur secondaire, de la table des adresses de Mailman du serveur primaire à relayer sur le serveur secondaire (par défaut, l’outil utilise/etc/postfix/mailmanSecondaryMX)-c <remote postmap command>: chemin complet, sur le serveur secondaire, de la commandepostmapde Postfix (par défaut, l’outil utilise/usr/sbin/postmap): domaines à traiter. Par défaut :-d <domains to extract>-d alldomains. Pour limiter l’export à certains domaines, il faut les entrer en les séparant par le signe%. Par exemple@mydomain.comoumy.domain.com%@second.domain.net. Pour tous les domaines, utiliser-d alldomains-e <option de rapport par email>: paramètre d’envoi de rapport d’exécution par email, soit ‘onerror‘, ‘forcemail‘ ou ‘nomail‘ (par défaut : ‘nomail‘)-E <adresse email pour les rapports>: adresse email à laquelle sera envoyé le rapport. Cette option doit être utilisée si les options ‘-e forcemail‘ ou ‘-e onerror‘ sont utilisées-j <fichier journal>: cette option active la journalisation à la place de la sortie standard. Spécifiez en argument le chemin du fichier de log (par exemple : ‘/var/log/mailmanSecondaryMX.log‘) ou utilisez ‘default‘ (/var/log/mailmanSecondaryMX.log)
A la première utilisation, pensez à faire manuellement un postmap /etc/postfix/mailmanSecondaryMX sur le serveur secondaire.
Le code source du script mailmanSecondaryMX.sh pour les plus vicieux
Voici donc le script :
#! /bin/bash
#------------------------------------------#
# mailmanSecondaryMX #
#------------------------------------------#
# #
# Extract Postfix's virtual_alias_maps to #
# a secondary MX Server #
# #
# Yvan Godard #
# godardyvan@gmail.com #
# #
# Version 0.3 -- august, 29 2014 #
# Under Licence #
# Creative Commons 4.0 BY NC SA #
# #
# http://goo.gl/znEUU4 #
# #
#------------------------------------------#
# Variables initialisation
VERSION="mailmanSecondaryMX v0.3 - 2014, Yvan Godard [godardyvan@gmail.com]"
help="no"
SCRIPT_DIR=$(dirname $0)
SCRIPT_NAME=$(basename $0)
RELAY_RECIPIENT_MAP=/var/lib/mailman/data/virtual-mailman
EXTRACTED_MAP=$(mktemp /tmp/mailmanSecondaryMX_map.XXXXX)
EXTRACTED_MAP_TEMP=$(mktemp /tmp/mailmanSecondaryMX_map_temp.XXXXX)
REMOTE_SERVER_ADDRESS=""
REMOTE_SERVER_USER="root"
REMOTE_SERVER_POSTMAP_CMD="/usr/sbin/postmap"
REMOTE_RELAY_RECIPIENT_MAP="/etc/postfix/mailmanSecondaryMX"
DOMAINS="alldomains"
DOMAINS_LIMIT=0
LIST_DOMAINS=$(mktemp /tmp/mailmanSecondaryMX_domains.XXXXX)
EMAIL_REPORT="nomail"
EMAIL_LEVEL=0
LOG="/var/log/mailmanSecondaryMX.log"
LOG_TEMP=$(mktemp /tmp/mailmanSecondaryMX_tmpLog.XXXXX)
LOG_ACTIVE=0
EMAIL_ADDRESS=""
SSH_TEST=0
help () {
echo -e "$VERSION\n"
echo -e "This tool is designed to export a Postfix's virtual_alias_maps to a secondary MX Server."
echo -e "SSH access to this second server should be possible without password (key authentication)."
echo -e "This tool is licensed under the Creative Commons 4.0 BY NC SA licence."
echo -e "\nDisclamer:"
echo -e "This tool is provide without any support and guarantee."
echo -e "\nSynopsis:"
echo -e "./${SCRIPT_NAME} [-h] | -s <secondary server address>"
echo -e " [-r <relay recipient map>] [-u <remote user>]"
echo -e " [-R <remote postfix map>] [-c <remote postmap command>]"
echo -e " [-d <domains to extract>]"
echo -e " [-e <email report option>] [-E <email address>] [-j <log file>]"
echo -e "\n\t-h: prints this help then exit"
echo -e "\nMandatory option:"
echo -e "\t-s <secondary server address>: the address of the secondary MX Server (IP or URL, e.g.: 'mysecondarymx.server.com')"
echo -e "\nOptional options:"
echo -e "\t-r <relay recipient map>: the full path of relay recipient map, as defined here http://goo.gl/39uDsc,"
echo -e "\t for the Mailman server (default: '${RELAY_RECIPIENT_MAP}')"
echo -e "\t-u <remote user>: the remote user who can connect through SSH to the secondary MX server,"
echo -e "\t and can lauch Postmap command (e.g.: 'myuser', default: '${REMOTE_SERVER_USER}'."
echo -e "\t-R <remote postfix map>: the full name of the remote Postfix map filename (e.g.: '/etc/postfix/map1',"
echo -e "\t default '${REMOTE_RELAY_RECIPIENT_MAP}''). This map file must be defined"
echo -e "\t in '/etc/postfix/main.cf' as a 'relay_recipient_maps ='"
echo -e "\t-c <remote postmap command>: the full path of sencondary MX server 'postmap' command (default: '${REMOTE_SERVER_POSTMAP_CMD}')"
echo -e "\t-d <domains to extract>: domains that must be processed with or without '@', separated by '%'"
echo -e "\t (e.g.: '@mydomain.com' or 'my.domain.com%@second.domain.net') or use 'alldomains'."
echo -e "\t For all domains, please enter '-d alldomains'. By default: '${DOMAINS}'"
echo -e "\t-e <email report option>: settings for sending a report by email, must be 'onerror', 'forcemail' or 'nomail',"
echo -e "\t default: '${EMAIL_REPORT}'."
echo -e "\t-E <email address>: email address to send the report, must be filled if '-e forcemail' or '-e onerror' options is used"
echo -e "\t-j <log file>: enables logging instead of standard output. Specify an argument for the full path to the log file"
echo -e "\t (e.g.: '$LOG') or use 'default' ($LOG)"
exit 0
}
error () {
echo -e "\n*** Error ***"
echo -e ${1}
echo -e "\n"${VERSION}
alldone 1
}
alldone () {
# Sleep to avoid multiple instances
[ $1 -ne 2 ] && sleep 30
# Redirect standard outpout
exec 1>&6 6>&-
# Logging if needed
[ $LOG_ACTIVE -eq 1 ] && cat $LOG_TEMP >> $LOG
# Print current log to standard outpout
[ $LOG_ACTIVE -ne 1 ] && cat $LOG_TEMP
[ $EMAIL_LEVEL -ne 0 ] && [ $1 -ne 0 ] && cat $LOG_TEMP | mail -s "[ERROR] ${SCRIPT_NAME} on ${HOSTNAME}" ${EMAIL_ADDRESS}
[ $EMAIL_LEVEL -eq 2 ] && [ $1 -eq 0 ] && cat $LOG_TEMP | mail -s "[OK] ${SCRIPT_NAME} on ${HOSTNAME}" ${EMAIL_ADDRESS}
# Remove temp files/folder
[ -f ${EXTRACTED_MAP} ] && rm -R ${EXTRACTED_MAP}
[ -f ${EXTRACTED_MAP_TEMP} ] && rm -R ${EXTRACTED_MAP_TEMP}
[ -f ${LIST_DOMAINS} ] && rm -R ${LIST_DOMAINS}
[ -f ${LOG_TEMP} ] && rm -R ${LOG_TEMP}
# Remove lock file
[ $1 -eq 0 ] && [ -e ${LOCKFILE} ] && rm ${LOCKSYNC}
exit ${1}
}
while getopts "hs:u:c:d:r:R:e:E:j:" OPTION
do
case "$OPTION" in
h) help="yes"
;;
s) REMOTE_SERVER_ADDRESS=${OPTARG}
;;
r) RELAY_RECIPIENT_MAP=${OPTARG}
;;
u) REMOTE_SERVER_USER=${OPTARG}
;;
R) REMOTE_RELAY_RECIPIENT_MAP=${OPTARG}
;;
c) REMOTE_SERVER_POSTMAP_CMD=${OPTARG}
;;
d) DOMAINS=${OPTARG}
if [[ ${DOMAINS} != "alldomains" ]]
then
echo ${DOMAINS} | perl -p -e 's/%/\n/g' | perl -p -e 's/ //g' | awk '!x[$0]++' >> ${LIST_DOMAINS}
DOMAINS_LIMIT=1
elif [[ ${DOMAINS} = "alldomains" ]]
then
DOMAINS_LIMIT=0
fi
;;
e) EMAIL_REPORT=${OPTARG}
;;
E) EMAIL_ADDRESS=${OPTARG}
;;
j) [ $OPTARG != "default" ] && LOG=${OPTARG}
LOG_ACTIVE=1
;;
esac
done
# Verifiy mandatory option
if [[ ${REMOTE_SERVER_ADDRESS} = "" ]]
then
help
alldone 1
fi
# Redirect standard outpout to temp file
exec 6>&1
exec >> ${LOG_TEMP}
# Start temp log file
echo -e "\n****************************** `date` ******************************\n"
echo -e "$0 started with options:"
echo -e "\t-s ${REMOTE_SERVER_ADDRESS} (secondary server address)"
echo -e "\t-r ${RELAY_RECIPIENT_MAP} (local relay recipient map)"
echo -e "\t-u ${REMOTE_SERVER_USER} (remote user)"
echo -e "\t-R ${REMOTE_RELAY_RECIPIENT_MAP} (remote postfix map)"
echo -e "\t-c ${REMOTE_SERVER_POSTMAP_CMD} (remote postmap command)"
if [[ ${DOMAINS_LIMIT} = "1" ]]; then
echo -e "\t-d (domains to extract):"
for LINE in $(cat ${LIST_DOMAINS})
do
echo -e "\t > ${LINE}"
done
elif [[ ${DOMAINS_LIMIT} = "0" ]]; then
echo -e "\t-d alldomains"
fi
echo -e "\t-e ${EMAIL_REPORT} (email report option)"
if [[ ${EMAIL_REPORT} != "nomail" ]]
then
echo -e "\t-E ${EMAIL_ADDRESS} (email report address)"
fi
if [[ ${LOG_ACTIVE} != "0" ]]
then
echo -e "\t-j ${LOG} (log file)"
fi
echo ""
# Lock system
export LOCKSYNC="/var/run/${SCRIPT_NAME}.lock"
PID=$$
# If lock file exists
if [ -f "${LOCKSYNC}" ]; then
echo "Previous lock file found..."
PIDLOCK=`cat ${LOCKSYNC}`
# Test if proccess is already running
if [ -f /proc/${PIDLOCK}/exe ]; then
# If yes ... kill the script
echo -e "> Process still active. Please try again later.\nEnd."
alldone 2
else
# If not, cleaning lock file
echo "> Process is complete: cleaning lock file."
rm ${LOCKSYNC}
fi
fi
# Create lock file
echo "Create lock file "${LOCKSYNC}
echo ${PID} > ${LOCKSYNC}
# Test of sending email parameter and check the consistency of the parameter email address
if [[ ${EMAIL_REPORT} = "forcemail" ]]
then
EMAIL_LEVEL=2
if [[ -z $EMAIL_ADDRESS ]]
then
echo -e "You used option '-e ${EMAIL_REPORT}' but you have not entered any email info.\n\t> We continue the process without sending email."
EMAIL_LEVEL=0
else
echo "${EMAIL_ADDRESS}" | grep '^[a-zA-Z0-9._-]*@[a-zA-Z0-9._-]*\.[a-zA-Z0-9._-]*$' > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo -e "This address '${EMAIL_ADDRESS}' does not seem valid.\n\t> We continue the process without sending email."
EMAIL_LEVEL=0
fi
fi
elif [[ ${EMAIL_REPORT} = "onerror" ]]
then
EMAIL_LEVEL=1
if [[ -z $EMAIL_ADDRESS ]]
then
echo -e "You used option '-e ${EMAIL_REPORT}' but you have not entered any email info.\n\t> We continue the process without sending email."
EMAIL_LEVEL=0
else
echo "${EMAIL_ADDRESS}" | grep '^[a-zA-Z0-9._-]*@[a-zA-Z0-9._-]*\.[a-zA-Z0-9._-]*$' > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo -e "This address '${EMAIL_ADDRESS}' does not seem valid.\n\t> We continue the process without sending email."
EMAIL_LEVEL=0
fi
fi
elif [[ ${EMAIL_REPORT} != "nomail" ]]
then
echo -e "\nOption '-e ${EMAIL_REPORT}' is not valid (must be: 'onerror', 'forcemail' or 'nomail').\n\t> We continue the process without sending email."
EMAIL_LEVEL=0
elif [[ ${EMAIL_REPORT} = "nomail" ]]
then
EMAIL_LEVEL=0
fi
# Test SSH connection
ssh -q -o "BatchMode=yes" ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS} "echo 2>&1" && SSH_TEST=1 || echo -e "Unable to access ssh://${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}."
if [[ ${SSH_TEST} = "0" ]]; then
error "Your SSH connection to ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS} doesn't work.\nPlease verify parameters.\nPerhaps you need to set up SSH Keys as described here: http://goo.gl/475wy4."
fi
# Test if remote POSTMAP command exists
TEST_POSTMAP_CMD=$(ssh ${REMOTE_SERVER_ADDRESS} -l ${REMOTE_SERVER_USER} test -f ${REMOTE_SERVER_POSTMAP_CMD} && echo OK)
if [[ ! ${TEST_POSTMAP_CMD} = "OK" ]]; then
error "Postmap command ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}:${REMOTE_SERVER_POSTMAP_CMD} doesn't exist.\nPlease verify parameters."
fi
# Test RELAY_RECIPIENT_MAP file
if [[ ! -f ${RELAY_RECIPIENT_MAP} ]]; then
error "Relay Recipient Map file '${RELAY_RECIPIENT_MAP}' to process doesn't exist.\nPlease verify parameters."
fi
# Test DOMAIN content
[[ ${DOMAINS_LIMIT} = "1" ]] && [[ -z $(cat ${LIST_DOMAINS}) ]] && echo "Option -d used without any domain. Process continues with '-d alldomains'." && DOMAINS="alldomains" && DOMAINS_LIMIT=0
# Step 1 : extract remote map
OLDIFS=$IFS; IFS=$'\n'
if [[ ${DOMAINS_LIMIT} = "1" ]]; then
for RELAY_RECIPIENT in $(cat ${RELAY_RECIPIENT_MAP})
do
for DOMAIN in $(cat ${LIST_DOMAINS})
do
echo "${RELAY_RECIPIENT}" | grep ${DOMAIN} > /dev/null 2>&1
[[ ${?} -eq 0 ]] && echo "${RELAY_RECIPIENT}" >> ${EXTRACTED_MAP_TEMP}
done
done
fi
[[ ${DOMAINS_LIMIT} = "1" ]] && [[ -z $(cat ${EXTRACTED_MAP_TEMP}) ]] && error "Nothing to extract. Please verify your parameters."
echo "# Relay recipient map file generated by ${0}" >> ${EXTRACTED_MAP}
echo "# on ${HOSTNAME}" >> ${EXTRACTED_MAP}
echo "# ${VERSION}" >> ${EXTRACTED_MAP}
echo "# `date`" >> ${EXTRACTED_MAP}
cat ${EXTRACTED_MAP_TEMP} | awk '!x[$0]++' >> ${EXTRACTED_MAP}
IFS=$OLDIFS
# Step 2 : send file to secondary MX Server
if [[ ${DOMAINS_LIMIT} = "0" ]]; then
[[ -f ${EXTRACTED_MAP} ]] && rm ${EXTRACTED_MAP}
echo -e "\n-> Sending original relay recipient map ${RELAY_RECIPIENT_MAP}...\n"
rsync -cave ssh ${RELAY_RECIPIENT_MAP} ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}:${REMOTE_RELAY_RECIPIENT_MAP}
if [ $? -ne 0 ]; then
ERROR_MESSAGE=$(echo $?)
error "Error while sending file:\nrsync -cave ssh ${RELAY_RECIPIENT_MAP} ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}:${REMOTE_RELAY_RECIPIENT_MAP}.\n${ERROR_MESSAGE}."
else
echo -e "-> Sending file to ${REMOTE_SERVER_ADDRESS}: OK"
fi
elif [[ ${DOMAINS_LIMIT} = "1" ]]; then
echo -e "\n-> Relay recipient map extracted to ${EXTRACTED_MAP}...\n"
rsync -cave ssh ${EXTRACTED_MAP} ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}:${REMOTE_RELAY_RECIPIENT_MAP}
if [ $? -ne 0 ]; then
ERROR_MESSAGE=$(echo $?)
error "Error while sending file:\nrsync -cave ssh ${EXTRACTED_MAP} ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS}:${REMOTE_RELAY_RECIPIENT_MAP}.\n${ERROR_MESSAGE}."
else
echo -e "-> Sending file to ${REMOTE_SERVER_ADDRESS}: OK\n"
fi
fi
# Step 3 : postmap
ERROR_SSH=$(ssh $REMOTE_SERVER_USER@$REMOTE_SERVER_ADDRESS "$REMOTE_SERVER_POSTMAP_CMD $REMOTE_RELAY_RECIPIENT_MAP && echo \$?")
if [[ ${ERROR_SSH} -ne 0 ]]; then
error "Error while running command:\nssh ${REMOTE_SERVER_USER}@${REMOTE_SERVER_ADDRESS} '${REMOTE_SERVER_POSTMAP_CMD} ${REMOTE_RELAY_RECIPIENT_MAP}'."
else
echo -e "-> Postmap on remote server (${REMOTE_SERVER_POSTMAP_CMD} ${REMOTE_RELAY_RECIPIENT_MAP}): OK"
fi
echo ""
echo "***************************** ${SCRIPT_NAME} finished ******************************"
alldone 0
Evidemment, avis, commentaires et compléments sont les bienvenus !