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.

Se connecter à TigerVPN avec Linux

J’ai profité d’une offre promotionnelle proposée par TigerVPN pour leur service de VPN à vie pour 30€. C’est toujours pratique de pouvoir changer d’IP en cas de route réseau défectueuse ou lorsque l’IP du FAI est banni par le service qu’on essaye d’atteindre (les restriction géographique par exemple tombent dans ce cas).

Bref, Tiger et la plupart de ses concurrents proposent des VPN clé-en-main pour plateforme Windows et mobile. On a donc souvent du IPSec/L2TP, OpenVPN, ou parfois l’utilisation d’un client maison (mais c’est vraiment pas génial sur mobile, du fait de la mauvaise intégration d’un point de vue de la continuité du service et de la consommation batterie). Sur ces plateformes, ça marche mais on ne trouve aucune informations techniques permettant de s’y connecter proprement depuis un client Linux.

Voici ma configuration, après pas mal d’essais, pour se connecter à TigerVPN en utilisant Strongswan sous Gentoo.

Tout d’abord, on aura besoin des paquets suivants :

emerge va xl2tpd strongswan

La configuration de la partie IPSec. Elle établit un tunnel entre le Linux et TigerVPN, uniquement pour les paquets udp sur le port 1701. C’est le port qu’utilise par défaut le protocole L2TP.

conn tigervpn
        add=route
        type=transport
        right=xxx.tigervpn.com
        esp=aes256-sha1!
        ike=aes256-sha1-modp1024!
        rekey=no
        keyingtries=1
        leftprotoport=udp/1701
        rightprotoport=udp/1701
        leftid=192.168.12.14
        leftsubnet=192.168.12.14/32
        rightid=%any
        leftcert=
        leftca=
        rightca=
        leftauth=psk
        rightauth=psk
        keyexchange=ikev1
        mobike=yes
192.168.12.14 %any : PSK "secret-partagé-tigervpn"

Maintenant, on passe à la configuration de la couche L2TP :

[lac tigervpn]
lns = xxx.tigervpn.com
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd
require authentication = yes

Puis la configuration de la couche PPP :

ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
idle 1800
#crtscts
mtu 1410
mru 1410
nodefaultroute
lock
proxyarp
silent
name your-username
password your-very-secret-password

On vérifiera que les fichiers contenant des mot de passes ne sont accessibles que par root !

Maintenant, on peut démarrer les différents services :

/etc/init.d/ipsec start
/etc/init.d/xl2tpd start
sleep 3
echo "c tigervpn" > /var/run/xl2tpd/l2tp-control

Le sleep s’assure que les deux daemons soient bien initialisées.

La commande echo demande à xl2tpd de se connecter au lac nommé tigervpn. Ce lac a pour LNS xxx.tigervpn.com, lequel doit passer en IPSec.

Si tout a bien fonctionné, une nouvelle interface pppX est visible. Il n’y aura plus qu’à ajouter les différentes routes vers les destinations que l’on souhaite faire passer dans le VPN.

Enjoy !

pfSense sur APU

Ce poste rapide explique comment faire fonctionner pfSense lorsqu’il est installé sur un APU pcEngine avec une SDCard

Pré-requis

Avant de commencer, mettez en place un environnement permettant de monter une partition UFS (un FreeBSD ou un Linux avec le module qui va bien).
Le mode LiveCD de FreeBSD dans une VM fonctionne, mais le clavier qwerty et l’éditeur vi va probablement compliquer la tâche.

Manipulation

  1. Dans l’environnement, branchez-y la carte SD et montez la partition 1 de la manière suivante : mount -t ufs /dev/da1s1a /mnt/p1 où da1s1a est la partition 1 du disque 1 sous FreeBSD et /mnt/p1 un dossier où le montage va se faire.
  2. Editer le fichier /mnt/p1/boot/loader.conf en y ajoutant les lignes suivantes :
    1. comconsole_speed= »115200″
    2. kern.cam.boot_delay=10000
  3. Démontez les partitions, la carte SD peut-être replacée dans l’APU.

La première ligne sert à aligner la vitesse de communication du port série avec celle du BIOS, ce qui évite de reparamétrer le terminal et d’y perdre des lignes.
La seconde ligne ajoute un délai permettant au kernel de charger les modules permettant d’accéder à la carte SD.

Notes

L’image de pfSense sous format nanosd comporte 3 partitions. Les deux premières contiennent chacune une copie du système. La troisième partition est celle contenant la configuration.
La manipulation doit donc être effectuée deux fois, une fois pour chacune des deux premières partitions.

Retour d’expériences

Au bout de quelques heures d’utilisation, je me rend compte que pfSense, c’est cool, mais dès qu’on souhaite faire quelque chose d’avancé (multi-homing, IPSec avec chiffrage ECC et authentification par certificat, configuration poussé du pare-feu, dns menteur,…), ça pu pas mal. L’interface graphique étant très limitative, mon intérêt pour pfSense a disparu.

Le boitier tourne depuis sous Gentoo avec un petit ssd au format m.sata pour le système principal et une copie de sauvegarde plus ancienne sur la carte  SD depuis janvier 2015, et j’en suis très content. Ca fait son travail, et ça le fait très bien.

Sources

Authentification SSH par AD

J’essaie lorsque c’est possible d’éviter la sur-multiplication des mots de passe à gérer. Dans un environnement principalement Microsoft, Active Directory permet de résoudre assez bien ce problème, c’est cool. Un annuaire pour les unir tous (et dans les ténèbres les lier).

Qu’en est-il des distributions Linux ? Je gère un petit nombre de serveurs sous Linux, pour lesquels l’intérêt de les intégrer dans un domaine Active Directory est proche du néant (sans parler du foutoir à mettre en place côté Linux). En effet, il n’y a pas de GPO, l’authentification par mot de passe est désactivé sur ces serveurs, et seul quelques personnes doivent pouvoir y avoir accès et de manière limitée.

Je me suis donc orienté vers une solution où la base utilisateurs des serveurs Linux reste locale (via /etc/passwd et /etc/shadow donc) mais en cherchant à centraliser la clé SSH de ces utilisateurs avec Active Directory.

David Gwynne (voir source) s’est déjà penché sur ce problème et a exhibé un paramètre contenu dans OpenSSH permettant de contrôler l’étape d’authentification via clé publique de manière très flexible, ce paramètre acceptant un script en entrée.

Le principe est alors très simple :

  1. un utilisateur tente de se connecter au serveur linux en SSH
  2. le serveur SSH récupère le nom d’utilisateur et exécute le script indiqué par le paramètre AuthorizedKeysCommand
    1. par mesure de sécurité, OpenSSH propose d’exécuter ce script sous une autre identité, ce qui est fortement préférable vu qu’OpenSSH est root
    2. ce script va se connecter à un serveur LDAP à l’aide d’identifiants dédié
    3. il va récupérer l’objet utilisateur correspondant au nom d’utilisateur, et s’il existe retourner sa ou ses clés publique SSH

L’intérêt consiste à éviter de devoir manipuler le fichier /home/{user}/.ssh/authorized_keys2 sur chaque serveur pour chaque utilisateur, la création d’un accès se faisant alors en deux temps :

  1. création du compte sur le serveur linux avec la commande useradd
  2. si ce n’est pas déjà fait, ajout de sa clé dans Active Directory

Mise en place de la solution

  1. Dans le fichier /etc/ssh/sshd_config, on ajoute/modifie les lignes suivantes:
AuthorizedKeysCommand /etc/ssh/authorized_keys.pl
AuthorizedKeysCommandUser _sshkeys

# si ce n'est pas déjà fait, ci-dessous les paramètres à modifier pour désactiver l'identification par mot de passe
PasswordAuthentication no
ChallengeResponseAuthentication no

2. Contenu du fichier /etc/ssh/authorized_keys.pl. Il faudra renseigner le nom du domaine, le nom d’utilisateur et mot de passe d’un compte pour se connecter à l’annuaire.

#!/usr/bin/perl -w

use strict;
use Net::DNS;
use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_INVALID_CREDENTIALS);
use Net::LDAP::Util qw(escape_filter_value);

sub ad_bind($$$)
{
	my $domain = shift;
	my $user = shift;
	my $pass = shift;

	my $res = Net::DNS::Resolver->new;
	my $query;
	my $answer;
	my $ldap_error;

	$query = $res->query("_ldap._tcp." . $domain, 'SRV');
	if (!$query) {
		die "unable to query SRV records for $domain";
	}

	my @answers = $query->answer;
	foreach $answer (@answers) {
		# next if ($answer->port != 389)

		my $ldap = new Net::LDAP($answer->target, timeout => 2);

		$ldap_error = $@;
		next unless ($ldap);

		my $mesg = $ldap->bind(sprintf("%s@%s", $user, uc($domain)),
			password => $pass);
		return ($ldap) if ($mesg->code == LDAP_SUCCESS);

		if ($mesg->code == LDAP_INVALID_CREDENTIALS) {
			return (undef, $mesg->error);
		}

		$ldap_error = $mesg->error;
	}

	return (undef, $ldap_error);
}

if (scalar @ARGV != 1) {
	die "username not specified";
}

my $username = $ARGV[0];
my $ad_filter = sprintf('(&(sAMAccountName=%s)(objectClass=user))', escape_filter_value($username));

my %d = (
	'kveer.fr' => {
		'user' => 'sshkeys',
		'pass' => 'ton_mot_de_passe',
		'base' => 'DC=kveer,DC=fr',
		'filter' => $ad_filter
	}
);

foreach my $domain (keys %d) {
	my $param = $d{$domain};

	my ($ds, $err) = ad_bind($domain, $param->{'user'}, $param->{'pass'});
	next unless ($ds);

	my $mesg = $ds->search(
		base	=> $param->{'base'},
		filter	=> $param->{'filter'},
		attrs	=> ['altSecurityIdentities']
	);
	next unless ($mesg->code == LDAP_SUCCESS);

	for (my $i = 0; $i < $mesg->count; $i++) {
		my $entry = $mesg->entry($i);

		my @ids = $entry->get_value('altSecurityIdentities');
		foreach my $id (@ids) {
			my ($type, $value) = split(/:/, $id, 2);
			print "$value\n" if ($type eq 'SSHKey');
		}
	}
}

3. puis enfin quelques commandes pour terminer la configuration (à adapter en fonction de la distribution)

#!/bin/bash

USERNAME=_sshkeys
SCRIPT_FILE=/etc/ssh/authorized_keys.pl

useradd -s /sbin/nologin -d /var/empty/ $USERNAME
chmod 750 "$SCRIPT_FILE"
chown root:"$USERNAME" "$SCRIPT_FILE"

Ajouter la clé SSH

Pour chaque utilisateur devant avoir un accès nunux, il suffit de déclarer leur clé publique au sein d’Active Directory, dans l’attribut altSecurityIdentities. L’avantage de cet attribut est qu’il existe déjà, et fonctionne à l’aide d’un préfixe sur les valeurs, de sorte à ce qu’il puisse être utilisé par un acteur différent (ici OpenSSH) sans impacter l’usage initial qui en est fait de cet attribut. Ci-dessous à travers l’outil de gestion graphiques de l’annuaire dsa.msc.

ssh_ad_dsa

Remarques

  • Le script n’est pas parfait et peut être améliorer. Il ne vérifie pas par exemple si le compte en question est désactivé ou non.
  • Le script utilise l’attribut altSecurityIdentities. On aurait très bien pu créer un attribut dédié, mais cette solution a l’avantage d’éviter de modifier manuellement le schéma AD.

Source

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) !

IPv6 sous Linux

Autant sous Windows l’IPv6 se configure tout seul, autant sous Linux, c’est une autre histoire, en particulier si ce dernier fait office de routeur IPv6 et qu’on a configuré proprement le pare-feu (ou alors je n’ai rien capté, c’est aussi possible).

Il y a deux aspects à voir, tout d’abord lorsque l’OS sert de routeur IPv6 (qu’il soit ou pas routeur IPv4 n’importe pas), c’est à dire lorsque net.ipv6.conf.default.forwarding = 1 , ce dernier n’écoute plus les Router Advertisement, même si net.ipv6.conf.default.accept_ra = 1  et par conséquent, l’IPv6, le masque de sous-réseau ainsi que les routes sont à configurer manuellement. Cela peut surprendre de prime abord.

Le second aspect se situe au niveau de la découverte de ses voisins. En IPv4, cela se fait avec des paquets ARP, alors qu’en IPv6, cela se fait avec des paquets ICMPv6.
Pour rappel, ARP est un protocole de lien, ce n’est pas encapsulé dans un paquet IP.
D’un point de vue du pare-feu, c’est pratique car alors que iptables ne peux filtrer des paquets ARP, puisque ces derniers n’ont rien à voir avec la couche IP (c’est le rôle d’ebtables), ip6tables sait parfaitement filtrer des paquets ICMPv6, eux-mêmes étant encapsulés dans un paquet IPv6. Mais il faut y penser lorsqu’on active son pare-feu.
Cela donne par exemple pour Gentoo :

# /etc/conf/net
config_eth0="192.168.3.2/24
2001:41d0:fe1c:1300:20c:29ff:fe99:fbf/56"
routes_eth0="
default via 192.168.3.1
"2000::/3 via fe80::5a98:35ff:fea1:a7ea" # ça ne sert à rien de router autre chose que des adresses internet pour un serveur dédié "normal"

La première chose qu’on fait en général avec le pare-feu, c’est de passer la stratégie par défaut à DROP, en ajoutant le minimum syndical, à savoir un accès SSH notamment si on n’est pas en local sur la machine. Mais faire cette manip coupe également le bon fonctionnement de l’IPv6.

Après analyse avec tcpdump des trames réseau, on en vient à la configuration d’un pare-feu stateful suivante :

# attention: on flush toute la table, et on change la règle par défaut afin de rester connecté
ip6tables -P INPUT ACCEPT
ip6tables -F INPUT
ip6tables -A INPUT -m ctstate --ctstate INVALID -j DROP
ip6tables -A INPUT -m ctstate --ctstate RELATED,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
ip6tables -P INPUT DROP
# vos règles d'accès commencent ici

 

Les deux premières règles permettent un minimum de protection contre des paquets éventuellement forgés.
La troisième permet d’autoriser toute connexion sur la boucle locale.

Les règles 4 et 5 sont celles qui font marcher le schmilblick et permet au système de découvrir ses voisins, en particulier l’adresse MAC de sa passerelle vers internet. Il n’est pas possible de filtrer par adresse source, car les requêtes peuvent être légitimement émises par des adresses locales (fe80::/10 ), multicast (ff00:/8 ) ou unicast routables (2000::/3 ), en conséquences on filtre sur le nombre de sauts, qui doit être de 1 avec le filtre hl (hop limit), sachant que dans le cas d’un serveur dédié, seul les réponses et requêtes de la passerelle sont désirées et légitimes.

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.

Utiliser TLSv1.1 et TLSv1.2 sous nginx

Suite à mes 2 précédents billets sur une bonne configuration SSL de son serveur web :

Alléluia, openssl 1.0.1 est enfin marqué stable pour Gentoo. Avec lui, le support de TLS en version 1.1 et 1.2 arrive, lesquels permettent notamment de corriger la fameuse attaque BEAST.

Pour utiliser ces protocoles sous nginx, il suffit juste de compiler openssl, recompiler nginx, redémarrer nginx et le tour est joué 😉

Et maintenant on arrive à la grosse blague du billet : Chrome, que je considère comme l’un des 3 navigateurs majeurs, avec Internet Explorer et Firefox, ne supporte que TLS en version 1.0, lequel est sensible à l’attaque BEAST. Difficile sous ces conditions de désactiver TLS 1.0 et inférieur.

Ci-dessous le score donné par les tests SSL de Qualys de mon serveur web avec TLS v1.0 désactivé par rapport à avant :

qualys_2013

ERRATA : il semble que Chrome, au moins dans sa version 24.0.1312.57, supporte TLS v1.1.

Utiliser ruby (on rails) avec nginx

Ruby on Rails, ce n’est vraiment pas la panacée du point du déploiement, et je dirais même que c’est une technologie chiante avec tous ses modules et bugs obscurs dû à des dépendances pourries, mais lorsqu’on désire utiliser l’excellent gestionnaire de projet redmine, il faut bien mettre les mains dans le cambouis.

Jusqu’à présent, je faisais tourner redmine en utilisant mongrel, donc avec une passerelle fastcgi pour le faire communiquer avec nginx.
C’est une solution complètement naze, j’en conviens, car de cette manière, une seule requête pouvait être traitée à la fois, mais au moins c’était fonctionnel.
J’ai pu tricher en détournant les requêtes vers les ressources statiques vers nginx de la manière suivante :

server {
	listen 443;
	listen [mon_ipv6]:443 ssl ipv6only=on;
	server_name redmine.mondomaine.com;

	access_log /var/log/nginx/redmine.access_log main;
	error_log /var/log/nginx/redmine.error_log notice;

	root /var/www/redmine/htdocs/public;

	location ~* (^$|/$|^/(projects|attachments)|^[^\.]+$) {
		proxy_pass http://127.0.0.1:1091;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header Host $host;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X_FORWARDED_PROTO 'https';
		proxy_redirect http://redmine.mondomaine.com https://$host;
	}
}

Mais ce genre de rustine atteint très rapidement ses limites dès lors que plusieurs personnes utilisent le site en même temps.

Depuis que redmine est passé en version 1.4.x, j’ai mis à jour les modules demandés selon les prérequis, et là ça marche… si on veut :

  • consommation de mon quad-core de manière très variable mais plus souvent vers le 80% que vers le 0%, même lorsqu’il n’y a personne
  • warning en folie sur des instructions dépréciées

C’est une situation qui n’est pas tenable très longtemps, mon instance teamspeak sur le même serveur pourra confirmer, ça laggait grave.

Comment remédier à ces 3 problèmes que je rappelle :

  • consommation CPU délirante
  • warnings
  • mono-tache (1 requête à la fois)

J’ai tenté la solution phusion passenger, qui consiste à compiler un module passenger à Nginx. Nginx n’étant pas modulaire, compiler un module pour Nginx revient à recompiler Nginx.

Solution 1

J’ai installé la dernière version stable disponible dans portage, la version =www-apache/passenger-3.0.7 , puis utilisé la commande passenger-install-nginx-module . J’ai tenté, j’ai eu une erreur, je n’ai pas cherché très longtemps car cette solution ne me plaît pas : la commande va recompiler nginx, avec les modules par défaut et le module passenger, donc exit les USE que j’avais utilisés sur nginx.

Solution 2

J’ai utilisé passenger –start . A la première exécution, il va compiler une version de nginx avec passenger et l’installer quelque part dans /var/lib.

Cette solution est mieux au niveau architecture car on se retrouve avec deux instances nginx :

  • l’instance d’origine, qui était là avant passenger, celle que j’ai compilé et configuré reste et prend le rôle de frontend
  • passenger se lance dans une seconde instance d’une autre compilation de nginx (celle incluant le module passenger, ici ce nginx n’est qu’une coquille pour passenger). Elle a le rôle de backend et héberge donc l’application ruby. Elle communique avec le frontend par fastcgi.

Ca n’a pas marché, passenger n’étant pas fichu de compiler correctement la dépendance libev.

Solution 3

Mettons donc à jour passenger avec gem en version 3.0.13 (gem install passenger -v 3.0.13 ), non encore stable sur ma distribution au moment où je rédige ce billet. En retestant la solution 2, ça marche 🙂
Mais j’ai toujours une consommation CPU délirante.

Solution 4 (c’est la bonne)

OK, la solution 3 fonctionne, mais avoir 2 nginx, c’est un peu naze. N’y-a-t-il pas moyen de compiler « manuellement » nginx ? Ainsi j’aurais passenger directement intégré à mon nginx principal.
En lisant la doc un peu plus loin, c’est effectivement possible.
Et bah c’est partie, je copie l’ebuild nginx dans mon overlay, et zou on intègre passenger.
Ça compile, très bon signe ; on notera que des fichiers supplémentaires ont été générés durant la compilation pour phusion passenger : l’agent Nginx.

J’ajoute la configuration suivante dans nginx :

http {
	# path obtenu avec passenger-config --root
	passenger_root /usr/local/lib64/ruby/gems/1.8/gems/passenger-3.0.13;
	passenger_max_pool_size 10;
}

server {
	listen 443;
	listen [mon_ipv6]:443 ssl ipv6only=on;
	server_name redmine.mondomaine.com;

	access_log /var/log/nginx/redmine.access_log main;
	error_log /var/log/nginx/redmine.error_log notice;

	root /var/www/redmine/htdocs/public;
	passenger_enabled on;
	passenger_use_global_queue on;
	passenger_user mongrel_redmine;
}

Et là c’est jackpot, tous les problèmes ont été résolus, que ça soit les vieux pics cpu ou les warnings, c’est maintenant de l’histoire ancienne (jusqu’à la prochaine grosse update de redmine…), et j’ai gagné en rapidité dans un environnement multi-utilisateurs vu que j’ai désormais un pool et non plus un seul processus pour gérer les requêtes vers l’application redmine (qui repose sur le framework ruby on rails). Le changement se ressent immédiatement.

Je n’ai pas trop cherché à comprendre pourquoi, mais j’ai obtenu une conf parfaitement fonctionnelle, donc ON NE TOUCHE PLUS.

Pour les intéressés sous Gentoo, j’ai un overlay contenant un ebuild permettant de compiler nginx avec passenger. Il faut juste ajouter ça dans layman pour synchroniser l’overlay : https://raw.github.com/LordVeovis/gentoo/master/repositories.xml.

Je suppose, sans l’avoir testé, que cet ebuild est capable de fonctionner avec une version plus ancienne de passenger, comme celle marquée comme stable dans portage, mais je ne testerais pas, tant que ça ne bug pas.

Voir aussi