Script API Ispconfig : changement d’IP dans toutes les zones DNS

Je m’occupe d’un service d’hébergement destiné aux actions politiques / citoyennes / artistiques… Ce service va rejoindre physiquement des voisins/copains eux aussi CHATONS : https://retzien.fr/. Nous allons mutualiser les serveurs et le temps de cerveau alloué au tâches administrative qui incombe à la gestion de ce type de service.

Pour préparer la migration de serveur j’ai fais un petit script qui se connecte à l’API d’ISPconfig et qui change tout les enregistrements DNS A ayant l’IP « X » par l’IP « Y » dans toutes les zones DNS du serveur. Ce script peut aussi s’avérer très utile en cas de changement de box internet Il se lance comme ceci :

php dnsChange.php -o=IP_ORIGINALE -n=NOUVELLE_IP

Le voici

<?php
/* 
 * Script de modification des enregistrements DNS A en cas de changement d'IP serveur
 * 
 * Exemple de lancement : 
 * 		php dnsChange.php -o=IP_ORIGINAL -n=NOUVELLE_IP
 * 
 * Sous licence Beerware
 * Par David Mercereau : http://david.mercereau.info
 */

// Configuration de la connexion ISPconfig
$CONFIG['remoteUser'] = 'UTILISATEUR DISTANT API ISPCONFIG';
$CONFIG['remotePassword'] = 'MOT DE PASSE DE L'UTILISATEUR DISTANT API ISPCONFIG';
$CONFIG['remoteSoapLocation'] = 'https://localhost:8080/remote/index.php';
$CONFIG['remoteSoapUri'] = 'https://localhost:8080/remote/';


// Test arguments
$options = getopt('o:n:');
if (count($options) < 2) {
	exit("Certains arguments sont manquants. -o ANCIENNEIP -n NOUVELLEIP .\n");
}
if (!filter_var($options['o'], FILTER_VALIDATE_IP)) {
        exit("L'origin n'est pas une IP valide.\n");
}
if (!filter_var($options['n'], FILTER_VALIDATE_IP)) {
        exit("La nouvelle IP n'est pas une IP valide.\n");
}

// Connexion à ISPconfig
$client = new SoapClient(null, array('location' => $CONFIG['remoteSoapLocation'],
	'uri'      => $CONFIG['remoteSoapUri'],
	'stream_context'=> stream_context_create(array('ssl'=> array('verify_peer'=>false,'verify_peer_name'=>false))),
	'trace' => 1));

// Login
if($session_id = $client->login($CONFIG['remoteUser'], $CONFIG['remotePassword'])) {
	echo "Login Ok. Session ID:".$session_id."\n\n";
}

echo "############################################################################################################################\n";
echo "Recherche de l'IP ".$options['o']." dans tout les enregistrements A des zones DNS du serveur pour la remplacer par ".$options['n']."\n";
echo "############################################################################################################################\n\n";

try {

	$dns_a_gets = $client->dns_a_get($session_id, -1);
        $nb_dns_a_get=0;
	foreach ($dns_a_gets as $dns_a_get) {
                if ($dns_a_get['type'] == 'A' && $dns_a_get['data'] == $options['o']) {
                        $nb_dns_a_get++;
                        $dns_zone_get = $client->dns_zone_get($session_id, $dns_a_get['zone']);
                        echo "Ip trouvé dans la zone ".$dns_zone_get['origin']." avec l'enregistrement ".$dns_a_get['name'].".".$dns_zone_get['origin'].". Voulez-vous la remplacer ? (Y/n) \n";
                        $handle = fopen ("php://stdin","r");
                        $line = fgets($handle);
                        if(trim($line) == 'yes' || trim($line) == 'y' || trim($line) == 'Y' || trim($line) == 'Yes'){
                                $dns_record = $client->dns_a_get($session_id, $dns_a_get['id']);
                                //print_r($dns_record);
                                $dns_record['data']=$options['n'];
                                //print_r($dns_record);
                                $affected_rows = $client->dns_a_update($session_id, $dns_a_get['sys_userid'], $dns_a_get['id'], $dns_record);
                                echo "Le changement à bien été opéré sur ".$affected_rows." enregistrement\n";
                        } else {
                                echo "Aucun changement effectué sur ".$dns_a_get['name'].".".$dns_zone_get['origin']."\n";
                        }
                        fclose($handle);
                }
        }

        if ($nb_dns_a_get == 0) {
                echo "Aucun enregistrement A avec l'IP  ".$options['o']."  n'a été trouvé. \n";
        }
	if($client->logout($session_id)) {
		echo "\nLogged out\n";
	}


} catch (SoapFault $e) {
	echo $client->__getLastResponse();
	die('SOAP Error: '.$e->getMessage()."\n");
}

?>

Script API Ispconfig création utilisateur

Je m’occupe d’un service d’hébergement destiné aux actions politiques / citoyennes / artistiques… Le serveur était plein de chez plein, ça tournait sans que je m’en occupe trop… J’ai décroché de ces geekeries pour d’autres occupations… Mais voilà Debian 7 était en fin de support, j’ai migré, fais du ménage… du coup il y a de nouveau de la place sur le serveur donc je ré-ouvre les portes.

De ce fait j’ai automatisé une tâche qui me prenait du temps jadis : la création des comptes (même si avec Ispconfig, c’est quand même clique clique). J’ai donc accouché d’un script PHP qui utilise l’API d’ISPconfig, on y passe en paramètre :

  • Nom d’utilisateur
  • Email de l’utilisateur
  • Sous domaine souhaité (ici : cequejesouhait.zici.fr)

Et il nous génère :

  • Création de l’utilisateur « client » pour l’accès au panel Ispconfig
  • Création du site web
    • Création d’une base de donnée et de son utilisateur
    • Création d’un compte FTP et SFTP pour l’accès au site web
  • Création d’un transfère mail (ici cequejesouhait@zici.fr vers l’email de l’utilisateur)

Le script est téléchargeable ici.

[ispconfig] Managesieve (dovecot) & Horde

sieve.infoDes dires de Wikipedia : « Sieve est un langage de filtrage du courrier électronique. Il est normalisé dans le RFC 5228. Sieve permet de filtrer sur les en-têtes d’un message qui suit le format du RFC 5322, c’est-à-dire un message Internet typique.  »

Sieve c’est très pratique car le filtrage du courrier est effectué au niveau du serveur dès la réception de l’email. Ce qui est presque indispensable quand on utilise plusieurs clients de messagerie (web &/ou desktop). Je vais donc utiliser Managesieve (qui est la partie serveur du protocole) pour Dovecot (projet Pigeonhole).

Actuellement le panel ISPconfig me permet d’éditer les filtres Sieve mais n’utilise pas Managesieve. ISPconfig stock en base (table mail_user_filter) et écrase le fichier à chaque modification.

Je ne souhaite plus passer par ISPconfig pour modifier mes filtres, mais directement par Horde (via Ingo) ou Roundcube (plugin) ou encore par thunderbird (plugin) selon mes besoins.

Installation de Managesieve

Rien de plus simple :

aptitude install dovecot-managesieved

Modifier le fichier /etc/dovecot/dovecot.conf :

< protocols = imap
> protocols = imap sieve

Puis redémarrer le service, le port 4190 doit ensuite être à l’écoute :

$ service dovecot restart
$ netstat -pnat | grep 4190
tcp        0      0 0.0.0.0:4190            0.0.0.0:*               LISTEN      11111/dovecot

Horde (ingo) & Sieve

Il vous suffit de modifier le fichier ingo/config/backends.php de votre instance horde de la façon suivante :

[...]
/* IMAP Example */
$backends['imap'] = array(
    // ENABLED by default
<    'disabled' => false,
>    'disabled' => true,
    'transport' => array(
        Ingo::RULE_ALL => array(
            'driver' => 'null',
            'params' => array(),
        ),
    ),
[...]
$backends['sieve'] = array(
    // Disabled by default
<    'disabled' => true,
>    'disabled' => false,
    'transport' => array(
        Ingo::RULE_ALL => array(
            'driver' => 'timsieved',
            'params' => array(
                // Hostname of the timsieved server
                'hostspec' => 'localhost',
                // Login type of the server
                'logintype' => 'PLAIN',
                // Enable/disable TLS encryption
                'usetls' => false,
                // Port number of the timsieved server
                'port' => 4190,
                // Name of the sieve script
<                'scriptname' => 'ingo',
>                'scriptname' => 'filtres',
                // Enable debugging. The sieve protocol communication is logged
[...]

Ne faites (peut être) pas ça chez vous

En effet ça ne s’avère pas être forcément la meilleure des solutions :

  • Il n’est apparemment pas possible d’importer des scripts existants.
  • Plus embêtant il s’avère que Horde fonctionne presque de la même façon qu’ISPconfig. A savoir qu’il stock en base les filtres et écrase le script sieve (via managesieve quand même) Ce qui me condamne à n’utilise qu’une interface (horde) pour modifier mes filtres :’-(

Bon je m’en contente, il est toujours plus agréable de modifier ses filtres via son webmail (horde) que via le panel ISPconfig.

[ispconfig] Remplacer Amavis/Spamassassin par Dspam

dspam-logo-textAjouter des services décentralisés « à la maison » c’est le bien mais à force d’empiler des briques… ça déborde. Et là j’étais à un stade ou la SWAP était un chouilla trop utilisée.

J’avais déjà désactivé ClamAv (anti-virus) pour économiser de la mémoire vive mais ça ne suffisait pas. Mon autre plus gros poste de dépense en RAM était Amavis/Spamassasin. Je me suis donc mis à chercher une alternative ; La plus légère et efficace semble être DSPAM.

Attention : Le fait de remplacer Amavis/Spamassasin va, de fait vous amputer de fonctionnalité administrable par le panel ISPconfig « Stratégie anti-spam », « liste blanche » car Ispconfig ne support pas (encore !?) DSPAM

Préparation

Étant donné que le serveur est déjà en prod j’ai modifié mon script de firewall (iptables) en spécifiant seulement mon adresse IP pour l’accès au SMTP (pour les tests) ça aura pour effet de faire patienter vos emails en file d’attente sur le serveur émetteur quoi que vous fassiez comme bêtise…

< iptables -A INPUT -p tcp --dport smtp -j ACCEPT
> iptables -A INPUT -p tcp --dport smtp -s VOTRE.IP.DE.MAISON -j ACCEPT

5 jours, c’est par défaut le temps d’attente maximal de la plupart des MTA. Vous pouvez donc théoriquement bricoler pendant 5 jours sans perdre d’email dans cet état…

Installation

Je suis sous Debian squeeze, les paquets Dsapm existe pour squeeze mais dans le backport (non activé pour ma part)

Installation des dépendances :

$ aptitude install libgd-gd2-perl libgd-graph-perl libgd-graph3d-perl libgd-text-perl dbconfig-common postfix-pcre

Ensuite on récupère les paquets sur le site de debian et ont les installent

$ dpkg -i libdspam7_3.10.1+dfsg-3~bpo60+1_amd64.deb
$ dpkg -i libdspam7-drv-mysql_3.10.1+dfsg-3~bpo60+1_amd64.deb
$ dpkg -i dspam_3.10.1+dfsg-3~bpo60+1_amd64.deb
$ dpkg -i dspam-webfrontend_3.10.1+dfsg-3~bpo60+1_all.deb

Note : dbconfig-common va vous demander ce qu’il faut pour créer la base de données.. »suivez le guide. »

Configuration

Postfix

Nous allons, dans postfix désactiver la partie Amavis et activer la partie Dspam /etc/postfix/main.cf :

58c58
< smtpd_client_restrictions = check_client_access mysql:/etc/postfix/mysql-virtual_client.cf
---
> smtpd_client_restrictions = check_client_access mysql:/etc/postfix/mysql-virtual_client.cf, check_client_access pcre:/etc/postfix/dspam_filter_access
71,72d70
< content_filter = amavis:[127.0.0.1]:10024
< receive_override_options = no_address_mappings
74a73,76
>
> dspam_destination_recipient_limit = 1

Même chose dans le fichier /etc/postfix/master.cf :

smtp      inet  n       -       -       -       -       smtpd
11a12
>    -o content_filter=dspam:
116,129c117,118
< 127.0.0.1:10025 inet n - - - - smtpd
<         -o content_filter=
<         -o local_recipient_maps=
<         -o relay_recipient_maps=
<         -o smtpd_restriction_classes=
<         -o smtpd_client_restrictions=
<         -o smtpd_helo_restrictions=
<         -o smtpd_sender_restrictions=
<         -o smtpd_recipient_restrictions=permit_mynetworks,reject
<         -o mynetworks=127.0.0.0/8
<         -o strict_rfc821_envelopes=yes
<         -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
<         -o smtpd_bind_address=127.0.0.1
< 
---
> dspam   unix    -       n       n       -       10      pipe
>         flags=Rhqu user=dspam argv=/usr/bin/dspam --deliver=innocent --user $user -i -f $sender -- $recipient

Dspam

Dans mon cas j’ai choisi de tout le temps délivrer les SPAM et de tagger le sujet. L’apprentissage se fera via l’interface web (dont nous verrons la configuration plus tard)

Activer le démarrage de Dspam /etc/default/dspam

< start=NO
---
> start=YES

La configuration de Dspam à largement été inspiré du howto d’UNIX garden :

29c29
< StorageDriver /usr/lib/dspam/libhash_drv.so
---
> StorageDriver /usr/lib/dspam/libmysql_drv.so
48c48
< TrustedDeliveryAgent "/usr/bin/procmail"
---
> TrustedDeliveryAgent "/usr/sbin/sendmail"
144c144
< Trust root
---
> #Trust root
146,149c146,149
< Trust www-data
< Trust mail
< Trust daemon
< Trust amavis
---
> #Trust www-data
> #Trust mail
> #Trust daemon
> #Trust amavis
151a152
> Trust postfix
297c298
< Preference "signatureLocation=message"	# { message | headers } -> default:message
---
> Preference "signatureLocation=headers"	# { message | headers } -> default:message
371a373,380
> MySQLServer     	127.0.0.1
> #MySQLPort
> MySQLUser               dspam
> MySQLPass               MOTDEPASSEDEMALADE
> MySQLDb                 dspam
> MySQLCompress           true
> MySQLUIDInSignature    on
> 
471c480
< LocalMX 127.0.0.1
---
> #LocalMX 127.0.0.1

Pour finir sur les tags j’ai dû faire la chose suivante :

$ mkdir /var/spool/dspam/txt/
$ echo ‘Scanned and tagged as SPAM by DSPAM’ > /var/spool/dspam/txt/msgtag.spam

Premier test

Redémarrage des services pour appliquer tous ces changements :

$ service amavis stop
$ service postfix restart
$ service dspam start

Pour tester vous pouvez en envoyer un message avec la commande telnet. Voici un petit script pratique pour automatiser le test :

#!/bin/bash
from="nimportequi@domain.fr"
to="david@domain.fr"
smtp="smtp.domain.fr"
(
    sleep 1
    echo "ehlo x"
    sleep 1
    echo "mail from:${from}"
    sleep 1
    echo "rcpt to:${to}"
    sleep 1
    echo "data"
    sleep 1
    echo "subject:Test message"
    sleep 1
    echo "from:${from}"
    sleep 1
    echo "to:${to}"
    sleep 1
    echo " "
    echo "Coucou."
    sleep 1
    echo "."
    sleep 1
    echo "QUIT"
) | telnet ${smtp} 25

Dans les entêtes du message vous devriez avoir du X-DSPAM comme ceci :

X-DSPAM-Result: Innocent
X-DSPAM-Processed: Thu Jul 1 00:03:19 2050
X-DSPAM-Confidence: 0.9751
X-DSPAM-Probability: 0.0000
X-DSPAM-Signature: 1,51e7a147199049585619662

Si tout fonctionne on dégage amavis (dit « le gros amavis ») du démarrage :

update-rc.d dspam defaults
update-rc.d amavis remove

Sinon fouiller vos logs (/var/log/mail.log /var/log/syslog…)

Interface web

L’interface web est en CGI et nous allons utiliser apache & suexec (qui est déjà embarqué avec ISPconfig) pour le faire tourner

Voici la configuration du virtualhost (sites-available/dspam)

<IfModule mod_suexec.c>
    <VirtualHost *:80>
        DocumentRoot /var/www/dspam

        # Problème css, image : 
        Alias /usr/share/dspam /usr/share/dspam

        ServerName dspam.domain.fr
        ServerAdmin dspam@domain.fr

        SuexecUserGroup dspam dspam

        <Directory /var/www/dspam>
            Options ExecCGI
            Options -Indexes
            Addhandler cgi-script .cgi
            DirectoryIndex dspam.cgi
        </Directory>

        <Directory />
            Options FollowSymLinks
            AllowOverride None
            Order allow,deny
            allow from all
            AuthType Basic
            AuthName "Restricted DSPAM user"
            Auth_MYSQLhost localhost
            Auth_MYSQLusername ispconfig
            Auth_MYSQLpassword MOTDEPASSESUPERCHAUD
            Auth_MYSQLdatabase dbispconfig
            Auth_MYSQLpwd_table mail_user
            Auth_MYSQLuid_field login
            Auth_MYSQLpwd_field password
        </Directory>
    </VirtualHost>
</IfModule>

On active le virtualhost :

$ a2ensite dspam
$ service apache2 graceful

On ajoute le compte admin de l’interface :

$ echo "david" > /etc/dspam/admins

Vous devriez maintenant pouvoir vous rendre sur l’interface :

  • http://dspam.domain.fr/ (interface utilisateur)
  • http://dspam.domain.fr/admin.cgi (interface d’administration)

Conclusion

L’objectif premier était d’économiser de la mémoire vive, c’est chose faite ! la preuve en image :

memory-day

Je suis aussi très satisfait de l’apprentissage de DSPAM. Au début ça peut faire peur parce qu’il laisse vraiment tout passer, mais l’apprentissage est rapide et significatif :

Les premiers jours d'utilisation de DSPAM
Les premiers jours d’utilisation de DSPAM

Ressources

Les sites ressources que j’ai utilisées :

ISPconfig – service email : DKIM & Amavis

By Nemo (Public Domain CC0)

Edit 13/04/2015 : Ajout d’@mynetworks pour que les réseaux connus soient marqués DKMI.

DKIM (DomainKeys Identified Mail)  est une norme d’authentification par clef publique/privé du nom de domaine de l’expéditeur d’un courrier électronique. Une partie de la signature est comprise dans l’entête du message & une autre dans un enregistrement DNS TXT dédié.

Elle constitue une protection efficace contre le spam et l’hameçonnage. Elle permet aussi surtout (et c’est en ça qu’elle nous intéresse le plus) d’éviter de se faire considérer comme SPAM.

Je parle d’ISPconfig dans mon titre car c’est ce qui motorise mon serveur en ce moment, mais je pense que la procédure fonctionne avec Amavis en règle générale.

Mise en place

Il faut déjà préparer le terrain :

srv:~$ mkdir /etc/amavis/pki
srv:~$ ln -s /etc/amavis/pki /var/lib/amavis/pki

Ensuite on génère la clef :

srv:~$ amavisd-new genrsa /var/lib/amavis/pki/dkmi-mercereau.info-key.pem

Puis il faut créer le fichier /etc/amavis/conf.d/99-dkmi avec la configuration suivante :

$enable_dkim_verification = 1;
$enable_dkim_signing = 1;
dkim_key('mercereau.info', 'default', '/var/lib/amavis/pki/dkmi-mercereau.info-key.pem');
@dkim_signature_options_bysender_maps = (
    { '.' => { ttl => 21*24*3600, c => 'relaxed/simple' } } 
);
@mynetworks = qw(0.0.0.0/8 
                127.0.0.0/8     
                123.123.123.123/32  # IP maison 1
                456.456.456.456/32  # IP boulot
);

Ensuite il faut redémarrer le service Amavis :

srv:~$ service amavis restart

Ensuite récupérer la clef DKIM qu’il va falloir insérer dans votre enregistrement DNS avec la commande suivante : :

srv:~$ amavisd-new showkeys
; key#2, domain mercereau.info, /var/lib/amavis/pki/dkmi-mercereau.info-key.pem
default._domainkey.mercereau.info.    3600 TXT (
  "v=DKIM1; p="
  "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvF925xN7NeMekLrSMSTtZaSx6"
  "9u6njS+mG99DcM1kqj+9RW79KsdKxNXlMull3Mcp6KwKHV7eb8bSvx2TeJ0YUxuA"
  "hZK0dc971R+F6Xmq4fYW02PeuF1fiyl2oL/rxojGT20anxEc9hzFR8jaCFv4CXhh"
  "1efU2BxngTD/onYQVQIDAQAB")

Il faut donc créer un enregistrement DNS comme tel :

default._domainkey 18000 TXT « v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
[..]
QVQIDAQAB »

Vérification

Après propagation DNS (que vous pouvez vérifier avec l’outil Dig), vous pouvez lancer la commande :

srv:~$ amavisd-new testkeys
TESTING#2: default._domainkey.mercereau.info => pass

Pour être vraiment certain que votre message est signé vous pouvez vous envoyer un email sur une adresse externe et vérifier les entêtes. Sinon vous pouvez envoyer un email à : check-auth [arobase] verifier.port25.com et vous reverser un rapport complet sur « l’état de votre port 25 »

==========================================================
Summary of Results
==========================================================
SPF check: pass
DomainKeys check: neutral
DKIM check: pass
Sender-ID check: pass
SpamAssassin check: ham

[…]
———————————————————-
SpamAssassin check details:
———————————————————-
SpamAssassin v3.3.1 (2010-03-16)

Result: ham (-2.0 points, 5.0 required)

La partie importante pour nous est donc le DKIM check qui doit être à « pass ». D’autre part je ne peux que vous conseiller d’ajouter aussi un enregistrement SPF.

Le rapport m’indique que j’ai un SpamAssassin score de -2 ce qui est très bon car la configuration par défaut de SpamAssassin annonce les messages en SPAM lorsqu’il dépasse un score de +5.

Ressources :

ISPconfig Postfix & Dovecot : unknown user

Après une installation de ISPconfig avec Dovecot sur une Debian j’ai eu un problème de réception d’email :

May 31 21:40:05 popof postfix/local[16076]: 94C832A028C8: to=<xxxxxxxxxxxxxxx>, orig_to=<xxxxxxxxxxxxxxxxxx>, relay=local, delay=2.3, delays=0.04/0.01/0/2.2, dsn=5.1.1, status=bounced (unknown user: "david")

La solution est de modifier le fichier dovecot-sql.conf comme ceci :

125,126c125
> user_query = SELECT email as user, maildir as home, CONCAT('maildir:', maildir, '/Maildir') as mail, uid, gid, CONCAT('*:storage=', quota, 'B') AS quota_rule, CONCAT(maildir, '/.sieve') as sieve FROM mail_user WHERE login = '%u' AND disable%Ls = 'n'
---
< #user_query = SELECT email as user, maildir as home, CONCAT('maildir:', maildir, '/Maildir') as mail, uid, gid, CONCAT('*:storage=', quota, 'B') AS quota_rule, CONCAT(maildir, '/.sieve') as sieve FROM mail_user WHERE login = '%u' AND disable%Ls = 'n'
< user_query = SELECT email as user, maildir as home, CONCAT('maildir:', maildir, '/Maildir') as mail, uid, gid, CONCAT('*:storage=', quota, 'B') AS quota_rule, CONCAT(maildir, '/.sieve') as sieve FROM mail_user WHERE login = '%u' OR email = '%u' AND disable%Ls = 'n'

La solution a été trouvé ici : http://www.howtoforge.com/forums/showthread.php?t=56499&page=2