Mise à jour du firmware de mon SSD, the hard way

J’ai un SSD pour lequel le constructeur a sorti une mise à jour du firmware. Il s’agit du modèle M550 de chez Crucial qui passe de la version M01 à la version M02. Ce SSD est au format mSata, dans mon routeur qui est un APU1 de PCEngines. Ce routeur n’a pas de sortie vidéo, ne fonctionne pas sous Windows mais sous Linux, et n’a pas de lecteur CD. Quelque chose me dit que cela ne va pas me prendre 5 minutes…

Pour mettre à niveau le firmware, je dispose de deux possibilités :

  • connecter le SSD sur une autre machine plus « standard » et faire la mise à jour depuis cette machine. J’aurais bien opté pour cette solution si j’avais eu un adaptateur SATA > mSATA, et puis cela aurait été moins fun, non ?
  • mettre à jour directement le SSD sans utiliser le tournevis

Crucial met à disposition le package de mise à niveau sous la forme d’une image CD. Je pourrais éventuellement brancher un lecteur CD en USB au routeur, voire même modifier le bootloader pour booter directement sur l’iso mais il est fort à parier que le CD n’a pas été prévu pour un contrôle depuis le port série, voie sans issue donc.

CD, dis-moi qui tu es

Explorons un peu le contenu de cette image.

Contenu de l'iso
Contenu de l’iso

On commence à avoir des choses intéressantes. Le CD contient un noyau linux (vmlinuz) et le bootloader est isolinux, un standard pour booter du linux depuis un CD ou une clé USB. Je serais tombé sur une mise à jour basée sur DOS (Caldera DOS, FreeDOS…), j’aurais été bon pour acheter l’adaptateur.

Examinons le fichier de configuration du bootloader:

DEFAULT sd

LABEL sd
KERNEL /boot/vmlinuz
INITRD /boot/core.gz
APPEND quiet base loglevel=3 waitusb=10 superuser rssd-fw-update rssd-fwdir=/opt/firmware rssd-model=M550   

On remarque un certain nombre de paramètres non standard, ainsi que le initrd.

Hacking the kernel

Pour continuer l’analyse de l’iso, je redémarre redémarre le routeur sur la sdcard (système de secours si le ssd lache) afin de laisser totalement inutilisé le SSD, puis copier le contenu de l’iso dans disons /tmp/crucial/iso . Qui dit initrd dit cpio. Je décompresse donc core.gz puis extrayons le contenu de l’archive cpio dans  /tmp/crucial/root .

Le point d’entrée par défaut est le script /init , vu qu’il n’a pas été redéfini dans les paramètres envoyés au kernel.
Il est d’une extrême simplicité et se résume à :

  • monter /proc
  • lancer /sbin/init

init , s’il s’agit du bon vieux process racine servant à faire fonctionner Linux, va se servir de /etc/init.d afin d’initialiser les différentes partie du système. Certains de ces scripts sont reconnaissable (rc.shutdown) voir inutile à vue (dhcp.sh, settime.sh…). Il y en a un toutefois qui retient mon attention : msd-config.sh car son nom ne m’est absolument pas familier.

Bingo ! Ce script, simple également, récupère les paramètres envoyés au kernel, via le fichier /proc/cmdline , et lancer le processus msecli  avec les paramètres adéquat. Il va notamment utiliser le paramètre kernel rssd-model=M550 , ce qui me conforte que je suis bien sur la bonne piste, le M550 étant pour rappel le modèle de mon SSD.

Au final, le script exécutera la commande suivante : /sbin/msecli -U -i /opt/firmware -m M550 -y .

Allons-y :

apu:/tmp/boot/root# ./sbin/msecli --help
-ash: ./sbin/msecli: not found
apu:/tmp/boot/root# ll ./sbin/msecli
-rwxr-xr-x    1 root     root      635.5K Apr  9 14:22 ./sbin/msecli
apu:/tmp/boot/root# ldd ./sbin/msecli
ldd: ./sbin/msecli: Not a valid dynamic program

Le fichier est bien présent, pourtant il n’est pas possible de l’exécuter. C’est plutôt bizarre…

Go down the rabbit hole

Une analyse sauvage du fichier (sous vim) m’apprend qu’il s’agit d’un exécutable non statique, c’est à dire qu’il utilise des librairies chargées dynamiquement. Sachant que le Linux hôte est Alpine Linux, la libc en vigueur n’est pas l’immonde glibc mais musl. Comme indiqué ci-dessous, la présence de /lib/ld-linux.so.2 trahit le caractère dynamique de l’exécutable. Cela explique également pourquoi le ldd sur l’hôte n’est pas parvenu à parser correctement l’exécutable.

^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@<90>)^E^H4^@^@^@Ì  ^@^@^@^@^@4^@ ^@^H^@(^@^]^@^Z^@^F^@^@^@4^@^@^@4<80>^D^H4<80>^D^H^@^A^@^@^@^A^@^@^E^@^@^@^D^@^@^@^C^@^@^@4^A^@^@4<81>^D^H4<81>^D^H^S^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A^@^@^@^@^@^@^@^@<80>^D^H^@<80>^D^H^T^U       ^@^T^U  ^@^E^@^@^@^@^P^@^@^A^@^@^@^T^U  ^@^T¥^M^H^T¥^M^H^T^C^@^@t1^@^@^F^@^@^@^@^P^@^@^B^@^@^@(^U       ^@(¥^M^H(¥^M^Hà^@^@^@à^@^@^@^F^@^@^@^D^@^@^@^D^@^@^@H^A^@^@H<81>^D^HH<81>^D^H ^@^@^@ ^@^@^@^D^@^@^@^D^@^@^@Påtd^@Ò^H^@^@R^M^H^@R^M^H<84>^N^@^@<84>^N^@^@^D^@^@^@^D^@^@^@Qåtd^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^F^@^@^@^D^@^@^@/lib/ld-linux.so.2^@^@^D^@^@^@^P^@^@^@^A^@^@^@GNU^@^@^@^@^@^B^@^@^@^F^@^@^@ ^@^@^@  ^B^@^@I^@^@^@<80>^@^@^@^L^@^@^@D

Je tente un chroot pour résoudre cela ?

apu:/tmp/boot/root# chroot . sh
apu:/# ldd sbin/msecli
        linux-gate.so.1 (0xe82ba000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xe82a0000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xe81e3000)
        libm.so.6 => /lib/libm.so.6 (0xe81ac000)
        libc.so.6 => /lib/libc.so.6 (0xe808f000)
        /lib/ld-linux.so.2 (0xe82bb000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xe8079000)
apu:/# msecli --help
USAGE:
msecli <Main Option> -?

    Provides the detailed usage information for the specified main option

NOTE: All options are not supported by all drives

msecli -L [-a] [-d|-P|-f] [-n <device-name>]  [-r ] [-s <out-filename>]

    Lists the basic information for all micron drives available in the system

msecli -S [-a] [-e|-d] [-l <log-type[0 | 1 | 2 | 3 | 6 | 7]>]
    [-t <Self-test type[0 | 1 | 2]>] [-n <device-name>] [-r] [-s <out-filename>]

    Lists the SMART values for the supported parameters for the micron drives
    available in the system.

msecli -M [-i <value> | -w <state-value> | -p <state-value> ] -n <device-name> [-r] [-s <out-filename>]

    To manage the Micron PCIe drives.

msecli -X [-B | -p <password> | -P -p <PSID> ] -n <device-name> [-r] [-s <out-filename>]

    Performs Secure Erase, Sanitize Block Erase, or PSID Revert for the
    specified micron drive

msecli -D -n <device-name> [-r] [-s <out-filename>]

    Displays the Driver and Host Information for the given <device-name>

msecli -T <UBI img-file> [ [-n <device-name> [-b] [-o]] [-i <firmware-slot>] | -d ] [-n <device-name>..] [-r] [-s <out-filename>]

    Updates the UBI image in the specified Micron PCIe drive.

msecli -m -n <device-name>[-d <duration>] [-i <interval>] [-r] [-s <out-filename>]

    Displays the performance data for the specified P320/P420 drive

msecli -G [-e|-E|-g] -n <device-name> [-a] [-d <Event/Error Log id> -t <Time Stamp> ][-r] [-s <out-filename>]

    Displays the firmware event/error logs for the specified P320/P420 drive.

msecli -Z -n <device-name> [-r] [-s <out-filename>]

    Prepares the specified P320/P420 drive for removal.

msecli -V [-a] [-r] [-s <out-filename>]

    Displays the Micron Storage Executive current Version.

msecli -P <zip file name> -n <device-name> [-r] [-s <out-filename>]

    Collects debug data from the system for each micron drive

msecli -U -i <fw-folder-path> [ -m <model-number> | -n <device-name> ] [-r] [-s <out-filename>]

    Performs firmware update with the firmware folder
    for the specified client drive


Copyright (C) 2015 Micron Technology, Inc.

Yeah ! Je tente d’afficher les informations SMART du SSD

apu:/# msecli -S

CMD_STATUS   : Device Error
STATUS_CODE  : 5

Copyright (C) 2015 Micron Technology, Inc.

Oops ? Après quelques minutes de réflexion, je me suis aperçu avoir encore fait le sauvage en omettant de monter /proc . Je corrige cela prestement puis affiche les informations sur le SSD à l’aide du cli :

apu:/# msecli -L

Drive Id             : 0
Device Name          : /dev/sda
Model No             : Crucial_CT128M550SSD3
Serial No            : xxxxxxxxxxxx
FW-Rev               : MU01
Total Size           : 128.04GB
Drive Status         : Drive is in good health
Sata Link Speed      : Gen3 (6.0 Gbps)
Temp(C)              : 61

Drive information is retrieved successfully
CMD_STATUS   : Success
STATUS_CODE  : 0

Copyright (C) 2015 Micron Technology, Inc.

So far so good. Je lance la mise à jour :

apu:/# msecli -U -i /opt/firmware -m M550
This will update all the M550 drives in the system
Are you sure you want to continue(Y|N):Y

Upgrading drive /dev/sda [Serial No. XXXXXXXXXXXX] from firmware MU01 to MU02
.............
Device Name  : /dev/sda
Firmware Update on /dev/sda Succeeded!
CMD_STATUS   : Success
STATUS_CODE  : 0

Copyright (C) 2015 Micron Technology, Inc.

Done ! La mise à jour est passé sans soucis. Je reboot sur le SSD pour le réinitialiser et repasser sur le système principal pour confirmer que tout s’est bien passer.

Parce qu’Un script vaut mille mots

Ci-dessous un résumé scripté des étapes que j’ai du suivre pour mettre à jour le SSD depuis Linux et sans booter sur l’iso :

#!/bin/sh

ROOT_CRUCIAL_ISO=/tmp/crucial/iso
ROOT_CRUCIAL=/tmp/crucial/root

mkdir -p "$ROOT_CRUCIAL"
gunzip "${ROOT_CRUCIAL_ISO}/boot/core.gz
pushd "$ROOT_CRUCIAL"
cpio -F "${ROOT_CRUCIAL_ISO}/boot/core" -i
popd

# mounting /proc is mandatory for the cli to properly work
mount -t proc none "${ROOT_CRUCIAL}/proc/"
mount --rbind /sys "${ROOT_CRUCIAL}/sys/"

# in case where the kernel has grsecurity enabled, we need to allow CAPS in the chroot
echo 0 > /proc/sys/kernel/grsecurity/chroot_caps 2> /dev/null

chroot "$ROOT_CRUCIAL" sh
msecli -U -i /opt/firmware -m M550

 

Contrôler les LEDs de l’APU (PC Engines)

Depuis 2 ans déjà, j’utilise pour mon accès à Internet personnel non pas une de ces box merdiques, moches, bancales (hello Freebox v6), obscures (black box) et pleines de vide (coucou Freebox v6) mais un APU de PC Engines. C’est une carte mère disposant d’un processeur intégré AMD à architecture amd64, 3 ports Ethernet et 3 leds accessibles via GPIO, donc grosso-modo un mini-pc à consommation très réduite. Ce truc est génial.

Par défaut la première LED indique simplement si le routeur est alimenté ou non et les deux autres leds sont éteintes. Je me suis enfin décidé à me pencher dessus afin de m’en servir pour afficher des indicateurs plus utiles tels que la charge réseau ou la charge cpu.

Le noyau Linux, actuellement en version 4.4.3 au moment où j’écris ces lignes, ne dispose pas de pilotes LED ou GPIO permettant d’accéder à ces LEDS depuis le user-space. Cependant Daduke a développé un petit module linux pour combler ce petit manque.

Après une correction mineure sur les sources, probablement du à une évolution des interfaces du noyau depuis, le module compile bien, se charge bien et permet bien d’accéder aux leds. Ceux-ci sont rendus accessibles par le kernel au userspace dans le dossier /sys/class/leds/.

Une fois que les leds sont accessibles, il suffit de les contrôler manuellement en envoyant 0 ou 255 à /sys/class/leds/{your_led}/brightness  comme suit :

# switch off the first led
echo 0 > /sys/class/leds/apu\:1/brightness

# swich on the first led
echo 255 > /sys/class/leds/apu\:1/brightness

Il est également possible d’utiliser un déclencheur géré directement par le kernel, ce qui est bien plus efficace que d’utiliser un processus en user-space si le trigger existe déjà dans le kernel :

# list available triggers on the first led
# the current trigger is in brackets
cat /sys/class/leds/apu\:1/trigger

# set the trigger heartbeat on the first led
echo heartbeat > /sys/class/leds/apu\:1/trigger

Génération du package Gentoo

L’écriture du package se découpe en 3 parties :

  • le fichier Makefile (ou Makefile.am)
  • le fichier ebuild
  • enfin le mode de génération du Makefile (statique ou via configure)

Maintenant que l’on a bien vérifié que ça marche, il est temps de packager afin de faciliter les mises à jour du noyau et éviter de crasser le système avec un vieux make install en dehors de tout contrôle du système de packages de la distribution.

Tout d’abord le Makefile : doit-on l’écrire, doit-on passer par automake, aclocal, autoscan, autoconf, tous ces outils ensembles ? Je n’ai pu trouver qu’un seul how-to sur Internet indiquant comment créer un configure en charge de générer le Makefile pour un module kernel utilisant ces outils, qui pour rappels, permettent entre autre de procéder à un certain nombre de vérification sur le système, la présence de librairies, et générer un Makefile compatible avec le système.
Le tutoriel ne fonctionne pas, indiquant à autoconf une instruction non reconnue.
Vu que tout ces outils semblent un peu trop usine à gaz, je décide de laisser tomber pour le moment et de me contenter du Makefile d’origine.

Ensuite c’est l’écriture de l’ebuild. Celui-ci n’a pas été trop difficile, la documentation des eclass étant plutôt complète et il existe des ebuilds sur lequel on peut s’appuyer comme net-firewall/ipset ou net-firewall/xtables-addons. Cela donne l’ebuild qui suit (cet ebuild compile 2 modules et non un seul, qu’on va voir dans la section suivante) :

# Copyright 1999-2016 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$

EAPI=5
inherit autotools linux-info linux-mod

DESCRIPTION="Kernel modules to access to leds on the apu motherboard"
HOMEPAGE="https://blog.kveer.fr"
SRC_URI=""

LICENSE="GPL-2"
SLOT="0"
KEYWORDS="amd64"
IUSE=""

DEPEND=""
RDEPEND="${DEPEND}"

MODULE_NAMES='leds-apu(kernel/drivers/leds) apu-btn-gpio(kernel/drivers/gpio)'

pkg_setup() {
    kernel_is -lt 4 3 3 && die "${PN} requires kernel greater than 4.3.3."

    CONFIG_CHECK='LEDS_CLASS MODULES'
    ERROR_LEDS_CLASS='leds-apu requires LEDS_CLASS support in your kernel.'
    ERROR_MODULES='Nonmodular kernel detected. Build modular kernel.'

    linux-mod_pkg_setup
}

src_unpack() {
    mkdir "$S"
    cp "${FILESDIR}"/* "${S}/"
}

src_compile() {
    set_arch_to_kernel
    #KERNEL_DIR=$KERNEL_DIR emake
    BUILD_PARAMS="KERNEL_DIR=$KERNEL_DIR CONFIG_DEBUG_SECTION_MISMATCH=y" linux-mod_src_compile
}

src_install() {
    BUILD_PARAMS="KERNEL_DIR=$KERNEL_DIR" linux-mod_src_install
}

On peut maintenant revenir au Makefile. J’ai trouvé une bonne référence indiquant comment fonctionne les scripts de compilation du kernel et comment de fait écrire un Makefile. Le problème rencontré avec le Makefile de Daduke est qu’il n’est capable de compiler qu’un seul module, or j’en avais deux. Cela m’a amené à créer deux fichiers:

  • un Makefile qui est extrêmement simple, se contentant de passer la main au makefile du noyau Linux
  • un Kbuild qui contient la liste des modules et les fichiers nécessaires à la compilation de chacun des modules
PWD = $(shell pwd)
KDIR = $(KERNEL_DIR)

modules module:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

install: $(MODULE)
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules_install

clean:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
obj-m := leds-apu.o apu-btn-gpio.o
leds-apu.y := leds-apu.c
apu-btn-gpio.y := apu-btn-gpio.c

Configurer les LEDs de l’apu

J’ai configuré mes leds comme suit:

  • la première led indique la charge CPU du routeur. J’utilise pour cela le module heartbeat, qui fait clignoter la led. La fréquence de clignotement est proportionnelle à la charge CPU (plus le routeur travaille, plus ça clognote vite)
  • la seconde led clignote pour tout paquet arrivant de l’interface wan
  • la dernière led clignote pour tout paquet arrivant de l’interface lan et sortant sur le net

Je paramètre les leds au démarrage du routeur avec ce petit script :

#!/bin/bash

echo heartbeat > /sys/devices/platform/apu-led/leds/apu\:1/trigger
echo netfilter-lednet-in > /sys/devices/platform/apu-led/leds/apu\:2/trigger
echo netfilter-lednet-out > /sys/devices/platform/apu-led/leds/apu\:3/trigger

J’utilise iptables pour déclencher le bip sur les paquets entrants ou sortants (ces deux règles sont un peu bidon et ne servent d’exemple qu’à l’utilisation de la cible LED, il faudra procéder de manière similaire pour capturer des trames IPv6 ou autre avec ebtables) :

iptables -A PREROUTING -i enp1s0 -j LED --led-trigger-id "lednet-in" --led-delay 10 --led-always-blink
iptables -A POSTROUTING -o enp1s0 -j LED --led-trigger-id "lednet-out" --led-delay 10 --led-always-blink

Enfin, on n’oubliera pas de charger les modules au démarrage en modifiant le fichier /etc/conf.d/modules :

modules='led_class ledtrig-heartbeat ledtrig-oneshot ledtrig-timer leds-apu'

N’hésitez pas à me laisser vos script en commentaires.

Amélioration du code du module

Avant de considérer le package comme terminé, j’ai activé tous les warning à gcc en ajoutant les flags -Wall CONFIG_DEBUG_SECTION_MISMATCH=y  et compilé le module, dans le but de corriger tous les warning relevés.  Et c’est là, c’est la merde…

Le principal warning est Section mismatch in reference from the function ma_function() to the variable ma_variable() . De ce que j’ai pu comprendre, cet avertissement indique qu’une fonction fait appel à une variable qui se trouve en dehors de sa section, donc potentiellement qui n’existe plus ou pas encore (donc un pointer stale).

C’est la merde parce que une fois l’erreur comprise, on lit le code source, on cherche à savoir comment ce type d’erreur se résout et on se rend compte que la documentation concernant les fonctions du kernel Linux et la manière de bien écrire un module est très morcelée. Je soulèverai notamment que :

  • pour beaucoup de fonctions constituantes du noyau Linux, il faut se contenter des deux pauvres lignes au-dessus de la signature de la fonction comme seule documentation, sans aucune information de contexte ou d’usage
  • la documentation est complètement éparpillée, entre le code source, les quelques rares fichiers du dossier Documentation dans les sources du noyau et le bouquin d’O’Reilly périmé, et les quelques mail d’une ML officielle ou d’une réponse sur stackoverflow d’un gars connaissant le noyau suffisamment

Franchement non seulement ça ne facilite en rien la maîtrise de l’infrastructure du noyau (et pour ma part cela ne concernait que la partie concernant les modules linux et les IO bruts, c’est à dire une infime partie du noyau), mais ça en devient même frustrant et retire pour ma part tout volonté à y apporter ma contribution (déjà que débugger du C sous Linux c’est une tanné, alors si en plus c’est en mode kernel avec une doc de merde…).

Fin

A mon sens et contrairement à d’autres projets utilisant des langages plus structurés (oui le C c’est quand même bien dégueulasse), il est absolument nécessaire d’avoir une documentation claire indiquant précisant l’impact de certaines macros telles que module_init, module_exit, la différence avec la macro module_platform_driver, l’impact du nom du driver, les différents moyens de déclarer de la mémoire, le mécanisme permettant de la mémoire privée pour un pilote, etc… J’ai dû découvrir tous ces éléments par essais successifs, ce qui n’est pas une méthode d’apprentissage très efficace. En l’état actuel, le développement dans le noyau Linux est pour ma part un no go.

Au final, je n’ai pu résoudre les SECTIONS MISMATCH. Cependant j’ai séparé le module initial de Daduke en 2, l’un concernant uniquement le bouton reset (qui se trouve sur la carte mère), et l’autre concernant le contrôle des LEDs. Le code source pourrait être factorisé mais après avoir passé une nuit à comprendre le fonctionnement d’un module Linux, je n’ai plus le courage de faire ce dev et perdre tous mes cheveux lors du débogage.

Je livre néanmoins le package permettant de compiler le module avec Gentoo, qui se trouve sur mon repo perso. Au moins, mes leds clignotent dans tous les sens, et ça c’est sympa #sapin-de-noel.

Monter un tunnel IPSec sous Linux

Ce billet détaille comment monter un tunnel IPSec sous Linux. C’est à mon sens un must have, principalement parce qu’IPSec est une merde ignoble sous Linux.
Mais avant de procéder plus avant, une question se pose.

Pourquoi IPSec ?

Actuellement je souhaite relier :

  • plusieurs serveurs sur Internet
  • plusieurs réseau locaux (celui du bureau et ceux des machines virtuelles derrières le routeur qui détient l’IP publique)

Voici mes possibilités pour réaliser ce « gros » réseau :

  • créer plein de règles nat sur chaque routeurs.
    • c’est pénible à maintenir
    • oblige à reconfigurer les services pour n’utiliser que certaines plages de ports, et sans chevauchement d’une machine/vm à l’autre
    • oblige à avoir une feuille avec la table de mapping et utiliser des ports non standards
      Pas géniale comme solution, et les tuyaux ne sont pas sécurisés.
      On peut alors reconfigurer tous les services pour protéger chaque flux, mais la manière de faire et la possibilité dépendent du service. Désormais on a la plupart des tuyaux qui sont sécurisés et encore plus de maintenance.
  • mettre en place OpenVPN qui répond entièrement à la problématique, mais :
    • il est architecturé en étoile (1 serveur, plusieurs clients). On peut aussi installer OpenVPN sur chaque routeur en mode serveur, mais c’est pas mal de configuration.
    • il fonctionne en faisant le transport sur la couche applicative du modèle OSI (les paquets sont encapsulé dans un flux SSL sur du TCP ou UDP), cela induit un peu d’overhead
      C’est la solution qui est actuellement en place chez moi.

Par rapport à OpenVPN, voici les avantages d’IPSec :

  • c’est du P2P, il n’y a pas de notion « serveur », les routeurs se connectent donc en direct
  • comme OpenVPN, la communication entre les 2 pairs est chiffrée
  • il est kernel-space sous Linux et Windows, il est donc moins lourd qu’OpenVPN et ne nécessite aucune installation sous Windows
  • son intégration aux protocoles IPs lui permet d’avoir un overhead plus faible qu’avec OpenVPN, donc une bande passante plus large
  • les tunnels d’OpenVPN semblent saturer vite vers 10Mbps chez moi

Quelle implémentation d’IPSec ?

C’est une question qui ne se pose que pour le monde Unix. Avoir le choix c’est bien, mais dans le cas d’IPSec, c’est un peu compliqué. Côté logiciel, il y a eu FreeSwan, puis OpenSwan, puis StrongSwan ; côté noyau également, il a existé plusieurs implémentations : klips et netkey.
Aujourd’hui, j’ai jeté mon dévolu sur strongswan. Klips n’existe plus et les autres logiciels ne sont plus maintenus depuis plusieurs années, c’est donc quasiment un choix par défaut, mais également pour la négociation par certificats que strongswan est le seul à proposer (et que je trouve beaucoup plus simple à configurer que le Pre-Shared Key).

La conf !

Elle est très simple. Voici un exemple fonctionnel ci-dessous qui utilise l’authentification mutuelle par certificat, pour le détail: RTFM, ce dernier est plutôt à jour et bien réalisé.
Je ne détaille pas comment générer les certificats, ce n’est pas le but du billet, et puis j’ai déjà traité ça ici.

config setup
cachecrls=yes
# uniqueids = no

# Add connections here.
conn %default
auto=start
compress=no # ne fonctionne pas
dpdaction=restart
esp=aes128-sha1!
ike=aes128-sha1-modp2048,aes128-sha1-modp1024!
rightallowany=yes
left=2001:41d0:fe1c:1300:20c:29ff:fe99:fbf
#left=%any
leftauth=pubkey
rightauth=pubkey
leftid="C=FR, O=Kveer, OU=Kveer IPSec Services, CN=Tefnout"
leftca="C=FR, O=Kveer, OU=Kveer IPSec Services, CN=Kveer IPSec CA"
rightca=%same
leftcert=tefnout.crt
leftfirewall=no
mobike=no
keyexchange=ikev2

conn illidan
right=illidan.kveer.fr
rightid="C=FR, O=Kveer, OU=Kveer IPSec Services, CN=Illidan"
leftsubnet=192.168.3.0/24
rightsubnet=192.168.14.128/27
# les règles iptables à ajouter.
iptables -A INPUT -p udp -m multiport --dport isakmp,ipsec-nat-t -m comment --comment ipsec -j ACCEPT
iptables -A INPUT -p esp -j ACCEPT
iptables -t nat -I POSTROUTING 1 -m policy --dir out --pol ipsec -j ACCEPT

La dernière règle iptables est à ne pas oublier si l’on souhaite faire fonctionner le tunnel pour les routeurs eux-mêmes. Dans le cas d’un tunnel IPv4, ça ne marchera pas, dans le cas d’un tunnel IPv6, le kernel va générer un Oops (la règle est obligatoire sous peine de non-sens, mais cela n’autorise pas le kernel à planter, c’est bien un bug, en attente de correction)

Intégration des modules vmware à linux

Il n’y a pas si longtemps, j’avais écrit un billet permettant de compiler soit-même les quelques modules VMware fournit dans les VMware Tools.
J’ai pu tester ce script à plusieurs reprises d’ailleurs, à chaque mise à jour de kernel en fait, et ça dépanne bien.
Il y a juste un patch, celui concernant le module vmci , qui est à faire manuellement. Pour une raison qui m’échappe encore (je n’ai pas trop cherché non plus, mais je penche vers un problème d’espaces), le copier/coller depuis le script ne fonctionne pas.

Et bien ce billet annonce la bonne nouvelle : ce script va faire partie des vieilleries car depuis la branche 3.9 du noyau Linux, ces modules font désormais partie intégrante du noyau.
Voici le nom des modules (j’en profite également pour rappeler l’existence du pilote vmxnet3 ):

  • CONIG_VMWARE_VSOCKETS
  • CONFIG_VMWARE_VMCI_VSOCKETS
  • CONFIG_VMWARE_BALLOON
  • CONFIG_VMWARE_VMCI
  • CONFIG_VMXNET3

Les re-voici dans le menuconfig, pour vous faciliter la recherche :

 

* Device Drivers --->
** Misc Devices --->
*** \[*\] VMware Balloon Driver
*** \[*\] VMware VMCI Driver
** \[*\] Network device support --->
*** VMware VMXNET3 ethernet driver
* \[*\] Networking support --->
** Networking options --->
*** <*> Virtual Socket protocol
*** <*> VMware VMCI transport for Virtual Sockets

Adieu les vmware tools sous Linux (et sans regrets) !

Compiler les modules vmware sous linux

Afin d’améliorer le fonctionnement et les performances des machines virtuelles, VMware fournit les VMware tools.
Ces  »tools » se décomposent en :

  • une application graphique (wait whaaat ? did you say UI ??)
  • des kernel modules pré-compilés, ainsi que les sources pour les autres noyaux.

J’aurais pu me contenter de fournir les patches, lesquels auraient été suffisant pour compiler les modules, mais en plus d’avoir des codes sources obsolètes, l’installeur s’est mis à dysfonctionner. Impossible donc de compter sur lui pour faire le boulot.

En décortiquant l’installeur, je suis arrivé à un binaire, qui est responsable de la localisation des linux headers sur ma machine, c’est lui qui pour une raison qui m’échappe, ne trouve plus les linux-headers, mais de par sa forme binaire, c’est une voie sans issue (je ne suis pas expert en reverse engineering, et encore moins sous Linux)…

Aucune importance : j’ai trouvé mieux. En décompressant les sources qui se trouvent dans ~/lib/modules/sources , il m’a suffit de lancer un make  et la magie s’est opérée : j’avais mes modules (enfin… après quelques petites modifications) !!

Ce script fonctionne précisément pour les kernels de la branche 3.8 patchés avec grsec. En enlevant les appels à pax_open_kernel  et pax_close_kernel  ou en mettant la bonne pré-condition, on pourrait le rendre compatible avec n’importe quel kernel  de la branche 3.8 je pense.

J’aurais pu fournir les fichiers patch à part, mais j’ai préféré ne garder qu’un seul fichier, afin de conserver une certaine simplicité de gestion pour la maintenance du script.

# sources:
# https://bugs.gentoo.org/attachment.cgi?id=289757&action=edit
# http://pastebin.com/dVPszctW
# https://bugs.gentoo.org/show_bug.cgi?id=461872
mount /mnt/cdrom
tar -xvzf /mnt/cdrom/vmware-tools-distrib.tar.gz /root/
cd /root/vmware-tools-distrib/lib/modules/source

echo compiling vmmemctl...
tar -xvf vmmemctl.tar
cd vmmemctl-only
make
cd ..

echo compiling vmsync
tar -xvf vmsync.tar
cd vmsync-only
echo '
--- driver/sync.c 2013-04-16 21:52:23.277342798 +0200
+++ driver/sync.c 2013-04-16 21:53:43.094056640 +0200
@@ -162,7 +162,7 @@
cancel_delayed_work(&state->thawTask);
list_for_each_safe(cur, tmp, &state->devices) {
dev = list_entry(cur, VmSyncBlockDevice, list);
- if (dev->sb != NULL && dev->sb->s_frozen != SB_UNFROZEN) {
+ if (dev->sb != NULL && dev->sb->s_writers.frozen != SB_UNFROZEN) {
thaw_bdev(dev->bdev, dev->sb);
atomic_dec(&gFreezeCount);
}
@@ -237,7 +237,7 @@
* the superblock is already frozen.
*/
if (inode->i_sb->s_bdev == NULL ||
- inode->i_sb->s_frozen != SB_UNFROZEN) {
+ inode->i_sb->s_writers.frozen != SB_UNFROZEN) {
result = (inode->i_sb->s_bdev == NULL) ? -EINVAL : -EALREADY;
compat_path_release(&nd);
goto exit;
' | patch sync.c
make
cd ..

echo compiling vmci
tar -xvf vmci.tar
cd vmci-only
echo '
--- linux/driver.c 2013-04-17 20:19:38.778514246 +0200
+++ linux/driver.c 2013-04-17 20:43:31.109809776 +0200
@@ -124,7 +124,7 @@
.name = "vmci",
.id_table = vmci_ids,
.probe = vmci_probe_device,
- .remove = __devexit_p(vmci_remove_device),
+ .remove = vmci_remove_device,
};

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
@@ -252,7 +252,21 @@
#define LinuxDriverUnlockIoctlPerFD(mutex) do {} while (0)
#endif

-static struct file_operations vmuser_fops;
+/* Initialize the file_operations structure */
+static struct file_operations vmuser_fops = {
+ .owner = THIS_MODULE,
+ .poll = LinuxDriverPoll,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = LinuxDriver_UnlockedIoctl,
+#else
+ .ioctl = LinuxDriver_Ioctl,
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = LinuxDriver_UnlockedIoctl,
+#endif
+ .open = LinuxDriver_Open,
+ .release = LinuxDriver_Close
+};


/*
@@ -389,26 +403,6 @@
return -ENOMEM;
}

- /*
- * Initialize the file_operations structure. Because this code is always
- * compiled as a module, this is fine to do it here and not in a static
- * initializer.
- */
-
- memset(&vmuser_fops, 0, sizeof vmuser_fops);
- vmuser_fops.owner = THIS_MODULE;
- vmuser_fops.poll = LinuxDriverPoll;
-#ifdef HAVE_UNLOCKED_IOCTL
- vmuser_fops.unlocked_ioctl = LinuxDriver_UnlockedIoctl;
-#else
- vmuser_fops.ioctl = LinuxDriver_Ioctl;
-#endif
-#ifdef HAVE_COMPAT_IOCTL
- vmuser_fops.compat_ioctl = LinuxDriver_UnlockedIoctl;
-#endif
- vmuser_fops.open = LinuxDriver_Open;
- vmuser_fops.release = LinuxDriver_Close;
-
sprintf(linuxState.deviceName, "vmci");
linuxState.major = 10;
linuxState.misc.minor = MISC_DYNAMIC_MINOR;
@@ -1750,7 +1744,7 @@
*-----------------------------------------------------------------------------
*/

-static int __devinit
+static int
vmci_probe_device(struct pci_dev *pdev, // IN: vmci PCI device
const struct pci_device_id *id) // IN: matching device ID
{
@@ -1978,7 +1972,7 @@
*-----------------------------------------------------------------------------
*/

-static void __devexit
+static void
vmci_remove_device(struct pci_dev* pdev)
{
struct vmci_device *dev = pci_get_drvdata(pdev);
' | patch linux/driver.c
make
cd ..

echo compiling vsock
tar -xvf vsock.tar
cd vsock-only
cat ../vmci-only/Module.symvers >> Module.symvers
cp linux/af_vsock.c linux/af_vsock.c.bak
echo '
--- linux/af_vsock.c 2013-04-17 20:25:44.093536289 +0200
+++ linux/af_vsock.c 2013-04-17 20:27:51.518312015 +0200
@@ -3160,11 +3160,15 @@
* else in the future.
*/
for (i = NPROTO - 1; i >= 0; i--) {
- vsockVmciFamilyOps.family = i;
+ pax_open_kernel();
+ *(int *)&vsockVmciFamilyOps.family = i;
+ pax_close_kernel();
err = sock_register(&vsockVmciFamilyOps);
if (err) {
Warning("Could not register address family %d.\n", i);
- vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_open_kernel();
+ *(int *)&vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_close_kernel();
} else {
vsockVmciDgramOps.family = i;
vsockVmciStreamOps.family = i;
@@ -3202,7 +3206,9 @@
sock_unregister(vsockVmciFamilyOps.family);
}

- vsockVmciDgramOps.family = vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_open_kernel();
+ vsockVmciDgramOps.family = *(int*)&vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY;
+ pax_close_kernel();
vsockVmciStreamOps.family = vsockVmciFamilyOps.family;
}' | patch -l linux/af_sock.c
make
cd ..

if [ -f pvscsi.tar ]
tar -xvf pvscsi.tar
cd pvscsi-only
echo '
--- pvscsi.c 2013-05-14 23:24:04.801773269 +0200
+++ pvscsi.c 2013-05-14 23:25:34.701065554 +0200
@@ -455,7 +455,7 @@
SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
}

-static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
+static int pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
{
adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
&adapter->ringStatePA);
@@ -1234,7 +1234,7 @@
* just use a statically allocated scatter list.
*
*/
-static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
+static int pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
{
struct pvscsi_ctx *ctx;
int i;
@@ -1317,7 +1317,7 @@
return numPhys;
}

-static int __devinit pvscsi_probe(struct pci_dev *pdev,
+static int pvscsi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct pvscsi_adapter *adapter;
@@ -1535,7 +1535,7 @@
.name = "pvscsi",
.id_table = pvscsi_pci_tbl,
.probe = pvscsi_probe,
- .remove = __devexit_p(pvscsi_remove),
+ .remove = pvscsi_remove,
COMPAT_PCI_SHUTDOWN(pvscsi_shutdown)
};' | patch pvscsi.c
make
cd ..
fi

echo installing
LIBDIR=/lib/modules/`uname -r`/misc
[ -d "$LIBDIR" ] || mkdir "$LIBDIR"
cp vmmemctl.o "$LIBDIR/vmmemctl.ko"
cp vmsync.o "$LIBDIR/vmsync.ko"
cp vmci.o "$LIBDIR/vmci.ko"
cp vsock.o "$LIBDIR/vsock.ko"
[ -f pvscsi.o ] && cp pvscsi.o "$LIBDIR/pvscsi.o"

// clean up
rm -R vmci-only vmsync-only vsock-only vmmemctl-only
[ -d pvscsi-only ] && rm -R pvscsi-only

// finished !
# refresh modules.dep.bin for modprobe
depmod

echo "You just have to add this line on /etc/conf.d/modules to auto-load the vmware modules"
echo "module_$(uname -r | sed -e 's/[-.]/_/g')=\"vmmemctl vmsync vmci vsock pvscsi\""

Le petit plus du script : il évite d’installer les VMware tools. Sans interface graphique, ils ne servent à rien, et sous Gentoo, ça pollue la sortie de revdep-rebuild .

Ce script ne compile pas les modules vmxnet  ni vmxnet3 , le premier parce qu’il est déprécié et parce que je ne l’utilise pas, le second parce qu’il est déjà intégré au noyau linux.
Dans des versions ultérieures de linux, il se pourrait que je n’ai même plus à gérer la compilation de vmci  et vsock  : en effet ces derniers pourraient faire une entrée dans le noyau linux.

Notes

J’ai finis de rédiger le script hier soir en compilant mes commandes balancées dans le terminal. Je n’ai jamais testé le script, donc à vos risques et périls !
En revanche, je peux prendre en compte vos remarques.

SElinux et cron utilisateurs

Depuis que je suis passé à SELinux sur une Gentoo, beaucoup de problèmes sont apparus. Afin d’avoir un système un minimum utilisable, j’ai passé SELinux en mode permissive, malgré tout, cela ne résout pas tout, notamment des crons utilisateurs qui ne se lancent plus.

Précisons tout d’abord que mon système de cron est vixie-cron, qui semble être le daemon préféré sous Gentoo.

J’ai eu très peu d’informations au début lorsque j’ai voulu trouver pourquoi les crons ne se lançaient plus, que ce soit dans /var/log/kern.log où se trouve les logs de SELinux, ou dans /var/log/syslog. Je savais que ça provenait de SELinux puisque côté ACL, tout était nickel, et puis ça marchait très bien avant. La seule trace concrète du problème apparaît lorsqu’on redémarre le daemon vixie ; on peut lire ceci dans les logs :

root@stormrage:[~]
# zgrep ENTRYPOINT /var/log/syslog-20120318.gz
Mar 17 20:42:01 stormrage cron[2943]: (munin) ENTRYPOINT FAILED (crontabs/munin)
Mar 17 20:42:01 stormrage cron[2943]: (anakin) ENTRYPOINT FAILED (crontabs/anakin)

 

Voyons les permissions SELinux sur les user-crons :

root@stormrage:[~]
# ls -lZ /var/spool/cron/crontabs/
total 12
-rw-------. 1 anakin crontab anakin:object_r:user_cron_spool_t 274 6 nov. 13:54 anakin
-rw-------. 1 munin crontab munin:object_r:user_cron_spool_t 1180 9 janv. 17:31 munin
-rw-------. 1 root crontab root:object_r:user_cron_spool_t 657 17 mars 20:41 root

 

Je suis encore relativement novice à SELinux, mais il est clair que munin et anakin ne sont pas relié explicitement à un utilisateur SELinux, du moins il faut que ce soit une action volontaire. Ainsi, en obtenant la liste des contextes valides pour l’utilisateur Linux munin depuis le contexte dans lequel le daemon s’exécute (voir la commande ci-dessous), on a un seul résultat, qui ne correspond pas au contexte actuel sur les user crontabs.

root@stormrage:[~]
# ps -AfZ | grep cron
system_u:system_r:crond_t root 2521 1 0 Mar17 ? 00:00:00 /usr/sbin/cron
root@stormrage:[~]
# getseuser munin system_u:system_r:crond_t
seuser: user_u, level (null)
Context 0 user_u:user_r:cronjob_t

D’où un chcon -u user_u  sur tous les user crontabs et le tour est joué.

root@stormrage:[~]
# ls -lZ /var/spool/cron/crontabs/
total 12
-rw-------. 1 anakin crontab user_u:object_r:user_cron_spool_t 274 6 nov. 13:54 anakin
-rw-------. 1 munin crontab user_u:object_r:user_cron_spool_t 1180 9 janv. 17:31 munin
-rw-------. 1 root crontab root:object_r:user_cron_spool_t 657 17 mars 20:41 root

Je ne sais pas si user_u est le bon utilisateur SELinux à appliquer, en terme de sécurité, mais au moins les crons se lancent à nouveau.

On notera cependant que restaurer les contextes par défaut avec restorecon ou rlpkg ré-appliquent des contextes invalides. De même, éditer depuis root les crontabs d’autres utilisateur avec un crontab -e  applique un mauvais contexte, il semble préférable de passer par un su/sudo au préalable.
Il faudra donc penser à vérifier spécifiquement les contextes sur ces fichiers à chaque fois que le système de fichier est relabelisé.

Au final, je reste persuadé que SELinux est une bonne technologie mais elle n’est pas prêt à l’emploi (cela vaut également pour Red Hat), même en partant avec une stratégie de base comme celle fournie par Tresys. A moins de faire un serveur très bâteau, il sera certain qu’il faudra auditer et réajuster la stratégie SELinux avant de la valider pour une production.

Voir aussi

Compiz et pas de barre de titre

Dans le cas où les barres de titre des applications ont disparues, désactivez le plus de choses dans Compiz, à part le déplacement et le redimensionnement des fenêtres. Ensuite changez le gestionnaire de thèmes via Compiz-fusion icon (ou via compiz directement si vous connaissez la commande) et activez le plug-in décorations des fenêtres. Ensuite rechargez compiz avec compiz-fusion icon.