Aller au contenu | Aller au menu | Aller à la recherche

Vieux con de hacker old school.....

Fil des billets - Fil des commentaires

lundi, mai 7 2018

Un annuaire pour les gouverner tous.......

Aujourd'hui, on va mettre en place un annuaire LDAP, un FusionDirectory en façade pour administrer le contenu du LDAP via une jolie interface Web, on va connecter un serveur mail (SMTP+IMAP+POP), une machine cliente réseau (via PAM) et quelques autres trucs, et on va coller du SSL partout.

Lire la suite...

mercredi, avril 4 2018

Mon nouvel hyperviseur Xen et ses petites subtilités

Disclaimer

Comme pour à peu près n'importe quel billet de la catégorie "Vieux con de hacker old school", ce billet parle de trucs techniques. J'ai glissé des liens ici et là pour que ma sœur puisse presque suivre les très grandes lignes (les autres personnes peuvent aussi cliquer, je suis de bonne humeur aujourd'hui), mais je n'expliquerai pas la plupart des concepts en détails.

Comme pour à peu près n'importe quel billet de la catégorie "Vieux con de hacker old school", tout ceci a été fait par un Vieux con de hacker old school (ah mais c'est donc ça l'explication du nom de la catégorie !!!!!!), dans des conditions de sécurité optimales, sur des machines consentantes, avec un vrai réseau de prod fonctionnel à coté pendant tout le temps de la préparation pour pouvoir aller buller sur facebook chercher les infos au fur et à mesure des problèmes qui se sont posés.

Comme pour à peu près n'importe quel billet de la catégorie "Vieux con de hacker old school", il n'y aura aucune photo de chaton dans ce billet. Il n'y aura d'ailleurs probablement pas du tout de photo, image ou tout autre truc du genre.......

Comme pour à peu près n'importe quel billet de la catégorie "Vieux con de hacker old school", libre à vous de suivre les indications de ce billet pour reproduire la même chose chez vous, je n'essaierai même plus de vous en dissuader, mais ne venez pas vous plaindre en cas de désagrément plus ou moins douloureux pour vos données, voire pour votre intégrité physique (et je ne veux pas savoir comment c'est arrivé dans ce cas !).

Comme pour à peu près n'importe quel billet de la catégorie "Vieux con de hacker old school", il y a très peu de chances pour que le contenu de ce billet vous aide d'une quelconque façon à pecho en boite.......

Comme pour n'importe quel billet de la catégorie "Vieux con de hacker old school", aucun animal mignon n'a été blessé, tué ou mangé pendant la réalisation des opérations.

Le contexte

Jusqu'à présent, j'avais toujours séparé mon NAS et mon hyperviseur Xen. Du coup, d'un coté, j'avais un serveur avec de gros disques (pour l'époque) en RAID miroir dédiés au stockage de mes photos données et peu d'autres contraintes, à coté un autre serveur (aussi avec des disques en RAID miroir, mais nettement plus petits) qui faisait tourner plusieurs machines virtuelles. Les problématiques étaient donc distinctes.

Un hyperviseur pour les gouverner tous....

Mais j'ai entre temps récupéré (gratos, du coup ca serait dommage de se priver) un serveur un peu ancien mais cependant tout à fait apte à gérer l'ensemble:

  • 2 sockets, 4 coeurs hyperthreadés par CPU, soit un total de 16 unités logiques de traitement d'après mon /proc/cpuinfo (nous ne débattrons pas ici de l'exactitude de ce calcul, en réalité j'ai bien 8 coeurs qui sont plus ou moins capables de chacun traiter 2 trucs en parallèle).
  • Du SATA 2. Je n'ai pas le budget pour acheter des disques SSD de plusieurs To, donc j'utilise des disques mécaniques, donc le SATA 2 est suffisant d'un point de vue performances.
  • 48Go de RAM. Bon, en vrai au départ, je n'avais que 8Go de RAM, ce qui est déjà correct, mais comme j'ai trouvé un lot de 6x8Go (de la DDR3 ECC Registered) à pas cher, j'en ai profité !
  • 10 Interfaces Ethernet Gigabit (dont 4 en SFP), et un connecteur disponible pour venir brancher des cartes 10Gbs.....

Du coup, même mon NAS va passer en machine virtuelle (après quelques mesures de performances disques qui confirment que les débits depuis une VM sont quasi identiques à ceux depuis le Dom0), et mes anciennes stratégies d'utilisation des disques deviennent difficilement applicables....

En plus, je me retrouve à utiliser de très gros disques (8To) sur un serveur dont le BIOS est un peu ancien, ce qui m'a posé quelques petits problèmes supplémentaires.

Un peu de stratégie en amont....

Une des questions existentielles qu'on se pose quand on déploie de nouveaux serveurs, c'est le partitionnement des disques.....
Jusqu'à présent, j'avais toujours utilisé des partitionnements assez classiques: un MBR, une partition principale pour le système de base, et une partition étendue qui contient le reste, dont un /usr en lecture seule avec de la marge pour d'éventuelles installations ultérieures d'outils/services/etc...., très souvent un /var séparé, un /tmp soit en RAMFS soit en partition séparée également, et un /home qui occupe tout le reste.

Sur un NAS dédié, ca fonctionne assez bien (on perd quelques Go sur quelques To, pas bien grave.....).
Sur mon ancien serveur de VMs, ca fonctionnait assez bien aussi, vu que toutes les VMs avaient leurs disques stockés en fichiers dans le /home.

Sur mon nouveau précieux serveur unique, c'est plus compliqué. Si j'avais décidé de laisser le /home géré par le Dom0, j'aurais pu rester plus ou moins sur ce modèle, mon /home gigantesque aurait stocké mes données ET les images de mes VMs. Sauf que, pour faire propre, l'hyperviseur ne fera vraiment pratiquement QUE hyperviser (il fera aussi ce qu'il est vraiment le seul à pouvoir faire, comme surveiller les disques, la température, et tant qu'à faire l'onduleur), donc le NAS sera assuré par une VM. Donc le gros /home de plusieurs To ne sera pas directement accessible par l'hyperviseur.

En vrai, là, j'aurais pu faire un truc un peu dégueulasse, genre un /home gigantesque sur le Dom0, et dedans une image presque aussi gigantesque utilisée par la VM qui fera le NAS....... Sauf que je veux un NAS qui dépote, et c'est vraiment une très très mauvaise base pour avoir de bonnes performances disques par la suite ! Accessoirement, j'aurais aussi eu la question de la taille de l'image: trop et je n'ai plus assez de place pour d'autres usages, pas assez et un jour je déborde sur cette partition (même si à priori, j'ai de la marge cette fois ci, mais je croyais déjà que c'était le cas sur les anciens disques de 2To.......).

Du coup, premier élément de solution: je vais (enfin ?) me mettre à utiliser Logical Volume Management, LVM pour les intimes (et on va rapidement devenir assez intimes.....). Dans mon cas, la possibilité d'ajouter des disques/partitions plus tard ne m'intéresse pas vraiment: j'ai déjà de la marge (enfin, pour l'instant), et surtout, parmi les rares contraintes de mon prochain serveur, je n'ai que 2 emplacements propres pour disques durs 3 1/2, qui seront donc d'entrée de jeu utilisés (par mes 2 disques de 8To en RAID miroir, pour les 2 du fond qui ne suivent pas.....).
Ce qui m'intéresse vraiment, c'est avant tout de pouvoir créer un volume logique ("logical volume", que nous appellerons simplement "lv" par la suite, ou "lvs" quand il y en aura plusieurs) avec une quantité d'espace allouée, et de pouvoir augmenter cet espace plus tard. Du coup, je n'alloue pas tout pour l'instant, et je pourrai rajouter un peu au fur et à mesure, là où ça sera nécessaire. Notez que, techniquement parlant, LVM permet aussi de faire l'inverse (réduire la taille d'un lv, et du système de fichiers dedans), mais c'est quand même plus ou moins déconseillé par plein de monde de faire dans ce sens......
Je suis un peu tenté aussi par d'autres fonctionnalités de LVM, comme la possibilité d'utiliser un disque SSD en cache pour le gros lv du /home, ou le fait de pouvoir faire des clichés (que nous appellerons "snaps" par la suite).

Mon plan de partitionnement se simplifie sacrément d'un coup: une énoooooorme partition entièrement dédiée à LVM, et je crée au fur et à mesure de mes besoins de petits lvs, pour le Dom0 comme pour les VMs. Bon, parmi ces "petits lvs", il y en aura un de plusieurs To dès le départ, mais l'idée reste la même, et à la fin de l'installation initiale, il y aura au moins 1 ou 2 TOs non alloués.

Il reste donc juste à organiser le partitionnement de chaque système.....

Diviser pour mieux régner

Le plus simple serait d'avoir une partition par système. Cette approche présente historiquement le gros avantage de ne pas trop se poser de questions de répartition d'espace entre les partitions, mais j'ai donc désormais LVM pour ne plus être coincé de ce coté là. En contrepartie, cette approche présente également au moins 2 problèmes potentiels:

Un /var/log ou un /tmp qui se remplissent trop peuvent saturer tout l'espace disque. Un /home aussi, mais dans mon cas, il est déjà fourni à part.

Comme les mêmes /var, /tmp et /home doivent être en lecture/écriture, tout le système doit l'être aussi. Or, avoir une partie de son système en lecture seule présente des avantages: réduction des risques d'erreurs de manipulation, (un peu) plus difficile pour un attaquant de poser durablement un rootkit ou équivalent, peu/pas de risques de casser le système de fichiers même en cas d'arrêt brutal du serveur. Bien sur, pour les opérations de maintenance (mises à jour, etc.....) on peut/doit repasser temporairement le système de fichiers en lecture/écriture.


Bref, je veux partitionner, et avoir une partie de mon système en lecture seule..... Dans un premier temps, du coup, j'ai envisagé de faire plein de partitions par système, comme d'habitude, dont /usr en lecture seule..... pour le Dom0, pas trop de problèmes, mais pour les VMs, c'est plus lourd: soit je crée un seul lv au niveau du Dom0, et je fais du LVM dans du LVM (le 2eme niveau étant géré au niveau de la VM), niveau perfs ca semble ne pas changer grand chose, mais le redimensionnement des lvs devient plus lourd. Soit je crée plein de lvs au niveau du Dom0, je passe tout ca à la VM, ca fait un fichier de conf Xen un peu compliqué, mais surtout ça va me compliquer la vie le jour où je voudrai faire des snaps de mes VMs !

Donc j'ai fait un état des lieux par répertoire. Je ne tiens pas compte des répertoires particuliers (/sys, /run, /dev, etc...... mais pas /etc !) qui sont de toutes façons gérés dynamiquement.
En gros, /var, /tmp, /root et /home doivent être en lecture/écriture. /root et /home contiendront très peu de données dans mon cas (un .bashrc, un .ssh/authorizedkeys et peut être 2-3 trucs temporaires de temps en temps). A vrai dire, une fois l'installation initiale d'un système terminée, le seul intérêt pour moi de les avoir dans la catégorie "lecture/écriture", c'est pour pouvoir enregistrer le .bashhistory........ du coup, j'ai une solution simple à base de liens symboliques pour regrouper ces répertoires dans un seul système de fichiers:

/var
/home -> var/home
/root -> var/root
/tmp -> var/tmp (ou en tmpfs)

Notez les liens relatifs (il n'y a pas de '/' au début), qui éviteront de mauvaises surprises si je me retrouve à monter les partitions en dehors de leur contexte normal.....

Selon le service fourni par les VMs, je voudrai éventuellement un /srv dédié, dans lequel j'irai stocker sites webs, base LDAP, base SQL, etc....

Idéalement, tout le reste devrait être en lecture seule, dont en particulier:

/
/bin
/sbin
/etc
/usr
/lib
/boot

En plus, tout ce reste en question représente assez bien le système (l'ensemble des programmes et de la configuration) pour une bonne partie de mes serveurs (hyperviseur inclus), et je serai bien content de pouvoir en faire un snap unique avant une grosse mise à jour des paquets.

Je me retrouve donc avec 3 voire 4 partitions par système (en comptant la partition SWAP), avec un découpage logique tout à fait cohérent pour faire des snaps: root, var, swap et srv.

Il reste juste un petit détail à régler...........

Debian en Read-only Root

Bah oui, un système avec /usr en lecture seule, c'est un peu trop facile, mais avec carrément / en lecture seule, on a un peu plus de trucs à gérer......

Premier point, /root, /var, /home et /tmp. On a déjà réglé ce problème par un /var dédié en lecture/écriture qui contient tout le reste et des liens symbolique depuis la racine.

Le second gros problème, c'est une partie du contenu de /etc. Et il y a déjà une page debian qui regroupe les questions relatives à un système avec / en lecture seule !

A partir de cette liste, que je pense être basée sur des versions plus anciennes de Debian, on peut déjà faire un peu de tri: la plupart des fichiers de configuration problématiques ne seront pas utilisés sur mes serveurs, d'autres sont déjà des liens tels qu'indiqués sur cette page, et une autre partie ne sont pas présents sur mes installations. Voyons ce qu'il reste:

courier imap

Sur la plupart de mes serveurs, il n'y aura pas d'IMAP. Sur le serveur de mail, la configuration sera normalement gérée via LDAP, donc pas besoin d'aller triturer dans /etc.

lvm

Les guests n'utiliseront pas eux même LVM, comme nous allons le voir un peu après. Pour le Dom0, il faudra remonter temporairement / en lecture/écriture pour certaines opérations LVM.

nologin

A tester !

resolv.conf

Mes serveurs auront une configuration DNS statique, donc un resolv.conf qui ne bougera pas, tout va bien.

passwd/shadow

Une fois les systèmes installés, soit les authentifications se feront via LDAP, soit les serveurs auront une configuration très très minimaliste de ce coté là (un compte d'administrateur qui se connecte en SSH par certificats puis qui passe en root), donc les rares changements de mots de passes nécessiteront de remonter temporairement / en lecture/écriture.

udev

Les fichiers indiqués sur la page Debian ne sont plus générés sur Stretch, donc pas de problèmes.

Test

Premier essai sur une VM sur l'ancien hyperviseur (quand je vous dit que tout est fait dans des conditions de sécurité optimales !). Après l'installation (partitions /, /var et swap), on déplace les répertoires nécessaires:

cd /
mv root /var/ && ln -s var/root
mv home /var/ && ln -s var/home
rm -rf /tmp && ln -s var/tmp  # /var/tmp existe déjà et doit avoir les mêmes permissions que /tmp

Notez l'absence de "/" pour le ln, comme je l'avais indiqué plus haut. Notez aussi que le "/" final après var pour les mv ne sert pas vraiment, c'est pour bien illustrer le fait qu'on déplace bien les répertoires à l'intérieur de /var.

Ensuite, on édite /etc/fstab:

UUID=[uuid de votre partition ROOT]    /       ext4    errors=remount-ro      0    1

devient:

UUID=[uuid de votre partition ROOT]    /       ext4    defaults,ro,noatime   0    1

(je ne suis pas certain que le "noatime" soit vraiment nécessaire)

Reboot, et ......... ça fonctionne ! Limite un peu trop facile, en fait........

Bien sur, pour se simplifier un peu la vie, l'édition de fstab aura lieu quand le système est complètement installé et configuré.

apt full-upgrade

Pour ne pas avoir à faire les manips à la main à chaque installation / mise à jour par apt, on va aller configurer un remontage à la volée, en créant /etc/apt/apt.conf.d/00RWRoot:

DPkg {
    // Auto re-mounting of a readonly /
    Pre-Invoke { "mount -o remount,rw /"; };
    Post-Invoke { "test ${NOAPTREMOUNT:-no} = yes || mount -o remount,ro / || true"; };
};

un joli prompt

Pour savoir d'un coup d'oeil si on a un / en lecture ou en écriture, il suffit de se créer un prompt sympa dans son .bash_profile:

function prompter(){
        if [ -z "$(findmnt / |egrep 'ext4\s+rw')" ];then
                # Read only
                PS1='\[\033[01;36m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
        else
                PS1='\[\033[01;31m\]\u@\[\033[01;36m\]\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
        fi
}
export PROMPT_COMMAND=prompter

J'ai mis un peu de temps à trouver un test fiable pour déterminer si / est en lecture ou lecture/écriture (les tests simples à base de "grep ro" ne fonctionnent pas dans tous les cas). Je ne peux pas garantir que ce test là fonctionne en permanence, on pourrait l'améliorer en vérifiant qu'on a bien soit "ro" soit "rw" (et gérer un 3eme état "sais pas").

Ah, et la notion de joli est subjective, l'important, c'est le principe de prompt dynamique et la technique pour détecter un / en ro ou rw.......

Partition en lecture seule

Pour le Dom0, on en restera là, mais pour les VMs, j'aurais aimé pouvoir vraiment bloquer les accès en écriture au niveau de l'hyperviseur, et les débloquer juste quand j'en ai besoin. Une solution simple consiste à déclarer la partition en lecture seule dans la configuration Xen de la VM, sauf que du coup, "débloquer les accès en écriture" implique de rebooter la VM (je n'ai pas trouvé de commande xl pour faire le changement à la volée).

J'ai également trouvé une option "-p r" / "-p rw" de lvchange, qui permet de changer les permissions du lv. Après quelques essais rapides, je constate bien le changement dans lvdisplay, mais ma VM peut toujours écrire sur le lv (et le changement est bien enregistré). Soit il y a un bug, soit les permissions en question sont autre chose (j'ai trouvé très très peu de documentation sur ce point).

Au passage, si j'en étais resté avec des fichiers pour mes disques de VMs, j'aurais peut être pu facilement faire un chmod........

Master et Snap

LVM permet une autre possibilité intéressante: avoir une partition de référence (le master), et faire fonctionner la VM sur un snap (un dérivé, dans ce cas). Aucun changement sur le snap n'est durable: il suffit de re-générer le snap à partir du master. Redoutable pour nettoyer un serveur compromis, temps de maintenance record (tout peut être préparé en amont, d'un point de vue de la machine de production, c'est juste un reboot), mais assez complexe à gérer au quotidien. Les mises à jour doivent être faites sur le master (soit en démarrant une autre copie de la VM, soit directement depuis le Dom0 à coup de chroot, par exemple), et j'ai déjà vu passer des mises à jour qui font des manipulations sur les données (base de données, etc.....), ce qui complique le suivi.

Bref, l'idée est jolie, je ferai probablement un test un jour sur un serveur secondaire (ou un serveur critique et super statique), mais pour l'instant, je laisse l'idée de coté.

Installation de l'hyperviseur

Le plan de bataille est prêt, il ne reste plus qu'à l'appliquer, à commencer par le Dom0.

Raid miroir, LVM, ROOT et GPT sont dans un bateau.....

Comme je l'avais évoqué au dessus, le BIOS de mon serveur est un peu ancien. Il ne sait donc pas du tout ce que veut dire EFI ou GPT, et c'est déjà bien qu'il détecte mes disques ..... sauf qu'il est à peu près convaincu qu'ils font environ 1.4To chacun.......

Comme le noyau Linux va rapidement lui même tout re-détecter sans se préoccuper du BIOS, y compris les disques (avec leur bonne capacité !), j'ai donc juste besoin que le BIOS réussisse à faire démarrer le noyau. Ma première idée a donc été de réviser légèrement mon plan de partitionnement, avec un petit /boot en début de disque.
Après avoir perdu pas mal de temps à essayer de faire installer GRUB2, j'ai finalement réalisé que l'installeur Debian m'avait créé des tables de partition GPT sur mes disques (alors qu'il m'avait juste demandé si je voulais des tables de partitions, oui/non). Pour que GRUB puisse s'installer sur des disques avec de telles tables, vous devez commencer par créer, au début de chaque disque concerné (les 2 dans mon cas, donc), une petite partition (la littérature sur le net parle de 1Mo comme étant déjà confortable, j'en ai mis 2 pour être installé en VIP.....) de type "BIOS Boot Partition" (disponible dans la liste des types de partitions durant l'installation). Cette partition n'est pas en RAID miroir: GRUB n'ira pratiquement jamais la modifier (il y a juste une partie du code de GRUB, pas de configuration), et si jamais il le fait, nous allons de toutes façons lui indiquer par la suite qu'il doit s'installer sur les 2 disques.

Avec cette petite diversion, j'en ai un peu oublié de créer mon /boot dédié, et je m'en suis rendu compte assez tard dans l'avancement du nouveau serveur..... En pratique, mon installation démarre. D'après lvdisplay -m, ma partition dom0root est physiquement au début du disque et devrait y rester, en tout cas tant que je ne l'étends pas, donc l'image du noyau et de l'initrd devraient rester accessibles (tant que je n'étends pas dom0root, donc.......). Comme j'ai quand même pris un peu de marge sur la taille de cette partition et que je prévois vraiment de ne pas avoir grand chose du tout d'installé directement sur l'hyperviseur, je prends le risque de rester avec ce découpage. Si ça se trouve, GRUB est peut être assez intelligent pour savoir adresser l'ensemble des disques durs, même s'ils ont mal été détectés par le BIOS..... Si vous vous retrouvez vous aussi à jouer avec des disques partiellement reconnus par votre BIOS...... faites un /boot dédié !

Ce petit problème étant résolu, voici le résumé de ce qu'il faut faire pour préparer ses disques dans l'installeur Debian:

  • Création d'une table des partitions sur les 2 disques s'ils sont neufs. Créer si nécessaire la partition "BIOS Boot Partition", donc.....
  • En cas de nécessité, créer un tout petit RAID en tout début de disque pour /boot, donc......
  • Créer une partition qui occupe tout le reste de l'espace sur chaque disque (elles s'appelleront probablement /dev/sdaX et /dev/sdbX, avec X=3 si vous avez créé la partition BIOS Boot Partition et la partition raid /boot, X=2 si vous n'avez créé que l'une des deux partitions citées à l'instant, et X=1 si vous n'en avez créé aucune des deux), et indiquer comme utilisation "physical volume for RAID".
  • Configure software RAID: RAID1, 2 devices actives, 0 spare, sélection des 2 partitions créées au dessus (probablement /dev/sdaX et /dev/sdbX, donc).
  • Configurer cette partition RAID (probablement /dev/md0, ou /dev/md1 si vous avez créé un /boot aussi en RAID1) comme "use as physical volume for LVM"
  • Configure LVM:
    • Create volume group (que nous appellerons vg0 dans le reste du billet, mais vous pouvez nommer le votre toto89324xfza si ça vous fait plaisir......), sélection de la partition md créée juste au dessus.
    • Create logical volume sur vg0 pour root, var et swap. Pour plus de lisibilité, j'ai nommé les miens dom0root, dom0var et dom0_swap, mais là aussi, vous pouvez mettre les noms arbitraires de votre choix....
  • Retour au menu de partitions, affectation des lv sur /, /var et swap
  • Pendant la conf de grub, choisir l'installation sur le secteur de démarrage, et indiquer le premier disque (sda)
  • après le reboot, dpkg-reconfigure grub-pc (ou dpkg-reconfigure grub-efi-amd64 si vous bootez en EFI, d'après ce que j'ai lu sur certaines docs) et sélection de sda et sdb (pour des mises à jour futures de grub). Là, je suppose qu'il s'installe sur sdb sans commande supplémentaire, mais comme je l'avais fait durant l'installation, je n'ai pas pu vérifier !

Et c'est parti pour ...... environ 10h de synchro du miroir dans mon cas !

Avec lvm, on peut faire plus subtil: créer un premier md relativement petit (20Go suffisent très très largement pour une install minimaliste d'une Debian serveur), qui va se synchroniser très rapidement durant l'installation (alors que la synchro du mien a plombé très sévèrement la vitesse d'installation de base du système !), et plus tard, à un moment tranquille, on crée un ou plusieurs autres gros md qu'on ajoute à vg0..... En utilisant cette technique, on pourrait aussi forcer dom0_root à être dans md0, qui est au début du disque, ce qui serait une autre façon de s'assurer que l'image du noyau restera dans la partie du disque accessible via le BIOS.

Configuration du Dom0

Quitte à détailler l'installation, petit rappel express pour transformer une "install Debian" en "install Debian Dom0":

apt install xen-system-amd64

dpkg-divert --divert /etc/grub.d/08linuxxen --rename /etc/grub.d/20linuxxen

Edition de /etc/default/grub (dans l'exemple, je lui réserve 2Go de RAM, vous ajusterez selon votre cas, j'ai le vague souvenir qu'il ne faut pas lui mettre trop peu):

GRUBCMDLINEXEN="dom0_mem=2048M,max:2048M"

/etc/xen/xend-config.sxp

(dom0-min-mem 2048)
(enable-dom0-ballooning no)

Ces 2 changements disent en gros que le Dom0 aura 2Go de mémoire dédiée pour lui, mais qu'il ne touche pas au reste, c'est pour les potes....

On applique les changements de la configuration de grub:

update-grub

Un petit reboot et on est bon !

Création des disques d'une VM

lvcreate -n guest1_root -L 2G vg0 /dev/md0
lvcreate -n guest1_var -L 2G vg0 /dev/md0
lvcreate -n guest1_swap -L 1G vg0 /dev/md0

A la fin de la ligne, /dev/md1 sert à s'assurer que ces LVs seront bien sur md0 (chez moi, c'est md0, chez vous, ca sera donc peut être md1, voire autre chose), même si plus tard on rajoute d'autres disques, partitions RAID ou je ne sais quoi d'autre.
L'option -n permet d'indiquer le nom du lv.
L'option -L permet de spécifier la taille, ici 2Go pour les partitions root et var (ce qui sera largement suffisant pour la plupart de mes installs serveurs, et il sera toujours temps d'agrandir les lv plus tard si besoin), et 1Go pour le swap.
Et vg0 pour indiquer le vg sur lequel on souhaite créer tout ça (oui, on pourrait avoir plusieurs vgs, et même que ça pourrait être pertinent dans certains cas).

Le fichier de conf Xen de la VM va donc contenir (j'ai viré ce qui ne concerne pas directement les disques):

boot = "pygrub"

# Uniquement pour le boot d'install depuis l'ISO
bootloader_args = [ 
         "--kernel=/install.amd/xen/vmlinuz",
         "--ramdisk=/install.amd/xen/initrd.gz"
 ]
 
disk = [
 # uniquement pour l'install
 'file:/home/Xen/debian-9.3.0-amd64-netinst.iso,hdc:cdrom,r',

 'phy:/dev/vg0/guest1_root,xvda1,rw',
 'phy:/dev/vg0/guest1_var,xvda2,rw',
 'phy:/dev/vg0/guest1_swap,xvda3,rw',
 ]

Et ca ne fonctionne pas: l'installer Debian voit bien qu'il s'agit de partitions, mais ne comprend pas leur type, et veut désespérément leur générer un MBR comme s'il s'agissait de disques....

Il faut donc d'abord créer les filesystem depuis le dom0:

mkfs.ext4 /dev/vg0/guest1_root
mkfs.ext4 /dev/vg0/guest1_var
mkswap /dev/vg0/guest1_swap

puis recommencer l'installation (ou me croire sur parole, et exécuter ces commandes avant la première installation.......).

Avec cette configuration, grub ne s'installe pas. A vrai dire, ce n'est pas très grave, vu qu'il n'est pas directement utilisé (c'est pygrub qui fait le boulot depuis le Dom0). J'ai trouvé chez Debian une solution, qui consiste à créer le fichier /boot/grub/menu.lst suivant dans la VM:

default         0
timeout         0

title           Debian GNU/Linux
root            (hd0,0)
kernel          /vmlinuz root=/dev/xvda1 ro
initrd          /initrd.img

Soit vous montez la partition depuis le Dom0 après l'installation, soit, à la fin de l'installation, vous le créez dans un shell:

mkdir /target/boot/grub
cat >/target/boot/grub/menu.lst

(et vous faites un copier/coller du fichier juste au dessus).

Comme le but est de filer les infos à pygrub, on peut aussi s'en sortir avec les paramètres root et bootloader_args du fichier de configuration de la VM:

bootloader_args = [ "--kernel=/vmlinuz", "--ramdisk=/initrd.img" ]
root        = '/dev/xvda1 ro'

Une VM en img vers LVM

Pour ma VM de firewall, la méthode décrite au dessus ne fonctionne pas: je dispose d'une image de VM au format OVA toute prête, et je ne peux pas faire ce que je veux au niveau des partitions, donc je vais utiliser un lv pour stocker directement cette image:

root@dom0:~ $ tar xf firewall.ova
root@dom0:~ $ ls *.vmdk
firewall.vmdk
root@dom0:~ $ qemu-img convert -O raw firewall.vmdk firewall.img
root@dom0:~ $ qemu-img info firewall.img
image: firewall.img
file format: raw
virtual size: 10G (10738466816 bytes)
disk size: 843M

Nous disposons maintenant d'une image RAW de la VM (bien sur, selon votre format d'origine, vous adapterez les premières étapes), et de sa taille exacte à l'octet près. Il ne reste plus qu'à créer un lv de cette taille (notez bien le b à la fin de la taille, par défaut lvcreate considère une autre unité) et à copier l'image:

root@dom0:~ $ lvcreate -n firewall_root -L10738466816b /dev/vg0
root@dom0:~ $ dd if=firewall.img of=/dev/vg0/firewall_root bs=1M

Et voilà !

Opérations de maintenance

Remplacement de disque physique

Ca fait pas mal de temps que je n'ai pas eu besoin de remplacer un disque (mais j'ai déjà eu plusieurs fois à le faire depuis que je stocke mes données en RAID1.....), du coup, pour l'instant, je consigne ici ce que j'ai trouvé comme infos sur le net.

Dites "33"

Première chose à faire, repérer le disque qui pose problème:

root@dom0:~$ cat /proc/mdstat 
Personalities : [raid1]
md0 : active raid1 sdb2[0]
     7813892096 blocks super 1.2 [2/1] [U_]
     bitmap: 0/59 pages [0KB], 65536KB chunk

Dans ce cas, c'est sda qui serait défaillant, nous partirons de ce principe pour tout le reste de l'exemple.

Attention chérie, ca va couper....

Maintenant qu'on connaît le disque fautif, nous allons confirmer à mdadm qu'il doit le considérer comme tel:

root@dom0:~$ mdadm --manage /dev/md0 --fail /dev/sda2

Dans mon cas, je n'ai que md0, et il est constitué de sda2 et sdb2. Si vous avez plusieurs md qui utilisent le disque défectueux, l'opération est à recommencer pour chaque partition. Les opérations suivantes aussi.....

Il est maintenant possible de l'enlever du RAID:

root@dom0:~$ mdadm --manage /dev/md0 --remove /dev/sda2

Une fois le disque complètement sorti du RAID, vous pouvez éteindre le serveur, remplacer le disque par un nouveau, et redémarrer (si votre configuration est bonne, vous saurez booter sur le disque encore actif du RAID, ce qui nécessitera peut être quelques manips au niveau du BIOS/EFI dans le cas où c'est justement sda qui a laché).

Le nouveau disque

Commencez par vérifier que vos disques ont toujours les mêmes noms ! Il semblerait que, dans certains cas, votre ancien sdb puisse être votre nouveau sda, et ca serait vraiment vraiment une mauvaise idée d'appliquer les manipulations ci après à l'envers !! Supposons cependant que l'ancien sdb soit toujours sdb, et que le disque tout neuf ait pris le nom de celui qu'il remplace: sda.

Première étape, il faut partitionner le nouveau disque. Plusieurs options sont possibles, y compris une construction manuelle de la table des partitions, mais si votre nouveau disque fait exactement la même taille que l'ancien, le plus rapide est de faire:

root@dom0:~$ sfdisk -d /dev/sdb | sfdisk /dev/sda

Nous pouvons maintenant ajouter la nouvelle partition au RAID1:

root@dom0:~$ mdadm --manage /dev/md0 --add /dev/sda2

A partir d ce moment, vous pouvez aller vérifier que le md a commencé à se synchroniser en faisant un cat /proc/mdstat. Et vous pouvez du coup aussi vous installer pour quelques heures d'angoisse, en attendant que la synchro soit finie (donc que toutes vos données soient à nouveau à l'abri sur 2 disques......).

Grub

Et il faudra aussi installer Grub sur le nouveau disque. A priori, ca devrait se faire via la commande suivante:

root@dom0:~$ grub-install /dev/sda

J'espère qu'il se passera plusieurs années avant que je ne puisse venir éditer ce billet et confirmer que cette séquence fonctionne.........

Extension de la taille d'une partition de VM

Déjà, on va vérifier qu'il reste de la place dans le vg:

root@dom0:~$ vgdisplay /dev/vg0
[.......]
    Free  PE / Size        xxxxx / Place libre

Ok, disons qu'il reste de la place, ajoutons donc 2Go sur la partition root de guest1:

root@dom0:~$ xl shutdown guest1
Shutting down domain X
Waiting for 1 domains
Domain X has been shut down, reason code 0
root@dom0:~$ lvresize -r -L +2G /dev/vg0/guest1_root
[.......]
root@dom0:~$ xl create /etc/xen/guest/guest1.xl

Et ...... c'est ....... tout !!!!! (c'est le -r qui dit à lvresize de faire faire dans la foulée le boulot de redimensionnement du filesystem en dessous). Enfin, presque: si vous avez le / de votre dom0 en lecture seule, il faut le repasser en lecture/écriture pour le lvresize, ce qui donne au final les commandes suivantes:

root@dom0:~$ xl shutdown -w guest1 && mount -o remount -w / && lvresize -r -L +2G /dev/vg0/guest1_root && mount -o remount -r / && xl create /etc/xen/guest/guest1.xl

Avec une bonne politique de nommage des volumes logiques, ca doit même pouvoir se scripter très facilement, en fait....... d'un autre coté, si le script est rentable, c'est que vous passez votre temps à redimensionner vos partitions, et vous pouvez vous poser d'autres questions, dans ce cas !

Snapshot d'une VM

Un snapshot LVM ne fait pas une véritable copie complète du lv d'origine: il retient son état au moment du snapshot, puis va uniquement stocker les changements (enfin, les anciennes versions avant changement, pour être précis). Nous allons donc allouer au snapshot une taille nettement inférieure à celle du volume d'origine s'il s'agit juste de faire une sauvegarde avant un changement de configuration un peu risqué. Par contre, s'il s'agit d'une sauvegarde juste avant une très très grosse mise à jour de version, par exemple, il faudra prévoir une taille assez conséquente pour le snapshot !

Créons donc un snapshot d'1Go de la partition root de guest1 (la VM est éteinte depuis quelques secondes, je ne sais pas trop s'il est possible/fiable de faire un snap d'une VM en cours de fonctionnement):

root@dom0:~$ lvcreate -L1G -s -n guest1_root_snap1 /dev/vg0/guest1_root

Et voilà..... l'opération ne prend que quelques secondes, on peut redémarrer guest1 de suite.

Si vous voulez faire une bonne vieille sauvegarde à l'ancienne de votre partition, dans un bon vieux fichier que vous pouvez stocker ailleurs, vous pouvez le faire sur le snap pendant que la VM a été redémarrée:

root@dom0:~$ dd if=/dev/vg0/guest1_root_snap1 bs=1M | bzip2 -9 > guest1_root_save.raw.bz2

Ce fichier sera restaurable sur le lv d'origine (VM stoppée) avec la commande suivante:

root@dom0:~$ bunzip2 -c guest1_root_save.raw.bz2 | dd of=/dev/vg0/guest1_root bs=1M

Dans les 2 cas, vous pouvez jouer avec l'option bs de dd (block size), d'autres valeurs pourraient vous permettre de gagner pas mal de temps sur l'opération (ou d'en perdre pas mal.....).

Quand votre snapshot ne sert plus à rien, il est détruit comme n'importe quel autre lv:

root@dom0:~$ lvremove /dev/vg0/guest1_root_snap1

Si par contre vous avez bien fait de faire un snap et que vous avez besoin de le restaurer, l'opération est un peu plus longue, et se fait via la commande suivante (avec la VM stoppée):

root@dom0:~$ lvconvert --merge /dev/vg0/guest1_root_snap1

N'apparaissent pas au générique de fin.......

Inévitablement, j'ai fait des choix à certains moments. Quelques uns méritent un peu plus d'explications.

LVM dans du LVM

Au début, on m'avait proposé l'idée de faire une partition LVM au niveau de l'hyperviseur pour chaque guest, et de considérer au niveau du guest cet ensemble comme un disque, donc de faire des partitions dessus, et tant qu'à faire en LVM aussi. Le fait d'empiler 2 niveaux de LVM semblait ne pas poser de gros problèmes de performances, mais c'est quand j'ai regardé comment étendre les partitions des VMs que j'ai décidé de renoncer à cette solution. En gros, il fallait étendre le lv coté hyperviseur (jusque là, ca me parait prévisible......), démarrer la VM, créer une partition sur l'espace supplémentaire désormais disponible, rajouter cette partition au vg du guest, et seulement après on pouvait à nouveau faire un lvextend au niveau du guest...... Pour les éventuelles réductions de taille de partition, même si ca ne devrait pas m'arriver en pratique, je n'ai même pas osé trop réfléchir à la séquence de trucs à faire !

Sur ce point, il semble cependant y avoir une alternative qui réduit un peu les manipulations: au niveau de la VM, ne pas faire de partition, mais embarquer l'ensemble du disque comme un PV, ce qui permettrait semble-t-il de redimensionner directement le vg dessus avec un pvresize après avoir étendu le lv au niveau du Dom0 (si vous avez compris cette phrase du premier coup, ne prenez pas le volant !). Notez que je n'ai pas testé, et qu'il s'agit donc d'une solution théorique à ma connaissance.


Dans tous les cas, ca m'aurait aussi un peu compliqué la vie au moment de faire des snaps: soit j'aurais du faire des snaps complets des VMs (root + var + swap + srv) au niveau de l'hyperviseur, soit ...... euh ..... j'aurais du réfléchir à comment balancer de l'espace en plus à la VM pour qu'elle fasse elle même son snap.

xen-tools

Plusieurs tutoriels font de la création de VMs avec xen-tools. Vu que je ne prévois pas de faire régulièrement de création de nouvelles VMs, je n'ai pas trop cherché à utiliser cette solution.

debootstrap

Il est également possible de créer une VM Debian sans la démarrer via debootstrap. Un peu comme pour les xen-tools, j'ai été plus vite à court terme en créant mes VMs en mode "classique", mais je m'intéresserai clairement à cette possibilité si je me rends compte que j'ai à fabriquer plus ou moins régulièrement de nouvelles VMs à l'avenir.

Thin provisioning

LVM supporte (depuis peu ?) le principe de thin provisioning. En gros, c'est la même idée que le surbooking, mais pour l'espace disque..... Je suppose que c'est assez pratique dans certains cas, mais dans le mien, je n'aime pas trop l'idée de vivre à crédit sur mon espace disque, surtout que je n'en ai pas besoin.

RAID1 via LVM

LVM gère depuis pas mal de temps une option "mirror". Pour le peu que j'en ai lu sur le net, l'objectif de ces mirrors est différent, et nettement moins pertinent que mdadm pour gérer un RAID1 de disques.


Depuis beaucoup moins de temps, LVM gère également un mode RAID1 (entre autres), qui semble lui très très proche d'un point de vue fonctionnel du RAID1 avec mdadm. J'ai repéré cette possibilité alors que j'avais déjà pas mal avancé sur mon installation, et le coté un peu nouveau de la solution a aussi contribué à ce que je laisse cette idée de coté.

Pour une version ultérieure, la question se posera peut être, mais j'avoue que pour l'instant, j'aime bien l'idée d'avoir rapidement une vue purement RAID d'un coté, et une gestion d'un seul "truc" (le md0) de l'autre.

FreeNAS

Pour le NAS, à un moment, je me suis posé la question d'utiliser FreeNAS, son fork Nas4Free ou une autre distrib dédiée. Au début, je n'avais pas mes 48Go de RAM, donc j'avais laissé tomber.
En plus, dans mon cas d'usage, les avantages de ZFS ne sont pas flagrants, mon install de VM NFS finale est vraiment réduite au strict nécessaire, et je n'ai pas creusé l'empilage LVM/ZFS.

Des trucs annexes....

Là, on sort des problématiques d'organisation de disques, mais si ces infos peuvent servir à des gens plus tard (dont potentiellement moi), alors tant mieux !

Ordre d'arrêt des VMs

Par défaut, lors de l'arrêt de l'hyperviseur, le script /etc/init.d/xendomains est appelé, et stoppe les VMs dans l'ordre inverse de leur création. Tant que vos VMs ne rebootent pas, l'idée peut sembler pertinente......

Une solution absolument propre consisterait à connaître un graphe de dépendance des VMs, et à stopper par lots en fonction de ces dépendances. Par exemple:

  • je stoppe le firewall HA passif en tache de fond
  • je stoppe tous les clients (qui dépendent du firewall, du NFS, du DNS, etc....)
  • puis je stoppe les serveurs publics
  • puis je stoppe les serveurs privés
  • puis je stoppe le firewall
  • puis je reboote.

Bien évidemment, dans ce monde parfait, les dépendances sont listées dans les fichiers de configuration.....

A défaut de ce monde parfait, voyons quelles solutions nous pouvons mettre en place.

Intercepter l'ordre de la liste

L'idée est de disposer de notre propre script, que j'ai dans mon cas placé en tant que /usr/lib/xen-common/bin/xen-list-domains (pensez à lui donner les droits d'exécution, bien sur !).

Nous allons alors détourner l'appel dans xendomains pour qu'il obtienne sa liste (et donc l'ordre) de notre script:

--- /etc/init.d/xendomains.old
+++ /etc/init.d/xendomains
 -185,12 +185,12  dostopshutdown()
     logactionbegin_msg "Shutting down Xen domain $name ($id)"
     xen shutdown $id 2>&1 1>/dev/null
     logactionend_msg $?
-  done < <(/usr/lib/xen-common/bin/xen-init-list)
+  done < <(/usr/lib/xen-common/bin/xen-list-domains)
   while read id name rest; do
     logactionbegin_msg "Waiting for Xen domain $name ($id) to shut down"
     timeoutdomain "$name" "$XENDOMAINSSTOP_MAXWAIT"
     logactionend_msg $?
-  done < <(/usr/lib/xen-common/bin/xen-init-list)
+  done < <(/usr/lib/xen-common/bin/xen-list-domains)
 }
 
 do_stop()

Nous avons toujours un arrêt en masse des VMs, mais au moins dans l'ordre qui nous arrange..... un xen shutdown -w serait potentiellement dangereux, vu qu'il n'existe apparemment pas d'option pour dire "attends x secondes maximum".

Il ne nous reste plus qu'à voir ce qui est faisable avec xen-list-domains

Nommage des VMs

Ma première tentative a été de mettre l'ordre en tant que nom des VMs: 01-firewall, 55-client, 99-firewall-slave, etc....

Du coup, xen-list-domains est assez simple:

#!/bin/bash

while read name id mem cpu state time ; do
        if [ "$name" != "Name" -a "$name" != "Domain-0" ];then
                echo "$id $name"
        fi
done < <(xl list|sort -r)

Cette approche présente 2 inconvénients:

  • Ca devient vite lourd de devoir se souvenir des numéros de priorité des VMs, qui sont du coup leurs noms aussi.....
  • Le script ne fonctionne pas au reboot.... Apparemment, xen-init-list se comporte un peu différemment pendant le reboot.

Script autonome

Du coup, j'ai fait une 2eme tentative en appelant xen-init-list, mais je ne peux plus trier directement via sort (on doit pouvoir le faire via awk ou un truc du genre, mais je ne suis pas expert). Donc je me suis retrouvé à faire mon nouveau xen-list-domains en perl (là, je sais faire). Et quitte à le faire en perl, je peux embarquer les priorités dans le script:

#!/usr/bin/perl

my %vmorder = (
    "firewall" => 99,
    "firewall-slave" => 0,
    "guest1" => 2,
    "srvnfs" => 98,
    "guest2" => 3,
    "srvlan" => 97,
    "srvpub" => 80,
    "machin" => 40,
    );

my %vmreal;

open(LIST, "/usr/lib/xen-common/bin/xen-init-list|");
while($line=<LIST>){
    my $order;
    my ($id, $name, @rest) = split(/ /, $line);
    chomp $name;
    if(defined($vmorder{$name})){
         $vmreal{$line}=$vmorder{$name};
    }else{
        $vmreal{$line}=1;
    }
}
close(LIST);

foreach (sort { $vmreal{$a} <=> $vmreal{$b} } keys %vmreal) {
   print ;
}

Les VMs non connues ont un poids de 1. Une VM qui a un poids de 0 explicite recevra son signal d'arrêt en premier, puis les VMs non connues, puis les autres connues dans l'ordre de poids (donc dans mon cas, 99 sera la dernière à recevoir son signal).

Dans une version ultérieure, je pourrais stocker les priorités ailleurs (dans les fichers de conf des VMs ?), voire les calculer à partir de dépendances. Vu que mes VMs et leurs dépendances sont assez statiques, je n'ai pas passé plus de temps sur cette partie.


En pratique, quand j'ai besoin de redémarrer l'hyperviseur, je préfère stopper manuellement toutes les VMs, et il est important de faire ca dans un screen, tmux, ou toute autre solution qui permettra que les dernières commandes soient effectivement lancées même quand votre terminal se fera couper le ssh sous le cable !

lundi, avril 2 2018

Déployer un firewall en VM sous Xen pour ma soeur II, le retour

Il y a une éternité très très longtemps, dans une galaxie très lointaine quelques temps, nous avions vu comment déployer un firewall en VM sous XEN. Aujourd'hui, nous allons faire la même chose, mais en mieux.......

Y'a quelqu'un ?

Si vous êtes particulièrement attentifs, vous aurez peut être remarqué que le dernier billet sur ce blog datait d'il y a environ 4 ans (on ne va pas pinailler pour quelques semaines). Et maintenant que je viens de le dire clairement, même si vous avez le niveau d'attention d'un chaton, il faudra au moins quelques bonnes secondes et une baballe qui passe pour vous faire oublier ce fait..... Vous me croyez si vous voulez, mais il semblerait que des gens (je veux dire, de vrais gens, pas seulement des bots) viennent encore sur ce blog de temps en temps. Ne me demandez pas pourquoi, je n'en ai aucune idée, mais les logs sont formels !

Et comme ces billets techniques me servent avant tout de notes pour le jour où je devrai moi même tout refaire en urgence, ca ne change de toutes façons pas grand chose à ma décision de dépoussiérer un peu et faire de nouveaux billets..... et oui, je viens de parler de nouveaux billets au pluriel.......

Ah, et maintenant que je vais cliquer sur "publier", je me dis que, si je l'avais finalisé un poil plus vite, ça aurait pu être une bonne blague !

Disclaimer

Ma soeur a beau avoir fait quelques progrès avec son mac (et je parle bien ici d'un ordinateur), ce billet contient quand même plein de morceaux de trucs techniques plus ou moins dégoulinants qui traînent un peu partout (mais a priori, ni OGMs ni aucune trace de noix). En particulier, il est très nettement préférable d'avoir déjà fait un plan à trois avec une Debian et un Xen au moins une fois dans sa vie.....
Si vous êtes expert pour dézinguer tous les aliens au pied de biche sur Xen, c'est très bien, mais ca ne vous sera d'aucune utilité ici.

Comme toujours sur ce blog, les actions décrites ci après ont été réalisées par un Vieux Con de Hacker Old School (tm) professionnel, dans des conditions de sécurité optimales, et avec un safeword pour rebasculer sur l'ancien hyperviseur+firewall en cas de problème.

Et aucun animal mignon n'a été blessé, tué ou mangé pendant l'intervention.

Et donc, le même truc en mieux, c'est quoi ?

Oui, c'est bon, j'y arrive, on dirait que certains n'ont pas appris la patience avec le temps !!!

L'idée de base reste la même: une machine physique, une Debian qui fait tourner un hyperviseur Xen (d'où les prérequis......), et parmi les VMs, un firewall (dans mon cas, toujours un Stormshield SNS, bien évidemment, mais je suppose que le principe fonctionnera aussi avec des distribs spécialisées comme pfSense ou une autre distrib firewall|) qui doit pouvoir gérer des interfaces physiques comme s'il était un firewall physique avec ces interfaces, ainsi que d'autres interfaces purement virtuelles pour les autres VMs. Un truc dans cette idée là: (oui, c'est exactement le même schéma que pour le billet précédent, maintenant que vous l'avez remarqué, vous savez où vous pouvez aller lire les explications un peu plus détaillées..........)

Mais en mieux..............

Premier point, tout utilise des versions plus récentes ! En 4-5 ans, vous imaginez ??? Rien que chez Debian, par exemple, bah il y a ....... 2 versions d'écart...... ok, c'est pas forcément le meilleur exemple.......
La version de Xen, par contre, fait un assez grand saut, puisqu'on était à l'époque en 4.1, et que nous allons cette fois-ci utiliser la 4.9 embarquée dans Stretch. Et là on commence à avoir des différences sérieuses: nous allons enfin utiliser le toolstack xl de Xen, et profiter de toutes les améliorations de performances et de support de fonctionnalités qui ont été faites pendant tout ce temps.

Ensuite, j'ai une nouvelle machine physique pour l'hyperviseur. Entre autres, la façade réseau de l'ancienne ressemble à ça:


alors que la façade de la nouvelle ressemble à ça:

Ne vous méprenez pas, ce n'est ici pas la taille qui compte (les 2 photos ne sont pas à la même échelle, déjà....), mais bien le nombre de trous d'interfaces réseau physiques, à savoir 24 sur le nouvel hyperviseur...... et on va vouloir s'en servir (éventuellement avec un peu de mauvaise foi si nécessaire, comme le nombre de RJ 45 réellement branchés sur la photo le laisse deviner......):

  • Le bloc de 8 interfaces à droite sera considéré comme un simple switch pour le firewall.
  • Les 4 interfaces avec des RJ45 bleus du milieu seront configurées en LACP. Note pour ma soeur: il n'est pas nécessaire d'avoir des RJ45 bleus pour faire du LACP, mais on s'y retrouve un peu plus facilement si on utilise des câbles de couleur différente......
  • Ce LACP sera en bridge avec les 4 interfaces au dessus, pour avoir au final un gros switch dont la moitié en LACP.
  • Les interfaces du bloc de gauche vont être groupées par paires (celle du dessus, et celle du dessous).

(j'expliquerai l'intérêt de chaque configuration au moment de la mettre en œuvre).

Jusqu'à récemment, je n'arrivais pas à démarrer une VM avec plus de 8 interfaces ethernet déclarées au total, et ma VM de firewall n'en gérait pas plus de 10. J'étais donc obligé de gérer toutes ces configurations directement sur l'hyperviseur.
Aujourd'hui, ces 2 limitations semblent avoir disparues, ou au moins avoir été repoussées: j'ai désormais une configuration fonctionnelle avec 20 interfaces pour la VM du firewall. Cependant, il me parait plus pertinent de gérer les topologies réseau au plus près des connecteurs, tant d'un point de vue performances que pour la simplicité de la configuration: coté hyperviseur, on gère le câblage, et coté firewall on s'occupe du découpage logique du réseau, de la sécurité, etc.... Mais libre à vous de prendre uniquement la version simple de la configuration que nous allons voir, de relier chaque interface au firewall et de gérer les topologies complexes directement sur le firewall.

Autres solutions possibles

Eh oui, ce point n'a pas changé en 4 ans, il y a toujours d'autres solutions possibles ! Au delà du fait que, si j'avais choisi d'autres solutions, je me serais quand même retrouvé à écrire ce paragraphe, passons en revue les alternatives qui méritent un peu plus de détails

Xen ?

Pour mon firewall, je ne pouvais pas partir sur les technos de containers logiciels genre LXC ou Docker, il me fallait un vrai hyperviseur. Du coup, Xen fonctionne bien, je connais et j'ai l'habitude, il est officiellement supporté par les VMs SNS, et je n'ai toujours pas trouvé d'autre hyperviseur qui justifie le changement depuis le temps. Pour être tout à fait exact, je n'ai pas plus que ça cherché non plus.......

PCI Passthrough

On pourrait toujours passer les interfaces réseau directement à la VM du firewall, la techno semble plus mature qu'à l'époque. Cela ne permet pas de brancher entre elles des VMs. Dans mon cas, le firewall n'embarque pas les pilotes nécessaires, donc la solution ne fonctionnerait pas. Et le fait de séparer le câblage de la configuration réseau pure me permet d'avoir une vue plus simple coté firewall.

MACVLAN / MACVTAP

Je n'ai vu ces technologies que très rapidement, elles semblent être des alternatives potentielles pour le cas des interfaces physiques. Pour le peu que j'en ai vu, ce type d'interfaces n'est pas adapté pour raccorder d'autres VMs à la VM du firewall (si tu as des informations pertinentes qui contredisent cette affirmation, ça m'intéresse !).

OpenVSwitch

Cette fois ci, j'ai vraiment essayé OpenVSwitch.... une version intermédiaire de ce billet expliquait même que j'utilisais au final cette solution en production.... et puis je me suis retrouvé à essayer d'avoir des bridges openvswitch fonctionnels et configurés assez tôt au démarrage de mon hyperviseur pour pouvoir démarrer la VM firewall automatiquement...... et je suis revenu aux bridges classiques.....

Du coup, j'ai assez avancé dans mes tests, et il n'y a finalement pas beaucoup de différences au niveau de la configuration:

  • il faut installer le paquet openvswitch-switch au lieu d'installer bridge-utils......
  • il faut modifier /etc/xen/xl.conf pour lui dire d'utiliser le script vif-openvswitch par défaut (ou forcer ce script pour chaque interface de chaque VM, si vous aimez vous compliquer les fichiers de configuration pour pas grand chose......)
  • la configuration des interfaces est un peu différente...... je vous aurais bien résumé comment on configure un bridge avec openvswitch, mais je viens un peu de vous dire que je n'ai pas réussi à avoir une configuration vraiment fonctionnelle en prod........
  • on a les mêmes problématiques d'isolation des interfaces
  • apparemment, on ne peut pas faire de tcpdump directement sur l'interface du bridge (mais j'ai repéré ce point peu de temps avant de rebasculer sur les bridges, donc l'info n'est peut être pas fiable......).

Installation de base

Dans le billet précédent, je n'avais pas du tout détaillé l'installation de base de l'hyperviseur, ni le fait de déployer une VM classique. Cette fois ci, il y a assez de subtilités techniques dans mon cas pour non seulement expliquer certains aspects de mon déploiement, mais en plus pour les expliquer dans un billet dédié.

Mais si vous avez fait une installation classique en suivant les consignes avec un hyperviseur Xen dessus (pas la peine de suivre toute la partie configuration réseau), c'est bien aussi.

C'est bon ? Ok, nous pouvons donc passer aux choses sérieuses !

Configuration réseau du Dom0

Y'a pas, j'ai beau retourner le billet dans tous les sens, on en revient à la configuration réseau à un moment, vu que c'est un peu Ze point technique qui fait que je m'[CENSURE] à rédiger ce billet......

Renommage des interfaces

Avec la nouvelle version de Debian, je me retrouve avec des noms d'interfaces modernes, prédictibles et poétiques comme "ens160" ou "enp11s0f1"...... ok, c'est stable même si on joue au bonneteau avec les interfaces réseau, mais pour s'y retrouver quand on est connecté en ssh sur le serveur pour vérifier un truc sur le réseau, c'est ....... enfin, c'est pas ....... disons que je ne suis pas hyper fan.....

Depuis quelques années sur d'autres installations, j'étais habitué à modifier le quasi magique /etc/udev/rules.d/70-persistent-net.rules...... Sauf que sur mon installation toute fraiche de Debian Stretch, il n'existe plus par défaut, le script tout aussi magique /lib/udev/write_net_rules qui est censé le fabriquer n'existe plus non plus, et les explications à propos de fichiers /etc/systemd/network/xx-nom.link ne fonctionnent pas non plus (je n'ai pas trop creusé pourquoi).

La seule solution qui fonctionne dans mon cas a donc été de recréer manuellement un /etc/udev/rules.d/76-netnames.rules (le numéro et le nom doivent venir du nième lien sur lequel j'ai cliqué pendant mes essais et reboots.......):

SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="ma:ca:dd:re:ss", NAME="ethlan1"

Vous noterez le nom: autant je ne suis donc vraiment pas fan des nommages prédictibles mais pas pratiques du tout, autant nous allons avoir ici pas mal d'interfaces réseau différentes, du coup, quitte à les renommer, autant utiliser des noms qui me permettront par la suite de facilement m'y retrouver, quitte à avoir des noms un peu plus longs. Donc toutes mes interfaces auront un nom du genre "type" "fonction" "numéro optionnel". Donc là, c'est une interface ethernet (eth), que j'utiliserai pour mon LAN, et je prévois d'en avoir au moins 2, donc je lui colle le numéro 1.
J'utiliserai cette convention dans tout le reste du billet sans forcément détailler à chaque fois.

Un cycle complet sera une série de 100 La ligne est bien sur à reproduire et à ajuster autant de fois que d'interfaces à renommer. Les adresses MAC sont à priori les identifiants les plus fiables pour correctement repérer les interfaces réseau, jusqu'au jour où on en rajoutera une, bien sur !

Voilà, il ne reste donc plus qu'à brancher un RJ45 sur chaque prise, repérer quelle interface a été détectée comme ayant un link up, noter son adresse MAC et mettre à jour dans le fichier......... Un petit reboot plus tard et j'ai plein de jolies interfaces avec des noms stables ET compréhensibles pour moi !

Un peu de config pour améliorer le débit

Fût un temps où je vous aurais donné la conf en 24,359 fois (et la 17eme vous aurait vraiment étonné !) et vraiment vraiment en kit. Sans aller jusqu'à dire qu'il vous suffit ici de copier/coller pour que tout fonctionne, je vais de suite poser une info simple: on va passer le MTU à 9000 partout, vu que le support des Jumbo Frames est désormais annoncé comme fonctionnel dans Xen. Avec un test simple (iperf) entre 2 VMs qui traversent le firewall (configuré en mode "laisse passer, cherche pas à analyser"), le simple fait de passer le mtu à 9000 (sur l'ensemble du chemin !!!) permet d'avoir plus ou moins x2 en performances.....

Voilà, voilà....... et à la fin, le Titanic coule.....

/etc/network/interfaces.d

Sur une ancienne version de ma configuration, je me suis retrouvé avec un /etc/network/interfaces difficile à maintenir. Et sur la nouvelle, le nombre total d'interfaces est plus élevé...

Du coup, pour cette nouvelle configuration toute vierge et encore un peu naïve, j'ai décidé de faire propre en utilisant quelques possibilités de ce fichier de configuration:

D'abord, nous allons utiliser les fonctions d'héritage de configuration. Pour prendre un exemple simple qui ne spoile pas trop la suite (parce que c'est pas du tout mon genre, de spoiler la suite):

iface ethtemplate inet static
       mtu 9000

iface ethlan1 inet manual inherits ethtemplate

Isolé, cet exemple ne sert pas à grand chose. Mais en rajoutant quelques autres options de configuration au template, et en se souvenant que nous allons avoir plus de 20 interfaces réseau qui vont utiliser ça, si un jour je veux modifier globalement le MTU de toutes mes interfaces (par exemple si je me rends compte à la fin que c'est encore un peu ambitieux d'utiliser les Jumbo Frames sous Xen ?), je serai content d'avoir utilisé un template !

Cet héritage a également grandement simplifié la version openvswitch de ma configuration, surtout quand j'ai découvert qu'il était possible de faire de l'héritage d'héritage. J'avais alors un truc dans le genre:

iface ovs_eth_template inet manual
            [de la conf très générique pour toutes mes ethernet]
 
iface bridgespecial_t template inet manual inherits ovs_eth_template
            ovs_bridge  bridgespecial

iface ethmembredubridge1 inet manual inherits bridgespecial_t
iface ethmembredubridge2 inet manual inherits bridgespecial_t
iface ethmembredubridge3 inet manual inherits bridgespecial_t
iface ethmembredubridge4 inet manual inherits bridgespecial_t

(parce que forcément, quand il y a une seule interface, c'est un peu moins pertinent.....).


Ensuite, presque tout sera réparti dans de petits fichiers posés dans /etc/network/interfaces.d, à part la configuration du loopback. Les configurations de templates auront des noms de fichiers qui commenceront par 00- (1 fichier par template), et ensuite les autres éléments de configuration seront groupés par bloc logique, également avec des préfixes numérotés pour s'assurer de l'ordre de configuration.


Enfin, plus pour faire joli qu'autre chose, et comme le nommage de mes interfaces me permettra de le faire, il y aura de temps en temps des utilisations d'expressions régulières.

Ca y est, on peut faire la conf réseau, maintenant ?

Oui, on peut, et même qu'on va le faire. Bien évidemment, chaque configuration réseau est unique, voyons donc un peu les différents cas de figures possibles, après libre à vous d'assembler comme il vous plaira.

My name is Bond.....

Ne vous plaignez pas du titre approximatif de cette section, avec un peu d'imagination vous pourrez trouver par vous même à quoi de pire vous avez échappé......

Le principe du bonding, également connu en tant que LACP ou IEEE_802.3ad, comme nous l'avons vu au dessus, est de regrouper plusieurs interfaces ethernet pour n'en faire qu'une seule virtuelle. Les avantages sont en particulier d'avoir un lien qui continue de fonctionner en cas de problème sur l'un des liens (câble coupé, interface défectueuse, etc.....), et pouvoir répartir (plus ou moins efficacement) la bande passante entre les interfaces. A ne pas confondre avec le principe du bondage, donc, qui parle de liens aussi, mais dont le but n'est pas le même !
La répartition de charge est cependant surtout efficace sur de multiples petites connexions de nombreuses sources/destinations différentes. Autant dire que dans mon cas, sur 4 liens, si j'arrive de temps en temps à en utiliser un peu 2, c'est déjà magnifique...... Je parle bien à nouveau du bonding.........

Mais nous n'allons pas nous arrêter pour si peux, et nous allons configurer une interface de type bond, dans /etc/network/interfaces/05-bondlink:

auto ethbond0 ethbond1 ethbond2 ethbond3 

auto bondlink

iface ethbond0 inet manual
iface ethbond1 inet manual
iface ethbond2 inet manual
iface ethbond3 inet manual

iface bondlink inet manual
   slaves ethbond0 ethbond1 ethbond2 ethbond3
   bond_mode 802.3ad
   lacp_rate fast
   bond_miimon 100
   bond_updelay 100
   xmi_hash_policy layer2+3
   mtu 9000

J'aurais bien aimé mettre une expression régulière pour slaves, vu que j'ai intelligemment nommé mes interfaces, mais je n'ai pas trouvé de syntaxe qui fonctionne (si quelqu'un a, ça m'intéresse !).

Les options suivantes sont là pour paramétrer plus précisément le mode d'agrégation de lien, vous pouvez les copier comme des sauvages ou aller lire la documentation pour déterminer quels paramètres vous voulez utiliser. Il est important d'utiliser les mêmes paramètres (en particulier la policy et le bond_mode) sur l'équipement en face !

Je n'ai pas installé le package "ifenslave" (et d'ailleurs, l'exécutable ifenslave est un script qui fait essentiellement passe-plat vers le binaire ip), et pourtant mon interface bondlink s'active correctement.

C'est peut être les soldes, mais j'ai aussi une interface bond0 qui traîne, alors qu'elle n'existe nulle part dans ma configuration (et le fait d'installer le package ifenslave n'y changera rien) !

Je n'utilise pas le template eth, qui sert pour l'instant seulement à fixer le MTU: c'est du coté de l'interface de bonding qu'il faut mettre le MTU à 9000, et c'est reporté automatiquement à toutes les interfaces.
Je n'ai pas créé non plus de template pour les interfaces de bonding, vu que je vais avoir une seule configuration de ce type sur mon serveur, mais si vous avez prévu d'en avoir plusieurs, vous pouvez bien évidemment regrouper la plupart des paramètres dans un template dédié.

D'après la doc, il y a d'autres possibilités de "bond_mode", pas forcément standardisées, mais qui pourrait être intéressantes si vous avez également un Linux en face (qui connaît ces modes, du coup).

Voilà, nous avons maintenant 4 interfaces agrégées en LACP..... sans IP, voyons tout de suite pourquoi.......

Bridges ethernet

Mais quelle transition magique !!!!
Chaque interface ou groupe d'interface (un LACP, par exemple.....) physique sera mise à disposition séparément de la VM firewall via un bridge (littéralement, un pont, donc) dédié. Les interfaces ou groupes n'auront donc jamais d'IP configurée: c'est ainsi que fonctionne une configuration réseau sous Linux, les IPs sont portées par les interfaces bridge (ce qui me parait assez logique, en fait.....).
La plupart des bridges n'auront pas non plus d'adresse IP pour autant: le Dom0 fait uniquement passe-plat (enfin, passe-paquets) entre des RJ45 (ou d'autres VMs) et les interfaces réseau du firewall. Voyons ca.....

Le cas de figure le plus basique est celui d'un bridge sans interfaces ethernet, qui est créé pour que des interfaces virtuelles (celles des VMs, donc) puissent plus tard y être raccrochées.
Par exemple, mon /etc/network/interfaces.d/80-brvpub (le bridge des VMs publiques) contient:

auto brvpub

iface brvpub inet manual inherits brtemplate
   bridge_ports none

La dernière ligne sert à bien confirmer que non, on est pas de gros boulets qui ont oublié la conf, oui on veut vraiment démarrer un bridge qui pour l'instant n'a aucune interface, merci: que ce soit le firewall ou les serveurs de l'autre coté, c'est dans la configuration des VMs que sera indiqué le fait de raccorder leurs interfaces virtuelle (vif) à ce bridge en particulier, pour une raison bien simple: ces interfaces n'existent pas encore au moment de la création du bridge !
Nous aurons le même cas dans tous les exemples ci-dessous, vous serez du coup bien aimables de revenir à chaque fois relire la ligne ci-dessus pour avoir l'explication, qui est la même.....

A peine plus complexe, un bridge qui relie (pour l'instant, donc) une seule interface ethernet. Voyons le contenu de /etc/network/interfaces.d/50-guest (une interface réseau dédiée chez moi aux équipements "invités", donc):

auto ethguest
auto brguest

iface ethguest inet manual inherits ethtemplate

iface brguest inet manual inherits brtemplate
  bridge_ports ethguest

Notez que ici, c'est dans le ethtemplate qu'il y a la configuration du MTU: le bridge prendra toujours forcément le plus petit MTU parmi les interfaces qui le composent.


Nous allons également créer le bridge dédié pour le LACP créé juste avant (oui, au début du billet, il devait être regroupé avec des interfaces en switch, mais au final, j'ai décidé de le mettre sur un bridge dédié, il n'y a que les imbéciles qui ne changent pas d'avis, je l'ai toujours dit et je n'en démordrai pas !), en rajoutant à la fin de /etc/network/interfaces/05-bondlink:

auto brbond

iface brbond inet manual inherits brtemplate
   bridge_ports bondlink


Et pour finir, nous allons "exploiter" le bloc de 8 interfaces pour créer un switch au niveau de l'hyperviseur, via /etc/network/interfaces/10-switch:

auto ethsw1 ethsw2 ethsw3 ethsw4 ethsw5 ethsw6 ethsw7 ethsw8

auto brswitch

iface ethsw1 inet manual inherits ethtemplate
iface ethsw2 inet manual inherits ethtemplate
iface ethsw3 inet manual inherits ethtemplate
iface ethsw4 inet manual inherits ethtemplate
iface ethsw5 inet manual inherits ethtemplate
iface ethsw6 inet manual inherits ethtemplate
iface ethsw7 inet manual inherits ethtemplate
iface ethsw8 inet manual inherits ethtemplate

iface brswitch inet manual inherits brtemplate
   bridge_ports regex ethsw.*

A mon grand regret, le seul endroit où j'ai réussi à trouver une syntaxe de gestion d'expressions régulières, c'est pour le bridge_ports. J'aurais aimé faire un truc genre:

auto ethsw.*
iface ethsw.* inet manual inherits ethtemplate

mais ca ne fonctionne pas (en tout cas sur ma version actuelle !), et mes recherches sur le net pour trouver une autre syntaxe permettant ce genre de choses sont restées infructueuses......


Si vous voulez finalement faire quand même un bridge qui regroupe switches et LACP, c'est facile: il suffit de configurer chaque LACP séparément, et vous pourrez finir par:

iface brenorme inet manual inherits brtemplate
   bridge_ports bondana bondjames ethsw1 ethsw2 ethsw3 ethsw4

On doit même pouvoir faire un truc classouille avec les regex, mais j'ai la flemme de faire des essais pour une configuration que je n'utiliserai finalement pas dans la vraie vie......

On pourrait accélérer le démarrage, stp ?

Oui, on l'avait à vrai dire déjà vu dans l'épisode précédent, ca met un temps fou à démarrer, à cause du Spanning Tree Protocol, STP, donc... Il suffit de le désactiver en indiquant dans le template de bridge:

iface brtemplate inet manual
       bridge_stp 0
       bridge_waitport 0
       bridge_fd 0

Ah, et donc, assurez vous de ne pas faire de boucles réseau, vu que vous venez de désactiver le truc qui les aurait détectées et plus ou moins gérées pour vous.........

Bypass de netfilter

A la création de chaque vif, les scripts Xen rajoutent des entrées de forwarding dans iptables, du genre:

0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-out vifxx --physdev-is-bridged
0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-in vifxx --physdev-is-bridged

Mais dans notre cas, l'analyse des paquets en transit est le boulot de la VM firewall, donc nous allons nous assurer que la pile IP de l'hyperviseur en fait le moins possible, en rajoutant ces 3 lignes dans /etc/sysctl.conf (ou dans un fichier dédié de /etc/sysctl.d si vous voulez faire propre..... ce qui est mieux......):

net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

Sur les versions relativement récentes des noyaux Linux, il sera également nécessaire de forcer le chargement du module br_netfilter au démarrage:

echo br_netfilter >> /etc/modules

(là aussi, vous pouvez faire un fichier dédié dans /etc/modules-load.d)

Petit reboot (ou actions équivalentes à la main si vous préférez), et on pourra constater que les paquets qui doivent transiter sur les bridges circulent sans soucis, même avec un ip_forward=0 au niveau de la configuration de l'hyperviseur, même avec une configuration de filtrage en BLOCK/DROP.

On pourrait du coup aller modifier les scripts de Xen pour qu'il ne génère pas les règles de forward.

Un peu d'isolation réseau.....

A ce moment de la configuration, que ca soit avec bridge ou avec openvswitch, j'ai des bridges sans IP qui se permettent de répondre à des requêtes ARP avec leur propre adresse MAC, ce qui fout rapidement un sacré bordel dans le réseau..... Si j'en crois ce que j'ai lu ici, le problème vient du noyau Linux, qui a une notion pas très exclusive des couples Adresse MAC / Adresse IP correspondante. Sur le principe, tant que c'est entre adultes consentants et fait de façon safe, je ne suis pas le genre de gars à critiquer ce genre de relations libres, sauf que là, justement, ca n'est pas safe du tout, il faut donc y remettre un peu d'ordre.

La solution proposée par Vincent (oui, style genre on est potes de longue date, alors que j'ai découvert son blog il y a quelques jours) est d'activer une fonctionnalité relativement récente du noyau Linux, qui est le filtrage des VLANs sur les ports d'un bridge (je ne fais quasiment que recopier.......). Comme je n'aime pas appliquer une commande sans comprendre ce qu'elle active vraiment, j'ai cherché un peu d'autres infos sur cette fonctionnalité, et à peu près à chaque fois, ca parlait de vraiment faire passer des VLANs sur un bridge, ce qui n'est pas du tout mon cas d'usage.

Toujours d'après Vincent, ebtables n'a pas l'air d'être une solution méga fiable, donc j'ai un peu mis de coté.

A un moment je vais arrêter de citer Vincent, mais pas encore là maintenant de suite, il évoque aussi la possibilité de résoudre le truc avec tc(8). Ce truc a l'air bien, faudra que je regarde......

En pratique, j'ai résolu au moins la partie émergée de mon problème avec une commande assez simple:

ip link set <dev> arp off

Et ce, sur toutes mes interfaces qui n'ont pas d'adresse IP. C'est à dire à peu près toutes mes interfaces, donc, sauf celle d'admin et le loopback.

Pour les bridge et les ethernet, il suffit de modifier les templates, qui deviennent:

iface brtemplate inet manual
       bridge_stp 0
       bridge_waitport 0
       bridge_fd 0
       post-up ip link set $IFACE arp off

iface ethtemplate inet manual 
       mtu 9000
       post-up ip link set $IFACE arp off

Et donc, on fera attention à bien déshériter l'interface d'admin et le loopback, en espérant qu'ils ne fassent pas de procès.......

Pour les vif, je n'ai pas trouvé mieux que modifier /etc/xen/scripts/xen-network-common.sh, et en particulier _setup_bridge_port():

--- /etc/xen/scripts/xen-network-common.sh
+++ /etc/xen/scripts/xen-network-common.sh
@@ -92,6 +92,8 @@ _setup_bridge_port() {
         # stolen by an Ethernet bridge for STP purposes.
         # (FE:FF:FF:FF:FF:FF)
         ip link set dev ${dev} address fe:ff:ff:ff:ff:ff || true
+        ip link set dev ${dev} arp off mtu 9000 || true
+       ethtool -K ${dev} tx off gso off gro off tso off
     fi
 
     # ... and configure it
@@ -133,6 +135,7 @@ add_to_bridge () {
 
 # Usage: set_mtu bridge dev
 set_mtu () {
+    return
     local bridge=$1
     local dev=$2
     mtu="`ip link show dev ${bridge}| awk '/mtu/ { print $5 }'`"

Notez que je ne suis pas tout à fait certain que tout ceci soit vraiment nécessaire: il suffit peut être de mettre cette option uniquement sur les bridges. Mais il n'y a vraiment vraiment aucune raison pour que le noyau s'occupe de quoi que ce soit lié à ARP (voire même de quoi que ce soit en général, d'ailleurs) sur ces interfaces, donc autant le lui dire. Et j'en profite pour glisser aussi une MTU à 9000 sur ces interfaces, n'ayant pas trouvé de méthode qui fonctionne via /etc/network/interfaces pour le faire. Et je désactive donc la fonction set_mtu(), qui ne réagit pas correctement dans le cas d'un bridge qui n'a pas déjà un MTU à 9000 (donc un bridge sans interface physique avec un MTU à 9000, soit plus de la moitié de mes bridges.....).

Je désactive aussi des trucs avec ethtool, l'explication est plus bas, et comme je suis un gars cool je vous livre le patch en une seule fois......

Un peu plus d'isolation réseau

A priori, ca ne devrait pas être nécessaire avec toute cette configuration, et c'est le rôle de la VM firewall d'assurer la sécurité réseau, mais un vieux dicton dit Ceinture, bretelles et calecon propre, donc je vais mettre en place un petit script de filtrage, qui autorise en entrée le ssh, le NTP et le ping:

#!/bin/sh

ADMINIF=bradmin

IPTABLES=/sbin/iptables

# Set filter default policies to "Drop"
# Done first for security reasons
$IPTABLES -t filter -P INPUT DROP
$IPTABLES -t filter -P FORWARD DROP
$IPTABLES -t filter -P OUTPUT DROP

$IPTABLES -t filter -F INPUT
$IPTABLES -t filter -F OUTPUT

# Creating table for Established / related packets.
# Create Table
$IPTABLES -t filter -N stateful

$IPTABLES -t filter -F stateful

# Accept all related packets
$IPTABLES -A stateful -m state --state ESTABLISHED -p tcp ! --syn  -j ACCEPT
$IPTABLES -A stateful -m state --state ESTABLISHED -p udp -j ACCEPT
$IPTABLES -A stateful -m state --state ESTABLISHED -p icmp -j ACCEPT
$IPTABLES -A stateful -m state --state RELATED -j ACCEPT

$IPTABLES -A stateful -m state --state INVALID -j NFLOG --nflog-threshold 20 --nflog-prefix "Fw: invalid state"
$IPTABLES -A stateful -m state --state INVALID -j DROP

$IPTABLES -A INPUT -i lo -j ACCEPT

$IPTABLES -A INPUT -j stateful
$IPTABLES -A INPUT -p tcp -i $ADMINIF --dport 22 --syn -j sshguard
$IPTABLES -A INPUT -m state --state NEW -p tcp -i $ADMINIF --dport 22 --syn -j ACCEPT
$IPTABLES -A INPUT -m state --state NEW -p udp -i $ADMINIF --dport 123 -j ACCEPT
$IPTABLES -A INPUT -m state --state NEW -p icmp -i $ADMINIF -j ACCEPT

$IPTABLES -A OUTPUT -o lo -j ACCEPT

# for the moment, allow all traffic from this host
$IPTABLES -A OUTPUT -j stateful
$IPTABLES -A OUTPUT -m state --state NEW -j ACCEPT

Ce script sera appelé en post-up dans la configuration du bridge d'admin (et cette version suppose que vous avez sshguard d'installé, mais si ce n'est pas le cas cela générera juste une erreur à la création de la règle qui fait référence à la chaine sshguard, le reste fonctionnera normalement).

Ca y est, on est assez isolés, là ?

Dans le cas décrit par Vincent, probablement pas. Sauf que, dans notre cas, l'hyperviseur ne fait aucun boulot de routage. D'ailleurs, le routage est désactivé: net.ipv4.ip_forward et net.ipv6.conf.all.forwarding sont à 0.
Du coup, une tentative de routage forcée consommera probablement quelques cycles CPU en trop avant de se faire jeter dehors, mais entre le routage désactivé, le script de filtrage et la désactivation de tout ce qui est ARP presque partout, je n'ai pas réussi à faire passer des paquets en douce en utilisant les techniques décrites par Vincent (ou en tentant quelques autres variantes).

La VM Firewall

Voilà son fichier de configuration:

name = "Firewall"

builder = 'hvm'

vcpus = 2
maxvcpus = 2
cpu_weight = 256

memory = 1024
maxmem = 1024

#on_poweroff = "restart"
on_watchdog = "restart"
on_crash = "restart"
on_reboot = "restart"

disk = [ 'file:/home/Xen/Firewall.img,hda,w' ]

usb = 0

vif = [
       'type=vif,vifname=vfwout,bridge=brout, mac=00:16:3e:xx:yy:0',
       'type=vif,vifname=vfwlan,bridge=brlan, mac=00:16:3e:xx:yy:1',
       'type=vif,vifname=vfwserver,bridge=brserver, mac=00:16:3e:xx:yy:2',
       'type=vif,vifname=vfwswitch,bridge=brswitch, mac=00:16:3e:xx:yy:3',
       'type=vif,vifname=vfwpub,bridge=brvpub, mac=00:16:3e:xx:yy:4',
       'type=vif,vifname=vfwwifi,bridge=brwifi, mac=00:16:3e:xx:yy:5',
       'type=vif,vifname=vfwbond,bridge=brbond, mac=00:16:3e:xx:yy:6',
       'type=vif,vifname=vfwadmin,bridge=bradmin, mac=00:16:3e:xx:yy:7',
    ]

serial = 'pty'

Même avec le passage au toolstack xl, pas beaucoup de différence avec l'ancienne configuration.
Quitte à mettre des noms prédictibles, stables et explicites à toutes les interfaces réseau de la configuration, autant le faire aussi avec les vif créées, via l'argument vifname=.
00:16:3e: est toujours le préfixe MAC (à peu près rien à voir avec l'ordinateur homonyme de ma soeur, sauf que son MAC a une MAC, et même probablement 2......) réservé pour XEN, vous mettez ce que vous voulez pour xx:yy (mais je vous conseille cependant fortement de mettre des valeurs hexadécimales valides..... et je vous conseille aussi de mettre la même valeur xx:yy pour toutes les lignes, ainsi que de numéroter le dernier octet dans l'ordre, mais là c'est plus pour faire joli et s'y retrouver facilement).
Chaque interface est donc reliée à un bridge différent, et avec les jolis noms de bridges utilisés avant, ca en devient assez lisible.
L'ancienne configuration précisait model=e1000, la nouvelle type=vif, pour des raisons de performances: la VM doit tourner en HVM (virtualisation complète), mais embarque les drivers "PV-HVM" de Xen qui sont plus efficaces qu'un mode complètement émulé (type=ioemu, ce qui est le cas par défaut, et model=e1000, qui est un driver assez efficace quand même en mode ioemu).

Le fichier de conf d'une VM

Là, on est dans du assez simple, la partie réseau de la conf d'une VM classique ressemblera à ça:

vif = [
        'vifname=vifnomdemavm,bridge=brnomdemavm,mac=00:16:3e:00:00:00',
     ]

Vous penserez à mettre autre chose que 00:00:00 à la fin de la MAC, et vous ajusterez le nom de l'interface et du bridge. Notez qu'il est bien sur possible de raccorder plusieurs VMs sur un seul bridge si besoin est.

accélération réseau et vifs

J'avais déjà eu le problème lors de mes anciennes installations: TCP Segmentation Offload et ses potes ne font pas trop bon ménage avec les interfaces Vif...

Ca semblait mieux sur mes premiers tests, mais mes interfaces étaient à ce moment toutes raccordées à des bridges qui contenaient aussi une interface ethernet (disposant elle d'un support de ces accélérations). Et dans mes premiers tests, laisser le support de ces trucs faisait gagner en performances, du coup, cette fois, j'ai essayé de ne pas être aussi bourrin qu'il y a quelques années, et j'ai fait quelques autres essais pour mieux comprendre...... J'ai cru à un moment pouvoir laisser activé l'accélération matérielle sur les bridges qui incluaient une interface ethernet physique. Si ca peut vous aider, j'avais alors fait cette modification (version openvswitch), qui profite du nommage de mes interfaces pour pouvoir déterminer facilement si une ethernet fait partie du bridge:

--- /etc/xen/scripts/vif-openvswitch.old
+++ /etc/xen/scripts/vif-openvswitch
@@ -79,6 +79,12 @@ add_to_openvswitch () {
 
     local vif_details="$(openvswitch_external_id_all $dev)"
 
+    # check if a physical ethernet is connected to the bridge
+    local breth="$(ovs-vsctl list-ifaces $bridge |grep '^eth')"
+    if [ -z "$breth" ]; then
+        ethtool -K $dev tx off gso off gro off tso off
+    fi 
+
     do_or_die ovs-vsctl --timeout=30 \
          if-exists del-port $dev \
         -- add-port "$bridge" $dev $tag_arg $trunk_arg $vif_details

Il y a probablement moyen de l'écrire de façon plus classouille.......

Sauf que ca ne fonctionne toujours pas dans tous les cas: en particulier, chez moi, l'interface d'administration de l'hyperviseur embarque une interface ethernet (pour faire de l'administration réseau urgente avec un portable et un RJ45 si besoin est), mais les paquets émis localement passent par l'interface Vif en fonctionnement normal. Et mon hyperviseur s'est retrouvé joignable en ping, mais ni en TCP, ni en UDP......

J'en suis donc revenu à une désactivation systématique de la fonction sur les vifs, en allant remodifier _setup_bridge_port() dans /etc/xen/scripts/xen-network-common.sh (extrait du patch précédent):

--- /etc/xen/scripts/xen-network-common.sh.old
+++ /etc/xen/scripts/xen-network-common.sh
@@ -92,6 +92,8 @@ _setup_bridge_port() {
         # stolen by an Ethernet bridge for STP purposes.
         # (FE:FF:FF:FF:FF:FF)
         ip link set dev ${dev} address fe:ff:ff:ff:ff:ff || true
+        ip link set dev ${dev} arp off mtu 9000 || true
+        ethtool -K ${dev} tx off gso off gro off tso off
     fi
 
     # ... and configure it

Au début, j'avais aussi tenté de désactiver rx (vérification du checksum en réception), mais il ne semble pas désactivable, et ca ne semble pas poser de problèmes...... Sur le coup, je n'ai apparemment même pas besoin de faire les mêmes désactivations dans mes VMs (à part le firewall, mais c'est fait automatiquement), ce qui m'arrangera quand je ferai d'autres installations en vraies conditions de prod (donc en utilisant le réseau dès l'installation).

Après mes premiers vrais benchs, je me suis retrouvé avec des vif désactivées dans le Dom0 et le message suivant dans le dmesg:

vif vif-xxx viftruc: txreq.offset: 18, size: 9014, end: 9032
vif vif-xxx viftruc: fatal error; disabling device

En cherchant une nouvelle fois un peu sur le net, en faisant quelques essais et en redémarrant à chaque fois la VM concernée par l'interface bloquée (je n'ai pas trouvé mieux comme solution pour réactiver ces interfaces), j'ai fini par constater que le problème ne se pose plus si je fais la même commande ethtool sur les interfaces des VMs. Et vu ce que j'ai eu le temps de mesurer avant d'avoir ces désactivations d'interfaces, les différences de performances sont à peu près inexistantes.

Edit: Après quelques mois d'utilisation, en fait, le problème arrive toujours de temps en temps. Les dernières fois où c'est arrivé, c'était lors d'un classique apt install <un truc>. J'ai eu le même problème en installant le même paquet sur 2 VMs différentes, mais le plantage n'a pas eu lieu sur le même paquet de dépendance en cours de téléchargement. En général, il me suffit d'effacer le paquet "partial" (qui avait fait planter) et de relancer le apt install pour que ça fonctionne. Par contre, une fois, je n'ai pas effacé le partial et relancé le apt install, et j'ai eu à nouveau le problème.

Enfin, quand je dis "il suffit de", ce n'est pas tout à fait vrai: je dois d'abord récupérer une interface fonctionnelle coté hyperviseur ! Si j'en crois ce que j'ai dit dans la première version de ce billet, je pouvais apparemment la récupérer juste en redémarrant la VM. Peut être à cause du nommage des interfaces, je me retrouve maintenant à devoir redémarrer l'ensemble de l'hyperviseur pour récupérer l'interface fonctionnelle, ce qui est franchement désagréable !
Même stoppant complètement la VM, en renommant son interface dans le .xl et en la redémarrant, je ne récupère pas l'interface coté hyperviseur.....

Démarrage et redémarrage....

ordre de démarrage des VMs

En prod, au moins la VM firewall doit démarrer automatiquement. Dans mon cas, la plupart des autres VMs aussi. En plus, certaines VMs peuvent dépendre d'autres au niveau du réseau. Par expérience sur mes anciens hyperviseurs, je réduis au maximum les dépendances réseau de mes VMs au démarrage: pas de DHCP, montages NFS en autofs ou équivalent, etc.... Il est cependant également possible de spécifier l'ordre de démarrage des VMs, en s'assurant de l'ordre des fichiers dans /etc/xen/auto (faites comme tout le monde, mettez des numéros au début des noms de vos fichiers).

Le réseau que vous demandez n'est pas encore disponible....

Dans ma version actuelle, tout fonctionne, mais pas au boot: les VMs ne démarrent pas, et en creusant un peu, c'est tout simplement parce que les bridges ne sont pas encore prêts au moment où le système veut démarrer les VMs. Après avoir creusé un peu, j'ai trouvé une solution assez simple qui consiste à préciser que le démarrage des domaines Xen nécessite un réseau déjà établi:

--- /etc/init.d/xendomains.old
+++ /etc/init.d/xendomains
@@ -1,8 +1,8 @@
 #!/bin/bash
 ### BEGIN INIT INFO
 # Provides:          xendomains
-# Required-Start:    $syslog $remote_fs xen
-# Required-Stop:     $syslog $remote_fs xen
+# Required-Start:    $syslog $remote_fs xen $network
+# Required-Stop:     $syslog $remote_fs xen $network
 # Should-Start:      drbd iscsi openvswitch-switch
 # Should-Stop:       drbd iscsi openvswitch-switch
 # X-Start-Before:    corosync heartbeat libvirtd

A propos de réseau pas disponible et de boot, dans ce genre de configurations, pensez à vérifier le contenu de /etc/network/if-up.d (et if-pre-up.d): dans mon cas, après avoir installé openntpd, je me suis retrouvé avec un script de redémarrage du démon à chaque activation d'interface réseau...... j'ai plus de 80 interfaces réseau, dont 30 activées pendant le démarrage...........

Redémarrage de l'hyperviseur

Par défaut, lors de l'arrêt de l'hyperviseur, les scripts (au moins chez Debian ?) stoppent les VMs dans l'ordre inverse de démarrage (la plus récemment démarrée en premier, donc). L'idée n'est pas mauvaise, jusqu'au moment où vous allez vouloir redémarrer une VM, pour une raison ou une autre..... Après quelques bricolages, j'en suis arrivé à la conclusion que le plus simple serait d'intercepter le script /usr/lib/xen-common/bin/xen-init-list (appelé par les scripts d'arrêt des domaines Xen) pour changer l'ordre de son affichage. Pour l'instant, je n'ai pas encore de solution générique propre, du coup il n'y a pas d'intérêt à partager mon bricolage qui ne fonctionne que chez moi.........

vendredi, juin 27 2014

Déployer un firewall sous Xen pour ma soeur.....

Il y a quelques temps, j'ai commencé à faire mumuse avec la virtualisation (sous Xen), y compris pour un cas de figure un poil complexe sur lequel je n'avais trouvé aucune doc sur le net (si vous voulez juste déployer quelques VMs classiques dans un hyperviseur Xen, vous n'êtes pas au bon endroit, il existe plein de tutoriels pour ce cas de figure basique).

Ca tombe bien, vu que je n'avais pas fait de nouveau billet aujourd'hui cette semaine ces derniers temps depuis au moins un an, et comme j'ai quelques potes qui envisagent de faire une conf similaire, c'est un prétexte que je ne pouvais pas louper.

En plus, ca me servira aussi probablement le jour ou mon disque dur va me lâcher et que je vais devoir tout refaire.......


Disclaimer

Attention: contrairement à ce que le titre pourrait laisser croire, ce billet contient de nombreux extraits naturels de trucs techniques plus ou moins compliqués. En particulier, des connaissances de base en virtualisation sont à peu près indispensables, et une expérience de Xen en particulier est vivement souhaitée.

En cas de dépressurisation cérébrale suite à la lecture de ce billet, des masques à oxygène ne tomberont probablement pas automatiquement du plafond. Vous êtes invités à repérer à l'avance les issues de secours, qui ne seront probablement pas non plus indiquées par des consignes lumineuses en cas d'incident.

Les actions décrites ci-après ont été réalisées par un Vieux Con de Hacker Old School (tm), qui porte des t-shirts de geek, qui va généralement plus vite en utilisant un shell ou emacs qu'avec une interface graphique, un gars qui a survécu à la Grande Guerre (entre l'Amiga et l'Atari ST), tout ça....

Et bien évidemment, tout a été réalisé dans des conditions optimales de sécurité, avec un bouton rouge, une équipe d'intervention d'urgence prête à réagir à tout moment, 8 litres de coca (note pour moi même: en rester là, j'ai déjà fait la reprise de la blague de Naheulbeuk avec la liste de courses.....), et un vrai réseau de prod à coté pendant toute la durée de l'opération.

Donc les enfants, NE FAITES SURTOUT PAS CA CHEZ VOUS, et si vous le faites quand même (bah oui, on ne va pas se mentir: dire à quelqu'un "ne fais surtout pas ça", c'est quand même un excellent moyen de l'inciter à le faire, juste pour voir.....), vous serez seul(e) responsable de toute conséquence dramatique qui en découlerait, comme une instabilité réseau, la perte de données, la création d'une brêche dans le continuum spatio-temporel, le départ de votre copine, un contrôle fiscal, l'arrivée de votre belle mère, une halitose subite, ou tout autre désagrément non listé ci-dessus.
Notez cependant que, à l'exception d'une légère instabilité réseau lors de la première mise en prod, aucun des autres symptômes cités en exemple n'est survenu lors de ma mise en prod !

Ah, et aucun animal mignon n'a été blessé, tué ou mangé pendant l'intervention.


Ok, et on voulait pas faire un vrai truc, à un moment ?

Ne soyez pas impatients, comme ça, si, effectivement, on voulait faire un vrai truc, que j'ai essayé de transcrire en un (magnifique, je sais) schema (cliquez dessus pour l'agrandir):

SchemaReseauDom0


En admettant que vous n'ayez pas tout à fait compris la configuration, malgré la profusion de couleurs et mon extraordinaire maîtrise de l'outil de création du truc, l'idée est donc, comme le titre du billet le suggérait quand même déjà relativement clairement, de déployer un firewall virtuel (ais-je vraiment besoin de préciser la marque du firewall dans mon cas ???) dans un hyperviseur Xen.

Ce qui pourrait paraître relativement classique (un hyperviseur, une VM, du réseau) pose cependant une problématique particulière dans mon cas: je veux pouvoir gérer à ma convenance un ou plusieurs bridges au niveau du firewall (donc avec filtrage, IPS, tout ça...), sans devoir faire de modifications de configuration sur le Dom0 à chaque fois, et évidemment sans perturbations par le Dom0, sans paquets qui passent discrètement d'une interface physique une autre en glissant un petit biffeton à l'hyperviseur, ou autres trucs pas recommandables dans un réseau de bonne famille et propre sur lui.

Dit autrement, tout doit réagir exactement comme si les interfaces réseau physiques étaient directement reliées au firewall.

Et, accessoirement, si ça peut fonctionner, c'est mieux, y compris avec d'autres VMs sur le même hyperviseur, également branchées sur des interfaces dédiées du firewall.

Dans tout le reste de l'explication, on considèrera 8 interfaces pour le firewall, dont 4 reliées à des interfaces physiques, si votre configuration est différente, l'idée reste la même, il suffit d'adapter....


Autres solutions possibles


Un autre hyperviseur

Euh, oui, on aurait pu, effectivement..... mais en gros, VMWare n'est pas OpenSource, Xen est officiellement supporté par ma VM de firewall (ok, la version commerciale, mais dans les faits, je sais que ça fonctionne bien), et aucune autre solution de virtualisation ne m'a paru avoir le "truc en plus" qui aurait justifié le changement de techno.

Et j'en vois deux dans le fond qui auraient de toutes façons fait la remarque "mais y'avait d'autres solutions" si j'en avais choisi une autre.....


OpenVSwitch

Pour la configuration réseau au niveau de l'hyperviseur, j'ai utilisé les bridges Linux (brctl, tout ça.....).
On aurait pu également utiliser OpenVSwitch, j'ai le souvenir d'avoir un peu regardé cette solution, et de l'avoir mise de coté pour une bonne raison à l'époque...... bonne raison dont je ne me souviens plus, évidemment....

Il est également possible que cette bonne raison soit devenue obsolète depuis.


Un firewall physique

Euh, oui, on peut, mais c'est pas écolo, c'est pas tendance (virtualisation, nuages, blabla.....), et c'est un peu trop facile pour être fun.
Accessoirement, ca ne répond pas non plus au cas de figure "autres VMs à filtrer via le firewall", en tout cas pas simplement, si j'ai plusieurs VMs que je veux segmenter entre elles.


Passer directement les interfaces réseau à la VM

Sur le papier (enfin, sur le site web, ne commencez pas à pinailler comme ça), Xen sait faire, ca s'appelle le PCI Passthrough.
Et c'est fort logiquement la première option que j'avais étudié quand j'ai commencé à bricoler ma configuration.

Sauf que, dans les faits, ca n'est pas aussi simple, il faut déjà avoir une carte mère qui supporte l'IOMMU, ce qui n'était pas mon cas.
Si la carte mère de votre Dom0 le supporte, cependant, c'est probablement une possibilité intéressante à tester !

Et cette solution ne gère pas le branchement entre le firewall et d'autres machines virtuelles, donc ne serait applicable que pour les interfaces physiques.


DomO, VM, etc...


Installation de l'hyperviseur Xen

L'installation de base est une procédure relativement simple, et allègrement documentée sur le net.

Dans notre exemple, il s'agira d'une Debian, je ne vous ferai pas l'affront d'expliquer ici comment on installe une Debian de base, et la mise en place de l'hyperviseur Xen est également documentée sur le wiki Debian sur Xen (n'allez pas trop loin dans les manips du wiki, en particulier pour tout ce qui est configuration réseau, c'est pour le cas "classique").

Note: il existe plusieurs facades pour configurer/administrer un Xen. J'étais parti pour une administration avec le toolstack Xl, mais j'ai eu divers problèmes avec, entre autres à cause de la version de Xen fournie par ma Debian. Je me suis donc rabattu sur le toolstack xm, raison pour laquelle les exemples de configuration seront fournis sous ce format.

Cependant, xm/xend est marqué DEPRECATED, il est donc préférable de convertir les configurations au format xl si vous utilisez une version récente de Xen.


Préparation et configuration de la VM Firewall

Dans mon cas, la VM Firewall fait tourner un dérivé de FreeBSD, ce qui nécessite une configuration un poil différente pour le démarrage:

name = "Firewall"

kernel = "/usr/lib/xen-4.1/boot/hvmloader"
builder = 'hvm'

vcpus = 1
maxvcpus = 1
cpu_weight = 512

memory = 1024
maxmem = 1024

#on_poweroff = "restart"
on_watchdog = "restart"
on_crash = "restart"
on_reboot = "restart"

serial = 'pty'

disk = [ 'file:/home/Xen/Firewall.img,hda,w' ]

Pour l'essentiel, cependant, cette partie de la configuration est très classique (vous penserez à mettre à jour le chemin de la ligne kernel, si votre version de Xen est différente, évidemment.....), je lui réserve 1 VCPU et 1Go de RAM dans mon cas, et comme c'est un firewall, je décrète que presque tous les cas d'arrêt de la VM nécessitent un redémarrage (sauf un arrêt explicite et volontaire).

Le serial='pty' me permettra également d'avoir un accès facile à la console de ma VM depuis un shell de l'hyperviseur en cas de soucis (note pour moi: KernelMsg=1). Selon votre VM firewall, la méthode sera peut être différente.


Configuration réseau


A un moment, on veut faire des trucs pour le réseau, il va bien falloir le configurer.....

Configuration du Dom0

D'un point de vue Dom0, on va créer plein de bridges à 2 interfaces: un pour chaque interface physique du Dom0 (à chaque fois couplé à une interface du firewall), et un pour chaque autre VM (ou éventuellement groupe de VMs) présente dans l'hyperviseur.
Il faut donc installer le package bridge-utils:

apt-get install bridge-utils

Les interfaces du Dom0 n'auront pas d'adresse IP (les bridges non plus, d'ailleurs), il va donc falloir forcer leur activation manuellement.
Dans /etc/network/interfaces, pour une interface physique ethx donnée, on aura donc:

auto ethx
auto brx

iface ethx inet manual

iface brx inet manual
       bridge_ports ethx

Dans le cas d'un bridge non relié à une interface physique, la seule différence est qu'on indiquera:

      bridge_ports none

(vous aurez la présence d'esprit de remplacer à chaque fois x par le numéro de l'interface, ça reste valable dans toute la suite de l'explication)

Notez que vous pouvez à priori remplacer "inet" par "inet6", pour faire tendance, ce qui ne devrait absolument rien changer: il ne s'agit pas ici de configurer des interfaces, mais juste de les activer et de les relier à des bridges qu'on crée au passage.
Selon le niveau de support et de configuration d'IPv6 par votre VM firewall, il sera peut être nécessaire de désactiver pas mal de choses au niveau IPv6 sur votre Dom0, y compris (surtout ?) les adresses Lien Local (celles en fe80::), ce qui vous évitera des boucles réseau étranges en IPv6...

Pour pouvoir accéder au Dom0 depuis le réseau, une interface aura un status un peu particulier: elle aura une adresse IP, la veinarde !
Enfin, plus précisément, le bridge sur lequel elle est attachée aura une IP, puisque c'est le fonctionnement normal sous Linux: une interface reliée à un bridge n'est plus utilisable directement.

Par rapport aux autres bridges, tout simplement, on va remplacer "inet manual" par "inet static" (et/ou par inet6 static si vous êtes tendance), et rajouter address, netmask, gateway (l'IP du firewall sur le LAN, en configuration finale, l'IP de votre passerelle actuelle pendant la préparation). Une configuration type DHCP, autoconf en v6 ou autre est à éviter: votre Dom0 démarrera logiquement avant tout le reste, y compris le firewall (bah oui, logique....), et ne saura donc peut être pas accéder à un serveur DHCP...
J'ai également du forcer son adresse MAC (hwaddress, j'ai mis l'adresse MAC de l'interface physique), vous verrez pourquoi après.

Idéalement, si votre niveau de parano est assez élevé (et surtout si vous avez un moyen assez facile d'accéder à votre Dom0 en console en cas de problèmes), dans la configuration finale, ce bridge sera reliée à une interface dédiée de la VM firewall (et donc à aucune interface physique).

  • avantage: permet de filtrer et protéger le Dom0
  • inconvénient: en cas de problème avec la VM firewall (erreur de configuration, reboot, mauvaise manip, etc...), la console sera le seul recours !

Une interface physique dédiée (et toujours reliée à une interface dédiée du firewall) vous permettra de venir brancher en urgence un ordinateur portable pour réparer d'éventuels problèmes, tout en bénéficiant de la même protection par le firewall en usage normal.

A défaut, si vous ne laissez pas n'importe qui se brancher sur votre LAN, et si vous mettez en place un filtrage au niveau du Dom0 n'autorisant que les accès ssh vers celui-ci (en certificats uniquement, évidemment), le niveau de sécurité restera tout à fait correct, et permettra une intervention simplifiée en cas de soucis.

Si vous êtes vraiment cool et partageur, un accès telnet en openbar sur une IP publique (et sans mot de passe pour root, on ne va pas commencer avec des mesquineries du genre) fera probablement plein d'heureux sur le net......

Selon votre topologie réseau, vous serez peut être amenés à configurer ainsi plusieurs interfaces/bridges sur le Dom0 (si vous avez plusieurs plans d'adressages qui peuvent servir à administrer en urgence), il suffira de reproduire la même idée pour chacune de ces interfaces.


Configuration réseau de la VM Firewall

Pour notre VM, nous utiliserons donc le script vif-bridge, ce qui donne dans la configuration de la VM firewall, pour la partie réseau:

vif = [
       'bridge=br0,model=e1000,mac=00:16:3e:xx:0',
       'bridge=br1,model=e1000,mac=00:16:3e:xx:1',
       'bridge=br2,model=e1000,mac=00:16:3e:xx:2',
       'bridge=br3,model=e1000,mac=00:16:3e:xx:3',
       'bridge=br4,model=e1000,mac=00:16:3e:xx:4',
       'bridge=br5,model=e1000,mac=00:16:3e:xx:5',
       'bridge=br6,model=e1000,mac=00:16:3e:xx:6',
       'bridge=br7,model=e1000,mac=00:16:3e:xx:7',
    ]

Ici, on force le type d'interface à e1000 (le type par défaut était moins performant au niveau de la VM, de mémoire), et on va également forcer les adresses MAC pour éviter d'avoir un bordel ambiant à chaque redémarrage de la VM. Le préfixe 00:16:3e est réservé pour Xen, ce qui évitera des collisions avec d'autres cartes ethernet du réseau, vous mettez ce que vous voulez à la place de :xx: (qui est d'ailleurs :xx:xx:, vu qu'une adresse MAC est codée sur 6 octets), et tant qu'à faire, on va numéroter le dernier octet dans l'ordre pour faire propre.


Bricolage et magie noire sur le Dom0

Si toutes vos interfaces ont des plans d'adressages différents, cette configuration devrait presque suffire à faire fonctionner le truc.
Sauf que, dans mon cas, je veux pouvoir gérer des bridges au niveau du firewall, et c'est là que ca devient poilu, vu que, en l'état, ça ne fonctionnera pas (ou en tout cas pas correctement).


Gestion des paquets entrants

Le premier problème, c'est que les interfaces du Dom0 n'accepteront pas les paquets à destination d'autres adresses MAC (ou en tout cas pas toujours, selon la configuration de plein de trucs). Il faut donc forcer toutes les interfaces avec l'adresse MAC de broadcast.

Nous allons donc rajouter la ligne suivante dans la configuration de toutes les ethx:

up /sbin/ifconfig ethx -arp hw ether fe:ff:ff:ff:ff:ff

Pareil pour les bridges, sauf qu'il est nécessaire de le faire en post-up (en phase "up", apparemment, le bridge n'existe pas encore assez pour l'opération):

post-up /sbin/ifconfig brx -arp hw ether fe:ff:ff:ff:ff:ff

C'est à ce moment que l'intérêt de forcer l'adresse MAC de l'interface d'administration apparaît....
Maintenant que j'y repense, je me rends également compte que, sur cette interface, l'option hwaddress fonctionne très bien, y compris sur une interface de type bridge...... je n'ai pas fait le test (j'étais déjà en prod quand j'y ai pensé), mais l'option devrait aussi logiquement fonctionner ici, pour les eth comme pour les br:

iface <un ethx ou un brx> inet manual
    hwaddress fe:ff:ff:ff:ff:ff
    bridge_ports ethx # seulement pour les interfaces brx


Bypass de netfilter

Le second obstacle, c'est que tous les paquets passent par le Netfilter du Dom0, qui peut donc les bloquer, les router, ou faire d'autres choses avec, que la morale n'approuve pas forcément...
Nous allons donc indiquer au système que les paquets qui passent sur un bridge ont une bouteille à l'intérieur, connaissent bien le patron, et seront gérés par le service de sécurité de la salle VIP s'ils ont des baskets ou une casquette.
Ca se fait en ajoutant à /etc/sysctl.conf:

net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

Activez à la main ces sysctl si vous voulez économiser un reboot.


Accélération du démarrage

A un moment, vous remarquerez que le démarrage du Dom0 est très long, curieusement depuis qu'on a modifié la configuration réseau pour rajouter tous les bridges....
Pour revenir à une vitesse de démarrage normale, il suffit d'ajouter les paramètres suivantes dans chaque section brx du fichier /etc/network/interfaces:

       bridge_stp off
       bridge_waitport 0
       bridge_fd 0

Evidemment, si vous avez un réseau avec plein de boucles, désactiver le protocole STP partout est probablement une très très mauvaise idée.....


Amélioration des performances

A ce moment, votre configuration devrait fonctionner, mais avec des performances réseau complètement pourries dès que ça traverse le firewall, voire des sessions TCP qui ne finissent jamais vraiment.

Une enquête approfondie réalisée conjointement entre Les Experts et NCIS révèle des trucs très étranges sur le réseau, dont par exemple des paquets qui dépassent allègrement la MTU, voire qui arrivent plus gros qu'ils ne sont partis.

Le grand méchant de cette histoire est finalement l'accélération matérielle des cartes réseau, en particulier la segmentation déportée des gros paquets et/ou le déport des sommes de contrôle.
Votre moteur de recherche préféré vous confirmera rapidement que ces trucs là ne font pas forcément bon ménage avec la virtualisation, en tout cas sous Xen, en tout cas aujourd'hui.

Nous allons donc désactiver tout ça sur les interfaces vif (le pendant sur le Dom0 des interfaces des VMs), à l'aide de la commande ethtool (donc oui, apt-get install ethtool).
Au passage, tant qu'on est là et qu'on parle de performances, réseau, on va également augmenter la taille de la file d'attente des interfaces vif, qui est à une valeur très faible par défaut.

Pour que les modifications soient faites automagiquement à chaque création d'interface vif, nous allons donc modifier le script /etc/xen/scripts/vif-bridge:

[....]
case "$command" in
   online)
       setup_virtual_bridge_port "$dev"
       mtu="`ip link show $bridge | awk '/mtu/ { print $5 }'`"
       if [ -n "$mtu" ] && [ "$mtu" -gt 0 ]
       then
               ip link set $dev mtu $mtu || :
       fi
       add_to_bridge "$bridge" "$dev"
       ;;

devient:

[....]
case "$command" in
   online)
       setup_virtual_bridge_port "$dev"
       mtu="`ip link show $bridge | awk '/mtu/ { print $5 }'`"
       if [ -n "$mtu" ] && [ "$mtu" -gt 0 ]
       then
               ip link set $dev mtu $mtu || :
       fi
       ethtool -K "$dev" tso off gso off gro off tx off rx off
       ifconfig "$dev" txqueuelen 1024
       add_to_bridge "$bridge" "$dev"
       ;;

Sur ma configuration, ces deux petites lignes permettront de faire passer le débit de "3-4 Mb/s quand ça fonctionne" à "environ 400Mb/s et ça fonctionne tout le temps", ce qui est quand même assez appréciable (c'est apparemment la limite de mon serveur de fichier qui commence à se faire vieux), il faut bien le reconnaître, et sans aucune substance illégale, en plus !

Il sera probablement nécessaire de désactiver les mêmes fonctions au niveau de votre VM firewall, si celle-ci ne le fait pas automatiquement. Dans mon cas, c'est fait nativement.

En théorie, sur un réseau Gb, on gagnerait encore en performances en utilisant des jumbo frames, sauf que, toujours sur ma version actuelle de Xen, le support des jumbo frames est expérimental....


Configuration réseau finale du Dom0

Pour résumer la configuration finale (oui, j'aurais pu directement vous donner cette configuration, mais l'intensité narrative y aurait perdu, et je me trouve à vrai dire déjà assez sympa comme ça de vous faire un résumé) de /etc/network/interfaces va donner, pour une interface ethx (oui, vous ferez à nouveau l'effort de remplacer x par le bon numéro, et de faire la conf pour chaque interface):

auto ethx brx

iface ethx inet manual
       hwaddress fe:ff:ff:ff:ff:ff

iface brx inet manual
       bridge_ports ethx
       bridge_stp off
       bridge_waitport 0
       bridge_fd 0
       hwaddress fe:ff:ff:ff:ff:ff

Et vous mettrez "bridge_ports none" au lieu de "bridge_ports ethx" pour les bridges non reliés à une interface physique, et vous ferez la modification de configuration qui va bien pour le bridge qui aura une adresse IP.


Configuration réseau de la VM

A partir de là, c'est un fonctionnement et une configuration normales au niveau de la VM firewall. Dans mon cas, j'ai une interface externe sur un plan d'adressage dédié (zone encadrée en rouge sur le schéma du début), et toutes les autres interfaces du firewall (chacun encadré en bleu sur le même schéma) sont mises dans un bridge commun (au niveau du firewall, donc).

Il reste à faire la configuration réseau de la VM, les règles de filtrage, la configuration IPS, le NAT, et tout ce qui est à configurer lors d'un déploiement de firewall.


D'autres VMs pendant qu'on y est...

A partir de ce moment, installer d'autres VMs sur l'hyperviseur est une activité classique:
On récupère de quoi faire une install, comme par exemple l'image ISO Debian pour une installation de DomU, on fait "ce qu'il faut" pour avoir un espace disque (dans mon cas, donc, qemu-img create, mais il parait que c'est plus tendance d'utiliser lvm), et on lance l'install en suivant un tutoriel classique d'installation de VM pour Xen.

Pour les archives, je rajoute à la configuration Xen de cette VM:

 extra = "console=hvc0"

Cette commande me permettra d'accéder à la console de la VM depuis un shell de l'hyperviseur (il me semble qu'il fallait aussi faire une modif dans la conf grub de la VM......).

La seule subtilité de notre configuration par rapport aux tutoriels standards est fort logiquement au niveau de la configuration réseau. Si on veut relier la VM à l'interface x du firewall (et comme on a fait un numérotage propre et homogène sur toute la ligne), on aura:

vif = [
       'bridge=brx,model=e1000,mac=00:16:3e:<de l'hexa>'
    ]

I am the king of the wooooorld !!!

Bon, ok, du monde, peut être pas, mais de mon hyperviseur et de ma VM firewall, en tout cas, oui, du moins tant que je conserve des mots de passe et une configuration de filtrage corrects.

Ca n'est pas forcément hyper efficace pour pecho en boite, et c'est déjà parfois assez limite comme sujet de discussion quand des amis passent à la maison et demandent naivement "quoi de neuf".
Ca pourrait faire le café, mais comme je n'en bois pas, je n'ai pas testé.

Mais au moins, ca permet d'avoir un firewall/IPS/UTM qui protège le réseau physique ET les machines virtuelles, tout en conservant une quantité de serveurs limitée, et si vous relisez bien le début du post, je n'avais pas promis plus !

mardi, novembre 2 2010

enum Vs define

Cramé pour cramé, et ayant repris une activité de blogging (oui, j'ai commencé ce billet y'a un bout de temps, juste après le SSTIC :-) ), autant pousser jusqu'au bout.....


En plus du vil et mesquin dépilage de vieux billets en attente (mais oui, ca va venir..... un jour..... surement......), ca fait quelques temps que je cherchais un sujet sympa pour un billet technique, à défaut de petits jeunes au labo pour m'en proposer de temps en temps (Jo, si tu lis cette phrase, tes pinces à tétons certifiées "CE" nous manquent ! :-) )


Le sujet du jour sera donc "enum ou defines dans un source en C", j'aurais bien fait monter le suspense méga grave, mais comme l'info était déjà dans le titre, j'ose espérer que vous vous en doutiez déjà, sinon vous risquez d'avoir du mal à lire la suite.....

T'as un problème qui te fait faire du soucis ?

Ouaip: au boulot, on se pose régulièrement la question quand on a une liste de valeurs possibles à stocker dans un int: on fait des defines ou un enum ? Et il se trouve qu'on s'est reposé aujourd'hui une question sur les enum......

Un exemple permettra presque à ma soeur de comprendre le problème, pour peu que ca soit concret.... Supposons qu'on veuille stocker dans une variable le jour de la semaine.
La solution à base de define donne ca:

#define DIMANCHE 0
#define LUNDI 1
#define MARDI 2
#define MERCREDI 3
#define JEUDI 4
#define VENDREDI 5
#define SAMEDI 6
int jourdelasemaine;

La solution à base d'enum donne ca:

enum {DIMANCHE, LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI} jourdelasemaine;

Round 1 ..... FIGHT !!!!

On voit tout de suite que la solution à base de defines a l'indéniable avantage de permettre de facturer plus de lignes de code au client, et qu'en plus elle passe mieux dans la mise en page du blog.....
Ca fait également plus technique, surtout si on se met à déclarer les valeurs en hexadécimal, ce qui donne:

#define DIMANCHE  0x0
#define LUNDI     0x1
#define MARDI     0x2
#define MERCREDI  0x3
#define JEUDI     0x4
#define VENDREDI  0x5
#define SAMEDI    0x6
int jourdelasemaine;

Alors que l'enum nous mache le travail, et va même jusqu'à commencer automatiquement la liste à 0 si on ne précise pas la première valeur.....

Bon, bah voila, je pensais qu'il serait un peu plus long que ca, ce billet, en fait.....

La prochaine fois, on parlera d'autre chose, du coup.....

Round 2 ..... FIGHT !!!!

Bon, je suis chaud bouillant comme une baraque à frites aussi vais-je rajouter l'hypothèse certes peu vraisemblable suivante: l'argent n'a pas d'odeur (en tout cas, pas beaucoup quand on est juste à coté de la baraque à frites susnommée), et on veut surtout faire un truc logique et intelligent.

En plus, par souci d'équité et de neutralité, je me dois de vous signaler qu'on peut aussi faire ca:

enum {
     DIMANCHE,
     LUNDI,
     MARDI,
     MERCREDI,
     JEUDI,
     VENDREDI,
     SAMEDI}
 jourdelasemaine;

Et donc facturer à peu près aussi cher l'implémentation à base d'enum.....

Une fois ce nouveau contexte établi, la solution à base d'enum pourrait paraitre la plus logique, en tout cas dans des langages de noobs...
Mais en C, il n'en est rien: la norme précise en effet qu'il ne s'agit que d'une facilité à l'édition, et que l'enum en question reste de toutes facons stocké dans un int.

On peut facilement le vérifier, par exemple avec le code suivant:

jourdelasemaine=SAMEDI;
printf("%d\n", jourdelasemaine);
jourdelasemaine++;
printf("%d\n", jourdelasemaine);
jourdelasemaine=42;
printf("%d\n", jourdelasemaine);

Tout ca fonctionne normalement, l'enum ne sert donc à rien par rapport à un define, si ce n'est éventuellement l'affectation automatique des valeurs (on a pas eu besoin de dire que dimanche=0, c'est implicite par défaut).... quand on a des valeurs qui se suivent, évidemment.....

Round 3 ..... FIGHT !!!!

D'un point de vue vérification de cohérence à la compilation, on est donc marrons (marron fricadelle, pour être précis), on peut faire tout et surtout n'importe quoi avec les deux approches, match NULL pour l'instant.....

Peut être à un petit détail près, qui m'a été signalé à l'instant par un stagiaire développeur du labo:

Dans le cas ou on fait un switch sans cas par défaut, si c'est un enum, le compilateur (en tout cas au moins un gcc récent) va signaler s'il manque une valeur de l'enum:

En reprenant notre enum de tout à l'heure, si on fait:

int isWE(jourdelasemaine j){
    switch(j){
        case lundi:
        case mardi:
        case jeudi:
        case vendredi:
            return 0;
            break;
        case samedi:
        case dimanche:
            return 1;
            break;
    }

}

Bah le compilateur va raler parcequ'on a oublié la valeur "mercredi"...... enfin, si on compile en -Wall Werror, mais tout le monde compile tous ses programmes comme ca, pas vrai ?

Ca suppose aussi qu'on n'utilise pas la facilité du target par defaut d'un switch, ce qui est quand même assez fréquent.......

Round 4 ..... FIGHT !!!!

Qu'en est-il de l'utilisation mémoire ?

A priori, léger avantage au define: un enum est systématiquement stocké sur un int (en tout cas, j'ai pas trouvé comment le faire tenir sur moins), alors que, dans le cas du define, on peut déclarer explicitement un short, un u_int8_t ou autre chose dans le genre qui ne prend pas trop de place quand on sait que ca va suffire pour faire tenir toutes nos valeurs possibles (et dans mon exemple, ca va tenir à l'aise), voire carrément un champ de bits si on est dans une struct et qu'on a d'autres champs de bits à poser à coté.......

Round 5 ..... FIGHT !!!!

Bon, je me foule vraiment pas en titres intermédiaires, moi, sur ce coup la......

le dernier aspect, c'est l'épépinage du code....

Et la, faut reconnaitre que l'enum a un avantage: gdb va indiquer la valeur de l'enum, alors que, avec un define, il va afficher la valeur numérique. Donc, dans le cas ou on est pas trop boulet et ou on a mis des noms explicites pour ses enum, faut admettre que ca fera gagner un peu de temps......

D'un autre coté, quand on est pas trop boulet, on n'a pas besoin de gdb, si ? :-D

And the winner is .....

Si on résume, le define est plus approprié pour grapiller quelques octets, et l'enum est plus facile pour débugguer....... dit autrement: le define, c'est pour les barbus, l'enum, c'est parfait pour les noobs qui codent avec des moufles......

La prochaine fois, on essaiera de trouver un sujet technique un peu plus poilu, quand même.......

lundi, août 31 2009

Vanhu is back, physically, and mentally....

Chuis pas mort

Bah non, au risque d'en décevoir certains, je suis toujours la, et avec des billets en cours de rédaction, en plus !!

Non, je n'ai pas été une des premières victimes de la grippe A, puisque je me suis auto-immunisé depuis des années grâce à un ingénieux vaccin: Saucisson


J'ai juste pris un peu de vacances, pour me remettre de la quantité titanesque de billets que j'avais fait en juin, et pour permettre aux serveurs hébergeant ce blog de souffler un peu, eux aussi......


Apocalypse now !!!

Et puis la, a peine revenu de vacances, alors que je commençais à préparer tranquillement mon billet de fin d'été, ça a été le drame.
Pardon: Le Drame, ce genre de choc psychologique qui fait que votre vie défile sous vos yeux, qui vous meurtrit jusqu'au plus profond de votre être, dont on met parfois des années à se remettre, en étant déjà content qu'il n'ait pas déclenché Armageddon: sans prévenir, sans même le moindre indice permettant de l'anticiper, j'ai perdu ma synchro ADSL !
Traduction pour ma soeur: mon accès à internet ne fonctionnait plus !

S'en est suivi une semaine de pure angoisse, d'abord à remonter l'info à mon FAI (FT ne gère plus ces choses la en direct....), ensuite à me demander comment j'allais récupérer les mails de mon domaine, en attente sur mes MX backups, bien sur, mais pour une durée classique de 5 jours seulement.....


Un être humain normal se serait noyé dans ses larmes, se serait mis à se repentir de ses péchés ou aurait été chercher une corde pour en finir de suite avec la vie......
Mais je suis un chef de projet professionnel, dont la devise est "il n'y a pas de problèmes, il n'y a que des solutions", et de toutes façons, mes glandes lacrymales fonctionnent à très faible rendement, me repentir de mes péchés me prendrait trop de temps, et, en professionnel du réseau que je suis, je n'envisage pas de me pendre avec autre chose qu'un cable réseau catégorie 5, et ceux que j'ai en stock et qui sont assez longs sont déjà utilisés......


Il me fallait donc monter en urgence un plan de continuité d'activité, avec un nom de code qui en jette grave, j'ai nommé:

Opération Castor Lépreux

Depuis le temps que j'y pensais, j'ai donc fait valider en urgence un accès internet via Numéricable (chez qui je suis déjà pour la télé), pour pouvoir établir en permanence, via cet accès, un tunnel VPN (IPsec, bien évidemment) entre chez moi et mon FAI "normal" (FDN, le FAI dont vous êtes le héros), chez qui est hébergé mon principal MX backup.

Que du facile en théorie, mais c'était sans compter sur la haute compétence de Numéricable, qui ne semble pas bien faire la différence entre "Colissimo 48h" et "au moins une semaine", puisque je n'ai toujours pas recu leur modem cable 6 jours plus tard......

Du coup, puisque Castor Lépreux ne pouvait pas résoudre mon problème à cour terme, il me fallait mettre en place un plan d'urgence pour le plan d'urgence, j'ai nommé:

Opération Belette Sclérosée

Coup de chance (mouais, tout est relatif.....), je dispose d'un voisin sympathique qui partage 24h/24 son accès Neuf aux autres abonnés du groupe SFR, et d'une amie également très sympathique qui a bien voulu me confier ses identifiants tous frais de l'opérateur en question......


Pour aller surfer comme un ouf sur les grandes vagues de l'internet, ca suffit largement.
Pour mettre à jour mes stations de travail, ca suffit aussi largement.
Pour donner et recevoir des nouvelles à des gens via des outils que la morale réprouve, ca tenait déjà pas mal du bricolage, puisqu'il fallait passer par une interface web vraiment pourrave.....
Pour vider les files d'attente de mes MX backups, la, comme ca, c'etait pas possible, puisque je ne maitrisais pas le routeur d'entrée que je squattais........

Fort heureusement, je maitrise par contre les serveurs de mon FAI (puisque je fais partie de l'équipe d'aminsys), et, gros coup de bol, je pouvais faire du SSH via cet accès (ce qui n'était pas gagné d'avance, puisque apparemment, tout ne passe pas) sur les serveurs en question......

Oui, j'ai fait un tunnel SSH à l'arrache, sur le Wifi d'un voisin, pour permettre à mes MX backups de se connecter au serveur principal chez moi...... j'en ai fait, des trucs à la con dans ma vie d'informaticien, celui la est quand même pas mal dans la liste........
Surtout que, pour une raison qui m'échappe (et que je ne chercherai pas à creuser.... enfin, j'espère que je n'aurai pas à le faire !), cet accès Wifi tombait toutes les deux heures, et nécessitait une déco/reco/réauthentification à la main pour refonctionner......

Mais bon, parfois, la fin justifie les moyens, comme on dit.....

Retour à la normale

Après à peine une petite semaine, alors que je commencais sérieusement à désespérer du coté de FT, et que j'avais placé la plupart de mes espoirs dans le fait que Numéricable et La Poste seraient pas à l'abri de me livrer un Colissimo en moins d'une semaine (sic.....), ce matin, oh surprise, mon lien était de nouveau opérationnel !!!

Du coup, Belette sclérosée peut aller à son tour prendre des vacances, et pour Castor Lépreux, j'hésite...... soit j'insulte Numéricable en leur expliquant ce que moi j'avais cru comprendre quand ils m'avaient dit "vous l'aurez dans les 48h", soit je maintiens l'opération à l'avenir, ca me permettra, pour finalement pas bien cher, de répartir la charge entre les 2 liens (je vais pas m'emmerder, ca sera une répartition très statique hors problèmes sur une des lignes).......


Comme quoi, j'ai bien fait de suivre les conseils du guide du routard galactique: Pas de panique !

samedi, juin 13 2009

Champomy pour tout le monde !!!!!!

Ca en fait, des billets de blog en peu de temps, hein ?

SSTIC, suite et fin

Première grande nouvelle: j'ai pris le temps de finaliser les billets sur le SSTIC...... normalement, cette fois ci, ils ne bougeront plus.

r194062

Ca faisait à peine 3 - 4 ans que j'attendais ce moment:

Author: vanhu
Date: Fri Jun 12 15:44:35 2009
New Revision: 194062
URL: http://svn.freebsd.org/changeset/ba...

Log:
Added support for NAT-Traversal (RFC 3948) in IPsec stack.

Thanks to (no special order) Emmanuel Dreyfus (manu@netbsd.org), Larry Baird (lab@gta.com), gnn, bz, and other FreeBSD devs, Julien Vanherzeele (julien.vanherzeele@netasq.com, for years of bug reporting), the PFSense team, and all people who used / tried the NAT-T patch for years and reported bugs, patches, etc...

X-MFC: never

Reviewed by: bz
Approved by: gnn(mentor)
Obtained from: NETASQ

Modified: blablabla.....


Ca y est, le patchset NAT-T est maintenant officiellement dans les sources de FreeBSD TRUNK, donc sera présent dans la version majeure 8.0 !!!

Bien sur, le boulot n'est pas fini, il reste encore à sortir une ipsec-tools 0.8 release (avec la partie userland à jour), à porter le nettoyage des ports sur NetBSD, et à vérifier comment ca se comporte sous Linux......

r194066

Dans la foulée, et comme un grand commit n'arrive jamais seul:

Author: gnn
Date: Fri Jun 12 16:00:42 2009
New Revision: 194066
URL: http://svn.freebsd.org/changeset/ba...

Log:
Free Yvan from mentorship.

Modified:
svnadmin/conf/mentors

Ce qui veut donc dire que je serai désormais le seul coupable si je casse un truc en commitant :-)

Ca en fait des nouvelles, pour une fin de semaine !!!!!!

lundi, avril 27 2009

Changement de vie......

La vie est ainsi faite qu'il y a parfois des situations qu'on pensait durer des années (voire une vie ?), et qui finalement évoluent, changent, parfois sur un coup de tête, parfois de façon lente et pernicieuse....

On vit tranquillement, au quotidien, et globalement, ça se passe plutôt bien......
Oh, bien sur, de temps en temps, on se surprend à envisager d'aller "voir ailleurs", ce que ca ferait..... rien que pour comparer, en étant persuadé qu'on préfère ce qu'on a, parcequ'il n'y a rien de mieux, parcequ'on est faits l'un pour l'autre, en quelque sorte, avec la même philosophie, les memes notions de choses importantes, etc.....

Bien sur, on ne le fait pas, aller essayer ailleurs..... par paresse, parcequ'on "le fera demain", ou en tout cas "quand on veut", parceque rien qu'à regarder de loin, c'est évident que ca ne nous plairait pas..... pas autant, en tout cas.....

Mais l'idée est là, désormais, et va commencer à germer.......


Et puis le temps passe..... le quotidien devient routine, les actions deviennent des automatismes. Et on ne se rend plus compte de toutes ces bonnes choses qui ont pourtant motivé notre choix il y a des mois, parfois des années.

Et le monde autour évolue..... avec ses bonnes et ses mauvaises choses, bien sur, et puis un jour, par hasard, "pour tester", on essaie "autre chose"...... oh, juste quelques minutes, rien que pour voir ce que ca fait..... et d'ailleurs, on est rapidement déçu, ca n'est pas pareil, on n'est pas à l'aise et on doit tâtonner en permanence pour obtenir ce que l'on veut.....

Forcément, on en revient déçu, on se rend compte que c'était stupide et qu'avec une once de réflexion avant, on n'aurait même pas essayé.......
Mais voilà, c'est fait..... et même avec cette sensation globale de déception, un petit coin du cerveau retient qu'il y a effectivement autre chose......
Et même si l'information est étouffée par l'essentiel du conscient, on retient aussi quelques agréables surprises dans cet essai, quelques originalités, quelques trucs qu'on ne connaissait pas et qui semblent sympa, voire agréables......


Et encore une fois, l'idée grandit, mûrit......


Pendant ce temps, le quotidien devient un peu plus pénible. Même si l'osmose semble encore existante de loin, de près, il n'en est plus rien. Le moindre petit détail qui n'est pas parfait devient plus difficile à supporter, et pour peu que ce détail persiste et ne soit pas facile à corriger, il en devient rapidement insupportable......

Et le temps passe...... au moins l'un va évoluer, avoir des attentes ou un mode de fonctionnement un peu différent. L'autre évoluera peut être aussi, ou peut être pas, mais dans les deux cas, la divergence commence à se ressentir clairement.

Et un jour on retente "autre chose"..... plus jeune, plus vivant, plus sexy et avec la promesse de nouvelles possibilités...... ca n'est toujours pas parfait, loin de la, évidemment, mais c'est une expérience qu'on va pousser plus loin, pour voir vraiment si cela pourrait aussi être un "quotidien" possible.....

Et un jour, on craque..... et on se lance corps et âme dans cette nouvelle possibilité.
Les repères ne sont toujours pas la, bien sur, et de nombreuses choses qui étaient devenues naturelles, instinctives, ne le sont plus du tout, et nécessitent du temps, des essais, des échecs, des déceptions et d'autres essais..... mais qu'importe, le changement est en route, la décision est prise, et quitte à renoncer à certaines facilités de ce qui est déjà "l'ancien quotidien", on évolue, on se crée presque une nouvelle vie, on s'adapte et on adapte.....

L'information devient alors inévitablement publique, souvent plus vite qu'on ne le croit, et parfois de façon plus ou moins déformée, et enterre définitivement toute possibilité de retour à "avant".
Il ne restera que quelques personnes à apprendre tardivement, au mieux par quelqu'un d'autre ou en s'en rendant compte par eux même, au pire en faisant une gaffe, qu'il faudra alors encaisser même si elle nous rappelle d'un coup tous ces bons cotés de cette vie "d'avant".......


Plus rien ne sera désormais pareil, maintenant, et il me faudra surement encore du temps pour vraiment tourner définitivement la page, mais, au risque de surprendre tout le monde (et pour avoir déjà surpris à peu près tous ceux qui sont déjà au courant), j'ai pris cette terrible décision il y a désormais quelques semaines: j'ai laissé tomber WindowMaker, le Window Manager que j'utilisais pourtant depuis près de 10 ans, et je suis passé à XFCE4......

vendredi, février 13 2009

Evenement important samedi 14 février 2009 !!!!!

Les plus observateurs l'auront déjà remarqué, et l'attendent probablement avec impatience, les plus distraits ne s'en étaient peut etre meme pas rendus comptes, mais demain, nous serons samedi 14 février 2009, et c'est un jour important.

Attention, je parle bien de "important"..... du genre "un jour à ne surtout pas louper", qu'on prépare plusieurs jours à l'avance, pour lequel on prend la peine de se laver (voire meme de se raser pour les plus fous), d'aller faire les courses (en achetant champagne, petits fours à la brandade de morue et gateau au chocolat pour le dessert), et ou les boulets qui l'ont loupé s'en voudront pendant un sacré bout de temps.


Eh oui, vous l'aviez deviné, je parle bien sur du passage cette nuit, à précisément 23h30 et 31 secondes GMT vendredi 13 février 2009 (soit 00:31:30 CET samedi 14 février 2009 pour les résidants de l'hexagone), au timestamp UNIX 1234567890 !!!


Pour ma soeur, qui se demande déjà ce qu'est un "timestamp UNIX", il est probablement utile de rappeler que, dans le monde UNIX, l'origine de l'univers date du 1er Janvier 1970 à 1:00:00 CET (donc à 00:00:00 GMT), et le temps est compté en secondes depuis cette date.

Par exemple, je suis né aux environs de 197210000 secondes après l'origine des temps UNIX, soit le 1er avril 1976 (désolé, je ne me souviens pas de l'heure exacte...).

Et donc, au moment ou j'écris ces lignes, nous sommes environ à 1234531110 secondes après l'origine des temps UNIX (on retrouve facilement ce nombre en faisant par exemple un perl -e 'print time()."";', ca fonctionne aussi sur un Mac, une fois qu'on a trouvé comment lancer un shell), et on peut déjà noter le début de ce nombre:12345....
Il suffit alors d'une rapide soustraction pour déduire que nous ne sommes qu'à 1234567890 - 1234531110 = 36780 secondes (à peu près, hein, j'ai refait le calcul et ca a déjà changé.... surement quelquechose de quantique la dessous !!!) de ce fameux timestamp remarquable de 1234567890 secondes, soit plus tout à fait 610 minutes, maintenant (quand je vous dit que ca évolue vite), soit un peu plus de 10 heures.....


Il vous reste donc 10 heures pour aller vous faire le maillot, acheter une bouteille de champ, la mettre au frais, et vous pourrez la déboucher cette nuit, à 00:31:30 heure francaise (vous pouvez vous aussi retrouver cette date exacte en faisant un date -r 1234567890 sous *BSD, ou un date -d "@1234567890" sous Linux), en meme temps que plein de geeks (qui déboucheront surement une bouteille de coca ou un sachet de café pour l'occasion, chacun son truc.....) pour féter l'évènement.


Ceux d'entre vous dont l'emploi du temps est plus que chargé prendront directement rendez-vous le samedi 9 octobre 2010 à 09:16:58 CEST, pour le passage au timestamp 9876543210 et le dimanche 7 septembre 2014 à 06:50:08 CEST pour le passage au timestamp UNIX 10000000000....

Les plus joueurs, quand à eux, peuvent bien évidemment générer un nombre au hasard, et constater qu'ils ont malheureusement loupé le rendez-vous du mardi 1er janvier 1970 à 01:00:04 CET


Et dire que pendant ce temps, certains en sont réduits à chercher désespérément un cadeau de dernière minute pour leur légitime, histoire de ne pas passer les 4-5 prochaines nuits dans le canapé.......

mercredi, juillet 30 2008

des photos et des bits......

Mais vous faites quoi, la, à être scotchés à l'ordi plutot que de profiter du soleil (si, si, même dans le nord on en a..... un peu....)


Bon... bah maintenant que vous êtes la, et puisqu'on est de toutes facons à la fin du mois (les vielles habitudes ont la vie dure.....), quelques nouvelles quand meme, puisqu'en plus, il y en a, des nouvelles !!!!


D'abord, je vous relatais il y a quelques mois mon cruel dilemme (qui était d'ailleurs au bas mot un trilemme, voire un n-lemme polynomial non euclidien, pour être exact) photographique, dû à la quasi-obsolescence d'un boitier qui m'intéressait mais dont le prix n'avait pour sa part subi qu'une légère vétusté, et qui le rendait finalement peu attractif, face aux modèles de la gamme d'en dessous comme face à son successeur dont on n'était sur ni du nom ni du prix ni des fonctionnalités ni de la date de sortie mais qu'on savait qu'il serait bien.......

Finalement, j'ai craqué pour la gamme en dessous: un Canon EOS 40D, dont le seul défaut à mon goût est l'absence de capteur plein format. Après quelques semaines d'essais divers et variés, j'ai déjà eu l'agréable surprise de constater que le fait de regarder au travers du viseur ne me donne pas une soudaine envie de rendre mon petit déjeuner (oui, pour ceux qui ne m'auraient pas vu le matin depuis longtemps, maintenant je prends un petit déjeuner), comme ca avait été le cas pour un réflex numérique encore plus entrée de gamme que j'avais essayé l'année dernière.

Reste à voir ce que ca donnera en séance photo en studio, premiers essais ce WE.....


Et j'ai aussi constaté qu'un appareil numérique a un effet désinhibant sur l'index: on déclenche à tout bout de champ, et pour la moindre occasion (réunion de famille, les potes qui sont partis faire les cons dans les bois dans des costumes médievaux, etc....) on se retrouve avec 500 à 1 000 photos (non, je n'ai pas accroché de touche de mon pavé numérique, je parle bien de cinq cent à mille photos) en stocks (et l'air con avec une grosse ampoule au bout du doigt, mais c'est les risques du métier, dus à un bête problème d'ergonomie, on est obligés d'appuyer sur le déclencheur avec l'index, au lieu de pouvoir simplement prendre la photo en faisant CTRL-u 5 CTRL-c Meta-p pour déclencher 5 photos d'affilée...)....

Et ca pose un problème: comment les stocker, ces photos ???

Bien sur, la réponse évidente est "bah tu les mets sur ton ordi !". Mais ca n'est pas aussi simple:

D'abord, avec un appareil à 10 Megapixels, ca me "pond" des photos qui font déjà 2 à 5 Mo (et c'est déjà au format JPEG, je ne parle même pas des photos prises et stockées en format RAW !). On est donc pas loin du Gigaoctet de données pour un simple samedi après midi (heureusement que je ne fais pas ca toutes les semaines !!). Bien sur, avec des disques qui stockent désormais un téraoctet (soit 1024 Gigaoctets, pour ceux qui auraient du mal à suivre), ca donne un peu de marge....

Mais voila: un disque dur, un jour, ca fait "crrrrrcrrrrrr" (et baygon n'y pourra rien, que ca soit le jaune ou le vert), ou ca subit une perte involontaire de données (opération d'administration système tard la nuit dans un état d'ébriété plus avancé que prévu, virus pour nos amis windowsiens, bug quelconque qui trainait par la, etc.....).

Bien sur, chez moi, le disque (500Go seulement...) est en RAID miroir: j'ai physiquement deux disques de 500Go, et toutes les données sont écrites automatiquement sur les DEUX disques. Comme ca, si l'un des deux lache, l'autre a encore les données.

Mais la encore, ca ne suffit pas. Si la maison brule (ou simplement la cave, dans mon cas), s'il y a une innondation, une malédiction vraiment trop injuste ou autre problème physique "de zone", les deux disques seront perdus simultanément.


La, tout le monde va me dire "bah t'as qu'a mettre ca sur CD/DVD"..... Sauf que, des CDs gravés (pour les CDs pressés en usine, c'est pas encore pareil) de plus de 10 ans, moi j'en ai, et je peux faire la liste de ceux qui sont illisibles depuis déjà plusieurs années (en gros, tous sauf les Verbatim.....).

Alors bien sur, on m'a aussi dit "bah t'as qu'a les copier sur une autre machine, genre une dedibox/kimsufi, une machine chez un copain, etc....". Mais ca ne fait que déplacer le problème, et en plus vers une zone qui devient au moins en partie en dehors de mon contrôle (même après avoir viré les clés SSH des admins de Free/OVH du serveur dédié en question....................).

Reste la sauvegarde sur bandes, mais c'est extrèmement cher, et une bande, c'est magnétique, et c'est facilement effacable sur une "erreur magnétique" (je vous ai déjà raconté celle de la secrétaire qui ramenait chez elle les sauvegardes automatisées de la boite, et qui faisait transiter les dites bandes magnétiques dans son sac à main, lui même fermé par un gros aimant ?).

La conclusion, c'est que si quelqu'un me fournit une solution pertinente (prix raisonnable, média amovible et qui est complètement protégé quand il est dans sa boite, fonctionne sous UNIX, vitesse de lecture et d'écriture "raisonnable", capacité de stockage conséquente, au moins quelques gigaoctets par média), il gagne toute ma considération et le droit de choisir le sujet d'un de mes billets futurs !!!


Mais ca n'est pas la seule nouvelle !!!


Ca faisait à peine 3 ans que je m'étais fixé cet "objectif", c'est arrivé presque sur un malentendu début juillet, suite à une relance de troll par quelqu'un que je connais: j'ai mon commit bit chez FreeBSD (et suis donc joignable désormais à l'adresse vanhu at FreeBSD.org, ZE grande classe !!!).

Pour ma soeur qui n'est toujours pas plus informaticienne que ca (meme si elle fait des progrès :-), et qui, m'ayant déjà vu en très légère tenue, doit se dire que j'étais déjà suffisemment équipé en la matière, le "commit bit" est en fait un indicateur qui dit que je suis maintenant un "membre de l'équipe" de FreeBSD (euh .... "un truc un peu comme Linux et qui est entre autre utilisé par Apple pour faire tourner ton Mac, mais aussi par plein de serveurs sur internet" :-), et que je peux directement apporter des modifications à ce FreeBSD sans devoir passer par d'autres personnes (enfin, ca n'est pas tout à fait exact pour l'instant, puisque comme pour la plupart des gros projets, il y a une première période ou j'ai un "mentor" qui valide ce que je fais).

Ce commit bit est bien évidemment aussi la raison qui fait que ce billet est classé "vieux con de hacker old school".


Le mois prochain, normalement, ca parlera voitures, économies, tentative d'écologie, tout ca .....

lundi, mai 19 2008

A y est: mon Soekris est en prod !!!!

En octobre 2007, j'ai fait une présentation (slides et vidéo) à l'EuroBSDCon. Et cette année la, tous les conférenciers (dont moi, donc...) se sont vus offrir un Soekris, et même pas une vielle version de fins de stocks, puisqu'on a eu des Net5501-70, c'est à dire le "haut de gamme" du Soekris....

Alors forcément, pour le quidam moyen, cette petite boiboite peut au mieux servir de guirlande de noel, ou de cale pour l'armoire normande héritée de la grand tante Aglaée (ah, la boite de bonbons chez la grand tante Aglaée, toujours pleine..... la boite, hein, pas la grand tante Aglaée....).

Mais pour un Vieux con de hacker Old school (TM), ca peut servir à plein de choses.....

Et pour moi en particulier, j'ai tout de suite eu ma petite idée sur l'utilisation principale que je ferais de ce machin: mon point d'accès Wifi (aussi appelé "AP', pour Acces Point, autant dire que nos chers Immortels ne se sont pas foulés le bicorne sur ce coup la....) !


Bien sur, je vois d'ici votre air pantois plein de stupéfaction médusée (ce qui n'est pas peu dire !), en apprenant qu'un "point d'accès Wifi" peut être autrechose qu'une "MachinBox", voire autre chose qu'un truc vendu tout prêt au rayon réseau informatique des supermarchés ??


Bah oui, et ca présente plusieurs avantages:

  • D'abord on peut éviter d'acheter des APs qui ne fonctionnent pas (si, si, il parait que ca existe.....)
  • Ensuite, ca permet de sécuriser son réseau via IPSec... vous me direz, ca marcherait aussi en WPA (message personnel pour ma soeur: "WPA", c'est ce que j'ai activé chez toi pour le Wifi, t'inquiète, c'est suffisant, tant que tu gardes bien secrète la clé "toto42".....), ce à quoi je rétorquerais "oui, mais ca marcherait aussi bien en IPSec"..... et dans mon cas, ca me permet en plus de faire des tests sur mes développements, alors que je ne développe pas DU TOUT sur WPA.....
  • Ca permet aussi d'avoir un controle complet sur un de ses équipements réseau / sécurité, ce qui est toujours appréciable.
  • Enfin, ca permet de trouver un usage à un Soekris qui trainait en dessous de l'armoire normande de grand tante Aglaée, et d'occuper un WE ou il n'y avait rien d'intéressant à la télé.....


Bon, ok, ca a pris un peu plus d'un WE, en fait..... Mais revoyons la scène au ralenti:

D'abord, le choix d'un système à installer.

J'aurais naturellement du choisir un NetBSD, en profiter pour fixer 2-3 trucs dans le stack IPSec, et justifier ainsi un peu plus mon status de "développeur NetBSD"....

Mais bon, j'avais pas prévu d'y passer trop de temps, je m'étais dit que ca, ca serait cool, mais pour plus tard, et que en attendant, je voulais un truc ou ca serait rapiiiiide à installer, et que je saurais configurer / maintenir assez rapidement.

En gros, j'avais le choix entre Un système d'exploitation qui utilise un PRNG tout pourri ou un système d'exploitation qui met des plombes à intégrer le support du NAT-T...

Le premier ne m'avait pas l'air bien trivial à installer sur une machine qui n'a ni écran, ni clavier, ni lecteur de CDRom (quoique ce dernier point peut se contourner facilement)... disons au moins que je n'avais pas de solution en tete pour faire dans le "propre"...


Du coup, j'ai démarré sur le second, avec dans l'idée d'avoir une machine de compilation séparée, et de venir ensuite "pousser" tout ce qu'il faut sur la CompactFlash du Soekris quand j'ai besoin de faire des mises à jour.

Avant de commencer à réinventer la roue, j'ai regardé ce qui se faisait, et j'ai trouvé un projet intéressant: TinyBSD, qui, de loin, de nuit et dans le brouillard, faisait tout ce que je voulais.

Quelques heures plus tard (ouais, la machine de compilation est pas vraiment ce que j'appellerais "une bete de guerre".... ou alors ca serait "un mulet de guerre", genre qui vous foudroie ..... a son rythme .....), mon premier "build" de TinyBSD est en route, puis le résultat est copié sur la flash (un dd d'un .bin, pour ceux que ca intéresse.....), et.....


Et ca marche pas, forcément, sinon ca serait pas drole....

Déjà, quelques galères au niveau du port série (seule facon de voir ce qui se passe pendant un démarrage, puisque, comme je l'ai dit plus haut, y'a ni sortie VGA/DVI/Peritel/autre, ni entrée clavier), mais ca s'est vite résolu.

Ensuite, la joie de voir mon premier kernel FreeBSD me dire qu'il démarre la dessus a rapidement été rattrapée par la déception de constater que "ca marche pas".... et vu le message d'erreur, ca sentait la CompactFlash toute pourrie (une vague odeur de petits fours à la morue restés une semaine dans un fond de poche, pour une obscure raison...).

Finalement, fausse alerte: une rapide recherche m'apprendra que je n'ai pas la toute dernière version du BIOS du Soekris, et que, pas de bol, il FAUT cette toute dernière version en question pour pouvoir booter un FreeBSD >= 6.3 (et j'utilise un 7.0, aux dernières nouvelles >= 6.3 dans un K-Espace vectoriel standard).


L'air de rien, je mettrai quand meme pas loin d'une heure à réussir à flasher ce [CENSURE] de BIOS, tout simplement parceque je n'avais pas désactivé le controle de flux hardware du port série..... et manifestement, c'est pas génant pour envoyer 2 caractères ici et la de temps en temps, mais ca pose un problème pour envoyer un BIOS de pas loin de 100ko.....


Et la, alléluya, ca a enfin booté ! Pour me faire planter mon lien série, et m'empécher de me loguer via cette interface, qui était, à ce moment la, mon seul accès possible à la future cale d'armoire normande de grand tante Aglaée !!!

Re-démontage de la CF, re-branchage sur la machine de compilation, triturages divers (/boot.conf) et variés (/etc/ttys), et la, oh joie inéffable, je me suis enfin retrouvé logué pour la première fois sur cette machine !!!


Mais bien sur, ca n'était pas fini pour autant..... Il m'aura fallu encore pas mal de temps, de tests et de suspicions (en tout bien tout honneur, hein) pour réussir à faire fonctionner ma carte Wifi (manifestement des changements de comportements depuis FreeBSD6, dernière version en date ou cette carte avait vraiment servi dans mon ancien AP, et encore, vu que l'AP pour le moins bruyant était relégué à la cave, donc était difficile à capter.....) correctement, et un peu de temps supplémentaire pour tout recommencer à zéro, mais cette fois ci avec le support du NAT-T, parceque j'aime bien que d'éventuels indiscrets de passage se demandent ce que ca peut bien etre que ce traffic UDP qui circule sur mon Wifi, plutot que de voir directement du traffic ESP, plus facilement identifiable.....


Et la, oh merveille de la technologie moderne: ca marche !!! Enfin, cette partie la....

Parceque, quitte à avoir dans mon bureau un petit machin qui tourne 24h/24 sans faire de bruit, je me suis dit que je pourrais aussi lui coller mon imprimante, anciennement configurée sur une station de travail....


"Et la, c'est le drame"..... Parceque TinyBSD a bien "un truc" pour gérer l'installation de ports, mais que c'est quand meme moins bien foutu et plus complexe à mettre en oeuvre que le système de base.

Pour des ports aussi "simples" qu'ipsec-tools (qui embarque 2 binaires, une librairie, un fichier de conf, un snickers et une canette de coca pour la pause), ca marche bien.

Pour un port "un poil moins simple" genre cups, qui dépend (RUN_DEPENDS pour ceux qui comprennent ce que ca veut dire) de plusieurs librairies, qui embarque dans le répertoire "doc" des pages HTML qu'il utilise en fait lors de son fonctionnement, qui a besoin de répertoires qui ne sont pas automatiquement "populated" dans /var, etc.....


Du coup, pour l'instant ca fonctionne avec une install "à moitié à la main", et va aussi falloir que je règle proprement l'histoire de mise à jour...

Démonter le bazar a bidule à chaque fois pour avoir accès à la flash, ca va rapidement me gonfler, ca "use" la CF pour rien (le remplissage actuel est de 20% de l'espace, en descente et avec vent arrière....), et surtout, faut que je trouve autre chose pendant ce temps pour faire tenir l'armoire normande de grand tante Aglaée !!!

La solution "made in bourrin" sera probablement de monter l'image de flash sur la machine de compilation, puis de tout balancer au Soekris par le réseau.... ca marchera bien tant que ca sera des mises à jour "mineures", ce qui me laisse un peu de temps pour (re)développer ZE technique de mise à jour d'une machine embarquée: un updateinit.

Allez, je vais pas me cramer un super sujet de billet, vous aurez droit à une explication détaillée du pourquoi du comment d'un updateinit dans un billet futur, si vous êtes sages, bien évidemment !!!


La vache ! quand on pense que, pendant ce temps, à Vera Cruz, il n'a fallu que 5 centièmes de seconde à X-OR pour revétir son scaphandre de combat !!!!

jeudi, mai 8 2008

Microsoft fait dans le social à Lille : réduction de fracture numérique ou augmentation de la facture ?

Oui, je sais, billet du mois, blabla.... Le plus bete, c'est que pour une fois, mon billet était tout prêt: c'est un texte dont je suis le rédacteur principal, fait courant avril en tant que vice président de CLX.

"Rédacteur principal", parceque c'est moi qui m'y suis collé, mais il a ensuite été discuté et un peu modifié en fonction des remontées des autres membres du bureau Clx.


Pour ceux qui n'auraient pas fait attention, le titre est donc:

Microsoft fait dans le social à Lille : réduction de fracture numérique ou augmentation de la facture ?


Tremblement de terre chez les Ch'tis : un géant de l'informatique va venir "dorloter" de jeunes pousses du cru, tout cela dans un nouveau parc hautement technologique (voir ici ou la) qui sera inauguré en fin d'année, avec, entre autres atouts, une interconnexion réseau digne des gros points d'échanges de la capitale, et une forte volonté politique locale d'en faire le plus gros incubateur d'Europe du Nord !

Jusque-là, à priori que du bon, surtout que nous pouvons lire ici, ici, la, et la aussi que le géant en question s'engage à faire ce parrainage "sans contreparties".


Mais l'affaire n'est peut-être pas aussi simple...

Le géant en question aurait pu être un célèbre moteur de recherche (qui a finalement préféré aller poser ses containers de serveurs chez nos amis Belges, pas très loin), et nous en serions certainement restés là.


Il se trouve que, finalement, parait-il en "plan B" suite au choix d'implantation du moteur suscité, le géant en question n'est rien d'autre que Microsoft, société certes célèbre pour ses logiciels et pour la fortune de son Chairman, mais au moins aussi célèbre pour ses procès antitrust, pour son lobbying intensif sur de nombreux fronts, pour ses "donations généreuses", "promotions étudiantes" et autres "remises préférentielles" qui pleuvent dès qu'il y a moyen "d'éduquer" les foules à ses outils et à rien d'autre.

De ce fait, nous sommes en droit de nous demander ce qu'une telle installation pourrait avoir comme conséquences, au moins sur la région.

D'abord, manifestement, de la création d'emplois, puisque Microsoft va également implanter un site sur place. Le montant de l'investissement n'est cependant pas précisé, il est donc un peu tôt pour savoir s'il va réussir à contrebalancer les montants astronomiques de licences de produits Microsoft payés tous les ans par la population française, qu'il s'agisse de particuliers, d'entreprises, voire même de l'État, voire même sans vraiment le savoir, quand il s'agit de vente de logiciels liés à l'achat d'un ordinateur...


Mais est-ce tout ?


Pas si sur, si l'on examine avec attention les petites phrases et les autres projets de la région qui pourraient être plus ou moins influencés par cette position de Microsoft :


D'abord le "volet sociétal", qui inclut un projet d'e-citoyenneté : une carte à puce pour la vie quotidienne.

Hors contexte, on peut déjà se demander à quoi servira vraiment cette carte, quelles informations contiendra-t-elle vraiment, quel contrôle aurons-nous sur son contenu, qui sera capable de la lire, est-ce qu'elle permettra de nous tracer d'une manière ou d'une autre ?

Mais bon, après tout, tout le monde utilise déjà une carte bleue, non ? Nous ne sommes peut-être plus à ça près ?

En revanche, le contexte pose une autre question: quel sera le lien entre cette carte et Microsoft ?

Est-ce que le géant de Redmond pourra imposer des outils "Windows only" pour s'en servir, et ainsi s'assurer une main mise sur tout un marché, avec la bénédiction des instances régionales ?


Vient ensuite le "volet social", qui parle de "remise à niveau d'informaticiens au chômage".

J'ai de suite pris rendez-vous chez l'oculiste, parce que, vous allez rire, en lisant pour la première fois cette phrase il y a quelques jours, j'ai accidentellement lu "formation d'informaticiens aux outils Microsoft, informaticiens qui intégreront du coup malgré eux la force marketing de Microsoft, le tout payé par la région"... il faut vraiment que je m'achète des lunettes...

Et pourtant, cela n'est pas nouveau.

Depuis de nombreuses années, Microsoft n'a de cesse de mettre ses logiciels, systèmes, technologies et outils en place (entre autres) dans tous les systèmes éducatifs de nombreux pays, ce qui leur permet de "convertir" nos chères têtes blondes en utilisateurs conditionnés à la "doctrine Windows" dès leur plus jeune âge.

À quoi bon s'embêter à passer une pub à la télé pour vanter les mérites d'un produit, quand on peut tout simplement avoir une armée d'enseignants, de formateurs, d'accompagnateurs qui expliquent au peuple qu'il n'existe que cela ?


Alors que pourtant, il n'existe pas que cela.... d'autres solutions existent, autant pour les systèmes d'exploitation que pour les suites bureautiques ou de nombreuses autres catégories de logiciels.

Certaines solutions se payent en plus le culot d'être gratuites pour l'utilisateur, et même d'être "Open Source" ("Libres", en bon français dans le texte) : la "recette" pour produire le programme est librement accessible, utilisable et modifiable par tous, éventuellement sous la principale contrainte de faire partager ses modifications !

Et pourquoi ne pas laisser diffuser librement les recettes des crêpes, de la blanquette de veau à l'ancienne ou pire de la fricadelle, pendant qu'on y est ?

Paradoxalement, de nombreuses startups dans le monde démarrent en utilisant des outils open Source, qui sont donc générateurs d'emplois (locaux, qui plus est !) malgré leur côté "distribution gratuite" (et ce modèle fonctionne manifestement bien pour certaines entreprises !).

Parallèlement, de nombreuses autres startups ne dépassent pas leur première année d'existence pour des raisons liées à des coûts de licences logicielles, voire des problèmes de brevets (pourtant, les brevets logiciels sont toujours invalides en Europe, à ce qu'on m'a dit ?). Les mêmes licences et brevets profitent cependant à d'autres, dont... eh oui, Microsoft, qui est l'un des principaux défenseurs de ces concepts, y comprit devant les tribunaux.


Qu'en penser, au final ?


Que les informaticiens (voir que la population en général) soient entre autres initiés aux outils Microsoft ? Cela peut paraitre logique... d'un autre coté, est-ce cela vous paraitrait logique que les futurs conducteurs soient uniquement formés "aux voitures Peugeot" ?

Les informaticiens (et nos chères têtes blondes, à leur niveau) doivent être formés aux principes généraux de l'informatique, et doivent appliquer leurs connaissances sur des outils variés.

C'est justement en manipulant et des outils Microsoft et des outils open Source et d'autres outils si possible qu'ils pourront vraiment en comprendre les principes importants.

Et c'est aussi de cette manière qu'ils pourront comparer, et choisir ultérieurement les solutions les plus adaptées à leurs besoins, en pleine possession de leur libre arbitre.


Que Microsoft investisse dans l'économie technologique en Europe ? C'est la moindre des choses, et il faudra certainement beaucoup plus qu'un site près de Lille et quelques sponsorings de start-up pour réinjecter tout le tribut financier que nos pays donnent généreusement à cette société depuis des années !


Mais si ce sponsoring permet d'augmenter encore plus la visibilité des outils Microsoft au détriment d'autres solutions au moins aussi pertinentes, et manifestement plus dévouées à l'utilisateur qu'au bénéfice de quelques actionnaires, le jeu en vaut-il vraiment la chandelle ?

jeudi, novembre 29 2007

Le monde merveilleux de l'informatique......

Récemment, il m'est arrivé un truc (ouais, ca m'arrive, des fois, des trucs.... c'est dingue, non ?): je me suis retrouvé devant un Windows..... jusque la, pas de bol mais c'est des choses qui arrivent (la preuve) parfois..... Et bien sur, c'etait pas pour moi, mais "pour un copain", hein !

Suite a l'installation d'une Debian sur la meme machine (ouais, un windows sur la machine, ok, mais faut voir à pas pousser mémé dans les orties, ca pourrait faire mal aux orties !), à quelques manipulations diverses et variées (bon, dont une qui m'a produit un très esthétique arc éléctrique quand j'ai rebranché un truc USB.....), et après avoir cru un instant que j'avais cramé la carte graphique (alors que non, fallait juste préciser Options NVAGP 1 dans le xorg.conf, option que j'avais jamais du préciser jusque la sur d'autres machines, sinon ca ramait sa mere grave a tel point que je croyais que la machine etait figée), je me suis retrouvé à faire une incantation rituelle toute bete, pas plus rationnelle que ca, mais tellement souvent utilisée par les informaticiens pour tenter de dépanner du matos: tout démonter et tout remonter (et c'était du coup un bon pretexte pour faire des fouilles archéologiques à l'intérieur de la machine, et par exemple retrouver des vis datant du néolithique fossilisées dans des strates de poussière.....).

Au remontage, à priori, tout va bien. Après avoir vérifié qu'il n'y avait pas trop de pièces restées en dehors (c'est une des lois de Murphy: quand on démonte quelquechose et qu'on le remonte dans la foulée, il y a *toujours* des pièces qu'on ne sait pas ou remonter..... Curieusement, la plupart du temps, ca fonctionne quand meme très bien sans.... du moins un certain temps....), je redémarre la machine, je constate que j'a toujours mon problème avec ma carte graphique, pour finalement trouver un peu plus tard (et après 3 réinstalls quand meme) cette fameuse histoire de NVAGP.

Puis vient un moment ou, pour une raison en plus toute bete et pas plus pertinente que ca, on en vient à vouloir redémarrer sur le Windows de la machine (j'ai dit "on", comme "on a gagné la coupe du monde", "on devrait quand meme s'attaquer à la vaisselle dans l'évier, ca fait une semaine qu'elle commence à bouger toute seule", "on doit sortir les poubelles ce soir", "maintenant on doit bosser de facon cohérente sur les trucs urgents, et pas selon l'humeur du matin", ou "vous inqietez pas, mon équipe et moi on s'en occupe", comprenez par la que c'était pas moi, hein.....).

Et la, c'est le drame: au dela bien sur du fait d'etre sous Windows (2k, pour etre plus précis), y'a plus de réseau...

Jusque la ca ne m'affole pas trop, je sais que le switch sur lequel est branché la machine est un switch "modèle a pas cher", que la moitié des cables n'ont plus le "petit machin en plastoque qui fait clic quand on branche la prise et qui garantit a peu près que RJ45 va pas trop se débrancher, mais qui garantit aussi qu'on va galérer pendant 5 bonnes minutes le jour ou on voudra débrancher le cable", le temps de débrancher/rebrancher tout ca, et...... et ca marche toujours pas....

Manifestement, le lien ethernet fonctionne, d'après les LEDS (coté switch comme coté machine), mais la machine n'a pas d'IP (ce qui , convenons en, est tout de suite moins pratique pour causer en IP avec d'autres machines, meme posées juste à coté).

Un petit coup de tcpdump depuis le serveur DHCP en face confirme qu'on voit pas grand chose arriver.....Curieux, la machine est bien en mode "s'il vous plait, je suis pas un voleur, je voudrais juste une adresse IP pour pouvoir causer avec le réseau, et éventuellement propager un ou deux worms, mais c'est tout, à vot' bon coeur, m'sieur dame"....

Problème simple, solution simple (et qui ira très bien pour le peu de temps ou le windows va encore rester sur la machine): on lui file une IP fixe (ca tombe bien, je connais l'IP que lui donne le serveur DHCP). Et la, surprise: windows me dit que l'adresse IP en question est déjà utilisée par une autre carte réseau, que si je vois pas l'autre carte en question dans la configuration, c'est qu'elle est pas branchée pour l'instant, mais que ca peut quand meme vachtement foutre un sacré bordel, mais que si vraiment je suis un ouf de première et que je veux quand meme tenter le truc, bah je peux cliquer sur "oui".

J'hésite un peu quand meme, si ca se trouve, la carte (réseau) a envoyé récemment au windows une carte (postale), en lui disant "salut, je surfe actuellement sur de méchantes vagues de l'internet, mais je reviens bientot, garde moi mon IP au chaud s'teup", voire le windows a fait une requète AD (Au Dela) pour causer avec la carte fantome, qui a juré de le hanter s'il ne sacrifiait pas quelques octets par semaine....

Mais bon, dans mon cas, la carte réseau fantome, je sais ou elle est (non, pas la, mais dans le PC, tout simplement), elle est pas fantome du tout, elle a juste été débranchée 2 minutes de la carte mère..... Et de toutes facons, je suis un ouf de première, j'ai pas peur, et meme si je suis pas en conditions de labo habituelles (c'est bon, vous connaissez, maintenant, non ?), je prends le risque (après avoir quand meme établi un périmètre de sécurité, faut faire gaffe aux civils....).

Et bah ca marche pas.... j'ai toujours 0.0.0.0 comme IP, et meme en re-re-refaisant la manip (réflexe pourtant curieux, d'autant plus que je l'ai presque systématiquement face à un problème windows, et à peu près jamais sous UNIX, allez comprendre pourquoi.....), pas moyen d'imposer cette foutue adresse IP, qui est pourtant celle de la machine, et celle de l'unique carte réseau jamais branchée sur cette machine, meme si elle a une fois ou deux changé de place dans le passé.....

Aux grands mots les grands remèdes, je décide donc de passer au "Plan B" (toujours avoir un Plan B, c'est important !), c'est pas un windows qui va me résister !

Plan B, donc, je reboote en "safe mode", en me disant que je vais voir toutes les cartes réseau connues, y compris celles pas actives (enfin, y compris les autres fois ou le windows a "découvert" la carte réseau....), et que j'aurai juste a dégager les "anciennes" (4, quand meme, ce qui fait un bon score pour une seule carte réseau physique .......).

Eh bah non, pas moyen de les voir, donc pas moyen de les effacer, ca se tient....

Je passe donc à la solution "Vieux con de hacker old school" (tm), j'ouvre un shell, et je tape emacs /etc/network/interfaces ...... avant de réaliser que je n'ai ouvert en fait qu'une "ligne de commande", qui ne connait ni emacs, ni /etc/network/interfaces......

Il est donc temps de passer du coté obscur, et de lancer "regedit" (on ne vous le répètera jamais assez: ne faites pas ca chez vous !!!!!). Je vérifie à tout hasard si CTRL-s fonctionne..... bah non (enfin, ca fait pas ce que je veux, en tout cas), et me résigne à commencer une recherche carrément hasardeuse sur ce qui pourrait bien correspondre à ces fameuses interfaces fantomes....

Fort logiquement, je fais d'abord une recherche par adresse IP, qui ne fonctionnera pas tout de suite: les adresses IP sont stockées sous forme décimale dans la registry, et pas en tant que chaine de caractères. Je finis donc par trouver "des trucs qui parlent manifestement de cartes réseau et qui connaissent l'IP problématique en décimal", et ne sachant pas quoi effacer la dedans, bah je vire tout......

Redémarrage de la machine, toujours pas de réseau.... assez normal pour l'instant: la carte réseau est vue comme "périphérique inconnu", je réinstalle donc le pilote "kivabien"..... Et elle est toujours "périphérique inconnu", malgré plusieurs tentatives, des reboots (toujours tenter un reboot sous windows.....), autant dire que j'en regrette presque le temps ou elle me disait "juste" que l'IP est déjà utilisée par une carte fantome.....

Et bien sur, comme j'ai tout fait a la main dans la registry, pas moyen de faire une "restauration de configuration fonctionnelle".

Il est alors tard, j'en ai marre, et j'en arrive à la seule décision rationnelle que j'aurais du prendre dès le début: le windows va dégager, une Debian c'est très bien, et pour le très peu d'utilisation de windows nécessaire, ca passera dans une machine virtuelle......

Et j'en profite du coup au passage (et pour ceux qui raleraient, c'est pareil) pour signaler l'existence de VirtualBox, une interface de gestion de machines virtuelles dans le meme genre que VMWare, mais diffusé sous GNU GPL.

mardi, octobre 30 2007

To optimize or not to optimize, that is the question.....

Rahlala, ces petits jeunes qui ne connaissent même pas ZE codeur, plus casse couilles que théo, plus intégriste que richard, et qui code de façon plus illisible que richie !!!!

J'ai nommé bien évidemment le fameux, l'unique (heureusement), l'incontournable (quoique): Daniel J. B. (rien à voir avec une marque de bouteille carrée a ma connaissance), auteur entre autres de plein de logiciels essentiellement interopérables avec eux même, sous une licence qui pourrait vous faire croire qu'elle est Opensource si vous n'y prêtez pas trop attention, et qui ont parfois du code source curieux.....

Entre autres exemples de code curieux, on avait trouvé à une époque un truc dans ce genre:

 for (;;) {
    if (!n) return; *to++ = *from++; --n;
    if (!n) return; *to++ = *from++; --n;
    if (!n) return; *to++ = *from++; --n;
    if (!n) return; *to++ = *from++; --n;
 }

L'idée de base de ce truc la, au delà du fait d’être moins lisible, c'est de faire moins d'itérations de boucle (un peu comme un -funroll-loops de gcc, quoi).

En des temps reculés, c'était sûrement pertinent (enfin, à part le fait de le rendre moins lisible, bien sur). Bien sur, je vous parle d'un temps que les moins de 20 ans ne peuvent pas connaître: les CPUs en ce temps la disposaient (parfois) de quelques kilooctets de mémoire cache, effectuaient sagement une instruction CPU à la fois, et le moindre JMP (lire "Jump", soit un saut à une autre adresse mémoire pour exécuter la prochaine instruction) ralentissait à mort le déroulement du programme.

Mais qu'en est-il aujourd'hui ?

Nos CPUs modernes disposent de plus de mémoire cache que mon Amiga 500 n'avait de Chip RAM (mémoire principale), ont des pipes (lire "tuyaux") d'instructions monstrueux, font de la prédiction de saut de branche, etc....

Encore une fois, il faut des professionnels au service des hommes, des gens volontaires, dévoués corps et âme à la cause, prêts à prendre tous les risques pour la science, fût-ce au péril de leur vie. Et en plus ca tombe bien, on est le 30 du mois et j'avais pas fait mon billet.....

ATTENTION: comme d'habitude, les tests sont effectués par des vieux cons de hackers old school, des gars qui ont survécu à des trucs terribles (installation d'une Gentoo sur un Octane, mise à jour en FreeBSD7, passage à Xorg 7.3, plus de papier dans les toilettes, réunions "OuaneToOuane", repas de famille avec la belle mère, etc....), des gars chevelus, avec des t shirt de geek, des gars qui comprennent celle de l'admin sys qui édite ses variables d'environnement, et PATH le chemin, des gars qui sont même morts de rire en se re-racontant cette blague, c'est pour vous dire !

Et bien sur, tout ça est fait dans un labo haute sécurité, on a les pompiers, le gros bouton rouge pour tout arrêter en cas de problèmes, l'infirmière aux gros seins au cas ou on aurait besoin d’être réanimés, un système d'exploitation robuste et fiable, des câbles antivol, une bouteille d'huile d'arachide, une superbe baie de brassage et deux gros airbags (ah, on me signale que non, apparemment il s'agirait toujours de l'infirmière sus-nommée.....).

Donc déconnez pas, ne FAITES PAS CA CHEZ VOUS, c'est dangereux, ou en tout cas venez pas chialer ici si vous vous êtes loutrés, que ca a formaté votre PC, fait mourir votre hamster, fait fuir votre copine, fait venir votre belle mère, vous a fait perdre au loto, ou toute autre catastrophe du genre.

Bon, donc, comment qu'on fait si on veut copier de la donnée rapidement:

  • La façon "classique", c'est de copier caractère par caractère.
  • La façon "classique et moderne", c'est de copier int par int.
  • La façon "a la D.J.", c'est de copier "classique", mais 4 affectations par boucle.
  • On peut faire quelques mélanges de ces techniques.
  • En en discutant un peu, on m'a fait découvrir la façon Duff's device.
  • Et bien sur, la façon "on se fait pas chier": on appelle bcopy(), tout simplement.....

Pour tester tout ça, on va utiliser un petit programme automatique, forcément, puisqu'un vieux dicton d'informaticien dit "ne fais jamais toi même ce que tu peux faire faire par un programme":

On va commencer par un beau tableau qui contient tous nos algos:

struct copy_alg_t {
	char	*name;
	void	(*func)(const void*, void*, size_t);
}copyalgs[] = {
	{"bcopy", bcopy},
	{"loop_char", loop_char},
	{"loop_int", loop_int},
	{"loop_4char", loop_4char},
	{"loop_16char", loop_16char},
	{"loop_256char", loop_256char},
	{"loop_4int", loop_4int},
	{"duff_char8", duff_char8},
	{"byte_copy", byte_copy},
	{"bcopy", bcopy},
	{"NULL", NULL},
};

bcopy() est présent au début et en fin de la table, juste pour vérifier qu'on n'a pas particulièrement d'effet de cache sur le premier algo de la liste.

loop_xxxx() sont des algos de loop, le xxxxx indique le type de valeur manipulée et la quantité de valeurs manipulées par itération de boucle.

Quelques exemples:

#define LOOP_CHAR_INLINE(src, dst, size){\
	char	*_s, *_d;\
	_s=(char*)src;\
	_d=(char*)dst;\
	while(size--)\
		*_d++=*_s++;\
}

Ca, c'est la version "octet par octet", je l'ai fait en define parce que j'ai bien senti le coup venir: toutes les autres versions vont avoir besoin de ça pour "finir" la copie si la taille n'est pas un multiple de la taille de bloc traitée, et comme j'avais ni envie de faire de copier-coller massif, et que je voulais pas prendre le "risque" de polluer les tests par des appels de fonctions supplémentaires, c'est passé en macro.

Du coup, loop_char(), c'est ça:

void loop_char(const void *src, void *dst, size_t size){
	LOOP_CHAR_INLINE(src, dst, size);
}

Et, pour vous donner 2 exemples un peu plus complexes:

void loop_4char(const void *src, void *dst, size_t size){
	char	*s, *d;
	s=(char*)src;
	d=(char*)dst;
	while(size>4){
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		size-=4;
	}
	LOOP_CHAR_INLINE(s, d, size);
}
void loop_4int(const void *src, void *dst, size_t size){
	int		*s, *d;
	s=(int*)src;
	d=(int*)dst;

	while(size > 4*sizeof(int) ){
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		size-=4*sizeof(int);
	}
	LOOP_CHAR_INLINE(s, d, size);
}

duff_char8(), c'est ca:

void duff_char8(const void *src, void *dst, size_t size){
	char	*s, *d;
	s=(char *)src;
	d=(char *)dst;

        switch (size % 8)  /* size > 0 assumed */
        {
           case 0:        do {  *d++ = *s++;
           case 7:              *d++ = *s++;
           case 6:              *d++ = *s++;
           case 5:              *d++ = *s++;
           case 4:              *d++ = *s++;
           case 3:              *d++ = *s++;
           case 2:              *d++ = *s++;
           case 1:              *d++ = *s++;
           } while ((size -= 8) > 0);
       }
}

Ouais, ça compile. Ca vous la coupe, hein ?

Et, pour reprendre le code exact de notre cher ami D.J., à peine adapté pour pouvoir s'intégrer dans notre procédure de test, on a aussi ça:

void byte_copy(const void *src, void *dst, size_t size)
{
	register char *from;
	register char *to;
	register size_t n;

	from=(char *)src;
	to=(char *)dst;
	n=size;

	for (;;) {
		if (!n) return; *to++ = *from++; --n;
		if (!n) return; *to++ = *from++; --n;
		if (!n) return; *to++ = *from++; --n;
		if (!n) return; *to++ = *from++; --n;
	}
}

Il ne nous manque plus qu'une fonction qui remplit un tableau avec des valeurs "aléatoires":

void init_refbuff(char *buff, size_t size){
	while(size--)
		*buff++= (size&0xFF) ^((size&0xFF00)>>8);
}

(ok, mon aléa est plus que discutable, c’était surtout pour ne pas avoir un tableau avec que des zéros).

Et bien sur, un programme principal, qui remplit le tableau de référence, et qui teste chaque algo en le chronométrant:

int main(int argc, char **argv){
	struct copy_alg_t	*alg;
	struct timeval		starttime, endtime;
	int		i;

	char	ref_buff[BUFSIZE];
	char	dst_buff[BUFSIZE];

	init_refbuff(ref_buff, BUFSIZE);

	for(alg=copyalgs; alg->func != NULL; alg++){
		bzero(dst_buff, BUFSIZE);
		printf("Alg %s...", alg->name);
		for(i=strlen(alg->name); i<15;i++)
			putchar(' ');

		gettimeofday(&starttime, NULL);
		alg->func(ref_buff, dst_buff, BUFSIZE);
		gettimeofday(&endtime, NULL);
		if(starttime.tv_usec > endtime.tv_usec){
			endtime.tv_usec+=1000000;
			endtime.tv_sec--;
		}
		printf("%10d micros",
			   (endtime.tv_sec-starttime.tv_sec)*1000000+
			   endtime.tv_usec-starttime.tv_usec
			);
		if(memcmp(ref_buff, dst_buff, BUFSIZE))
			printf("***  Mismatch !!!  ***");
		else
			printf(" Ok");
	}

	return 0;
}

Notez quelques points quand même:

  • On donne uniquement un résultat en microsecondes.
  • On fait un bzero du buffer de destination a chaque fois, "au cas ou".
  • On vérifie bien que la copie est conforme à l'original, ça permet de vérifier qu'on s'est pas loutrés dans les implémentations, et qu'elles font toutes ce qu'elles sont censées faire.

Après 2-3 tests rapides, BUFSIZE sera fixé à 16*1024*1024, soit 16 mégaoctets, qui permet d'avoir des tests relativement rapides, mais tout de même facilement mesurables.

Pour ceux qui se diraient "feignasse, le Vanhu, il aurait quand même pu coder vite fait un truc qui les trie et qui les affiche du plus rapide au plus lent", je répondrai juste: "bande de noobs....":

vanhu@darkstar ~/work/c$ make copy
vanhu@darkstar ~/work/c$ ./copy |sort -k3 -n
Alg bcopy...               15807 micros Ok
Alg bcopy...               16139 micros Ok
Alg loop_64int...          30093 micros Ok
Alg loop_int...            37409 micros Ok
Alg loop_4int...           38564 micros Ok
Alg loop_256char...       175909 micros Ok
Alg duff_char8...         178660 micros Ok
Alg loop_4char...         202243 micros Ok
Alg loop_16char...        230192 micros Ok
Alg byte_copy...          234586 micros Ok
Alg loop_char...          245383 micros Ok
vanhu@darkstar ~/work/c$

Bien sur, il faut exécuter ca plusieurs fois et faire une moyenne, on constate que certains algos très proches dans le classement ne sont pas toujours classés dans le meme ordre, mais ca donne une bonne idée.

On constate donc:

  • Que tous les algos font bien ce qu'on leur demande, c'est déjà ca ! :-)
  • loop_char() est bien nettement le plus lent, et se retrouve systématiquement en dernière position.
  • byte_copy() fait a peine mieux, alors qu'il utilise pourtant des register, qu'il divise par 4 le nombre de boucles, tout ca..... C'etait bien la peine de réécrire la moitié de la libc...
  • loop_xchar() font pas vraiment mieux non plus, sauf peut etre loop_256char(), et encore, pas toujours.
  • Meme combat pour duff_char8(), qui arrive quand meme a etre vaguement devant ses potes qui travaillent aussi 8 bits par 8 bits (voire 16 par 16) quand meme.
  • On a une grosse accélération dès qu'on passe en int, tout simplement, et la encore, ca change pas des masses qu'on travaille par 1 ou par 64 int (bon, par 64 ints, c'est toujours un poil plus rapide que par 1 ou par 4, qui se valent en moyenne).
  • bcopy() met une valise à tout le monde, tout le temps....


Conclusions:

  • Vous faites pas chier à recoder la libc, les gars....
  • Vaut mieux travailler par mots machine et faire de grosses boucles, que faire du unroll octet par octet.
  • Faut vraiment faire un unroll massif pour que ca commence à se sentir sur de grosses données.
  • bcopy() met une valise à tout le monde, je sais plus si je l'avais déjà dit....

Forcément, la question que vous vous posez surement, c'est "comment qu'y fait bzero() pour mettre une valise à tout le monde"

Bah simple: il est op-ti-mi-zé, et il sait que, dans son contexte précis, il peut exploiter les possibilités du processeur:

[des trucs qui vont bien pour avoir un programme avec les symboles de déboguage, tout ca chargé dans un gdb]

(gdb) disass bcopy
Dump of assembler code for function bcopy:

[des trucs pour gérer les arguments, pour voir si la taille fait pas zero, s'il fait pas trop froid dehors, si on a bien éteint le gaz, tout ca]

0x2812fa97 <bcopy+23>:  mov    %ecx,%edx
0x2812fa99 <bcopy+25>:  shr    $0x2,%ecx
0x2812fa9c <bcopy+28>:  repz movsl %ds:(%esi),%es:(%edi)
0x2812fa9e <bcopy+30>:  mov    %edx,%ecx
0x2812faa0 <bcopy+32>:  and    $0x3,%ecx
0x2812faa3 <bcopy+35>:  repz movsb %ds:(%esi),%es:(%edi)

Donc voila, il fait faire sa boucle par le CPU, tout simplement, et ca dépote sa maman.....

Le mois prochain, on causera d'un truc ou il y a pas besoin de foutre des copies de code partout, parceque c'est super galère à faire dans un truc de blog, y'a la moitié du code qui est interprété par le bazar a bidule.

vendredi, septembre 28 2007

To optimize or not to optimize ?

Aujourd'hui, j'ai essayé de mettre fin à mes jours: j'ai mangé d'une traite un tas de cachous lajaunie, provenant d'une boite qu'on a retrouvé lors d'une recherche archéologique entre nos bureaux au boulot.....

Bah comme dit le proverbe: "ce qui ne te tue pas te rend plus fort"...... me sens vachement plus fort..... et ... euh.... j'ai tellement d'inspirations qui se battent dans ma tete a propos de mon billet du mois que j'ai du mal a faire le tri......

Voila voila......

Alors du coup, j'ai décidé de vous faire plancher vous aussi sur un petit exercice qui m'a été proposé lors d'un "entretien" (j'aime pas le mot, disons que je discute avec des gens d'opportunités possibles :-) ) avec "un moteur de recherche super connu qui recrute a mort, y compris en europe, mais que je vous donne pas le nom pour que vous cherchiez un peu quand meme").

La question de base est simple (je vous la fait en francais, rien que pour vous): soit un tableau d'entiers qu'il est vachement grand, le tableau (vraiment grand, comme dans "ah la vache, quand meme !", voire "ca fait combien de chiffres, un nombre comme ca ???", ou carrément "mais meme mon disque dur stocke pas autant !!!"). On veut pouvoir compter le nombre de bits de ce tableau a 1 (ou a zéro, commencez pas à chipoter, c'est la meme méthode) rapidement. Pour ca, comme on est pas des raclures quand meme, et qu'on se doute un peu que le comptage des bits un a un dans le tableau va prendre un poil de temps, on vous file autant de mémoire vive que vous voulez (et vu les gens qui disent ca, j'ai tendance à y croire !!!).

On ramasse les copies dans 5 minutes......

Bien sur, il y a un indice: "vous avez plein de RAM"..... on se dit donc tout de suite qu'il va bien falloir utiliser un cache de quelquechose dans l'histoire, tant qu'a faire, ca serait bete de gacher tant de mémoire, de toutes facons, c'est la tournée du patron.

Il ne reste donc plus qu'a trouver que cacher:

- les restes de la blanquette de veau à l'ancienne d'hier soir, déjà. Bonne initiative pour etre sur de pouvoir les finir ce soir, mais soyons lucides, il vaut mieux les cacher au fond du frigo, derrière les trucs infames et repoussants (légumes, salade, yaourts 0%, etc....) que dans de la RAM (surtout qu'il faudrait la retrouver, après, la blanquette, dans autant de mémoire !!!).

- Les clés de bagnole, on oublie tout de suite, ca a fait un bide au ciné, déjà.

- le stock de saucissons. Laissez tomber, je suis un radar ambulant pour trouver toute trace de cochonnaille qui traine a moins de 500 metres, votre sauciflar n'a aucune chance de survie avec moi.....

- Une liste de sites webs intéressants autant qu'originaux, expliquant l'utilité de chaussures à semelles compensées pour la pèche aux crustacés..... mouais, on aura aussi vite fait d'utiliser un moteur de recherche pour ca, hein....

- Des trucs qu'on risque de calculer souvent. Ca, ca parait bien, comme base, pour des trucs à mettre dans un cache lors de gros calculs, ca doit etre ca.....

Et qu'est-ce qu'on va calculer souvent ? Bah le nombre de bits "d'une zone".

Forcément, générer un cache de l'ensemble des résultats possible de toutes les combinaisons du tableau gigantesque n'est pas crédible: ok, on nous a promis toute la mémoire qu'on veut, mais faudrait tant qu'à faire qu'on ne soit pas obligé de demander à nos arrière - arrière - arrière - arrière petits enfants de regarder le résultat (qui sera vraisemblablement 42), tellement ca serait long à remplir.

Non, on va travailler par blocs. Reflexe de base chez tout informaticien normalement (?) constitué (c'est à dire équipé de doigts assez souples pour faire CTRL-Space META-w CTRL-y CTRL-x CTRL-s), travailler par blocs de 32 bits.

On va donc faire une table de 2^32 valeurs, chaque valeur représentant le nombre de bits à un de sa position. Soit, en pratique: cache[i]=nbbits(i)

Sauf que 2^32 valeurs, ca fait quand meme déjà pas mal. Aucun problème pour ce qui est de la quantité de mémoire, c'est toujours la tournée du patron (rappel: ne FAITES PAS CA CHEZ VOUS !), c'est plutot pour le temps de remplissage que ca pourrait etre assez long.

Du coup, on peut se dire "bah pas grave, on va travailler avec un cache sur 8 bits, au lieu d'un cache sur 32 bits". Bah oui, un cache comme ca sur 8 bits, ca se remplit en meme pas 1 seconde sur une machine moderne.

Et si on pousse un peu plus loin, on peut meme se rendre compte que, une fois qu'on a le cache 8 bits, on peut s'en servir pour très rapidement générer le cache sur 32 bits (ou n'importe quel autre multiple de 8):

cache32[i] = cache8 [ (i&0xFF000000)>>24] + cache8 [ (i&0x00FF0000)>>16] cache8 [ (i&0x0000FF00)>>8] cache8 [ (i&0xFF)]

Et hop, le tour est joué.....

Sauf qu'en en rediscutant et en y réfléchissant, y'a un truc pas aussi simple que ca.....

Les machines actuelles sont tout, sauf un truc simple avec "un processeur" et "une mémoire".

Et entre autres choses, il y a des mémoires cache, qui sont nettement plus rapides que la mémoire principale, mais aussi nettement plus petits.

Et si on sait qu'un tableau de 256 entrées (notre cache de calcul sur 8 bits) tiendra meme sur dans la mémoire cache du 68000 de mon Amiga 500, je dois me rabbatre sur un PC récent pour qu'une table de 65536 entrées (un cache de 16 bits) aie de bonne chances de rester complètement dans la mémoire cache du CPU pendant toute l'exécution du programme, et ma table de 2^32 entrées, ca sent le sapin (pour ceux qui, au début de l'article, étaient déjà en train de préparer un commentaire genre "ah le noob, maintenant on a des machines sur 64 bits", votre table de 2^64 entrées dans un cache CPU, vous pouvez vous la mettre au fond a droite..... s'il y a assez de place, bien sur....).

On se trouve donc confronté à un épineux dilemne:

Soit on traite beaucoup de bits d'un coup (non, ceci n'est pas une insulte ou une phrase déplacée, c'est juste une discussion d'informaticiens), mais on est surs de faire une quantité massive de cache miss (je parle bien du cache mémoire du CPU), donc on va lire très souvent notre cache de calcul en mémoire principale (qui est plus lente).

Soit on s'assure que notre cache de calcul restera dans le cache du CPU, mais on travaille par plus petites quantités de données.

Et la, vous vous dites "ouhlala, heureusement qu'une équipe de professionel va faire ce test à ma place, dans un environnement stérile, avec des charlottes qui donnent l'air con sur la tete, un gros bouton rouge pour tout arréter en cas de problèmes, une équipe de secours sur place prète à intervenir et tout et tout !!".....

Et bien sur, en finaud (terme officialisé par l'académie francais pour traduire "hacker", qui, je vous le rappelle, n'a au départ aucune connotation négative et signale juste une curiosité technique) que je suis, j'ai sorti mon éditeur préféré, et j'ai commencé à coder le "proof of concept !".

Le quoi ?

Le "proof of concept !"

Le quoi ?

Le .... le "proufe aufe concepte", la preuve qui prouve que le concept marche, quoi.....

Aaaahhhh !!!! le "proof of concept !".....

Ouais, voila, j'aurais du commencer par dire ca..... je peux continuer, maintenant, c'est bon ?

Donc, disais-je, avant d'avoir été innoportunément interrompu (m'en fous, j'ai les traces des gens qui viennent lire le blog, je pourrai retrouver le coupable !), j'ai commencé à coder vite fait une preuve de concept ("une proof of concept", quoi).

Avant de me rendre compte d'un léger tout petit détail: je n'ai PAS toute la mémoire que je veux sur ma machine, et quand je déclare un tableau de 0xFFFFFFFF entrées (ouais, si je fais 1<<32 ou 0xFFFFFFFF+1, ca fait un tour de compteur, ca vaut zero....), mon compilateur m'envoie bouler, poliment, certes, mais fermement.....

Et meme si j'essaie subtilement un

unsigned char bits_32[1<<16][1<<16];

meme résultat, on m'explique que je suis bien gentil, mais qu'il faudrait pas non plus que je rève eveillé, ca existe pas des tabeaux comme ca, dans la vraie vie....

Du coup, je me retrouve dans un cas "a la con": un tableau de 2^16 octets, il va tenir dans mon cache, et un tableau de 2^24 octets, c'est une valeur "a la con" (en pratique, on n'aura pas la meme accélaration qu'en manipulant des mots machine, soit des blocs de 32 bits).....

Du coup, me reste 2 solutions:

- Ressortir mon Amiga.... tiens, si j'ai un peu le temps ce WE, je vais essayer.

- Finir mon process de recrutement chez "un moteur de recherche super connu qui embauche un max en ce moment", quoique ca m'arrangerait carrément qu'ils ouvrent un campus pas trop loin de Lille, plutot que de me proposer de bosser en Suisse, au Royaume Uni ou dans les pays scandinaves.....

Au moins, la bonne nouvelle, c'est que j'aurai pas eu a mettre une charlotte blanche qui donne l'air con sur la tete aujourd'hui.........

lundi, mai 21 2007

Impossible de faire un malloc de 512 Megas ???

Rahlala, encore une fois, la foule en délire réclame un avis d'expert sur un problème technique qui leur fait faire des cauchemards la nuit, qui leur coupe l'appétit, voire qui les perturbe en jouant a leur shoot-them-all préféré (ne nous affollons pas pour autant, avec les poke qui vont bien, leur vaisseau ne craint pas grand chose d'autre qu'un tag ou deux sur l'arrière des tuyères d'échappement).

Le problème peut encore une fois se résumer dans un code C fort simple:

#include <stdio.h>
#include <stdlib.h>

#define MEGA 1024*1024
#define VALUE 512

int main(void){
	char *p;

	p=malloc(VALUE*MEGA);
	if(p != NULL){
		printf("Malloc ok
");
		free(p);
	}else{
		printf("Malloc failed
");
	}

	return EXIT_SUCCESS;
}

Soit un bete programme qui tente d'allouer VALUE MegaOctets (il est aujourd'hui formellement interdit de troller sur la valeur réelle d'un MegaOctet, notre équipe technique aura ici volontairement choisi 1 Ko == 1024 Octets pour des raisons qui devraient vous paraitre évidentes d'ici la fin de cet article), et qui nous dit "ok" ou "pas ok" (notez bien que le programme aurait aussi pu dire "mayo" ou "ketchup", voire "brandade de morue" ou "blanquette de veau à l'ancienne", mais on est ici dans un blog sérieux, avec des tests réalisés par une équipe de professionel (non, il n'y a pas de faute a "professionel", l'équipe étant essentiellement composée d'une personne), donc le programme répond "ok" si le test est ok, ou "failed" (qu'on peut traduire par "échoué", pour les plus anglophobes) si le test a échoué).

Attention !!!

Comme d'habitude, le développement et l'éxecution des programmes fournis ici sont fait par des professionels, en environnement de laboratoire, avec un gros bouton rouge d'urgence, les pompiers sur le qui-vive, une infirmière aux gros seins prete à faire de la réanimation d'urgence, et un agent d'assurances qui sue des gouttes grosses comme mon poing (et pas seulement parcequ'il a une vue plongeante sur le décolleté de l'infirmière), NE FAITES PAS CA CHEZ VOUS LES ENFANTS !!!!!! (sauf éventuellement profiter du décolleté d'une infirmière de passage, mais ca c'est votre vie privée, hein, ca me regarde pas).

Il se trouve que, après plusieurs tests, la limite évidente a partir de laquelle le programme répond "failed" est 512 (donc 512 MégaOctets), sur une machine disposant pourtant d'1 GigaOctet (le double, donc) de RAM, 2 GigaOctets de SWAP, et n'ayant pas de compilation d'OpenOffice en cours......

Premier test évident, refaire la meme expérience en tant que root, on obtient le même résultat..... La tentation est bien évidemment grande d'aller directement regarder le code source de malloc(3), mais il y a peut etre finalement un autre test plus simple à faire en premier:

vanhu@darkstar $ ulimit
unlimited
vanhu@darkstar $

Damned, un vieux gars en cape toute miteuse m'a pourtant encore dit ce WE de me fier a la Force.... On m'aurait menti ??

Peut etre pas:

vanhu@darkstar $ ulimit -a
.....
data seg size           (kbytes, -d) 524288
.....

524288..... curieusement, équivalent à 512*1024, et représentant un nombre de kilooctets.....

Un premier test permet de rapidement se convaincre que ca influe effectivement: il suffit de faire un ulimit -d <511*1024>, et la, pouf, ca ne fonctionne plus avec VALUE a 511, alors que ca marchait encore il y a 2 minutes, et que ca fonctionne toujours dans le shell d'a coté ou on a pas fait cette commande magique.....

La suite du test est normalement "simplement" de faire un ulimit -d <520*1024> et la, pas de bol, il se trouve qu'on ne peut pas "gonfler" les limites (heureusement....), et que la comme ca, j'ai pas que ca a faire d'aller retrouver le fichier de conf dans /etc ou on met les valeurs par défaut, juste pour quelques noobs qui se posent des questions a 2 balles et qui connaissent meme pas ulimit.....

C'etait bien la peine de me déranger pour ca, c'est bien parceque je n'avais pas encore fait mon billet du mois, tiens !

jeudi, avril 19 2007

De l'inutilité de top....

J'ai recu des demandes d'un nombreux collègue pour écrire quelquechose à propos de la commande UNIX top(1).

Soyons clairs: cette commande ne sert pas à grand chose....

La question du jour était la différence entre "Size" et "Res", et la réponse à la grande question qui empèche aujourd'hui encore une meilleure compréhension de grands mystères comme l'originie de l'univers, la reproduction des huitres du bassin d'arcachon, la capacité des femmes a causer string et maquillage pendant toute une après midi ou la signification exacte des chiffres alignés sur une feuille d'impots: comment faire pour savoir *vraiment* quelle est la consommation mémoire d'un programme ?

Et, après quelques expérimentations, la réponse reste désespérément "on sait pas"....

Res indique manifestement la quantité de mémoire du processus qui est vraiment "utilisée" en mémoire vive.

Un premier test en laboratoire (attention, les enfants, ne faites pas ca chez vous, cette expérience a été réalisée par un professionel !!!) permet de constater que la mémoire n'apparaitra pas en "Res" tant qu'elle n'est pas utilisée:

int main(void){
	char *p=malloc(128*1024*1024);
	int i;
	
	sleep(10);
	printf("1");
	
	for(i=0; i<128*1024*1024; i++)
		p[i]='a';
	printf("2");
	
	sleep(5);
	printf("3");
	free(p);
	p=NULL;
	
	sleep(5);
	printf("4");
}

En exécutant ce programme, on constate que "Size" gonfle tout de suite, alors que "Res" ne gonflera que entre "1" et "2".

Par contre, si on regarde la fin de l'exécution, on constate que ni "Size" ni "Res" ne diminuent après l'appel a free(3). Après quelques instants de frayeur, ou une équipe quasi complète d'ingénierie a failli renier ses croyances les plus profondes pour aller élever des chèvres dans le larzac (je vous avais prévenu que c'est dangereux, et qu'il faut pas faire ca chez vous !!!), je tente un 2eme test: je transforme mon main() en fonction locale, et je crée un nouveau main() qui va appeler plusieurs fois d'affilée ma fonction.

La tension a tout de suite baissé d'un cran quand nous avons constaté que ni "Size" ni "Res" ne gonflaient, et que la consommation mémoire restait stable quelque soit le nombre d'appels. Ouf, l'univers peut encore continuer de fonctionner.

Mais tout ca nous a quand meme confirmé une chose: ni "Size" ni "Res" ne nous montrent la consommation "réelle" d'un programme, mais nous montrent au mieux un "pic" (et encore, je vous fais grace des caches disques qui apparaissent dans "Size", et qui peuvent ammener une équipe de développeurs a passer plusieurs journées à chasser un Memory Leak qui n'existe pas, ambiance garantie !).....

Bref, l'univers peut continuer de tourner, certes, mais c'est pas aujourd'hui que je comprendrai comment certaines de mes copines peuvent parler de leur string pendant toute une après midi........