Autoconfig (thunderbird) & Autodiscover (outlook) sur messagerie hébergé ISPconfig

Thunderbird et Outlook représente une grosse part de marché dans les clients de messagerie. Je m’occupe de plusieurs service de messagerie (pro, asso…) et il est bien commode pour les usagés que « ça tombe en marche » tout seul pour le paramétrage…

A savoir :

  • Thunderbird chercher les paramètres dans un XML sur http://autoconfig.votredomain.fr/mail/config-v1.1.xml
  • Outlook cherche l’enregistrement DNS _autodiscover._tcp.votredomain.fr l’url à contacter et ensuite récupère le XML : https://url_donner_dans_le_dns/Autodiscover/Autodiscover.xml

Du coup nous allons rassemblé ces 2 demandes avec un htaccess et un script PHP qui génère le bon format XML fonction de si c’est outlook our thunderbird qui demande…

Pour cela il faut :

  • Créer un site « autoconfig.votredomain.fr » (ou un alias si vous avez plusieurs site) et faire pointer l’enregistrement DNS bien sûr…
  • Créer l’enregistrement DNS SRV type :
    • _autodiscover._tcp.votredomain.fr. 3600 IN SRV 10 10 443 autoconfig.votredomain.fr.

Dans le site autoconfig.votredomain.fr il vous suffit de glisser 2 fichiers à la racine :

Fichier .htaccess contient :

RewriteEngine On
RewriteRule ^Autodiscover/Autodiscover.xml autoconfig-mail.php
RewriteRule ^mail/config-v1.1.xml autoconfig-mail.php

Fichier autoconfig-mail.php contient :

<?php

/*
By David Mercereau
Licence Beerware
*/

function extract_domain($domain) {
    if(preg_match("/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i", $domain, $matches)) {
        return $matches['domain'];
    } else {
        return $domain;
    }
}

$domain = extract_domain($_SERVER['SERVER_NAME']);
$mailServeur='mail.'.$domain;

if (preg_match('/^\/mail\/config-v1\.1\.xml/', $_SERVER['REQUEST_URI'])) {
	header('Content-Type: text/xml');
    header('Content-Type: application/xml');
	?>
<clientConfig version="1.1">
	<emailProvider id="<?= $domain ?>">
	  <domain><?= $domain ?></domain>
	  <displayName><?= $domain ?></displayName>
	  <displayShortName><?= $domain ?></displayShortName>
	  <incomingServer type="imap">
		 <hostname><?= $mailServeur ?></hostname>
		 <port>143</port>
		 <socketType>STARTTLS</socketType>
		 <username>%EMAILADDRESS%</username>
		 <authentication>password-cleartext</authentication>
	  </incomingServer>
	  <outgoingServer type="smtp">
		 <hostname><?= $mailServeur ?></hostname>
		 <port>587</port>
		 <socketType>STARTTLS</socketType>
		 <username>%EMAILADDRESS%</username>
		 <authentication>password-cleartext</authentication>
	  </outgoingServer>
	  <documentation url="https://webmail.<?= $domain ?>">
		  <descr lang="fr">Connexion Webmail</descr>
		  <descr lang="en">Webmail connexion</descr>
	  </documentation>
	  <documentation url="http://projet.retzo.net/projects/hebergement/wiki">
		<descr lang="fr">Documentation</descr>
		<descr lang="en">Generic settings page</descr>
	  </documentation>
	</emailProvider>
</clientConfig>
	<?php
} else {
	// Outlook
	//get raw POST data so we can extract the email address
	$data = file_get_contents("php://input");
	preg_match("/\<EMailAddress\>(.*?)\<\/EMailAddress\>/", $data, $matches);

	//set Content-Type
	header('Content-Type: text/xml');
    header('Content-Type: application/xml');
	echo '<?xml version="1.0" encoding="utf-8" ?>'; 
	?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
   <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
	   <Account>
		   <AccountType>email</AccountType>
		   <Action>settings</Action>
		   <Protocol>
			   <Type>IMAP</Type>
			   <Server><?= $mailServeur ?></Server>
			   <Port>993</Port>
			   <DomainRequired>off</DomainRequired>
			   <LoginName><?php echo $matches[1]; ?></LoginName>
			   <SPA>off</SPA>
			   <SSL>on</SSL>
			   <AuthRequired>on</AuthRequired>
		   </Protocol>
		   <Protocol>
			   <Type>POP3</Type>
			   <Server><?= $mailServeur ?></Server>
			   <Port>995</Port>
			   <DomainRequired>off</DomainRequired>
			   <LoginName><?php echo $matches[1]; ?></LoginName>
			   <SPA>off</SPA>
			   <SSL>on</SSL>
			   <AuthRequired>on</AuthRequired>
		   </Protocol>
		   <Protocol>
			   <Type>SMTP</Type>
			   <Server><?= $mailServeur ?></Server>
			   <Port>587</Port>
			   <DomainRequired>off</DomainRequired>
			   <LoginName><?php echo $matches[1]; ?></LoginName>
			   <SPA>off</SPA>
			   <Encryption>TLS</Encryption>
			   <AuthRequired>on</AuthRequired>
			   <UsePOPAuth>off</UsePOPAuth>
			   <SMTPLast>off</SMTPLast>
		   </Protocol>
	   </Account>
   </Response>
</Autodiscover>
	<?php
}

?>

Des ressources :

Migration Plesk 18 > ISPconfig 3

Pour l’association le Retzien.fr (dont je fais parti) j’ai effectué une migration de Plesk 18 vers ISPconfig 3. Plesk avait été mis en œuvre avant mon arrivé car l’administrateur en place connaissait le produit. Mais c’est un produit payant, de plus en plus cher, et pour une association qui a du mal à avoir un équilibre financier c’était difficile à assumer. Plesk est très « beau » et user friendly mais en tant qu’administrateur système je le trouve plutôt opaque (on ne sais pas exactement ce qu’il fait, pas trop de respect des « us et coutumes » de Linux (position des fichiers de config…), ET CE N’EST PAS LIBRE !!!

ISPconfig est libre et propose un script de migration (ISPConfig Migration Toolkit) mais ne supporte pas les dernières versions de Plesk, car le chiffrement des mots de passe n’est plus compatible. Du coup j’ai dû bricoler… Ce bricolage est l’objet de cet article.

Note : ici il n’est question que de la partie mail/DNS et compte client, car c’est la seule fonctionnalité du serveur, les fonctions web n’ont pas été testées.

Installation ISPconfig + Migration Toolkit

Je ne détaille pas ici la procédure d’installation d’ISPconfig 3 et l’usage du « Migration Toolkit » qui sont déjà bien documentés, dans mon cas :

Suite à l’usage du « Migration toolkit », même si le Plesk source n’est pas supporté, je n’ai pas d’erreur mais j’ai plusieurs problèmes que je vais essayer de régler par différents scripts.

Préambules : Les scripts suivants utilisent l’API d’ISPconfig (il faut donc créer un utilisateur distant, dans Système/utilisateur distant). ils doivent donc les lancer depuis le serveur source (Plesk).

Authentification IMAP

Le problème

Il est impossible de se connecter au serveur avec les comptes IMAP. Le log dit :

Error: sql(xxxxxxx@retzien.fr,::1,<MdFia/2k3toAAAAAAAAAAAAAAAAAAAAB>): Invalid password in passdb: crypt() failed: Invalid argument

Effectivement le chiffrement en base est différent :

Mais on va s’en sortir en rusant, en effet Plesk ne crypte/ »hash » pas les mots de passe mais les chiffre :

  • Crypter / « hasher » : c’est convertir définitivement un chaîne (ici un mot de passe) en un code. Pour savoir si le mot de passe proposé pour l’authentification est bon il faut crypter / « hasher » le mot de passe et le comparer.
  • Chiffrer : c’est convertir une chaîne (le mot de passe ici) avec une clé, cette fonction est réversible, et qui détient la clé peut donc déchiffrer la chaîne (le mot de passe)
  • Plus d’info sur la différence ici

Les mots de passe dans Plesk sont donc « lisibles » pour qui possède la clé de déchiffrement (l’administrateur par exemple ou un hacker qui obtiendrait l’accès au serveur…)

Pour vous en persuader, lancez la commande ci après et vous obtiendrez un beau tableau 1er colonne « émail », 2ème « mot de passe en clair »

/usr/local/psa/admin/sbin/mail_auth_view

Mais pourquoi les mots de passes ne sont ils pas cryptés ?

Plesk propose le support de l’authentification « mot de passe chiffré ». Et cette fonctionnalité est de fait contrainte à avoir le mot de passe en clair sur le serveur car il faut pouvoir le déchiffrer.

Exemple de méthode d’authentification sous Thunderbird

ISPconfig ne supporte pas cette fonctionnalité, car qu’il considère que ce serait un trop gros trou dans la sécurité du serveur, et qu’avec le chiffrement de la connexion SSL/TLS ou START/TLS ce n’est pas nécessaire d’en remettre une couche.

Effet de bord important : si vos utilisateurs ont configurés leur client de messagerie en « mot de passe chiffré » il faudra leur faire modifier ce paramétrage (IMAP & SMTP)

De ce que je comprends, cette méthode d’authentification chiffrée avait sa place quand les serveur IMAP/SMTP ne proposait pas de chiffrement de connexion SSL/TLS ou START/TLS.

Les sources de ce que j’avance :

La solution

La solution consiste, sur le serveur Plesk à lancer le script ci-après qui :

  • Lance la commande Plesk « magique » pour voir les mots de passe déchiffrés
  • « Parse » le résultat… Se connecte à ISPconfig et change le mot de chaque boîte émail
<?php
 
/* 
 * licence Beerware
 * By David Mercereau : http://david.mercereau.info
 */
 
// Plan B : https://www.besuchet.net/2016/06/plesk-11-encrypted-hashed-password-authentication-php-on-psa-database/
 
// ISPconfig Remote Config
$CONFIG['remoteUser'] = 'retzien';
$CONFIG['remotePassword'] = 'XXXXXXXXXX';
$CONFIG['remoteSoapLocation'] = 'https://192.168.1.64:8080/remote/index.php';
$CONFIG['remoteSoapUri'] = 'https://192.168.1.64:8080/remote/';
$CONFIG['logfile'] = '/tmp/migration-email-password.log';
 
function toLog($txt, $level) {
	file_put_contents($GLOBALS['CONFIG']['logfile'],date("[j/m/y H:i:s]")." - ".$level." - $txt \r\n",FILE_APPEND);
	echo "[$level] $txt\n";
}
 
// Connexion to 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'])) {
	toLog('Login Ok. Session ID:'.$session_id, 'info');
}
 
# Launch plesk command for auth view in plain text
exec('/usr/local/psa/admin/sbin/mail_auth_view', $out, $return);
if ($return == 0 || $out[0] == null){
	foreach ($out as $line) {
		$lineExplode = explode('|', $line);
		if (isset($lineExplode[1]) &amp;&amp; isset($lineExplode[3])) {
			$email = str_replace(' ', '', $lineExplode[1]);
			$password = str_replace(' ', '', $lineExplode[3]);
			# Verification data
			if (filter_var($email, FILTER_VALIDATE_EMAIL) &amp;&amp; $password != '') {				
				# mail_user_get
				try {
					$mail_user_get = $client->mail_user_get($session_id, array('email' => $email));
					if (count($mail_user_get) > 0) {
						# Update data
						$mail_user_get['password']=$password;
						$mail_user_get['move_junk']='y';
						$affected_rows = $client->mail_user_update($session_id, 0, $mail_user_get[0]['mailuser_id'], $mail_user_get);
						if ($affected_rows == 0) {
							toLog('No change for '.$email, 'error');
						} else {
							toLog('Ok for '.$email, 'info');
						}
					} else {
						toLog('email '.$email.' not present in ISPconfig', 'warn');
					}
				} catch (SoapFault $e) {
					echo $client->__getLastResponse();
					die('SOAP Error: '.$e->getMessage());
				}
			} 
		}
	}
}
 
if($client->logout($session_id)) {
	toLog('Logged out.', 'info');
}
 
?>

IMAP subscriptions

La souscription au dossier IMAP (le fait de voir ou non certains dossiers et/ou d’en cacher d’autres) n’a pas migré, donc si l’utilisateur avait ajouté un dossier IMAP, il ne le voyez plus sur le nouveau serveur, pourtant le dossier est bien là.. c’est fâcheux. Pour corriger ça, lancez ce petit script sur le Plesk (source) :

#!/bin/bash
target='root@192.168.1.64'
find  /var/qmail/mailnames/ -name subscriptions | while IFS=$'\n' read f ; do
	domain=$(echo $f | cut -d/ -f5)
	user=$(echo $f | cut -d/ -f6)
	echo ${user}@${domain}
	scp /var/qmail/mailnames/${domain}/${user}/Maildir/subscriptions ${target}:/var/vmail/${domain}/${user}/Maildir/
done

DKIM & Spam policy

Ce script ajoute une politique de spam “normal” (modifiable) et récupère les anciennes clefs DKIM générées par Plesk pour les appliquer dans ISPconfig. Ceci pour éviter d’avoir à retoucher au DNS pendant la migration (toujours à lancer depuis le Plesk) :

<?php
 
/* 
 * licence Beerware
 * By David Mercereau : http://david.mercereau.info
 */
 
// ISPconfig Remote Config
$CONFIG['remoteUser'] = 'retzien';
$CONFIG['remotePassword'] = 'XXXXXXX';
$CONFIG['remoteSoapLocation'] = 'https://192.168.1.64:8080/remote/index.php';
$CONFIG['remoteSoapUri'] = 'https://192.168.1.64:8080/remote/';
$CONFIG['logfile'] = '/tmp/migration-email-password.log';
$CONFIG['DKIM-Selector'] = 'default';
$CONFIG['policy_id'] = 5;
$CONFIG['priority'] = 5;
 
function toLog($txt, $level) {
	file_put_contents($GLOBALS['CONFIG']['logfile'],date("[j/m/y H:i:s]")." - ".$level." - $txt \r\n",FILE_APPEND);
	echo "[$level] $txt\n";
}
 
// Connexion to 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'])) {
	toLog('Login Ok. Session ID:'.$session_id, 'info');
}
 
$mail_domain_record = $client->mail_domain_get($session_id, array('active' => 'y'));
 
foreach ($mail_domain_record as $mail_domain) {
	# DKIM
	if (is_dir('/etc/domainkeys/'.$mail_domain['domain'])) {
		$getPrivateKey=file_get_contents('/etc/domainkeys/'.$mail_domain['domain'].'/'.$CONFIG['DKIM-Selector']);
		exec('openssl rsa -in /etc/domainkeys/'.$mail_domain['domain'].'/'.$CONFIG['DKIM-Selector'].' -pubout', $out, $return);
		if ($return == 0 || $out[0] == null){
			$getPublicKey='';
			foreach ($out as $line) {
				$getPublicKey.=$line."\n";
				//~ if (!preg_match('/^---/', $line))  {
					//~ $getPublicKeyDns.=$line;
				//~ }
			}
			$mail_domain['dkim'] = 'y';
			$mail_domain['dkim_selector'] = $CONFIG['DKIM-Selector'];
			$mail_domain['dkim_private'] = $getPrivateKey;
			$mail_domain['dkim_public'] = $getPublicKey;
			$affected_rows = $client->mail_domain_update($session_id, 0, $mail_domain['domain_id'], $mail_domain);
			if ($affected_rows == 0) {
				toLog('DKIM No change for '.$mail_domain['domain']. ' already exist ?', 'error');
			} else {
				toLog('DKIM Ok for '.$mail_domain['domain'], 'info');
			}
		}
	} else {
		toLog($mail_domain['domain'].' haven\'t got dkim key on plesk', 'warn');
	}		
	# Spam Policy add in domain 
	try {
		$client_id = 1;
		$params = array(
			'server_id' => 1,
			'priority' => $CONFIG['priority'],
			'policy_id' => $CONFIG['policy_id'],
			'email' => '@'.$mail_domain['domain'],
			'fullname' => '@'.$mail_domain['domain'],
			'local' => 'Y'
		);
		$affected_rows = $client->mail_spamfilter_user_add($session_id, $client_id, $params);
		toLog('Spam policy Ok for '.$mail_domain['domain'], 'info');
	} catch (SoapFault $e) {
		toLog('Spam policy no change for '.$mail_domain['domain']. ' already exist ?', 'error');
	}
}
 
if($client->logout($session_id)) {
	toLog('Logged out.', 'info');
}
 
?>

Les mailings lists

Les mailings lists ne sont pas migrées, pourtant c’est du « Mailman 2 » des 2 côtés donc ça ne devrait pas être très compliqué.

Ce script est à lancer depuis le Plesk en mode « CLI » (ligne de commande) et vous avez quelques paramètres à modifier au début.

Le script :

  • Liste les mailings lists avec la commande /var/lib/mailman/bin/list_lists
  • Les créer dans ispconfig
  • Donne les commandes rsync pour synchroniser les données (archives, paramètres…)
 <?php
 
/* 
 * licence Beerware
 * By David Mercereau : http://david.mercereau.info
 */
 
// ISPconfig Remote Config
$CONFIG['remoteUser'] = 'retzien';
$CONFIG['remotePassword'] = 'XXXXX';
$CONFIG['remoteSoapLocation'] = 'https://192.168.1.64:8080/remote/index.php';
$CONFIG['remoteSoapUri'] = 'https://192.168.1.64:8080/remote/';
$CONFIG['logfile'] = '/tmp/migration-email-password.log';
$CONFIG['clientId'] = 3;
 
function toLog($txt, $level) {
	file_put_contents($GLOBALS['CONFIG']['logfile'],date("[j/m/y H:i:s]")." - ".$level." - $txt \r\n",FILE_APPEND);
	echo "[$level] $txt\n";
}
 
// Connexion to 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'])) {
	toLog('Login Ok. Session ID:'.$session_id, 'info');
}

# Launch plesk command for auth view in plain text
exec('/var/lib/mailman/bin/list_lists -b', $out, $return);
if ($return == 0 || $out[0] == null){
	foreach ($out as $line) {
			unset($host_name);
			unset($owner);
		exec('/usr/lib/mailman/bin/config_list -o /tmp/'.$line.' '.$line.' ; grep ^host_name /tmp/'.$line.' | cut -d"\'" -f2', $host_name, $return);
		exec('/usr/lib/mailman/bin/config_list -o /tmp/'.$line.' '.$line.' ; grep ^owner /tmp/'.$line.' | cut -d"\'" -f2', $owner, $return);
		try {
			$params = array(
				'server_id' => 1,
				'domain' => $host_name[0],
				'listname' => $line,
				'email' => $owner[0],
				'password' => 'temp123!'
			);
			$mailinglist_id = $client->mail_mailinglist_add($session_id, $CONFIG['clientId'], $params);
			toLog('Ok for '.$line.'@'.$host_name[0]);
		} catch (SoapFault $e) {
			toLog('SOAP Error: '.$line. ' - '. $e->getMessage(), 'error');
		}
	}
}
 
if($client->logout($session_id)) {
	toLog('Logged out.', 'info');
}
 
echo "Patienteez 5min que Ispconfig est tout ajouté puis : \n";
echo "for list in \$(/var/lib/mailman/bin/list_lists -b); do\n
	rsync -az --progress  /var/lib/mailman/archives/public/\$list/ root@192.168.1.64:/var/lib/mailman/archives/public/\$list/\n
	rsync -az --progress  /var/lib/mailman/archives/private/\$list/ root@192.168.1.64:/var/lib/mailman/archives/private/\$list/\n
	rsync -az --progress  /var/lib/mailman/archives/private/\${list}.mbox root@192.168.1.64:/var/lib/mailman/archives/private/\n
	rsync -az --progress  /var/lib/mailman/lists/\$list/ root@192.168.1.64:/var/lib/mailman/lists/\$list/\n
done\n";
 
?>

En sortie le script donne quelques commandes « rsync » à exécuter par un copier / coller direct dans le terminal du serveur source (Plesk) pour synchroniser les données (archives, paramètres…)

Ensuite sur le serveur decdestination (ISPconfig) lancez la commande :

/var/lib/mailman/bin/check_perms -f

Conclusion

La migration c’est bien passée, c’est transparent pour l’utilisateur, à part le problème de « mot de passe chiffré » à « mot de passe normal », qu’il faut faire changer avant la migration de préférence.

Sauvegarde de serveur écologiquement soutenable (à froid)

Pour les besoins de mes activités pro et associatives, j’ai besoin de faire de la sauvegarde quotidienne de serveur. Les serveurs c’est déjà plutôt énergivore en électricité/climatisation (quoi que pas tant que ça si on s’auto-héberge – pas de climatisation par exemple). Je cherche à faire diminuer le coup énergétique tant que je peux, et les sauvegardes ne sont pas en reste.

Je suis donc parti sur une solution :

  • Froide : c’est-à-dire qui ne serait allumée qu’au besoin (au moment de la sauvegarde) et pas allumée 24h/24 comme à l’habitude dans un datacenter.
  • Faible consommation : Puisque composée d’un Raspberry Pi et d’un disque dur 2,5° = consommation ~3Wh
  • Auto-hébergée : ça veut dire « à la maison », ça évite les climatiseurs des datacenters (vitaux quand la concentration de serveurs est forte) et on garde le contrôle sur les données/le matériel.

En solution logiciel, j’utilise Dirvish, qui est une surcouche à Rsync, qui permet (en gros) de faire des sauvegardes « full » tous les jours en ne copiant que les différences. En gros il ne télécharge et enregistre que les fichiers différents de la veille et utilise l’adresse INODE sur le disque des fichiers qui n’ont pas changé. De cette façon, toute l’arborescence est complète tous les jours mais elle ne prend pas plus d’espace disque que la différence (un peu magique quand on ne connait pas le fonctionnement d’un partitionnement sous linux). De cette façon, ma sauvegarde quotidienne prend ~1h pour sauvegarder ~150Go sur 4 serveurs.

Le matériel utilisé

J’utilise donc pour mes sauvegardes à froid :

  • Un Raspberry Pi (4 ici mais pas vraiment d’importance – quoi que avec un Zero j’ai essayé c’est pas terrible en perf…)
  • Un disque dur 2,5° mécanique (ça serait encore plus économe en électricité avec un SSD) dans un boîtier USB 3.0

Consommation totale : 3Wh constaté au Wattmètre

Comparatif consommation

Pour comparaison (discutable j’en conviens) dans mon cas avec la solution choisie et une VM ou un serveur que j’aurais pu louer dans un datacenter :

RaspberryPi + DisqueDatacenter
Temps d’allumageLe temps de la sauvegarde (ici ~1h mais variable)24h/24h
Consommation électrique (W)38 à 170 (1)
Consommation journalière (Wh/j)3192 à 4080

(1) Consommation serveur nuancée entre une VM (8W) et un serveur physique dédié (170W) – chiffres de l’ADM (source). C’est tout relatif, ça dépend des cas, c’est une hypothèse de travail. Je n’ai pas non plus considéré les équipements actifs pour simplifier (chez moi juste une box, dans un datacenter beaucoup de switch/routeurs : même s’ils sont mutualisés, ça a un coût énergétique).

Avec cette méthode de sauvegarde à froid, dans mon cas, on est entre 64 et 1360 fois moins énergivore que dans le cas d’une sauvegarde ‘classique » en datacenter.

L’allumage automatique peut se faire de différente façon :

  • BIOS : certain bios permette un « réveil » à heure fixe mais ce n’est pas le cas du Raspberry Pi
  • WakeOnLan (fait par le serveur a sauvegarde par exemple)
  • Une prise programmable avec l’heure
  • PvMonit (dans mon cas) logiciel avec lequel je gère le surplus de mon énergie solaire

Dans tous les cas, le Raspberry Pi s’éteint de lui même après la sauvegarde (shutdown -h now dans le script à la fin de la sauvegarde).

Il y a quand même des désavantages à faire la sauvegarde « à la maison » :

  • Débit montant limité : dans le cas ou il faut remonter le serveur en entier (toute la sauvegarde) ça peut prendre un certain temps car le débit montant d’une box est souvent faible
    • Ce cas est très très rare
    • Ce défaut n’est plus si on est en situation auto-hébergée : on va brancher notre disque dur avec la sauvegarde directement sur le serveur à reconstruire
  • Restaurer à distance peut être problématique : si on est pas sur site et qu’on a besoin d’accéder aux sauvegardes, ça peut être problématique
    • Tout dépend de la conception pour l’allumage automatique. Dans mon cas, je peux allumer à distance au besoin avec PvMonit.

Après quelques tests de débits/écritures, voici ce que je constate sur ce « nas » de sauvegarde :

root@nas(ro):/mnt/nas# curl -4 -o ./test100M http://bouygues.testdebit.info/100M.iso
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 95.3M  100 95.3M    0     0  7145k      0  0:00:13  0:00:13 --:--:-- 7509k

En comparaison avec mon ordinateur :

david@monordinateurportable:/tmp$ curl -4 -o ./test100M http://bouygues.testdebit.info/100M.iso
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 95.3M  100 95.3M    0     0  8424k      0  0:00:11  0:00:11 --:--:-- 8689k

A ~100k de différence ça me semble pas significatif voir même plutôt bon ! (surpris même que ce soit si bon…)

Plus gros, plus redondant

Pour ceux qui ont de plus gros besoins en capacité / redondance de disque, il y a des HAT pour Raspberry Pi où il est possible de connecter plusieurs disques :

Héberger un WordPress : sécurité, optimisation, économie d’énergie

WordPress occupe une part très importante dans l’usage des CMS à l’heure ou j’écris ce billet (62%). Voilà ce qui peut l’expliquer à mon sens :

  • Simple d’utilisation côté utilisateur
  • Plutôt simple côté développeur
  • Un nombre incalculable de thèmes et de plugins sont à disposition (communauté conséquente).

Mais il a aussi les défaut de ses qualités :

  • Côté utilisation de ressources énergétiques (serveur), WordPress est une catastrophe… Surtout dès qu’on lui ajoute des plugins (qui n’en ajoute pas ?).
  • Côté sécurité, il est très attaqué (car très utilisé). Le sport préféré des hackers, c’est le brute force…

Je vais tâcher de balayer ici mes trucs pour transformer un wordpress énergivore en wordpress sobre et sécurisé 🙂

Transformer en site statique (parfait pour les sites vitrines)

Un site « statique » c’est quoi ? C’est un site ou le code est uniquement exécuté sur le client et le serveur ne fait rien d’autre que « servir la page ». Alors qu’un site dynamique par opposition (sous wordpress pour l’exemple) va générer des pages « à la volée » et donc utiliser des ressources sur le serveur.

Mon parti-pris, c’est de convertir le wordpress « visible pour les visiteurs » en site statique « html ». L’utilisateur continue d’utiliser / d’alimenter son site via l’interface (bien faite et connue) wordpress, mais le visiteur lui ce qu’il voit c’est un site « html », sans aucun code dynamique (php ici) exécuté. De cette façon :

  • Le site est inattaquable par les hackers/spameurs, il n’y a plus de code dynamique, donc plus de moyen de corrompre le site / le serveur
  • L’affichage pour le visiteur est beaucoup plus rapide.
  • Le serveur consomme beaucoup moins de ressources (moins d’électricité, plus petit serveur…).
  • Les utilisateurs qui connaissent déjà wordpress ne sont pas bouleversés / impactés par cette optimisation.

Pour cela j’utilise un plugin wordpress qui s’appelle wp2static. Voici la structure du répertoire « web » qui est la racine du site :

  • /wp/ : site wordpress avec le plugin wp2static. Adresse utilisée par l’administrateur du site pour faire ces modifications
  • /static/ : le site statique généré par le plugin
  • /.htaccess : contient les redirections pour que le visiteur ne voit pas de différence

Dans le wordpress installé dans /wp/ rendez -vous dans wp2statics et configuré :

  • Where will you host the optimized version of your site? : Subdirectory
  • Destination URL : http://votresite.fr/
  • Target Directory : /var/www/votreiste.fr/web/static/

Puis dans l’onglet Crawling indiqué :

  • Exclude certain URLs :
    • /wp/
    • /wp-content/uploads/
    • /wp-admin/

Quand c’est fait, vous pouvez cliquer sur Start Static Export.

Ajouter dans votre configuration apache ou dans un .htaccess à la racine du site :

RewriteEngine on
# Pas de ré-écriture pour le contenu uploadé (images, 
RewriteRule ^/wp-content/uploads/(.*)$          /wp/wp-content/uploads//$1 [L]
# Rediriger toutes les requêtes vers /static sauf quand l'accès est souhaité sur le site wordpress original (/wp)
RewriteCond %{REQUEST_URI} !^/wp/ 
RewriteRule ^(.*)$          /static/$1 [L]

Inconvénient majeur de ce type de « transformation » en site full static

  • A chaque modification l’utilisateur doit penser à re-générer le site statique
  • Le contenu dynamique (formulaire de contact / commentaire) est impossible :
    • Pour les commentaires, c’est possible avec des commentaires chargés en Javascript, typiquement HashOver Next (alternative auto-hébergable à DISQUS). J’ai fait un script de migration WordPress > HashOver : wp2hashover
    • Pour les commentaires, il est possible d’utiliser des formulaires en iframe ou formulaire javascript/ajax

Transformer en (presque) site statique (W3 Total Cache)

La solution wp2static n’était pas complètement satisfaisante et j’ai continué mes recherches. En testant le plugin W3 Total Cache, je me suis aperçu qu’il faisait exactement ce que je cherchais à faire et même plus !

Après paramétrage, j’ai trouvé ceci dans le fichier .htaccess :

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
[...]
    RewriteCond %{REQUEST_METHOD} !=POST
[...]
    RewriteCond "%{DOCUMENT_ROOT}/wp-content/cache/page_enhanced/%{HTTP_HOST}/%{REQUEST_URI}/_index%{ENV:W3TC_SSL}%{ENV:W3TC_PREVIEW}.html%{ENV:W3TC_ENC}" -f
    RewriteRule .* "/wp-content/cache/page_enhanced/%{HTTP_HOST}/%{REQUEST_URI}/_index%{ENV:W3TC_SSL}%{ENV:W3TC_PREVIEW}.html%{ENV:W3TC_ENC}" [L]
</IfModule>

Pour résumer, ce que font les règles c’est :

  • Si la page HTML static a été générée (le fichier existe) dans /var/www/decroissant-au-beurre.com/web/wp-content/cache/page_enhanced/decroissant-au-beurre.com/services/_index_ssl.html (pour l’appel à https://decroissant-au-beurre.com/services/ alors on l’appelle directement (pas d’exécution de code PHP… ) SINON on appelle la page « normale » dans wordpress et celui-ci génère la page de cache /static pour le prochain visiteur
  • Dès qu’il y a une requête POST, le cache n’est pas appelé. Donc tous les formulaires restent compatibles avec le cache et seules les pages de résultats ne seront pas tirées du cache.

Ca veut dire qu’a performance égale avec la solution précédente, ce plugin fait mieux car il gomme les défauts précédents et permet de continuer d’utiliser des formulaires/les commentaires.

C’est parfait ! Le cache peut aussi être stocké via memcache et non sur disque (ce qui rend l’accès / l’affichage encore plus rapide (d’autres technologies possibles : APC / Xcache…)

Les fonctionnalités supplémentaires :

Minifier : diminuer trafic réseau

W3 Total Cache propose une fonctionnalité de Minification

minifier signifie réduire la taille du code. C’est un processus très utilisé en programmation web pour réduire la taille d’un programme à télécharger depuis un serveur et ainsi réduire l’encombrement du réseau. Cela peut aussi être considéré comme une forme d’offuscation du code.

Pour cela on supprime tous les commentaires et les espaces qui ne gêneront pas le bon fonctionnement de l’application. On remplace aussi le nom des variables interne à l’application pour les réduire à un seul ou deux caractères. Il est aussi possible d’utiliser certaines écritures compactes propres aux langages (couleur en hexadécimal, raccourcis…)

https://fr.wikipedia.org/wiki/Minification

Si on analyse le code après avoir activé la Minification on observe :

<!DOCTYPE html><html lang=fr-FR><head><script>window.w3tc_lazyload=1,window.lazyLoadOptions={elements_selector:".lazy",callback_loaded:function(t){var e;try{e=new CustomEvent([...]

Alors qu’un site non-minifié donne plutôt :

<!DOCTYPE html>
<html lang="fr-FR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
<link rel="stylesheet" id="extend-builder-css-css" href="assets/static/css/theme.css" type="text/css" media="all">
<style id="extend-builder-css-inline-css" type="text/css">
/* page css */
/* part css : theme */

.h-y-container > *:not(:last-child), .h-x-

Vous pouvez minifier tout ou partie du code (HTML/ CSS / Javascript…). Certains plugins peuvent ne plus fonctionner à cause de la minification. Il est donc important de tester votre site après avoir activé ces options.

Lazy Loading : Afficher les images au besoin

Si vous avez un site très long, il est possible que des images soient présentes en bas de page. Il est pertinent de ne charger ces pages qu’au besoin (que si vous allez jusqu’en bas de la page). Et bien la fonction Lazy Loading permet cela.

Cette fonctionnalité permet aussi un gain non négligeable de bande passante.

Mise en cache navigateur

Vous pouvez améliorer les entêtes navigateurs en spécifiant à celui-ci des dates d’expiration de fichier. De cette façon il ne va pas retourner charger la/les pages sur le serveur en cas de nouvelles visites.

Mesures performance

Voilà un stress test (envoi de requêtes massives) du même site avec ou sans génération en site statique (fait avec W3 total cache ici) :

On constate que c’est 20 fois plus rapide à l’affichage et que c’est plutôt constant. Est-ce que c’est 20 fois moins énergivore ? ça c’est difficile à mesurer mais c’est forcément moins…

Voici ci-après le poids de chargement d’une même page sans optimisation et avec Lazy Loading + Minification

  • Sans optimisation : 21,5Mo
  • Avec Minification et Lazy : 3,9Mo

Soit 5,5 fois moins lourd…

Sécurité

Le minimum pour sécuriser un wordpress c’est :

  • Faire les mises à jour (de nombreux plugins existent pour automatiser cela)
  • Ne pas utiliser « admin » en nom d’utilisateur admin
  • Avoir des mots de passe forts
  • Bloquer les tentatives de brutes forces :

WordPress se fait pas mal attaquer du fait qu’il soit populaire. La principale attaque (hors SPAM sur les commentaires) est faite par brute-force sur la page wp-login.php et xmlrpc. J’ai consacré un article dédié pour intégrer les logs de WordPress dans Fail2ban, et ainsi pouvoir bloquer les tentatives au niveau firewall : WordPress & fail2ban : stopper la brute-force « POST /wp-login.php » (uniquement possible sur un hébergement dédié). C’est particulièrement efficace comme vous pouvez le voir sur ce graphique :

Graphique fail2ban sur 1 semaine

Gestion serveur mutualisé

Si vous gérez un serveur mutualisé, vous pouvez déployer le plugin de cache et faire les mise à jour de tous les wordpress installé sur votre serveur avec wp-cli-isp (taillé pour les serveurs sous ISPconfig, mais c’est adaptable)

Diminuer son impact numérique : comment envoyer des pièces jointes

Petit tuto vidéo pour vous expliquer comment envoyer des pièces jointes en limitant votre impact sur l’environnement. Il s’agit d’un service (open source) d’envoi de fichier avec expiration

Le service tourne avec un logiciel libre en PHP (sans base de donnée) que c’est « moi qui l’a fait » : file2link mais il y a beaucoup d’autres alternatives :

PvMonit 3.0 : Cloud + Programmation surplus d’énergie par Blockly

La version 3.0 de PvMonit vient de sortir ! Au programme service de Cloud et gestion simplifié de la programmation des relais pour gérer le surplus d’énergie via Blobkly.

PvMonit c’est un logiciel libre de monitoring de système électrique solaire autonome qui est capable de gérer le surplus d’énergie solaire. Pour en savoir plus c’est par ici

Un petit tour vidéo des nouvelles fonctionnalités :

Démonstration de l’interface blockly dans PvMonit c’est par ici.

Pour souscrire au service Cloud c’est par ici.

Programmer les ordres (surplus électrique)

La doc écrite est ici : https://framagit.org/kepon/PvMonit/-/blob/master/domo/relay.script.d/README.md et la doc vidéo c’est juste en dessous :

Installation / mise à jour

Pour l’installation de PvMonit le tuto se trouve dans le INSTALL.md du projet : https://framagit.org/kepon/PvMonit/-/blob/master/INSTALL.md

Pour les mises à jours, reportez vous au UPGRADE.md : https://framagit.org/kepon/PvMonit/-/blob/master/UPGRADE.md

Si vous n’avez pas les compétences (ou pour soutenir le projet) je propose le service d’installation sur mesure « clef en main » ou le téléchargement d’une image pour la carte SD prêt à l’emploi.

Vous pouvez soutenir le projet par ici.

PvMonit – Boîtier impression 3D

Un utilisateur de PvMonit (logiciel libre de monitoring photovoltaïque autonome et de gestion du surplus solaire) ayant pour alias Akoirium, à contribué à PvMonit en proposant des boîtier imprimable en 3D. Voilà ce que ça donne avant et après :

Pour le TM1638 (circuit de gauche)

Le circuit TM1638 est utilisé pour la gestion de la domotique (optimisation surplus solaire), il existait déjà des modèles de boîtier, Akoirium s’en ai donc inspiré :

  • https://www.thingiverse.com/thing:3578683
  • Un autre modèle : https://www.thingiverse.com/thing:2794902

Voici les fichiers sources sous licence GPL

Pour l’Adafruit 16×2 Character LCD

Pour l’adafruit 16×2, utilisé pour lire les informations de l’installation solaire, Là Akoirium c’est aussi inspiré de projet existant sur thingiverse. C’est pas parfait (il faut gratouillé un peu quand certaine soudures sont trop épaisses), mais c’est franchement pas mal. De mon côté il m’avait envoyé les boutons mais je les ai perdu donc j’ai coupé des visses de 2×20 pour faire les boutons n’ayant pas d’imprimante 3D, ça fait le taf 🙂

Voici les fichiers sources sous licence GPL

Merci encore à Akoirium pour cette contribution !