Introduction

Pour gérer un agenda, il existe un standard depuis maintenant une dizaine d'années. Il s'agit du protocole CalDAV qui est normalisé par la RFC 4791. Techniquement, ce protocole n'est qu'une surcouche au protocole WebDAV qui permet "l'écriture" depuis un client HTTP vers un serveur HTTP (implémentation de la méthode PUT). Les fichiers qu'on peut écrire peuvent prendre différents formats et pour CalDAV, on stocke et on renvoie uniquement des fichiers ICS.

Il existe de nombreuses implémentations de serveur CalDAV. La plus connue et la plus aboutie étant sans doute Davical qui est l'implémentation de référence. C'est plutôt un logiciel fait pour héberger plusieurs milliers de comptes CalDAV et son utilisation implique d'employer PostgreSQL pour tout ce qui concerne le stockage des données. Mais mettre en place Davical requière pas mal de compétences d'administration système, en plus d'avoir un niveau correct en matière d'administration de base de données. Pourtant, CalDAV se prète assez aux utilisations légères. En effet, tout passe par HTTP et un évènement est généralement une donnée de quelques milliers d'octets au plus.

Fort heureusement, il existe des implémentations, certes moins complètes, mais qui ont l'intérêt de s'installer relativement facilement. Je peux citer ici les serveurs Radicale et Baikal

Dans cet article, nous allons étudier comment mettre en place un service CalDAV basé sur Baikal 0.2.7 au sein de l'environnement Debian Wheezy (je sais, Jessie va bientôt sortir mais pour la production, on repose encore pour quelques semaines/mois sur Wheezy, la distribution stable de Debian). Cette mise en place se déroulera selon quelques règles de sécurité et quelques paramétrages personnels.

Enfin, sachez que cet article n'a pas vocation à être une documentation de référence. Je n'ai pas la prétention d'être un expert du sujet. Néanmoins, les éléments présentés permettront de donner une approche plus concrète d'une installation de ce service.

A propos de Baikal et de notre cahier des charges

Commençons par les choses qui fachent: Baikal est codé en PHP ! Je sais, vous voudriez un truc plus hype (JS/Rails/Python) mais il va falloir faire avec. Il repose sur SabreDAV qui est un framework WebDAV en PHP. C'est le modèle de référence en PHP ce qui est donc plutôt une bonne chose pour Baikal.

En plus de toute l'architecture liée à la gestion de CalDAV/CardDAV, baikal propose une interface d'administration web (basée sur PHP) qui permet de le configurer assez simplement. Néanmoins, cette interface ne permet pas de consulter son agenda avec un navigateur web. Ce sera le cas pour la version 2.0 de baikal qui est actuellement en version release-candidate mais qui présente encore de nombreuses failles qui le rende non compatible avec de la production informatique.

Nous allons donc utiliser baikal dans une version 0.2.7 et voici donc notre cahier des charges:

  • Nous voulons un service CalDAV accessible depuis Internet.
  • Nous voulons un service CardDAV accessible depuis Internet.
  • Le service CalDAV ne sera accessible que via un canal de communication chiffré.
  • L'accès au service CalDAV devra reposer sur un mécanisme d'authentification éprouvé.
  • J'ai pour habitude de déléguer assez de confiance dans le module d'authentification d'Apache. C'est ce dernier qui sera utilisé comme pour toutes les applications PHP hébergées sur mon serveur auto-hébergé.
  • Le mécanisme d'authentification HTTP utilise des comptes dédiés définis dans un fichier texte et lisible par Apache.
  • L'installation devra pouvoir être réalisée sous Debian Wheezy.
  • L'interface administrateur ne sera pas disponible après l'installation.
  • Apache sera le serveur Web en entrée et le service sera accessible via un port 443 ouvert (et non un port dédié).
  • Le service sera accessible via une URL dédiée sur le serveur HTTP (via un alias).
  • Nous allons utiliser un stockage des données au format SQLite.
  • Nous n'allons pas utiliser les mécanismes de stockage de Debian pour le stockage de la base de données.
  • Le service doit être relativement modeste et ne demander que peu de ressources serveur.
  • La cible d'utilisation est d'environ 2 à 5 utilisateurs simultanés.
  • Le service CalDAV devra pouvoir être accessible aux clients CalDAV de Firefox OS. Ces derniers ne gèrent qu'une authentification de type Basic (et non Digest).
  • Comme le service sera accessible sur Internet, il faudra prendre des mesures pour empêcher les attaques d'authentification par la force brute.

Nos besoins sont donc assez limités mais ils recommandent quand même un certain niveau de sécurité.

Il faut bien noter, avant de commencer l'installation que les méthodes d'authentification de Baikal 0.2.7 sont assez légères: par défaut, elles reposent sur une authentification indépendante où le mot de passe est stocké (le hash uniquement) dans la base de données de Baikal (qui peut être au format SQLite ou MySQL). Or, ce n'est pas ce que nous voulons !

En effet, PHP est connu pour être une porte d'entrée assez ouverte en cas d'attaque publique, il n'y a qu'à voir toutes les attaques portées sur les logiciels PHP un peu connus (comme Wordpress par exemple) pour voir que tôt ou tard, le système sera cracké. Un bon frein à toutes ces attaques et ces failles, c'est de placer l'accès à ces services PHP derrière une authentification HTTP. Ce mécanisme d'authentification est géré par le serveur Web lui-même et comme il ne fait que ça, il y a assez peu d'attaques qui fonctionnent dessus. De plus, les attaques sont facilement repérables dans les logs du serveur Web ce qui n'est pas du tout le cas pour Baikal qui ne remonte même pas les erreurs d'authentification (SabreDAV non plus d'ailleurs, sauf dans des versions plus modernes).

Mais utiliser un mécanisme d'authentification HTTP va poser des problèmes à Baikal: en effet, rien n'est prévu à ce niveau pour gérer autre chose que le mécanisme d'authentification sur la base de données. Il faudra donc modifier le code de Baikal. Je vous rassure, c'est très facile, il suffit de modifier deux lignes !

Configuration du service CalDAV de baikal sous Debian Wheezy

Installation du paquet

Baikal n'existe pas sous forme de paquet pour Debian. Il faut donc télécharger le code, réaliser la configuration et créer la configuration Apache.

Pour notre cas, et pour respecter la FHS ainsi que la charte Debian, et nous allons tout mettre dans /opt/baikal/.

Néanmoins, Baikal impose d'installer quelques paquets au minimum pour fonctionner: php5, sqlite3 pour le backup des bases SQLite.

# aptitude install php5 php5-sqlite sqlite3
# cd /opt/
# wget http://baikal-server.com/get/baikal-regular-0.2.7.tgz
# tar -xzf baikal-regular-0.2.7.tgz
# mv baikal-regular baikal

Ensuite, nous devons appliquer un patch pour gérer l'authentification HTTP pour CalDAV et CardDAV. Voici le contenu de baikal-httpauth.patch:

--- baikal/Core/Frameworks/Baikal/WWWRoot/cal.php  2014-02-03 21:46:11.000000000 +0100
+++ baikal-httpauth/Core/Frameworks/Baikal/WWWRoot/cal.php    2015-03-27 16:48:30.430385115 +0100
@@ -74,7 +74,11 @@
 $server->setBaseUri(BAIKAL_CAL_BASEURI);

 # Server Plugins
-$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
+## Setting Apache HTTP Authentication
+$apacheBackend = new \Sabre\DAV\Auth\Backend\Apache();
+$server->addPlugin(new \Sabre\DAV\Auth\Plugin($apacheBackend, BAIKAL_AUTH_REALM));
+
+#$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
 $server->addPlugin(new \Sabre\DAVACL\Plugin());
 $server->addPlugin(new \Sabre\CalDAV\Plugin());

--- baikal/Core/Frameworks/Baikal/WWWRoot/card.php 2014-02-03 21:46:11.000000000 +0100
+++ baikal-httpauth/Core/Frameworks/Baikal/WWWRoot/card.php       2015-03-27 16:52:29.366387339 +0100
@@ -71,7 +71,11 @@
 $server->setBaseUri(BAIKAL_CARD_BASEURI);

 # Plugins 
-$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
+## Setting Apache HTTP Authentication
+$apacheBackend = new \Sabre\DAV\Auth\Backend\Apache();
+$server->addPlugin(new \Sabre\DAV\Auth\Plugin($apacheBackend, BAIKAL_AUTH_REALM));
+
+#$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
 $server->addPlugin(new \Sabre\CardDAV\Plugin());
 $server->addPlugin(new \Sabre\DAVACL\Plugin());

Il reste maintenant à appliquer les patchs aux bons fichiers et à gérer les droits d'accès:

# cd /opt/
# patch -p0 < baikal-httpauth.patch
# chown -R www-data:www-data /opt/baikal

Maintenant, nous pouvons enchaîner avec la configuration d'Apache.

Modification de la configuration d'Apache

La documentation officielle de Baïkal recommande l'utilisation d'un Virtualhost dédié (sous la forme dav.mydomain.com). Pour ma part, ce n'est pas ce que je souhaite. En effet, créer un sous-domaine dédié est un truc hasbeen ! Si si, les trucs qui commencent par www. quelquechose en 2015 ont peu d'intérêt. Si je tape une URL dans un navigateur web, c'est bien pour accéder au service web qui se fait sur un port dédié (le 80 ou le 443). Pas besoin de préciser quel service je veux. Pourquoi pas un mail.mydomain.com, un imap.mydomain.com, un pendant qu'on y est ? Pour ma part, il faut que le service soit disponible sur une URL du type: http://mydomain.com/calendar/. Pour y parvenir, nous allons donc mettre en place un Alias sous Apache.

Il faut également retenir que Baïkal recourre fortement aux fichiers d'Override (.htaccess). Cette gestion n'est pas terrible. En effet, sur un serveur avec des ressources limitées, il vaut mieux ne pas avoir de fichier .htaccess qui doivent être analysés à chaque ouverture de page. De plus, pour ma part, je préferre les configurations centralisées: tout dans le minimum de fichiers. Ça permet de retenir simplement dans quel fichier va se trouver le problème et de plus, tout est disponible dans un simple éditeur de texte, directement sous la main, sans avoir à faire de grep pour trouver dans quel fichier on a foutu la conf de telle partie du site web.

Bien sûr, vous me direz: "Mais comment je fais pour désactiver uniquement le service CalDAV et pas le reste ?". C'est simple, tu lis l'unique fichier de conf et tu mets à jour les lignes concernées ! C'est plus long que de taper a2dissite baikal certes mais ça te permet de te plonger dans la conf globale d'Apache. En plus, on peut trouver des solutions moins élégantes pour gérer un Alias dans un fichier dédié.

Pour la partie TLS, je vais considérer que vous mettez à jour un fichier de configuration d'Apache qui gère déjà ça !

Voici la configuration d'Apache à ajouter dans votre fichier de configuration centralisé, dans un virtualhost qui gère TLS !

        # Gestion de Baikal Caldav Server
        Alias /caldav /opt/baikal/html
        <Directory /opt/baikal/html>
          # Au cas où, on impose d'être dans un truc chiffré.
      # Si vous avez bien configuré Apache, vous ne devriez pas en avoir besoin
      SSLRequireSSL
          Options -Indexes FollowSymLinks
          AllowOverride All
          Order allow,deny
          Allow from all
          AuthType Basic
          AuthName "Baikal authentication"
          AuthUserFile /etc/apache2/webdav-users
          Require valid-user
        </Directory>

Pour la gestion des comptes, il vous faut le fameux fichier /etc/apache2/webdav-users. Vous pouvez le produire en utilisant htpasswd et surtout, en lisant la documentation officielle d'Apache 2.2 sur le sujet...

N'oubliez pas de relancer le service pour prendre en compte la configuration:

# service apache2 restart

Configuration

La configuration de Baikal s'effectue en ligne en utilisant un questionnaire accessible directement via le serveur HTTP sous forme de page HTML dédiée. Je déteste ce mode d'installation car il ne me semble pas sécurisé et qu'en plus, il faut que le développeur du logiciel code un "Wizard" d'installation. En plus, cette méthode brouille les cartes de l'administrateur système. En effet, rien ne vaut la création du fichier de configuration directement à la main: ça permet d'abord de s'en souvenir pour plus tard. Ça permet également de documenter la manière de faire. La méthode "Wizard" doit documenter en plus, l'activation du mode Wizard ainsi que présenter les différents éléments de configuration de l'interface d'installation.

Pour accéder à l'interface d'administration, vous devez d'abord créer un fichier spécifique dans l'arborescence de l'installation:

# touch /opt/baikal/Specific/ENABLE_INSTALL

Ensuite, rendez-vous sur l'URL d'installation: https://votreserveur.votredomaine/caldav/admin/

Une fois sur la page renseignez les valeurs adaptées. Voici celles que j'ai adopté en fonction du cahier des charges sus-cité:

  • Server TimeZone: Europe/Paris
  • Enable CalDAV: True
  • Enable CardDAV: True
  • WebDAV authentication type: Basic (en fait ça n'a aucune importance)
  • Admin password: mettez un mot de passe dédié, son hash est stocké dans le fichier de configuration.

La deuxième page vous demande où stocker le fichier de base de données SQLite. Pour ma part, je place toutes les DB dans un répertoire spécifique de mon serveur: /var/local/db. L'emplacement sera donc /var/local/db/baikal.sqlite.

Ensuite, vous devez copier le fichier de base de données au bon endroit (Baikal ne le fait pas pour vous, cette feignasse !):

# cp /opt/baikal/Core/Resources/Db/SQLite/db.sqlite /var/local/db/baikal.sqlite
# chown www-data:www-data /var/local/db/baikal.sqlite

Vous pouvez continuer l'installation en retournant dans la page d'administration. Vous devez aller modifier des variables dans la page intitulée "System settings". Pour que votre installation fonctionne correctement, il reste un dernier facteur à modifier. En effet, je vous avais dit que Baikal se basait sur une installation par VirtualHost. Or, ce n'est pas ce que nous avons configuré. Il faut donc modifier encore un paramètre de configuration pour prendre en compte cet élément. Ce paramètre est l'URI de base des services CalDAV et CardDAV. (CalDAV base URI et CardDAV base URI). Les valeurs doivent refléter l'URI d'accès à notre service soit:

  • CalDAV base URI: "/caldav/cal.php"
  • CardDAV base URI: "/caldav/card.php"

Pour la gestion des comptes, nous allons nous appuyer sur l'interface administrateur. En effet, c'est un moyen simple de créer des comptes en initialisant les éléments dans la base de données. Vous devez faire en sorte que les identifiants des comptes soient identiques aux comptes que vous avez créé dans le fichier /etc/apache2/webdav-users. Pour le mot de passe, vous pouvez mettre le même mot de passe que pour Apache mais ce n'est pas obligatoire. En effet, le patch que nous avons appliqué permet d'accéder aux données en utilisant simplement l'identifiant du compte, Baikal ne vérifie pas le mot de passe.

Une fois que les créations de comptes sont effectuées, il faut désactiver l'accès administrateur. On peut le faire depuis l'interface d'administration mais vous pouvez simplement modifier le contenu de /opt/baikal/Specific/config.php avec ce qui suit (le hash pour le mot de passe administrateur sera forcément différent chez vous):

# Timezone of your users, if unsure, check http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
define("PROJECT_TIMEZONE", 'Europe/Paris');

# CardDAV ON/OFF switch; default TRUE
define("BAIKAL_CARD_ENABLED", TRUE);

# CalDAV ON/OFF switch; default TRUE
define("BAIKAL_CAL_ENABLED", TRUE);

# WebDAV authentication type; default Digest
define("BAIKAL_DAV_AUTH_TYPE", 'Basic');

# Baïkal Web Admin ON/OFF switch; default TRUE
define("BAIKAL_ADMIN_ENABLED", FALSE);

# Baïkal Web Admin autolock ON/OFF switch; default FALSE
define("BAIKAL_ADMIN_AUTOLOCKENABLED", TRUE);

# Baïkal Web admin password hash; Set via Baïkal Web Admin
define("BAIKAL_ADMIN_PASSWORDHASH", '76dee1f1b9bc3e6de0ab63eb706a4ae7');

Pour mémoire, voici le contenu de /opt/baikal/Specific/config.system.php:

# PATH to SabreDAV
define("BAIKAL_PATH_SABREDAV", PROJECT_PATH_FRAMEWORKS . "SabreDAV/lib/Sabre/");

# If you change this value, you'll have to re-generate passwords for all your users
define("BAIKAL_AUTH_REALM", 'BaikalDAV');

# Should begin and end with a "/"
define("BAIKAL_CARD_BASEURI", "/caldav/card.php/");

# Should begin and end with a "/"
define("BAIKAL_CAL_BASEURI", "/caldav/cal.php/");

# Define path to Baïkal Database SQLite file
define("PROJECT_SQLITE_FILE", "/var/local/db/baikal.sqlite");

# MySQL > Use MySQL instead of SQLite ?
define("PROJECT_DB_MYSQL", FALSE);

# MySQL > Host, including ':portnumber' if port is not the default one (3306)
define("PROJECT_DB_MYSQL_HOST", '');

# MySQL > Database name
define("PROJECT_DB_MYSQL_DBNAME", '');

# MySQL > Username
define("PROJECT_DB_MYSQL_USERNAME", '');

# MySQL > Password
define("PROJECT_DB_MYSQL_PASSWORD", '');

# A random 32 bytes key that will be used to encrypt data
define("BAIKAL_ENCRYPTION_KEY", 'a178a328f480c55bff702e60a3579c93');

# The currently configured Baïkal version
define("BAIKAL_CONFIGURED_VERSION", '0.2.7');

On y retrouve bien nos URI ainsi que l'emplacement de la base de données.

Sécurisation de l'authentification

Je ne vais pas revenir sur ce sujet plus en détails, il suffit de lire le cahier des charges et la partie sur l'installation de baikal pour voir comment faire pour donner l'accès uniquement aux client autorisés par une authentification HTTP.

Mais, il nous faut également lutter contre les attaques d'authentification par la force brute. Pour ce point précis, j'utilise Fail2ban qui s'en sort plutôt pas mal même s'il présente de vrais problèmes (pas de support IPv6 notamment).

Voici le fichier de règles fail2ban à appliquer. Il se nomme /etc/fail2ban/filter.d/apache-auth.conf et il c'est celui par défaut du paquet fail2ban de Debian.

# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
# $Revision$
#

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = apache-common.conf

[Definition]

# Option:  failregex
# Notes.:  regex to match the password failure messages in the logfile. The
#          host must be matched by a group named "host". The tag "<HOST>" can
#          be used for standard IP/hostname matching and is only an alias for
#          (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values:  TEXT
#
failregex = ^%(_apache_error_client)s user .* (authentication failure|not found|password mismatch)\s*$

# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
ignoreregex =

On voit bien que tout problème sur l'authentification HTTP Apache sera pris en compte, que ce soit par rapport à un utilisateur non existant ou un problème de mot de passe. Ce filtre est générique pour l'authentification Apache, il ne cible pas le service CalDAV en particulier. On pourrait créer un fichier dédié en ajoutant le motif de recherche de l'URL de votre service CalDAV mais pour ma part, je pense qu'il vaut mieux ne pas créer un énième fichier de filtre si peu différent de l'original (pour ne pas compliquer la maintenance).

Et voici maintenant comment appliquer la surveillance en se basant sur le filtre précédent, à mettre dans le fichier /etc/fail2ban/jail.local:

[apache-http-auth]
enabled  = true
port     = http,https
filter   = apache-auth
logpath  = /var/log/apache2/error.log
maxretry = 3
action   = %(action_mwl)s

Pour ma part, l'action action_mwl envoie un email avec les lignes de logs incriminées.

Sauvegarde et restauration

Dans la sauvegarde, il faut prévoir beaucoup de choses. D'abord, il faut gérer la configuration de l'application. Pour notre cas, elle se trouve dans /opt/baikal/Specific/. Mais vu que Baïkal occupe peu de volume, on peut se retrancher assez facilement vers la sauvegarde de l'ensemble du répertoire /opt/baikal/.

La configuration Apache sera sauvegardée en copiant le fichier de configuration.

Il reste les bases de données SQLite. On pourrait être tenté de faire une copie directe des fichiers, après tout, SQLite n'est jamais qu'un fichier. Mais ce n'est pas la bonne manière de le faire. Il faut utiliser la méthode de backup online. Pour se faire, un simple script Bash armé de l'utilitaire sqlite3 fait l'affaire:

    # sqlite3 /var/local/db/baikal.sqlite ".backup /var/local/db/backup/baikal.sqlite"

Bien entendu, étant donné que j'ai plusieurs bases de données SQLite sur cette machine, je dispose du script suivant qui gère toute la chaîne de sauvegarde (/usr/local/bin/sqlite_backup.sh):

#!/bin/bash

# Script to correctly backup SQLite3 databases
# Copyright 2015, Médéric RIBREUX <mederic.ribreux@medspx.fr>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.This script is in GPLv3

# Some variables
SQLITEBIN="/usr/bin/sqlite3"
CONFFILE="/etc/sqlite_backup.conf"

# Verify we have everything
CMDS="sqlite3 awk"
for i in $CMDS
do
  command -v $i >/dev/null && continue || { echo "You need to install $i to launch this script..."; exit 1; }
done

# Verify if config file exists
if [ ! -f "$CONFFILE" ]; then
    echo "Configuration file cannot be found !"
    exit 2
fi

# Verify if SQLite3 exists
if [ ! -f "$SQLITEBIN" ]; then
    echo "SQLite3 is not the right path to sqlite3 !"
    exit 2
fi

# Parse config file and do main loop
ERRORCODE=0
while read v; do
    case $v in
        '#'*) continue;;
        *) src=${v%% *}
       dst=${v#* }
       $SQLITEBIN ${src} ".backup ${dst}"
       RETURNVAL=$?
       if [ "$RETURNVAL" -gt "0" ]; then
            ERRORCODE=$RETURNVAL
       fi
       ;;
    esac
done < "$CONFFILE"

# End
exit "$ERRORCODE"

Le script va puiser dans un fichier de configuration la liste des bases de données à sauvegarder. Ce fichier se nomme /etc/sqlite_bakcup.conf. Voici un exemple de contenu:

# SQLite Databases to backup
/var/local/db/baikal.sqlite /var/local/db/backup/baikal.sqlite

Pour automatiser la sauvegarde (ce qui est indispensable: sauvegarde non automatisée = sauvegarde non faite), on peut ajouter une crontab. Pour ma part, je la place dans la crontab du système, c'est-à-dire: /etc/crontab:

# SQLite backups on every saturdays at 9am
0 9 * * 6   root    /usr/local/bin/sqlite_backup.sh

Configuration pour des clients Firefox OS

J'ai un smartphone sous Firefox OS 2.0 (un geeksphone Revolution). Il peut se connecter à un serveur CalDAV. Mais comme tous les smartphones, il est forcément limité. Dans notre cas, il ne gère pas l'authentification en mode Digest.

Désactiver l'authentification Digest et utiliser l'authentification Basic. Comme nous travaillons dans une session TLS, le mot de passe sera envoyé de manière chiffrée. Comme d'habitude, le mode Digest n'est pas bien géré par les clients Web et c'est bien dommage.

Pour se connecter, il suffit d'utiliser l'URL suivante: https://votreserveur.votredomaine/caldav/cal.php/calendars/identifiant_utilisateur/default/

Attention, le client CalDAV de FirefoxOS est assez basique: il ne gère pas la synchronisation avec le mode offline. Donc pour créer un évènement sur le serveur Baikal, il faut absolument pouvoir s'y connecter. Espérons qu'avec le temps on puisse transférer facilement des évènements du calendrier local vers un calendrier en ligne...

Conclusion

Baikal permet de monter un service CalDAV/CardDAV de manière assez simple en très peu de temps. Ses besoins en termes de performances permettent de le faire tourner sur une configuration légère telle que celles qu'on peut retrouver sur les plugcomputers ou sur les cartes SOC embarquées comme le Raspberry Pi ou ses nombreux clones plus performants.

Nous avons vu que l'aspect sécurité ne doit pas être négligé et qu'il implique de modifier très légèrement le source de Baikal. Muni d'une authentification HTTP effectuée par le serveur Web, on peut espérer qu'il soit plus résistant aux attaques venus d'Internet et qui sont fortement susceptibles de se produire.

Dans les faits, il est donc possible de bénéficier d'un service d'agenda en ligne sans recourrir au cloud ! En plus, ce serveur CalDAV fonctionne bien avec FirefoxOS, ce qui est, en 2015, plutôt une bonne nouvelle.

Posted mar. 17 mars 2015 20:15:14

Introduction

Parfois, on veut balancer des gros fichiers (des vidéos, des archives PDF, de la musique, tout une collection de photos) d'un ordinateur à un autre. Le faire avec une clef USB ou même un disque USB prend du temps: il faut d'abord réaliser un transfert sur le périphérique intermédiaire et seulement après, on peut faire la deuxième copie. De plus, il faut avoir à disposition un câble USB et peut-être aussi une alimentation pour le disque USB intermédiaire.

Voici la méthode que j'utilise qui est finalement assez simple à mettre en oeuvre dans le cadre de ma configuration. D'abord, la majorité des machines que j'administre à la maison sont connectées au réseau local par du Wifi. De facto, elles disposent toutes d'un port Ethernet qui est le plus souvent inutilisé !

Information essentielle: les cartes réseaux de maintenant savent très bien se passer des fameux câbles Ethernet croisés qu'il fallait auparavant avoir à disposition. Donc, de facto, vous avez un connecteur Gigabit à votre disposition. Gigabit signifie que vous pouvez transférer des fichiers aux alentours de 85-90 Mo/s. Un film 720p encodé en H264 ou Webm d'environ 1,2 Go mettra donc 15 secondes pour être transféré !

Voyons comment faire sous Debian...

Configurations réseau ethernet

Le principe est simple: nous allons créer un sous-réseau manuel (192.168.1.0/24) et configurer chaque carte pour utiliser ce sous-réseau.

Voici ce que donne une partie du fichier /etc/network/interfaces pour la carte eth0 d'une machine:

# Configuration Ethernet pour la copie rapide
allow-hotplug eth0

iface eth0 inet static
      address 192.168.1.x
      netmask 255.255.255.0
      network 192.168.1.0
      broadcast 192.168.1.255

Vous pouvez également créer un fichier dédié à cette interface avec le contenu sus-cité et le placer dans /etc/network/interfaces.d/.

Il faut remplacer x par un numéro unique pour chaque machine. Pour ne pas me tromper, je prends le numéro terminal de l'adresse IP attribuée par DHCP, ce qui me permet de ne pas avoir de doublon, quoiqu'il arrive.

Configuration automatique de l'interface réseau

Le terme allow-hotplug indique que la carte réseau sera configurée automatiquement lorsqu'un câble réseau sera branché. Mais ce n'est pas le cas si vous n'avez pas installé et configuré le paquet ifplugd. Ce dernier assure le travail du "hotplug". Vous devez donc l'installer et également le configurer à minima.

Tout ce que vous avez à faire, c'est de modifier le fichier /etc/default/ifplugd pour indiquer quelles interfaces surveiller. Voici un exemple de configuration:

# This file may be changed either manually or by running dpkg-reconfigure.
#
# N.B.: dpkg-reconfigure deletes everything from this file except for
# the assignments to variables INTERFACES, HOTPLUG_INTERFACES, ARGS and
# SUSPEND_ACTION.  When run it uses the current values of those variables
# as their default values, thus preserving the administrator's changes.
#
# This file is sourced by both the init script /etc/init.d/ifplugd and
# the udev script /lib/udev/ifplugd.agent to give default values.
# The init script starts ifplugd for all interfaces listed in
# INTERFACES, and the udev script starts ifplugd for all interfaces
# listed in HOTPLUG_INTERFACES. The special value all starts one
# ifplugd for all interfaces being present.
INTERFACES="eth0"
HOTPLUG_INTERFACES="eth0"
ARGS="-q -f -u0 -d10 -w -I"
SUSPEND_ACTION="stop"

J'ai juste renseigné eth0 dans les variables INTERFACES et HOTPLUG_INTERFACES. Après avoir relancé le service, cette configuration devrait fonctionner. A noter que lorsque vous débranchez la prise, l'interface réseau reste active (sans IP forcément).

Faire la copie

Une fois la configuration réalisée, il suffit de brancher le câble réseau entre les deux machines. L'autoplug permet d'activer les cartes toutes seules.

Ensuite, scp fera le reste:

$ scp ip_machine_distante:/emplacement/des/fichiers/a/copier /repertoire/vers_où/copier

Même si SSH chiffre la communication, on a des vitesses de transfert de l'ordre de 90Mo/s...

Conclusion

Avec juste un câble ethernet, on a de quoi copier rapidement de grandes quantités de fichiers avec une configuration minimaliste...

Je ne sais pas si des câbles USB de machine à machine existent mais ça serait une bonne idée !

Posted mar. 24 mars 2015 15:10:12

Introduction

Le DNS, c'est la base de tout Internet aujourd'hui. On ne s'en rend même plus compte mais sans DNS, pas d'Internet facile à utiliser, pas de noms sur le réseau, juste des adresses IP à taper à la main. Si écrire une adresse IPv4 reste possible, il n'en est pas de même pour une adresse IPv6 ! Le DNS est donc obligatoire. Pour ceux qui l'ignorent, le Domain Name System est une méthode permettant de faire le lien entre un nom de machine sous forme quasi-humaine en adresse IP, utilisable par un système d'exploitation. La forme quasi-humaine est une syntaxe de parcours d'arbre. Chaque branche possède un nom et le délimiteur est le caractère point ('.').

Ainsi, medspx.homenet.org indique qu'on veut accéder à la machine nommée medspx, située dans le domaine homenet, lui-même situé dans le domaine général org. Je vous invite à lire la page d'introduction de Wikipedia sur le sujet pour une introduction plus complète.

Cet article tentera de montrer comment monter son propre serveur DNS en auto-hébergement pour permettre de nommer des machines sur le réseau local ainsi que pour servir de résolveur DNS global. Au cours de ces étapes, il nous faudra faire une petite introduction sur le fonctionnement du DNS. Pour la mise en oeuvre, nous allons utiliser le vénérable serveur Bind dans la configuration de Debian Wheezy. Il existe d'autres serveurs avec des fonctionnalités différentes mais Bind reste l'implémentation de référence (pas forcément la plus performante) et vous trouverez pléthore de documentation sur son utilisation.

Avant de commencer, sachez que cet article n'a pas vocation à servir de référence. Il illustre juste la mise en place d'une configuration adaptée à un réseau local avec des règles de sécurité bien définies.

Les différents modes du DNS

Pour notre cas d'illustration, nous allons étudier deux modes de fonctionnement du DNS:

  • le serveur faisant autorité sur un domaine qui nous appartient.
  • le serveur faisant office de résolveur centralisé.

Ces deux modes sont bien différents. Commençons par le plus simple à expliquer: le serveur faisant autorité. Comme son nom l'indique, un serveur qui fait autorité est un serveur capable de faire la correspondance entre des noms complets (FQDN) et des adresses IP. Le terme d'autorité indique que le serveur est la source de référence de la correspondance. En tant que référence, il peut donc être requêté par d'autres machines pour lui demander de faire la correspondance et de renvoyer l'adresse IP. Prenons le domaine example.com. Le serveur faisant autorité est la machine capable de donner l'adresse IP de la machine a.example.com.

Le système DNS est donc composé d'un tas de serveurs qui font autorité. La "base de données" des noms/adresses IP est donc distribuée sur ces serveurs faisant autorité. Mais, en plus d'être distribué, le DNS est également hiérarchique. En effet, il faut un moyen de contacter les serveurs faisant autorité. Ce moyen se situe au niveau des domaines supérieurs. Dans example.com, com est un domaine supérieur. Le serveur qui gère le domaine com (qui fait autorité sur le domaine) connaît donc l'adresse IP du serveur DNS faisant autorité pour le domaine example.com. C'est assez simple à gérer et on voit bien l'importance de la filiation pour la résolution de noms.

Lorsque vous avez un nom de domaine (example.com) et que vous avez un serveur faisant autorité dessus, cela signifie que vous pourrez définir autant de noms de machines situées sous ce domaine. Votre serveur DNS stockera ces informations de référence et les mettra à disposition des machines sur le réseau. Si vous êtes connectés à Internet, cela signifie que potentiellement, d'autres machines pourront vous poser des questions.

A l'inverse, un résolveur est un mode de fonctionnement bien particulier qui permet de regrouper les demandes de résolution de nom en un point central. Concrètement, chaque client du réseau va demander au résolveur de faire la requête DNS pour son compte.

Pour mieux comprendre le fonctionnement d'un résolveur, étudions rapidement ce qui se passe quand on fait une requête DNS complète. A la base, la machine doit disposer d'un minimum d'informations. Ces informations de base sont les adresses IP des serveurs d'autorité de la racine. La racine, c'est le point culminant du système mondial distribué du DNS. Les serveurs (car ils sont plusieurs) de la racine connaissent les adresses IP des domaines TLD (Top Level Domain) comme .com, .org, .net, etc. Ces serveur de TLD connaissent ensuite les correspondances pour les domaines du type linuxfr.org, homenet.org, duckduckgo.com, etc. La racine n'a pas de nom, c'est juste le caractère '.'. Une adresse DNS complète est du type 'medspx.homenet.org.'. Mais l'usage veut qu'on supprime le point terminal.

Donc le résolveur doit connaître les adresses IP de la racine. Ensuite, une résolution complète prend la forme qui suit:

  • le résolveur décompose le domaine requêté (ici www.medspx.homenet.org.) Il extrait le dernier domaine (.org).
  • Il interroge la racine pour connaître l'IP du serveur DNS qui fait autorité sur le domaine .org.
  • La racine lui donne une IP.
  • Le résolveur récupère l'IP et interroge le serveur DNS qui fait autorité sur .org. Il lui demande l'adresse du DNS de homenet.
  • le DNS .org répond et donne une IP.
  • Le résolveur récupère l'IP et interroge le serveur DNS qui fait autorité sur homenet. Il lui demande l'adresse du DNS de medspx.
  • Le DNS homenet répond et donne une IP.
  • Le résolveur demande l'adresse IP de la machine www au serveur DNS faisant autorité sur medspx.
  • medspx répond et donne l'IP.
  • La résolution est terminée, le résolveur renvoie la réponse au client qui a fait la requête.

Voilà comment fonctionne la résolution DNS. On le voit, à chaque fois, on interroge la source faisant autorité. Aucune machine ne fait autorité pour tout le DNS. La distribution de l'autorité permet de distribuer les données sur de nombreuses branches d'un arbre. L'intérêt est que lorsqu'une branche est coupée, l'arbre reste vivant et on peut continuer à interroger les autres branches (d'un niveau égal ou supérieur au domaine coupé). C'est donc un modèle relativement résilient.

L'exemple typique d'un résolveur DNS est le serveur DNS de votre FAI: il fait la requête DNS complète pour vous. L'intérêt du résolveur c'est qu'il peut mettre en cache les données. On peut imaginer qu'au sein du réseau d'un FAI, ou même à l'échelle d'un réseau local, de nombreux clients fassent la même requête. Genre, quelle est l'adresse IP de debian.org ou de google.fr ? Au lieu de faire la requête complète qui interroge la racine, le domaine TLD et le domaine final, on ne fait qu'un seul passage. C'est un peu plus performant.

Toutefois, sachez que les résolveurs DNS des FAI ne sont pas parfaits. D'abord, ils peuvent tomber en panne. Ça été le cas pour le FAI Free sur le réseau duquel j'ai pu mesurer plus d'une dizaine de fois en quelques années des coupures de service DNS pendant plus de 10 minutes. Ce qui se passe est assez sidérant: plus rien de ce qui peut aller sur le réseau ne fonctionne. Mais si vous connaissez une adresse IP, vous pouvez y accéder sans problème. Autre point plus inquiétant et plus en lien avec l'actualité, les résolveurs DNS de votre FAI peuvent mentir ! Ce comportement est loin d'être anodin. En effet, comme je l'avais dit en introduction, sans DNS, pas grand chose de ce qui doit aller sur le réseau ne fonctionne ! Si votre résolveur DNS dit qu'un nom de machine n'existe pas alors qu'il existe quand même, vous n'y avez tout simplement pas accès. Pire, le résolveur DNS peut indiquer une autre adresse IP et vous diriger vers une mauvaise machine.

Le mensonge DNS est généralement la technique utilisée pour censurer des sites web: le gouvernement décide qu'un site ne doit pas être consulté et indique aux FAI l'adresse (le FQDN) à filtrer. Les résolveurs DNS des FAI renvoient une fausse adresse IP à leurs clients. Le site web disparaît. Si vous avez votre propre résolveur ce n'est pas possible (sauf en faisant du DPI mais c'est un autre sujet).

N'importe qui peut néanmoins mettre en place un résolveur complet sur sa propre machine. Il suffit de connaître les adresses IP des serveurs de la racine (la liste est publique) et le tour est joué, avec le bon logiciel. Si vous avez un serveur chez vous, vous pouvez mettre en place un service de résolution centralisée. Ce dernier fonctionnera techniquement (à peu près) comme je l'ai indiqué et ne sera pas sujet à d'éventuels mensonges.

A propos des fichiers de zone

Les fichiers de zone sont les fichiers de données du serveur. En effet, comme tout programme démon sous Unix, un serveur DNS dispose de fichiers de configuration permettant de déterminer son comportement. Mais comme nous l'avons déjà vu auparavant, le DNS sert essentiellement à faire la correspondance entre des noms et des adresses IP et il faut bien que ces données soient stockées quelque part.

Ces données sont stockées dans des fichiers de zone qu'on nomme également "resource records" (RR). Ces fichiers ont un format bien particulier qu'il convient de présenter.

  • Dans ces fichiers, les commentaires sont précédés du caractère ';'
  • Les fichiers de zone peuvent contenir des directives et des enregistrements.
  • Les directives sont peu nombreuses ($TTL, $INCLUDE, $ORIGIN).
  • Chaque enregistrement du fichier contient 5 champs séparés par un espace ou un TAB.
    • Le premier champ indique le nom du domaine ou le nom d'une machine. Il répond à trois règles:
    • si le nom est absent, on prend le nom de l'enregistrement précédent
    • le nom doit commencer au premier caractère de la ligne.
    • si le nom n'est pas un nom complet FQDN, on utilise le nom de la zone pour faire un FQDN
    • un nom FQDN complet comprend également la racine. Voilà pourquoi on ajoute toujours un point à la fin des adresses.
    • le second champ correspond à la validité de l'enregistrement plus communément dénommée Time To Live (TTL).
    • le troisième champ indique le protocole utilisé. Sur ce point, seul le protocole IN (Internet) subsiste.
    • le quatrième champ indique le type d'information de l'enregistrement. Vous pouvez trouver une liste de ces types sur cette page.
    • Enfin le dernier champ contient les données qui peuvent être réparties sur plusieurs lignes avec les délimiteurs '(' et ')'.

Avec ces 5 champs, on peut définir de nombreuses choses.

Prenons un premier exemple qui nous permettra de présenter différents types d'information (le quatrième champ):

server1        IN      A   192.168.0.2

Dans cet exemple:

  • le premier champ vaut server1, il s'agit du nom de machine
  • pour le deuxième champ, il aurait fallu trouver un TTL mais il est souvent défini en amont via la directive $TTL. En règle générale, on ne met rien.
  • on trouve ensuite la valeur IN qui indique le protocole utilisé. C'est tout le temps le même et on peut omettre ce champ. Pour des raisons historiques (et pour ne pas oublier qu'il y a 5 champs), je le laisse.
  • le quatrième champ indique A pour "Address" et permet de dire que ce qui suit est l'adresse IP qui correspond au nom de la machine.
  • le cinquième champ contient 192.168.0.2 qui est la donnée de l'enregistrement qui correspond à l'IP de la machine server1.

Étudions maintenant le type CNAME:

server2                IN        CNAME        server1

Ici, nous définissons que la machine server2 sera un alias de server1. Toute résolution de nom de server2 renverra comme réponse que server2 est un alias de server1 (si on veut l'ip de server2, il faudra faire une requête DNS pour l'IP de server1).

Enfin, il existe un type bien particulier: le type SOA. Ce dernier permet d'indiquer qu'on définit un serveur faisant autorité (master). Étudions un exemple de déclaration SOA:

@   IN  SOA dns.example.org. admin.example.org. (
          201401071     ; Numéro de série du fichier de zone
             604800     ; Refresh
          86400     ; Retry
        2419200     ; Expire
         604800 )   ; Negative Cache TTL
  • Le premier champ qui vaut détermine le nom de la zone utilisée. le symbole @ indique que le nom de la zone est issu des fichiers de configuration du serveur bind (directive zone "nom_de_zone").
  • Le TTL n'est pas indiqué
  • On retrouve notre bon protocole IN.
  • Suivi du type d'enregistrement: SOA pour "Start of Authority"
  • Enfin, on trouve les données réparties de la manière suivante:
    • (
    • numéro de série du fichier de zone (on met souvent la date du jour+l'heure)
    • le temps de rafraichissement. c'est utilisé s'il existe un serveur esclave qui doit interroger régulièrement le serveur maître.
    • le temps entre deux essais de rafraichissement.
    • le temps d'expiration: si le serveur maître n'est pas disponible pour les serveurs DNS esclaves pendant une durée supérieure à ce temps, alors le serveur esclave cesse d'être serveur d'autorité.
    • TTL minimum: c'est le temps pendant lequel vous souhaitez que les données DNS restente en cache des résolveurs externes.
    • Les temps sont exprimés en secondes.

Notre cahier des charges

Comme d'habitude, avant de mettre en place un service, il faut étudier nos besoins.

  • Il nous faut un résolveur central pour les machines de notre réseau local.
  • Ce résolveur doit pouvoir mettre en place un système de cache pour éviter les requêtes incessantes vers l'extérieur.
  • Le service réseau du résolveur ne doit être accessible qu'au réseau local.
  • En plus du résolveur nous voulons pouvoir nommer des machines sur notre réseau local.
  • Le nom de domaine ne sera disponible que sur le réseau local.
  • Le nom de domaine n'aura de signification que pour le réseau local.
  • Le service réseau du serveur DNS faisant autorité au niveau local sera accessible uniquement aux machines du réseau local.
  • Les machines du réseau local configurées par DHCP devraient obtenir une configuration automatique vers le serveur DNS.
  • Le seul moyen de configurer le serveur sera via des fichiers configuration.
  • Le serveur sera unique.

Configuration du résolveur Bind sous Debian Wheezy

Installation du paquet

Le service Bind s'installe avec peu de paquets:

# aptitude install bind9

Répartition de la configuration

Sous Debian, la configuration de Bind se retrouve dans le répertoire /etc/bind/. Elle se répartit entre les fichiers de zones et les fichiers de configuration du serveur. Ces derniers sont distribués. Pour ma part, j'ai choisi de concentrer la configuration du serveur en un seul fichier nommé named.conf. Les fichiers de zone seront créés selon le code: un fichier de zone par zone !

Une fois que la configuration a été modifiée, il faut relancer le service pour la prendre en compte via:

# service bind9 restart

Configuration du serveur

Voici la configuration du serveur que j'héberge sur mon réseau local. Nous allons la commenter ensemble pour mieux la comprendre. Pour des raisons de simplification, j'ai concentré ma configuration dans un seul fichier qui reste modeste. Je procède toujours ainsi pour les configurations qui doivent rester simple car cela permet de ne pas chercher pendant des heures quel fichier modifier.

Vous pouvez bien sûr lire la documentation de référence de Bind pour connaître la signification et la syntaxe de chaque directive.

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the 
// structure of BIND configuration files in Debian, *BEFORE* you customize 
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

// On commence pas définir une acl qui permet de définir notre réseau local
// cette acl est nommée reseau_local et elle concerne le réseau 192.168.0.0/24, un réseau IPv4.
acl reseau_local { 192.168.0.0/24; };

// la directive controls permet d'indiquer comment configurer le démon
// de contrôle de Bind. Pour ma part, je ne souhaite pas que ce démon soit présent
// pour des questions de simplicité et de sécurité
controls { };


// Ici, on inclue les clefs de DNSSEC. Je ne vais pas revenir dessus car je pourrais en parler pendant des heures.
// Sachez juste que DNSSEC est une tentative de sécurisation du système DNS.
include "/etc/bind/bind.keys";

// Voici les options générales du service Bind.
options {
   // On lui indique d'écouter sur localhost (pour la résolution interne de la machine qui héberge le résolveur, eat your own dog food)
    // ainsi que sur l'ip de la machine qui écoute sur le réseau local.
   // Bien entendu, on écoute sur le port 53 car c'est la norme de base du DNS.
        listen-on { 127.0.0.1; 192.168.0.x; };
        port 53;
    // répertoire de travail du serveur
    directory "/var/cache/bind";

    // un minimum de configuration de DNSSEC
    dnssec-validation auto;

    // Le serveur 
    auth-nxdomain no;    # conform to RFC1035

    // on écoute sur le port localhost IPv6. Conclusion, le service DNS ne sera pas disponible par IPv6.
    listen-on-v6 { ::1; };

    // cette simple directive permet de dire qu'on souhaite mettre en place un résolveur.
   // Pas très compliqué !
   recursion yes;
};

// Configuration des journaux systèmes (les logs)
// Attention, cette configuration est en mode paranoïaque: tout est loggué, y compris les requêtes DNS des clients
// Les logs peuvent donc être très volumineux.
logging {
    // on commence à définir des "canaux" de logs: ce sont des définitions d'emplacement de fichiers
         channel security_warning {
         // Les avertissements de sécurité iront dans /var/log/bind/security.log
         // on garde 3 versions du fichier et on créé un nouveau fichier tous les 100ko
                 file "/var/log/bind/security.log" versions 3 size 100k;
                 severity warning;
                 print-severity  yes;
                 print-time      yes;
         };

         channel client_info {
                 file "/var/log/bind/requests.log" versions 2 size 10m;
                 severity info;
                 print-severity  yes;
                 print-time      yes;
         };

         channel bind_log {
                 file "/var/log/bind/bind.log" versions 3 size 1m;
                 severity info;
                 print-category  yes;
                 print-severity  yes;
                 print-time      yes;
         };

     // ensuite, on fait la répartition vers les fichiers en fonction de la catégorie
         category default { bind_log; };
     category client { client_info; };
     category resolver { client_info; };
     category queries { client_info; };
         category lame-servers { null; };
         category update { null; };
         category update-security { null; };
         category security { security_warning; }; 
};

// Définition des zones
// c'est notre domaine local
zone "ici" {
          // le serveur fait autorité, il est de type master.
          type master;
      // le fichier de zone est stocké dans /etc/bind/db.ici
      file "/etc/bind/db.ici";
      // localhost et les machines définies dans l'acl "reseau_local" (voir plus haut) peuvent faire des requêtes DNS sur ce serveur
       allow-query { localhost; reseau_local; };
       // On ne peut pas mettre à jour les données de ce serveur autrement que par la configuration.
       allow-update { none; };
};

// Cette zone permet de configurer la résolution DNS inverse (on donne une IP et on obtient le nom en échange).
zone "0.168.192.in-addr.arpa" {
        type master;
    file "/etc/bind/db.192.168.0";
    allow-query { localhost; reseau_local; };
    allow-update { none; };
};

// C'est ici qu'on indique le fichier contenant les IP des serveur racine.
// consultez ce fichier, vous verrez les adresses !
zone "." {
     type hint;
     file "/etc/bind/db.root";
};

// Cette "zone" permet aux clients de merde (MS-Windows) qui font des
// requêtes sur localhost, d'avoir une réponse censée (127.0.0.1 ou ::1)
// Dans le cas normal, un client normal ne fait PAS de demande de résolution de localhost
// Il sait par défaut que ça correspond à 127.0.0.1 ou ::1.
zone "localhost" {
     type master;
     file "/etc/bind/db.local";
};

// C'est la même chose que pour localhost mais en résolution inverse
zone "127.in-addr.arpa" {
     type master;
     file "/etc/bind/db.127";
};


zone "0.in-addr.arpa" {
     type master;
     file "/etc/bind/db.0";
};

zone "255.in-addr.arpa" {
     type master;
     file "/etc/bind/db.255";
};

// Include empty zones for RFC 1918 DNS queries (from fucked clients)
include "/etc/bind/zones.rfc1918";
};

On le voit, cette configuration est assez simple. Tout tient dans un seul fichier et nous avons peu de zones à servir. Intéressons-nous maintenant aux données des fichiers de zone.

Fichiers de zones

Voici quelques fichiers de zone précédemment référencés dans la configuration du serveur DNS. Je ne vais pas indiquer les fichiers de zone de base (racine, localhost, reverse localhost) mais bien les fichiers de notre configuration

/etc/bind/db.ici:

;
; Fichier de zone du réseau local ici.
;
; Le TTL global sera de 1 journée
$TTL    1d
; Notre premier enregistrement est un type SOA pour indiquer
; que le fichier gère un serveur faisant autorité.
; le nom du serveur de nom qui fait autorité est debianplug.ici.
; l'adresse email est du type email.nom_de_domaine (on remplace @ par un .).
@  IN  SOA serveur.ici. admin.ici. (
             201401071     ; Numéro de série du fichier de zone
                604800     ; Refresh
             86400     ; Retry
           2419200     ; Expire
            604800 )   ; TTL
; Indique que le nom du serveur faisant autorité pour la zone est serveur.ici.
; Si on a une configuration avec des esclaves, on peut mettre plusieurs enregistrements NS
@  IN  NS  serveur.ici.
; un enregistrement A (adresse IPv4) pour le nom "serveur"
serveur        IN  A   192.168.0.1
; un enregistrement AAAA (adresse IPv6) pour le nom "serveur"
serveur        IN  AAAA    2a21:2e35:8c57:24ef:bad:cafe:bad:caca
machinea   IN  A   192.168.0.2
machineb   IN  A   192.168.0.3
machinec   IN  A   192.168.0.4
machinec   IN  AAAA    2a21:2e35:8a57:24ef:babe:babe:babe:babe
; un alias de machined vers machinec
machined   IN  CNAME   machinec

Voici la zone inverse, stockée dans /etc/bind/db.192.168.0:

;
; Fichier de configuration de la zone DNS inverse locale
;
; Le TTL sera de 3 jours
$TTL 3d

; la zone est un peu spéciale. le "réseau inversé".in-addr.arpa.
; est une zone dédiée à la résolution inverse.
; le serveur d'autorité sera toujours notre serveur DNS.
0.168.192.in-addr.arpa.       IN      SOA     serveur.ici. admin.ici.     (
                  2014013101 ;Serial Number
                          8H ;refresh
                          2H ;retry
                          4W ;expire
                          1d)
; Le nom du serveur désservant la zone sera bien serveur.ici
@    IN    NS      serveur.ici.
; ensuite on trouve les enregistrements de type PTR (pointeur)
1         IN      PTR     serveur.ici.
2         IN      PTR     machinea.ici.
3         IN      PTR     machineb.ici.
4         IN      PTR     machinec.ici.

Sécurité

Une partie de la sécurité est gérée au niveau de fichier de configuration de Bind. En effet, on a indiqué qu'on souhaitait que le serveur soit uniquement accédé par les adresses IPv4 du réseau local 192.168.0.0/24. Néanmoins, il faut bien avoir à l'esprit que si Bind est une implémentation de référence, elle a un lourd historique de failles de sécurité.

Il serait donc vraiment indispensable de fermer le port 53 à toute machine non autorisée. Voici quelques règles pour iptables au format iptables-save:

-A INPUT -s 192.168.0.0/24 -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -s 192.168.0.0/24 -i eth0 -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m tcp --dport 53 -j ACCEPT

Et voici celles pour ip6tables:

-A INPUT -s 2a21:2e35:8c57:24ef::/64 -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -s 2a21:2e35:8c57:24ef::/64 -i eth0 -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m tcp --dport 53 -j ACCEPT

Globalement, on ouvre le port 53 en entrée uniquement pour les machines du réseau local. Par contre on permet à toute requête DNS vers le port 53 de sortir. En effet, notre machine est également un résolveur DNS qui doit pouvoir interroger les autres serveurs DNS de référence.

Tests de fonctionnement

Maintenant que notre configuration est prête, il faut réaliser quelques tests:

  • Voir si le résolveur local fonctionne correctement.
  • Voir si les machines de notre réseau local sont correctement identifiées.

Vous pouvez utiliser la commande dig issue du paquet dnsutils:

$ dig @ip_du_serveur_dns_à_tester nom_de_domaine_complet

Dans notre cas, voici quelques exemples:

$ dig @192.168.0.1 machinea.ici
; <<>> DiG 9.9.5-9-Debian <<>> @192.168.0.1 machinea.ici
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43547
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 3

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;machinea.ici.     IN  A

;; ANSWER SECTION:
machinea.ici.      86400   IN  A   192.168.0.2

;; AUTHORITY SECTION:
ici.           86400   IN  NS  serveur.ici.

;; ADDITIONAL SECTION:
serveur.ici.       86400   IN  A   192.168.0.1
serveur.ici.       86400   IN  AAAA    2a21:2e35:8c57:24ef:bad:cafe:bad:caca

;; Query time: 2 msec
;; SERVER: 192.168.0.1#53(192.168.0.1)
;; WHEN: Thu Mar 26 20:32:47 CET 2015
;; MSG SIZE  rcvd: 123

On voit que la réponse est correcte: on obtient une section ANSWER avec l'adresse IP de la machine. On voit également que les renseignements sur la partie du serveur faisant autorité sont corrects. Vous pouvez également utiliser la commande host qui affiche moins de détails pour vérifier votre configuration effective de serveur DNS.

Configuration des machines clientes du réseau local

Une fois que le service DNS fonctionne correctement, il faut faire en sorte que les clients du réseau local l'utilisent. Deux méthodes sont disponibles:

  • la méthode manuelle qui consiste à configurer le poste de travail en lui donnant l'adresse IP du serveur DNS.
  • on peut demander au service DHCP d'indiquer l'adresse IP du serveur DNS.

Pour la méthode manuelle, je ne parlerai que de la distribution Debian. Vous pouvez utiliser le fichier /etc/resolv.conf pour paramétrer manuellement le service DNS. En voici un exemple:

domain ici
search ici
nameserver 192.168.0.1

Ici, le domaine auquel appartient la machine se nomme ici. Par défaut, la recherche DNS ajoutera l'extension .ici lors de la recherche des noms courts. Enfin, le serveur de nom aura pour adresse IP 192.168.0.1

Cette configuration manuelle peut également être répercutée dans le fichier /etc/network/interfaces à l'aide des quelques lignes à ajouter à la configuration d'une interface:

iface eth0 inet static
        address 192.168.0.10
        netmask 255.255.255.0
        network 192.168.0.0
        broadcast 192.168.0.255
    dns-nameservers 192.168.0.1
    dns-domain ici
    dns-search ici

Mais on peut aller plus loin et indiquer au serveur DHCP de fournir cette information aux clients qui demandent une adresse IP. Il suffit d'ajouter les lignes suivantes dans la configuration du serveur DHCP (/etc/dhcp/dhcpd.conf):

# Options communes à tous les sous-réseaux gérés par le serveur DHCP.
## Définition de la résolution de nom
option domain-name "ici";
option domain-name-servers 192.168.0.4;

Conclusion

Avec relativement peu d'effort de configuration, on peut mettre en place un service DNS efficace pour un réseau chez soi. Même si le serveur DNS Bind peut (presque) tout faire, on voit bien qu'on peut limiter son périmètre pour gérer un seul serveur DNS centralisé qui fait tout. Cette configuration est adaptée pour les petits réseaux locaux, typiquement les réseaux qu'on retrouve dans la majorité des foyers de ce pays (et d'autres).

Utiliser un vrai service de cache de résolution DNS présente l'intérêt de limiter les requêtes DNS qui partent sur Internet.

Autre intérêt, le DNS étant un élément de base, conserver les logs du DNS permet de savoir à peu près ce que font les clients du réseau local. Quand on regarde les statistiques, on voit que ça n'est pas brillant: les clients font des requêtes DNS toutes les 5 secondes pour le même domaine: il n'y a pas de cache local. Un résolveur est donc quasi-indispensable sur un réseau local.

De plus, notre configuration permet d'avoir de vraies réponses DNS (genuine): le mensonge DNS n'est pas possible (sauf avec des moyens assez complexes à mettre en oeuvre).

L'inconvénient de notre installation est qu'elle concentre en un seul point la gestion du DNS ce qui peut poser de sérieux problèmes lorsque le service devient inopérant (le serveur est cassé ou la configuration est perdue). En règle générale, pour les réseaux un peu plus sérieux, on délègue le service DNS à plusieurs machines. C'est expressément prévu dans Bind avec la notion de serveurs esclaves qui viennent régulièrement interroger le serveur maître.

Si vous souhaitez installer un serveur qui fait autorité sur Internet et non plus uniquement sur votre petit réseau local, sachez que vous DEVEZ disposer d'au moins un serveur de secours toujours disponible.

En attendant, vous pouvez vous faire la main avec la configuration de Bind en local à peu de frais...

Posted jeu. 26 mars 2015 19:46:00 Tags:

Hello,

ces derniers jours, plusieurs annonces de baisse de prix sur le stockage en ligne viennent de nous faire passer un cap en ce qui concerne la sauvegarde.

En effet, Amazon a annoncé un service de stockage illimité pour 60$ par an.

Aujourd'hui, OVH annonce 10 To pour 50€/an sur son service Hubic. L'ancien prix était de 120€ par an.

Ces niveaux de prix sont maintenant plus compétitifs que de nombreuses solutions de sauvegardes à la maison. Faisons un rapide calcul avec un petit exemple de solution simple à mettre en place pour un administrateur système averti…

Nous voulons un dispositif de sauvegarde qui permette de sauvegarder 10 To avec un niveau de service qui peut approcher celui des deux fournisseurs évoqués plus haut. Concrètement, cela revient à mettre en place un serveur physique dédié équipé de plusieurs disques en RAID (1 ou 10 pour un cas simple). Le RAID permet de pallier rapidement à une déficience d'un ou de plusieurs disques. Mais cette solution n'est pas suffisante: en cas de sinistre complet sur le serveur, les données sont perdues. Il faut donc également un dispositif d'externalisation qui prendra la forme de disque(s) externe(s) sur lesquels on réalise à une fréquence donnée (1 fois par trimestre ou par mois selon le niveau de sécurité exigé) une copie complète des données du serveur (cela peut prendre la forme d'une copie différentielle par exemple). Pour ma part, l'externalisation est véritablement indispensable si l'on veut pouvoir approcher (sans l'égaler bien sûr mais avec un niveau de service acceptable) le niveau de service des deux fournisseurs sus-cités. En termes de durée de vie, nous allons considérer que les disques seront en fin de vie au bout de 5 ans (c'est parfois la durée de la garantie constructeur).

En étudiant rapidement les prix des disques durs du marché, on se rend compte que le meilleur coût au To est situé dans les disques durs de 3To qui sont à peu près à 100€ pièce. A ce niveau, on peut envisager d'obtenir une capacité protégée par un RAID6 avec 5 disques durs (6 disques pour du RAID10). On obtient 9To disponibles, ce qui est un peu inférieur aux 10To mais qui permet de limiter les coûts d'achats (mes calculs veulent juste montrer raisonnablement que même en rognant sur de nombreux postes, les offres des fournisseurs sont plus avantageuses). Avec 6 disques on obtient une capacité protégée de 12To ce qui dépasse ce qu'offre Hubic. Je mets de côté le problème du nombre de connecteurs SATA indispensables à la mise en oeuvre physique de ce stockage.

Pour l'externalisation, on retiendra 3 disques de 3To chacun. Au total, on doit disposer de 8 disques dur de 3To à 100€ chacun, ce qui donne un total de 800€ environ pour l'achat des disques.

Il faut également considérer la consommation électrique du serveur. Nous allons prendre une machine qui consomme peu. Elle peut prendre la forme d'un plugcomputer ou une carte SOC telle que le BeagleBone Black ou le RaspBerry Pi, à la condition qu'ils disposent de suffisamment de connecteurs disques (ce qui n'est pas gagné). Sur une autre forme, on peut envisager la récupération d'une machine normale (desktop) qui dispose souvent de plusieurs connecteurs SATA. En allumant la machine uniquement pour la sauvegarde, on peut tout à fait maîtriser le coût de l'énergie. Mon calcul montre que la consommation électrique d'une machine de 100W qui fonctionne 3h par semaine (le temps de faire une sauvegarde hebdomadaire d'au moins 500Go de données modifiées depuis la dernière sauvegarde) monte à près de 16kWh/an. A 14,4 centimes d'€ le kWh (tarif de base EDF), on obtient un coût électrique d'environ 2,3 € ce qui est (presque) marginal. Disons que le coût d'acquisition de la machine se situe aux alentours de 200€ (CM+RAM+CPU+Alim).

  • Achat des disques: 800 €
  • Boiters d'externalisation: 3x20 € = 60€
  • Achat du serveur: 200€
  • Consommation électrique annuelle: 2,3 €

Si on fait la somme sur 5 ans on obtient les chiffres suivants:

  • Sauvegarde à la maison (coût total d'achat): 1060 €
  • Sauvegarde à la maison (consommation d'énergie sur 5 ans): 11,5 € (on peut sans doute augmenter ce poste)
  • Sauvegarde à la maison (total): 1071,5 €
  • OVH Hubic: 50€ * 5 = 250 €
  • OVH Hubic (anciens tarifs): 120€ * 5 = 600 €
  • Amazon: 60€ * 5 = 300 €

D'un point de vue purement économique, Hubic et Amazon sont bien largement en dessous de ce qu'on peut faire chez soi, à capacité presque équivalente. De plus les calculs simplifiés ne prennent pas en compte les cas de pannes: changer un seul disque dur demande de rajouter 100€.

On peut néanmoins diminuer le poste de sauvegarde à la maison en jouant sur plusieurs tableaux. Par exemple, on peut avoir besoin de stocker moins que 10To. Pour stocker 3To de données, par exemple, il faudra beaucoup moins de matériel (3 disques de 3To au lieu de 8) et on peut également envisager de recycler une machine périmée à condition qu'elle dispose d'au moins 2 connecteurs SATA (ce qui est généralement le cas). Pour ma part, j'arrive à un coût quasi-minimal de 325€ pour une solution de stockage de 3To avec externalisation en utilisant une machine recyclée qui m'a coûté 10€. On voit que même ce montant est supérieur à ce que propose OVH et Amazon, ce qui n'était pas le cas avant (600€ pour l'ancien tarif d'Hubic).

Je reste sincèrement et éthiquement partisan de l'auto-hébergement mais, ma conclusion est que, d'un point de vue purement économique, l'auto-hébergement d'une solution de sauvegarde pour particulier n'est plus du tout rentable depuis ces derniers jours…

On peut néanmoins aller un peu plus loin en essayant d'évaluer quels sont les avantages qui restent d'une solution de sauvegarde auto-hébergée par rapport aux offres des fournisseurs sus-cités ?

J'en vois rapidement un qui me semble majeur: pour les personnes disposant d'un accès à Internet avec un débit montant faible (typiquement l'ADSL), le temps de chargement initial sera forcément très long. Un rapide calcul montre qu'avec un débit montant de 100ko/s (c'est le débit de crête de mon abonnement par exemple), le chargement de 10 To prendra environ 1240 jours (soit environ 3 ans et demi). Un débit montant 10 fois plus rapide mettra environ 4 mois, ce qui reste très long. A l'inverse, un débit montant de 50Mbits/s (débit montant typique de la fibre) permettra de réaliser le chargement complet au bout de 19 jours ce qui est beaucoup plus raisonnable.

Le temps de restauration doit également être pris en compte. Avec mon abonnement ADSL, j'ai un débit descendant de 500ko/s et la restauration des 10To devrait me prendre environ 8 mois. Avec la fibre, on descend à des temps bien inférieurs (12 jours environ).

Bien entendu, ces temps correspondent à la mobilisation de toute l'offre (celle d'Hubic au moins) ce qui n'est pas forcément le cas de figure de tout le monde. Pour ma part, mes besoins de sauvegarde se situent aux alentours de 3To ce qui comprend le stockage des données sur plusieurs sauvegardes (4 sauvegardes hebdomadaires et 6 sauvegardes mensuelles conservées en permanence). Sur mon abonnement ADSL, mes temps de sauvegarde totale et de restauration sont respectivement d'un peu plus d'un an et d'un peu plus de 2 mois, ce qui reste très long. Avec une sauvegarde à la maison, je peux monter jusqu'à un débit de 70Mo/s ce qui prend respectivement 12h à sauvegarder et 12h à restaurer. Ces temps sont déjà beaucoup plus intéressants.

Un autre point d'intérêt de la solution de sauvegarde "à la maison", c'est le fait qu'on puisse dédupliquer le contenu des sauvegardes permettant de diminuer sensiblement la quantité d'espace disque utilisée pour stocker des données qui sont redondantes. Par exemple, le système des sauvegardes hebdomadaires et mensuelles sur un espace disque dédupliqué consommera vraiment peu d'espace car les ratios de déduplication observés pour ce genre de sauvegardes qui stockent souvent la même chose à un delta près sont vraiment très bons (facteur de 1 à 10). Il existe maintenant de nombreux logiciels libres qui gère la déduplication (ZFS, Obnam, Attic, etc.).

Enfin, et pour rester sur l'aspect sécurité, disposer d'une sauvegarde à la maison c'est également une meilleure assurance pour lutter contre l'arrêt brutal d'un service. Effectivement, il est possible (même si j'en doute fortement) que ces tarifs soient uniquement des offres d'appel destinées à attirer le chaland et qu'ils vont augmenter par la suite. Peut-être encore, qu'Amazon ou OVH peuvent suspendre assez sèchement votre compte pour de nombreuses raisons (le compte a été piraté; il est utilisé pour stocker des choses répréhensibles par la loi; vous stockez finalement trop de données, pas en "bon père de famille", etc.). Enfin, on peut penser qu'à ce niveau de prix, si l'offre rencontre un réel succès et qu'une grande majorité de comptes stocke à la limite de la capacité, le fournisseur révise sa politique de prix ou limite le nombre de nouveaux abonnements.

Pour conclure sur ce sujet, je pense que l'on vient de franchir un seuil économique. Si jusqu'à présent, sauvegarder physiquement à la maison était souvent plus rentable, ce n'est maintenant plus du tout le cas. D'ailleurs, mes calculs mettent clairement de côté le facteur de temps humain. Même si on dispose d'une solution entièrement automatisée, je pense qu'administrer une machine de sauvegarde reste plus long (et nécessite plus de compétences) qu'utiliser un compte de service de sauvegarde externe. Je pense aussi qu'il est fort possible que ces tarifs baissent à l'avenir, sans doute, plus rapidement encore que les tarifs d'achat des disques durs.

Le cap est franchi, reste à voir quelles seront les conditions d'évolution de ces offres sur du moyen et du long terme (car la sauvegarde est bien un sujet de long terme)…

Posted sam. 28 mars 2015 15:38:00