Planet Asrall

January 15, 2011

Gatien Gaspard

Accès à un lecteur réseau depuis Apache

Nos données étant de plus en plus souvent stockées sur des supports distants, il peut être nécessaire de donner accès à un lecteur réseau à notre serveur Apache.

Cette opération se révèle extrêmement facile sur un système Gnu/Linux puisqu’il suffit de créer un point de montage sur le système et d’autoriser son accès via la configuration d’Apache:

Monter le lecteur réseau automatiquement via fstab (y ajouter la ligne suivante):

//192.168.0.10/apache /mnt/netdrive smbfs rw,user,uid=1000,gid=1000,credentials=/etc/samba/pub.cred 0 0

user = n’importe quel utilisateur peut monter et démonter le partage
uid,gid = à qui appartient le partage (par défaut root.root avec les droits 755 donc un utilisateur ne peut écrire par défaut dans le partage)
credentials = on met pas l’utilisateur et le mot de passe en clair dans fstab mais dans un fichier /etc/samba/pub.cred (protégé en lecture lui)

le fichier pub.cred contient:
username=Domain\Apache
password=motdepasseassocie

source: Monter un partage Samba avec fstab de Jérome Hanoteau

Configurer les droits d’accès depuis Apache (/etc/apache2/sites-available/default ou autre), il faut ajouter les lignes suivantes (à adapter) entre les balises VirtualHost souhaitées:

1
2
3
4
5
6
7
Alias /netdrive/ "/mnt/netdrive/"
<Directory "/mnt/netdrive/">
    Options -Indexes MultiViews FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

En revanche sous Windows, impossible d’accéder à un lecteur réseau depuis Apache avec une configuration par défaut. Et pour cause, par défaut le serveur Apache est lancé en tant que service par l’utilisateur « System », or cet utilisateur ne peut accéder aux partages réseaux.

La solution apparait donc simplement, il suffit de modifier le compte avec lequel le serveur Apache est lancé. Il faut remplacer l’utilisateur « System » par un utilisateur du domaine ayant les droits nécessaires sur les répertoires concernés. Pour la suite nous utiliserons donc l’utilisateur « domain\Apache ».

Les étapes:

  • Win+R
  • Services.msc
  • Clic droit sur « Apache »
  • Onglet « Log on »
  • Modifier le compte comme sur l’image ci-contre
  • Valider
  • Ajouter les droits sur les répertoires nécessaires au fonctionnement d’Apache pour cet utilisateur:
    • C:\Program Files\Apache Software Foundation\Apache2.2
    • C:\Windows\temp

Pour ajouter les droits sur un répertoire, il faut effectuer un clic droit sur celui-ci puis « Propriétés » puis dans l’onglet « Security », ajouter l’utilisateur souhaité et lui donner les droits souhaités (lecture, écriture pour les répertoires locaux nécessaires à Apache, lecture et/ou écriture pour les lecteurs réseaux en fonction des besoins).

Il faut également effectuer cette opération sur les répertoires réseaux que l’on souhaite lui rendre accessible.

/!\ !!! Nous touchons ici à la gestion des droits d’accès, veillez à ne pas fournir trop de droits à cet utilisateur afin de limiter l’impact en cas d’erreur dans la configuration (utiliser un utilisateur de base de votre domaine et ne lui donner accès qu’aux répertoires nécessaires). !!! /!\

N’oublions pas de relancer le service Apache une fois ces modifications effectuées afin de valider le fonctionnement du serveur avec ce nouvel utilisateur.

Nous passons ensuite à la configuration d’Apache à proprement parler, comment rendre accessible ce fameux lecteur réseaux ?

Contrairement à ce que l’on pourrait penser, il est impossible de dire à Apache d’utiliser un lecteur réseau identifié par une lettre. De même, la gestion des droits à ce répertoire doit se faire au niveau Windows et non dans la configuration d’Apache.

Ainsi au niveau de la configuration d’Apache lui-même, rien de spécial. Il est possible d’ajouter un alias pour faciliter les choses mais ce n’est pas obligatoire.

Alias /netdrive/ « //192.169.0.10/apache/ »

Il suffit ensuite d’utiliser soit le chemin réel, soit l’alias dans notre code PHP ou autre afin d’accéder à ces données.

Exemple:

Download test.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// Affiche le contenu d'un fichier sur le réseau et déplace celui-ci
$rep = "//192.169.0.10/apache/";
$annee = date("Y");
$dest_rep = "//192.169.0.10/apache/$annee";
$dir = opendir($rep);
while ($f = readdir($dir))
{
   if(is_file($rep.$f))
   {
     echo $f;
     rename($rep.$f, $dest_rep.$f);
   }
}
?>

by Gatien at January 15, 2011 01:17 PM

November 20, 2014

Luc Didry

OpenWRT : les hôtes du lan ne peuvent pas se connecter les uns aux autres

Quand la neufbox comme box pour mon ADSL chez LDN a commencé à rendre l’âme, j’ai acheté un modem-routeur Buffalo (AirStation Nfiniti WBMR-HP-G300H) histoire d’avoir un truc hackable sur lequel je pourrais mettre OpenWRT et faire plus que juste modem-routeur.

Ça marchait très bien jusque là. Le passage à OpenWRT s’est fait relativement facilement et j’ai pu le transformer en plus en serveur DLNA, ce qui m’a bien servi jusqu’à ce que je m’installe un Kodi (ex-XBMC).

J’ai voulu tester ce soir un Raspberry Pi dans l’optique d’en faire un serveur de backup. Problème : il n’arrivait pas à contacter un autre ordinateur du réseau, alors même qu’ils étaient tous les deux connectés en filaire sur le routeur ! Alors que depuis mon pc en wifi, je pouvais contacter n’importe quel ordinateur dans le LAN et que le routeur contactait bien les deux ordinateurs.

La raison est finalement simple : OpenWRT isole chaque port physique. Pour résoudre ça, j’ai bien trouvé une astuce, à savoir mettre ceci :

config switch 'eth0'
    option reset '1'
    option enable_vlan '1'

config switch_vlan
    option device 'eth0'
    option vlan '1'
    option ports '0 1 2 3 4 5'

dans le /etc/config/network et rebooter le Buffalo. Bah oui, mais ça ne marchait pas !

J’ai par contre trouvé ces commandes à taper dans le terminal :

swconfig dev eth0 vlan 1 set ports "0 1 2 3 4 5"
swconfig dev eth0 set apply

Et là ça fonctionne tout de suite ! Et pour que cela soit exécuté à chaque redémarrage, j’ai posé ces commandes dans /etc/rc.local (oui, c’est sale, mais je n’avais pas envie d’avoir à découvrir les subtilités d’OpenWRT plus que ça).

Bref : ça marche !

Partager

Flattr this!

by Luc at November 20, 2014 10:54 PM

February 16, 2014

Luc Didry

Lut.im, un service d’hébergement d’images gratuit, libre et anonyme

Que celui qui n’a jamais voulu partager simplement une capture d’écran lève le doigt. Personne ?

Logo de LUTIm

Le partage d’images nous confronte souvent à divers problèmes :

  • un email prend du temps (retrouver l’adresse du destinataire, l’envoi, etc.) ;
  • un email prend de la place. Ce n’est pas grand chose, mais pour une image jetable, c’est de l’espace disque perdu, que ce soit dans le dossier « Envoyé » de l’expéditeur ou celui du destinataire. Oui, on peut supprimer le mail, mais c’est encore une action à effectuer.
  • une solution commme imgur nous ramène au sempiternel problème des Conditions Générales d’Utilisation imbitables, pas traduites et qu’on ne lit de toute façon jamais en entier. Pour ce genre de service, on risque de fournir certains droits à l’hébergeur… et ça c’est pas cool !
  • un owncloud (ou équivalent) fera bien le travail, au prix d’une certaine complexité de partage et de liens à la longueur ahurissante.

Pour répondre à cette problèmatique, j’ai codé LUTIm (prononcez comme lutin). Écrit en Perl avec le framework Mojolicious, utilisant le Twitter Bootstrap, un sous ensemble de Font Awesome et un plugin jQuery légèrement modifié pour la gestion du glisser/déposer, LUTIm est un logiciel libre (licence AGPL) de partage d’image anonyme et gratuit.

Capture d'écran de l'interface de LUTIm

Le principe est simple : on glisse/dépose des images (ou via le sélecteur de fichier classique) et on récupère 3 liens :

  • un lien vers l’image (utilisable dans une balise img par exemple) ;
  • un lien de téléchargement de l’image (pour éviter le Clic droit > Enregistrer sous) ;
  • un lien vers une page qui affiche l’image et qui est utilisable sur Twitter (l’image apparaîtra dans le tweet).

Des options du formulaire d’envoi permettent de supprimer automatiquement les images après la première consultation ou après 24h.

Bien évidemment, pour des questions légales, il n’est pas possible d’avoir un service totalement anonyme : les IPs des envoyeurs d’image et celles des consulteurs sont enregistrées dans les logs, mais c’est quelque chose de tout à fait habituel sur tout site web. Les IPs des envoyeurs ainsi que celle du dernier consulteur sont enregistrées dans la base SQLite pour accélerer la recherche d’informations en cas de requète judiciaire (je sais comme il peut être fastidieux et long de chercher dans des logs).

Lors de la suppression automatique d’une image, le fichier est bel et bien supprimé, mais son entrée en base de données persiste et contient l’empreinte SHA512 du fichier.

De par sa nature libre, vous pouvez bien évidemment installer et utiliser très facilement LUTIm sur votre propre serveur, mais vous pouvez aussi vous contenter d’utiliser l’instance officielle : http://lut.im

LUTIm est disponible en français et en anglais, la langue étant choisie selon les préférences du navigateur. Toutes les bonnes volontés sont les bienvenues pour proposer d’autres langues !

Enfin, LUTIm propose un plugin pour Shutter, logiciel de capture d’écran, pour permettre à celui-ci d’envoyer les capture sur http://lut.im directement (plugin à installer soi-même, le site du projet ayant l’air cassé, je n’ai pu leur remonter le plugin).

Flattr this!

by Luc at February 16, 2014 12:34 AM

July 18, 2014

Florent Peterschmitt

OpenSSL facile avec genssl

Ça ne doit pas être la première collection de scripts permettant de faire cela, mais quand on veut apprendre à se servir un peu d’un outil, on est toujours tenté de ré-inventer la roue afin de comprendre plus facilement.

J’ai eu besoin, comme beaucoup de monde, de créer des certificats SSL “serveurs” et “clients”, signés par une CA.

Pif paf poum :

https://git.beastie.eu/Leryan/genssl

C’est tout expliqué dans le README comment l’utiliser, et les exemples de scripts sont là pour aider à démarrer.

Tout cela mériterait d’être amélioré avec la gestion des CRL et une meilleure gestion des variables d’environnement et du fichier openssl.cnf.

by Florent Peterschmitt at July 18, 2014 10:30 AM

July 11, 2014

Romain Dessort

Reverse proxy avec re-chiffrement du flux vers le backend

Dans un environnement web critique/à fort trafic, il est courant d'avoir de multiples serveurs web, et un ou deux load-balancer devant permettant de répartir la charge et d'avoir une tolérance de panne sur les serveurs web.
Dans le cas d'un site en HTTPS, le flux est en général déchiffré par les load-balancer, puis transite en clair vers les frontaux. Cela permet de décharger les frontaux du traitement cryptographique et au load-balancer d'interpréter le HTTP, donc d'avoir certaines fonctionnalités intéressantes (répartition du trafic en fonction du virtualhost ou de l'URL, gestion des cookies de session, interprétation des codes d'erreurs HTTP renvoyés par les frontaux pour anticiper un problème, etc…).
Cependant le fait que le flux HTTP transite en clair entre les load-balancers et les frontaux peut poser problème dans certains cas :

  • les données qui y transitent nécessitent un haut degré de confidentialité (numéros de cartes bancaires par exemple) ;
  • le réseau entre le load-balancer et les frontaux n'est pas sûr (le flux repasse sur Internet par exemple).

Dans ces cas, la solution est de re-chiffrer le trafic après traitement par le load-balancer, puis chaque frontal le déchiffre à nouveau. Il faut bien noter que cette charge supplémentaire en traitement cryptographique (déchiffrement, re-chiffrement, re-déchiffrement), n'est pas anodine et la quantité de requêtes par seconde que peut encaisser la plateforme sera bien moindre que dans le cas d'un simple déchiffrement du flux par le load-balancer.

Pour en venir au cœur de l'article, j'ai dû mettre en place récemment une architecture de ce type, et j'ai été confronté à un problème dont je ne m'attendais pas vraiment : peu de reverse proxies HTTP savent re-chiffrer le flux HTTP vers les backends !

Parmi les logiciels existants :

  • HAProxy : dans sa version stable actuelle (1.4), il ne sait pas déchiffrer le trafic entrant, et donc encore moins re-chiffer derrière. Sa version encore en dév (1.5) supporte le déchiffrement du HTTPS néanmoins ;
  • Nginx : il sait bien sûr déchiffrer le HTTPS mais pas le re-chiffrer vers les backends ensuite ;
  • Pound : pareil que Nginx, ne sais que déchiffrer ;
  • Apache : le seul après recherche à savoir faire les 2 !

Voici la conf (en somme très simple) d'Apache pour lui dire de re-chiffrer le flux vers les backends. Elle nécessite bien évidemment d'avoir mod_proxy_http et mod_ssl :

<VirtualHost *:443>

    # Activation de SSL pour le trafic entrant, cas dans le cas d'un
    # serveur web classique.
    SSLEngine On
    SSLCertificateFile    /etc/ssl/certs/example.com.crt
    SSLCACertificateFile  /etc/ssl/certs/example.com-intermediate.crt
    SSLCertificateKeyFile /etc/ssl/private/example.com.key

    # Activation de SSL pour la communication avec les backends.
    # On vérifie au passage que le certificat fourni par le backend est
    # bien valide.
    SSLProxyEngine          On
    SSLProxyCheckPeerExpire On
    SSLProxyVerifyDepth     10

    # Configuration classique du mod_proxy, mis à part qu'on spécifie bien
    # https dans l'URL du backend.
    ProxyRequests     Off
    ProxyPreserveHost On
    ProxyPass         / https://192.0.2.42:443/ keepalive=On retry=5
    ProxyPassReverse  / https://192.0.2.42:443/

    ErrorLog  /var/log/apache2/example.com_error.log
    CustomLog /var/log/apache2/example.com_access.log combined
</VirtualHost>

July 11, 2014 08:50 PM

January 05, 2011

Gatien Gaspard

Squid, Proxy et Reverse-Proxy sur un même serveur

Un petit mémo sur Squid pour ouvrir cette nouvelle année pour laquelle je souhaite mes Meilleurs Voeux à chacun de vous, fidèles lecteurs ou lecteurs d’un jour, vous êtes de plus en plus nombreux à suivre ce blog et je vous en remercie, continuons sur cette lancée pour 2011.

Squid peut être configuré en tant que « Proxy » (comportement par défaut), il permet dans ce cas à ses utilisateurs d’accéder à tout ou partie d’internet ou de divers Intranet. Il peut également servir de « Reverse-Proxy » , il permet alors de rendre disponible certaines ressources internes (provenant d’un ou plusieurs serveurs) tout en proposant une mise en cache, ce qui va alléger la charge des serveurs situés derrière lui.

Schéma du principe de fonctionnement du Reverse-Proxy:

Par défaut, une seule instance de squid peut être lancée à la fois. Si l’on souhaite mettre en oeuvre un Proxy et un Reverse-Proxy sur un même serveur, il va falloir procéder à quelques modification dans la configuration de squid.

Nous n’allons pas nous occuper ici de la configuration de Squid en mode Proxy puisque la configuration par défaut permet déjà ce fonctionnement. Nous allons donc permettre le lancement de multiples instances et configurer le Reverse-Proxy.

Nous allons donc avoir 2 fichiers de configurations différents:

/etc/squid/squid.conf
/etc/squid/squid-reverse.conf

Voyons un exemple de ce que peut donner la configuration du fichier squid-reverse.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# nom du reverse-proxy et port d'écoute
visible_hostname test
http_port 80 defaultsite=192.168.0.5
#
# le site proposé par défaut par le reverse-proxy se trouve sur 192.168.0.5:
cache_peer 192.168.0.5 parent 80 0 no-query no-digest originserver name=test
acl webserver dst 192.168.0.5
http_access allow webserver
#
# autoriser l'utilisation du reverse-proxy sur le port 80 pour tout le monde:
acl all src 0.0.0.0/0.0.0.0
http_access allow all
acl web port 80
http_access allow web
#
# configuration du cache:
cache_dir ufs /var/spool/squid3 2048 16 256
coredump_dir /var/spool/squid3
cache_effective_group proxy
cache_mem 50 MB
minimum_object_size 0 KB
maximum_object_size 64096 KB
#
# Emplacement du PID et des fichiers de logs:
pid_filename /var/run/squid-reverse.pid
access_log /var/log/squid/access-reverse.log squid
cache_log /var/log/squid/cache-reverse.log
#
forwarded_for on

Nous devrons ensuite générer le cache correspondant à ces 2 configurations:

/usr/sbin/squid -z

/usr/sbin/squid -z -f /etc/squid/squid-reverse.conf

Puis modifier le script de lancement de squid, ou en ajouter un pour les gérer indépendamment, nous allons ajouter un fichier:

/etc/init.d/squid-reverse

Pour faire simple et tester, il suffit d’y placer la commande de lancement suivante:

/usr/sbin/squid -f /etc/squid/squid-reverse.conf

Libre à vous ensuite de créer un véritable script de lancement en vous basant sur le script de lancement de Squid ou sur le template disponible (« /etc/init.d/skeleton« ).

Lancer ces scripts automatiquement au démarrage pour démarrer le Proxy et le Reverse-Proxy:

update-rc.d -f squid defaults

update-rc.d -f squid-reverse defaults

Nos 2 services ne devant pas être utilisés par les mêmes personnes, le proxy sera utilisé depuis le réseau interne tandis que le reverse-proxy sera utilisé par les internautes souhaitant accéder à notre site, nous penserons également à configurer un parefeu à l’aide d’iptables afin de sécuriser ce serveur proxy, un exemple suivra dans le prochain article.

Sources et ressources relatives:

by Gatien at January 05, 2011 04:41 AM

September 22, 2015

Luc Didry

Erco : Exabgp Routes Control­ler

Pour les besoins du réseau Lothaire (aka « mon boulot »), j’ai écrit un service web permet­tant de pilo­ter Exabgp (logi­ciel permet­tant de faire des annonces de routes en BGP. C’est du Soft­ware-Defi­ned Networ­king) afin de lui ajou­ter ou de lui reti­rer des routes de façon simple, afin de faire du RTBH (Remo­tely Trig­ge­red Black Hole).

Ce logi­ciel (libre, bien sûr, comment pour­rait-il en être autre­ment ?), s’ap­pelle Erco (Exabgp ROutes COntrol­ler). Le site de présen­ta­tion est https://erco.xyz, et une démo est dispo­nible sur https://erco.xyz/demo/.
Soyez indul­gents sur le temps de char­ge­ment du site, c’est hébergé chez moi, derrière de l’ADSL.

Le service expose une API REST, ce qui permet à Erco d’être piloté par des programmes.

Pour les besoins du service, j’ai écrit un programme pour expo­ser l’API d’Exabgp, et inci­dem­ment, j’ai écrit un client en ligne de commande pour cette API.

Les sources d’Erco sont sur https://git.frama­soft.org/luc/erco.

Pour les curieux de tech­nique, le logi­ciel est écrit en Perl avec le frame­work Mojo­li­cious (bien sûr, comment pour­rait-il en être autre­ment ? 1).

1 : c’est pas parce qu’on n’est pas vendredi qu’on n’a pas le droit de trol­ler un peu :-P

Parta­ger

Flattr this!

by Luc at September 22, 2015 06:45 AM

April 30, 2015

Luc Didry

Le sculpteur

Couverture de la BD « Le sculpteur »

Couverture de la BD « Le sculpteur »

Scott McCloud est un auteur de bande dessinée qui m’est cher. Pas pour ses BDs, non, mais pour son essai sur la BD, L’art invisible où il explique l’objet analysé grâce à celui-ci. C’est une BD sur la BD. Jamais pédant, l’auteur se met en scène pour nous entraîner derrière les case, pour nous apprendre pourquoi nous aimons autant la BD, ce qui en fait un médium si particulier.

Bref, quand j’ai vu qu’un nouvel ouvrage de Scott McCloud était sorti, j’ai hésité. Parce qu’il restait pour moi celui qui m’a fait comprendre l’infinie richesse qui se cache derrière les coups de crayons. Voir ce qu’il en faisait n’allait-il pas me décevoir ? Comment sortir quelque chose d’aussi brillant que l’art invisible ?

J’ai eu l’occasion de lire quelques pages du sculpteur chez une personne qui m’a hébergée à Bruxelles.

Quelques pages ont suffit à conquérir mon cœur.

Le dessin est à mille lieues de l’art invisible et m’a fait terriblement penser au trait d’un autre auteur américain, Craig Thompson, qui m’a vraiment remué avec Blankets, manteau de neige.

Dans le sculpteur, Scott McCloud revisite le pacte faustien avec un sculpteur new-yorkais, David Smith, non sans talent mais sans succès. Il passe alors un pacte qui lui permet de modeler n’importe quel matériau de ses mains nues contre une échéance : il mourra au bout de 200 jours.

David est torturé, paumé et même son nouveau talent ne l’aide pas… jusqu’à ce qu’il retrouve Meg, une artiste rencontrée quelques heures à peine après avoir passé son marché.

Au-delà de l’histoire que je ne vous dévoilerai pas plus1, le dessin en noir, blanc et nuances de bleu est magnifique et la narration nous emporte. Il est dur, très dur de lâcher ce pavé2, même après l’avoir fini. Il fait partie de ces quelques ouvrages qui vous laissent une sensation au fond du cœur après l’avoir refermé.

À lire. Vraiment.


  1. ça serait dommage d’en dire plus 

  2. près de 500 pages quand même 

Partager

Flattr this!

by Luc at April 30, 2015 11:44 PM

August 25, 2013

Luc Didry

Cacher les moteurs de recherche par défaut sur Firefox pour Android

La nouvelle nightly de Firefox pour Android amène plusieurs changements. Certains au niveau de la page d’accueil, d’autres au niveau des moteurs de recherche et sans doute d’autres trucs sous le capot que je n’ai pas vu (mais j’ai l’impression qu’il est plus rapide qu’avant).

Au niveau des moteurs de recherche, il y a un truc génial : un appui long sur le champ de recherche d’un site permet de l’ajouter à la liste de ses moteurs. C’est franchement pratique et j’ai hâte d’avoir ça sur la version desktop.

Par contre, on ne peut plus désactiver les moteurs de recherche intégrés dans le navigateur, et ça c’est dommage.

Pour les cacher, il suffit d’aller à l’adresse about:config, de rechercher browser.search.loadFromJars et de le passer à false.
Simple, mais efficace.

Flattr this!

by Luc at August 25, 2013 04:12 PM

August 07, 2014

Florent Peterschmitt

Puppet - Gestion des dépendances entre classes

Problème

Nous souhaitons faire en sorte que l’exécution de classes Puppet se fasse dans un ordre bien choisi et sans avoir à gérer les dépendences à la main de cette façon :

Ressource B require -> Ressource A

En effet, si on doit s’assurer que tous les éléments présents dans une classe s’exécutent avant la ressource (ici une autre classe) A, alors il faudra utiliser le mécanisme de containment.

Ce qui m’a amené à trouver la solution provient du ticket 8040.

Solutions

Il existe une solution moche et une propre, la moche étant un workaround pour les version inférieures à 3.4.0 de Puppet.

Solution Puppet < 3.4.0

class init {
    anchor {'init::begin':} ->
    class {'init::install':} ->
    class {'init::config':} ->
    anchor {'init::end':}
}

Solution Puppet >= 3.4.0

class init {
    contain init::install
    contain init::config

    Class['init::install'] ->
    Class['init::config']
}

Mise en œuvre

Pour ma configuration, voici ce qui a été réalisé :

by Florent Peterschmitt at August 07, 2014 05:30 PM

May 04, 2013

Florent Peterschmitt

ZFS - FreeBSD 9.1-RELEASE et 9-STABLE

Bon, cette fois plutôt que de tout expliquer parce que c’est déjà fait, je vous propose un script adaptable très facilement en fonction de vos besoins.

Je vous conseil de vous faire un « mirroir » pour l’installation. Téléchargez le script, les archives que vous voulez, mettez ça sur quelconque serveur web/FTP, modifiez le script selon vos besoins et zou !

Ajoutez aussi quelques fichiers de conf dont vous pouvez avoir besoin pour votre machine. Par exemple, un serveur qui une fois démarré n’est plus accessible que par le réseau… a besoin d’avoir le réseau configuré au démarrage. Captain obvious !

Démarrez en mode LiveCD (ou en rescue-pro chez OVH ça marche aussi)

kbdmap # pour le clavier

Configurez le réseau : dhclient, ifconfig…

dhclient <interface>
# Ou alors
ifconfig <interface> <ip>
cd /tmp
fetch http://<source>/zfs.sh
chmod +x zfs.sh
./zfs.sh

Vous être chrootés dans le nouveau système, bricolez, rebootez, savourez !

by Florent Peterschmitt at May 04, 2013 01:15 PM

July 18, 2017

Romain Dessort

Rando-voyage dans les provinces maritimes du Canada

Je reviens d'un voyage-rando dans les provinces maritimes du Canada, principalement à Terre-Neuve. Comme d'habitude, les photos sont ici mais j'ai aussi pour l'occasion fait un récit de mon séjour là-bas.

Ça m'a pris quelques modifications dans gallepy (mon générateur de galeries photos statique), pour pouvoir ajouter un onglet Récit si un story.mdwn est présent dans le répertoire de la galerie, mais je suis plutôt content du résultat.

La suite du récit arrivera dans les prochains jours.

July 18, 2017 09:53 PM

September 10, 2015

Romain Dessort

How to enlarge your raw disk image

Extending a disk image file (of a KVM guest for example) is a tricky operation, especially if we haven't enough disk space to keep a backup of the image we will working on. So I write a little howto for the next time we have to do this operation.

Prerequisites

First, you need to have the following packages installed: qemu-utils kpartx parted.

Make sure your VM is halted and no processes have opened the file:

# lsof $img

Note: for following commands, I assume $img contains the path to your image file.

Extend the image file

Pretty easy step, you can simply use qemu-img tool:

# qemu-img resize $img +50G
Image resized.

You can mount the image and check that it has the correct size:

# kpartx -v -a $img
add map loop0p1 (254:13): 0 195318207 linear /dev/loop0 63
# fdisk -l /dev/loop0 

Disk /dev/loop0: 214.7 GB, 214748364800 bytes
[...]

Extend the partition

Then, you have to extend the partition. That supposes of course either the disk image contains only one partition (which could be a LVM PV) or the partition you want to extend is at the end of the image.

Before all, backup your partition table:

# sfdisk -d /dev/loop0 >~/loop0.parts

(It could be restored with sfdisk /dev/loop0 <~/loop0.parts).

And remove and recreate the partition using all free space with parted:

# parted /dev/loop0 print
Model:  (file)
Disk /dev/loop0: 215GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End    Size   Type     File system  Flags
 1      32.3kB  100GB  100GB  primary  ext3
..
# parted /dev/loop0 rm 1
# parted /dev/loop0 mkpart primary ext3 32.3kB 215GB
# parted /dev/loop0 print

Remount the image to see her new size:

# kpartx -d $img
loop deleted : /dev/loop0
# kpartx -v -a $img
add map loop0p1 (254:13): 0 419430337 linear /dev/loop0 63

Extend the filesystem

We are almost there, remaining the filesystem. First, this is a good thing to run a fsck before all:

# e2fsck -f /dev/mapper/loop0p1 
e2fsck 1.42.5 (29-Jul-2012)
/dev/mapper/loop0p1: recovering journal
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/mapper/loop0p1: 41753/6111232 files (9.1% non-contiguous), 23479003/24414775 blocks

Then, resize the filesystem:

# resize2fs /dev/mapper/loop0p1 
resize2fs 1.42.5 (29-Jul-2012)
Resizing the filesystem on /dev/mapper/loop0p1 to 52428792 (4k) blocks.
The filesystem on /dev/mapper/loop0p1 is now 52428792 blocks long.

It's done! We can check if we see the correct size after mounting the partition:

# mount /dev/mapper/loop0p1 /mnt
# df -h /mnt
Filesystem           Size  Used Avail Use% Mounted on
/dev/mapper/loop0p1  197G   89G  109G  45% /mnt
# umount /mnt

Finally, unmount the image:

# kpartx -d $img
loop deleted : /dev/loop0

September 10, 2015 05:04 PM

February 12, 2012

Brice Lopez

Slackbuild : weboob 0.a

logo weboob

Weboob (WEB Out Of Browser, appréciez le jeu de mot) permet d'obtenir rapidement le contenu de certains sites web sans passer par un navigateur.

Il y a plusieurs intérêts à cela : éviter l'utilisation du plugin flash pour regarder une vidéo, ne pas subir des pages lourdingues bourrées de pub, ou réaliser une requête sur plusieurs sites à la fois.

En bonus : tout peut être fait dans la console.


Installer weboob


L'installation pour la plupart des distributions est très bien expliquée sur le site de weboob.

Pour l'installer sous slackware, j'ai réalisé un slackbuild qui automatise la création du paquet. Étant donné le délai nécessaire à la validation du script sur slackbuilds.org, je le met à disposition ici :

slackbuild : weboob.tar.gz
sources : weboob-0.a.tar.gz (md5sum : 15568485fd2bea4c53b69a6edb8c7e45)

Afin de pouvoir construire weboob, vous aurez besoin de pysetuptools.

Les programmes mechanize, python-dateutil, html2text, nose, feedparser, lxml, rtmpdump, et PyYAML seront ensuite nécessaires au bon fonctionnement des backends.


Utilisation


Avec la nouvelle release, les backends sont téléchargés sur les dépots de weboob la première fois qu'il y en a besoin. Il faut donc commencer par récupérer la liste de ceux qui sont disponibles :

$ weboob-config update

Ensuite, il ne reste plus qu'à tester les applications qui vous intéressent.


Videoob


Videoob permet de télécharger des vidéos disponibles en streaming sur les sites d'Arte, Canal+, DailyMotion, France Télévisions, INA, Nolife, Radio France, Youjizz, Youporn et Youtube. Bon, vous n'êtes pas obligés d'activer tous les backends, hein ;-)

Lors du premier démarrage, un menu interactif vous permettra d'activer ceux que vous souhaitez.

Par exemple pour télécharger les derniers épisodes de "Bref", on démarre l'application videoob :

$ videoob

On désactive tous les backends configurés sauf celui de canalplus :

videoob> backends disable arte dailymotion francetelevisions

On cherche les épisodes de bref :

videoob> search bref
Search pattern: bref
* (1) Bref. J'ai eu 30 ans. — Episode du 08/02 (canalplus)
            2012-02-08 16:02:00
* (2) Bref. J'ai eu une panne. — Episode du 06/02 (canalplus)
            2012-02-06 21:37:00
* (3) Bref. Je me suis fait agresser. — Episode du 02/02 (canalplus)
            2012-02-02 20:40:00
* (4) Bref. Je suis allé aux urgences. — Episode du 31/01 (canalplus)
            2012-01-31 17:02:00
* (5) Bref. J'ai un nouvel appart'. — Episode du 26/01 (canalplus)
            2012-01-26 20:50:00
* (6) Bref. J'ai pris le métro. — Episode du 24/01 (canalplus)
            2012-01-24 21:22:00
* (7) Bref. J'étais à côté de cette fille. — Episode du 18/01 (canalplus)
            2012-01-18 18:37:00
* (8) Bref. J'ai déménagé. — Episode du 12/01 (canalplus)
            2012-01-12 18:37:00
* (9) Bref. J'ai voulu partir en vacances. — Episode du 10/01 (canalplus)
            2012-01-10 18:32:00
* (10) Bref. J'y pense et je souris. — Episode du 06/01 (canalplus)
            2012-01-06 18:22:00 

On les télécharge le dernier :

videoob:/search> download 1

On peut aussi le jouer directement dans mplayer :

videoob:/search> play 1


Wetboobs


Héhé, oui, c'est possible d'aller encore plus loin dans les jeux de mots graveleux. Mais, contrairement à ce que son nom laisse supposer, wetboobs n'est pas une application réservée aux adultes : elle permet simplement de consulter la météo.

Lancer l'application :

$ wetboobs

Rechercher une ville :

wetboobs> search clermont-ferrand
* (1) Clermont-Ferrand, Auvergne, France (yahoo)
* (2) Clermont-Ferrand 63000 (meteofrance)

Consulter les prévisions météo de Clermont-Ferrand sur Yahoo :

wetboobs> forecasts 1
* 12 Feb 2012:    (-11°C - -4°C) Clear
* 13 Feb 2012:    (-1°C - 2°C) Partly Cloudy

Si on connait l'identifiant de la ville, on peut tout faire en une commande :

$ wetboobs forecasts 586242@yahoo
* 12 Feb 2012:    (-11°C - -4°C) Clear
* 13 Feb 2012:    (-1°C - 2°C) Partly Cloudy


Et tout le reste...


Il ne s'agit que d'un rapide apperçu, weboob contient de nombreuses applications et backends. Vous pourrez approfondir la question directement sur leur site.

by St3rk at February 12, 2012 02:36 PM

June 08, 2011

Simon Florentin

[1 an en Suède]: Världsrekord i vattenkrig

Comme annoncé précédemment, la plus grosse bataille d'eau du monde à eu lieu à Linköping hier après-midi, le record était jusqu'à présent de 3000 personnes il me semble. Selon les organisateurs, 1h après le lancement de l’évènement, 3500 personnes étaient déjà en train de combattre.

by Simon at June 08, 2011 10:29 AM

June 03, 2015

Luc Didry

Mise à jour d’OpenWRT sur Buffalo WBMR-HP-G300H

EDIT :

WARNING : il y a un sale bug dans l’IPv6 de Barrier Brea­ker ! Je perds l’IPv6 une fois par jour : https://dev.open­wrt.org/ticket/12888

Ne mettez pas à jour, sauf si vous vous foutez d’IPv6 (et vous ne devriez pas !).

Comme je suis abonné à un vrai FAI qui four­nit du vrai Inter­net bio, sans filtre ni surveillance, fait à la main et roulé sous les aisselles, je n’ai pas de box four­nie par celui-ci (enfin, si, on en prête, des neuf­box ancienne géné­ra­tion mais c’est pas top, ça fait pas d’IPv6).

Du coup, je m’étais payé un modem/routeur Buffalo WBMR-HP-G300H qui a l’im­mense avan­tage d’être compa­tible OpenWRT. OpenWRT est un firm­ware de modem/routeur libre, ce qui veut dire que flasher son routeur avec OpenWRT, c’est aussi jouis­sif que de libé­rer un ordi en mettant une distri­bu­tion GNU/Linux sur un ordi fourni avec Windows. :D

Je ne vais pas détailler l’ins­tal­la­tion d’OpenWRT, mais sa mise à jour, de la version 12.09 (Atti­tude Adjust­ment) vers la dernière version en date, la 14.07 (Barrier Brea­ker).

Mise à jour

Tout d’abord, on va récu­pé­rer l’image kiva­bien et l’en­voyer sur le routeur (partons du prin­cipe que le routeur est acces­sible à l’IP 192.168.1.1) :

wget https://downloads.openwrt.org/barrier_breaker/14.07/lantiq/xway/openwrt-lantiq-xway-WBMR-squashfs.image
scp root@192.168.1.1:/tmp

(voir ici pour libé­rer de la place s’il n’y en a pas assez dans /tmp)

On va aussi récu­pé­rer un fichier néces­saire pour la suite et on se le garde au chaud sur son ordi :

wget https://downloads.openwrt.org/barrier_breaker/14.07/lantiq/xway/packages/base/kmod-ltq-adsl-ar9-fw-a_0.1-1_lantiq.ipk

On met à jour :

ssh root@192.168.1.1
cd /tmp
sysupgrade openwrt-lantiq-xway-WBMR-squashfs.image

Un petit reboot s’il ne le fait pas tout seul.

Et là, c’est le drame !

Plus d’ADSL !

C’est simple : l’ADSL sur ligne télé­pho­nique clas­sique néces­site l’an­nexe a alors que l’image vient avec l’an­nexe b (ne me deman­dez pas la diffé­rence entre ces deux trucs ni ce que c’est).

On envoie alors le paquet télé­chargé tout à l’heure :

scp kmod-ltq-adsl-ar9-fw-a_0.1-1_lantiq.ipk root@192.168.1.1:/tmp

On supprime l’an­nexe b et on installe l’an­nexe a :

ssh root@192.168.1.1
cd /tmp
opkg remove kmod-ltq-dsl-firmware-b-ar9
opkg install kmod-ltq-adsl-ar9-fw-a_0.1-1_lantiq.ipk

Ensuite on met à jour /etc/config/network, dans la section config adsl :

config adsl 'dsl'
    option annex 'a'
    option firmware '/lib/firmware/adsl.bin'

On reboot (oui, je suis une feignasse qui ne souvient plus quel service il a redé­marré pour faire fonc­tion­ner le brol). Et là, norma­le­ment, ça fonc­tionne.

M’en­fin ? Où est passé mon IPv6 ?

Barrier Brea­ker simpli­fie la gestion de l’IPv6 en inté­grant tout ce qu’il faut pour que ça fonc­tionne, sans paquet supplé­men­taire. Mais comme ce n’était pas le cas avant, on a une confi­gu­ra­tion qui ne va pas :-(

On commence par virer radvd :

opkg remove radvd

Ou alors, vous pouvez juste l’ar­rê­ter et le désac­ti­ver :

/etc/init.d/radvd stop
/etc/init.d/radvd disable

Ensuite on met à jour /etc/config/network (atten­tion, je ne fais qu’ajou­ter de la config dans les diffé­rentes section, ne suppri­mez pas ce que vous avez déjà) :

config interface 'lan'
    option ip6assign '64'
    option ip6hint '42'

Expli­ca­tion : ip6assign '64' indique que vous assi­gnez un réseau /64 à votre réseau local et ip6hint '42' sera conca­téné à votre bloc d’IPv6.

C’est pas clair, donc voici un exemple : mettons que votre FAI vous four­nisse le bloc 2001:DB8:1337::/48. Avec les options ci-dessus, les machines de votre réseau local piochera dans le bloc 2001:DB8:1337:42::/64.

Ça, norma­le­ment, vous l’avez déjà si vous aviez déjà confi­guré de l’IPv6 avant la mise à jour :

config interface 'wan'
    option ipv6 '1'

Et on ajoute une nouvelle section :

config interface wan6
    option ifname   '@wan'
    option proto    dhcpv6

Main­te­nant on modi­fie /etc/config/dhcp (toujours en ajout dans les sections exis­tantes) :

config dhcp 'lan'
    option interface 'lan'
    option ra 'server'
    option dhcpv6 'server'
    option ra_management '2'

Véri­fiez que cette section a bien été ajou­tée lors de la mise à jour d’OpenWRT :

config odhcpd 'odhcpd'
    option maindhcp '0'
    option leasefile '/tmp/hosts/odhcpd'
    option leasetrigger '/usr/sbin/odhcpd-update'

On s’as­sure que /etc/sysctl.conf contienne bien ces deux lignes :

net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1

Et on reboote.

Voilà, norma­le­ment ça devrait rouler tout seul :-)

Et pour finir, une jolie nimage de la nouvelle inter­face web d’OpenWRT qui vient avec Barrier Brea­ker :

Capture d'écran de l'interface web de Barrier Breaker

Sources :

Parta­ger

Flattr this!

by Luc at June 03, 2015 06:00 AM

September 26, 2014

Florent Peterschmitt

Marre des trolls systemd sur les mailing lists ?

Quand y’en a marre…

Déjà que c’est très pénible d’écouter les reproches consternantes au sujet de systemd et de tous les défauts de la Terre qu’il soit-disant embarque avec lui, mais si en plus on se les tape dans les ML, rien ne va plus.

Donc :

if header :contains ["List-Post"] "user@lists.debian.org" {
 if header :matches "Subject" ["*systemd*"] {
   fileinto "Trash";
 } else {
  fileinto "Debian.Users";
 }
}

On pourrait même pousser le vice plus loin en filtrant aussi sur "*init*".

Faites’tous’chier.

by Florent Peterschmitt at September 26, 2014 05:00 PM

August 13, 2013

Florent Peterschmitt

Utiliser gpg-agent pour ssh

gpg-agent

Lancez l’agent GPG dans votre console, ou votre session X.org (via le fichier .xsession pour ceux qui l’utilisent), pour ça on pourra faire indifféremment du type de session :

eval $(gpg-agent --daemon --enable-ssh-support)

On pourra ajouter l’option suivante pour avoir une zolie interface fort pratique pour nous demander la passphrase, qu’on souhaite mémoriser, de la clef GPG ou SSH voulue :

--pinentry-program USRBINDIR/pinentry(-gtk|-qt4|-ncurses)

Pour SSH, petite manip’

Le manuel de gpg-agent stipule qu’il faudra exécuter un premier ssh-add pour que le support des clefs SSH soit effectif.

Enjoy

by Florent Peterschmitt at August 13, 2013 04:30 PM

January 21, 2016

Romain Dessort

Getting Galaxy S2 GPS to work on Replicant

Last week I gave a talk about Replicant with a live (re)installation of it on my phone at Club Linux Atomic. You can download the slides (in french) here (and LaTeX sources here). There are available under the Creative Common BY-SA 3.0 license.

But let's come back to the topic of this post, the GPS chip of the Samsung Galaxy S2. After my talk, someone asked me how I got the GPS module to work on my phone. There is indeed no support of it on Replicant as the chip (a SiRF star IV GSD4t) is not recognized by standard free software tools like gpsd. Someones already worked on it, but without success at this time. All the work is on this Replicant's wiki page. So if you really need it, you have to use the proprietary code.

I realize that there is no documentation explaining how to install this proprietary code, so here is what I did on my phone. Bear in mind that, unlike proprietary firmwares you need for WiFi/Bluetooth chipset, the proprietary code needed for the GPS is a “real” software which run on your main SoC, and could potentially have access to all your data.

  • First, let's download the CyanogenMod archive corresponding to the Galaxy S2. Replicant is based on CyanogenMod and we are sure that the code we need is chipped with the CyanogenMod distribution.
  • Then you need to extract and upload to your phone this 2 files: gps.exynos4.so and libsecril-client.so:

    adb push system/lib/hw/gps.exynos4.so /system/lib/hw/
    adb shell chmod 644 /system/lib/hw/gps.exynos4.so
    adb push system/lib/libsecril-client.so /system/lib/
    adb shell chmod 644 /system/lib/libsecril-client.so
    

And that's it! Pretty simple, just 2 files but we should have to know the good ones.

January 21, 2016 08:24 PM

December 07, 2016

Simon Florentin

[MCP] Un peu de théorie avec les SysEx

Entrons dans le vif du sujet: le détail du protocole et l'affichage de données sur un écran LCD!

by Simon at December 07, 2016 09:26 AM

July 24, 2016

Luc Didry

Retour sur le « No poo »

Suite à mon jour­nal sur le Seitan sur LinuxFr (ou à la paru­tion de mon article de blog sur Diaspo­ra*, je sais plus), on m’a parlé du « No poo » et notam­ment de cet article. C’est un mouve­ment qui consiste à ne plus se laver les cheveux.

« Beurk, c’est dégueu­lasse ! » me direz-vous. Et bien non :-)

J’ai donc essayé le No poo. Ça fait un peu plus de deux mois main­te­nant que je ne me lave plus les cheveux qu’au bicar­bo­nate et jus de citron.

« Mais pourquoi donc faire cela ? »

Ma foi, j’ai toujours trouvé cela embê­tant de me mettre sur les cheveux un truc dont la compo­si­tion est impos­sible à comprendre à moins d’avoir un docto­rat en chimie.

Un peu moins de chimie dans la vie dans la vie quoti­dienne, ça ne peut pas faire de mal :-)

Résul­tat au bout de deux mois : je ne me lave plus les cheveux tous les jours, ils sont plus épais et plus doux, et après un lavage, ils sont aussi bien démê­lés qu’a­vec un sham­pooing clas­sique (j’ai les cheveux longs et légè­re­ment bouclés, ils s’em­mêlent faci­le­ment). Le prix ? J’ai pas fait trop gaffe… un paquet de 500g de bicar­bo­nate coûte un peu moins de 4€ et on en a pour plus de 2 mois. ± 3€ pour 50cl de jus de citron, qui dure plus de 2 mois… Alors oui, les bouteilles de sham­pooing peuvent coûter moins cher, mais si je ne dis pas de bêtises, je les vidais vite. Pour moi, c’est gagnant au niveau qualité et au niveau prix.

Je ne peux donc que vous encou­ra­ger à tester :-)

EDIT : dans le jour­nal LinuxFr que j’ai fait de cet article, Wendigo a pointé ce docu­men­taire. C’est sur le gel douche, mais ce qui s’y dit doit être valable pour les sham­pooings.

Parta­ger

by Luc at July 24, 2016 12:20 PM

April 05, 2015

Luc Didry

Salut vie privée, je t’aimais bien

Il y a des fois où je n’aime pas avoir raison. Je disais dans un précédent billet :

Il va falloir être attentif pour le futur. Non pas en créant tant et plus de lois sécuritaires et liberticides, mais en traitant le mal à la racine : pauvreté, exclusion, racisme… Ce sont les causes réelles de la radicalisation de certains paumés (ce terme n’est pas utilisé ici dans son sens péjoratif).

Jens Stoltenberg, premier ministre norvégien, déclarait ceci après l’attentat à Oslo et sur l’île d’Utoya :

« Nous allons répondre à la terreur par plus de démocratie, plus d’ouverture et de tolérance. »

Ça serait pas mal que tout le monde s’en souvienne.

Bah voilà, ça n’a pas loupé. Le projet de loi relatif au renseignement est arrivé au pas de charge.

Alors que le gouvernement annonçait ne pas vouloir légiférer dans l’urgence après les attentats de janvier 2015, ce projet de loi sera présenté en procédure accélérée. En gros : moins de débats, moins de temps pour se faire une opinion, etc.

TL;DR

Les services de renseignement disposeront d’une palette gigantesque de moyens techniques pour espionner, sous les ordres du premier ministre. La légitimité de leurs actions sera contrôlée a posteriori par une commission indépendante qui, elle, n’aura que des moyens réduits. Et les raisons des actions des services de renseignement seront multiples :

  • L’indépendance nationale, l’intégrité du territoire et la défense nationale ;
  • Les intérêts majeurs de la politique étrangère et la prévention de toute forme d’ingérence étrangère ;
  • Les intérêts économiques, industriels et scientifiques majeurs de la France ;
  • La prévention du terrorisme ;
  • La prévention des atteintes à la forme républicaine des institutions, des violences collectives de nature à porter atteinte à la sécurité nationale ou de la reconstitution ou d’actions tendant au maintien de groupements dissous en application de l’article L. 212-1 ;
  • La prévention de la criminalité et de la délinquance organisées ;
  • La prévention de la prolifération des armes de destruction massive.

On notera que certains de nos chers députés se focalisent sur l’argument du terrorisme. Sûrement parce que ça permet de dire « Si tu n’est pas pour cette loi, alors tu es pour les terroristes » (Sur l’article de Merome, un député ose dire « Vous défendez la liberté des terroristes »). C’est un peu facile d’oublier que cette loi permet aussi l’espionnage des mouvements sociaux. Le gouvernement nous assure que c’est juste pour éviter le regroupement de mouvements identitaires.

Aie confiance

Aie confiance – Le livre de la jungle © Disney

Navré, face à ceux qui nous gouvernent, je ne demande pas de la confiance, mais des garanties. Il n’y en a pas. Il n’y a aucune autorité judiciaire dans la boucle (la séparation des pouvoirs, c’est trop has been ?). Et quand bien même on voudrait se baser sur la confiance, comment faire confiance à ceux qui ne font que contredire ce qu’ils disaient lorsqu’ils étaient dans l’opposition ou ce qu’ils disaient il y a 2 mois (voir le début de cet article) ?

Petite note technique

Parmi les moyens techniques octroyés aux services de renseignement, il y une idée sans doute piquée aux services américains : la surveillance de masse. Oui oui. De belles boîtes noires posées chez les FAI histoire de surveiller le trafic internet de tout le monde et de déclencher des alertes grâce à des algorithmes bien évidemment secrets. Ça vous rappelle Minority report ? Oui, moi aussi.

Même le président de la commission de contrôle actuelle (qui serait remplacée par une autre dans ce projet de loi) dit qu’il y a un problème.

Encore un truc de geek quoi ?

Bah non. Reporters sans frontière prend aussi parti contre ce projet de loi, comme Amnesty International, la Ligue des droits de l’Homme, le syndicat de la magistrature, l’EFF, etc. Je vous invite à aller voir sur le standblog les articles sur ce projet de loi. Les revues de presse de Tristan Nitot sont très bien fournies.

Accessoirement

Comme d’habitude, ce projet de loi est rédigé dans un jargon imbitable (au-delà même du jargon légal), et les amendements proposés ne sont pas mieux. Il est dur pour le citoyen de suivre tout ça. La fabrique de la loi fournit un site qui permet de voir plus facilement les modifications d’une loi : http://git.lafabriquedelaloi.fr/parlement/pjl14-renseignement/. Faites-y donc un tour.

EDIT : Tiens, un autre lien intéressant à lire : http://signal.eu.org/blog/2015/03/17/pourquoi-la-loi-renseignement-va-etre-votee/

Partager

Flattr this!

by Luc at April 05, 2015 01:09 PM

November 30, 2014

Luc Didry

Création d’un epub, 1er essai

J’aime ce qu’écrit Neil Jomunsi, mais je n’aime pas lire des nouvelles ou des livres sur l’écran de mon ordinateur, ça me fatigue les yeux. Par contre, sur ma liseuse, pas de problème.

Hors, Neil a publié voici quelque temps une nouvelle sur son site. Je n’ai jamais eu le courage de la lire sur son site, mais j’avais terriblement envie de la lire. Je l’ai donc convertie en epub !

J’ai tout d’abord pris le texte de la nouvelle et l’ai « converti » en markdown (converti entre guillemets, vu qu’il n’y avait que les titres qui demandaient une adaptation).

Ensuite j’ai commencé à jouer avec pandoc pour convertir tout ça en epub. J’ai plus particulièrement regardé du côté de http://johnmacfarlane.net/pandoc/epub.html et http://johnmacfarlane.net/pandoc/README#epub-metadata.

J’avais un epub. Chouette. Mais il était un poil trop brut de fonderie et pas très joli. J’ai fait une couverture avec le selfie du macaque qui a fait le tour de la planète il y a quelques mois. Un petit coup de Gimp, et roule !

Histoire de vérifier l’epub, et pouvoir le modifier simplement, j’ai utilisé Sigil. J’ai viré la table des matières (les chapitres s’appelant 1., 2., etc, la table des matières n’avait pas grand intérêt), rajouté un petit texte sur l’auteur, modifié un peu la feuille de style pour faire un peu plus propre et utiliser la police LiberationSerif. Bref, un peu de polish. Pour les modifications de CSS, j’ai créé un fichier css pour pouvoir le réutiliser si je regénérais l’epub avec pandoc.

La commande pandoc finale :

pandoc kima.md -f markdown -t epub \
--epub-cover-image=kima.png \
--epub-stylesheet=epub.css --smart \
--epub-embed-font=/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf \
--epub-embed-font=/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf \
--epub-embed-font=/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf \
--epub-embed-font=/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf \
-o kima.epub

Pour trouver des astuces et en savoir plus sur l’epub, j’ai consulté le livre Créer des ebooks avec Sigil de Benoit Huot que j’avais acheté il y a plusieurs mois. Car oui, l’epub, c’est de l’HTML et de la CSS, et ça, je maîtrise, mais il n’y a pas que ça.

Bref, le résultat est là : https://lstu.fr/kima. C’est un premier essai, donc c’est forcément un peu maladroit (surtout la couverture), m’enfin c’était pour le fun.

La nouvelle de Neil est en licence Creative Commons NC-BY-SA (je ne me serais pas amusé à faire un epub si ça avait été du full copyright), donc n’hésitez pas à partager le lien et l’epub

Partager

Flattr this!

by Luc at November 30, 2014 12:03 AM

February 10, 2016

Florent Peterschmitt

i3status - brightnessctl

MAJ : brightnessctl

Dernièrement j’ai try Rust, mais sans rien avoir à en faire. Donc c’est passé à la trappe. Et puis récemment je suis tombé sur une petite limitation de Nginx qui ne permet pas de décompresser le body d’une requête HTTP, avant de la transmettre à un backend défini avec proxy_pass.

Et comme Nginx est écrit en C, et que je suis un peu… rouillé dans ce langage, je me suis dit que s’y remettre tranquillement, ça serait une bonne idée avant d’attaquer.

brightnessctl

brightnessctl

Petit programme type daemon, écrit en C avec libX11.

On démarrera ça via son .xsession par exemple.

i3status

Du coup, tant qu’à faire, petit patch pour i3status, afin d’afficher le pourcentage actuel de luminosité de l’écran, sur un laptop.

Et la conf ~/.i3status.conf :

brightness {
    max = "/sys/class/backlight/intel_backlight/max_brightness"
    cur = "/sys/class/backlight/intel_backlight/brightness"
    #txt = "lum: "
}

Inutile au possible, je sais.

by Florent Peterschmitt at February 10, 2016 02:56 PM

April 27, 2015

Florent Peterschmitt

systemd-networkd v219 et au delà

Un petit billet pour dire que si tout d’un coup, après passage en version 219 ou supérieure de systemd vous vous retrouvez avec vos interfaces virtuelles et bridges ne pouvant plus faire de NAT, et que vous gérez ces mêmes interfaces avec networkd, alors il vous faudra faire un peu plus de configuration.

Prenons par exemple un fichier lxcbr0.network :

[Match]
Name=lxcbr0

[Network]
Address=10.2.2.254/24
IPForward=yes
IPMasquerade=yes

[Route]
Gateway=10.2.2.254/24

Le bout de ChangeLog qui nous intéresse :

* networkd gained support for configuring bridge forwarding
     database entries (fdb) from .network files.

* networkd .network files gained support for configuring
     per-link IPv4/IPv6 packet forwarding as well as IPv4
     masquerading. This is by default turned on for veth links to
     containers, as registered by systemd-nspawn. This means that
     nspawn containers run with --network-veth will now get
     automatic routed access to the host's networks without any
     further configuration or setup, as long as networkd runs on
     the host. 

Et le bout de man correspondant :

[NETWORK] SECTION OPTIONS

... bla bla bla bla bla bla ...

IPForward=
    Configures IP forwarding for the network interface. If enabled incoming packets on the
    network interface will be forwarded to other interfaces according to the routing table.
    Takes either a boolean argument, or the values "ipv4" or "ipv6", which only enables IP
    forwarding for the specified address family.

IPMasquerade=
    Configures IP masquerading for the network interface. If enabled packets forwarded from the
    network interface will be appear as coming from the local host. Takes a boolean argument.
    Implies IPForward=yes.

Si je ne me trompe pas, dans le cas de l’utilisation d’interface de type veth, avec LXC par exemple (;-P), les paquets entrants seront transférés à l’interface vethBLAH appartenant au conteneur.

Et la seconde option nous sert pour faire notre SNAT.

Tout cela avec sysctl net.ipv4.ip_forward=1 et iptables -t nat -A POSTROUTING -j MASQUERADE, un coup de systemctl restart systemd-networkd et pouf.

Notez qu’avec Ubuntu 15.04, c’est systemd 219 qui est embarqué.

Ça m’aura quand même coûté une heure de temps cette connerie, et je vois pas bien ce que cette option vient foutre ici. Je suis pas contre une explication de la nécessité de gérer ce paramètre depuis networkd au lieu de laisser faire iptables/nftables.

by Florent Peterschmitt at April 27, 2015 08:53 PM

May 10, 2013

Florent Peterschmitt

Portal premier du nom sous GNU/Linux

Ça marche bien, quelques freeze habituels avec les jeux Valve sous Linux (liés à ma carte graphique Intel HD3000 sans doute) mais… un nouveau joueur verrait ses parties gâchées par quelques glitchs. Voyez plutôt :

portal 1 portal 2 portal 3 portal 4

by Florent Peterschmitt at May 10, 2013 05:38 PM

May 10, 2016

Luc Didry

RequestT­ra­cker : telle­ment puis­sant

RequestT­ra­cker (RT) est un outil de tickets extrê­me­ment puis­sant et flexible. Telle­ment flexible qu’on en vient à se prendre la tête pour faire des trucs de oufs.

Je suis en train d’en mettre un en place pour Frama­soft depuis quelques semaines. Je mets long­temps à le mettre en place pour plusieurs raisons :

  • j’ai d’autres projets à fouet­ter
  • il a fallu que je fasse des aller/retours avec Pouhiou pour cerner au mieux les besoins de notre équipe de support
  • je fais faire des trucs rigo­los à RT qui sont assez galère à mettre en place

Je vais faire un petit tour des trucs que j’ai mis en place sur un RT 4.4.0.

Utili­ser le plus addres­sing

De façon simple, pour que RT traite les tickets qui arrivent sur l’adresse email dédiée, on la met dans le /etc/aliases de sa machine. Ça fait un truc comme ça :

rt:         "|/opt/rt4/bin/rt-mailgate --queue general --action correspond --url https://rt.example.org/"
rt-comment: "|/opt/rt4/bin/rt-mailgate --queue general --action comment --url https://rt.example.org/"

Vous note­rez que cela met les mails à desti­na­tion de cette adresse dans la queue (ou file) general. Or on utilise géné­ra­le­ment plus d’une queue dans un système de ticket : cela permet de diri­ger auto­ma­tique­ment vers les personnes les plus à même d’y répondre.

Le problème avec ce système, c’est qu’il faudrait ajou­ter une adresse dédiée à chaque fois que l’on crée une nouvelle file. Ça peut vite deve­nir usant.

On va donc utili­ser le plus addres­sing. Cette tech­nique, toute bête, consiste à ajou­ter un discri­mi­nant à une adresse mail, précédé géné­ra­le­ment d’un + (mais on peut confi­gu­rer son serveur de mail pour utili­ser n’im­porte quel carac­tère). rt@example.org aura alors pour alias (par exemple) rt+file_bidule@example.org.

Pour que RT puisse utili­ser cette tech­nique, il faut ajou­ter --extension=queue dans la commande du /etc/aliases :

rt:         "|/opt/rt4/bin/rt-mailgate --extension=queue --queue general --action correspond --url https://rt.example.org/"
rt-comment: "|/opt/rt4/bin/rt-mailgate --extension=queue --queue general --action comment --url https://rt.example.org/"

Voilà ! Il ne vous reste plus qu’à créer vos files via l’in­ter­face web. Atten­tion, créez-les avec un nom qui passera dans une adresse mail. Pas d’es­pace par exemple.

RT récu­pé­rera le nom de la file kiva­bien dans la partie entre le + et le @ et placera auto­ma­tique­ment le ticket dans cette file, tout en gardant la file general par défaut.

Les articles

Quoi de plus casse-pieds que de se répé­ter encore et encore en donnant toujours la même réponse ? Heureu­se­ment il y a les articles qui peuvent vous servir de réponses pré-enre­gis­trées :-)

Créa­tion des classes et des articles

Allez dans le menu Administration > Articles > Classes > Ajouter, créez vos classes (j’en ai créé une par file, n’ayant pas réussi à assi­gner auto­ma­tique­ment des articles aux files), cochez Tous les articles de cette classe doivent être disponibles sous forme de liste sur la page de réponse d'un ticket, déco­chez Inclure le résumé de l'article et Inclure le nom de l'article et cochez Include le champs personnalisé 'Content' > Value (oh la belle typo de traduc­tion) qui appa­raî­tra après avoir enre­gis­tré la classe (pour ces trois derniers, vous faites comme vous le sentez hein).

Liez la classe à une file via le menu S'applique à.

Voilà, vous n’avez plus qu’à créer vos articles dans la classe que vous venez de créer via le menu Articles > Ajouter.

Et là, magie, lorsque vous répon­dez via l’in­ter­face web, vous pour­rez choi­sir une réponse pré-enre­gis­trée.

Place­ment des articles dans la réponse

Je suis un grand fan du bottom-post­ing, mais RT place l’ar­ticle au-dessus de la cita­tion du message précé­dent. Remé­dions à cela.

cd /opt/rt4
mkdir -p local/html/Elements/
cp share/html/Elements/MessageBox local/html/Elements/
vi local/html/Elements/MessageBox

Cher­chez la ligne conte­nant

% $m->comp('/Articles/Elements/IncludeArticle', %ARGS) if $IncludeArticle;

et rempla­cez-la par

% if ($IncludeArticle) {
%    my $article = $m->scomp('/Articles/Elements/IncludeArticle', %ARGS);
%    $article    =~ s{\n}{<br />}g;
%    $article    = RT::Interface::Email::ConvertHTMLToText($article);
%    $Default   .= $article unless ($Default =~ s/(.*)(-- .*)/$1$article$2/m);
% }

Hop ! votre article se trouve main­te­nant entre la cita­tion et votre signa­ture :-)

(un redé­mar­rage de RT est peut-être néces­saire pour que cela soit pris en compte)

Ajout des articles perti­nents dans le mail de noti­fi­ca­tion d’un nouveau message

Une des forces de RT est de permettre aux inter­ve­nants de répondre aux tickets par mail. Le problème est que cela empêche de piocher dans les réponses pré-enre­gis­trées.

Qu’à cela ne tienne, ajou­tons-les au mail de noti­fi­ca­tion envoyé aux membres du support.

Allez dans Administration > Global > Modèles > Choisir. Il faut modi­fier le modèle Notification de modification HTML (oui, j’ai traduit le nom de mes modèles, mais il est simple à repé­rer, il est utilisé par les scrips 8 et 11).

Ajou­tez ceci en bas du modèle :

{ my $hotlist = RT::Articles->new( RT->SystemUser );
  $hotlist->LimitHotlistClasses;
  $hotlist->LimitAppliedClasses( Queue => $Ticket->QueueObj );
  my $content   = "-- \n<p><b>Réponses pré-enregistrées pour cette catégorie de tickets:</b></p>";

  if ($hotlist->Count) {
    while (my $article = $hotlist->Next) {
      $content .= '<p><b>'.$article->Name.'</b><br/>';
      my $class   = $article->ClassObj;
      my $cfs     = $class->ArticleCustomFields;
      my %include = (Name => 1, Summary => 1);
      $include{"CF-Title-".$_->Id} = $include{"CF-Value-".$_->Id} = 1 while $_ = $cfs->Next;
      $include{$_} = not $class->FirstAttribute("Skip-$_") for keys %include;

      while (my $cf = $cfs->Next) {
        next unless $include{"CF-Title-".$cf->Id} or $include{"CF-Value-".$cf->Id};
        my $values = $article->CustomFieldValues($cf->Id);
        if ($values->Count == 1) {
          my $value = $values->First;
          if ($value && $include{"CF-Value-".$cf->Id}) {
            $content .= '<br/>';
            my $c     = $value->Content || $value->LargeContent;
            $c =~ s/\r?\n/<br\/>/g;
            $content .= $c;
          }
        } else {
          my $val = $values->Next;
          if ($val && $include{"CF-Value-".$cf->Id}) {
            $content .= '<br/>';
            my $c     = $value->Content || $value->LargeContent;
            $c =~ s/\r?\n/<br\/>/g;
            $content .= $c;
          }
          while ($val = $values->Next) {
            if ($include{"CF-Value-".$cf->Id}) {
              $content .= '<br/>';
              my $c     = $value->Content || $value->LargeContent;
              $c =~ s/\r?\n/<br\/>/g;
              $content .= $c;
            }
          }
        }
      }
      $content .= "<br/>-----------</p>\n";
    }
  }
  $content;
}
{$content}

C’est moche et long, je sais. Dites-vous que j’ai passé plus d’une après-midi pour trou­ver ça, la docu­men­ta­tion est inexis­tante pour faire ça.

Les inter­ve­nants n’au­ront plus qu’à copier-coller l’ar­ticle qui se trouve au bas de leur mail de noti­fi­ca­tion dans leur réponse :-)

Commandes par mail

C’est beau de répondre par mail, mais il faut encore se connec­ter à l’in­ter­face web pour effec­tuer certaines actions. Comme je suis fier d’être fainéant, j’ai créé un scrip pour auto­ri­ser certaines actions par mail.

Mais avant ça, préci­sions :

  • RT permet aux inter­ve­nants de discu­ter du ticket sans que cela soit vu par le créa­teur du ticket : c’est le but de l’adresse rt-comment@example.org du début de l’ar­ticle. On va utili­ser cette adresse pour pilo­ter RT par mail
  • un scrip est une action effec­tuée par RT en réponse à un évène­ment, en utili­sant de façon option­nelle un modèle. Typique­ment, il y a un scrip qui envoie (action) un mail (d’après un modèle) aux membres du support lorsqu’un ticket est créé (évène­ment).

Créons donc un scrip. Menu Administration > Scrips > Ajouter.

  • Condi­tion (évène­ment) => Lors d’un commen­taire
  • Action => défi­nie par l’uti­li­sa­teur
  • Modèle => Modèle vide

Dans le Programme de préparation d'action personnalisé: :

if ($self->TransactionObj->Attachments->First->Content =~ m/#(JePrends|Fermeture|Spam)/i) {
    return 1;
} else {
    return 0;
}

Oui, j’au­rais pu faire un one-liner, mais il faut que ça reste lisible faci­le­ment, et quand on passe des heures à faire des bidouilles comme ça, on appré­cie les codes lisibles en un coup d’œil.

Dans le Code d'action personnalisée (commit): :

if ($self->TransactionObj->Attachments->First->Content =~ m/#JePrends/i) {
    if ( $self->TicketObj->OwnerAsString eq '' ) {
        my $id = $self->TransactionObj->Creator;
        $RT::Logger->info("Setting owner to ".$id);
        $self->TicketObj->SetOwner($id, 'SET');
    }
} elsif ($self->TransactionObj->Attachments->First->Content =~ m/#Fermeture/i) {
    $RT::Logger->info("Closing ticket");
    $self->TicketObj->SetStatus('resolved');
} elsif ($self->TransactionObj->Attachments->First->Content =~ m/#Spam/i) {
    my $ticket = $self->TicketObj;
    my ($status, $msg) = $ticket->SetStatus('rejected');
    $RT::Logger->error("Couldn't delete ticket: $msg") unless $status;

    my $requestors = $ticket->Requestor->UserMembersObj;
    while (my $requestor = $requestors->Next) {
        $requestor->SetDisabled(1);
        $RT::Logger->info("Disabling user ".$requestor->Format." because he's likely a spammer");
    }
}

return 1;

Voilà, enre­gis­trez et c’est bon.

Lorsqu’un commen­taire contien­dra une commande, elle sera exécu­tée :

  • #JePrends => l’in­ter­ve­nant s’as­signe le ticket
  • #Fermeture => le ticket est marqué comme résolu
  • #Spam => le ticket est supprimé et son auteur ne pourra plus ouvrir de tickets, son adresse mail sera black­lis­tée

Et le spam alors ?

Pour le spam, prépa­rez d’abord un spamassassin pour votre serveur de mails. Ce n’est pas l’objet de cet article, il n’y a qu’à fouiller un peu le web pour trou­ver des tutos.

On va recréer un scrip, mais avant cela on va créer une nouvelle file nommée spam (menu Administration > Files > Ajouter).

Pour notre nouveau scrip :

  • Condi­tion (évène­ment) => Lors d’une créa­tion
  • Action => défi­nie par l’uti­li­sa­teur
  • Modèle => Modèle vide

Dans le Programme de préparation d'action personnalisé: :

if ( $self->TicketObj->Subject !~ /\[ .* \]/i ) {
  my $inMessage = $self->TransactionObj->Attachments->First;

  # if no message attachment - assume web UI
  return 0 if (!$inMessage);

  # exit if not email message
  return 0 if (!$inMessage->GetHeader('Received'));

  return ($inMessage->GetHeader('X-Spam-Level') =~ m/\*+/) ? 1 : 0;
} else {
  return 1;
}

Dans le Code d'action personnalisée (commit): :

my $spamlevel = $self->TransactionObj->Attachments->First->GetHeader('X-Spam-Level');
if ($spamlevel =~ m/\*\*\*+/) {
  if ($spamlevel =~ m/\*\*\*\*\*/) {
    $RT::Logger->info("This mail seems to be a spam => deleting");
    $self->TicketObj->Delete();
  } else {
    $RT::Logger->info("This mail seems to be a spam => queue spam");
    $self->TicketObj->SetQueue('spam');
  }
}
return 1;

Avec cela, les mails ayant un score de 5 ou plus au spamLe­vel seront suppri­més, et ceux qui ont entre 3 et 5 vont au purga­toire, dans la file spam.

Prenez soin de dépla­cer ce scrip tout en haut de la liste pour qu’il soit le premier exécuté.

Plugins

En vrac, les plugins que j’uti­lise :

Les deux premiers sont main­te­nant inté­grés à RT, il n’y a pas besoin de les instal­ler, juste de les confi­gu­rer. Ils servent respec­ti­ve­ment à assu­rer l’au­then­ti­fi­ca­tion LDAP à l’in­ter­face web, et à impor­ter en masse les comptes du LDAP pour permettre à l’ad­mi­nis­tra­teur de mettre les colla­bo­ra­teurs dans les bons groupes sans attendre qu’ils se soient logués une première fois.

Le dernier plugin ajoute un S dans le menu des tickets, permet­tant de les décla­rer comme spam d’un simple clic.

Conclu­sion

On peut faire de merveilleuses choses avec RT, pour peu que l’on ait le temps de fouiller dans la docu­men­ta­tion (offi­cielle ou non)… et dans le code !

Une fois bien confi­guré, il devrait permettre d’al­lé­ger la charge de travail du groupe de support et je peux vous dire que pour en faire depuis plus de deux ans pour Frama­soft et bien plus pour mon ancien boulot, ce n’est pas quelque chose à négli­ger :-)

NB : bien évidem­ment, ce superbe logi­ciel est en Perl :D

Crédits de la photo d’illus­tra­tion : CC-BY GotC­re­dit, trou­vée sur Flickr

Parta­ger

by Luc at May 10, 2016 06:51 PM

December 22, 2013

Alexandre Bailly

SteamOS

Logo SteamOS

Tiens, comme c'est dimanche, si je testais SteamOS ?

Edit : Le tutoriel détaillé pour installer SteamOS en 2015 est présent ici : http://blog.chibi-nah.fr/tuto-installation-steamos



SteamOS ?

Il s'agit d'une distribution dérivée de Debian, créée par Valve Software (Half-Life, Steam, Portal, L4D…), et destinée à fonctionner sur les machines appelées "SteamBox". À noter, n'importe qui peut monter sa propre "SteamBox".

Installation.

J'avais une partition de 9 Go inutilisée, et une distrib debian sid sous la main.
Ne souhaitant pas suivre les instructions "officielles", car trop pénibles (nécessite un UEFI, un disque de 500 Go, une clé usb externe, effacera la totalité du disque dur, pas de dual boot, support des cartes nvidia uniquement…[1], faisons ça à la main, de manière artisanale.
Donc, utilisation de debootstrap, chroot, compilation d'un kernel 3.12.6 (j'en ai profité pour mettre à jour mon kernel sous sid dans la foulée), et création des paquets .deb.
Installation des paquets, configuration de grub sur la partition sda7, mise à jour de mon grub "principal", puis reboot.

Et là, c'est le drame.

Kernel panic ! Unable to mount root.

Bon, reboot sous sid, et regardons ce qui cloche.

Premier point, je compile mes noyaux sans initrd. Ce truc là sert à rien quand on compile ses propres noyaux, vu que les pilotes requis, genre, ext4, sont compilés en dur et non en tant que module (où est l'intérêt de les mettre en module ? faut monter une image intermédiaire qui contient le module pour monter la partition root pour charger les modules… bref).

Second point, cette andouille de GRUB, malgré l'option ROOT-UUID = false, a réussi à mettre un root=uuid=<UUID de la partition>. Pour que l'UUID soit reconnu, il faut charger l'initrd, qui va alors chercher quelle partition correspond à l'UUID passé en paramètre, puis va corriger la partition root. Encore un truc tordu écrit par je ne sais qui, et dont l'utilité est discutable. Re bref.

Je remet à jour grub, via update-grub (sachant qu'au départ, ce qui m'a fait basculer de lilo à grub, c'est la nécessité de mettre à jour lilo à chaque modif et que grub, suffisait d'enregistrer le fichier. Désormais, on change un paramètre de grub, faut lancer une commande qui va générer un fichier de config totalement incompréhensible. Cf un ancien article).

Cette fois, je vérifie que le grub.cfg ne contient pas d'UUID.

C'est bon ? On reboot ?

Qui a dit : pas de multiboot ?

Un redémarrage plus tard, arrivée sur Debian Wheezy SteamOS, suivi de Login:.

Bien.

Tiens, définissons un mot de passe root, c'est toujours utile.

Les utilisateurs et les dépots valve ont été configurés lors de l'installation, reste à finir les 2-3 trucs qui reste, genre pulse-audio, drivers…

Démarrage de lightdm, et … plaf ! Écran noir.

Vive openSSH

Connexion en urgence via ssh (une excellente idée de l'installer avant toute chose, au cas où), kill de lightdm, et constatation des dégats. Lightdm n'a pas arrêté de faire "start-error-respawn-error-respawn-error-respawn", en boucle. Suffisamment de quoi générer 10 Mo de log en 30 secondes.

Démarrage de X via startx, et arrivée sur le bureau Gnome 3.4, en mode "classique" (ou failsafe).
Steam demandant glx, vérifions que déjà, je suis sur la radeon (ah, tiens ? j'ai laissé DIGD au lieu de DDIS dans rc.local, corrigeons ça). Arrêt de gnome et X, basculons sur la radeon (sudo echo DDIS > /sys/kernel/debug/vgaswitcheroo/switch), relançons X, et… toujours pas de glx (glxinfo : pas de DRI, pas de DRM, rien).

Bon, ajoutons debian wheezy en apt-pinning, puis mettons à jour les dépots.

Tant que j'y suis, ajoutons les paquets de base, genre htop, vim, mc, et de quoi configurer alsa.

Bizarre, j'ai bien le driver radeon libre dans les dépots steam, mon kernel est le même que sur sid, et j'ai pas de problèmes pour faire tourner UT2004 dessus. Doit manquer un paquet ou deux. Installons alors mesa, et tout ce qui concerne DRI-DRM et radeon.
Effectivement, il manquait quelques paquets.

La liste complète de tous les paquets installés. Certains ne sont pas disponible dans les dépots de valve ni de debian. Il s'agit des paquets liés au linux-*, spécifiques à ma machine.
À noter, j'ai commencé à faire le ménage dans les paquets, mais il reste des trucs à virer, genre, apache.

Ooooh, le beau troll gnome

Cette fois, gnome démarre en mode "normal", et me montre un beau bureau (/me tousse), en précisant bien que c'est la radeon qui tourne. glxinfo montre que DRI est bien actif, et également plein plein d'extensions GL*. Ça semble fonctionner.
Démarrons alors steam en mode "desktop". Ça marche.
Réactivons alors lightdm, un reboot et une mise à jour steam plus tard, voilà le mode "Big Picture". Entrons les informations de connexion, et explorons les menus.

Démarrage de SteamOS.
Animation du démarrage de Steam.
Steam, en mode Big Picture
Paramètres.
Et voilà, SteamOS avec une radeon. C'était pas si compliqué.
SteamOS avec le pilote radeon libre \o/

Quelques trucs vides ou encore en test, rien de grave. Quelques bugs bien pénibles, comme un crash de l'overlay steam (pas moyen d'en sortir, obligé de tuer steam via un tty), ou le redémarrage de steam quand on sort d'un jeu, et… pas de son.
C'est le moment de configurer correctement alsa, donc je commencer par jarter pulse-audio. Tant que la base ne marche pas, ça sert à rien de bidouiller la surcouche. Donc, dans /etc/default/pulse-audio, désactivons son démarrage auto, puis arrêt du service, /etc/init.d/pulse-audio stop.

Après une heure d'acharnement et de lecture de doc (mais pourquoi ça marche avec root mais pas avec les utilisateurs), je m'aperçois que c'est simplement le groupe "pulse" qu'il faut utiliser en plus d'audio ¬_¬'. Hop, usermod -aG pulse steam.

Reboot, et cette fois, j'ai le son. Je crois que j'ai oublié de réactiver pa. Je verrai ça plus tard.

Plus qu'à tester

Faisons quelques tests, en évitant d'appeler l'overlay en pleine partie, et ça marche. Musique, vidéo, interface "relativement" fluide (avec le driver radeon libre), et le petit plus : SteamOS fonctionnel avec une radeon, alors que ce n'est pas (encore) supporté.

Télécharger au format webm, 640x360, lisible par vlc

1 : http://store.steampowered.com/steamos/buildyourown

by nah at December 22, 2013 10:07 PM

September 17, 2015

Florent Peterschmitt

journald: taggle, rsyslog: je t’aime toujours

Dans mon précédent billet, j’avais défragmenté /var/log/journald pour récupérer un temps de démarrage correct avec journald.

Maintenant, j’ai récupéré un temps de démarrage normal en le dégageant :

Un petit coup de man journald.conf, puis édition de /etc/systemd/journald.conf :

[Journal]
Storage=none
ForwardToSyslog=yes

On installe, active et démarre rsyslog (ou syslog-ng, peu importe), et on redémarre journald :

systemctl enable rsyslog
systemctl restart rsyslog
systemctl restart systemd-journald

Voilà qui est fait.

C’est un contournement, mais je m’en fout, je n’utilise pas journald sur ma bécane.

Si on veut pouvoir conserver journald, mais pour la durée de vie de l’uptime en cours, volatile pourra être utilisé à la place de none pour l’option Storage.

by Florent Peterschmitt at September 17, 2015 08:08 PM

September 15, 2013

Florent Peterschmitt

Un cluster MongoDB avec redondance et répartition de charge

Buts de la réplication avec MongoDB

  • Assurer la résistance à la panne d’au moins une machine.
  • Permettre des accès en lecture plus rapides.
  • Dans un environnement géographiquement étendu, réduit les latences réseau si l’application utilisant la base n’a pas besoin des informations de dernière date.

Choix d’architecture

Le premier (et nécessaire pour le second) est une architecture simple avec un maître (primaire) et des esclaves (secondaires).

Les écritures sont contrôlées par le primaire et retransmises aux secondaires (qui peuvent être plus que deux).

C’est l’architecture de « réplicats » :

               Clients
               Driver
                 ||
                 ||
              Primaire                 }
        /-----^      ^-----\           } base mongodb
Réplica1                    Réplica2   }

Le problème de cette architecture étant la répartition de la charge sur de grosses bases. Si une base subit plus de transactions que la machine ne peut en satisfaire (limitation des disques, CPU…), il devient intéressant d’utiliser une architecture en « shards » :

                            Clients
                            Driver
                              ||
                              ||
                            Routeur_________
                               |            \_____Serveurs de configuration (>1)
                      _________|____________/
                     |      |     |      |           }
                     |      |     |      |           } base mongodb
                    Shard Shard Shard  Shard (>0)    }

Le routeur se chargera de communiquer avec les serveurs de configuration pour pouvoir trouver les données et les donner aux bons shards.

Les shards peuvent être des bases mongo seules mais seront plutôt des réplicats.

En production, MongoDB recommande d’utiliser trois serveurs de configuration pour assurer la continuité de service. Le choix du nombre de shards est une science que je n’ai pas encore apprise.

Pour se connecter à une base MongoDB, nous devons utiliser un « driver » qui est en fait une brique logicielle qui nous utiliserons dans toute application voulant utiliser MongoDB. J’ai choisi le driver en Python pour réaliser les tests.

L’architecture en shards est ce qu’on pourrait appeler une extension bigrement essentielle des réplicats et permettent de distribuer le travail au travers de multiples machines.

Environnement de travail

Trois machines virtuelles hébergeront chacune une instance de mongod, une base MongoDB.

Machines :

  • Debian sid
  • MongoDB 2.4.5
  • Heartbeat 3.0.5 installé et configuré. Fichier haresourses vide.

Réseau :

  • Réseau privé 192.168.56.0/24
  • hôte : .1
  • mongodb0 : .2
  • mongodb1 : .3
  • mongodb2 : .4

Faire

Nous allons voir comment mettre en place un « replica set », la réplication simple avec la répartition de charge que peut offrir les secondaires en lecture.

1. Configurer Heartbeat sur toutes les machines

/etc/ha.d/ha.cf

bcast eth1
deadtime 5
keepalive 2
mcast eth0 239.0.0.1 694 1 0
node mongodb2.localhost mongodb1.localhost mongodb0.localhost

/etc/ha.d/authkeys

auth 1
1 md5 cacc670d1040becee271a4be61cf303d

/etc/ha.d/haresources

-vide-

2. Configurer les DNS

Nous devons avoir des hôtes suivant ce schéma :

  • mongodb0.localhost # primaire
  • mongodb1.localhost # secondaire1
  • mongodb2.localhost # secondaire2

3. Sur chaque machine

Configurer la base MongoDB :

$EDITOR /etc/mongodb.conf
: replSet = rs0

replSet sert à indiquer à notre base à quel « replica set » elle appartient. Le nom est donc arbitraire mais doit être identique pour toutes les bases voulant appartenir au même groupe, évidemment.

Lancer la base MongoDB si ce n’est pas déjà fait (Debian prend soin de tout lancer à notre place, même quand on ne le veut pas…).

$ service mongodb start

4. Sur la machine maître, ou primaire

S’y connecter :

$ mongo

Initier la réplication :

> rs.initiate()
{
    "info2" : "no configuration explicitly specified -- making one",
    "me" : "mongodb0.localhost:27017",
    "info" : "Config now saved locally.  Should come online in about a minute.",
    "ok" : 1
}

MongoDB va générer une configuration pour la réplication, nous pouvons l’afficher :

> rs.config()
{
    "_id" : "rs0",
    "version" : 1,
    "members" : [
        {
            "_id" : 0,
            "host" : "mongodb0.localhost:27017"
        }
    ]
}

Ajouter les secondaires :

> rs.add('mongodb1.localhost')
{ "ok" : 1 }
> rs.add('mongodb2.localhost')
{ "ok" : 1 }

Statut de la réplication (des champs ont été retirés) :

mongodb0 :

rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2013-09-14T15:00:44Z"),
    "myState" : 1,
    "members" : [
            {
                    "_id" : 0,
                    "name" : "mongodb0.localhost:27017",
                    "stateStr" : "PRIMARY",
                    "self" : true
            },
            {
                    "_id" : 1,
                    "name" : "mongodb1.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "syncingTo" : "mongodb0.localhost:27017"
            },
            {
                    "_id" : 2,
                    "name" : "mongodb2.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "syncingTo" : "mongodb0.localhost:27017"
            }
    ],
    "ok" : 1
}

Un peut d’attente est nécessaire pour que l’élection se fasse, de l’ordre de quelques secondes. Nous voyons que mongodb1 et mongodb2 se synchronisent sur mongodb0, car c’est le primaire.

Les serveurs secondaires doivent voir chacun de leurs copains :

mongodb1 :

rs0:SECONDARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2013-09-14T15:03:06Z"),
    "myState" : 2,
    "syncingTo" : "mongodb0.localhost:27017",
    "members" : [
            {
                    "_id" : 0,
                    "name" : "mongodb0.localhost:27017",
                    "stateStr" : "PRIMARY",
                    "pingMs" : 3
            },
            {
                    "_id" : 1,
                    "name" : "mongodb1.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "self" : true
            },
            {
                    "_id" : 2,
                    "name" : "mongodb2.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "pingMs" : 1,
                    "syncingTo" : "mongodb0.localhost:27017"
            }
    ],
    "ok" : 1
}

Statut des bases à ce stade :

  • mongodb0 -> primaire
  • mongodb1 -> secondaire
  • mongodb2 -> secondaire

5. Tester l’élection

Coupons le primaire (mongodb0) (brutalement ou pas) :

mongodb1 :

rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2013-09-14T15:14:53Z"),
    "myState" : 1,
    "members" : [
            {
                    "_id" : 0,
                    "name" : "mongodb0.localhost:27017",
                    "stateStr" : "(not reachable/healthy)",
            },
            {
                    "_id" : 1,
                    "name" : "mongodb1.localhost:27017",
                    "stateStr" : "PRIMARY",
                    "errmsg" : "db exception in producer: 10278 dbclient error communicating with server: mongodb0.localhost:27017",
                    "self" : true
            },
            {
                    "_id" : 2,
                    "name" : "mongodb2.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "syncingTo" : "mongodb1.localhost:27017"
            }
    ],
    "ok" : 1
}

Le champs “stateStr” nous indique que mongodb0 n’est plus existante. Aussi, un nouveau primaire a été élu, il s’agit de mongodb1. De ce fait, mongodb2 se synchronise sur mongodb1. On pourra voir –presque– la même chose sur la machine restée secondaire.

Redémarrons maintenant l’ancienne machine primaire (mongodb0) :

mongodb1 :

rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2013-09-14T15:15:44Z"),
    "myState" : 1,
    "members" : [
            {
                    "_id" : 0,
                    "name" : "mongodb0.localhost:27017",
                    "stateStr" : "SECONDARY",
                    "syncingTo" : "mongodb1.localhost:27017"
            }

Nous voyons maintenant que la base mongodb0 se synchronise avec la nouvelle machine primaire mongodb1.

Désormais, nous avons :

  • mongodb0 -> secondaire
  • mongodb1 -> primaire
  • mongodb2 -> secondaire

6. Tester la réplication

J’ai réalisé un petit script pour faciliter les essais. Le script écrit et lit dans la base « test » et la collection « posts ».

#!/usr/bin/env python

import datetime
import time
import signal
import argparse
import inspect
import sys

import pymongo

class MongoDBTest:
    CONTW = True

    def sigtrap_int(self, signum, frame):
        MongoDBTest.CONTW = False

    def __init__(self, readsec, servers=[
        'mongodb0.localhost:27017',
        'mongodb1.localhost:27017',
        'mongodb2.localhost:27017']):

        self.client = pymongo.MongoReplicaSetClient(
                'mongodb://{}/'.format(','.join(servers)),
                replicaSet='rs0',
                connectTimeoutMS=1000,
                socketTimeoutMS=1000)

        self.db = self.client['test']
        rpref = pymongo.read_preferences.ReadPreference
        if readsec:
            # read from secondaries
            self.db.read_preference = rpref.SECONDARY
        else:
            self.db.read_preference = rpref.PRIMARY

        signal.signal(signal.SIGINT, self.sigtrap_int)

    def insert(self):
        date = datetime.datetime.now()
        insert_post = {'date': date.strftime('%H:%M:%S')}
        self.db.posts.insert(insert_post)

    def remove(self, oid=None):
        if not oid:
            oid = self.db.posts.find_one()['_id']
        self.db.posts.remove(oid)

    def printall(self):
        header = '#### {} : {} | Secondaries: {}Master: {} ####\n'
        poststr = 'id: {} | date: {}\n'

        secs = self.db.connection.secondaries
        secstr = ''
        primary = 'None'
        try:
            primary = self.db.connection.primary[0]
        except TypeError:
            pass
        posts = self.db.posts.find()
        date = datetime.datetime.now().strftime('%H:%M:%S')
        caller = inspect.stack()[1][3]
        for sec in secs: secstr += str(sec[0]) + ' '

        sys.stdout.write(header.format(date, caller, secstr, primary))
        for post in posts:
            sys.stdout.write(poststr.format(post['_id'], post['date']))
        sys.stdout.flush()

    def drop(self):
        for post in self.db.posts.find():
            self.remove(post['_id'])

    def fill(self, count):
        if count < 1:
            count = 1
        for i in range(0, count):
            self.insert()

    ############## TESTS ##############

    def test_fill_roll(self):
        self.remove()
        self.insert()

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--init', dest='init', action='store_true')
    parser.add_argument('--add', dest='add', action='store_true')
    parser.add_argument('--show', dest='show', action='store_true')
    parser.add_argument('--test-fill-roll', dest='test_fr', action='store_true')
    parser.add_argument('--read-sec', dest='readsec', action='store_true',
            default=False)
    parser.add_argument('--forever', dest='forever', action='store_true',
            default=False)
    args = parser.parse_args()

    mtest = MongoDBTest(args.readsec)

    while MongoDBTest.CONTW:
        try:
            if args.init:
                mtest.drop()
            if args.add:
                mtest.fill(2)

            if args.test_fr:
                mtest.test_fill_roll()

            if args.show:
                mtest.printall()

            if not args.forever:
                break
            else:
                time.sleep(1)

        except pymongo.errors.AutoReconnect:
            sys.stderr.write('Cannot auto-reconnect\n')
            time.sleep(1) # should not exists, but 20% cooler for tests
        except pymongo.errors.ConnectionFailure:
            sys.stderr.write('Cannot connect to a master\n')
            time.sleep(1) # should not exists, but 20% cooler for tests
        except ConnectionRefusedError:
            pass
            time.sleep(1) # should not exists, but 20% cooler for tests
        except AssertionError:
            pass
            time.sleep(1) # should not exists, but 20% cooler for tests
        finally:
            sys.stderr.flush()

if __name__ == '__main__':
    main()

Vider la base y écrire des données et les afficher (lecture depuis le primaire) :

python mongotest.py --init --add --show

#### 19:15:09 : main | Secondaries: mongodb1.localhost mongodb2.localhost Master: mongodb0.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09

Éteindre le nœud primaire, attendre l’élection d’un nouveau primaire et tenter de lire les données. Elles doivent être identiques.

python mongotest.py --show

#### 19:16:46 : main | Secondaries: mongodb1.localhost Master: mongodb2.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09

Ajouter des données et vérifier leur présence :

python mongotest.py --add --show

#### 19:17:15 : main | Secondaries: mongodb1.localhost Master: mongodb2.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09
id: 5235eb9abb45f956252e2a26 | date: 19:17:14
id: 5235eb9bbb45f956252e2a27 | date: 19:17:15

Rallumez le précédent nœud (mongodb0), attendez sa synchronisation puis éteignez le nouveau nœud primaire (mongodb2), les données du premier et second ajout (—add) doivent être présentes :

python mongotest.py --show

#### 19:21:22 : main | Secondaries: mongodb1.localhost Master: mongodb0.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09
id: 5235eb9abb45f956252e2a26 | date: 19:17:14
id: 5235eb9bbb45f956252e2a27 | date: 19:17:15

Voyons ce qu’il se passe quand nous voulons écrire, puis lire depuis les secondaires :

python mongotest.py --test-fill-roll --show --forever --read-sec

#### 17:56:22 : test_fill_roll | Secondaries: mongodb2.localhost Master: mongodb0.localhost ####
id: 5235d8a3bb45f943f54f71dd | date: 17:56:19
id: 5235d8a4bb45f943f54f71de | date: 17:56:20
id: 5235d8a5bb45f943f54f71df | date: 17:56:21
#### 17:56:23 : test_fill_roll | Secondaries: mongodb2.localhost Master: mongodb0.localhost ####
id: 5235d8a4bb45f943f54f71de | date: 17:56:20
id: 5235d8a5bb45f943f54f71df | date: 17:56:21
id: 5235d8a6bb45f943f54f71e0 | date: 17:56:22
id: 5235d8a7bb45f943f54f71e1 | date: 17:56:23

test-fill-roll supprime le premier enregistrement (le plus vieux) et ajoute un enregistrement immédiatement après. Une pause d’une seconde est faite entre chaque.

On voit ici qu’un insertion n’a pas eu le temps de percer jusqu’au secondaire et il se trouve que nous avons lu depuis ce secondaire, d’où un enregistrement manquant. Quand on lit depuis le primaire, nous n’avons pas ce problème, les données reçues sont celles écrites récemment.

Amusons nous à couper le maître, par exemple. Ici une extinction brutale de la VM ne posera pas de problème, mais on peut aussi le faire plus doucement avec l’ACPI.

Nous obtiendrons cela, par exemple :

python mongotest.py --test-fill-roll --show --forever

#### 18:37:59 : main | Secondaries: mongodb2.localhost mongodb0.localhost Master: mongodb1.localhost ####
id: 5235e264bb45f94e2c774f1c | date: 18:37:56
id: 5235e265bb45f94e2c774f1d | date: 18:37:57
id: 5235e266bb45f94e2c774f1e | date: 18:37:58
id: 5235e267bb45f94e2c774f1f | date: 18:37:59
Cannot auto-reconnect
Cannot auto-reconnect
Cannot auto-reconnect
Cannot auto-reconnect
Cannot auto-reconnect
Cannot auto-reconnect
Cannot auto-reconnect
#### 18:38:11 : main | Secondaries: mongodb2.localhost Master: mongodb0.localhost ####
id: 5235e265bb45f94e2c774f1d | date: 18:37:57
id: 5235e266bb45f94e2c774f1e | date: 18:37:58
id: 5235e267bb45f94e2c774f1f | date: 18:37:59
id: 5235e273bb45f94e2c774f20 | date: 18:38:11

Le temps pendant lequel nous ne pouvons pas nous connecter dure quelques secondes, dépendant de la configuration Heartbeat.

7. L’accès en lecture seule

Très simple, lancer les trois bases et le test de lecture seule, puis éteindre le primaire :

python mongotest.py --show --forever --read-sec

#### 20:34:55 : main | Secondaries: mongodb0.localhost mongodb2.localhost Master: mongodb1.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09
id: 5235eb9abb45f956252e2a26 | date: 19:17:14
id: 5235eb9bbb45f956252e2a27 | date: 19:17:15
#### 20:34:56 : main | Secondaries: mongodb2.localhost Master: mongodb0.localhost ####
id: 5235eb1dbb45f954fea1a8ec | date: 19:15:09
id: 5235eb1dbb45f954fea1a8ed | date: 19:15:09
id: 5235eb9abb45f956252e2a26 | date: 19:17:14
id: 5235eb9bbb45f956252e2a27 | date: 19:17:15

Aucune action d’écriture n’est réalisée dans cet essai. Pendant le temps où aucun primaire n’a été ré-élu, on a continué à lire les donnés présentes et la reprise s’est faite sans aucun problème. Pas de coupure non plus lors de l’extinction du primaire.

Conclusion

La réplication avec MongoDB est presque magique, après quelques heures de documentation et essais on arrive à une installation pleinement fonctionnelle.

J’ai préféré mettre l’accent sur des démonstrations plutôt que de me lancer dans une architecture à shards, bien que le principale soit fait avec les réplicats. À suivre dans un prochain épisode.

À propos de ce qui est fait :

  • La configuration est très simple, j’ai même cru à un piège concernant Heartbeat.
  • Viandage au début, écrire sur la base « local » n’est pas une bonne idée car contenant la configuration de la base…
  • La reprise de service (élection d’un nouveau primaire) est tout de même longue : 10 secondes environ.

À faire/tester :

  • Éloignement géographique.
  • Sauvegarder une base MongoDB.
  • Les bases en « shards », avec les routeurs et serveurs de configuration.
  • Comparer avec d’autres bases, comme CouchDB par exemple, qui propose une réplication à double sens.

Une petite vidéo (qui vaut le coup d’installer flash) : http://www.youtube.com/watch?v=jyx8iP5tfCI

by Florent Peterschmitt at September 15, 2013 07:10 PM