IPv6 sur EdgeRouter (Red by SFR / SFR)

Petit achievement aujourd’hui, j’ai réussi à avoir l’IPv6 de mon fournisseur d’accès directement depuis mon routeur EdgeRouter-4. Pour remettre en contexte, ma box SFR est dans son carton. Le seul équipement de SFR que j’utilise est l’ONT, qui converti un signal fibre en cuivre entre-autre.

Jusqu’à présent, j’utilisais un tunnel IPv6-in-IPv4 fourni par Hurricane. C’est gratuit, la bande passante est plutôt grosse, mais n’arrange sûrement pas à réduire la latence par rapport à une connectivité IPv6 directement fournie par l’opérateur (du moins, je pense).

Pourquoi ?

Avant de détailler comment j’ai fait, j’aimerais expliquer pourquoi je n’utilise pas la box de l’opérateur, après tout elle est gratuite et propose plein de fonctionalités, non ? C’est vrai mais… non, pour plein de raisons :

  • en matière de sécurité, je trouve totalement malsain d’avoir une box opérateur, propriété de l’opérateur, contrôlée par l’opérateur au sein de mon réseau local. L’utilisation d’un routeur sous mon contrôle évite une fuite des données vers l’opérateur. Distinction franche entre mon réseau local et le réseau opérateur.
  • si je change d’opérateur, je n’ai pas à reconfigurer mon réseau interne. Seul la patte WAN sera modifiée. J’ai zéro adhérence avec mes opérateurs Internet ou de téléphonie.
  • je n’ai plus de téléphone fixe et la merdasse télévisuelle ne m’encourage pas à utiliser le décodeur fournit. Au pire, il s’agit juste de VLAN à faire passer, donc là aussi la box n’est pas indispensable
  • j’ai vraiment plus de fonctionnalités avec mon routeur que toutes les box opérateurs réunies. Je peux monter un tunnel GRE, IPSec v2, avoir des routes conditionnelles etc… Il s’agit de fonctionnalités que j’utilise.
  • Ca marche (avez-vous déjà utilisé l’interface d’administration de la box Orange) ?
  • Ca marche vraiment. Impossible de faire fonctionner un tunnel IPSec le plus bidon possible en utilisant la box Orange.
  • Ca marche même sous stress, là où la box opérateur plante avec un simple DoS (désolé Nunzz ^^ »)

Il est clair que remplacer sa box n’est pas à la portée de tout le monde, mais rien que pour la fiabilité, ça donne envi.

Mimer la box opérateur

Le plus simple pour trouver comment faire est encore d’analyser la communication entre la box opérateur et le réseau opérateur. Cela tombe bien, j’avais justement un switch manageable ce qui m’a permis de mettre en place un port mirroring et ainsi pouvoir sniffer le traffic depuis mon pc portable en live.

Contrairement à ce que j’avais lu ici ou , j’ai été ravi de lire que l’obtention d’un bloc IPv6 se fait de manière non seulement simple mais suivant les standards (enfin presque) ! En effet, le bloc IPv6 s’obtient via le protocole DHCPv6 en 4 messages :

  • SOLICIT: le client hurle (broadcast) à la recherche d’un serveur DHCP
  • ADVERTISE: un serveur DHCP répond à l’aide
  • REQUEST: le client souhaite obtenir une délégation sur un bloc IPv6
  • REPLY: le serveur, tout aimable qu’il est, répond positivement à la requête
Capture de la requête DHCPv6
Capture de la requête DHCPv6

Bloc IPv6 vs IPv4

De prime abord, le processus pour obtenir une IPv6 semble plus compliquée. Cette apparente complexitée s’explique par le fait qu’il n’y a pas de NAT en IPv6 (enfin si mais on va supposer que non). Cela signifie que tout équipement souhaitant aller sur Internet ne peut pas utiliser l’IP du routeur, comme cela se fait en IPv4 mais doit avoir sa propre IPv6 routable. Routable par opposition à une IPv6 privée (commençant par fe80:) qui pourrait être l’équivalent du 192.168.x.y en IPv4.

Pour répondre à cette problématique d’IP routable, on a deux choix de réseaux :

  • il n’y a plus de réseau local, uniquement le réseau de l’opérateur dont vous et tout vos équipements font parti. Cette solution n’est absolument pas raisonnable tant pour l’usager que pour l’opérateur d’un point de vue de la sécurité.
  • le réseau local de chaque utilisateur et le réseau opérateur, séparés par un routeur

La solution retenue est bien entendue la seconde. Sans IP routable, la communication d’un périphérique est limitée à son réseau local. L’obtention d’une IPv6 se fait donc par un appareil se trouvant lui aussi dans le réseau local, c’est à dire la box opérateur.

Cette box va donc demander à l’opérateur quels sont les IPs qu’il peut distribuer en local, c’est le prefix delegation (PD). L’opérateur fournit à chaque box, qui en fait la demande, une délégation sur un préfixe IPv6 (ou bloc IPv6), ce qui va permettre deux choses :

  • la box va pouvoir distribuer une IP dans ce pool aux objects connectés
  • ce bloc sera routé depuis Internet vers la box (pinger une IP de ce pool va arriver sur le routeur, qui forwardera (ou pas) le paquet)

Patcher Wide-DHCPv6

L’OS EdgeMax est tout à fait en mesure de gérer intégralement la stack IPv6, au moins en CLI vu que la GUI tarde à le gérer proprement. En tant que client DHCPv6, il utilise wide-dhcpv6 pour obtenir une prefix delegation. Là où ça coince, est que le serveur attend deux informations qu’il n’est pas possible de setter dans la configuration de wide-dhcpv6 :

  • le client identifier (option 1) avec un DUID de type 3 (link-layer adress) et non 1 (link-layer adress plus time). Ce pré-requis est obligatoire. Actuellement la valeur est l’adresse mac de la box côté LAN mais pour plus de sûreté, prenez la valeur indiquée dans la capture
  • le vendor class (option 16) qui est l’id du modèle de la box. Il s’agit d’un paramètre optionnelle pour le moment.
Requête DHCPv6 avec les deux options à reproduire
Détail de la requête DHCPv6

Vu que Ubiquiti est respectueux de la licence GPL, j’ai pu récupérer le code source patché de wide-dhcpv6 et commencer à travailler me casser les dents dessus.

Fixer les bugs n’étaient pas une mince affaire. Le EdgeRouter fonctionne sous une architecture MIPS. J’ai donc du monter une vm avec qemu, elle-même dans une VM linux puisque je suis sous Windows, afin de pouvoir compiler la chose. Le débuggage avait comme un arrière goût des années 80 : une compilation qui prend 15 minutes à chaque fois et des outils franchement pas pratique, genre gdb. Et une doc complètement inexistante bien entendu, avec des noms de variables incompréhensible. Au-secours !

Mais j’ai fini par me sortir de ce sable mouvant, voici donc les patches ubnt-wide-dhcpv6-201.tar

Comme c’est ultra chiant à compiler, voici également le binaire à glisser à la place de /usr/sbin/dhcp6c sur le routeur.

Au passage, Ubuntu sous Hyper-v Gen 2, ça marche 🙂

Configurer le Edgerouter

Il va falloir au préalable récupérer quelques informations contenues dans la capture faite précédemment. Elles sont indispensables pour pouvoir configurer proprement le EdgeRouter :

  • le DUID dans le message SOLICIT
  • le vendor class value dans le message SOLICIT
  • le prefix

Il y a 3 sections à configurer :

  • le dhcpv6 pour obtenir un préfixe côté WAN
  • le radvd (router-advert) pour distribuer les préfixes côté LAN
  • l’override de l’adresse mac de l’interface WAN

Ci-dessous ma configuration EdgeRouteur :

interfaces {
    ethernet eth0 {
        address 192.168.x.254/24
        address 2a02:8428:xx:xx00::ff/64
        description lan0
        ...
        ipv6 {
            dup-addr-detect-transmits 1
            router-advert {
                cur-hop-limit 64
                link-mtu 0
                managed-flag true
                max-interval 600
                name-server 2a02:8428:xx:xx::ff
                other-config-flag false
                prefix 2a02:8428:xx:xx::/64 {
                    autonomous-flag true
                    on-link-flag true
                    preferred-lifetime 14400
                    valid-lifetime 28800
                }
                reachable-time 0
                retrans-timer 0
                send-advert true
            }
        }
    }
    ethernet eth2 {
        address dhcp
        description wan0
        ...
        dhcpv6-pd {
            duid 00030001e45d41dexxxx
            no-dns
            pd 1 {
                interface eth0 {
                    prefix-id 1
                }
                prefix-length 56
            }
            prefix-only
            rapid-commit disable
        }
        mac e4:5d:41:de:xx:xx
    }
}

SFR distribue un préfix /56. Un réseau local avec radvd utilise un préfix en /64. Cela signifie que vous pouvez avoir 256 réseaux locaux. Récupérez le préfixe distribué par SFR (depuis la box par exemple) et choisissez un sous-préfixe en /64 pour votre LAN. C’est ce qu’il faudra renseigner dans interfaces -> ethernet ethx -> ipv6 -> router-advert -> prefix.

Fixez également l’IPv6 de l’interface LAN du routeur, afin de pouvoir spécifier un serveur DNS qui écoute sur la stack IPv6, c’est le paramètre interfaces -> ethernet ethx -> ipv6 -> router-advert -> name-server.

Enfin remplacer l’adresse mac de l’interface WAN par celle de l’opérateur. Ce n’est pas obligatoire mais devrait mieux supporter les évolutions du réseau de l’opérateur.

Afin de mieux mimer les requêtes DHCPv6 (et parce que de toute façon il n’est pas possible de générer le bon fichier de conf en cli), on va écraser complètement le fichier généré par la section dhcpv6-pd par notre propre version.

# interface wan
interface eth2 {
        request domain-name-servers, domain-name;
        duid 00:03:00:01:e4:5d:41:de:xx:xx;
        send ia-pd 1;
        # récupéré par le sniff, il s'agit de mettre la valeur brute du vendor class
        send raw-option 16 00:00:a0:0c:00:40:6e:65:75:66:62:6f:78:5f:4e:42;
        script "/opt/vyatta/sbin/ubnt-dhcp6c-script";
};

id-assoc pd 1 {
        prefix ::/0 0 0;
        # interface lan
        prefix-interface eth0 {
                sla-id 1;
                sla-len 8;
        };

        # interface lab
        prefix-interface eth0.213 {
                sla-id 213;
                sla-len 8;
        };
};

Je donne ici un exemple où je récupère un préfix en /56 de SFR et j’assigne deux /64 pour mes deux réseaux locaux :

  • eth0, qui est le réseau principal
  • eth0.213 qui est mon réseau lab

Cela permet de mettre en évidence le rôle des paramètres sla-id et sla-len. sla-len est la différence entre le /56 donné par l’opérateur et le /64 que j’assigne à un réseau, soit 64-56=8.

sla-id est de longeur sla-len, il peut donc avoir une valeur entre 0 et 255 inclus. C’est le nombre qui sera suffixé au préfix. Par exemple mon préfixe donné est 2a02:8428:1234:3300::/56. Alors :

  • avec un sla-id de 1, le subnet de eth0 sera 2a02:8428:1234:3301::/64
  • avec un sla-id de 213, le subnet de eth0.213 sera 2a02:8428:1234:33d5::/64

Il ne reste plus qu’à rendre la configuration et le binaire persistent, à l’aide de /config/user-data et d’un petit script de restauration. Jetez un oeil à mon article de présentation de l’ER-4 pour se faire.

Plus

Ca marche aussi avec Sosh / Orange. Il faudra sniffer le traffic entre la box et l’ONT afin de récupérer les bonnes options et valeurs.

Source

Packager Gitlab

Pour diverses raisons, je vomis Debian, Ubuntu, CentOS et autres distributions populaires. Au contraire, je prends mon pied avec Gentoo, et plus récemment avec Alpine. Je ne m’étendrais pas plus que cela, ce sujet mérite à mon avis un poste dédié. La plupart du temps, je m’y retrouve mais pour GitLab, une aventure a commencé.

Il était une fois…

Pour l’historique, j’ai commencé avec SVN pour gérer mes sources et ceux de ma boîte. A cette époque là, il n’existait pas réellement d’alternative cloud, il fallait gérer soi-même l’hébergement, ce que j’ai fait naturellement.

Puis il y a eu des déplacements, l’impossibilité de fetcher le dépôt SVN sur le téléphone ou lorsque je n’étais pas sur mon poste de travail ou tout simplement le souhait de vouloir consulter une version spécifique du code sans devoir récupérer la copie à partir de mon poste. Pour rappel, contrairement à git ou mercurius, une copie de travail svn ne contient qu’une seule version (la base de travail) et les modifications en cours (les fichiers modifiés par rapport à la base). L’historique est consultable à travers un client svn mais est en réalité stocké sur un serveur svn. Un serveur « git » est un abus de langage au sens où git est un VCS décentralisé : la copie sur un serveur git contient autant d’information que la copie sur un poste de travail, autrement dit : il est possible de consulter tout l’historique d’un dépôt uniquement à partir d’une copie sur un poste de travail en déconnecté. C’est l’un des points forts de git, mais rend de fait les dépôts locaux assez fat. J’avais déjà mis en place redmine, un outil de gestion de projet relativement simple, pour la gestion des accès et il proposait un accès web aux dépôts. Ca a fait le taff durant plusieurs années.

Pendant ce temps, l’écosystème git se développe, github devient incontournable, des alternatives cloud apparaissent avec bitbucket, gitorius, gitlab, les outils permettant d’utiliser git sans prises de têtes deviennent assez matures, il est temps d’abandonner svn pour git, les avantages de git n’étant plus dans l’ombre de sa ligne de commande ô-combien puissante, mais horrible d’utilisation. Eventuellement nos projets passent sous git mais toujours sous gestion redmine.

En parallèle je fais une excursion professionnelle sous TFS 2010, le produit s’intégrant bien avec une gestion centralisé de la sécurité sous Active Directory. J’ai expérimenté TFS et son VCS propriétaire TFVC au travers d’un projet web. TFVC est une véritable horreur côté dev, chaque fichier modifié devant d’abord être marqué modifiable. TFS est en soit pas mal, mais TFVC ne figure pas dans ses points forts, étant en outre un VCS centralisé à l’instar de SVN.

Et un jour (automne 2016), Gitlab est arrivé sur la table. Un clone de github, que j’apprécie pour sa simplicité et la notion de merge-request, à installer chez soi (on-premise). Gitlab propose des packages d’installation pour les distributions usuelles mais rien pour Gentoo, si ce n’est l’installation from source. J’avoue ne pas être très surpris. J’y vais donc à suivre le tutoriel d’installation à partir des sources et, afin de contrôler les fichiers installés et faciliter les mises à jour, créer les ebuilds (packages Gentoo boites blanches).

Gitlab

Gitlab est un produit complexe. Chacun de ses composants a sa propre installation, qui fonctionne mais n’est pas clean d’un point de vue packaging (il n’y a pas de clean up des codes sources go après compilation par exemple, ce qui ne sert à rien au runtime). L’écriture des ebuilds est en soit un petit challenge, que je relève haut la main… jusqu’à ce que gitlab passe à ruby 2.3 à partir de leur mouture 9.x. Gentoo est encore à ce jour coincé en ruby 2.2. Impossible de mettre à jour Gitlab, je le laisse en état pendant plusieurs mois en attendant de trouver une solution qui m’éviterais d’installer un ruby 2.3 instable sous Gentoo.

Plusieurs mois plus tard je monte une plateforme Docker, pour l’instant standalone. Il est peut-être temps de voir s’il l’on peu dockeriser Gitlab. Gitlab propose sa propre image Docker officielle mais elle est basée sur Ubuntu (et pas la dernière) et comme dit au début, c’est à vomir. Rien que par la surcharge pondérale des images docker basées sur Ubuntu/Debian en comparaison de Alpine, surcharge qui ne les rendent même pas plus pratique à l’utilisation. Faite donc un less sur l’image Cassandra, Oops ya pas, on tente view alors, ah bah ça n’existe pas, et vi n’est pas non plus fournit. En 5MB, alpine fournit tout ça de base et plus. Je ne fais pas spécialement de pub pour Alpine mais il est difficile de ne pas s’énerver quand on se retrouve à poil à débugger une image fat et moisie.

Maîtrisant plutôt bien l’installation source de Gitlab ainsi que le packaging Alpine, je me lance dans la création d’une image Docker permettant de faire tourner une version à jour de Gitlab. Cela ma conduit à créer plusieurs packages intermédiaires, ruby2.3, yarn (node js), gitlab-shell, gitlab-gitaly. Après pas mal d’effort, j’arrive à avoir une image fonctionnelle en 10.2.5 ! Youpi !!

git-archive

Mais c’est aussi là que j’arrête l’image custo. Alors que l’image officielle de gitlab arrive à intégrer Gitlab et toutes ses dépendances et même plus (postgresql, redis, mattermost, prometheus), mon image ne contient que Gitlab et se prend 100Mo de plus, même en supprimant les packages non-nécessaires au runtime. Frustrant mais s’explique notamment par le manque d’instructions sur le clean, celles-ci étant décrites dans le cookbook ayant servi à concevoir l’image officielle. Mais je n’ai pas le courage de déchiffrer ce qu’à fait le chef.
J’ai mis beaucoup d’effort dans la conception de cette image Docker mais ce n’est pas assez pour qu’elle soit prod-ready, contrairement aux ebuilds Gentoo. Cette fois, j’abandonne, Ubuntu a gagné. J’ai converti la base de données mysql en postgresql pour pouvoir utiliser l’image officielle Docker et c’est dessus que je vais rester. Cette défaite va me permettre de me focus sur d’autres projets à hacker 🙂

Le travail n’aura pas été totalement vain. J’aurais appris au moins deux choses :

  • l’architecture de Gitlab
  • savoir sacrifier certaines choses pour pouvoir se concentrer sur d’autres

Sur cette fin, je félicite d’avance les acharnés qui m’auront lu jusqu’au bout :p

Description de l’authentification via NTLM en HTTP

Format Lien

Petit article que j’ai déniché expliquant comment fonctionne une authentification avec un site web utilisant le fournisseur NTLM. Note: je ne dis pas si c’est bien ou pas d’utiliser NTLM, juste de savoir comment ça marche.

Cela explique toutefois pourquoi je n’arrive pas à reverse-proxifier un site web qui utilise NTLM pour authentifier le client avec nginx.

NTLM Authentication Scheme for HTTP

git 4 windows avec un certificat SSL non-reconnu

Une histoire de magasin

J’ai de temps en temps besoin d’utiliser git en ligne de commande lorsque le GUI, en l’occurrence Visual Studio, ne suffit pas. Mais dans ce cas, la ligne de commande peut refuser de faire un pull ou un push si le dépôt git commun est accessible via HTTPS et qu’il utilise un certificat interne. C’est à dire que le certificat présenté par le serveur n’est pas auto-signé, mais signé par une autorité de certification non reconnue, comme c’est souvent le cas en entreprise (la vente de certificat n’est rien de plus qu’une grosse arnaque).

Ce problème existe aussi pour les autres outils graphiques qui sont basés sur git for windows, comme l’excellent TortoiseGit.

On est alors confronté au soucis suivant : même si Windows, Visual Studio, Internet Explorer ou Chrome reconnaissent ce certificat, ce n’est pas le cas de git en ligne de commande sous Windows. La raison est que le magasin des certificats racines n’est pas le même. Ce problème avec git n’existe pas sous un système *NIX. Cela arrive typiquement dans les cas suivants :

  • le navigateur est Firefox
  • l’application est écrite en Java
  • l’application embarque mingw ou similaire

Chacune de ces applications ou runtime utilisent leur propre magasin de certificat, ce qui explique qu’une négociation SSL qui passe sous VStudio ne passe plus avec git.

Faire marcher git en SSL avec un certificat interne

J’indique dans ce billet comment ajouter un certificat racine pour git 4 windows. La commande est extrêmement simple, mais la difficulté a résidé à savoir quel est le fichier à modifier. En effet, si l’on regarde le packaging de git, on peut voir des certificats trainer un peu partout, ce n’est pas très propre:

  • ~/usr/ssl/certs/ca-bundle.crt
  • ~/usr/ssl/certs/ca-bundle.trust.crt
  • ~/usr/ssl/cert.pem
  • ~/etc/pki/ca-trust/extracted/java/cacerts
  • ~/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
  • ~/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  • ~/mingw64/etc/pki/ca-trust/extracted/java/cacerts
  • ~/mingw64/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
  • ~/mingw64/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  • ~/mingw64/share/pki/ca-trust-source/ca-bundle.trust.crt
  • ~/mingw64/ssl/cert.pem
  • ~/mingw64/ssl/certs/ca-bundle.crt
  • ~/mingw64/ssl/certs/ca-bundle.trust.crt

Alors lequel est-ce ? Et bien c’est l’avant dernier.

Une fois qu’on l’a identifié, la manipulation est enfantine : il suffit d’éditer ce fichier et de lui coller tous les certificats racines supplémentaires au format PEM, comme le fait cette commande avec un shell type bash :

cat ca.crt >> /c/Program\ Files/Git/mingw64/ssl/certs/ca-bundle.crt

Cette manipulation sera à refaire après chaque mise à jour de git for windows.

Il existe un paramètre permettant d’indiquer à l’outil en ligne de commande de ne pas vérifier le certificat du serveur. Cela n’est pas du tout équivalent à ma solution en ce sens que ma solution permet bien d’authentifier le serveur, et que cette solution, si elle fonctionne pour git, ne fonctionne pas forcément pour les outils basés sur git comme TortoiseGit.

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.

Extraire un dossier d’un dépôt git avec suivi du renommage

git contient une commande permettant d’extraire un dossier d’un dépôt existant. C’est pratique lorsque le dépôt contenait initialement un seul projet, qui a évolué au cours du temps, grossit, et est devenu un truc trop gros pour rester dans un dépôt unique. Cela se fait avec la commande git –subdirectory , mais cette commande ne suivait pas le renommage du dossier à extraire et l’on se retrouvait donc avec un historique tronqué.

Le script ci-dessous fonctionne (c’est déjà ça), en travaillant sur le dépôt lui-même plutôt que l’espace de travail, lequel ne reflète qu’une version spécifique. Il :

  • ne garde du dépôt que tout ce qui se trouve ou bien dans Kveer.MyAwesomeProject, ou bien dans MyAwesomeProject, qui sont un même dossier que j’ai renommé à un moment donné
  • il fait remonter à la racine le contenu des dossiers susmentionnés
  • quitte à casser tout l’historique, j’en profite pour unifier le nom et l’email utilisé dans les commits (on pourra ajouter une condition pour ne réécrire que ses propres commits)
  • je supprime le dossier package pour réduire la taille du dépôt
  • plus qu’à nettoyer
  • le nouveau dépôt est prêt à être chargé.
# extracts the subdirectory
# i take this opportunity to rewrite author name and email of my commits
git filter-branch --prune-empty --env-filter '
GIT_AUTHOR_EMAIL=my-awesome-email@kveer.fr
GIT_AUTHOR_NAME=Veovis' --index-filter '
    # Delete files which are NOT needed
    git ls-files -z | egrep -zv  "^(Kveer.MyAwesomeProject|MyAwesomeProject)" |
        xargs -0 -r git rm --cached -q
    # Move files to root directory
    git ls-files -s | sed -e "s-\t\(Kveer.MyAwesomeProject\|MyAwesomeProject\)/-\t-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        ( test ! -f "$GIT_INDEX_FILE.new" \
            || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" )
'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# more cleaning by removing a directory that should not be tracked
git filter-branch --index-filter \
  'git rm --ignore-unmatch --cached packages'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# cleans the repo
git gc
git repack -Ad


La commande subdirectory ne doit pas servir à modifier un dépôt existant. En effet il réécrit tous les commits, tous les hash sont différents. Le nouveau dépô n’aura donc plus aucun lien avec l’ancien dépôt et il sera impossible de faire un push autrement que sur un dépôt vierge ou bien en forçant le passage.

Sources

Bloquer l’accès à un site web pour tout le monde sauf…

Lors du développement d’un site web, il est pratique à partir d’un certain moment de le publier et pouvoir le rendre accessible à une minorité de personnes (du genre le client pour commencer à avoir du feedback).

Je décris dans ce billet différentes méthodes permettant de répondre à ce besoin.

Filtrage par IP

Une solution naïve serait de bloquer l’accès au site sauf à une poignée d’IPs, qui serait les IPs des personnes autorisées à consulter le site en construction. Cela cependant ne fonctionne pas si l’IP n’est pas connu à l’avance, comme par exemple lorsque le FAI fournit des IPs d’un pool dynamiques ou tout simplement lors d’une présentation (Orange et Bouygues notamment).

Filtrage par Cookie

Une seconde solution, et c’est celle que je veux présenter ici, se base sur un autre mécanisme. Je trouve cette solution particulièrement élégante car simple tout en répondant parfaitement au problème.
Dans cette solution, la clé n’est plus l’IP comme dans la première solution mais le cookie. La présence d’un cookie détermine l’accès au site.

L’implémentation la plus simple se fait en deux phases:

  • la première phase est la création d’une page-à-la-con dont le seul objectif sera d’insérer le cookie au navigateur puis d’effectuer une redirection par JavaScript vers le point d’entrée du site.
  • la seconde phase est l’implémentation de la restriction, en général au sein du serveur web car alors c’est de la configuration pur et il n’est pas nécessaire de bidouiller le code source du site.

La restriction doit répondre aux règles suivantes :

  1. L’url permettant de poser le cookie doit être en accès libre. Il faut la garder secrète.
  2. L’accès au site doit être autorisée si la requête provient d’une IP autorisée ou si le cookie d’accès est présent
  3. Poser un fichier robots.txt pour éviter que l’url du premier point soit crawlé par les principaux moteurs de recherche.

Ci-dessous un exemple d’implémentation de la restriction façon Apache

SetEnvIf Request_URI "youhou_cookie\.php$" let_me_in
SetEnvIfNoCase Cookie "euphor=tissu" let_me_in

order deny,allow
allow from 82.247.154.152
allow from 193.248.37.205
allow from 2001:470:1f13:129f::/64
allow from env=let_me_in
deny from all

Et voilà une version pour IIS

<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <clear />
                <rule name="cookie-redirect" enabled="true" patternSyntax="ExactMatch" stopProcessing="true">
                    <match url="entry-url" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Redirect" url="/" appendQueryString="false" redirectType="Found" />
                </rule>
                <rule name="cookie-checker" patternSyntax="ECMAScript" stopProcessing="true">
                    <match url=".*" negate="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{REQUEST_URI}" pattern="^entry-url$" negate="true" />
                        <add input="{HTTP_COOKIE}" pattern="pass=euphor" negate="true" />
                    </conditions>
                    <action type="AbortRequest" />
                </rule>
            </rules>
            <outboundRules>
                <rule name="set-cookie" preCondition="url-checker" patternSyntax="ECMAScript" stopProcessing="false">
                    <match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
                    <action type="Rewrite" value="pass=euphor; Max-Age=86400" replace="true" />
                </rule>
                <preConditions>
                    <preCondition name="url-checker">
                        <add input="{REQUEST_URI}" pattern="entry-url" />
                    </preCondition>
                </preConditions>
            </outboundRules>
        </rewrite>
        <httpErrors errorMode="Detailed" />
    </system.webServer>
</configuration>

 

Il sera préférable d’effectuer la redirection en javascript plutôt qu’avec un code HTTP 302, on évite quelques cas à priori bizarre (je ne me suis pas penché dessus) où le cookie n’est pas pris en compte par le navigateur.

FAQ

  1. Pas besoin de s’embêter avec tout ça, il suffit simplement de ne pas laisser trainer l’url du site non ?
    Il suffit de surfer sur le site avec Google Chrome par exemple et le site se fait référencer dans la semaine.
  2. En plaçant un robots.txt, on évite que le site se fasse référencer par les moteurs de recherche, c’est bon là ?
    Oui Google et Bing! respectent le fichier robots.txt, mais ce n’est pas un simple fichier qui va bloquer, techniquement parlant, le référencement par tout moteur de recherche. Le crawler de la bibliothèque nationale de France par exemple ignore complètement ce fichier
  3. Obligé de suivre la solution 2 alors ?
    Sous l’angle de la sécurité, garder secret un nom de domaine ou poser un fichier robots.txt c’est de l’esbrouffe. Tant qu’il n’existe pas de barrière solide, on peut doit considérer que le site est en accès libre. La solution 1 peut suffire mais la solution 2 apporte de la souplesse.

Purger massivement des MKV

J’ai écrit un petit script en Powershell afin de pouvoir traiter en masse des fichiers MKV en y enlevant toutes les pistes qui ne m’intéressaient pas.
Les MKV ont en effet la capacité d’embarquer plusieurs flux vidéos, audios, sous-titres et même des fichiers joints (des polices embarquées par exemple pour une utilisation avec les sous-titres), ce qui est très pratique.
Dans mon cas, il s’agissait de vidéos avec 3 flux audios, 3 sous-titres dont les 2/3 en russe (que je ne parle pas), le dernier tiers en anglais.

Afin d’alléger les fichiers et ainsi pouvoir les visionner sur mon téléphone, j’ai cherché le moyen de pouvoir effectuer cette purge, mais rapidement. Pour cela, les MKVToolnix, la boite à outil officielle du conteneur MKV vont être très utiles, ils contiennent :

  • un outil permettant d’analyser un MKV et donc de voir ce qu’il contient
  • un outil d’extraction
  • et enfin un outil de « création »

Le script en question est donc surtout un « parser » pour mkvinfo, l’outil d’analyse, permettant ensuite de lancer tout seul la suite des opérations.

Voici la sorti type d’un mkvinfo

+ En-tête EBML
|+ Type de document : matroska
|+ Version du type de document : 2
|+ Version lue du type de document : 2
+ Segment, taille 2409578102
|+ En-tête de recherche (sous-entrées ignorées)
|+ EbmlVoid (taille : 4012)
|+ Informations sur le segment
| + Coefficient temporel : 1000000
| + Application de multiplexage : libebml v0.7.9 + libmatroska v0.8.1
| + Application d'écriture : mkvmerge v3.2.0 ('Beginnings') built on Feb 12 2010 16:46:17
| + Durée : 3479.872s (00:57:59.872)
| + Date : Mon Mar 01 18:00:07 2010 UTC
| + Titre : Alpha News 1x01
| + UID de segment : 0x9b 0xe4 0xc8 0x25 0x73 0x3d 0xb7 0xed 0x81 0xc7 0x25 0x6e 0xb9 0x36 0x9e 0x8e
|+ Pistes du segment
| + Une piste
| + Numéro de piste : 1 (identifiant de piste pour mkvmerge & mkvextract : 0)
| + UID de piste : 323836576
| + Type de piste : video
| + Activé : 1
| + Marque par défaut : 1
| + Marque de forçage : 0
| + Marque de laçage : 0
| + Cache minimum : 1
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : V_MPEG4/ISO/AVC
| + Codec de décodage intégral : 1
| + Données de codec privées, longueur 95 (profil h.264 : High @L3.1)
| + Durée par défaut : 41.708ms (23.976 images/champs par seconde pour une piste vidéo)
| + Langue : eng
| + Nom : "Pilot"
| + Piste vidéo
| + Largeur (pixels) : 1280
| + Hauteur (pixels) : 720
| + Entrelacé : 0
| + Largeur à l'écran : 1280
| + Hauteur à l'écran : 720
| + Une piste
| + Numéro de piste : 2 (identifiant de piste pour mkvmerge & mkvextract : 1)
| + UID de piste : 3094425017
| + Type de piste : audio
| + Activé : 1
| + Marque par défaut : 1
| + Marque de forçage : 0
| + Marque de laçage : 1
| + Cache minimum : 0
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : A_AC3
| + Codec de décodage intégral : 1
| + Durée par défaut : 32.000ms (31.250 images/champs par seconde pour une piste vidéo)
| + Langue : rus
| + Nom : Dolby Digital 5.1 448 kbps @ Lostfilm
| + Audio track
| + Fréquence d'échantillonnage : 48000
| + Canaux : 6
| + Une piste
| + Numéro de piste : 3 (identifiant de piste pour mkvmerge & mkvextract : 2)
| + UID de piste : 31873723
| + Type de piste : audio
| + Activé : 1
| + Marque par défaut : 0
| + Marque de forçage : 0
| + Marque de laçage : 1
| + Cache minimum : 0
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : A_AC3
| + Codec de décodage intégral : 1
| + Durée par défaut : 32.000ms (31.250 images/champs par seconde pour une piste vidéo)
| + Langue : rus
| + Nom : Dolby Digital 5.1 448 kbps @ foxlight
| + Audio track
| + Fréquence d'échantillonnage : 48000
| + Canaux : 6
| + Une piste
| + Numéro de piste : 4 (identifiant de piste pour mkvmerge & mkvextract : 3)
| + UID de piste : 3654252222
| + Type de piste : audio
| + Activé : 1
| + Marque par défaut : 0
| + Marque de forçage : 0
| + Marque de laçage : 1
| + Cache minimum : 0
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : A_AC3
| + Codec de décodage intégral : 1
| + Durée par défaut : 32.000ms (31.250 images/champs par seconde pour une piste vidéo)
| + Langue : eng
| + Nom : Dolby Digital 5.1 448 kbps
| + Audio track
| + Fréquence d'échantillonnage : 48000
| + Canaux : 6
| + Une piste
| + Numéro de piste : 5 (identifiant de piste pour mkvmerge & mkvextract : 4)
| + UID de piste : 152612246
| + Type de piste : subtitles
| + Activé : 1
| + Marque par défaut : 0
| + Marque de forçage : 0
| + Marque de laçage : 0
| + Cache minimum : 0
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : S_TEXT/ASS
| + Codec de décodage intégral : 1
| + Données de codec privées, longueur 1555
| + Langue : rus
| + Nom : Russian @ ASS @ Faker020285
| + Une piste
| + Numéro de piste : 6 (identifiant de piste pour mkvmerge & mkvextract : 5)
| + UID de piste : 2319477894
| + Type de piste : subtitles
| + Activé : 1
| + Marque par défaut : 0
| + Marque de forçage : 0
| + Marque de laçage : 0
| + Cache minimum : 0
| + Coefficient temporel : 1
| + Identifiant d'ajout de bloc maximum : 0
| + Identifiant du codec : S_TEXT/ASS
| + Codec de décodage intégral : 1
| + Données de codec privées, longueur 1166
| + Langue : eng
| + Nom : English @ ASS
|+ EbmlVoid (taille : 1024)
|+ Pièces jointes
| + Joints
| + Nom du fichier : corbel_0.ttf
| + Type MIME : application/x-truetype-font
| + Données du fichier, taille : 260648
| + UIF du fichier : 4097748661
| + Description du fichier : Imported font from Breaking Bad 1x01 Pilot HD720p.ass
| + Joints
| + Nom du fichier : GARA_0.ttf
| + Type MIME : application/x-truetype-font
| + Données du fichier, taille : 198069
| + UIF du fichier : 1642350759
| + Description du fichier : Imported font from Breaking Bad 1x01 Pilot HD720p.ass
| + Joints
| + Nom du fichier : Candara_0.ttf
| + Type MIME : application/x-truetype-font
| + Données du fichier, taille : 218504
| + UIF du fichier : 3442251435
| + Description du fichier : Imported font from Breaking Bad 1x01 Pilot HD720p.ass
| + Joints
| + Nom du fichier : HermesCTT Regular_0.ttf
| + Type MIME : application/x-truetype-font
| + Données du fichier, taille : 54693
| + UIF du fichier : 2891661193
| + Description du fichier : Imported font from Breaking Bad 1x01 Pilot HD720p.ass
| + Joints
| + Nom du fichier : GOTHIC_0.ttf
| + Type MIME : application/x-truetype-font
| + Données du fichier, taille : 137565
| + UIF du fichier : 61850035
| + Description du fichier : Imported font from Breaking Bad 1x01 Pilot HD720p.ass
|+ Cluster

Cela va être très pratique à parser, les informations d’un même élément étant étalés sur plusieurs lignes.
L’idée va être de rassembler toutes les informations intéressantes dans un tableau avec :
* en première colonne, le numéro de piste pour mkvextract
* en seconde colonne, le type de la piste
* en troisième colonne, la langue de la piste

A coup de regex, cela donne quelque chose dans le style :

Piste Type Lang File 
----- ---- ---- ---- 
0 
video 
eng 
1 
audio 
rus 
2 
audio 
rus 
3 
audio 
eng 
4 
subtitles 
rus 
5 
subtitles 
eng 
corbel_0.ttf 
GARA_0.ttf 
Candara_0.ttf 
HermesCTT Regula...
GOTHIC_0.ttf

On retrouve bien le fait que l’information est éparpillée : pour chaque piste, l’information doit être lu sur 3 colonnes, mais à partir de ces informations, cela est suffisant pour lancer la commande de purge, on a donc tous les éléments en main pour écrire le script, ci-dessous.

function TransformMKV ($file) {
	$mkvinfo = 'C:\Program Files\MKVToolNix\mkvinfo.exe'
	$mkvextract = 'C:\Program Files\MKVToolNix\mkvextract.exe'
	$mkvmerge = 'C:\Program Files\MKVToolNix\mkvmerge.exe'
	$workdir = 'D:\temps'

	$rawOutput = & $mkvinfo $file
	$output = $rawOutput | Select-String -Pattern 'mkvextract : (?<piste>[0-9]+)', `
		'Type de piste : (?<type>.+)', `
		'Langue : (?<lang>.+)', `
		'Nom du fichier : (?<file>.+)'

	$data = $output | Select-Object @{"Label"="Piste";"Expression"={$_.Matches[0].Groups['piste'].Value}}, `
		@{"Label"="Type";"Expression"={$_.Matches[0].Groups['type'].Value}}, `
		@{"Label"="Lang";"Expression"={$_.Matches[0].Groups['lang'].Value}}, `
		@{"Label"="File";"Expression"={$_.Matches[0].Groups['file'].Value}}

	$i = 0
	$audioTracks = @()
	$subTracks = @()

	if ((Test-Path $workdir -PathType Container) -ne $true) {
		New-Item $workdir -ItemType folder
	}

	while ($i -lt $data.Count) {
		$line = $data[$i]
		$piste = $line.piste

		if ($piste -ne $null -and $piste -ne '') {
			$type = $data[++$i].type
			$lang = $data[++$i].lang

			if ($lang -eq 'rus') { continue }
			if ($type -eq 'subtitles') { $subTracks += $piste }
			elseif ($type -eq 'audio') { $audioTracks += $piste }
		}

		$i++
	}

	& $mkvmerge -q -o ("{0}\{1}" -f $workdir, [System.IO.Path]::GetFileName($file)) --default-language eng -a ($audioTracks -join ',') -s ($subTracks -join ',') "$file"
}

Get-Item -Path 'W:\source\*.mkv' | ForEach-Object { TransformMKV($_) }

 

C’est écrit à l’arrache, mais pour ce qui me concerne, il a été suffisant et reste simple à modifier.
On commence par indiquer en dur où se trouve les différents binaires, ainsi qu’un dossier de travail qui sera crée automatiquement s’il n’existe pas.
On balance un mkvinfo sur le fichier source qu’on transforme en object avec la cmdlet Select-Object . Il ne faut surtout pas utiliser Format-Table, lequel ne sert qu’à faire de l’affichage, et ne peut donc être pipé que dans la Console.

Durant la boucle, on rassemble les morceaux et on liste l’ensemble des pistes à garder (j’ai posé en hypothèse que je n’ai qu’une seule piste vidéo, et que donc je garde toutes les pistes vidéos lors de la purge).

Enfin on exécute mkvmerge, lequel a la capacité de créer des mkv à partir de flux nu, mais également à partir d’autres container, comme un mkv. La première solution étant plus difficile (car alors il faut également gérer l’existence et l’extraction des tags, chapitres, fichiers embarqués), je me suis tourné var la seconde solution, où il suffit d’indiquer ce que l’on souhaite garder et le reste est conservé.
La ligne de commande indique donc de garder tous les flux vidéos, tous les tags, chapitres et fichier embarqués, et de ne conserver que les flux audios et sous-titres spécifiés.

Enjoy !

Redémarrer proprement un hôte Windows hébergeant des vms

Depuis la version 8 de VMware Workstation, Vmware a semble-t-il combiné VMware Workstation avec VMware Server en terme de fonctionnalités. On remarquera d’ailleurs que VMware Server n’est plus mis à jour depuis 2009 et plus supporté depuis 2011 par l’éditeur.
Il est donc possible désormais de démarrer des machines virtuelles en même tant que l’hôte. Il n’est plus nécessaire d’ouvrir VMware Workstation à chaque fois, il n’est plus nécessaire d’ouvrir une session et de la laisser ouverte tant qu’on en a besoin. C’est très pratique cependant s’il est possible d’avoir un démarrage automatique, il n’y a pas d’arrêt automatique autre qu’un arrêt brutal des machines virtuelles lorsque l’hôte s’arrête. Du moins il n’y a pas d’interface graphique ni de documentation officielle permettant de paramétrer ce point.

Voici donc la documentation non-officielle pour paramétrer l’auto-stop. Je n’ai pas poussé afin de savoir s’il était possible de configurer l’auto-stop par vm ni qu’elles sont toutes les possibilités, mais je pense que la méthode décrite ci-dessous suffira.

  • Éditez le fichier %ProgramData%\VMware\hostd\vmAutoStart.xml
  • Remplacez PowerOff  par Suspend  dans le nœud stopAction . Cela aura pour effet de suspendre les vms à l’arrêt de l’hôte. Shutdown  marche peut-être également mais je ne l’ai pas testé.
  • Redémarrez le service gérant les machines virtuelles, éventuellement en prenant soin d’éteindre proprement les machines virtuelles.
  • Le tour est joué.

Au cas où il y aurait des utilisateurs de VMware Workstation sous Linux (y en a-t-il ?), je suppose que la méthode est similaire et que le fichier correspondant se trouve quelque part dans /var/lib .

Mise en garde : C’est peu probable mais VMware pourrait très bien retirer cette fonctionnalités dans une prochaine version ou mise à jour de VMware Workstation.

Installer PHP sous Windows mais sans Apache

Vu l’incapacité de l’équipe PHP à fournir des instructions d’installation correctes (genre à jour) sous Windows (ou le manque de volonté, cela revient au même), et parce qu’Apache sous Windows est une hérésie (alors que sous Linux, c’est simplement de la débilité), je m’atèle à résoudre ce problème.

Installation automatique

Je ne m’étendrai pas beaucoup là-dessus, il suffit de suivre les instructions de cette page. La dernière fois que j’ai tenté cette installation, j’ai eu quelques soucis mineurs, on va dire que pour une install rapide, ça ne marche pas trop mal.

Installation manuelle pour IIS6

A quelques détails près, la procédure à suivre est similaire à celle pour IIS7. Par contre continuer à utiliser II6 et surtout pour autre chose que du .NET, ça craint d’un point de vue sécurité.

Installation manuelle pour IIS7 et +

La procédure est identique en tout point à celle pour IIS Express (plus bas), sauf qu’il faut être admin de la machine et que le bon fichier de configuration se trouve dans C:\Windows\System32\inetsrv\config .
L’avantage néanmoins d’avoir un IIS pas express, c’est qu’il y a une interface graphique, c’est probablement plus simple et safe que de sortir directement le burin.

Je détaillerai prochainement la partie graphique (avec des screen) et complètement cette section.

Installation manuelle pour IIS Express

  • Installez IIS Express si ce n’est pas déjà fait en le téléchargeant ici. C’est la seule étape qui nécessite d’être administrateur de la machine, tout le reste peut être réalisé par un utilisateur lambda.
  • Téléchargez la (dernière de préférence) version de PHP ici en prenant soin de préférer la version non thread-safe (NTS) : http://windows.php.net/download/#php-5.4
  • Décompressez php où vous le souhaitez, par exemple dans %ProgramFiles(x86)%\PHP 5.4.17  si vous êtes administrateur ou bien dans %USERPROFILE\AppData\Local\PHP 5.4.17 s’il s’agit d’une installation locale à un utilisateur particulier. Pour la suite de la procédure, on considèrera %INSTALL% %INSTALL%  comme le répertoire d’installation de PHP.
  • Éditez le fichier %USERPROFILE%\Documents\IISExpress\config\applicationhost.config de l’utilisateur souhaitant bénéficier de PHP sur sa plateforme IIS Express
  • Cherchez le nœud fastCgi  dans le nœud system.webServer  et ajoutez-y le nœud suivant : <application fullPath= »%INSTALL%\php-cgi.exe » />
  • Ajoutez ensuite le handler suivant (nœud à ajouter dans le nœud handlers) <add name= »php5″ path= »*.php » verb= »* » modules= »FastCgiModule » scriptProcessor= »%INSTALL%\php-cgi.exe » resourceType= »File » /> . Attention, ce handler doit être ajouté avant les handlers génériques, en particuliers les handlers suivants : StaticFileModule  et les ExtentionlessUrl* .
  • Éditez le fichier %INSTALL%\php.ini  en prenant comme base le fichier php.ini-production  (par exemple) qui se trouve dans le même répertoire
    • Fixez le path comme suit : include_path = « .;%INSTALL%\ext »
    • Fixez cgi.force_redirect = 0
    • Dans la section [Date] , fixez le timezone comme suit : date.timezone = Europe/Paris
    • Spécifiez la variable ext à ext
    • [Optionnel] Mettez la variable short_tags  à On
  • C’est normalement terminé, il n’y a plus qu’à tester le bon fonctionnement de PHP avec le classique <?php phpinfo();

Note

L’équipe PHP ne propose que des builds 32 bits. Il y a des builds 64 bits, mais elles ne sont actuellement pas considérées comme stable.