first commit

This commit is contained in:
VC
2025-02-27 12:51:50 +01:00
commit f5e914e533
234 changed files with 11519 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
public/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "themes/terminimal"]
path = themes/terminimal
url = https://github.com/pawroman/zola-theme-terminimal.git

0
README.md Normal file
View File

46
config.toml Normal file
View File

@@ -0,0 +1,46 @@
# The URL the site will be built for
base_url = "https://blog.libertus.eu"
title = "Ad majorem lulzis gloriam"
description = "Vomito ergo sum"
default_language = "fr"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = true
generate_feeds = true
theme = "terminimal"
taxonomies = [
{name = "tags", feed = true}, # each tag will have its own feed
]
# Configuration of the Markdown rendering
[markdown]
highlight_code = true
highlight_theme = "base16-ocean-dark"
[extra]
author = "Hylobates Agilis"
generate_feeds = true
## terminimal
accent_color = "red"
logo_text = "Ad majorem lulzis gloriam"
copyright_html = '<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a>'
post_view_navigation_prompt = "Encore soif?"
# menu is enabled by adding menu_items (optional)
menu_items = [
# each of these is optional, name and url are required
# $BASE_URL is going to be substituted by base_url from configuration
{name = "blog", url = "$BASE_URL"},
{name = "tags", url = "$BASE_URL/tags"},
]

View File

@@ -0,0 +1,72 @@
+++
title = "IPv6 dans la pratique (partie 1)"
date = 2009-12-11
aliases = [ "post/2009/12/03/IPv6-dans-la-pratique-(partie-1)"]
[taxonomies]
tags = [ "ipv6" ]
+++
rézo : n.m.
1. Ensemble de lutins qui courent très vite dans les fils pour acheminer des petits paquets avec du pr0n dedans.
<!-- more -->
C'est officiel depuis un certain temps déjà : les quelques 4 milliards de minous BOUZINv4 disponibles sur Internénette seront bientôt épuisées. Le protocole BOUZINv4 n'a pas été conçu pour supporter un grand nombre de minous : pour vous donner une idée du problème que cela représente, il existe plus de sites de pr0n que de minous BOUZINv4 (affirmation non-vérifiée et non vérifiable). Donc, mes chers amis, si on veut pouvoir continuer à partager de l'asian porn en toute liberté, il est grand temps de se préparer à la migration vers BOUZINv6.
Je vais donc commencer une série d'articles sur la mise en pratique de BOUZINv6 allant de la théorie jusqu'aux enjeux réels et aux problèmes que posent ce nouveau standard, en passant par une mise en pratique sur ton LAN et la configuration d'un serveur.
# Késseksa BOUZINv6 ?
Il y a quelques années de cela, quand Internénette s'appelait encore « les autoroutes de l'information », les petits gars qui ont fait BOUZINv4 se sont rendus compte que l'application de ce protocole à échelle planétaire (une première dans le domaine de l'informatique) n'était pas exempt de tout défaut :
* le nombre de minous n'est pas assez élevé ;
* le routage est devenu une galère sans nom ;
* le manque de minous conduit à l'invention de protocole ++sale++ genre NAPT (Network Adress Port Translation) avec tous les soucis que cela peut causer ;
Pour continuer de faire grandir Internénette à vitesse constante (doubler la quantité de porn disponible tous les 6 mois environ), il fallait inventer mieux et gommer les défauts de BOUZINv4.
Ainsi naquit BOUZINv6.
# Comment que ça marche ch'truc-là ?
BOUZINv6 a plusieurs propriétés intéressantes :
* supporter un nombre de minous complètement hallucinants : 2^128 soit environ beaucoup mais vraiment beaucoup de minous ;
* réduire la taille des tables de routage ;
* simplifier grandement le support des différents protocoles au-dessus de lui ;
* simplifier grandement le protocole en lui-même en supprimant la [fragmentation](http://fr.wikipedia.org/wiki/Fragmentation_%28informatique%29), le [CRC](http://fr.wikipedia.org/wiki/Contr%C3%B4le_de_redondance_cyclique), les entêtes à taille variables, les options ;
* intégrer directement la sécurité ;
* intégrer directement la mobilité ;
* intégrer directement la [Quality of Service (QoS)](http://fr.wikipedia.org/wiki/Qualit%C3%A9_de_service) ;
* intégrer directement les mécanismes de configuration automatique type [APIPA](http://fr.wikipedia.org/wiki/APIPA) ;
* ~~intégrer directement le pr0n ;~~
Bref, c'est un protocole du **bien** avec des **poils**.
# Et concrètement sur la babasse à Bibi ?
Les protocoles BOUZINv4 et BOUZINv6 ne sont pas compatibles entre eux : ils sont tous les deux au même niveau du modèle OSI, on ne peut donc pas monter A sur B (même si nous verrons qu'il y a une solution, **sale**, pour le faire).
Cela force tous les systèmes à avoir deux piles BOUZIN montées en parallèle et d'interagir avec les deux en même temps. Même s'il existe des passerelles entre les deux mondes BOUZIN, elles sont rares et permettent en général à BOUZINv6 d'accéder à BOUZINv4 rarement le contraire.
Petit avantage de BOUZINv6 sur BOUZINv4, les pubis supportent nativement plusieurs minous, on est donc plus obligé de passer par un mécanisme de pubis virtuels (genre eth0:0, eth0:1, etc…).
Ta machine aura donc nécessairement au moins un minou BOUZINv4, et deux minous BOUZINv6 pour aller sur Internénette. Pourquoi deux ? Et bien justement à cause de…
# Minoutage v6
C'est la partie un peu relou que tu connais peut-être déjà et que tu as le droit de zapper. Oui, tu peux en profiter pour te prendre une petite binouze grand coquinou ;).
Les minous BOUZINv6 se présentent en 8 groupes de 2 octets (16bits) en représentation hexadécimale de la manière suivante :
* dead:beef:0000:0000:bebe:caca:1234:5678
* dead:beef:0:0:bebe:caca:1234:5678
* dead:beef::bebe:caca:1234:5678
Les trois écritures précédentes sont strictement équivalentes : on peut réduire les groupes de 0000 librement et utiliser, une seule fois par minou, l'abbréviation « :: ». On donne en général le [CIDR](http://fr.wikipedia.org/wiki/CIDR#Principe_du_CIDR) du minou en même temps que ce dernier pour des raisons pratiques.
Il n'existe pas de broadcast en BOUZINv6. Il est remplacé par un mécanisme de multicast moins gourmand en ressources rézo et un peu plus commutateur-friendly.
Il existe également plusieurs portées pour BOUZINv6 : le scope local et le scope global (et le scope site, mais comme personne ne s'en sert, on s'en tape). Le premier est limité au pubis sur lequel il est et ne peut pas dépasser le premier routeur : il est de la forme fe80::/64. Le second est valable sur tout l'Internénette (d'où les deux minous du paragraphe précédents).
Dernier point de détail pour finir cet article : les 48 premiers bits du minou correspondent au FAI. Les 16 suivants au site. Et le reste est consacré à tous les pubis derrière le routeur.
Pour la prochaine fois, on va voir comment qu'on fait pour configurer la babasse à Bibi.
Banzaï !

View File

@@ -0,0 +1,84 @@
+++
title = "IPv6 dans la pratique (partie 2)"
date = 2009-12-17
aliases = [ "post/2009/12/07/IPv6-dans-la-pratique-(partie-2)"]
[taxonomies]
tags = [ "ipv6" ]
+++
babasse : n.f. bidule informatique dont le fonctionnement est obscur mais qui a le bon goût d'héberger du pr0n.
<!-- more -->
Comme promis la dernière fois, on va te montrer comme tu peux monter un réseau BOUZINv6 sur tes babasses de ton réseau domestique…
Donc, pour cela, il te faudra :
* Un switch (bête et basique…) ;
* Deux babasses (nous les appelerons clara et morgane) ;
* De la bière (beaucoup…).
# Activation d'IPv6 sur les babasses
Comme je préfère m'attacher au côté théorique, je ne détaillerai la configuration que pour deux distribs de ton OS préféré : une Daubian (donc valable pour les dérivées) pour clara et une CentOS (même principe) pour morgane. Et comme j'ai dit qu'on le faisait sur des systèmes d'exploitation, on le fera pas sous Windows (TRO FOR CASSSÉÉÉÉ LOLILOL!!).
Bon grosso merdo, c'est le même principe pour les autres systèmes (\*BSD, Solaris, AiX, etc…), à quelques variantes prêt. Je te conseille donc de te reporter à la doc correspondante (y compris celle, très fournie, de Microsoft) pour les détails.
Donc, clara et morgane sont dans un bateau et on va activer les modules BOUZINv6 pour les deux. Sous CentOS, pas grand chose à faire (en fait, c'est activé par défaut puisque le module kivabien est chargé dans la kernaille par défaut). Modifiez le fichier `/etc/sysconfig/network` pour avoir qqch du genre :
```
NETWORKING=yes
NETWORKING_IPV6=yes
HOSTNAME=morgane
```
Sous Daubian, on ira modifier le fichier `/etc/network/interfaces` :
```
auto lo
iface lo inet loopback
iface eth0 inet dhcp
pre-up modprobe ipv6
```
*On me glisse amicalement dans l'oreillette que cette manip' n'est plus nécessaire dans les versions avancées de Daubian (autrement dit la unstable…).*
Après avoir redémarré ce qui concerne le réseau sur les deux bécanes, voici ce qu'on obtient :
```
clara:~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 08:00:27:c2:45:18
adr inet6: fe80::a00:27ff:fec2:4518/64 Scope:Lien
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:1000
RX bytes:684 (684.0 B) TX bytes:2520 (2.4 KiB)
Interruption:11 Adresse de base:0xd020
```
L'intéressant, c'est le minou BOUZINv6 à la troisième ligne. Remarque qu'on te précise qu'il est à portée lien. Pas plus.
# Cachez ce minou que je ne saurai voir
ais est-ce que clara et morgane peuvent communiquer (et éventuellement faire des cochonneries ensemble) ? Et ben ouais ! Comment clara peut découvrir morgane ? Et bien, c'est simple :
```
clara:~# ping6 -I eth0 ff02::1
PING ff02::1(ff02::1) from fe80::a00:27ff:fec2:4518 eth0: 56 data bytes
64 bytes from fe80::a00:27ff:fec2:4518: icmp_seq=1 ttl=64 time=1.10 ms
64 bytes from fe80::a00:27ff:fe86:fe4f: icmp_seq=1 ttl=64 time=5.99 ms (DUP!)
```
Ah ça, t'aurais pas pu la deviner tout seul celle-là !! Bon, on avait dit qu'il n'y a plus de broadcast en BOUZINv6. Ça, c'était pour faire bien dans la rédaction de la norme parce que le broadcast, c'est **sale**. Donc dans la pratique, on a une adresse générique dite de « découverte » (basée sur ICMPv6) pour aller regarder ce qui se passe chez les copines. Vérifions, si clara peut voir the girl next door :
```
clara:~# ip -6 neigh show
fe80::a00:27ff:fe86:fe4f dev eth0 lladdr 08:00:27:86:fe:4f REACHABLE
```
Je peux même communiquer avec morgane directement :
```
clara:~# ping6 -I eth0 fe80::a00:27ff:fe86:fe4f
PING fe80::a00:27ff:fe86:fe4f(fe80::a00:27ff:fe86:fe4f) from fe80::a00:27ff:fec2:4518 eth0: 56 data bytes
64 bytes from fe80::a00:27ff:fe86:fe4f: icmp_seq=1 ttl=64 time=0.448 ms
64 bytes from fe80::a00:27ff:fe86:fe4f: icmp_seq=2 ttl=64 time=1.36 ms
```
Oui, mais à un détail près : à chaque fois, on est tenu de préciser l'interface sur laquelle on veut communiquer (en général, en utilisant `%ethX` à la fin de l'adresse BOUZINv6. Pourquoi ? Et bien, c'est à cause de la fameuse portée limitée au lien de ce minou… Du coup ce n'est pas si pratique que ça (d'autant plus qu'on ne peut pas toujours préciser l'interface) et surtout, ce n'est pas très élégant.
Du coup, pour la prochaine fois, on va voir comment on peut faire pour résoudre cette épineux problème, en utilisant la portée site. « Mais je croyais que ça servait à rien ?!? » AH ! AH ! IT'S A TRAP !!§! En fait, c'est l'équivalent des adresses 10.0.0.0/8, 172.16.0.0/12 et 192.168.0.0/16 de BOUZINv4 pour les minous non routées sur Internénette… Et on verra comment on peut s'en sortir avec ça…

View File

@@ -0,0 +1,83 @@
+++
title = "IPv6 dans la pratique (partie 3)"
date = 2009-12-25
aliases = [ "post/2009/12/09/IPv6-dans-la-pratique-(partie-3)"]
[taxonomies]
tags = [ "ipv6" ]
+++
minou : n.m. un truc avec lequel on s'amuse bien.
<!-- more -->
La dernière fois qu'on s'est vu, mon petit lecteur frippon préféré, on en était resté au montage d'un réseau BOUZINv6 en mode autoconfiguration (enfin plutôt stateless). Ça a relativement bien marché à un détail près : faire fonctionner des vrais applis du vrai monde réel dans ces conditions-là, c'est un peu chaud les haricots. « Sa mère la fille de mauvaise vie, maison des madames qui vendent leur corps, excrément, il va encore falloir bouger son rectum pour obtenir un résultat un tant soit peu exploitable ». Bah oui…
Tu vas donc retrousser tes manches et actionner tes petits doigts potelés pour monter un routeur BOUZINv6 sur ton zoli LAN pour que ça commence à ressembler à quelque chose.
# Mazette, dat soundz grit
On va donc installer l'équivalent (très basique) d'une sorte de DHCP pour transformer clara en distributeur de minous BOUZINv6. La chose en l'occurence se nomme `radvd`.
Donc, on va commencer par attribuer une minou à clara :
```
clara:~# ifconfig eth0 inet6 add fec0:beef:dead:bebe::1/64
clara:~# ifconfig eth0 | grep inet6
adr inet6: fec0:beef:dead:bebe::1/64 Scope:Site
adr inet6: fe80::a00:27ff:fec2:4518/64 Scope:Lien
```
inou, loin d'être choisi au hasard : en fait, les 4 premiers groupes de 2 octets correspondent à l'indicateur de site. Dans ce mode de configuration, les babasses détermineront leurs minous en se basant sur la configuration de leur minou MAC, d'où le masque à 64bits.
Et tu obtiens donc bien une portée de lien et une portée de site (avec en bit bonus un préfixe qui tue !). On attaque une configuration (extrêmement basique) de radvd via `/etc/radvd.conf` :
```
interface eth0
{
AdvSendAdvert on;
prefix fec0:beef:dead:bebe::/64
{
AdvOnLink on;
AdvAutonomous on;
};
};
```
Bon, je me suis pas trop foulé pour celui-ci, on le trouve dans le man… On oublie pas d'activer le forward (un peu comme en BOUZINv4) :
```
clara:~# echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
```
Et c'est partie pour le rodéo ! Une fois `radvd` lancé, il suffit de patienter quelques secondes et là, miracle de la science moderne :
```
[root@morgane ~]# ping6 fec0:beef:dead:bebe::1 -c 2
PING fec0:beef:dead:bebe::1(fec0:beef:dead:bebe::1) 56 data bytes
64 bytes from fec0:beef:dead:bebe::1: icmp_seq=0 ttl=64 time=0.863 ms
64 bytes from fec0:beef:dead:bebe::1: icmp_seq=1 ttl=64 time=2.05 ms
--- fec0:beef:dead:bebe::1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.863/1.458/2.054/0.596 ms, pipe 2
```
IT'S ALIVEEEE !!!§§! ALIIIIIIIIIIIIVE !!§#
Et oui mon mignon, clara et morgane sont maintenant dans le même lit, dans une pose lacive, sur la même longueur d'onde (GRRRR !). Bon, allez, just for fun, on va voir si on peut faire tourner une appli sur notre réseau local. On installe donc un apache des familles sur clara, qu'on démarre joyeusement. Un petit coup d'œil sur les connexions côté clara :
```
clara:~# netstat -lapute | grep -i apache
tcp6 0 0 [::]:www [::]:* LISTEN root 7737 2853/apache2
```
La coquine écoute bien sur tous les ports. Voyons maintenant si l'ensemble s'interpénètre :
```
[root@morgane ~]# wget http://[fec0:beef:dead:bebe::1]
--2009-12-09 23:30:26-- http://[fec0:beef:dead:bebe::1]/
Connexion vers fec0:beef:dead:bebe::1:80...connecté.
requête HTTP transmise, en attente de la réponse...200 OK
Longueur: 45 [text/html]
Saving to: `index.html'
100%[======================================>] 45 --.-K/s in 0s
2009-12-09 23:30:26 (3,60 MB/s) - « index.html » sauvegardé [45/45]
```
\o/. Tu auras remarqué que nous avons utilisé des `[]` pour mettre le minou de clara : ce n'est pas une mesure de protection mais simplement un standard dans BOUZINv6. Nous avons ici configuré un réseau type intranet, mais le principe pour faire un routeur BOUZINv6 sur Internet est en gros le même : on remplace simplement la portée site par une portée globale au niveau de `radvd`.
Voilà, petit chenapan, tu peux maintenant échanger du pr0n librement dans la joie et l'allégresse avec la satisfaction du geek accompli. Pour le prochain numéro, on va voir ce peut vraiment apporter BOUZINv6 dans le quoitidien du nerdadmin de base (en dehors des considérations de masturbations informatiques bien entendu).

View File

@@ -0,0 +1,42 @@
+++
title = "IPv6 (plus vraiment) dans la pratique (partie 4)"
date = 2010-01-01
aliases = [ "post/2009/12/22/IPv6-(plus-vraiment)-dans-la-pratique-(partie-4)"]
[taxonomies]
tags = [ "ipv6" ]
+++
gens : n.m. des gars qui utilisent le Internénette
<!-- more -->
Bon alors, c'est bien joli tout ça, on a configuré un rézo BOUZINv6, on a vu qu'on pouvait faire tourner des applis dessus, mais qu'est-ce que ça apporte vraiment et pourquoi je casse mon cul de singe à t'expliquer tout ça ?
# Moins de NAT, plus de fun !
Le premier truc dont il faut bien se rendre compte, c'est que, en BOUZINv6, tous les appareils connectés à Internénette vont avoir un minou public. Ne plus avoir à faire de NAT va grandement simplifier le fonctionnement de pas mal de choses :
* Les applications qui font de l'allocation dynamique de ports ;
* Les applications qui doivent fonctionner à la fois en mode client et serveur (comme le transfert de fichiers dans un célèbre client de messagerie instantannée) ;
* Les machines clientes sont en minou fixe ;
* Les applications mobiles qui vont profiter de BOUZINv6Mobile permettant aux clients de garder le même minou, même s'ils changent de routeur ;
* La possibilité de faire tourner plusieurs fois le même service sur le même port derrière un même routeur.
Bon maintenant, tu vas me dire : « C'est bien joli tout ça, mais si tout le monde est en minou public, est-ce que ça craint pas un peu du boudin quand même question sécurité ? ». Le fait est qu'un NAT sur un routeur protège pas mal les babasses du réseau local des méchants Monsieur de l'Internénette. Maintenant, tous les paquets, même sans NAT, continuent de passer par le routeur et peuvent donc être filtrés.
# Et pour ma super babasse sur Internénette ?
Pour le nerdadmin de base, il y a aussi quelques avantages notamment quand il s'agit d'héberger plusieurs domaines, donc plusieurs sites web ou services. Jusqu'à présent, soit on se servait de ports différents (en configurant chaque service sur un port différent : efficace, mais pas super classe), soit on faisait des hôtes virtuels par nom (grande spécialité d'Apache : on attaque un service différent en fonction du nom de domaine). C'est bien mignon tout ça, mais c'est quand même un peu casse-bonbons dans quelques situations :
* On est obligé de transmettre au client un numéro de port différent de celui configuré par défaut pour un service donné (pas très élégant, pas toujours possible et en plus le client peut tomber sur un service qui ne lui est pas destiné) ;
* Dans le cas d'Apache, il est impossible de faire du HTTPS avec plusieurs certificats pour plusieurs VirtualHost : comme la connexion est chiffrée de bout-en-bout, le nom de domaine ne peut être transmis avant la tentative de connexion. Apache est donc forcé de transmettre le premier certificat qui lui tombe sous la main et pas le certificat spécifique au VirtualHost.
Et bien, BOUZINv6 perment de s'affranchir de tout ce bordel assez facilement puisque l'on dispose quasi-systématiquement d'une plage de quelques milliers de minous quand on a un serveur (chez OVH au hasard) : plus d'hôtes virtuels par nom, plus besoin de faire tourner des instances du même service sur des ports différents, on va tout faire sur des minous différents ! C'est bibi qui va être content !
Alors ouais, va quand même falloir se palucher des tables de pare-feux bien cradingues (imagine quand tu as une dizaine de minous sur ta babasse et autant de services associés !), mais bon on a rien sans rien et pouvoir envoyer le bon certificat au bon client en SSL, ça pète quand même grave !
# Mais alors pourquoi diable ne sommes-nous pas tous passés en BOUZINv6 depuis belle lurette ?
Et bien malheureusement, les raisons du blocage sont multiples :
* L'apparition des NAT a considérablement ralenti la nécessité de passer en BOUZINv6 (même si c'est un peu bidon en fait) ;
* La pénurie de minous BOUZINv4 ne concernent que très peu les pays occidentaux (et comme c'est pas pour nos culs, bah on s'en tape quoi…) ;
* Malgré le support de grands acteurs de l'industrie (Google, Microsoft, Apple, etc…), les fournisseurs de contenus (Youtube, Wikipedia, etc…) sont essentiellement en BOUZINv4 et ne sont pas pressés de passer en BOUZINv6 dans la mesure où leurs clients sont essentiellement en BOUZINv4 aussi ;
* Même si de plus en plus de systèmes sont BOUZINv6 Ready, beaucoup d'internautes ne pourront pas passer facilement en BOUZINv6 (support, configuration, etc…).
Alors oui, on est parti pour une période de transition lente et douloureuse entre BOUZINv4 et BOUZINv6, oui, il va falloir faire des efforts pour tout basculer dans le nouveau protocole. Maintenant, en tant que sysadmin consciencieux et légèrement geek sur les bords, avec toutes les billes que je t'ai donné, tu peux commencer à te préparer sereinement pour ne pas être emmerdé le jour de la grande migration…

View File

@@ -0,0 +1,89 @@
+++
title = "Mes deux centimes : changer de disque dur avec pvmove"
date = 2010-03-03
aliases = [ "post/2010/02/22/Mes-deux-centimes-:-changer-de-disque-dur-avec-pvmove"]
[taxonomies]
tags = [ "système", "linux", "lvm" ]
+++
« C'est la grande foire des disques durs sur le stand boucherie ! »
<!-- more -->
En tant que petit macaque bien averti, tu as installé ta nouvelle babasse avec deux disques drus (un pour les données et un pour le système) et tu as mis un magnifique système LVM pour gérer le tout parce que tu es un singe du bien et que tu sais que LVM, c'est la méga-classe internationale, que ça fait grandir ton pénis et que ça sauve les bébés phoques.
Seulement voilà, tout ne va pas si bien que ça au pays des Bisounours et tu as déjà rempli de pr0n ton volume logique de données, tu ne peux plus stocker les photos de tes beuveries entre potes, et ça c'est moyennement lol. Tu as donc décidé de « enlarge you disk » en le remplaçant par un plus gros. Seulement voilà : la copie disque à disque des données par le système va prendre beaucoup de temps et empêcher l'accès aux données ; comment faire quelque chose de plus intelligent sans te priver de l'utilisation de ta babasse pendant des heures ?
# pvmove ou comment tout bouger sans rien toucher…
Et bien on va tout simplement utiliser une des nombreuses fonctionnalités qui chient la classe dans LVM : la migration de volume logique d'un volume physique à un autre via la commande `pvmove`.
Bon évidemment, je te conseille de t'entraîner sur des partitions pas importantes (les données de ta mère, l'ordinateur de ta petite sœur, etc…) avant d'attaquer le vrai transfert. Et fais aussi une petit sauvegarde on ne sait jamais (perdre l'intégrale de Marc Dorcel, ça me ferait mal…). J'ai donc un disque dur de 2Gio (c'était brocante dans ma cage ce week-end…), plein, dans un Volume Group `vgdata` avec un seul volume logique `home` dedans :
```
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdb1 vgdata lvm2 a- 2.00G 0
# vgs
VG #PV #LV #SN Attr VSize VFree
vgdata 1 1 0 wz--n- 2.00G 0
# lvs
LV VG Attr LSize Origin Snap% Move Log Copy% Convert
home vgdata -wi-ao 2.00G
# df -h | grep home
/dev/mapper/vgdata-home
2.0G 1.9G 34M 99% /home
```
Je m'arrange donc pour brancher mon deuxième disque de 4Gio (on trouve des trésors dans ces brocantes, c'est pas croyable !) `/dev/sdc1` et créer un nouveau volume physique dessus. N'oublie pas de demander de l'aide à tes parents avant d'éventrer ton PC :
```
# pvcreate /dev/sdc1
Physical volume "/dev/sdc1" successfully created
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdb1 vgdata lvm2 a- 2.00G 0
/dev/sdc1 lvm2 -- 4.00G 4.00G
```
J'ai maintenant un nouveau volume physique qui n'appartient à aucun groupe de volume. Je vais donc tranquillement l'ajouter dans le volume group existant :
```
# vgextend vgdata /dev/sdc1
Volume group "vgdata" successfully extended
# vgs
VG #PV #LV #SN Attr VSize VFree
vgdata 2 1 0 wz--n- 5.99G 4.00
```
Tes yeux de lynx auront remarqué que mon volume group fait maintenant une taille totale de 4+2Gio donc 4Gio libre (sans volume logique). Maintenant, on va transférer les données à proprement parler en indiquant que l'on souhaite que le volume logique `home` soit maintenant géré sur le volume physique `/dev/sdc1` (ce qui est possible parce que les volumes physiques sont dans le même groupe de volumes) :
```
# pvmove --name home /dev/sdb1 /dev/sdc1
/dev/sdb1: Moved: 14.3%
/dev/sdb1: Moved: 29.2%
/dev/sdb1: Moved: 40.9%
/dev/sdb1: Moved: 58.1%
/dev/sdb1: Moved: 75.9%
/dev/sdb1: Moved: 92.6%
/dev/sdb1: Moved: 100.0%
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdb1 vgdata lvm2 a- 2.00G 2.00G
/dev/sdc1 vgdata lvm2 a- 4.00G 2.00G
```
on volume physique d'origine ne contient plus de données (100% de l'espace est dispo) et mon nouveau volume physique est rempli à moitié. Les données ont donc bien été transférées et ma machine est restée utilisable pendant toute la durée du transfert \o/. Je peux maintenant virer le deuxième disque du volume group :
```
# vgreduce vgdata /dev/sdb1
Removed "/dev/sdb1" from volume group "vgdata"
# pvremove /dev/sdb1
Labels on physical volume "/dev/sdb1" successfully wiped
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdc1 vgdata lvm2 a- 4.00G 2.00G
```
Il ne reste plus qu'à retirer le disque de la bécane.
Bon évidemment toute cette opération présupposait que tes disques durs étaient manipulables à chaud (USB, SATA, SCSI, SAS) ou que tu redémarres à chaque ajout/retrait (ce que personnellement je recommanderais). On l'a fait sur des données, on peut également le faire sur des partitions systèmes à une petite exception près : `/boot` ne peut pas être sur une partition LVM pour le moment, il faudra donc prendre des précautions particulières pour cette partoche.
Ouf ! Maintenant que j'ai sauvé la planète, je vais pouvoir prendre des vacances bien méritées…

View File

@@ -0,0 +1,51 @@
+++
title = "Les pérégrinations de quelques butineurs en goguette…"
date = 2010-05-14
aliases = [ "post/2010/05/11/Les-pérégrinations-de-quelques-butineurs-en-goguette…"]
[taxonomies]
tags = [ "internet", "web", "nagivateur" ]
+++
Ou comment que c'est gaulé dedans le bouzin en fait…
<!-- more -->
Safari, navigateur historiquement Pomme sous Windows, Google grand maître du monde qui propose un navigateur rapide et performant, Firefox entré dans les mœurs et Internet Explorer vieillissant et fatigué. Voilà comment on pourrait résumer le paysage de l'interwebz des butineurs ces derniers temps. Mais au final, qu'est-ce qu'il y a sous le capot ? Qu'est-ce qui fait la vraie différence entre ces différents produits ? Qu'est-ce qui se cache sous l'étiquette ? ~~Bienvenue dans Capital !~~ Une proposition d'éclaircissement…
# Dessine-moi un navigateur
En dehors des options d'ergonomie et de la gestion des sécurités (certificats, proxy, porn mode, etc…) un butineur de l'interwebz peut se résumer à deux choses : ~~le porn et le pr0n~~ le moteur de rendu HTML et le moteur d'exécution JavaScript.
## Moteur de rendu
Le premier est là pour afficher les pages correctement, l'objectif étant que ça ne ressemble pas à un Picasso réalisé sous Paint. Le moteur de rendu HTML doit à la fois gérer les nombreuses propriétés CSS, le placement des différents éléments, la taille des caractères, le rendu global et les animations, si besoin est, de l'ensemble. Le moteur doit donc prendre en charge de multiples objets avec de multiples propriétés et paramètres, qui, en plus, sont souvent amenés à changer au cours de l'affichage de la page sur les sites interactifs. On peut dénombrer 4 moteurs de rendu HTML différents :
* Trident utilisé par Internet Explorer et consorts (AOL, etc…) ;
* Gecko utilisé par Mozilla dans tous ses produits (Firefox, Thunderbird, SeaMonkey) ;
* Webkit dérivé de KHTML et utilisé par Safari, Chrome, Konqueror, Midori ;
* Presto développé et utilisé par Opera.
Soyons clair tout de suite : le premier est complètement à la rue depuis bien longtemps (« Die IE6 ! Die ! ») et passe à peine l'[acid test 2](http://www.webstandards.org/files/acid2/test.html) en version 8.0. Quant à l'[acid test 3](http://acid3.acidtests.org/), il réalise un score minable de 20% quand tous ses petits camarades font entre 94% (Gecko) et 100% (Webkit, Presto). [En prime, Microsoft force la compatibilité IE7 sur IE8 pour une liste prédéterminée de sites !](http://geekarlier.com/?a=internet-explorer-8-microsoft-incompatible-css-html-w3c-standards) Voilà pourquoi, à titre personnel, je préfèrerais me frotter les couilles avec des orties plutôt que d'utiliser IE. Internet Explorer, c'est comme les lib dubs, la ferme des célébrités et la Tecktonik : IL VA FALLOIR ARRÊTER MAINTENANT !
Pour les autres, je dirais que c'est un peu kif-kif : Presto, Webkit et Gecko ont un très bon rendu et la différence entre chacun tient aujourd'hui plus au support de quelques paramètres CSS obscurs et à l'intégraton de technologie de coupage de coin type HTML5, CSS3, etc… La rapidité de d'interprétation peut également servir de critères même si les différences sont réellement minimes.
## Moteur JS
Cette partie concerne l'exécution de code sur le navigateur client et est particulièrement importante depuis l'arrivée des boîtes à outils type jQuery, MooTools, etc… et des sites interactifs du Web 2.0. La norme JavaScript ne précisant pas de manière de compiler le code, plusieurs méthodes ont émergé : interprétation brute et conne, compilation d'un code intermédiaire (bytecode) et compilation directe en langage machine. La qualité et la performance d'un interpréteur JavaScript va dépendre fortement des optimisations en fonction des boîtes à outils sus-citées mais également de la manière de procéder pour compiler le code.
Comment mesure-t-on cette performance ? Et bien avec des benchmarks dont le plus célèbre est probablement [SunSpider](http://www2.webkit.org/perf/sunspider-0.9/sunspider.html) ou le [Google V8 Benchmark](http://v8.googlecode.com/svn/data/benchmarks/v5/run.html) (qui donne souvent V8 largement en tête pour une raison simple : le code a été optimisé pour lui…). Voici donc nos principaux candidats :
* Trident JS : inclus dans IE, il interprète (mal) le JS. C'est probablement le plus mauvais interpréteur qu'on ait jamais vu tout langage confondu. Ma grand-mère pourrait compiler du code JS plus vite et mieux, et elle est atteinte d'Alzheimer.
* SpiderMonkey : c'est le moteur historique de Mozilla. Ces performances sont moyennes mais il se défend quand même pas mal…
* TraceMonkey : c'est le nouveau moteur de Mozilla, intégré dans Firefox 3.6 et qui sera encore amélioré en 3.7. C'est pas mal non plus…
* JägerMonkey : c'est le prochain moteur de Mozilla, qui sera disponible avec Firefox 4. On en a pas d'aperçu pour le moment, mais étant basé sur le code de WebKit, il est fort possible qu'il soit bien plus performant que les deux autres singes de la Fondation.
* V8 : c'est le moteur de Chrome de Google et c'est un bête de course car le code est compilé en langage machine avant d'être exécuté.
* WebKit JS : c'est le moteur associé à WebKit. Il est très performant mais toutefois moins que V8.
Si Trident est à la rue comme d'habitude, en testant les différents moteurs sur différents systèmes et navigateurs, on constate que quoiqu'il arrive V8 est en tête, suivi de près par WebKit. Les singes sont un peu plus en retrait avec un net et logique avantage de Trace sur Spider. [Pour ceux qui veulent du factuel.](http://frederic.bezies.free.fr/blog/?p=3149)
La méthode employée par V8 explique facilement ces résultats : une fois que le code est compilé en langage machine, il est maintenu en mémoire et son exécution est extrêmement rapide. Par contre, cela devrait logiquement le rendre moins portable…
## Du coup, conclusion
Voilà, j'espère que ce petit article t'aura permis de te faire une idée sur le fonctionnement interne d'un navigateur et peut-être que cela orientera ton choix. Pour le moment, à titre personnel, je reste sur FireFox pour des questions d'ergonomie et de traçage d'activité (mais j'y reviendrai probablement bientôt). Le navigateur idéal n'existe pas, mais on pourrait déjà l'imaginer :
* aussi extensible et ergonomique que FireFox ;
* avec le moteur de rendu WebKit ;
* avec le moteur JS V8 ;
* Et libre bien entendu…

View File

@@ -0,0 +1,39 @@
+++
title = "Pourquoi je ne crois pas au logiciel libre (partie 1)"
date = 2010-05-28
aliases = [ "post/2010/05/26/Pourquoi-je-ne-crois-pas-au-logiciel-libre-(partie-1)"]
[taxonomies]
tags = [ "logiciel libre" ]
+++
icrosoft, Apple, Google et Yahoo auront toujours plus de crédibilité à la barre qu'un barbu avec des lunettes…
<!-- more -->
Non, je ne crois pas aux logiciels libres, même si je vois/participe/contribue au vivier. Et pourquoi donc ? Simplement, parce que je pense sincèrement que si l'effort et l'éthique sont louables, la capacité du libre à subsister est intrinsèquement limitée par ses propres règles, à commencer par l'aspect juridique des licences.
# GPL-moi ça !
Les licences du libre (reconnues comme telles par la [FSF](http://www.fsf.org/)) sont nombreuses et variées. Néanmoins, on peut dégager quelques critères communs :
* Un code source ouvert ;
* La possibilité d'étudier librement ce code source ;
* La possibilité de réutiliser totalement, partiellement avec ou sans autorisation avec des conséquences ou non ce code source.
Je ne vais pas tout lister ici, d'abord parce que c'est chiant et ensuite, parce que ce n'est pas le fond du problème (je dis ça pour les 3-4 geeks au fond de la salle qui finiront bien par ramener leur fraise à un moment ou à un autre !).
Si certaines licences sont assez peu regardantes sur la réutilisation du code (la licence BSD permet de fermer du code à l'origine libre !), d'autres comme la [GPL](http://www.gnu.org/licenses/gpl.html) impose que l'utilisation de code libre rende le code résultant libre (on parle alors de licence contaminante). Se pose alors la question de la vérification du respect strict de ces licences.
Et comment fait-on cela ? Et bien, c'est tout simplement impossible. Il faut compter sur la bonne volonté de quelques bricolos siphonés du processeur pour décompiler des logiciels et regarder sous le capot (en toute illégalité et dans le non-respect le plus total de la licence d'utilisation des logiciels en question). Comment, dans un monde où essayer de comprendre comment marche telle ou telle appli peut rapporter quelques années de prison bien méritées, la [FSF|http://www.fsf.org/|en] peut-elle vérifier la bonne application des licences ? La réponse est triviale : elle ne peut pas. Et quand bien même elle débusquerait une faille, elle ne pourrait pas se payer l'armée de juristes assoiffés de sang nécessaires pour faire taire l'éditeur fautif.
Et je ne parle même pas des soucis un peu bord-cadre genre « Et si j'utilise un langage libre pour programmer un truc pas libre ? ».
# Le brevet n'est pas ton ami
Le logiciel libre étant en général fait par un troupeau de hippies barbus communistes, il est pauvre. En dehors de quelques projets sponsorisés, à des fins tout sauf altruistes, par des grands pontes de l'industrie, la plupart des logiciels libres naissent, vivent et meurent dans un garage entre une odeur d'aisselles rances et une machine à café.
Le logiciel libre est donc non seulement sans moyen ou presque pour faire respecter ses beaux principes, mais en plus il ne peut pas se permettre de violer un brevet logiciel ou une licence déjà existante. Concrètement, cela veut dire que le développeur d'un codec pour lire du pr0n en WMV est dans l'illégalité la plus complète sur une grande partie de la planète et que tous ses utilisateurs peuvent très bien écoper de lourdes amendes pour l'avoir simplement regardé la codaz avec un peu trop d'insistance.
Il faut donc trouver des contournements aux brevets, perdre du temps [à réinventer la roue carrée](http://fr.wikipedia.org/wiki/R%C3%A9inventer_la_roue_carr%C3%A9e), le tout sans jamais espérer une retombée économique.
# Foutaises
Comme tu peux le voir, il y a donc déjà un premier problème majeur d'ordre juridique qui fait que je ne pense pas que le logiciel libre pourra percer un jour. Si une entreprise bien connue décide [de breveter le clic de souris](http://www.pcinpact.com/actu/news/Microsoft_le_double_click_et_les_derives_du_brevet.htm), l'ensemble des logiciels libres l'utilisant se retrouvera mis au placard à moins de raquer.
Et comme tu l'as vu en première partie, le simple fait de faire respecter une licence peut devenir une vraie gageure quand on a pas les moyens d'avoir à sa botte les 4 cavaliers de l'apocalypse version Ally McBeal.

View File

@@ -0,0 +1,29 @@
+++
title = "Pourquoi je ne crois pas au logiciel libre (partie 2)"
date = 2010-07-02
aliases = [ "post/2010/07/02/Pourquoi-je-ne-crois-pas-au-logiciel-libre-(partie-2)"]
[taxonomies]
tags = [ "logiciel libre" ]
+++
C'est la crise ma pauv' dame…
<!-- more -->
Comme on l'a déjà vu précédemment, [le logiciel libre est pauvre](/pourquoi-je-ne-crois-pas-au-logiciel-libre-partie-1/). Alors non seulement, il n'a pas les moyens de lutter contre le logiciel propriétaire sur le terrain juridique, mais il a encore moins les moyens de lutter sur le plan purement financier et économique.
# Business PAN!
Quand un projet libre se lance et qu'il commence à grossir (en fait, quand la quantité de café consommé la nuit sur le projet est plus importante que la journée au boulot), se pose souvent la question, au sein de ces fondateurs, de savoir comment ils vont fonctionner. [Beaucoup de modèles économiques existent](http://aful.org/professionnels/modeles-economiques-logiciels-libres/differents-modeles) mais en réalité très peu fonctionnent. Soyons même parfaitement honnête, le seul modèle qui fonctionne réellement est celui du mécénat : c'est ce qui a permis à Ubuntu et à la Mozilla Foundation d'être ceux qu'ils sont aujourd'hui.
Sans l'apport massif de capitaux de Mark Shuttleworth d'un côté et de Google Sponsoring de l'autre, ces deux projets ne seraient certainement pas aussi florissants aujourd'hui. En dehors de cela ? Le désert. Peu d'entreprises, qui ne survivent en général pas très longtemps.
ême une entreprise bien installée sur ce marché comme [Red Hat a une croissance très limitée](http://philippe.scoffoni.net/lindustrie-logiciel-open-source-ressembler-a-logiciel-proprietaire/). Et elle n'est pas à l'abri qu'un concurrent reprenne tout son travail ([CentOS par exemple…](http://www.centos.org)) et le mette à disposition d'une nouvelle clientèle pour moins cher. Après tout, le code source est libre, non ?
# Tout est question de brouzoufs
« Vous avez déjà Windows 2008 Server ? Prenez donc une licence SQL Server 2008, c'est gratuit pour vous la première année ! ». Merci M. Lobby. La main mise et la base de clients énormes de certaines compagnies freinent également beaucoup certains projets Open Source. Comment contrer les outils de communication, de marketing et de promotion d'un géant du logiciel qui brassent des millions quand on est à peine capable de ne pas se faire mettre à la porte des deux pauvres bureaux qu'on a loué les yeux de la tête pour héberger une start-up spécialisé en open-source ? C'est simple : on ne peut pas. Il faut compter sur le bouche-à-oreille et la bonne volonté de quelques maoïstes infiltrés dans les DSI du monde entier qui tentent désespérément de faire comprendre à tous leurs collègues que le logiciel libre « CAY MIEUX » et « CAY PLUS LOL ».
ais il y a pire encore. Les rachats de projet ouvert sont de plus en plus fréquents. En fait, dès qu'un projet commence à émerger, on peut être pratiquement sûr qu'il sera racheté par une grosse boîte d'édition de logiciels, qui fera sûrement tout ce qu'elle peut pour changer la licence et ainsi empêcher la concurrence de produire du service ou des logiciels compatibles. Xen racheté par Citrix, MySQL racheté par Oracle, CUPS racheté par Apple ne sont que des exemples symptomatiques de cette pratique tellement commode : laisser les petits contributeurs indépendants et les étudiants faire le boulot et vendre le tout quand le projet a atteint un potentiel-pognon suffisant. Et les contributeurs et les utilisateurs ? Qui se préoccupe encore des barbus quand le chèque a plusieurs zéros…
« Oui mais comme le code est sous licence libre, on peut toujours faire un fork et continuer ! ». Oui, jeune padawan, tu as bien appris ta leçon. Mais qui va-t-on trouver pour continuer ? Le crève-la-faim qui était à l'origine du projet et qui est bien content d'avoir un salaire chez place-a-sofware-company-here, et qui est maintenant lié par un contrat de travail et un NDA ? Ça fait des années qu'ils portent son projet à bout de bras, parfois dans les conditions les plus difficiles alors ça m'étonnerait sincèrement qu'il lâche tout pour des questions d'éthique et de philosophie pour continuer de développer dans un garage. Quelqu'un d'autre reprendrait le code ? Peu probable également. Il faut tout de même voir que malgré tout le talent de certaines équipes, les logiciels libres sont en général le fruit d'une poignée de programmeurs de génie qu'on ne peut jamais remplacer si facilement. Tu t'imagines reprendre le code source de MySQL ? Bon courage !
Difficile pour le hippie communiste de gagner sa maigre pitence dans ce monde de brutes.

View File

@@ -0,0 +1,67 @@
+++
title = "IPv6, tunnelbroker et table forward (partie 1)"
date = 2010-07-09
aliases = [ "post/2010/06/30/IPv6,-tunnelbroker-et-table-forward"]
[taxonomies]
tags = [ "ipv6" ]
+++
« Moi, quand je m'ennuie, je fais de l'IPv6, ça me purifie. »
*Jean-Michel, éleveur de paquets IP dans la Creuse depuis plus de 15 ans*
<!-- more -->
Tu le sais maintenant, j'aime IPv6. Je voudrais coucher avec, l'épouser, lui faire deux gosses et le laisser sur le bord de la route un beau matin avec le RMI pour seul revenu. Le grand amour quoi… Comme mon fournisseur d'accès adoré ne fait pas d'IPv6 (plus que 400 jours avant la pénurie, on est laaaaaaaaaaaaaaaaarge), je me suis dit que j'allais copieusement l'envoyer paître et faire les choses moi-même. Comment ? En profitant d'une des options de la norme IPv6 : 6in4 via un [Tunnel Broker](http://fr.wikipedia.org/wiki/Tunnel_broker).
# 36:15 kinenveu
« Ventre Saint-Gris, mais comment diable cela peut-il bien fonctionner ? ». Et bien, c'est très simple un fournisseur d'accès de second niveau ([Freenet6](http://go6.net/), [Sixxs](http://www.sixxs.net) ou encore [Hurricane Electric](http://www.tunnelbroker.net)) met à ta disposition une plage d'interconnexion IPv6 et en sus un éventuel /48 pour ton amusement personnel. J'ai donc choisi [Hurricane Electric](http://www.tunnelbroker.net), notamment pour la simplicité de la configuration mais aussi pour la performance du réseau hôte. Après avoir renseigné l'ensemble avec un nom/mail bidon, on peut accéder à une interface qui permet de créer des tunnels.
# Continue de creuser, je sens que ça vient…
ais alors comment qu'on fait quand on en est là ? On remonte ces manches et on bricole un routeur IPv6 en un tour de main comme qui rigole. Petit préliminaire (grrrr…). Il te faut donc :
* Une IPv4 fixe sur Internet (pas indispensable, mais disons que c'est toujours plus pratique) ;
* L'adresse IPv4 de la passerelle d'interconnexion ;
* L'adresse IPv6 de la passerelle (mettons 2002:dead:beef::1/64) et l'adresse du point d'appui côté client (l'autre morceau du réseau, donc 2002:dead:beef::2/64) ;
* Une Debian 5.0 standard fraîchement installé sur une petit bécane qui fera office de routeur et qui sera connecté à ton réseau local ;
* Un /48 fourni par le broker (ici dans mon exemple 2001:dede:abba::/48) ;
* Une bouteille de bon tord-boyau du Kentucky :bufford:.
# Vite mon clavier, VIIIIIIITE !
On commence donc par mettre une IP fixe sur le réseau local la babasse Debian (dispensable mais pratique), en éditant ''/etc/network/interfaces'' :
```
iface eth0 inet static
address 192.168.0.2
netmask 255.255.255.0
network 192.168.0.0
broadcast 192.168.0.255
gateway 192.168.0.1
```
On en profite pour mettre à la suite dans le même fichier les informations concernant le broker :
```
iface 6in4 inet6 v4tunnel
address 2002:dead:beef::2 # Adresse du point d'appui
netmask 64
endpoint 216.66.84.42 # Adresse de la passerelle v4
gateway 2002:dead:beef::1 # Adresse de la passerelle
ttl 255
```
Au redémarrage, on devrait voir monter une interface nommée 6in4 et profiter instantanément d'une connectivité IPv6 :
```
# ifconfig 6in4
6in4 Link encap:IPv6-dans-IPv4
adr inet6: 2002:dead:beef::1/64 Scope:Global
[…]
# ping6 -c1 ipv6.google.com
PING ipv6.google.com(2a00:1450:8001::63) 56 data bytes
64 bytes from 2a00:1450:8001::63: icmp_seq=1 ttl=55 time=262 ms
[…]
```
\o/
Pour la prochaine fois, on verra comment faire profiter l'ensemble de ton réseau local d'une connectivité IPv6 et, en super bonus, comment le protéger des vilains méchants du grand Internet.

View File

@@ -0,0 +1,33 @@
+++
title = "Pourquoi je ne crois pas au logiciel libre (partie 3)"
date = 2010-07-16
aliases = [ "post/2010/07/23/Pourquoi-je-ne-crois-pas-au-logiciel-libre-(partie-3)"]
[taxonomies]
tags = [ "logiciel libre" ]
+++
« Montrer que l'on a acheté l'Édition Intégrale Plaquée Or de Windows est un signe extérieur de richesse. Montrer que l'on a téléchargé la version 9.10 d'Ubuntu, non. »
<!-- more -->
Pour conclure cette série d'articles, je vais entamer le volet probablement le plus important, le plus capital, le plus essentiel. Oui, utiliser du logiciel libre, ça fait pauvre, radin, clodo, gitan… Et ça, nos chers Directeurs des Systèmes d'Informations ont beaucoup de mal à s'y résigner.
# « Pas assez cher mon fils. »
Parce crâner dans les réunions de directions en vantant son architecture révolutionnaire qui permet de faire la pluie, le beau temps, le café, la vaisselle et le ménage dans la salle-machine et qui a seulement coûté un bras et demi à l'entreprise, c'est quand même beaucoup plus valorisant que d'expliquer pourquoi utiliser du code source ouvert est important pour la transparence, l'évolutivité et la pérennité des solutions.
Aaaah… Solution… Le mot magique. Aujourd'hui une entreprise se moque éperdument du fait qu'un produit soit plus ou moins ouvert, l'enferme plus ou moins, la rende plus ou moins captive d'un éditeur. Non, aujourd'hui, la DSI standard cherche une « solution » pour répondre rapidement à un « besoin », au détriment de toutes ces considérations, pourtant basiques et permettant d'apporter des réponses bien plus pertinentes à long terme. Aucune importance tout cela, l'utilisateur/enfant gâté veut tout, tout de suite et veut en plus que le produit complètement fermé s'intègre parfaitement avec ses derniers caprices, chose qui serait bien plus simple à faire dans un environnement ouvert.
# Mafia du soft en force !!1!
Quant aux « standards de l'industrie » si chers à toutes les sociétés de services, c'est un sophisme dont nous n'avons pas encore mesurer toute l'absurdité. Sous prétexte que quelques grands groupes utilisent tel ou tel produit, on devrait automatiquement l'intégrer dans toutes les entreprises, quelque soit leur taille, leur métier ou leur spécificité. « Comment ? Vous n'avez pas insert-a-software-name-here dans votre infrastructure ? Bah, il va falloir mon bon Monsieur, parce que c'est un standard de l'industrie et que nous, on bosse avec… ». Navrant… Et je passe sur le tristement célèbre « Ah… Vous le faites tourner sous Linux… Désolé, on ne va pas être compatible alors… ».
ais le côté obscur du soft, l'informatique de gestion, est probablement la pire des mafias : n'importe quel logiciel de finance/comptabilité/paie, même le plus mal écrit, même le plus archaïque et le plus mal-pratique ne pourra probablement jamais être libre. Pourquoi ? Ils sont tous validés par les banques, les organismes financiers et des normes et brevets drastiques en terme de coût. C'est écrit avec les pieds, leur mise en œuvre est laborieuse, ils ont un coût prohibitif seulement voilà, ça concerne les pépettes, la thune, le pèse, donc l'entreprise est prête à mettre n'importe quel prix.
Et que dire des développeurs ? Pourvu qu'ils pondent le moins de lignes de code possible (moins de code = plus de glande sur Internet, on aurait du mal à les blâmer), ils se foutent éperdument que les logiciels, bibliothèques ou langages qu'ils utilisent soit propriétaires, fermés, soumis à brevet, etc… Après tout, ce qui compte c'est la solution.
# Thèse, anti-thèse, foutaises
Le logiciel libre, c'est avant tout une philosophie et une méthode. Les mentalités sont en train de changer (ou alors le nombre de hippies communistes inflitrés augementent, c'est selon…) mais c'est lent, très lent. Oui, des logiciels libres ont réussi à devenir des standards de l'industrie (Apache, Tomcat, MySQL, Linux dans une moindre mesure) mais l'évolution est lente et douloureuse et les problèmes sont encore nombreux.
Donc, je ne crois pas au logiciel libre. En tout cas, je ne pense pas qu'il pourra un jour devenir une évidence pour tout le monde, à tous les niveaux, particuliers ou professionnels. Non, cela restera une activité intéressante, une passion de bricoleurs, un formidable terrain d'expérimentation, une manière ingénieuse d'apprendre et de se former. Mais tant qu'il existera une industrie du logiciel, elle ne sera jamais libre.
Alors oui, je rêve d'un monde où tous les formats seraient ouverts, où tous les logiciels seraient libres et donc où tout ne serait que service, où les entreprises et les éditeurs de logiciels ne seraient jugés que sur leur compétence et non pas sur leur capacité à emprisonner des clients dans un portfolio applicatif contraignant. Mais ce monde n'existe pas et n'existera probablement jamais.

View File

@@ -0,0 +1,13 @@
+++
title = "IPv4 : plus que 6% d'adresses disponibles…"
date = 2010-07-18
aliases = [ "post/2010/07/18/IPv4-:-plus-que-6-d-adresses-disponibles…"]
[taxonomies]
tags = [ "ipv6" ]
+++
[Je me disais aussi que mon compteur descendait drôlement vite depuis quelques jours…](http://tunnelbroker.net/) [L'IANA vient d'émettre un nouveau bulletin d'alerte](http://www.zdnet.fr/blogs/infra-net/ipv6-il-y-a-urgence-a-migrer-selon-l-iana-et-la-commission-europeenne-mais-les-freins-sont-encore-nombreux-39752710.htm) concernant la pénurie d'adresses allouables en IPv4 (le dernier datait d'avril pour indiquer qu'il restait seulement 8%).
Tu peux donc admirer un magnifique archétype du comportement humain : l'homme fonce droit vers un mur avec une Ferrari, il sait qu'il va se crasher, mais il continue d'accélérer…
Heureusement que je ne suis qu'un singe…

View File

@@ -0,0 +1,81 @@
+++
title = "IPv6, tunnelbroker et table forward (partie 2)"
date = 2010-07-23
aliases = [ "post/2010/07/01/IPv6,-tunnelbroker-et-table-forward-(partie-2)"]
[taxonomies]
tags = [ "ipv6" ]
+++
« Mes geeks ? Je leur donne de l'IPv6 tous les jours ! C'est pour ça qu'ils ont la barbe douce et soyeuse. »
*Bernard, producteur de geeks depuis 20 ans*
<!-- more -->
[Dans la première partie|/post/2010/06/30/IPv6%2C-tunnelbroker-et-table-forward], on a vu comment monter un tunnel à l'aide d'un fournisseur de tunnel. Maintenant, il est grand temps de relier Tatie Jeanette à l'Interwebz v6.
# Passe, passe, passe le /48, y'a du monde sur la corde à linge…
Si ta mémoire n'est pas encore complètement anéantie par les vapeurs d'alcool, [tu te souviens sûrement](/post/2009/12/09/IPv6-dans-la-pratique-%28partie-3%29) qu'on avait déjà fait appel à `radvd` pour annoncer des adresses et un routeur sur le réseau. Donc rebelote.
On va donc commencer par choisir une adresse de routeur sur la plage `2001:dede:abba::/48` pour le premier réseau. Toujours dans `/etc/network/interfaces` :
```
iface eth0 inet6 static
# Ceci est purement une convention
# tu peux mettre ce que tu veux grand coquin
address 2001:dede:abba:0:ff:ff:ff:ff
netmask 64
```
Et on configure un `radvd` des familles pour prévenir tout le monde qu'il y a de la soupe IPv6 dans la cuisine, avec un truc du genre :
```
interface eth0 {
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix 2001:dede:abba::/64 {
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
};
```
Dans `/etc/radvd.conf`. Comme j'ai horreur de réfléchir (faudrait pas que je me fasse un claquage de cerveau en pleine Coupe du monde, ce serait dommage), c'est l'exemple (adapté) du manuel. La seule subtilité dans tout ça, c'est que j'ai volontairement choisi un réseau en /64 pour subdiviser le /48. Pourquoi ? Parce que j'avais pas envie de faire simple ce matin…
Si tu as un message d'avertissement étrange au démarrage de radvd, je te conseille de décommenter la ligne suivante dans `/etc/init.d/radvd` et de la couper/coller au début de la fonction `start` de ce même fichier :
```
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
```
Parce que si tu ne forwardes pas, les nains se vautreront comme des glands au lieu de courir très vite dans les fils.
Bref, au bout de quelques secondes, tu devrais voir apparaître de magnifiques adresses IPv6 dans tout ton LAN chéri et tu devrais pourvoir alors découvrir les merveilles de l'Interwebz v6 avec encore plus de porno jap.
# Toc, toc !
Avec comme seul petit bémol que maintenant, ton réseau local a le trou béant puisque chaque machine est connectée en direct sur Internet à travers IPv6 et ton routeur. Horreur, scandale, ignominie, Barbara Stresand, comment va-t-on faire pour protéger mes pauvres babasses Windaube ?!? Il est vrai que maintenant, n'importe quel gugusse sur Internet peut voir directement ton imprimante et ton FTP local de pr0n, c'est bof bof. C'est donc le moment de mettre à profit la table `FORWARD` de `ip6tables` pour obtenir une config proche de ce qu'on a en IPv4+NAT.
Sur le routeur, on procède donc comme suit :
```
# On jète tout ce qui travers le routeur
# IPv6 ne fonctionne plus que sur le routeur à ce moment
ip6tables -P FORWARD DROP
# On ouvre complètement ICMPv6
# On peut de nouveau pinguer l'Internetv6
ip6tables -A FORWARD -p icmpv6 -j ACCEPT
# On autorise l'ensemble du /64 à sortir vers Internet
ip6tables -A FORWARD -p all -s 2001:dede:abba::/64 -j ACCEPT
# On autorise les connexions retours légitimes
ip6tables -A FORWARD -d 2001:dede:abba::/64 -m state \
--state RELATED,ESTABLISHED -j ACCEPT
# Pour autoriser des serveurs sur le LAN (DNS, HTTP, etc…)
# il suffit de les ajouter
# Ici, on autorise l'accès à un serveur Web disponible sur le LAN
ip6tables -A FORWARD -p tcp -d <adresse IPv6 complète du serveur>/128 \
--dport 80 -j ACCEPT
# Pour le fun, on loggue tous les paquets merdeux
ip6tables -A FORWARD -j LOG
```
Et pour vérifier qu'on a pas fait de la merde en barre, on peut toujours s'appuyer sur [un scan de port IPv6 des familles](http://www.subnetonline.com/pages/ipv6-network-tools/online-ipv6-port-scanner.php) qui permettra de valider que personne n'a accès aux machines derrière le routeur.
En exercice pour la semaine prochaine, tu me copieras 100 fois ton adresse IPv6 et tu protègeras aussi le routeur avec `ip6tables`.

View File

@@ -0,0 +1,57 @@
+++
title = "Mais non, c'est pas difficile de faire son propre serveur mail (1)"
date = 2010-08-13
aliases = [ "post/2010/08/13/Mais-non,-c-est-pas-difficile-de-faire-son-propre-serveur-mail-(1)"]
[taxonomies]
tags = [ "mail", "système", "linux", "auto-hébergement" ]
+++
« *Des fois, je reçois même des messages qui me proposent d'agrandir mon pénis. Ils pensent vraiment à tout* »
ichel, utilisateur convaincu du mail…
<!-- more -->
Tu en as marre de voir de la publicité pour des anneaux vibrants sur la gauche de tes messages ? Tu n'en peux plus des limites stupides genre « *je peux pas envoyer un flim en HD de 10Gio par mail, stro nul LOLILOL* » ? Tu n'a pas de problème particulier d'érection du coup tu te demandes à quoi servent 90% des messages que tu reçois ? Tu te dis que ce serait bien pratique de connaître l'admin de ta messagerie pour lui payer une bière parce qu'il fait un taf formidable ?
Rassure-toi jeune chimpanzé, je suis là pour t'aider. Je vais t'apprendre à monter ton propre serveur de messagerie comme ça tu pourras briller en société et surtout, surtout, SURTOUT pé-cho à mort… C'est simple depuis que j'ai monté le mien, j'ai presque failli avoir une relation sexuelle avec une jeune fille consentante, c'est pour te dire à quel point la méthode est infaillible !
# I DID IT FOR THE LULZ
onter un serveur de messagerie complet, c'est pas aussi compliqué que de laver la vaisselle ou de prendre une douche, mais faut quand même être capable de marcher et mâcher un chewing-gum en même temps. C'est pour ça, qu'on va faire un truc nouveau, que t'as sûrement pas l'habitude : s'organiser. Voilà, on est grand maintenant, va falloir arrêter de faire le foufou et de partir bille en tête dans toutes les directions. On va donc procéder comme suit :
* on va lister les quelques logiciels de hippies communistes dont on a besoin ;
* on va voir ce qu'il faut faire au niveau des DNS ;
* on va monter un truc de base qui marche et qui authentifie ;
* on va sécuriser le bouzin pour éviter que des vilains monsieur chopent les coordonnées de ta prostituée habituelle (ou tout autre message à caractère pas important genre ta banque, ton compte paypal, etc…) ;
* on va s'assurer que les vendeurs de pillules bleues à pas cher ne vont plus t'embêter ;
* on va se mettre un super Webmail 2.0 qui tue la mort pour impressionner tes amis.
Donc : postfix, dovecot, openssl, ce sera la base pour la messagerie à proprement parler. Pour le Webmail, on aura besoin de apache et mysql. Tous ces machins peuvent être trouvés dans toutes les bonnes boucheries.
# Je ne vois pas le rapport avec le Mexique…
Pour commencer va falloir réserver un nom de domaine, ou bien te faire déléguer un sous-domaine par un copain. Pour l'exemple, on va prendre `buttse.cx`. Après, il va falloir manipuler [les enregistrements MX](http://fr.wikipedia.org/wiki/Domain_Name_System#MX_record).
Késséencore que ce truc ? Ça permet simplement aux logiciels de messagerie de déterminer quel est le ou les serveurs qui s'occupent de gérer les messages pour ton domaine, avec un poids pour déterminer la priorité. Donc un simple enregistrement MX de poids 1 pointant sur l'IP de ton choix suffit à déterminer la messagerie pour tout un domaine ou un sous-domaine. Si on met deux enregistrements avec des poids différents, les messages seront envoyés à l'un puis à l'autre si le premier ne répond pas (on parle alors de MX secondaires). En mettant le même poids, on répartit les messages entre les deux.
```
$ host -t mx buttse.cx
buttse.cx mail is handled by 10 26.57.145.36
buttse.cx mail is handled by 15 128.56.69.32
$ host -t mx ilike.buttse.cx
ilike.buttse.cx mail is handled by 1 relaismsg.minefi.gouv.fr.
ilike.buttse.cx mail is handled by 1 malinois.minefi.gouv.fr.
```
Parce qu'on est pas des gens qui puent des doigts, on peut faire un truc plus propre en mettant un enregistrement A (IPv4) et AAAA (IPv6) et en faisant pointer la messagerie dessus. J0r :
```
$ host -t mx ihad.buttse.cx
ihad.buttse.cx mail is handled by 1 mx.buttse.cx.
$ host mx.buttse.cx
mx.buttse.cx has address 92.122.22.149
mx.buttse.cx has IPv6 address 2001:dead:beaf:abba::1
```
À la semaine prochaine.

View File

@@ -0,0 +1,58 @@
+++
title = "Mais non, c'est pas difficile de faire son propre serveur mail (2)"
date = 2010-08-20
aliases = [ "post/2010/08/11/Mais-non,-c-est-pas-difficile-de-faire-son-propre-serveur-mail-(2)"]
[taxonomies]
tags = [ "mail", "système", "linux", "auto-hébergement" ]
+++
Avec Postfix, je suis détendu du mail…
<!-- more -->
aintenant que le DNS de `buttse.cx` est correctement réglé, on va mobiliser quelques neurones pour paramétrer Postfix de sorte à ce que ça ressemble vaguement à quelque chose. Mais d'abord, késsekessé Postfix et à quoi ça va bien pouvoir nous servir ?
# I HAZ A MAIL SERVER
Comme tu t'en doutes probablement déjà, Postfix n'est pas un presse-purée informatique un peu sofistiqué. C'est ce qu'on appelle un [Mail Transfer Agent](http://fr.wikipedia.org/wiki/Mail_Transfer_Agent) ou MTA. Ça permet donc d'envoyer des messages vers l'Interwebz et d'en recevoir sur les adresses MX que l'on a configuré. Si j'étais pas une grosse faignasse, il aurait fallu également s'intéresser à un [Mail Delivery Agent](http://fr.wikipedia.org/wiki/Mail_Delivery_Agent) ou MDA pour livrer les messages. Seulement comme on va faire une configuration de Postfix assez simple, il sera capable de faire office de MDA, tout seul comme un grand.
On va donc faire un vulgaire « je balance les messages aux utilisateurs locaux sans trop réfléchir », simple, bête et méchant.
# Bien joué les Smarties
Une conf de base qui tourne et qui est capable de faire MTA/MDA, ça va pas chercher bien loin. Attention, démonstration :
```
inet_protocols = all
inet_interfaces = all
mynetworks = 1.2.3.4, 127.0.0.0/8, [2001:dead:beef::]/64, [::1]/128
myhostname = lakeketteajeanmi.buttse.cx
myorigin = buttse.cx
mydestination = $myhostname,localhost.$mydomain,localhost,buttse.cx
mail_owner = postfix
unknown_local_recipient_reject_code = 550
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
home_mailbox = Maildir/
```
Tout de suite, tu sens la puissance du merdier : en genre 10 lignes, tu es capable d'envoyer des messages depuis `mynetworks`, d'accepter les messages à destination de `mydestination` et de les livrer, au format `Maildir`, dans le répertoire personnel de l'utilisateur de la machine.
Pour les quelques raffinements supplémentaires, il s'agit essentiellement d'être capable d'écouter sur toutes les interfaces en IPv4/6 (`inet_protocols` et `inet_interfaces`) et de préciser le domaine de messagerie des utilisateurs locaux pour les messages sortants via `myorigin`. Bien sûr, rien ne t'empêche de bricoler un peu les rézo pour adapter à ton cas (par exemple autoriser directement ton réseau LAN).
Bon, je sais que c'est dur parce que tu viens de faire un gros effort intellectuel, mais il va quand même falloir se remuer les doigts de pied, pour démarrer Postfix et faire un ou deux petits essais :
```
$ echo "test" | mail -s "Come suck my…" zuckerberg@facebook.com
$ echo "un autre test" | mail -s "TA MÈRE LOL" root@buttse.cx
```
Il suffit de vérifier le log des messages (`/var/log/maillog` ou `/var/log/mail.log`) pour savoir si ça a marché ou pas. Alors, si ton fournisseur de Minitel 2.0 bloque le port 25 ou que ton adresse est blacklisté/greylisté (je ne te cache pas que c'est plus que probable), ça passera probablement pas. Le message sera considéré comme du spam ou rejeté. La seule solution dans ce cas, c'est de passer par le serveur SMTP de ton fournisseur d'accès pour tous les messages sortants. On s'accroche parce que c'est super compliqué :
```
relayhost = [smtp.monfaiquipue.com]:25
```
Pfiou… Va te falloir au moins deux ou trois jours pour récupérer après un machin pareil ! En ajoutant cette simple ligne, ton serveur de mail passera par ton fournisseur d'accès pour l'envoi au lieu d'envoyer lui-même. Ça induit un temps de latence supplémentaire et quelques autres désagréments, mais c'est malheureusement (trop) souvent la seule solution…
Bon, j'ai l'impression que tu es tout rouge et tout moite, j'ai pas envie que tu te fasses un claquage de cerveau, on verra donc la partie authentification et accès à distance la semaine prochaine.

View File

@@ -0,0 +1,89 @@
+++
title = "Mais non, c'est pas difficile de faire son propre serveur mail (3)"
date = 2010-08-27
aliases = [ "post/2010/08/27/Mais-non,-c-est-pas-difficile-de-faire-son-propre-serveur-mail-(3)"]
[taxonomies]
tags = [ "mail", "système", "linux", "auto-hébergement" ]
+++
« La messagerie, c'est cool ! »
*Chacal-puant, grand chef sioux*
<!-- more -->
Bon alors, si tu as bien tout suivi, tu devrais normalement être en mesure d'envoyer des messages cochons à tout un tas de personnes et en recevoir dans ton magnifique répertoire personnel, dans un dossier appelé `Maildir`. Sauf que pour le moment, tu es pas capable d'envoyer des messages depuis n'importe où avec ton serveur et en prime, tu peux consulter tes messages seulement en local avec… `cat`… Tout cela sent quand même pas mal mauvais des aisselles…
Va donc falloir se sortir les doigts du pot de confiture et les reposer sur son petit clavier pour sécurauthentifier (ne cherche pas, c'est un néologisme maison) ton serveur de messagerie. Pour cela, on va faire un peu de SSL, histoire de mettre du chiffrement des familles et qu'aucun mot de passe ne circule en clair, un peu de Dovecot et (encore) un peu de Postfix pour ce qui concerne l'authentification.
# ENVOIE DU SSL GROS !§!!
Je ne vais pas te donner un cours sur OpenSSL ou sur le SSL en général. D'abord, parce que j'ai pas que ça à glander non plus (on est là pour un serveur mail, pas pour faire joujou avec du chiffrement), ensuite parce qu'on trouve plein de gens bien moins simiesques qui expliqueront probablement ça bien mieux. Saches donc que tu auras besoin du certificat racine (CA) de ton autorité préférée, de ta clé privée et de ton certificat. Allez, je suis bon prince, [je te file](http://hausheer.osola.com/docs/9) un [lien ou deux](http://www.tc.umn.edu/~brams006/selfsign.html) pour pas que tu galères comme un porc.
Un config de `dovecot` fonctionnel qui fait du SSL, ça va pas chercher bien loin :
```
protocols = imap imaps
listen = *,[::]
## SSL settings
ssl_cert_file = /chemin/vers/mon/certificat/server.crt
ssl_key_file = /chemin/vers/ma/cle/privee/server.key
protocol imap {
}
```
Si tu t'attendais à un grand tour de magie avec des lapins, des paillettes et les boobs de l'assistante, ça doit te faire tout drôle : oui, il suffit de pas grand chose pour faire un serveur IMAP parfaitement opérationnel et sécurisé (qui fait du STARTTLS ++ET++ du IMAPS). Tu peux donc configurer Thunderbird et admirer ta boîte de réception :').
aintenant que tu t'es bien échauffé les paluches, on peut continuer la même tambouille avec Postfix :
```
# Référence de chiffrement TLS
smptd_tls_CAfile = /chemin/vers/mon/certificat/racine/root.pem
smtpd_tls_cert_file = /chemin/vers/mon/certificat/server.crt
smtpd_tls_key_file = /chemin/vers/ma/cle/privee/server.key
smtpd_use_tls = yes
smtp_use_tls = yes
smptd_tls_auth_only = yes
```
Comme pour `dovecot`, on indique le chemin des certificats et on précise qu'on souhaite l'utiliser pour le SMTP en mode client, donc d'un serveur à un client, ou d'un serveur à un autre serveur. Par ailleurs, on autorisera l'authentification que s'il y a chiffrement.
# Nom, prénom, matricule…
Dovecot authentifie correctement et en chiffré. Reste que les utilisateurs légitimes ne peuvent toujours pas envoyer de messages depuis l'extérieur de ton réseau. On va donc bricoler un petit truc entre Dovecot et Postfix pour aider un peu. Première étape, créer une socket d'identification au niveau de `dovecot` :
```
auth default {
mechanisms = plain
passdb pam {
}
userdb passwd {
}
user = root
socket listen {
client {
path = /var/spool/postfix/private/auth
mode = 0660
user = postfix
group = postfix
}
}
}
```
Tu peux arrêter de m'applaudir… Non, je ne fais pas de la magie, je fais simplement bien mon boulot… Et je remets le couvert avec Postfix :
```
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
```
En fait, on indique simplement le type et le chemin de la socket puis on complète les autorisations (dernière ligne), pour préciser qu'on accepte les messages authentifiés.
Il y a franchement pas de quoi faire sauter une braguette, mais avec le peu que tu as tapoté sur ton petit clavier avec tes petits doigts musclés, tu as un serveur parfaitement opérationnel : envoi et réception de messages, authentifiés, sécurisés et à distance. La semaine prochaine, on se protègera des vendeurs de Viagra et du Roi du Sénégal.

View File

@@ -0,0 +1,62 @@
+++
title = "Mais non, c'est pas difficile de faire son propre serveur mail (4)"
date = 2010-09-24
aliases = [ "post/2010/09/24/Mais-non,-c-est-pas-difficile-de-faire-son-propre-serveur-mail-(4)"]
[taxonomies]
tags = [ "mail", "système", "linux", "auto-hébergement" ]
+++
Pfiou, je vais pouvoir prendre des vacances bien méritées !
<!-- more -->
aintenant que tu as ton propre serveur de messagerie et que ce dernier est sécurisé avec du chiffrement dans tous les sens, qu'il est accessible de tout l'Interwebz sauf des maychamps pédo-nazi-terroristes, il est temps de botter le cul des vendeurs de pilules bleues !
# Y a-t-il un pourriel dans la boîte ?
On va donc s'installer un petit anti-spam des familles, j'ai nommé Spamassassin. Une fois que tu l'auras installé (en suivant la merveilleuse documentation de ta distribution), il faut demander gentiment à Postfix de balancer les messages à ce dernier avant de faire une livraison.
On pourrait faire subtil en considérant que les messages en sortie (vers Internet) ne doivent pas repasser par l'anti-spam. Mais bon, la subtilité c'est pas vraiment le genre de la maison. Et puis, j'ai la flemme et ça a à peu près autant d'intérêt que d'essayer de se chatouiller les testicules avec le nez. On va donc tout faire passer par spamassassin sans réfléchir.
Donc, au niveau de Postfix, on remplace la ligne suivant du fichier `/etc/postfix/master.cf` :
```
smtp inet n - - - - smtpd
```
Par :
```
smtp inet n - - - - \
smtpd -o content_filter=spamassassin
```
On crée ensuite une directive spamassasin (correspondant en fait à une vulgaire commande) :
```
spamassassin unix - n\ n\ -\ -\ pipe
user=nobody argv=/usr/bin/spamc -f -e
/usr/sbin/sendmail -oi -f ${sender} ${recipient}
```
Un démarrage de spamassassin et petit reload de Postfix et clic-clac merci Kodak, tout le monde doit retrouver ces petits. Pour en être sûr, un petit coup d'œil dans les logs devrait suffire.
# Déterminer qui veut agrandir ton pénis
Je ne prétends pas faire un cours complet sur Spamassassin : on pourrait y passer des heures et pour une messagerie à usage strictement privée, tu ne devrais normalement pas recevoir des kilotonnes de pourriels. Néanmoins, pour assurer le minimum, je te conseille ces quelques lignes à placer dans le `local.cf` de Spamassassin :
```
required_hits 5
report_safe 1
rewrite_header Subject *****SPAM*****
use_bayes 1
bayes_auto_learn 1
ok_locales fr\
whitelist_from *@buttse.cx
```
Cette config permet de spécifier le langage « normal » que l'on utilise dans les messages, comment signaler les messages frauduleux, etc… Bref, c'est le minimum syndical mais si tu reçois beaucoup de chaînes de l'amour, il faudra sûrement ajouter pas mal de choses pour obtenir un résultat optimal.
# Thèse, anti-thèse, foutaises
Cette petite série d'articles t'aura permis d'installer et de configurer un serveur de messagerie basique, simple et pourtant très efficace que tu pourras mettre au choix sur un serveur hébergé ou (mieux) dans ta maison personnelle appartenante ou (encore mieux) sur ta babasse personnelle à toi. Ça va te permettre de faire plein de choses rigolotes qu'on ne peut d'habitude pas faire chez les fournisseurs de messagerie habituels comme :
* avoir un domaine de messagerie original et coquin ;
* pouvoir créer des alias à la volée pour avoir des adresses « jetables » ;
* assurer facilement la sauvegarde de ta messagerie ;
* décider toi-même des quotas de messagerie, de la taille des messages, des limitations par dossier…

View File

@@ -0,0 +1,76 @@
+++
title = "Appliquer facilement un filtre HADOPI et journaliser ses entrées"
date = 2010-09-27
aliases = [ "post/2010/09/27/Appliquer-facilement-un-filtre-HADOPI-et-le-journaliser-les-entrées"]
[taxonomies]
tags = [ "hadopi", "pare-feux" ]
+++
Juste pour le fun…
<!-- more -->
Si tu maîtrises à 100% ton routeur (ou si tu maîtrises à 100% ton pare-feu personnel), tu peux toi aussi te prémunir très facilement des soucis que tu pourrais rencontrer sur Internet.
Pour cela, c'est très simple, on commence par se rendre sur [le site du RIPE](http://www.ripe.net/db/whois-free.html) (c'est l'organisme qui distribue les adresses IP sur Internet pour la partie européenne). Une fois que tu auras tapé le mot-clé « HADOPI » dans le champs recherche, tu pourras admirer les plages d'adresses IP qui ont été attribuées à la Haute Autorité. Oui, parce qu'il faut savoir que la HADOPI ne peut pas utiliser des adresses chez M. Orange ou M. SFR pour espionner les rézo de P2P. Sinon, elle pourrait se faire dénoncer elle-même, ce qui provoquerait une rupture du continuum espace-temps qui pourrait déchirer le tissu même de l'univers… Ou elle pourrait s'envoyer une lettre recommandé et se suspendre l'accès Internet.
Bien sûr, il ne faut pas non plus oublier les quelques adresses indispensables de [TMG](http://fr.wikipedia.org/wiki/Trident_Media_Guard), le bras armé de la HADOPI. La page Wikipédia nous renseigne également sur les IP publiques utilisées par les serveurs chargés de faire la détection sur les rézo P2P, des contrefacteurs honteux, de ces criminels en puissance qui menacent de lapider nos artistes que nous aimons tant à coup de bits (sans mauvais jeu de mots… enfin, si en fait ^^).
# Ainsi font, font, font les petits paquets IP
On va donc se servir de cette mine de renseignement ainsi que de la [calculatrice IP de HobbesWorld|http://www.hobbesworld.com/reseaux/calcip.php|fr], que je me permet de remercier au passage, pour mettre en place quelques règles dans ton pare-feu/routeur préféré. Le but ici est exclusivement de respecter les lois et directives européennes et notamment 2002/58/CE et 95/46/CE relative à la protection de la vie privée et des données personnelles.
Donc, prenons par exemple, cette plage pour la HADOPI :
![HADOPI, plage 3, sept. 2010](/.hadopi_ip_test_m.jpg)
Notre petite calculatrice IP nous indique cette plage correspond au masque 28, ce que l'on peut vérifier très facilement avec la commande `sipcalc` :
```
$ sipcalc 90.80.100.192/28 | grep range
Network range \ - 90.80.100.192 - 90.80.100.207
Usable range \ - 90.80.100.193 - 90.80.100.206
```
Allez, je suis bon prince, je t'épargne les calculs, voici l'ensemble des adresses pouvant être utilisées par la HADOPI ou la société TMG, avec leur masque et tout le tralala :
* 85.159.236.252/30
* 85.159.232.80/30
* 82.138.81.211
* 193.107.240.0/22
* 193.105.197.0/24
* 90.80.155.240/28
* 90.80.155.208/28
* 90.80.100.192/28
* 80.12.48.0/24
* 195.6.180.141
Voilà, voilà, voilà *\*sifflote\**
Reste maintenant à éditer quelques règles au niveau du pare-feu pour empêcher les empêcheurs de tourner en rond de nuire à tes télécha^W^Wta vie privée.
# « HADOPI, ça marche ! »
J'utilise un routeur/pare-feu Linux, je donnerai donc seulement les règles `iptables` qui vont bien, je te charge d'adapter à ta situation/ton matériel. Donc, une règle toute conne comme celle qui suit devrait faire l'affaire :
```
iptables -I FORWARD -p all -s 85.159.236.252/30,85.159.232.80/30,\
82.138.81.211/32,193.107.240.0/22,193.105.197.0/24\
90.80.155.240/28,90.80.155.208/28,90.80.100.192/28\
80.12.48.0/24,195.6.180.141/32 -j DROP
```
Bon évidemment, il vaut mieux la mettre avant d'autres règles (des fois que tu ais d'autres choses à bloquer…). Maintenant, il ne reste plus qu'à vérifier que cela fonctionne…
# Hé, ho, on est pas cousins !
Pour cela, je te propose deux méthodes, une compliquée, une simple :
* pinguer l'adresse 193.105.197.1 : si elle répond, c'est pas bon. Si elle ne répond pas, c'est bon.
* télécharger un film sur un réseau P2P quelconque et attendre la lettre recommandée.
Plus sérieusement, on peut très simplement logguer les paquets avec une petite bafouille de ce type, avant la règle donnée au point précédent :
```
iptables -I FORWARD -p all -s 85.159.236.252/30,85.159.232.80/30,\
82.138.81.211/32,193.107.240.0/22,193.105.197.0/24\
90.80.155.240/28,90.80.155.208/28,90.80.100.192/28\
80.12.48.0/24,195.6.180.141/32 -j LOG --log-prefix 'HADOPI'
```
Si nos petits camarades responsables de certains trackers célèbres peuvent appliquer le même filtre, ce serait encore mieux. Et au passage, je vous déconseille fortement de vous connecter avec un client eMule sur les adresse en 85.x.x.x citées dans le présent billet.

View File

@@ -0,0 +1,74 @@
+++
title = "Organiser des volumes miroirs LVM"
date = 2010-10-11
aliases = [ "post/2010/10/11/Organiser-des-volumes-mirroirs-LVM"]
[taxonomies]
tags = [ "système", "linux", "lvm" ]
+++
Je sais pas pourquoi mais je sens que ça va intéresser nettement moins de monde que la HADOPI
<!-- more -->
LVM, c'est le bien. Chaque fois que tu utilises LVM, tu sauves un pays de la famine, ton pénis grandit et tu reconquiers l'être aimé.
Aujourd'hui, je vais te montrer comment on peut faire du RAID, via les volumes miroirs et LVM. Et là, normalement, ta première réaction, c'est de saigner du nez : mais pourquoi faire du RAID avec un machin qu'est pas conçu pour faire du RAID alors qu'on a un truc pour faire du RAID, `mdadm` en l'occurence ? D'abord, parce qu'on peut (si, si, c'est une bonne raison) et ensuite parce que c'est bien gentil les outils standards de Linux pour faire du RAID mais c'est pas souple pour deux ronds : impossible d'agrandir un disque à chaud (à moins de faire des partoches dans tous les sens, c'est pas super élégant) et le changement de disque est une vraie plaie.
Alors oui, les petits singes un peu malins qui vont me dire qu'on pourrait très bien monter un RAID dans un LVM… Oui mais voilà, c'est super crade, on rajoute une couche pour pas grand chose, c'est toujours pas très souple et, si on voulait aller jusqu'au bout de la logique, il faudrait monter un LVM sur le RAID sur le LVM. Autant, je conçois que la partouze peut être une activité enrichissante, autant je trouve que là, c'est un peu beaucoup de participants les uns sur les autres.
# Miroir, mon beau miroir, dis-moi qui est le plus geek
J'ai un disque SATA `/dev/sda` et un disque `/dev/sdb` de 20Gio chacun, que l'on a transformé en volume physique, et qui sont dans le même groupe de volumes `marmouset_sucks`. Pour créer directement un volume miroir, on peut simplement utiliser une commande du type :
```
# lvcreate --name levrette --size 5G --mirrors 1 --mirrorlog core marmouset_sucks
Logical volume "levrette" created
# lvs -a -o +devices
LV VG Attr LSize
levrette marmouset_sucks mwi-a- 5,00g
[levrette_mimage_0] marmouset_sucks Iwi-ao 5,00g
[levrette_mimage_1] marmouset_sucks Iwi-ao 5,00g
```
Les options `--mirrors` et `--mirrorlog` servent respectivement à indiquer le nombre de copie du volume d'origine que l'on souhaite (en l'occurence 1, tu auras donc les données 2 endroits différents) et à préciser la méthode de conservation des données (ce que l'on verra en détails après). La dernière commande te permet de constater que ton volume est bien répliqué sur deux Physical Volumes distincts. Peut-on faire la même chose avec un volume existant ? Ben tiens ! On va s'gêner :
```
# lvcreate --name missionnaire --size 2G marmouset_sucks
Logical volume "missionnaire" created
# lvconvert --mirrors 1 --mirrorlog core /dev/marmouset_sucks/missionnaire
marmouset_sucks/missionnaire: Converted: 0,4%
[…]
marmouset_sucks/missionnaire: Converted: 100%
```
Contrairement à un RAID classique, qu'on peut difficilement créer après-coup, là tu fais à peu près ce que tu peux tant que tu as 2 PVs dans ton VG (non, c'est pas un acronyme pour vagin cochon !).
# Et donc c'est quoi le mirrorlog ?
Ahem, ahem… C'est un peu là qu'on commence à voir les limites du truc (et qu'on commence au passage à se rendre compte que c'est pas vraiment fait pour ^^). Si tu changes le statut de tes volumes (activation/désactivation), tu vas constater que les volumes se reconstruisent après (!) :
```
# vgchange -an marmouset_sucks
0 logical volume(s) in volume group "marmouset_sucks" now active
# vgchange -ay marmouset_sucks
2 logical volume(s) in volume group "marmouset_sucks" now active
# lvs
# lvs | grep marmou
levrette marmouset_sucks mwi-a- 5,00g 57,34
missionnaire marmouset_sucks mwi-a- 2,00g 100,00
```
\#fail comme disent les jeunes. Alors est-on condamné à tout reconstruire à chaque démarrage de la machine ? Tu te doutes bien que non.
Par défaut, LVM crée un journal des volumes miroirs sur un troisième volume physique. Ce dernier ne prend pas grand chose (1 extent par volume répliqué) et on peut donc très bien le placer sur une toute petite partition sur le disque système ou même dans un volume logique sur un autre volume group ! Donc, en ajoutant un `/dev/sdc` (qui pourrait donc être à peu près n'importe quoi si tu as tout bien suivi) :
```
# pvcreate /dev/sdc
Physical volume "/dev/sdc" successfully created
# lvcreate --size 200M --name helicoptere_thailandais --mirrors 1 marmouset_sucks
Logical volume "helicoptere_thailandais" created
# lvs -a -o +devices | grep heli
# lvs -a -o +devices | grep heli
helicoptere_thailandais marmouset_sucks mwi-a- 200,00m
[helicoptere_thailandais_mimage_0] marmouset_sucks iwi-ao 200,00m
[helicoptere_thailandais_mimage_1] marmouset_sucks iwi-ao 200,00m
[helicoptere_thailandais_mlog] marmouset_sucks lwi-ao 4,00m
```
Et le tour est joué \o/

View File

@@ -0,0 +1,40 @@
+++
title = "La licence globale aurait-elle été possible ?"
date = 2010-10-22
aliases = ["post/2010/10/22/La-licence-globale-aurait-elle-été-possible"]
[taxonomies]
tags = [ "internet", "hadopi" ]
+++
En ces temps troubles de HADOPI, celle qu'on avait fait sortir par la porte revient discrètement par la fenêtre…
<!-- more -->
J'ai pu constater que mes proches (et pas seulement ceux couverts de poux qui m'accompagnent dans ma cage) ont de plus en plus recours à des abonnements vers des services de VPN, d'anonymisation d'IP, de streaming ou de téléchargement direct. C'est un phénomène un peu nouveau : depuis que la Haute Autorité Des Orang-Outang Pour les Idioties sévit, même légèrement, tous ces dispositifs payants deviennent vachement à la mode.
Et c'est là que certains ressortent le spectre de la licence globale, supposée apporter une solution aux problèmes du téléchargement et de la rémunération des artistes. Après tout, c'est vrai : l'argent qui rentre dans les caisses de RapidShare ou MegaUpload, tu peux être certain qu'il ne verra jamais le comptable de la SACEM ! Payer une licence globale aurait donc été bien mieux que de laisser émerger des sociétés douteuses et peu scrupuleuses amassant beaucoup, mais alors beaucoup de pognons.
Et cela sans compter sur la quantité faramineuse que risque de coûter la HADOPI aux contribuables, puisqu'il va bien falloir payer TMG, [ces pots de miels débiles](/appliquer-facilement-un-filtre-hadopi-et-le-journaliser-les-entrees/), la dénonciation des IPs auprès des opérateurs, les lettres recommandés, et j'en passe ! Alors, la licence globale, une si bonne idée ?
# Permis de tuer
Il est vrai que la licence globale, ou l'extension de la taxe pour copie privée aux connexions Internet (en plus des CD, DVD, BD et autres médias de stockage plus ou moins volatiles) apparaît comme une sorte de miracle à l'heure où MegaVideo fait un chiffre d'affaire probablement hors de portée de l'imagination d'un Babouin. L'argent retourne dans les poches des ayant-droits et avec relativement peu d'intermédiaires en prime.
Néanmoins, elle pose un problème majeur : comment va-t-on répartir les 15€/mois dont tout abonné à l'Interwebz devrait s'affranchir ? On peut s'appuyer sur les statistiques des rézo P2P via une société tierce. Cela permettrait de rémunérer équitablement les ayant-droits mais ça ne donnerait pas beaucoup d'indications sur comment rémunérer les artistes derrière.
Alors pourquoi ne pas nationaliser les trackers BitTorrent ? En les déclarant d'utilité publique, on pourrait facilement tirer les chiffres de téléchargement et donner la rémunération correspondante aux gens qui la méritent. Et puis, ça permettrait à certaines chaînes de la TNT de diffuser le top 50 Emule de la semaine.
Ou alors une déclaration individuelle tous les mois ? Avec une notation de l'œuvre en prime, histoire d'avoir un retour client ?
Ahem, ahem… Tu sens bien que le système atteint là une sorte de limite. Sans compter le fait qu'on peut facilement faire des choses pour la création française mais qu'il serait presque impossible de faire pareil pour le cinéma américain ou la chanson engagée suédoise. 
Il faut aussi voir que l'on va probablement ruiner les intermédiaires actuels comme la FNAC, UGC ou Micromafia. Si je paye pour télécharger, quel est l'intérêt d'aller repayer pour aller au cinéma ? J'ai déjà peu de scrupules à copier les CD de mes potes puisque je paye indirectement la SACEM sur le média vierge… Alors si on m'y autorise et même m'y encourage, aucune raison de se faire chier : je vais piller comme un porc tout ce que je trouve !
# O RLY ?
Entre une HADOPI répressive au possible et une licence globale impossible à appliquer et peut-être trop permissive, finalement le statu quo d'il y a 5 ans donnait un bon compromis. Mais maintenant, il est trop tard.
Et finalement tout cela ne solde pas le fond du problème : à l'heure de la diffusion massive, immédiate et généralisée de la culture, peut-on encore faire entrer dans le domaine public des œuvres dont l'auteur est mort il y a 70 ans ? Imagine qu'il faudra attendre 70 ans après la toute proche overdose de Lady Gaga pour pouvoir tranquillement siffloter Poker Face sous la douche… C'est déjà une éternité à l'échelle de nos vies, à l'échelle d'Internet, ce n'est pas tenable.
Ce système débile et essouflé du XXème siècle ne pourra plus tenir bien longtemps… Il lui manque juste un déclic avant d'imploser…

View File

@@ -0,0 +1,32 @@
+++
title = "Moins de 100 jours avant la pénurie d'IPv4"
date = 2010-11-24
aliases = [ "post/2010/11/24/Moins-de-100-jours-avant-la-pénurie-d-IPv4"]
[taxonomies]
tags = [ "ipv6" ]
+++
Et nos chers FAI semblent toujours s'en battre les cacahuètes avec des pelles à tarte !
<!-- more -->
Depuis le temps que je scrute le compteur de [HE](http://ipv6.he.net/statistics/) en attendant patiemment la fin du monde, je dois avouer que la folie de ces derniers jours ne m'a pas laissé de marbre. Le compteur est tout de même passé d'un glorieux 215 jours à moins de 110, et ce d'un seul coup.
En fait, l'[APNIC](http://www.apnic.net/) et l'[AfriNIC](http://www.afrinic.net/) ont récemment récupéré respectivement 2 et 1 /8 qui a fait baisser très rapidement le compteur sous la barre de 4% d'adresses IPv4 encore disponibles. Et la migration dans tout ça ? Ça ressemble à un running gag mais pour l'instant les FAI en France étant à la rue.
Allez, faisons un petit tour d'horizon *for the lulz*. On va commencer par mes meilleurs copains de l'univers, j'ai nommé Orange. En plus d'être probablement l'un des plus lamentables FAI de cette planète (sans rire, c'est simplement extraordinaire… ils sont vraiment hors-concours sur un nombre incalculable de sujets…), c'est également un grand pionnier en matière d'IPv6. Depuis maintenant 10 mois, ils proposent UNE SEULE ET UNIQUE offre, réservée aux entreprises et non compatible avec le reste de leur gamme. Ils sont donc à la hauteur de mes attentes.
SFR… Hum comment dire ? Je ne sais même pas comment je pourrais exprimer ça… Il surpasse même Orange sur ce coup-là : l'opérateur histoirique a au moins eu le mérite de proposer quelque chose… Là, pas une offre, pas une option, pas un mot, même pas un murmure. On dirait bien que Neuf est plus occupé à faire naviguer les gens dans leur TV qu'à s'occuper de son réseau. C'est vrai que la télé, c'est le média de l'avenir !
Free est le bon élève si on regarde de loin. En effet, on peut très facilement activer des fonctionnalités IPv6 sur sa Freebox, c'est une simple option à cocher. Pourtant, tout n'est pas si rose et malheureusement Free ne fait pas du natif : un tunnel 6in4 est crée entre la Freebox et une gateway au bout du réseau de Free pour permettre une connectivité IPv6. En plus de proposer des perfs lamentables, les options de sécurité liées à IPv6 sont risibles. Pas autant qu'Orange, mais quand même.
[Troisième proposition sur le forum ouvert de Numéricable pour le haut débit](http://2020.numericable.fr/category/chapitres/tres-haut-debit), le réseau de Numéricable n'est pourtant pas encore prêt à passer à l'IPv6 (en tout cas pas en natif). [DOCSIS 3.0](http://fr.wikipedia.org/wiki/DOCSIS) devrait être disponible [dans 1 million de foyer d'ici un mois](http://2020.numericable.fr/blogs/docsis-30-la-norme-du-tres-haut-debit). Seule cette norme permet de passer de l'Internet IPv6 en natif sans faire des bricolages immondes comme Free. Mais pour l'instant, malgré la proposition faite à Numéricable et cette information, c'est toujours le silence de mort du côté de chez Noos.
Si on devait faire un podium et accorder le prix de la nullité, pour une fois, il n'irait pas à Orange (quelle bonne surprise tout de même !) :
* SFR : OLOLZ
* Orange : OLOLZ mais fait des efforts pour s'améliorer… Enfin, disons que c'est toujours mieux que SFR (même si pour le coup, c'est pas bien difficile).
* Numéricable : OLOLZ tout est prêt pour… Paris ! Mais rien n'est fait et les abonnés de campagne n'auront que leur nBox pour pleurer.
* Free : bien mais pas top. Ça reste une solution vite fait/mal fait, même si elle a le mérite d'être présente.
Alors est-ce que nos chers opérateurs seront capables de retourner leur veste en moins de 100 jours ? Est-ce qu'ils s'en tamponnent les glaouillis avec une batte cloutée ? Est-ce que Orange peut être encore plus médiocre que maintenant ? Existe-t-il seulement une limite à leur nullité ? La réponse ~~ce soir 20h50 dans…~~ dans moins de 100 jours maintenant.

View File

@@ -0,0 +1,145 @@
+++
title = "Du routage à moteur dans ton tunnel IPSEC"
date = 2010-11-26
aliases = [ "post/2010/11/26/Du-routage-à-moteur-dans-ton-tunnel-IPSEC"]
[taxonomies]
tags = [ "système", "rézo" ]
+++
Aaaaaaaaaaaah ! OSPF. Rien que son évocation fait déjà mouiller la petite culotte de toutes les vierges du réseau qui me lisent…
<!-- more -->
# Problème à tique
Le VPN, c'est bien. Ça permet de faire des choses sales sur les rézo P2P sans se faire gauler, mais surtout ça permet aux entreprises d'étendre leur réseau à moindre coût en se servant de l'Interwebz. Le souci, c'est que tant qu'on a un réseau simple, le VPN est simple, dès que le réseau devient plus compliqué, tu commences à pleurer ta maman.
Illustration. Dans un monde idéal, le siège de l'entreprise a une IP publique et est relié à un bureau détaché par un tunnel. L'ensemble des rézo privés entre A et B est complètement cohérent et ne se recoupe évidemment jamais :
![Le VPN qui ne peut pas exister parce qu'on vit dans la réalité](/.vpn_ideal_m.jpg)
Dans ce cas idyllique, il n'y a qu'un seul tunnel ESP à gérer de chaque côté : celui qui va du réseau 192.168.0.0/24 vers le réseau 10.0.0.0/22 dans un sens et inversement pour l'autre routeur. Maintenant, on ne vit pas au pays des Teletubbies sous LSD et ce genre de cas n'arrive pour ainsi dire jamais. Dans la réalité, on a des trucs comme ça :
![WTF !?!](/.vpn_reel_m.jpg)
ême si on pourrait s'amuser à faire quelques optimisations, il faut se rendre à l'évidence : la discontinuité des plages d'adresses va être une plaie ouverte, béante, infectée et purulente. 5 rézo d'un côté, 10 de l'autre, ça nous fait 50 tunnels ESP. Et on ne parle ici que de deux sites, je te laisse imaginer le résultat quand on en vient à gérer plusieurs bureaux distants.
Il serait grand temps de mettre du routage à moteur dans toute ce merdier histoire d'optimiser tes performances à Tetris en toute quiétude.
# YATTAAAAAAAAAA
Comme je ne suis pas chaud pour le faire à la brute sur un OpenBSD, je te propose de faire un petit exemple de configuration avec [Vyatta](http://vyatta.org/) et deux routeurs.
On va donc faire un truc qui ressemble vaguement au schéma du dessus. Je te passe les subtilités sans intérêt concernant la configuration du VPN à proprement parler, sache simplement qu'on ne crée qu'un seul tunnel ESP entre deux hôtes gérés par une interface de loopback des deux côtés.
```
vpn {
ipsec {
[…]
site-to-site {
peer 202.156.82.2 {
[…]
local-ip 202.156.82.1
tunnel 1 {
esp-group ESP_STD
local-subnet 169.254.0.1/32
remote-subnet 169.254.0.2/32
}
}
}
}
}
```
Et on obtient ça :
![Un seul tunnel ESP](/.vpn_ipsec_1tunnel_m.jpg)
```
vyatta@vttr1:~$ /bin/ping -I 169.254.0.1 169.254.0.2 -c3
PING 169.254.0.2 (169.254.0.2) from 169.254.0.1 : 56(84) bytes of data.
64 bytes from 169.254.0.2: icmp_seq=1 ttl=64 time=1.99 ms
64 bytes from 169.254.0.2: icmp_seq=2 ttl=64 time=3.19 ms
64 bytes from 169.254.0.2: icmp_seq=3 ttl=64 time=2.70 ms
--- 169.254.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 1.994/2.631/3.198/0.497 ms
```
Bref, ça marche tout simplement. Le seul problème, c'est qu'il s'agit de deux hôtes différents, par définition ils ne sont pas dans le même réseau. Impossible donc de faire tourner un service type OSPF entre nos deux routeurs sans ajouter une petite couche supplémentaire.
# IP dans IP, c'est un truc sexuel ça non ?
On va donc monter un tunnel GRE (ou SIT si on veut faire de l'IPv6) pour faire passer une plage d'adresses partagée (169.254.1.0/30) entre les deux routeurs en s'appuyant sur les adresses virtuelles de notre tunnel IPSEC.
```
vyatta@vttr1:~$ configure
[edit]
vyatta@vttr1# set interfaces tunnel tun0 encapsulation gre
[edit]
vyatta@vttr1# set interfaces tunnel tun0 address 169.254.1.1/30
[edit]
vyatta@vttr1# set interfaces tunnel tun0 local-ip 169.254.0.1
[edit]
vyatta@vttr1# set interfaces tunnel tun0 remote-ip 169.254.0.2
[…]
vyatta@vttr1:~$ show interfaces tunnel
Interface IP Address State Link Description
tun0 169.254.1.1/30 up up
```
On applique la même configuration miroir sur l'autre routeur et on obtient un domaine de broadcast qui s'étend entre les deux routeurs parfaitement à l'abri à l'intérieur d'un tunnel IPSEC :
```
vyatta@vttr1:~$ ping 169.254.1.2
PING 169.254.1.2 (169.254.1.2) 56(84) bytes of data.
PING 169.254.1.2 (169.254.1.2) 56(84) bytes of data.
64 bytes from 169.254.1.2: icmp_seq=1 ttl=64 time=0.792 ms
--- 169.254.1.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.792/0.792/0.792/0.000 ms
```
# Oly Shit Per Fuck
La plupart du temps, quand on parle d'OSPF, une partie non-négligeable de l'audience commence à vomir de manière incontrôlée. Aussi, comme je tiens à ton estomac, je ne vais pas te faire l'affront d'entrer dans des détails glauques, on va se contenter de quelques lignes sur nos routeurs, juste pour voir le principe :
```
protocols {
ospf {
area 0.0.0.0 {
network 169.254.1.0/30
}
redistribute {
static {
}
}
}
}
[…]
vyatta@vttr1:~$ show ip ospf neighbor
Neighbor ID Pri State Dead Time Address Interface RXmtL RqstL DBsmL
192.168.56.101 1 Full/DROther 33.937s 169.254.1.2 tun0:169.254.1.1 0 0 0
```
Il suffit alors de déclarer des routes statiques sur une interface active d'un côté pour les voir magiquement apparaître de l'autre ! En supposant que je déclare un réseau d'interconnexion 192.168.0.0/24 sur le routeur vttr1, je peux déclarer la route statique suivante :
```
vyatta@vttr1# set protocols static route 10.0.0.0/22 next-hop 192.168.0.2
[edit]
```
Et la voir apparaître aussitôt sur vttr2 :
```
vyatta@vttr2:~$ show ip route ospf
[…]
O>* 10.0.0.0/22 [110/20] via 169.254.1.1, tun0, 00:01:55
O 169.254.1.0/30 [110/10] is directly connected, tun0, 00:07:59
```
# Foutaises
oyennant donc un petit effort de configuration et en sacrifiant quelques moutons et plages d'adresses IP (un gag s'est d'ailleurs dissimulé dans ce billet, si tu es capable de le trouver, je te donne un carambar), on peut simplifier considérablement le déploiement d'un réseau VPN à grande échelle avec des plans d'adressage chaotique entre différents sites distants. Cela va également simplifier, dans une moindre mesure, les interconnexions VPN maillés entre plusieurs sites, même si le risque de se retrouver avec un gros bordel est beaucoup plus important qu'avec une architecture comprenant simplement un hub central.

View File

@@ -0,0 +1,84 @@
+++
title = "Il est temps de bouger ton gros cul pour sauver la liberté !"
date = 2010-12-05
aliases = [ "post/2010/12/05/Il-est-temps-de-bouger-ton-gros-cul-pour-sauver-la-liberté-!"]
[taxonomies]
tags = [ "internet", "wikileaks" ]
+++
Fais ton propre miroir Wikileaks \o/
<!-- more -->
Si tu habites sur cette planète, tu n'es pas sans savoir que [Wikileaks a dévoilé par le passé et encore maintenant tout un tas d'informations qui n'étaient pas supposées arriver jusqu'au oreille du public](http://fr.wikipedia.org/wiki/Wikileaks), les dernières en date étant [les fameux câbles diplomatiques américains qui sont en train de mettre une belle grouille planétaire](http://www.lemonde.fr/international/article/2010/11/30/wikileaks-nicolas-sarkozy-l-americain_1447153_3210.html). Or, il se trouve que [notre cher gouvernement veut faire fermer sa gueule](http://www.lefigaro.fr/hightech/2010/12/03/01007-20101203ARTFIG00446-le-gouvernement-ne-veut-pas-de-wikileaks-en-france.php) à ces fauteurs de trouble, ces dangereux terroristes, ces immondes chiens galeux, qui **te permettent** de découvrir la vérité sur tout un tas de sujet.
[Et si personne n'en veut](http://www.lefigaro.fr/hightech/2010/12/03/01007-20101203ARTFIG00446-le-gouvernement-ne-veut-pas-de-wikileaks-en-france.php), il faut pourtant bien qu'il existe, il faut démontrer au monde entier qu'on ne plaisante pas avec ce genre de choses et qu'on ne peut pas museler un site de cette importance impunément. Je te propose donc, puisque tu n'as probablement pas grand chose à faire ce dimanche, de créer ton propre miroir de Wikileaks et permettre ainsi que ce dernier soit virtuellement ineffaçable. Au moins, ça t'occupera entre deux hentaïs.
Histoire de faire du ludique en prime (j'aime bien le ludique), je te propose de réaliser un chroot rsync avec accès HTTP directement sur ton serveur. Les prérequis pour faire un miroir Wikileaks son simple :
* Une machine Unix (en l'occurence, je vais te montrer comme faire avec un Linux faut de mieux…) ;
* un serveur HTTP (n'importe lequel devrait faire l'affaire) ;
* un accès SSH ;
* du rsync ;
* environ 2Gio d'espace disque ;
* une paire de testicules (Mesdames, je vous propose d'utiliser des testicules virtuelles ou d'emprunter celles de votre mâle dominant).
# This time, it's fucking serious !
Couilles molles, s'abstenir ! Réaliser ce genre d'opérations est périlleux non seulement pour ton système mais aussi pour la connexion Internet qui le supporte. Tu pourras pas dire que t'es pas prévenu.
On va commencer par vérifier qu'on a bien un serveur Web et un OpenSSH des familles qui marchent tous les deux correctement et qui sont accessibles sur Internet (en IPv4 ou v6, les deux sont acceptés). Il faut également rsync sur le serveur (la plupart des distribs l'ont par défaut) et le petit script `setup-chrootdir-rsync.sh` que l'on peut trouver dans le paquet libpam-chroot ou quelque chose du genre dans la plupart des distribs (ce n'est pas indispensable, mais ça aide).
On commence donc par créer l'utilisateur qui va bien et on y adjoint la clé RSA de Wikileaks :
```
# useradd --user-group -m --shell /bin/bash wikileaks
# su - wikileaks
$ mkdir .ssh
$ wget http://46.59.1.2/id_rsa.pub -O ~/.ssh/authorized_keys
$ mkdir www
$ exit
```
L'utilisateur est prêt, il ne reste plus qu'à contrôler que le répertoire `www` dans son home directory est bien accessible par tout le monde (y compris Apache donc, c'est un peu le but de l'opération). On va donc s'attaquer au serveur OpenSSH. Je présuppose que l'authentification par clé est activée (c'est en général, mais pas toujours, le cas). On ajoute donc simplement quelques lignes en fin de fichier `sshd_config` :
```
atch User wikileaks
ChrootDirectory %h\
X11Forwarding no\
AllowTcpForwarding no\
```
aintenant que `wikileaks` est chrooté dans son répertoire personnel, l'accès au serveur va être extrêmement limité. D'autant plus que pour que le chroot fonctionne au niveau de SSH, il va falloir ajouter une petite subtilité :
```
# chown root.root /home/wikileaks/
```
Sinon, OpenSSH refusera de chrooter quoique ce soit ! Alors, c'est bien joli tout ça, mais maintenant si on essaye de se connecter avec l'utilisateur `wikileaks`, on obtient donc une magnifique erreur :
```
/bin/bash : no such file or directory
```
Ce qui est logique vu que le shell de l'utilisateur n'existe pas dans le chroot (en l'occurence son homedir). C'est là qu'intervient `setup-chrootdir-rsync.sh` qui va peupler l'ensemble de ce répertoire avec toutes les données nécessaires pour faire tout marcher correctement (si tu ne le trouves pas sur ta distrib, tu dois pouvoir le télécharger sans difficulté sur Internet). Il te suffit alors de le rendre exécutable et de donner le nom du répertoire du chroot `/home/wikileaks` en paramètre.
Ce petit script fait l'essentiel du boulot, toutefois, comme il commence à dater un peu, il va manquer quelques petites bricoles pour que tout fonctionne bien. D'abord, sur les machines non-i386 (x86_64/ppc/ppc64/etc…), le script ne copie pas le programme de chargement de tous les programmes `ld-linux`. Il suffit le regarder la dernière ligne de la commande `ldd` pour savoir quel est ce programme :
```
$ ldd /bin/bash
linux-vdso.so.1 => (0x00007fff25bff000)\
libreadline.so.6 => /lib/libreadline.so.6 (0x00007fb8c8596000)\
libncursesw.so.5 => /lib/libncursesw.so.5 (0x00007fb8c833c000)\
libdl.so.2 => /lib/libdl.so.2 (0x00007fb8c8138000)\
libc.so.6 => /lib/libc.so.6 (0x00007fb8c7ddb000)\
/lib/ld-linux-x86-64.so.2 (0x00007fb8c87d9000)\
```
Il faut donc vérifier sa présence dans `/home/wikileaks/lib/`. Pareil pour `rsync`, il va sûrement falloir ajouter `libattr` et `libacl` pour qu'il fonctionne correctement. La même commande te permettra de vérifier cela et de compléter éventuellement avec les bibliothèques/exécutables qui sont indispensables au fonctionnement de `rsync` (si tu n'as pas le petit script, il va falloir tout se palucher à la main).
Rien ne vaut un petit test avec ta clé personnelle ou avec un mot de passe (qu'il faudra bien entendu supprimer après) pour être certain que tout va bien, et hop!, il ne te reste maintenant plus qu'à mettre un virtualhost sur ton Apache, d'y associer le répertoire `/home/wikileaks/www/` et le tour est joué.
aintenant, pour devenir le sauveteur de la planète et faire chier tous les gouvernements du monde, rend-toi sur [cette page](http://46.59.1.2/mass-mirror.html) pour participer à la super opération de mass mirror de Wikileaks. Il te suffit d'indiquer l'adresse v4 ou v6 du serveur SSH, le nom d'utilisateur, le répertoire en écriture et le nom de domaine (ou à défaut www.wikileaks.org) et de t'ajouter à la liste.
aintenant, aux dirigeants malveillants de cette planète, tous ensemble :
![](http://forum.nintendojo.fr/images/smilies/dtc.gif)

View File

@@ -0,0 +1,36 @@
+++
title = "Dis Tonton hylobates, comme ça marche Internet ? (Partie 1)"
date = 2011-02-02
aliases = [ "post/2011/02/02/Dis-Tonton-Gibbon,-comme-ça-marche-Internet-(Partie-1)"]
[taxonomies]
tags = [ "internet" ]
+++
Parce qu'on m'a récemment posé la question (ce qui me fait dire qu'on a les lecteurs qu'on mérite…), je vais essayer de tenter une explication de comment que ça marche IPv4, pourquoi c'est un protocole tout pourri du siècle dernier et pourquoi IPv6, c'est merveilleux, beau, chaud et que ça sauve des bébés phoques et agrandit ton pénis.
<!-- more -->
Tout d'abord, il faut savoir que l'Internet Protocol version 4 repose sur un nombre fini d'adresses. Dans un réseau où il y a des centaines de machines, le plus simple pour savoir qui est qui, c'est simplement de tout numéroter (un peu comme le téléphone, on donne un numéro à chaque ligne). Comme la puissance des machines du début d'Internet (fin des années 70) est limitée et que le nombre de machines sur Internet à cette époque est limité (un ordinateur est cher, il y a donc peu d'ordinateurs et ces derniers permettent en général de relier de multiples terminaux passifs), les créateurs d'Internet ont décidé de coder le nombre de machines possibles sur le réseau sur 32bits, ce qui apparaissait alors comme largement suffisant.
Calcul rapide, 32 bits, ça nous fait 2^32 adresses possibles soit 4 294 967 296 d'adresses, beaucoup plus qu'il n'en faut pour les quelques milliers d'ordinateurs de l'époque (et dans les faits, c'était suffisant jusqu'à il y a environ 10 ans). Une adresse IP est donc simplement un numéro dans le grand ensemble des numéros disponibles. Pour donner un exemple concret, l'adresse IP de ce serveur est 91.121.21.148 en représentation humainement lisible (4 chiffres de 0 à 255), ce qui correspond pour la machine au numéro 1 534 662 036. Au final, ce n'est qu'un numéro unique dans l'ensemble des numéros du réseau.
aintenant qu'on sait comment on va numéroter les machines, ce serait bien qu'on arrive à décider qui va les numéroter pour éviter d'avoir des doublons (imagine la situation si deux personnes avaient le même numéro de téléphone par exemple). On crée donc l'IANA qui est chargée de distribuer de très grandes plages d'adresses à des RIR, Regional Internet Registries, au nombre de un par « continent » :
* RIPE (Réseau IP Européens) pour l'Europe et le Moyen-Orient ;
* ARIN pour l'Amérique du Nord ;
* LACNIC pour l'Amérique du Sud ;
* APNIC pour l'Asie-Pacifique ;
* AfriNIC pour l'Afrique.
Ces autorités régionales distribuent des plages d'adresses IP (plus petites) aux entreprises et FAI de leur continent respectif. On avait donc une grande quantité d'adresses disponibles et on en a donné des morceaux au fur et à mesure.
Les soucis ont commencé le jour où de nouveaux besoins sont apparus et notamment au niveau des rézo privés. L'IANA a alors décidé de réserver des plages d'adresses pour d'autres usages que de les distribuer :
* Les 268 435 456 d'adresses de la plage 240.0.0.0/4 (allant donc de l'adresse 4 026 531 840 à l'adresse 4 294 967 295 la dernière adresse possible en IPv4) ont été réservées pour des usages futures qui ne sont jamais apparus ;
* Il y a de nouveau 268 435 456 d'adresses qui ont été réservé pour ceux qu'on appelle le multicast (et qui n'a jamais fonctionné sur Internet) ;
* Les plages d'adresses 10.0.0.0/8 (16 777 216 d'adresses), 192.168.0.0/16 (65 536) et 172.16.0.0/12 (1 048 576 d'adresses) sont réservées pour l'adressage privée (à l'intérieur des rézo d'entreprises par exemple) ;
* Il y a un nombre incalculable d'adresses qui sont gaspillés pour les besoins du routage sur Internet (simplement parce qu'IPv4 « gâche » des adresses pour le découpage du réseau…);
* Et il y a des plages interdites, utilisées pour du test, etc…
Au final, il n'y a qu'environ 3 milliards et quelques d'adresses réellement utilisables sur le réseau Internet. Or, si tu fais bien le calcul, tu as sûrement chez toi un fixe, un portable ou deux, un mobile dernière génération avec du Wi-Fi, une imprimante, une(des) console(s) de jeux, etc… qui doivent tous aller sur Internet d'une manière ou d'une autre. Comme il y a 2 milliards d'internautes, on en arrive rapidement à avoir plus de 12 milliards de périphériques connectés au réseau Internet pour seulement 3 milliards d'adresses, ça ne peut pas fonctionner (je suis formel, j'ai essayé, 12, ça rentre pas dans 3 même avec un chausse-pied).
Pour la prochaine fois, j'expliquerai donc comment on a fait pour contourner le souci et quels autres problèmes cela a entraîné.

View File

@@ -0,0 +1,46 @@
+++
title = "Dis Tonton hylobates, comme ça marche Internet ? (Partie 2)"
date = 2011-02-03
aliases = [ "post/2011/02/03/Dis-Tonton-Gibbon,-comme-ça-marche-Internet-(Partie-2)"]
[taxonomies]
tags = [ "internet" ]
+++
Comme on l'a vu précédemment, il n'y a déjà pas assez d'adresses pour tous les périphériques qui se connectent actuellement (et en fait depuis quelques années) au réseau Internet. Pour éviter la super mouise, il a donc fallu ruser comme des chacals…
<!-- more -->
On a donc un problème. Et ce problème il est connu depuis qu'Internet est devenu accessible au grand public. On sait qu'on a pas assez d'adresses pour tout le monde. Mais on est malin et on a donc inventé le NAT (Network Address Translation = traduction d'adresses rézo). Le principe, c'est que chaque ordinateur du réseau domestique ou du réseau d'entreprise a une adresse privée (qui ne marche donc pas sur Internet), mais connaît une machine qui a à la fois une adresse privée et une adresse publique (Freebox, Livebox, etc…).
Basiquement, quand une machine du réseau local veut aller joindre une machine d'Internet, le boîtier Internet va prendre en compte sa requête, effacer l'adresse de l'expéditeur pour la remplacer par son adresse publique et la transmettre sur Internet. Concrètement tu tapes http://blogdesinges.fr sur ton ordi qui est en 192.168.1.15 par exemple, la boîboîte voit arriver un paquet qui dit :
```
Je suis 192.168.1.15 et je veux joindre 91.121.21.148
```
Si la box envoit le paquet tel quel, ça ne peut pas marcher : Internet va lui dire que le paquet ne peut pas avoir pour expéditeur 192.168.1.15 parce que c'est une adresse privée (l'équivalent informatique d'un Dans Ton Cul). Donc la box, pas folle, remplace cette adresse par son adresse :
```
Je suis <IP publique de la box du joyeux lecteur> et je veux joindre 91.121.21.148
```
Et elle attend patiemment la réponse. Quand la réponse vient, elle a cette forme :
```
Je suis 91.121.21.148 et je veux joindre <IP publique de la box du joyeux lecteur>
```
Le symétrique de la précédente. Comme la box sait qu'elle a envoyé une requête, elle sait à quel ordinateur du réseau local ce paquet est destiné. Elle remplace donc simplement l'adresse du destinataire par l'adresse 192.168.1.15. Et le tour est joué, la page s'affiche. Bon évidemment, dans la pratique, c'est un tout petit peu plus compliqué que cela : chaque paquet suit effectivement ce cheminement là, mais il y a nettement plus de choses à faire pour afficher une simple page Web.
Le problème ici, c'est qu'on peut bien envoyer des requêtes pour recevoir des réponses. Mais on ne peut pas recevoir directement des requêtes. Si la box reçoit un truc et qu'elle n'avait rien demandé et que personne dans le réseau local n'avait rien demandé, elle le jette. Purement et simplement. C'est plus simple à imaginer avec un téléphone : tu peux appeler les gens et ils peuvent alors te parler, mais ils ne peuvent pas t'appeler. Si c'était le cas du serveur de Blog2Singes, ce serait extrêmement problématique parce qu'il serait incapable de répondre aux requêtes.
C'est le deuxième principal problème d'IPv4 : le NAT a permis de reculer le problème de quelques années, mais n'a rien réglé. Au contraire, il a fermé une partie du réseau et les concepteurs d'applications comme la VoIP et le P2P ont dû trouver des astuces de porc pour continuer de fonctionner malgré les barrières.
Donc la solution, c'est simplement IPv6 \o/\
Ce coup-ci, il a été décidé qu'on allait coder les adresses sur 128 bits soit 340 282 366 920 938 463 463 374 607 431 768 211 456 d'adresses possibles ou 667 millions de milliards d'adresses IP par mm² de la surface terrestres (!). Donc là plus de souci : comme on a plus de pénurie, on peut donner des adresses à tout le monde (et même plusieurs centaines de millions de milliards d'adresses pour chaque connexion) et on a plus besoin de faire de la traduction d'adresses. Toutes les applications qui posaient problème ne posent donc plus de problème et on peut même envisager d'en faire des nouvelles qui peuvent utiliser toutes les particularités d'un réseau 100% connecté \o/\
Les adresses IPv6 permettent de retrouver un monde où tous les ordinateurs peuvent se connecter entre et ou les box ne font plus que connecter vraiment les ordinateurs à Internet et pas bricoler au milieu pour que ça marche.
a conclusion sera : IPv6, c'est bien, mangez-en.

View File

@@ -0,0 +1,70 @@
+++
title = "Qualité de Service et auto-hébergement : principes de base"
date = 2011-04-04
aliases = [ "post/2011/04/04/Qualité-de-Service-et-auto-hébergement-:-principes-de-base"]
[taxonomies]
tags = [ "internet", "rézo" ]
+++
Jeune macaque aventurier, tu t'auto-héberges pour prouver que tu as une paire de testicules en béton et continuer de faire des choses sales sur le Ouaib en toute impunité ou presque, et à cette fin, tu as probablement chez toi une magnifique connexion ADSL avec 8Mbits de bande passante descendante et un tout petit mégabit de bande passante montante, par beau temps et quand le vent porte dans la bonne direction. On va pas aller bien loin avec ça… D'ailleurs, je tiens à remercier personnellement, et pour la 12 748ème fois cette année, France Télécom pour avoir fait le choix débile de l'ADSL plutôt que du SDSL il y a de cela environ une quinzaine d'années…
<!-- more -->
Bref, tu vas rapidement te retrouver avec un souci majeur : comment rendre les différentes choses que tu héberges chez toi accessibles dans un temps raisonnable pendant que ~~bittorrent tourne à donf !~~ ta ligne est occupée d'autre part ? Comment faire pour être certain que ton blog/forum/site sur l'échangisme animalier continuera d'être accessible alors que tu envoies les photos de ta dernière partie fine en pièce jointe dans un message ?
La réponse est simple jeune macaque, il te faut faire de la qualité de service©.
# Qualité de KÔA ?
Sans m'attarder sur les principaux algorithmes, implémentations, etc, je vais simplement faire un tout petit rappel sur les principes de base du bouzin. Ce qui nous intéresse, c'est bien de faire de la qualité de service en sortie, sur le lien montant donc, histoire de garantir que la messagerie instantanée passera avant le passionnant blog vidéo de ta petite sœur. Ça peut paraître évident mais on ne peut pas faire de QoS en entrée, seulement en sortie. Si le facteur bourre ta boîte aux lettres avec des colis, des recommandés et des lettres, tu ne peux rien y faire (à part lui lâcher ton doberman au cul chaque fois qu'il se pointe). Par contre, tu peux très bien déterminer quel type de courrier tu envoies et de quelle manière.
C'est donc un peu le principe de base : réserver de la bande passante dédiée à certaines applications, réserver de la bande passante garantie à d'autres, plafonner l'utilisation de certaines applis, donner la priorité à des flux plutôt qu'à d'autres en fonction de leur importance ou de leur fonctionnement.
# Tonton hylobates, c'est quoi une classe de service ?
Au fin fond d'IPv4/IPv6, il existe un champs qui porte le doux nom de ToS/Traffic Class et qui permet de faire de classifier simplement le traffic.
Les 6 premiers bits de ce champs permettent donc de définir des classes de service de la plus prioritaire à la moins prioritaire (c'est ce qu'on appelle le DSCP). Les 2 bits restants sont inutilisés pour le moment (mais on trouve des notations sur 8 bits aussi). Si tu sens venir le truc merdique, tu as tout-à-fait raison : en fait les normes correspondantes à ce codage ont été conçues pour être rétro-compatible avec les précédentes méthodes de qualité de service. D'où un gros bordel au décodage… Mais pas de panique, avec un tableau de correspondance tout con, on s'en sort à merveille !
Voici donc toutes les classes DiffServ :
<table>
<tr><td>Binaire</td><td>Classe</td><td>DSCP (6bits) dec - hex</td><td>DiffServ (8bits) dec - hex</td></tr>
<tr><td>000000</td><td>BE</td><td>0 - 0x00</td><td>0 - 0x00</td></tr>
<tr><td>001010</td><td>AF11</td><td>10 - 0x0a</td><td>40 - 0x28</td></tr>
<tr><td>001100</td><td>AF12</td><td>12 - 0x0c</td><td>48 - 0x30</td></tr>
<tr><td>001110</td><td>AF13</td><td>14 - 0x0e</td><td>56 - 0x38</td></tr>
<tr><td>010010</td><td>AF21</td><td>18 - 0x12</td><td>72 - 0x48</td></tr>
<tr><td>010100</td><td>AF22</td><td>20 - 0x14</td><td>80 - 0x50</td></tr>
<tr><td>010110</td><td>AF23</td><td>22 - 0x16</td><td>88 - 0x58</td></tr>
<tr><td>011010</td><td>AF31</td><td>26 - 0x1a</td><td>104 - 0x68</td></tr>
<tr><td>011100</td><td>AF32</td><td>28 - 0x1c</td><td>112 - 0x70</td></tr>
<tr><td>011110</td><td>AF33</td><td>30 - 0x1e</td><td>120 - 0x78</td></tr>
<tr><td>100010</td><td>AF41</td><td>34 - 0x22</td><td>136 - 0x88</td></tr>
<tr><td>100100</td><td>AF42</td><td>36 - 0x24</td><td>144 - 0x90</td></tr>
<tr><td>100110</td><td>AF43</td><td>38 - 0x26</td><td>152 - 0x98</td></tr>
<tr><td>101110</td><td>EF</td><td>46 - 0x2e</td><td>184 - 0xb8</td></tr>
</table>
Pour calculer facilement un DiffServ, il suffit en fait d'avoir le nombre binaire et de faire une petite conversion sur 6 bits ou 8 bits (ce qu'on peut faire très facilement dans une console Python) :
```
>>> # Classe AF41 en DSCP (6 bits)
...
>>> print(hex(0b100010))
0x22
>>> # Classe AF41 en DiffServ (8 bits)
...
>>> print(hex(0b100010*4))
0x88
```
Bon alors qu'est-ce que ça veut dire tout ce charabia en langage de singe ? Simple.
BE = Best Effort. En gros, c'est le mode de QoS d'Internet, ton routeur achemine ce qu'il peut, comme il peut, quand il peut. C'est le mode par défaut, n'importe quel ordi émet des paquets avec le champs ToS à 0.
AF = Assured Forwarding. Ce sont nos classes de services, réparties en 4 grandes familles (AF1, AF2, AF3, AF4) correspondant à des priorités de traffic (Bronze, Silver, Gold, Platinium). Dans chaque famille, on va définir également une priorité de drop, qui permettra en cas de congestion de sacrifier certains types de traffic au profit d'autres (en l'occurence AF11 est low drop et sera donc moins prioritaire que AF21, mais droppé après AF13 en cas de congestion).
EF = Expedited Forwarding. C'est le traffic qu'il faut envoyer de suite sans traîner et sans réfléchir. C'est typiquement le cas pour les applications de voix sur IP : il faut expédier le plus vite possible les paquets quoiqu'il arrive.
Et ben voilà, je te laisse digérer tous ces bits, la prochaine fois on verra comment marquer intelligemment les paquets avec les bonnes classes grâce à iptables et comment vérifier que tu as pas fait une connerie derrière.

View File

@@ -0,0 +1,85 @@
+++
title = "Qualité de Service et auto-hébergement : premiers pas"
date = 2011-04-22
aliases = [ "post/2011/04/22/Qualité-de-Service-et-auto-hébergement-:-premiers-pas"]
[taxonomies]
tags = [ "internet", "rézo", "auto-hébergement" ]
+++
« *La QoS, c'est le machin qui sert à rien, c'est ça ?* »
Jean-Marc, ingénieur chez Cisco.
<!-- more -->
aintenant que je t'ai bien mis l'eau à la bouche [en te promettant monts et merveilles](/qualite-de-service-et-auto-hebergement-principes-de-base/) pour ton auto-hébergement personnalisé à toi, on va passer aux choses sérieuses.
Et comment donc ? Ben, on va commencer par évaluer le traffic sortant puis faire du marquage DSCP sur les paquets IP qui viennent de ton réseau et qui vont sur l'Interwebz.
# T'as des baskets, tu sors pas…
Tout bon plan de Qualité de Service doit commencer par une question simple : qu'est-ce qui est prioritaire et qu'est-ce qui ne l'est pas ? Va donc falloir te retrousser les manches et voir quels services sont exposés depuis chez toi vers Internet.
Il est important de distinguer dans un premier temps les services prioritaires, des services moins prioritaires. Il va falloir en fait, faire la différence de manière stricte entre ce qui est important, ce qui est interactif et ce qui consomme de la bande passante. Il est évident qu'une connexion SSH doit être transportée en priorité du fait de son interactivité, mais elle ne consomme que peu de bande passante. Inversement, une visite sur ton site consomme un peu plus de bande passante, mais n'est peut-être pas aussi prioritaire que ta messagerie instantanée.
Pour te donner un exemple concret, on pourrait considérer les services suivants en sortie de ta connexion Internet, par ordre d'importance :
* Upload de vidéos vers Youporn
* Bittorrent
* Emule
* Limewire
* VoIP (Skype, SIP, etc…)
* Ton serveur SMTP
* Ton serveur Jabber
* Les galeries de Photos de tes nains de jardin
* Le wiki sur la vie sexuelle de ton poisson rouge
* Et très très loin derrière, le blog de ta petite sœur
Les 4 premiers services sont très prioritaires et ne doivent pas être coupés. Cependant, le premier n'étant pas utilisé en continue (quoique…), on peut considérer qu'il doit avoir une priorité un peu plus haute que les autres, histoire de leur marcher dessus en cas de congestion. Pour ce qui est de la bande passante, on pourra considérer qu'il lui faut un minimum (sinon ce sera simplement insupportable d'uploader une vidéo !) et un maximum (histoire de pas casser les couilles à tout le monde à la maison chaque fois que tu uploades du pr0n). Un bon candidat pour un AF41. Le P2P pourrait être classé en AF42/AF43.
La voix sur IP consommant une bande passante simplement OLOLZ, on peut la mettre en EF, tu ne verras probablement pas la différence en temps normal de toutes manières.
Les serveurs de type SMTP et Jabber fonctionnent différemment : on peut tolérer quelques secondes de latence dans un cas comme dans l'autre, mais on peut carrément jeter ce qui concerne le SMTP (si l'envoi échoue, il sera repris plus tard). Même s'ils sont importants, leur fonctionnement permet une certaine souplesse. On peut donc partir sur AF31 pour le serveur Jabber et AF21 pour le serveur SMTP.
Pour les trois derniers points, on va se heurter à un gros problème : si tu as un seul serveur Web qui dessert sous forme de VirtualHost tous ces services, comment faire pour les différencier ? Et bien, c'est simple, on peut pas… À moins de faire de l'IPv6 avec une adresse différente pour chaque site Web ou d'avoir autant de redirection de ports que de sites Web, tu n'arriveras à rien… Il va donc falloir se résoudre à classer tout ce qui est Web dans la même classe de service (style AF22/AF23) avec un minimum/maximum de bande passante.
# Marquage-culotte :guyroux:
Et comment on va faire ce marquage ? En utilisant la chaîne PREROUTING de la table mangle d'iptables. Et là, normalement tu dois saigner du nez « *KÔA ? La table mangle ?!? Ce machin immonde que personne sait à quoi ça sert !!* ». Parfaitement jeune chimpanzé, et pour une raison bien simple : c'est la toute première chaîne traversée dans iptables ([tu peux d'ailleurs t'en convaincre ici](http://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg)).
arquer un paquet est donc d'une simplicité crasse :
```
iptables -t mangle -A PREROUTING -s <l'IP côté LAN de ton serveur Jabber> \
-i <Interface LAN> -p tcp --sport 5269 -j DSCP --set-dscp 0x1a
```
Tous les paquets entrants sur l'interface LAN de ton routeur, en provenance de ton serveur Jabber et à destination du port S2S XMPP seront marqués AF31 (== 0x1a en hexadécimal). Comment vérifier que le marquage est bien effectif ? Tout simplement en capturant les paquets, toujours via iptables sur l'interface WAN du routeur avec quelque chose comme ça :
```
iptables -I FORWARD -p tcp --sport 5269 -j LOG
```
Avant le marquage, on obtient des choses de ce type, avec les champs TOS=0x00 et PREC=0x00 :
```
Apr 20 14:31:56 monzob kernel: [2655643.233769] IN=eth1 OUT=eth0 \
SRC=<LAN serveur Jabber> DST=<Autre serveur Jabber> LEN=52 \
TOS=0x00 PREC=0x00 TTL=63 ID=65024 DF PROTO=TCP SPT=5269 \
DPT=51779 WINDOW=2003 RES=0x00 ACK URGP=0
```
Et après le marquage :
```
Apr 20 14:36:55 monzob kernel: [2655941.626164] IN=eth1 OUT=eth0 \
SRC=<LAN serveur Jabber> DST=<Autre serveur Jabber> LEN=52 \
TOS=0x08 PREC=0x60 TTL=63 ID=65036 DF PROTO=TCP SPT=5269 \
DPT=51779 WINDOW=2003 RES=0x00 ACK URGP=0
```
Petit calcul rapide pour vérifier qu'on ne s'est pas complètement planté :
```
>>> hex((0x08+0x60)/4)
'0x1a'
```
Que j'explique sinon tu vas aller te jeter sous un bus : La précédence (PREC) et le type de service (TOS) sont les deux éléments clés de la précédente norme et forme un ensemble sur 8 bits (le premier étant les 3 premiers bits, le second les 4 suivants et le dernier est toujours à 0). AF31 est en binaire 011010 multiplié par 4 pour le mettre sur 8 bits, soit 01101000. En hexadécimal, cela donne donc 68, soit 60 + 8 toujours en hexadécimal. Pour obtenir les 6 premier bits, il faut décaler 2 fois à droite, soit l'équivalent d'une division par 4. D'où la valeur DSCP.
Je te laisse te faire des nœuds au cerveau pour tout capter, je te retrouve la semaine prochaine pour mettre en place des files avec des priorités.

View File

@@ -0,0 +1,106 @@
+++
title = "Qualité de Service et auto-hébergement : it's getting serious"
date = 2011-06-17
aliases = [ "post/2011/06/17/Qualité-de-Service-et-auto-hébergement-:-it-s-getting-serious"]
[taxonomies]
tags = [ "internet", "rézo", "auto-hébergement" ]
+++
Quand on est un singe bien, on plaisante pas avec ces conneries !
<!-- more -->
Ah ah ! Tu croyais que je t'avais oublié hein ! Et bien non, je profite simplement d'un de mes rares moments de sobriété pour finir ce que j'avais commencé. La dernière fois, on s'était occupé de marquer les paquets avec ''iptables''. Tu as donc maintenant des jolies paquets avec des étiquettes dessus pour dire ce que c'est, il va donc s'agir de les ranger dans le bonne ordre et de pas faire n'importe quoi avec.
# Bande pas Sante !
aintenant, on va vraiment pouvoir rentrer dans le lard de la bestiole, la gestion de la QoS sous Linux. D'abord, il faut que tu saches que c'est un merdier infâme, avec des couches dans tous les sens et des morceaux que plus personne sait très bien à quoi ils servent. Enfin, il y a probablement des gens qui savent, mais pour le clampin de base, c'est juste imbuvable.
L'outil de base se nomme `tc` (traffic control) et il permet de contrôler le trafic :merci captain obvious:. Le seul problème de cet outil, c'est que sa syntaxe est absolument inutilisable (comme la plupart des outils rézo sous Linux). Le plus simple est donc de ne pas l'utiliser et de passer plutôt par un autre outil `tcng` (traffic control next generation) qui fait la même chose mais en plus lisible.
D'autre part, il existe des dizaines d'algo pour faire de la qualité de services. Des seaux à jetons (token bucket), des seaux percés (leaky bucket), des seaux de plages :joke:, etc… Toutes ces méthodes ont des avantages et des inconvénients. On pourrait discuter pendant des heures des différentes méthodes, mais ce ne serait pas forcément intéressants et ça ne permettrait pas de faire un truc rapide, facile et indigeste pour de la gestion basique.
On va donc s'intéresser uniquement au hierarchical token bucket qui présente l'intérêt d'avoir à peu près toutes les fonctionnalités possibles (limitation de bande passante, minimum réservé, priorité, etc…) sans être trop prise de tête à mettre en place.
# Envoyons du pâté :proutprout:
Posons les bases. Admettons que ton interface de sortie sur le grand Nain Ternet soit eth0 et que tu disposes de 1000kbps de bande passante montante. Tu utilises le zoli marquage de paquets avec 3 classes différentes : EF, AF41 et AF42. On commence donc avec un fichier texte très basique qui ne fait que reprendre cette définition et poser quelques variables :
```
#include <fields.tc>
dev eth0 {
egress {
class( <$ef> )
if ip_dscp == 0b101110
;
class( <$af41> )
if ip_dscp == 0b100010
;
class( <$af42> )
if ip_dscp == 0b100100
;
htb() {
class ( rate 1000kbps ) {
}
}
}
}
```
On définit donc les différentes classes qu'on va utiliser en se référant à notre magnifique tableau de conversion qui permet de les avoir en un clin d'œil. Ici, je ne me sers volontairement que du champs DSCP (d'où le `include <fields.tc>`), mais on pourrait très bien déterminer l'importance des classes directement via `tc` (c'est juste franchement moins souple).
Ensuite, on crée un htb (hierarchical token bucket) qdisc (queueing discipline), une sorte de super-classe. Rien ne pourra sortir de ce cadre et l'upload général de la machine sera limité à 1000kbps. D'où l'intérêt de connaître assez bien sa connexion. Cette super-classe est très importante puisqu'elle va permettre de gérer plus facilement le minimum et le maximum de bande passante de chaque classe DSCP. Sans elle, `tc` n'appliquera que le minimum mais sera incapable de déterminer s'il peut accorder plus de bande passante pour le maximum.
# T'as la classe baby !
À l'intérieur de cette classe, il n'y a plus qu'à mettre les autres classes, comme par exemple EF :
```
class( rate 32kbps, ceil 32kbps, prio 0 ) {
$ef = class( rate 32kbps, ceil 32kbps );
}
```
On indique le minimum réservé `rate` (ne pourra pas descendre en dessous sauf si une application plus prioritaire demande de la bande passante), le maximum qu'elle pourra atteindre `ceil` (pour éviter que le blog de ta petite sœur n'explose ta connexion après que ta petite sœur a publié les photos de sa dernière IRL wakfu) et la priorité. Comme c'est EF, on passe en priorité 0, la plus élevée.
Concrètement ici, dès que des paquets EF arrivent, ils se voient réserver 32kbps de bande passante, au détriment des autres classes si nécessaire, mais ne peuvent pas dépasser 32kbps. Cela correspond à une conversation téléphonique, pas 2).
Voilà une autre exemple qui ferait une très bonne classe pour du HTTP (AF41) et du SMTP (AF42) :
```
class ( rate 256kbps, ceil 512kbps, prio 1 ) {
$af41 = class( rate 128kbps, ceil 384kbps );
$af42 = class( rate 16kbps, ceil 128kbps );
}
```
Dans la même classe, on se retrouve donc avec AF41 et AF42 qui partage le mini et le maxi, mais pas de la même manière. Ici, il n'y a pas de question de priorité, mais seulement de partage de bande passante. Si il y a engorgement, les deux classes prendront cher.
## Application du bouzin.
Et maintenant ? On a un joli fichier qui contient des jolies définitions, il ne reste plus qu'à demander à ''tcng'' de transformer ça en charabia illisible :
```
# tcng qos.tc
# ================================ Device eth0 ================================
tc qdisc add dev eth0 handle 1:0 root dsmark indices 4 default_index 0
tc qdisc add dev eth0 handle 2:0 parent 1:0 htb
tc class add dev eth0 parent 2:0 classid 2:1 htb rate 125000bps
tc class add dev eth0 parent 2:1 classid 2:2 htb rate 4000bps ceil 4000bps prio 0
tc class add dev eth0 parent 2:2 classid 2:3 htb rate 4000bps ceil 4000bps prio 0
tc class add dev eth0 parent 2:1 classid 2:4 htb rate 32000bps ceil 64000bps prio 1
tc class add dev eth0 parent 2:4 classid 2:5 htb rate 16000bps ceil 48000bps prio 1
tc class add dev eth0 parent 2:4 classid 2:6 htb rate 2000bps ceil 16000bps prio 1
tc filter add dev eth0 parent 2:0 protocol all prio 1 tcindex mask 0x3 shift 0
tc filter add dev eth0 parent 2:0 protocol all prio 1 handle 3 tcindex classid 2:6
tc filter add dev eth0 parent 2:0 protocol all prio 1 handle 2 tcindex classid 2:5
tc filter add dev eth0 parent 2:0 protocol all prio 1 handle 1 tcindex classid 2:3
tc filter add dev eth0 parent 1:0 protocol all prio 1 u32 match u8 0xb8 0xfc at 1 classid 1:1
tc filter add dev eth0 parent 1:0 protocol all prio 1 u32 match u8 0x88 0xfc at 1 classid 1:2
tc filter add dev eth0 parent 1:0 protocol all prio 1 u32 match u8 0x90 0xfc at 1 classid 1:3
```
Un bon coup de copier/coller dans ton terminal et le tour est joué !!
Il va te rester encore un peu de boulot pour tester les règles et vérifier que tout fonctionne correctement. Mon conseil : tenter un téléchargement bête de fichier et voir comment réagissent les plafonds et les minima. C'est en général un très bon test et relativement facile à faire.
Bon, vais essayer de retrouver cette liqueur de banane moi…

View File

@@ -0,0 +1,96 @@
+++
title = "Faire mentir un serveur DNS"
date = 2011-08-30
aliases = [ "post/2011/08/30/Faire-mentir-un-serveur-DNS"]
[taxonomies]
tags = [ "internet", "dns" ]
+++
Tout ce qu'il faut pour passer pour un pédo-nazi terroriste…
<!-- more -->
entir, c'est mal. Tout le monde te le dit tout le temps, faut pas mentir. Ouais, ptet bien, mais des fois, t'as besoin de cacher des choses, d'empêcher les méchants de regarder tes petits trafics ou simplement de protéger ton réseau domestique des sociétés américaines qui ne te veulent que du bien en collectant les infos personnelles de ta grand-mère.
Et un bon moyen d'empêcher les innocents de se faire espionner, c'est tout simplement de les empêcher d'accéder à un certains nombres de services…
# Petit mot sur les vues
Les vues sont un mécanisme de bind qui permet de servir une zone différente (donc un/des enregistrements différents) en fonction de la source de la requête ou de la destination de la requête.
C'était très pratique dans le vieil Internet v4 pour répondre des choses différentes côté LAN et côté WAN pour une zone que tu gères, comme l'adresse d'un serveur par exemple : il est évident que si l'on indique la même chose en interne et en externe, soit Internet va voir une adresse LAN privée et ne pourra pas la joindre, soit les machines du LAN vont voir la propre adresse WAN de la connexion et ne vont pas arriver à joindre la machine directement…
Et bien entendu, on peut se servir de cette mécanique pour faire des choses super sales, comme ne pas servir une zone pour certains résolveurs. On pourrait par exemple empêcher les résolveurs d'Orange d'accéder à une zone ou leur répondre de la merde chaque fois qu'ils demandent…
La méthode étant super fastidieuse et pas forcément toujours intéressante parce que trop facilement contournable, on va plutôt se concentrer sur des mensonges plus simples à raconter.
# À la bourrin
Quand on a un serveur DNS qui fait du cache, la première chose est de s'assurer que les clients concernés (qui s'en servent comme résolveur) ne peuvent pas accéder aux DNS sur Internet (genre le trop célèbre `8.8.8.8`). Parce que sinon, tu auras beau te démener autant que tu veux, ça ne servira à rien du tout ! Une fois que c'est fait, pour empêcher complètement la résolution d'une zone, la méthode de bourrin consiste à déclarer qu'on en ait le maître :
```
zone "gouv.fr" {
type master;
file "bidon";
};
```
Où évidemment `bidon` est un fichier de zone parfaitement valide mais qui renvoit localhost pour toutes les requêtes (`/etc/bind/db.empty` est un bon candidat sous Debian).
Le souci, c'est que ce n'est pas bien souple et c'est surtout encore une fois très fastidieux parce qu'il faut répéter le processus sur chaque zone qu'on souhaite bloquer. Donc pour faire des choses un peu performante, c'est relativement lourd et on y passe des plombes, puisqu'il faut créer une zone pour chaque… Sans compter le fait qu'il faut répéter l'opération sur chaque résolveur…
# Plus subtil
Du coup, les derniers développement de bind ont amené une méthode un peu plus subtil, j'ai nommé RPZ (response policy zone). Là, on est dans l'artillerie lourde puisqu'il suffit d'ajouter un nom DNS dans une seule zone pour empêcher complètement sa résolution avec la réponse NXDOMAIN (le domaine n'existe pas…). C'est quand même vachement plus propre que de répondre systématiquement localhost.
Tout cela est d'ailleurs d'une facilité déconcertante à mettre en place, à maintenir et à répliquer puisqu'il s'agit d'une vulgaire zone ! On commence par ajouter la nouvelle instructions dans les options :
```
options {
response-policy { zone "gofuckyourmoth.er"; };
}
```
Puis on crée la référence de la zone en question :
```
zone "gofuckyourmoth.er" {
type master;
file "fuckyouapple";
};
```
Le fichier `fuckyouapple` doit alors contenir une zone valide où l'on place simplement des enregistrements CNAME bidon…
```
$TTL 36H
@ SOA gofuckyourmoth.er. robert.oot.fr (13 6h 10m 15d 30m)
NS gofuckyourmoth.er.
; on renvoit simplement NXDOMAIN pour apple.com
apple.com CNAME .
*.apple.com CNAME .
; ou carrément pour .ru !
*.ru CNAME .
```
Et toutes les possibilités sont ouvertes puisqu'on peut interdire tout ou presque et très facilement en prime.
On peut même faire plus vicelard en renvoyant une autre adresse que celle demandée :
```
*.megasexxx.com CNAME disney.fr.
```
Et pour vérifier que ça marche, suffit de demander les résolutions :
```
$ dig +short @localhost www.megasexxx.com
disney.fr.
68.71.220.121
```
Voilà, maintenant toi aussi tu peux transformer ton réseau domestique en succursale de la Syrie…

View File

@@ -0,0 +1,97 @@
+++
title = "Projet du week-end : monte ton propre chromebook mais avec des poils"
date = 2011-09-02
aliases = [ "post/2011/09/02/Projet-du-week-end-:-monte-ton-propre-chromebook-mais-avec-des-poils"]
[taxonomies]
tags = [ "bricole-picole" ]
+++
Une clé USB, un vieux netbook, un réseau Wi-Fi et deux litres de Coca. Voyons comment ces différents éléments peuvent interagir ensemble…
<!-- more -->
Le recyclage de PC, c'est un peu le scrapbooking du geek en vadrouille, l'artisanat pour informaticien, la petit touche de déco dans la maison façon Damidot avec de la barbe…
Comme je te sens tout émoustillé, on va se faire un petit chromebook, le côté big brother en moins, avec une distrib' d'hommes, qui sent la bébête, ArchLinux.
# Installation de Arch
Ce que j'aime avec Arch, c'est que tout est simple. Suffit de prendre l'iso officiel et de le `dd` violemment sur une clé USB et ça marche, pas besoin de UnetBootin, de FedoraUSBCreator et autres conneries… Non, simple, efficace, presque soviétique…
Bref, je vais pas détailler l'installation de la distrib en elle-même, parce que c'est pas bien intéressant, juste quelques conseils pour un netbook :
* Méfie-toi grandement des drivers de cartes rézo et notamment Wi-Fi : on est pas à l'abri d'un bricolage immonde, j'ai personnellement dû faire face à [ça](http://wireless.kernel.org/en/users/Drivers/b43) ; c'est pas bien compliqué, mais ça agace vite.
* Pour le partitionnement, ne me fais pas l'insulte de ne pas utiliser LVM. C'est pas parce qu'on en a pas besoin qu'il faut pas l'utiliser…
* Pour le choix des paquets, sauf prérequis spéciaux, contente-toi du strict minimum ;
Avec [la bonne doc qui va bien](https://wiki.archlinux.org/index.php/Official_Arch_Linux_Install_Guide), ça passe de toutes manières comme une lettre à la poste…
# Installation et configuration de netcfg
Dans la série « les outils de malades mentaux d'une simplicité crasse et d'une efficacité sans borne », je te présente `netcfg`. Kesseksa ? C'est tout simplement un gestionnaire de connexion, avec choix du profil qui marche intégralement en ligne de commande !
Il se trouve dans `core` et est parfaitement supporté par `/etc/rc.conf` (c'est suffisamment rare pour être signalé). Bon, comment que ça marche ? Une fois installé grâce à `pacman` (je te conseille d'ailleurs de jeter un œil sur les paquets recommandés, il y aura au moins besoin de `wpa_supplicant`), les profils sont stockés dans `/etc/network.d/` et quelques exemples sont dans `/etc/network.d/example/`.
Pour avoir la classe, suffit de repomper le fichier `/etc/network.d/example/wireless-wpa` en changeant les paramètres et évidemment le nom et en ajoutant bien entendu :
```
IPV6=STATELESS
```
Une fois cette opération réalisée, il faut aller commettre un autre forfait dans `/etc/rc.conf`, à savoir virer `network` et le remplacer par :
```
DAEMONS=(... net-profiles ...)
```
Tout en indiquant :
```
NETWORKS=(lenomduprofildeouf)
```
Correspondant au fichier dans `/etc/network.d/`. Et c'est tout ! Même Steve Jobs, paix à son âme *(NDLR : au moment de la rédaction de ces lignes, il n'est pas encore mort… Espérons que ce soit le cas d'ici à la publication…)*, peut aller se rhabiller question simplicité d'utilisation et ergonomie !
# Installation de Yaourt/ALSA/Xorg/FluxBox/gstreamer
Va maintenant falloir mettre un peu de sucre autour de tout ça. On va commencer par l'indispensable `yaourt` où ça se résume à peu de choses près à ça :
```
wget http://aur.archlinux.org/packages/package-query/package-query.tar.gz
tar zxvf package-query.tar.gz
cd package-query
makepkg -si
cd ..
wget http://aur.archlinux.org/packages/yaourt/yaourt.tar.gz
tar zxvf yaourt.tar.gz
cd yaourt
makepkg -si
cd ..
```
Il y a probablement besoin de tout un tas de saloperie pour faire tomber tout ça en marche (genre `gcc`), mais je te laisse le soin de trouver tout seul (faut pas déconner non plus, installer `yaourt`, ça devrait être du gâteau…).
Faut seulement une commande pour installer le reste :
```
useradd -m -g users filsdepute && yaourt -S xorg-server xorg-init xterm xorg-utils \
xorg-server-utils xf86-video-<ta carte graphique de ouf> xf86-input-evdev dbus \
policykit consolekit fluxbox alsa gstreamer0.10-plugins && echo \
"exec ck-launch-session startfluxbox" >> /home/filsdepute/.xinitrc && sed \
-i 's/syslog-ng/syslog-ng dbus/' /etc/rc.conf && sed -i "s?id:3:initdefault:?id:5:initdefault:?;
s?x:5:respawn:/usr/bin/xdm -nodaemon?x:5:once:/bin/su - -- filsdepute -l -c '/usr/bin/startx \
</dev/null >/dev/null 2>\&1'?" \\
/etc/inittab && mkdir -p /home/filsdepute/.fluxbox/ && \
touch /home/filsdepute/.fluxbox/startup && chown -R \
filsdepute:users /home/filsdepute/ && chmod 750 \
/home/filsdepute/.fluxbox/startup && setxkbmap fr bepo \
>> /home/filsdepute/.fluxbox/startup
```
Ouais, je sais j'abuse un peu… Mais basiquement c'est ça : tout installer, créer l'utilisateur qui va bien, lui faire démarrer X automatiquement, lui mettre un clavier qui ressemble à quelque chose, mettre fluxbox dans X… Pfiou… Avec tout ça, reste plus qu'à redémarrer et admirer le bouzin en fonctionnement.
# Installation de Jumanji
[Jumanji](https://pwmt.org/projects/jumanji/) est un navigateur avec une interface utilisateur inspiré de `vi`. À base de Webkit, il est **beaucoup** plus léger que Firefox et comme tu as déjà installé gstreamer, il peut très bien lire des vidéos en HTML5 sans que ton minable processeur de netbook ne commence à souffrir le martyr.
Donc il ne reste plus qu'une chose :
```
yaourt -S jumanji
```
Ton projet du week-end est bouclé… PARTY TIME !!!§§!

View File

@@ -0,0 +1,69 @@
+++
title = "Multiples BSSID avec hostapd"
date = 2011-09-24
aliases = [ "post/2011/09/24/Multiples-BSSID-avec-hostapd"]
[taxonomies]
tags = [ "rézo" ]
+++
« La doc, c'est mal »
Les développeurs d'OpenBSD
<!-- more -->
Parfois, il arrive que tu regardes le man pour te sortir d'un mauvais pas. Et parfois, le man te répond poliment d'aller te faire enculer par un babouin galeux. Manque d'infos, commandes incomplètes, peu claires ou obsolètes, voire même pas de man du tout, c'est le lot de certains logiciels qui pourtant sont bien pratiques une fois qu'on les maîtrise.
`hostapd` est le couteau suisse indispensable dès qu'on commence à vouloir faire caca avec des points d'accès Wi-Fi. C'est bien simple : on peut faire tout et n'importe quoi avec, le meilleur comme le pire. Tous les standards modernes et moins modernes sont supportés.
Partant de ce principe, je me suis dit que ce serait marrant de faire chier les voisins en m'arrangeant pour émettre un SSID qui soit le même que le leur mais sans rien au bout (pas de DHCP ou pas de NAT au choix) ou pire : un réseau ouvert, NATé et tout le toutim mais avec un gros tcpdump de macaque pour récupérer des cookies Facebook.
Donc, comment faire pour créer de multiples BSSID (plusieurs SSID sur plusieurs adresses MAC différentes) avec `hostapd` ?
Le premier qui me répond que c'est écrit au fond du fichier de configuration, je lui enfonce une banane dans le fondement !
Certes, un exemple est donné, mais il y manque malheureusement un détail subtil. Dans le principe, une configuration super basique de `hostapd` ressemble à cela :
```
# Paramètres généraux
driver=nl80211
hw_mode=g
channel=6
# Un SSID sécurisé en WPA2/AES et bridgé avec d'autres interfaces
interface=wlan0
bridge=br0
ssid=CasseToiPauvCon
wpa=2
wpa_pairwise=CCMP
wpa_passphrase=vienstouchermongrosbaton
```
Jusqu'à rien de spectaculaire, je passe sur les options de configuration dont tout le monde se fout. À noter simplement que le bridge en lui-même doit être créé avant de lancer `hostapd` avec la commande `brctl` par exemple.
Pour le second BSSID, suffirait donc théoriquement de rajouter ces quelques lignes :
```
bss=wlan0_0
bssid=00:80:48:6a:31:62
ssid=tamerelol
```
Sachant que l'adresse MAC de la carte d'origine est `00:80:48:6a:31:61`, `hostapd` va simplement créer une interface virtuelle avec une adresse MAC différente et enregistrer un SSID dessus. Le problème si l'on fait cela, c'est que ça va merder bien comme il faut pour une raison assez obscure…
`hostapd` indique en effet que le masque ne correspond pas à l'adresse du premier BSSID. La réaction normale de n'importe quel gorille devant cela étant : WTF ?!?
Renseignement pris, il faut impérativement que l'adresse MAC du premier BSSID (donc du premier réseau) se termine par un 0 au niveau binaire pour pas que le driver merde. Oui, je sais… Pourquoi 0 ? Quel rapport avec le masque ? Quel rapport avec le driver ? Quel rapport avec la choucroute ?
Alors comment fait-on quand on n'a pas la chance d'avoir une carte Wi-Fi dont l'adresse MAC se termine par un 0 ? Et bien, on la change tout simplement ! Avant de démarrer `hostapd`, il suffit de prendre au hasard une adresse MAC qui nous arrange :
```
ifconfig wlan0 hw ether 02:21:91:aa:aa:00
```
Et de modifier le second BSSID dans `hostapd.conf` :
```
bssid=02:21:91:aa:aa:01
```
Et le tour est joué ! À toi maintenant les joies d'affichier fièrement un FreeWifi/SFRWifi/Livebox-xxxx/NUMERICABLE-XXXX/etc… pour casser les burnes de tes voisins de palier !

View File

@@ -0,0 +1,42 @@
+++
title = "Quelques pistes pour une sauvegarde répartie (1)"
date = 2011-09-27
aliases = [ "post/2011/09/27/Quelques-pistes-pour-une-sauvegarde-répartie-(1)"]
[taxonomies]
tags = [ "système", "sauvegarde" ]
+++
Ou comment mettre toutes tes saloperies sur les PCs des autres pour être peinard…
<!-- more -->
L'une des grosses problématiques quand on s'auto-héberge, c'est la sauvegarde. Pour le moment, il n'y a concrètement que trois solutions :
* Faire ça en local : ça peut être suffisant pour revenir en arrière, mais si le chat pisse sur la machine, tu vas te retrouver en culotte courte ;
* Faire ça sur un disque externe : mieux, mais faudrait en réalité toujours se trimballer avec ;
* Externaliser la sauvegarde : et donc la confier potentiellement à des malfaisants.
`duplicity` permet de résoudre partiellement le souci de la sauvegarde externalisée : il est possible de chiffrer les fichiers de sauvegarde avant de les envoyer à l'extérieur.
C'est effectivement mieux, seulement tu restes dépendant d'un service éventuellement payant, et en plus, ça reste de la sauvegarde « old school », avec un catalogue en local, un média de sauvegarde.
Pas de quoi faire sauter une braguette, ce serait quand même bien de tenter un truc plus couillu, un machin qui pourrait se répliquer à des dizaines d'exemplaires en quelques minutes, voire quelques heures. Et en fait, tant qu'il s'agit de fichier relativement petit, ce n'est pas si difficile que cela à faire…
Comment alors ? En utilisant le seul système, réparti, robuste et accessible dans le monde entier, j'ai nommé le DNS.
La procédure est relativement simple :
* créer une archive contenant les données sauvegardées (je pense sincèrement qu'au delà de 10Mio, il vaut mieux tenter les choses autrement…) ;
* chiffrer cette archive (avant de montrer son cul au monde entier, on met des gros pixels devant comme dans les pornos jap !) ;
* effectuer une somme MD5 du fichier (cela servira de clé pour le DNS) ;
* encoder le fichier en ASCII grâce à UUencode : cet utilitaire crée des fichiers ASCII à partir de fichiers binaires. À ce moment, nous avons un fichier purement texte représentant un fichier chiffré d'une archive (tu suis toujours là, c'est bon ?).
* Utiliser l'utilitaire `split` pour séparer le fichier en morceaux de 65535 octets (la taille maximale d'un engistrement TXT sur un serveur DNS).
Il suffit alors d'insérer le contenu des fichiers générés par `split` dans des enregistrements TXT dans ton serveur DNS, en prenant bien soin de les numéroter correctement (par exemple <somme MD5>-numéro) et voilà le tour est joué. En quelques minutes, les serveurs DNS secondaires vont récupérer l'intégralité de ta sauvegarde et pour peu qu'ils soient un peu nombreux (2, 3 ou 4), cela crée autant de copies de cette sauvegarde qui sera alors accessible de n'importe où.
Et pour la taille ? Avec une archive compressée de 319Kio, j'obtiens un fichier chiffré de 320Kio (rien d'extraordinaire) et un fichier uuencodé de 441Kio (soit environ 37% de plus) ce qui nous donne 6\*64+57Kio au niveau de `split`.
Avec quelques bases (dotClear, SPIP, RSSLounge, etc…), un dump compressé MySQL ou des fichiers SQLite doivent facilement atteindre les 6 ou 7Mio chez tout bon singe qui se respecte. En extrapolant un peu, on peut raisonnablement penser que cela permet de placer 10Mio de données dans un fichiers de zone.
Alors ouais, c'est un peu la méthode à la porc, mais reconnais que c'est quand même pas mal efficace. En plus, ça permet de squatter en toute impunité les serveurs DNS secondaires de ton registrar ou de ton hébergeur, sans que celui-ci puisse soupçonner quoique ce soit. Manque juste le script poilu autour de ça qui permet de sauvegarder plus facilement que de tout faire à la main.
Au prochain épisode, une idée pour les gros (très gros) fichiers de sauvegardes.

View File

@@ -0,0 +1,56 @@
+++
title = "Dépucelage DNSSEC (1)"
date = 2011-12-04
aliases = [ "post/2011/12/04/Dépucelage-DNSSEC-(1)"]
[taxonomies]
tags = [ "internet", "système", "dns" ]
+++
C'est quand quelqu'un t'oblige [à faire confiance à un tiers](http://reflets.info/quand-lindustrie-du-divertissement-envisage-de-nous-interdire-de-choisir-nos-dns/) qu'il faut aller lui chier sur son bureau.
<!-- more -->
Non ce blog n'est pas mort, il y a encore des survivants dans la cage pour venir te chatouiller les testicules là où ça gratte bien. Des survivants qui constatent que plus ça va, plus on se fout très ouvertement de leur gueule.
Je me suis donc intéressé récemment à comment que ça marche DNSSEC et comment ça va permettre de continuer à faire de la merde en toute quiétude malgré les menaces plus ou moins ouvertes des petits chouchous du législateur.
# Bon alors skoi encore ce machin ?
Le gros souci du DNS, c'est que c'est pas fiable pour deux ronds. Pour éviter d'avoir à faire trop de résolutions, on se base sur des serveurs cache qui respectent plus ou moins les normes, mais surtout qui peuvent très facilement être censurés par l'administrateur de ce dernier ou par l'État.
Comme si ça ne suffisait pas, il est très facile pour un FAI indélicat ou un État « démocratique » de filtrer les requêtes à destination des serveurs racines et de répondre n'importe quoi. Bref, le DNS, c'est le premier point de censure. Et si ce n'est pas le seul problème que DNSSEC veut adresser, c'est ce qui m'intéresse pour le moment le plus.
# Tout-à-fait Thierry !
Avant de s'attaquer à comment ça marche®, je tiens à préciser quelque chose tout de suite : on peut difficilement parler DNSSEC à la main. Parce qu'il y a de nombreuses opérations pour une seule résolution. La description suivante reprend donc les grands principes, mais pas le détail technique complet (je te rassure jeune chimpanzé, tu verras des trucs avec plus de poils pour la prochaine fois).
Commençons par le commencement : comment ça marche une résolution de nom ? Mettons que tu veuilles accéder à `www.mescouilles.xxx` (pour les intéressés, c'est libre pour le moment), voici à peu près ce que va faire ton résolveur :
* quoiqu'il arrive le zozo connaît ., la racine du DNS ; il va donc lui demander qui sont les nameservers pour xxx. ;
* puis il va demander aux namesevers de xxx. qui gère mescouilles.xxx. ;
* puis il va demander au responsable de ce nom de domaine quelle est l'adresse IP de www.mescouilles.xxx.
Avec DNSSEC cette réponse va s'accompagner d'un enregistrement `RRSIG`, la signature de l'enregistrement que l'on vient de recevoir. Signature qui est garantie par une classique paire de clés publique/privée (on parle alors de `Zone Signing Key` ou ZSK)… qui se trouve dans le DNS.
À ce moment-là, tu te dis sûrement que ça va être effectivement compliqué de forger à la volée un enregistrement et une clé correspondante, mais que ce n'est pas infaisable. En plus, si la zone DNS que tu interroges est polluée par un vilain pirate, rien ne l'empêche jusqu'à présent de modifier la zone pour y insérer sa clé et signer lui-même les enregistrements.
Sauf que, bien évidemment, tout a été prévu. Une seconde paire de clés sert à signer la clé publique de la ZSK. On parle de KSK `Key Signing Key` ''(NDLA : de ce que j'ai compris, celle-ci n'est pas indispensable, mais on y reviendra plus tard)'', donc la partie publique est dans le DNS (je te rassure il n'y a pas de KSKSK ni de DSK) et l'« emprunte » est partagée (`Delegation Signer` ou DS) dans la zone parente !
Le système est donc bouclé : la racine est auto-signée et tout le monde connaît sa clé publique ; les zones filles enregistrent systématiquement un DS dans la zone parente pour garantir la solidité de leurs propres enregistrements. Pour s'assurer de ne pas être victime des pirates de l'Internet ou des pirates de la politique, il suffit donc de vérifier chaque enregistrement renvoyé lors de chaque étape (et de garder le tout en cache pour accélérer les manœuvres suivantes).
# Les questions que tu te poses sûrement
## Pourquoi une ZSK et une KSK ?
Dans ce vaste bordel, tu n'auras pas manqué de remarquer qu'on utilise deux clés pour chaque zone. Une clé pour la signer et une clé pour signer la clé de signature (là, j'ai dû paumer les derniers lecteurs…). La première raison est cryptographique : possédant la clé publique, les messages en clair (les enregistrements) et les messages chiffrés (les signatures `RRSIG`), il est théoriquement plus facile de reconstituer une clé privée par simple calcul (ce fut l'un des principaux éléments dans le cassage du code d'Enigma par exemple). Donc la seule solution (débile), c'est de changer très régulièrement cette clé.
La seconde raison est surtout organisationnelle : il est plus pratique de ne charger qu'une seule fois la clé au niveau du registrar plutôt que de la changer tous les 3 mois pour resigner les zones.
Et il y a aussi une relation, que je n'ai pas bien comprise je dois le reconnaître, entre les enregistrements `RRSIG`, les TTLs des enregistrements de la zone et la durée de validité de la clé de signature de la zone.
## Et pour les réponses négatives ?
Assez naturellement, on en vient à se dire que le censeur n'a pas grand chose à faire : au lieu de répondre de la merde qu'il ne pourra pas signer correctement, il suffit de répondre que le domaine n'existe pas.
Oui mais non, le cas a été prévu. Chaque zone dispose d'un enregistrement `NSEC` couplé à sa signature, qui permet donc de prouver que la réponse n'existe effectivement pas.
Les fans d'aspirine peuvent aller [vérifier une requête complète](http://dnssec-debugger.verisignlabs.com/www.example.com) et les différentes validations qui ont lieu. Pour les autres, rendez-vous la prochaine fois pour commencer à faire du caca avec des zones DNS.

View File

@@ -0,0 +1,53 @@
+++
title = "Mes deux centimes : créer des adresses « génériques » avec Postfix"
date = 2012-01-13
aliases = [ "post/2012/01/13/Mes-deux-centimes-:-créer-des-adresses-«-génériques-»-avec-Postfix"]
[taxonomies]
tags = [ "système", "mail" ]
+++
Parce que les adresses normales, c'est pas assez 2.0
<!-- more -->
Tu le sais depuis longtemps, l'Interwebz est un repère de pédo-nazis terroristes qui veulent rien qu'à te violer dans les toilettes avec des cuillères en bois. Mais il y a aussi les gens qui sont conscients des vrais problèmes de la vie et qui te propose régulièrement d'agrandir ton pénis ou de venir te marier en Russie ou encore te propose des réductions exceptionnelles sur les pilules de kikitoudur™.
Quand on laisse son adresse mail personnelle traîner sur certains sites Web douteux (ou pas d'ailleurs), on peut parfois avoir la désagréable surprise de se retrouver avec des messages non sollicités dont il est difficile de se débarasser.
Plus difficile encore trouver le fils de p\*\*\* qui a vendu ton adresse à un tiers, se torchant au passage avec la loi Informatique et Liberté de 1978. Et évidemment, je ne parle pas du cas, pas rare non plus, ou la base de données des courriels des utilisateurs/clients de <place your favorite site here> se fait hacker par Anonymous.
Bref, la tentation est grande d'utiliser une adresse mail différente pour chaque service/société sur Internet, ne serait-ce que pour avoir une traçabilité de qui a vendu quoi à qui (toujours utile pour taper sur les doigts des fautifs ou au moins les envoyer chier dans les règles).
La solution que j'utilisais à titre personnel jusqu'à présent consistait en un petit script générant une adresse mail imbitable et l'ajoutant dans /etc/aliases. Petit extrait :
```
#!/bin/bash
mailto="$1" # le courriel de destination (le vrai)
comment="$2" # un commentaire
# Formation d'une chaîne au hasard
# à partir du numéro de process qui lance le script
str0="$$"
str1=$(echo "$str0" | md5sum | md5sum)
firstletter=$(($RANDOM/2048))
str2=${str1:$firstletter:16}
# Affichage de l'adresse et màj de /etc/aliases
echo "Mail jetable : $str2@mescouilles.xxx"
echo "$str2: $mailto ($comment)" >> /etc/aliases
/usr/bin/newaliases
/etc/init.d/postfix reload &> /dev/null
```
C'est une bonne solution dans la mesure où cela donne vraiment des adresses uniques et difficilement exploitables par les nuisibles. Malheureusement, cette méthode exige d'avoir son serveur de messagerie sous la main ou de coder une page Web pour générer des adresses, bref cela ralentit considérablement les démarches pour générer une adresse jetable. Il faut donc réserver cette méthode aux plus douteux sites Web (et c'est pas toujours facile à déterminer…).
Ce serait quand même bien plus pratique de demander à Postfix de faire le boulot à ta place histoire que tu puisses encore plus glander devant My Little Pony. Comment ? Et bien, c'est en fait assez simple et cela tient en une seule option :
```
recipient_delimiter = +
```
J'ai mis un + mais on peut mettre n'importe quel caractère de la table ascii (sachant que « a » n'est pas forcément très recommandé…). Que va faire Postfix avec cela ? Mettons que l'on reçoit un `moncul+surlacommonde@mescouilles.xxx`. Postfix va commencer par vérifier que cette entrée n'existe pas dans `/etc/aliases` (ce qui permet d'ailleurs de rediriger les messages devenues indésirables vers `/dev/null`), puis que le compte `moncul+surlacommonde` n'existe pas et enfin, il va supprimer tout ce qui suit le + et vérifier que le compte `moncul` existe. Si ce n'est pas le cas, le message est refusé.
Une fois le paramètre mis en place, tu disposes donc d'autant d'adresses que tu le souhaites pour tous les services qui t'en demandent une : gibbon+youporn@buttse.cx, gibbon+xhamster@buttse.cx, gibbon+xvideos@buttse.cx, etc…

View File

@@ -0,0 +1,89 @@
+++
title = "Dépucelage DNSSEC (2)"
date = 2012-05-09
aliases = [ "post/2012/05/09/Dépucelage-DNSSEC-(2)"]
[taxonomies]
tags = [ "internet", "dns" ]
+++
Parce qu'il y a des moments où il faut aller péter des culs !
<!-- more -->
Dans la première partie, on a vu le principe de fonctionnement théorique, on va maintenant passer à la pratique. Il faut donc commencer par vérifier que ton registrar préféré peut prendre la clé que tu vas générer dans quelques minutes.
Il faut aussi t'assurer que la société ou l'association qui s'occupe de la racine de ton domaine prenne bien en compte DNSSEC. Il est ouvert sur .eu et .net pour tous, je ne connais pas le statut des autres.
Je précise que tout a été réalisé avec Bind 9.7, ce qui implique beaucoup de manipulation manuelle. Bind 9.9 semble bien plus optimisé, mais je n'ai pas encore eu le temps de m'y pencher.
# Générer ses clés
Un peu comme en SSH, il va falloir créer des clés pour signer les zones DNS. Comme on l'a vu avant, il y a deux paires de clés à générer : la ZSK (Zone Signing Key) et la KSK (Key Signing Key). On commence par la KSK, avec la magnifique ligne suivante :
```
# dnssec-keygen -f KSK -r /dev/urandom -a RSASHA256 -b 2048 -n ZONE mabite.net
```
On précise ici simplement le nom de la zone à signer et l'algorithme à utiliser. Attention, logiquement on ne doit pas utiliser `-r /dev/urandom` qui génère des nombres pseudo-aléatoires pas assez aléatoires (le problème c'est que, sans ça, ça prend un peu des plombes à faire !).
On renomme la clé générée (parce qu'elle a un nom absolument imbitable) :
```
# mv Kmabite.net.+008+51505.key Kmabite.net.ksk.key
# mv Kmabite.net.+008+51505.private Kmabite.net.ksk.private
```
Et rebelote pour la ZSK (c'est à peu près les mêmes commandes) :
```
# dnssec-keygen -r /dev/urandom -a RSASHA256 -b 2048 -n ZONE mabite.net
Kmabite.net.+008+61026
# mv Kmabite.net.+008+61026.key Kmabite.net.zsk.key
# mv Kmabite.net.+008+61026.private Kmabite.net.zsk.private
```
aintenant, on va délicatement insérer dans la zone nos grosses clés fraîchement générées :
```
# cat db.mabite.net | tail -n 2
$include /etc/bind/Kmabite.net.zsk.key ; ZSK
$include /etc/bind/Kmabite.net.ksk.key ; KSK
```
# Oh oui ! Signe-moi la zone !! Plus fort !
agie de l'informatique et des clés RSA, tu es maintenant prêt à générer une zone signée parfaitement valide d'un point de vue DNS. Pour cela, il suffit d'utiliser la commande ci-dessous :
```
# dnssec-signzone -e20120601000000 -p -t -g -k Kmabite.net.ksk.key -o mabite.net db.mabite.net Kmabite.net.zsk.key
```
Alors là, je sais : elle est un peu raide. Pourtant, c'est pas si compliqué que ça : `-e` permet d'indiquer la durée de validité de la clé de signature de zone (en l'occurence le 1er juin 2012). Les autres options servent simplement à indiquer le fichier de zone d'origine `db.mabite.net`, la zone en question `mabite.net` et les différents clés de chiffrement.
Elle est un peu dure à avaler, mais c'est parce que c'est la première… Tu vas t'habituer.
Une fois que la zone est signée, tu obtiens deux magnifiques fichiers, le fichier `db.mabite.net` et le fichier `db.mabite.net.signed` (au passage tu peux voir qu'il a sévèrement engraissé !). Il suffit donc d'activer proprement DNSSEC au niveau de Bind :
```
options {
dnssec-enable yes;
}
```
Et de changer la zone servie :
```
zone "mabite.net" {
type master;
file "/etc/bind/db.mabite.net.signed";
};
```
À partir de ce moment, il « suffit » que les serveurs primaires et secondaires supportent bien DNSSEC et… c'est presque fini !
Il reste en effet une ultime étape : donner les informations nécessaires à ton registrar pour qu'il puisse poser les informations de la KSK dans la zone mère de ta zone. Malheureusement, à cette étape, il faudra voir directement avec lui pour savoir quelle est la procédure.
Ah et pour la vérification rendez-vous [](http://dnssec-debugger.verisignlabs.com/) ou [](http://dnsviz.net/).
Voilà, maintenant, plus personne ne peut usurper ta zone (enfin, en tout cas pas les clients « sérieux »). Ouf, bon ben, je vais reprendre 4 mois de vacances moi, parce que je suis épuisé là…

View File

@@ -0,0 +1,42 @@
+++
title = "Fabriquer un lecteur de carte mémoire Nintendo 64 (1)"
date = 2012-08-24
aliases = [ "post/2012/08/24/Fabriquer-un-lecteur-de-carte-mémoire-Nintendo-64-(1)"]
[taxonomies]
tags = [ "jouage", "rebours", "bricole-picole" ]
+++
Diantre, mais pourquoi fabriquer une merde pareille ?
<!-- more -->
Le gros défaut des premières consoles CD disponibles était très probablement leur incapacité à sauvegarder quoique ce soit directement sur le média. Bien sûr, il reste la parade des mots de passe, mais chacun sait que ça reste moins pratique qu'une vulgaire sauvegarde de la progression. De ce point de vue, les cartouches gardent un côté simplicité/praticité/tu-branches-ça-marche que l'on peut difficilement égaler. Cependant, il existe une parade infaillible pour les fabriquants de console : la carte mémoire.
# Comment que c'est foutu…
Que ce soit en cartouche ou en carte mémoire, il y a finalement assez peu de méthodes permettant de sauvegarder des données de volumétrie relativement faible et à faible coût (s'agit pas de mettre trop de brouzoufs dedans quand même !) :
* puce EEPROM qui est claqué à chaque sauvegarde (et qui, de ce que je me souviens, a une durée de vie à peu près illimité) ;
* une puce SRAM maintenue par une pile (dont la durée de vie est donc celle de la pile) ;
* une puce de FlashRAM, dont le fonctionnement est proche au niveau adressage de la SRAM, mais dont la durée de vie est quasi illimitée.
Le gros problème, c'est que la FlashRAM coûte beaucoup plus cher que le reste. Elle sera donc très rarement utilisée, en tout cas dans les cartouches. À ma connaissance, seul *Command & Conquer 64* utilise une sauvegarde interne à base de FlashRAM.
Nous avons donc d'un côté des cartouches, faciles à utiliser et où ajouter une sauvegarde est possible, mais malheureusement augmente drastiquement le prix et de l'autre des cartes mémoires, certes moins faciles à utiliser, mais qui permettent de mutualiser les sauvegardes des jeux et de donner un peu de souplesse au joueur (il est possible de copier, de transporter ces sauvegardes, on est plus dépendant du média de jeu en lui-même).
Avec l'apparition d'Internet, pouvoir échanger des fichiers de sauvegarde (ou simplement garantir l'intégrité de ces fichiers de sauvegarde en les sauvegardant (sic !)) est devenu un vrai besoin. C'est bien gentil d'avoir débloqué tous les persos de ton jeu de baston préféré, mais c'est bien aussi d'en faire profiter les potes. Si aujourd'hui, ce besoin est en général couvert (via des hacks ou non) par les consoles modernes, pour les vieux clous, il faut se débrouiller tout seul !
# Le « controller pak »
La Nintendo 64 étant une console à cartouches, la carte mémoire, « controller pak » ou « memory pak », conçue pour la console peut paraître un peu inutile (voire un peu ridicule). Au départ, elle a été vendue comme permettant de transporter plus facilement ses sauvegardes, ses profils hors de chez soi. Ça aurait effectivement pu être bien, si elle ne possédait pas 3 défauts majeures :
* la console n'est capable d'adresser que 32Kio sur la carte mémoire de la console. Toutes les cartes mémoires fonctionnent donc sur le même modèle : les cartes mémoires ne peuvent contenir que 16 fichiers (16 « notes », codés sur 8 bits donc) et 123 pages de 2Kb chacun (10Kb étant réservés à l'adressage et à la gestion des notes en question) ;
* elle fonctionne avec de la SRAM maintenue par une batterie (ce qui est **mal** mais alors **super mal**) ;
* énormément de jeux utilisent quasiment l'intégralité d'une carte mémoire pour sauvegarder, sans possibilité de réduire ou de compresser ! *World Cup '98* et *ISS 98* utilise respectivement 121 et 117 pages sur les 123 disponibles !
Quand on a une grosse ludothèque, il peut arriver de jongler avec 5 ou 6 cartes mémoires différentes, sans compter le fait que les cartes mémoires non-officielles ont une certaine tendance à se corrompre ou à perdre des données sans crier gare.
Ce petit projet va donc permettre deux choses : ne conserver qu'une ou deux cartes mémoires pour un usage quotidien ; permettre de sauvegarder les sauvegardes dans un endroit plus sûr (en l'occurence mon PC). Accessoirement, cela va aussi permettre de continuer sur émulateur une partie commencée sur console et inversement.
Officieusement, il n'existe qu'un seul moyen de faire : le DexDrive 64. Vendu seulement aux États-Unis, cet accessoire se branche sur secteur et sur un port série et, à l'aide d'un logiciel qui ne tourne que sous Windows et qui n'est plus du tout maintenu, permet de récupérer les sauvegardes d'une carte mémoire.
ais, ça c'est pas drôle. Il y a en fait une caractéristique des cartes mémoires N64 qui va être très sympa à exploiter : au lieu d'être branché sur la console, les cartes mémoires N64 se branchent sur les manettes. Hors la manette N64 est un vulgaire périphérique série que l'on peut très facilement manipuler avec… Arduino !

View File

@@ -0,0 +1,60 @@
+++
title = "Fabriquer un lecteur de carte mémoire Nintendo 64 (2)"
date = 2012-08-31
aliases = [ "post/2012/08/31/Fabriquer-un-lecteur-de-carte-mémoire-Nintendo-64-(2)"]
[taxonomies]
tags = [ "jouage", "rebours", "bricole-picole" ]
+++
Les spécifs un peu détaillés du bordel
<!-- more -->
Ce billet va compiler, plus ou moins dans l'ordre, toutes les informations et hack que j'ai pu glaner ça et là pour tenter de reconstituer un lecteur de carte mémoire Nintendo 64. C'est donc, pour le moment en tout cas, le seul endroit où l'on pourra avoir une idée complète du fonctionnement de la manette N64 et pas, comme on le voit souvent, du statut des sticks et des boutons ou de comment fonctionne le Rumble Pack.
# Un périphérique série
Dans la grande tradition de Nintendo (faire le moins cher possible mais qui fonctionne), la manette N64 est un simple périphérique série avec seulement 3 broches : la terre, l'alimentation (entre 3 et 3.8V, mais 3.3 est la valeur recommandée) et une ligne de donnée bidirectionnelle.
![Schéma de branchement de la manette N64, août 2012](/N64_schema.jpg)
Source : [Instructables.com](http://www.instructables.com/id/Use-an-Arduino-with-an-N64-controller/)
Les bits qui passent sur la ligne fonctionnent quand même d'une manière un peu particulière :
![des bits et des zéros, août 2012](/n64bits.gif)
Source : [Nintendo 64 to PS/2 Mouse Translator](https://courses.cit.cornell.edu/ee476/FinalProjects/s2002/jew17/lld.html)
Pour transmettre un 1, il faut mettre la ligne en LOW (pas de tension) pendant une microseconde, puis la mettre en HIGH (tension) pendant 3 microsecondes. Pour un zéro, c'est le contraire : 3 microsecondes de LOW pour une microseconde de HIGH. La réception de données fonctionne exactement de la même manière. Maintenant, tout se résume à une histoire de bits et de timing.
# Sors ton bit
La manette et le slot pour la carte mémoire sont a priori assez stupide (je dis a priori parce que j'ai pas encore tâté la bête à 100%). La manette reçoit une série d'instructions suivie d'un bit stop (un dernier bit inséré à la fin de la chaîne de contrôle). Elle répond 2 microsecondes après le bit stop, envoit un bit stop puis reste silencieuse pendant 200 microsecondes (impossible de l'interroger).
Une compilation de différente source ([un projet pour brancher des manettes Gamecube sur des consoles N64](http://svn.navi.cx/misc/trunk/wasabi/devices/cube64/), [le code source de Mupen64+, l'un des rares émulateurs libres de la console](http://code.google.com/p/mupen64plus/) et diverses informations glânées sur des fora) permettent de dégager le comportement approximatif ci-dessous.
Envoyer 0x00 initialise la manette (je ne sais pas bien à quoi cela correspond mais probablement l'initialisation du stick analogique en position neutre par exemple). La console répond des choses (pas très intéressantes) pour savoir quel accessoire est branché sur la manette (Rumble Pak, Controller Pak, Transfer Pak, etc).
Envoyer 0x01 demande à la console l'état des boutons. Elle renvoit alors 16 bits qui correspondent aux boutons (1 pour enfoncé) : A, B, Z, Start, Pad Haut, Pad Bas, Pad Gauche, Pad Droit, rien, rien, L, R, C-Haut, C-Bas, C-Gauche et C-Droit ; puis elle renvoit 16 bits correspondant à la position (axe X et axe Y) du stick analogique.
Envoyer 0x02 provoque une lecture sur le contacteur de la manette. Les deux octets suivants correspondent à une adresse sur le contacteur. Les adresses en question semblent correspondre au mappage suivant :
* 0x0000 -> 0x7FFF : les adresses mémoires de la carte mémoire ;
* 0x8000 : identification et initialisation ;
* 0xC01B : contrôle du moteur du Rumble Pak.
La manette envoit systématiquement 32 octets de données en réponse lorsque l'on interroge une adresse de la carte mémoire. A priori, interroger autre chose provoque l'envoi de message d'erreur divers (pas forcément toujours clair d'ailleurs…). Bref, lire la carte mémoire en entier devrait consister à envoyer des codes 0x02 puis des adresses de 0x00, 0x00 à 0x7F, 0xFF et lire les 32 octets donnés à chaque réponse. Il ne reste en théorie plus qu'à tout mettre au format brut dans un fichier.
Envoyer 0x03 provoque une écriture sur le contacteur de la manette. En fait, « écriture » est un bien grand mot. Ça ressemble plus à une fonction à tout faire. Par exemple envoyer 0x03 suivi de 0xC0, 0x1B suivi d'une série de 1fait vibrer le Rumble Pak (une série de 0 arrête les vibrations). Envoyer 0x03 suivi de 0x80, 0x01 renvoit une valeur qui semble être un checksum des données venant juste d'être écrites. Apparemment l'adresse du statut pour un Controller Pak (0x8001) correspond à un checksum après une écriture et à 0x00 pour signaler que c'est bien un Controller Pak.
L'adressage en lui-même semble contenir un petit trick permettant de s'assurer que l'adresse est lue sur des bornes précises de 32 octets (tous les 0x0020 adresses et pas en plein milieu !). L'émulateur Mupen64+ effectue les opérations suivantes sur les 2 octets envoyés pour obtenir l'adresse à lire dans la mémoire :
```
int address = (OCTET1 << 8) | OCTET2; // permet de calculer l'adresse sur 16 bits au lieu de 2 fois 8 bits
address &= 0xFFE0; // masque permettant de supprimer les éventuelles irrégularités dans les lectures
```
Cela semble d'ailleurs être confirmé par les traces : 02 00 35 provoque une lecture de 32 octets en 0100 sur la carte mémoire. C'est très probablement le rôle du GAL situé sur la carte mémoire entre la SRAM et les pins.
Bref, la suite au prochain numéro avec le fonctionnement de base sur une carte Arduino !

View File

@@ -0,0 +1,122 @@
+++
title = "nginx : ne pas faire de la merde avec PHP"
date = 2012-09-14
aliases = [ "post/2012/09/14/nginx-:-ne-pas-faire-de-la-merde-avec-PHP"]
[taxonomies]
tags = [ "système", "nginx", "php" ]
+++
> Du pain, du vin, du PHP
*Le gars dans la pub là*
<!-- more -->
Ave visiteur !
Si toi aussi tu rêves d'utiliser un serveur Web moderne, avec des belles lignes de configuration toutes propres et fourni avec les piles, ça fait probablement un moment que tu t'intéresses à nginx (prononce… euh… prononce comme tu veux en fait). Le serveur Web venu du pays de Tonton Staline envoit sévèrement du pâté quand il s'agit de servir des contenus statiques ou de faire des reverse proxies avec des poils autour, mais il a un très gros défaut pour les débutants : il n'interprète pas directement les CGI ou le PHP.
Il faut donc se coltiner à la mano des processus PHP pour interpréter tout le caca qu'il y a besoin d'interpréter… et il faut vachement se méfier de ce qu'on lui fait bouffer.
# PHP rapide interface de passerelle commune
Pour faire tourner du PHP sur nginx (prononce… mouais enfin, vaut mieux pas le prononcer en fait), il faut donc commencer par lancer ce qui concerne PHP grâce à `spawn-fcgi` avec un machin qui ressemble vaguement à ça :
```
/usr/bin/spawn-fcgi -s /var/run/fastcgi-php.run -U www-data -u www-data -g www-data -f /usr/lib/cgi-bin/php -P /var/run/fastcgi-php.pid
```
Ça lance donc une processus d'interprétation de code PHP (CGI) le tout avec l'utilisateur Web standard sous Debian (oui, je fais mes tests sous Debian et **j'assume**) vers une socket Unix toute propre `/var/run/fastcgi-php.run`. Pour lancer plusieurs processus de ce type, il faut ajouter quelques variables d'environnement avant la commande :
```
PHP_FCGI_MAX_REQUESTS=500
PHP_FCGI_CHILDREN=4
```
Donc, 5 processus (papa et 4 fistons) et 500 requêtes maximum par processus.
Simple, efficace, pas cher. Au niveau de nginx (pro… nan mais sérieusement, ils auraient pas pu trouver un meilleur nom ?), il faut mettre les bons paramètres chaque fois qu'on veut interpréter un fichier PHP. Voici un exemple qu'on trouve un peu partout sur le net :
```
location ~ \.php$ {
fastcgi_pass unix:/var/run/fastcgi-php.run;
include fastcgi_params;
}
```
À vue de pied pas trop de soucis : chaque fois qu'nginx (bon ok, j'arrête) croise une URL qui se termine en `.php`, il balance le tout dans la socket Unix (et quelques autres paramètres à la con…). C'est là qu'on va commencer à se fâcher.
# bad interpreter
En effet, le problème avec cette configuration, c'est que nginx peut balancer __à peu près n'importe quoi à PHP__. Mais vraiment. Et sans réfléchir PHP va aller interpréter le premier truc cohérent dans le chemin qu'on lui envoit (va falloir qu'on discute un jour de ce qui est « cohérent »). Il est donc tout à fait possible de former une URL de ce type :
```
http://buttse.cx/image.png/test.php
```
Et d'aller donc interpréter une image dans PHP. Ouais, c'est moche. Premier truc donc, intercaller ce qu'il faut comme bout de code pour que ça ne puisse pas arriver. Et la magie `try_files` entre en action :
```
location ~ \.php$ {
try_files $uri $uri/ =404;
fastcgi_pass unix:/var/run/fastcgi-php.run;
include fastcgi_params;
}
```
Là, impossible de baiser le système : si l'URL est foireuse, nginx renvoit 404 dans la face du monsieur en l'éclaboussant en prime. Maintenant, le souci, c'est que de temps en temps, il y a besoin d'avoir des URLs qui ne se terminent pas en `.php` mais qui doivent quand même être envoyées vers un fichier PHP.
Genre au pif, sur un blog quelconque `http://buttse.cx/post/2012/02/OMG-Un-butt-plug-sans-fil` doit être en réalité être interprété par `index.php`.
L'exemple que l'on trouve généralement est le suivant :
```
location / {
try_files $uri $uri/ /index.php$uri?$args;
}
## tout ce qui n'est pas statique (dossier ou fichier) est converti en URL et envoyé dans index.php
location ~ ^(.+\.php)(/.*)?$ {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass unix:/var/run/fastcgi-php.run;
include /etc/nginx/fastcgi_params;
}
## tout ce qui se termine par .php ou avec des trucs derrière est interprété
```
Et boum !! Même arnaque que précédemment ! Si je tape l'innocente URL suivante :
```
http://buttse.cx/image.png/tamere.php/youpi/troll
```
PHP va de nouveau aller piétiner les baloches des autres. Et le pire, c'est que je ne peux pas me permettre de mettre un `try_files` avant, parce que justement le fichier PHP ne correspond pas au bout de l'URL en question. Je pourrais mettre les deux règles bout-à-bout : dans le bon ordre, cela permettrait de « limiter » le problème. Mais je pourrais toujours envoyer des saloperies à des scripts PHP légitimes, genre :
```
http://buttse.cx/admin.php/youpi/troll/face/lol/catz
```
# Sauve-nous !
La seule solution que j'ai trouvée (et probablement pas la plus élégante) consiste à spécifier systématiquement les URLs qui ont besoin de `PATH_INFO` pour fonctionner. Ça donne un truc un peu crade :
```
location ~ ^/(index|menu|path|images).php(/.*)+ {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass unix:/var/run/fastcgi-php.run;
include fastcgi_params;
}
## les scripts index.php, menu.php, path.php et images.php suivi de paramètres sous forme d'URLs (obligatoire à cause du +)
## sont interprétés en PATH_INFO
location ~ \.php$ {
try_files $uri $uri/ =404;
fastcgi_pass unix:/var/run/fastcgi-php.run;
include fastcgi_params;
}
## les autres fichiers PHP sont interprétés directement en vérifiant qu'ils existent
```
Cela impose de connaître tous les scripts qui ont besoin d'utiliser du `PATH_INFO` et leur chemin complet pour ne pas se faire empapaouter par le premier crypto-terroriste venu. C'est donc particulièrement lourd, même si nginx permet d'arriver à tout faire tenir en un seul `location`.
Voilà, maintenant que j'ai sauvé l'humanité, je vais aller faire une sieste.

View File

@@ -0,0 +1,51 @@
+++
title = "Fabriquer un lecteur de carte mémoire Nintendo 64 (3)"
date = 2012-09-21
aliases = [ "post/2012/09/21/Fabriquer-un-lecteur-de-carte-mémoire-Nintendo-64-(3)"]
[taxonomies]
tags = [ "jouage", "rebours", "bricole-picole" ]
+++
> J'avais jamais vu autant de bits d'un coup…
*Oui, je sais, elle est facile.*
<!-- more -->
Ave lecteur,
Plongeons plus profondément dans les entrailles de la bestioles et profitons-en pour apporter quelques petites corrections par rapport à la dernier fois.
# Compagnie Républicaine de Connards
es périgrinations au royaume du bit sur port série m'ont appris quelques petites subtilités vis-à-vis de l'adressage des cartes mémoires Nintendo 64. Dans le principe, tout ce que j'ai donné précédemment est effectivement vrai : les adresses vont de 0x0000 à 0x7FFF et l'on ne peut taper que sur les multiples de 0x20 de sorte à toujours lire ou écrire 32 octets de données.
aintenant, il y a une petite subtilité dans l'adressage en lui-même : il n'y a pas seulement un CRC pour les données (ce qui paraît logique) mais aussi un CRC pour les adresses (!). Lorsque l'on veut lire ou écrire des données, il faut donc passer une moulinette pour transformer les adresses de lecture.
Heureusement pour moi (et surtout pour mes neurones), [le projet Cube64](http://svn.navi.cx/misc/trunk/wasabi/devices/cube64/notes/addr_encoder.py) a déjà un algorithme tout prêt (en Python) pour calculer ce CRC d'adresse. L'auteur a également fait une rétro-ingénierie du CRC de données (qui n'est pas le même et qui est beaucoup plus compliqué) mais je n'ai pas vraiment eu le temps de m'y intéresser jusqu'à présent. Et même si c'est cohérent pour un dumper/loader de cartes mémoire, c'est pour le moment trop complexe à implémenter dans Arduino.
Le CRC de données est codé sur 8 bits, sous forme d'un octet de retour systématiquement : quand on demande à lire 32 octets de mémoire, la manette en renvoit 33, le dernier étant le CRC de données ; quand on demande à écrire 32 octets en mémoire, la manette utilise l'octet de CRC en guise de réponse. Le CRC d'adresse lui est codé sur 5 bits. Comme les adresses sont systématiquement « arrondies » de 0x20 en 0x20, cela laisse exactement 5 bits de libre dans les bits de poids faible et c'est là qu'on glisse ce nouveau CRC.
Bref, comment que ça marche le bouzin ? Chaque fois que l'on souhaite lire la carte mémoire, on envoit donc l'instruction 0x02 suivie de l'adresse **codée via le CRC** à partir de l'adresse en clair. Balayer l'ensemble du contenue de la mémoire demande ainsi une opération supplémentaire, la traduction des adresses.
Ainsi, pour lire le secteur 0x0020 de la carte mémoire, on doit respecter l'ordre suivant, dans un timing très précis :
* envoyer 0x02 à la manette (ordre de lecture) ;
* calculer le CRC de l'adresse -> 0x0015 ;
* envoyer l'adresse et le CRC additionné : 0x0035 ;
* lire 32 octets de données, lire 1 octet de CRC.
Et évidemment, il faut exécuter tout ça dans un timing absolument impeccable.
# Présente n64cmd par Waffle
Heureusement pour moi (et pour mes neurones), [Waffle](http://arduino.cc/forum/index.php?action=profile;u=4537) a écrit une petite fonction tout en assembleur du meilleur goût pour effectuer des lectures et des écritures successives sur le bus de la manette N64, adapté pour Arduino, j'ai nommé : [n64cmd](http://pastebin.com/f286fdbc2) !
La fonction est assez simple : elle prend en entrée un pointeur vers une structure d'octet qui contiendra la réponse (préférentiellement des entiers non-signés sur 8 bits `uint8_t` ou plus classiquement des `unsigned char`), suivi de la taille que l'on souhaite lire, un pointeur vers une structure d'octet qui contient la question, suivi de sa taille. La fonction en elle-même renvoit le nombre d'octets lus au total (qui doit toujours être inférieur ou égal à la taille demandée de la réponse).
Waffle a donné un petit exemple d'utilisation sous la forme d'un programme Arduino qui renvoit sur le port série les boutons qui viennent d'être appuyés sur la manette, le tout en utilisant une structure faisant 4 octets pile poil. Cette version n'utilise donc que l'instruction 0x01.
Pour les branchements, il faut relier les ports GND et 3.3V sur les ports correspondants de la manette et mettre le port série de la manette sur le port 9. Il est également nécessaire de mettre une résistance de tirage de 1K entre le 3.3V et le port série (cela permet d'éviter les erreurs lors de la lecture).
Pour le lulz et en attendant la suite, ton serviteur te propose une version détournée du sketch de Waffle qui permet de déclencher et d'arrête à intervalles réguliers le kit vibration préalablement branché sur la manette N64. Une bonne soirée en perspective :).

View File

@@ -0,0 +1,51 @@
+++
title = "Fabriquer un lecteur de carte mémoire Nintendo 64 (4)"
date = 2012-10-05
aliases = [ "post/2012/10/05/Fabriquer-un-lecteur-de-carte-mémoire-Nintendo-64-(4)"]
[taxonomies]
tags = [ "jouage", "rebours", "bricole-picole" ]
+++
Pour quelques lignes de C de plus…
<!-- more -->
Ah ah !
Bon, chose prosmise, chose dûe, maintenant que le machin marche à peu près et commence à ressemble à quelque chose, je peux enfin publier le code sous ma licence préférée : la licence Rien À Branler. Pour résumer : j'assume d'avoir codé ça comme un gros goret et que des fois ça marche pas et que je sais pas pourquoi. Pour l'instant, c'est à peu près présentable et à peu près utilisable. Si tu es un gentil monsieur du bien, tu peux prendre ce code, le copier et faire les améliorations dont il a (grandement) besoin.
Ceci dit, quelques explications sur la partie Arduino (et le pourquoi du comment j'ai fait ça comme ça).
# Le loop de base
Le `setup()` n'a rien de particulier : il met simplement le Arduino en 115200 (la vitesse maximale du port série). Cela permet d'accélérer sensiblement les sauvegardes et les restaurations, sachant que, quoiqu'il arrive, cela prend toujours un temps non négligeable (environ 1 minute et demi).
Le `loop()` est très classique aussi : en fonction de la touche reçue, on déclenche la fonction `Backup()` ou la fonction `Restore()`.
# Sauvegarde des cartes mémoire
La fonction de sauvegarde va effectuer deux boucles imbriquées qui permettent de balayer toutes les valeurs d'adresses de 0x0000 à 0x7fe0. Après avoir encodé l'adresse avec le CRC d'adresses, `n64cmd()` envoit ce qu'il faut à la manette et récupère la réponse. Celle-ci est affichée sous la forme `<adresse en clair>:<données sous forme de code hexadécimal>\n`.\
Ne reste plus qu'à lire ces données correctement depuis le port série. J'ai volontairement choisi de ne pas recoder ces informations sous forme binaire mais de conserver le format texte (ce qui fait qu'un dump complet d'une carte mémoire prend environ 71Kio au lieu des 32Kio du format « brut »). Il s'agit donc simplement de lire caractère caractère jusqu'à tomber sur deux `\n` de suite indiquant la fin de la lecture.\
Et pour les gens qui se poseraient la question : se connecter à un Arduino fait un reset automatique. Il faut donc attendre environ 2 secondes avant de commencer à envoyer du caca sur la ligne série, sinon tout est perdu. Si ça avait été mieux documenté ce truc-là, j'aurais pas galéré autant…
# Restauration des cartes mémoire
La restauration ne fonctionne pas tout à fait de la même manière : parce que le hardware est assez limité, il a fallu que je réinitialise complètement la variable `buffer` qui permet de lire ce qui est entré au niveau du port série à chaque itération. En effet, que ce soit avec un malloc ou avec une initialisation standard de tableau en C, Arduino recycle toujours le même secteur mémoire, laissant des trucs assez crade au passage. `memset` absolument obligatoire sur le coup, sous peine de grosse prise de tête au moment de la relecture de la mémoire… Bref.
Donc, il s'agit ensuite de convertir des couples de deux caractères en hexadécimal tout au long d'une chaîne exactement similaire à la précédente. `sscanf` est donc ton ami et avec plein de pointeurs bien crades et bien illisibles, on arrive à lire toute la chaîne dans la variable `command` et à passer cette dernière à `n64cmd()`. Voilà ce que j'appelle du code McDo : rapide, facile et indigeste.
# Et donc maintenant ?
Dans l'absolu tout fonctionne. Il y a encore quelques petits soucis de temps en temps :
* pour une raison que j'ignore, le Arduino n'envoit des fois strictement aucune donnée. En général, il faut le débrancher et le rebrancher et ça se remet en marche.
* Je ne sais toujours pas si l'initialisation de la carte mémoire est toujours utile. A priori oui, mais je n'ai jamais réussi à trouver si c'était vrai ou pas.
* Il n'y a toujours pas de vérification du fameux 33ème octet correspondant au checksum de données. En pratiquement 3 semaines d'utilisation intensive, je n'ai eu toutefois qu'une seule lecture incohérente de la carte mémoire.
ême si c'est une première étape encourageante (et que cela permet de résoudre mon problème), il faudrait encore continuer pour compléter les fonctionnalités. D'abord, en essayant de lire le contenu de la carte mémoire à partir d'un dump. Je sais déjà comment trouver la liste des notes. Mais je ne sais malheureusement pas pour le moment comment isoler chaque note sur la carte mémoire. Ce serait quand même foutrement classe de pouvoir lire le contenu d'un dump pour savoir ce qu'i y a dedans !
Et cela pourrait amener à rendre le tout compatible avec les fichiers produits par DexDrive64, ce qui permettrait de récupérer des sauvegardes directement sur [GameFAQS](http://www.gamefaqs.com/) ou autres. Je n'ai pas encore pris le temps d'analyser dans le détail comment fonctionne les fichiers DexDrive64, mais je suis certain que cela ne doit pas être bien compliqué.
La compatibilité avec les émulateurs pourraient être intéressantes aussi. Pour le moment, je ne sais pas trop comment faire non plus : je récupère 32Kio de données dans mes `.ampk`, les émulateurs créent des fichiers `.mpk` qui font en général 128Kio. C'est quoi le vide autour ? Est-ce que je peux le remplir (non, il n'y a pas de sous-entendu scabreux ici. Jamais.) ? Aucune idée pour le moment.

View File

@@ -0,0 +1,128 @@
+++
title = "DNSSEC : méthode BIND9.9"
date = 2013-02-15
aliases = [ "post/2013/02/15/DNSSEC-:-méthode-BIND9.9"]
[taxonomies]
tags = [ "internet", "dns" ]
+++
Nan, parce que la [précédente](/depucelage-dnssec-1/) [méthode](/depucelage-dnssec-2/), c'était vraiment _hardcore_
<!-- more -->
Ah ah, ça faisait longtemps qu'on avait pas fait des trucs un peu gue-din avec du DNSSEC et des poils autour.
DNSSEC en BIND9.7, c'était quand même un bon pain dans le cul : obligé de gérer des clés (c'est pas forcément ce qu'il y a de pire), de signer les zones systématiquement et surtout pas vraiment de souplesse.
Heureusement BIND9.9 apporte une solution durable et élégante au problème, avec une touche de barbe quand même.
# Comment ça marche-t-il donc dis donc ?
Le principe de base dans BIND9.9 pour automatiser et simplifier le plus possible DNSSEC est de transformer les zones en zones dynamiques. Ça peut paraître bizarre de faire que du dynamique alors qu'il n'y a pas particulièrement de raison, mais c'est là tout le génie de la chose : BIND re-signe tout seul les zones comme un grand à chaque modification et re-signe même automatiquement les zones plusieurs fois par jour !
À la base, il faut toujours générer les clés (et toujours de la même manière), sauf que là, on va toutes les grouper dans le même répertoire (en espérant qu'elles fassent pas de cochonneries ces petites dévergondées).
La méthode de génération des clés est toujours la même (que ce soit pour la KSK ou la ZSK) :
```
# dnssec-keygen -f KSK -r /dev/urandom -a RSASHA256 -b 2048 -n ZONE ve.lu
# dnssec-keygen -r /dev/urandom -a RSASHA256 -b 2048 -n ZONE ve.lu
```
Dans /etc/bind/keys, on a donc :
```
ls /etc/bind/keys/
/etc/bind/keys/Kve.lu.+008+10578.key
/etc/bind/keys/Kve.lu.+008+10578.private
/etc/bind/keys/Kve.lu.+008+45370.key
/etc/bind/keys/Kve.lu.+008+45370.private
```
Sauf que cette fois-ci, il faut éviter de renommer les clés (BIND les repère comme ça, je ne sais pas exactement pourquoi). Et va falloir dire à pépère où elles sont les cléclés pour pas qu'il soit paumé :
```
options {
[…]
directory "/var/cache/bind";
key-directory "/etc/bind/keys";
}
```
D'ailleurs, il faut en profiter pour mettre un répertoire de cache, c'est là où BIND fera toutes ses salades avec les zones tampons et les re-calculs de clé. Au passage, n'oublie surtout pas de rendre les clés privées lisibles par l'utilisateur bind (quelque soit la distribution d'ailleurs), sinon il va avoir du mal à savoir ce qui se passe.
Avec ces instructions, en redémarrant le serveur de nom, il devrait cracher un peu de purée dans les logs pour dire qu'il a compris qu'il y avait des clés.
# Dynamise-moi la zone DNS
On y vient petit margoulin. On va commencer, par créer une zone DNS tout ce qu'il y a de plus normale, bien propre sur elle, avec quelques enregistrements rigolos :
```
$ORIGIN .
$TTL 21600 ; 6 hours
ve.lu \ IN SOA\ nameserverdelamort.mabite.net. root.mabite.net. (
\ \ \ 2012111710 ; serial
\ \ \ 10800 ; refresh (3 hours)
\ \ \ 900 ; retry (15 minutes)
\ \ \ 604800 ; expire (1 week)
\ \ \ 10800 ; minimum (3 hours)
\ \ \ )
NS\ nameserverdelamort.mabite.net.
A\ 1.2.3.4
```
Et pour faire sobre, au niveau de BIND, la configuration de zone peut être réduite au strict minimum :
```
zone "ve.lu" {
type master;
file "/etc/bind/masters/db.ve.lu";
};
```
On va maintenant rendre la zone dynamique, ce qui permettra de lui baisser sa petite culotte et de lui mettre un gros taquet dans les fesses à n'importe quel moment de la journée. La zone dynamique, c'est un peu la culotte fendue du DNS avec les salsifis qui dépassent du cabas :
```
allow-update { localhost; }
```
À partir de maintenant, on peut faire des modifs à la volée avec `nsupdate` (et une syntaxe un peu à chier, faut reconnaître mais bon) :
```
# nsupdate -l
> zone ve.lu
> update add www.ve.lu. 86400 CNAME superserverweb.moncul.net.
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;ve.lu. \ \ \ IN\ SOA
;; UPDATE SECTION:
www.ve.lu. \ 86400\ IN\ CNAME\ superserverweb.moncul.net.
> send
> quit
```
`nsupdate` permet donc de sélectionner une zone, d'envoyer des modifications (ajout ou suppression) voire même des modifications conditionnelles, et de les appliquer par un simple send.
On peut d'ailleurs voir que le fichier journal de la zone `ve.lu` a été créé et qu'il contient notre dernière modif :
```
# named-journalprint db.ve.lu.jnl
del ve.lu. \ \ 21600\ IN\ SOA\ nameserverdelamort.mabite.net. root.mabite.net. 2012111710 10800 900 604800 10800
add ve.lu. \ \ 21600\ IN\ SOA\ nameserverdelamort.mabite.net. root.mabite.net. 2012111711 10800 900 604800 10800
add www.ve.lu. \ 86400\ IN\ CNAME\ superserverweb.moncul.net.
```
En prime, il met automatiquement à jour la version de la zone (tu sais, le truc que tu oublies tout le temps et qui te fout bien la rage !). Et maintenant, magie du DNSSEC, en une seule ligne supplémentaire dans la section de la zone, BIND signe tout seul la zone dynamique :
```
auto-dnssec maintain;
```
Et là, après un reload, les soirées de l'ambassadeur sont toujours un succès : non seulement la zone est mis à jour automatiquement grâce à `nsupdate` mais en plus, c'est BIND qui se débrouille tout seul comme un grand pour tout signer à chaque modif \o/\
La semaine prochaine, je fais de la blanquette de veau.

View File

@@ -0,0 +1,110 @@
+++
title = "OpenBSD en lecture seule"
date = 2013-07-12
aliases = [ "post/2013/07/12/OpenBSD-en-lecture-seule"]
[taxonomies]
tags = [ "système", "openbsd" ]
+++
Parce que de temps en temps, il faut savoir fermer sa gueule…
<!-- more -->
OpenBSD, c'est beau, c'est bien et ça a du poil aux pattes et tout ce qui va avec. Pour faire un routeur maison, c'est simplement du bonheur en terme de stabilité, de fiabilité et de fonctionnalités. Il ne reste qu'un problème : la robustesse vis-à-vis des coupures électriques.
Un bon routeur ~~est un routeur mort~~ se doit de pouvoir résister en cas de coupure de jus, d'attaque nucléaire ou de pénurie de café. Et en l'occurence, le système de base d'OpenBSD ne le peut pas sans un petit coup de pouce de notre ami `mfs(8)` (nan, ce n'est pas sale, c'est vraiment son nom).
# Un copain de RamFS
`mfs(8)`, pour Memory File System, est un copain de `RamFS` (et vaguement de `tmpfs`) avec grosso mode les mêmes fonctionnalités : il s'agit de charger en RAM un morceau de système de fichier présent sur disque de sorte à pouvoir passer ce dernier en lecture seule et ne plus risquer de désagrément en cas de redémarrage intempestif. Bien entendu, ça a un coût en RAM, mais tu vas pouvoir constater qu'il n'est pas bien difficile de faire tourner un système basique mais parfaitement fonctionnel avec finalement pas grand chose.
# Admettons, t'installes un OpenBSD
Petit note préliminaire : il est relativement facile de basculer un système déjà installé en lecture seule. Ça demande simplement un peu plus de manip et il faut être très prudent à chaque étape. Je préfère décrire ici la méthode pour installer un OpenBSD en lecture seule (c'est moins barbant…).
Première chose : à l'installation, virer la partition de swap (b) et ne garder que un `/` (a disons 1Gio) et un `/mfs` (d disons 1Gio aussi) ; bon, les tailles importent peu, tu fais comme tu le sens mais souviens-toi bien que tu vas charger des trucs en RAM… Faudrait donc pas non plus prévoir un disque de 1Tio si tu n'as que 256Mio de RAM.
Bref, une fois l'installation terminée, tu vas te retrouver avec un partitionnement qui ressemble à peu près à ça :
```
# mount
/dev/wd0a on / type ffs (local)
/dev/wd0d on /mfs type ffs (local, nodev, nosuid)
```
# Et là, le file system, il prend le chocolat et il encule la marmotte
Il s'agit maintenant de transférer les éléments indispensables au bon fonctionnement du système sur la partition `/mfs`, j'ai nommé `/dev`, `/var` et `/tmp`.
Pour `/dev`, c'est assez simple :
```
# mkdir -p /mfs/dev
# cd /mfs/dev
# sh /dev/MAKEDEV all
```
Tu peux d'ores et déjà reprendre ton `/etc/fstab` et le modifier pour mettre `/dev` en RAM. Pour cela, il suffit d'ajouter cette ligne :
```
swap /dev mfs rw,nosuid,noatime,noexec,-P=/mfs/dev,-s=4M,-i=2048 0 0
```
Le `-P` sert à indiquer quel répertoire doit être chargé en RAM au démarrage ; le `-s` sert à indiquer la taille du `mfs` en question ; les autres options sont simplement là pour la déco.
Il s'agit maintenant de passer `/var` en mfs. Là, c'est un peu plus sioux :
```
# rm -Rf /var/{account,admin,audit,crash,games,rwho,www}
# cd /
# tar cpf - ./var |( cd /mfs ; tar xpf - )
tar: Ustar cannot archive a socket ./var/cron/tabs/.sock
tar: Ustar cannot archive a socket ./var/empty/dev/log
# sync
```
On commence donc par supprimer les répertoires inutiles (bien entendu, si tu te sers d'un de ces répertoires, mieux vaut éviter de le supprimer…) et on utilise `tar` pour tout transférer, comme ça on peut s'assurer que les droits d'accès et certains fichiers spéciaux sont bien conservés à l'identique.
Pour `/tmp`, tu peux y aller comme une grosse brute :
```
# rm -Rf /tmp
# ln -s /var/tmp /tmp
```
On va maintenant pouvoir créer le `mfs` correspondant à `/var` dans `/etc/fstab` :
```
swap /var mfs rw,nosuid,noatime,noexec,nodev,-P=/mfs/var,-s=64M 0 0
```
Et paf ! Ça fait des Chocapic ! Reste plus qu'à passer tout le rest en lecture seule, proprement :
```
/dev/wd0a / ffs ro,noatime,softdep 1 1
/dev/wd0d /mfs ffs ro,nodev,nosuid,softdep 1 2
```
L'option `softdep` permet de s'assurer de la cohérence du système de fichiers pendant le peu de temps qu'il sera en lecture/écriture (pendant une partie du boot en fait).
# Oui, mais maintenant, tout ce que je mets dans /var disparaît après un reboot :(
J'arrive, petit con. Une fois que tu as rebooté (en ayant pris la précaution de bien relire ton `/etc/fstab` pour être certain qu'il n'y a pas de connerie de dedans), tu peux voir que tout se passe à merveille :
```
# mount
/dev/wd0a on / type ffs (local, noatime, read-only)
/dev/wd0d on /mfs type ffs (local, noatime, nodev, nosuid, read-only, softdep)
mfs:26119 on /dev type mfs (asynchronous, local, noatime, noexec, nosuid, size=8192 512-blocks)
mfs:1977 on /var type mfs (asynchronous, local, noatime, nodev, noexec, nosuid, size=131072 512-blocks)
```
Tous les systèmes de fichiers sont en lecture seule ou en RAM, tu peux donc bourriner comme un goret sur la prise électrique, tu auras très peu de chance de mettre en danger une partition. Par contre, effectivement les changements dans `/var` ne sont plus persistents. Mais on va pouvoir arranger ça très vite à grand coup de `rsync`.
Je te propose donc le script suivant :
```
#! /bin/sh
mount -uw /mfs
/usr/local/bin/rsync -a --delete --exclude=spool /var/ /mfs/var/
mount -ur /mfs
```
À mettre dans `/usr/local/bin` avec les droits d'exécution en ayant au préalable installer `rsync` via `pkg_add`.
Pour te simplifier la vie, tu peux mettre ce script dans la `crontab` de root et éventuellement dans le `/etc/rc.shutdown` (comme ça en cas de reboot « propre », tout est synchronisé).
Alors, on dit merci qui ?

View File

@@ -0,0 +1,338 @@
+++
title = "Passerelles VPN redondantes avec OpenBSD et BIRD"
date = 2014-08-22
aliases = [ "post/2014/08/22/Passerelles-VPN-redondantes-avec-OpenBSD-et-BIRD"]
[taxonomies]
tags = [ "système", "rézo", "openbsd" ]
+++
BIRD, c'est bon, mangez-en !
<!-- more -->
OpenBSD, c'est le papa de tous les IPSEC (enfin presque) et surtout OpenBSD, c'est truffé de petits outils pour rendre IPSEC plus résistant et résilient. On va voir aujourd'hui comment construire une passerelle VPN avec des morceaux de routage dynamique dedans.
# L'hypothèse de départ
On a un site A avec deux passerelles VPN et un routeur OSPF et un site B avec seulement deux routeurs VPN. Pour cette exemple, on va supposer que chaque site à son propre opérateur (même si pour des raisons de simplicité, ce sera le même réseau pour la maquette) avec suffisamment d'adresses IPv4/v6 publiques (au moins 3 par opérateur).
Basiquement, ça va ressembler à ça :
![vpn_redondant_2_operateurs.png, août 2014](/vpn_redondant_2_operateurs.png)
Je ne détaillerai pas volontairement la configuration de `pf` et `pfsync` (c'est pas le but et vous êtes assez grands pour le faire tout seul, faut pas déconner non plus).
# Configuration réseau
On va donc connement commencer par la configuration réseau. Sur le site A rien de particulier, si ce n'est la configuration de `carp` pour la passerelle en elle-même sur la patte Internet :
```
inet 100.64.0.254 255.255.255.0 NONE advbase 1 advskew 0 carpdev vio0 vhid 20 pass labiteadudule
```
C'est en théorie inutile sur la patte LAN puisqu'OSPF qui va se charger d'annoncer les bonnes routes. En l'occurence, les passerelles étant actives/passives, il va falloir s'arranger pour que la passerelle active annonce la route et que la passerelle passive n'annonce rien du tout.
Côté site B, pas grand chose de plus que cette configuration là, même astuce pour le `carp` :
```
inet 100.64.0.100 255.255.255.0 NONE advbase 1 advskew 0 carpdev vio0 vhid 30 pass lateteatoto
```
Sauf qu'il faut aussi prévoir un `carp` côté LAN pour servir de passerelle aux machines distantes :
```
inet 192.168.1.254 255.255.255.0 NONE advbase 1 advskew 0 carpdev vio1 vhid 40 pass cocolasticot
inet6 alias fddc:a021:7c20:1::254 64
```
L'adresse IPv6 ici est totalement dispensable (`rtadvd` sur OpenBSD permet d'annoncer des routes sur une interface `carp` ou d'annoncer des priorités, donc aucun intérêt) mais ça peut simplifier le débogage.
# Oh oui !! Mets-la moi dans le tunnel !!
Dernier petit point de configuration réseau : on va monter un tunnel `gif` entre les deux paires de routeurs. Ça nous permettra de gérer le routage sans avoir à se prendre la tête avec les tables IPSEC d'OpenBSD, qui peuvent être particulièrement pénibles dans certains cas. Il est à noter également que les fameuses tables en question ne sont pas des tables de routage, parce qu'IPSEC n'est pas un protocole de routage. C'est duraille hein, mais c'est comme ça.
Du coup `gif` et on se prend pas le chou.
Bref, la configuration est exactement symétrique entre le site A et le site B. On a donc en A :
```
tunnel 100.64.0.254 100.64.0.100
inet 169.254.0.0 255.255.255.254 169.254.0.1
inet6 fddc:a021:7c20:ffff::0 128 fddc:a021:7c20:ffff::1
```
Et en B :
```
tunnel 100.64.0.100 100.64.0.254
inet 169.254.0.1 255.255.255.254 169.254.0.0
inet6 fddc:a021:7c20:ffff::1 128 fddc:a021:7c20:ffff::0
```
Subtilité supplémentaire : on ouvre le tunnel entre les adresses publiques des interfaces `carp`. Comme ça, le tunnel n'est pas vraiment ouvert sur le routeur passif (en théorie, en pratique, il peut arriver qu'il tente d'initier le tunnel, mais on a un moyen très simple de l'en empêcher).
Normalement avec cette configuration-là, on a déjà une communication possible au moins dans le tunnel. Il va simplement nous manquer les routes de part et d'autre du tunnel. Ainsi depuis *bvpn-1* :
```
$ ping -c3 -I 169.254.0.1 169.254.0.0
PING 169.254.0.0 (169.254.0.0): 56 data bytes
64 bytes from 169.254.0.0: icmp_seq=0 ttl=255 time=0.646 ms
64 bytes from 169.254.0.0: icmp_seq=1 ttl=255 time=0.698 ms
64 bytes from 169.254.0.0: icmp_seq=2 ttl=255 time=0.635 ms
--- 169.254.0.0 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.635/0.659/0.698/0.040 ms
$ ping6 -c3 -S fddc:a021:7c20:ffff::1 fddc:a021:7c20:ffff::
PING6(56=40+8+8 bytes) fddc:a021:7c20:ffff::1 --fddc:a021:7c20:ffff::
16 bytes from fddc:a021:7c20:ffff::, icmp_seq=0 hlim=64 time=0.685 ms
16 bytes from fddc:a021:7c20:ffff::, icmp_seq=1 hlim=64 time=0.832 ms
16 bytes from fddc:a021:7c20:ffff::, icmp_seq=2 hlim=64 time=0.724 ms
--- fddc:a021:7c20:ffff:: ping6 statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.685/0.747/0.832/0.062 ms
```
# BIRD, le petit zozio sur la bran-cheuh
[BIRD](http://bird.network.cz/), c'est juste le meilleur routeur open source à ce jour. Si, si. Et c'est justement l'occasion d'en parler, parce que c'est vraiment un logiciel qui fait le café, la vaisselle, le ménage, qui te taille une petite pipe et qui se fait oublier après. ~~La femme parfaite en somme~~Le logiciel de routage préféré de Bibi ! Il fonctionne en IPv4 et en IPv6 avec une petite particularité : `bird` gère l'IPv4 et `bird6` l'IPv6.
[BIRD](http://bird.network.cz/) sait parler OSPF, BGP, RIP et probablement deux ou trois autres trucs dont tout le monde se fout. Son principe de fonctionnement est relativement simple : on peut créer autant de routeur virtuel que l'on souhaite par protocole ; chaque de ces routeurs virtuels (`protocols` dans BIRD) va échanger des routes avec une table interne (la *BIRD Internal Routing Table*). On peut décider ce qu'on injecte ou ce qu'on extrait de chaque protocole très facilement, via un (très) puissant système de filtre.
Si on prend un routeur OSPF standard, ça donnera à peu près ça :
![bird_internal_tables.png, août 2014](/bird_internal_tables.png)
Évidemment, chaque route dans la table interne va avoir tout un tas de propriétés associées (ici, je n'ai représenté que le `RTS` *Routing Table Source*, mais il y en a plein d'autres).
On va donc configurer `artr-1` pour qu'il annonce des routes statiques et ses propres interfaces connectées :
```
log syslog { warning, error };
router id 192.168.0.1;
## « direct » correspond aux routes connectées
protocol direct {
interface "vio*";
}
## « kernel » correspond aux routes du
## noyau (routes statiques ajoutées à la main par exemple)
## c'est aussi grâce à lui qu'on va exporter la table BIRD
## vers le système
protocol kernel {
persist; # les routes sont persistentes même si BIRD plante
scan time 20;
export all;
}
# pseudo-protocole permettant de surveiller les interfaces
protocol device {
scan time 10;
}
## pour les routes statiques gérées par BIRD
protocol static {
route 10.0.0.0/8 via "lo0";
route 172.16.0.0/12 via "lo0";
route 192.168.0.0/16 via "lo0";
}
## notre fameux routeur OSPF
protocol ospf {
import all;
export all;
area 0.0.0.0 {
interface "vio0" {
};
};
}
```
Oui, oui, c'est tout :). Le protocole `static` va permettre de créer des routes statiques un peu bidon histoire qu'on ait quelque chose à annoncer en OSPF (par défaut, il est en `import all` donc toutes les routes statiques seront envoyées dans BIRD). Le protocole `kernel` permet d'écrire les routes de la *BIRD Internal Routing Table* vers le système pour « réellement » router.
Pour la partie IPv6, c'est presque tout pareil : on prend le fichier `/etc/bird.conf` et le copie en `/etc/bird6.conf` en changeant simplement les routes statiques. C'est l'utilitaire `birdc` qui permet de communiquer directement avec BIRD, démonstration :
```
$ birdc
BIRD 1.4.0 ready.
birdshow ospf neighbors
ospf1:
Router ID Pri State DTime Interface Router IP
birdshow route
10.0.0.0/8 dev lo0 [static1 10:15:23] * (200)
192.168.0.0/24 dev vio0 [direct1 10:15:23] * (240)
dev vio0 [ospf1 10:15:24] I (150/10) [192.168.0.1]
192.168.0.0/16 dev lo0 [static1 10:15:23] * (200)
172.16.0.0/12 dev lo0 [static1 10:15:23] * (200)
```
Et toutes les routes se retrouvent bien dans le noyau :
```
$ route -n show -inet
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
10/8 127.0.0.1 U1 0 0 33192 56 lo0
127/8 127.0.0.1 UGRS 0 0 33192 8 lo0
127.0.0.1 127.0.0.1 UH 1 0 33192 4 lo0
172.16/12 127.0.0.1 U1 0 0 33192 56 lo0
192.168.0/24 link#1 UC 1 0 - 4 vio0
192.168/16 127.0.0.1 U1 0 0 33192 56 lo0
192.168.0.1 52:54:00:1f:76:10 UHLc 0 4 - 4 lo0
224/4 127.0.0.1 URS 0 0 33192 8 lo0
```
## Configuration OSPF des VPN du site A
Pour les VPN du site A, nous allons avoir un petit souci. Si on annonce toutes les routes connectées (incluant donc le tunnel `gif`), ça ne va pas beaucoup nous aider à router. Il va donc falloir annoncer cela sous forme de route statique. Mais comme on ne peut pas annoncer la même route statique des deux côtés, il va falloir procéder autrement.
Côté site B tout d'abord, on va ajouter des routes statiques à la montée des interfaces `gif`. Pas très compliqué, il suffit de rajouter cela à la fin du fichier `/etc/hostname.gif0` :
```
!route -n add -inet 10/8 169.254.0.0
!route -n add -inet 172.16/12 169.254.0.0
!route -n add -inet 192.168/16 169.254.0.0
!route -n add -inet6 fc00::/7 fddc:a021:7c20:ffff::0
```
ême principe côté site A :
```
!route -n add -inet 192.168.1/24 169.254.0.1
!route -n add -inet6 fddc:a021:7c20:1::/64 fddc:a021:7c20:ffff::1
```
Ainsi au démarrage de l'interface, les routes sont montées automatiquement dans le noyau et il suffit de demander à BIRD de les apprendre. Pour empêcher les routes d'être annoncées sur le routeur de secours côté site A, on va se servir de `ifstated` pour faire monter/descendre les interfaces en fonction du maître. Ci-dessous `/etc/ifstated.conf` :
```
init-state auto ## état d'origine au démarrage de ifstated
## variable prenant true ou false en fonction de l'état de carp0
fw_carp_up = "carp0.link.up"
fw_carp_init = "carp0.link.unknown"
state auto {
if ($fw_carp_init)
run "sleep 10"
if ($fw_carp_up)
set-state fw_master
if (! $fw_carp_up)
set-state fw_slave
}
state fw_master { # si on devient master CARP
init {
run "ifconfig gif0 up"
}
if($fw_carp_init)
run "sleep 2"
if(! $fw_carp_up)
set-state fw_slave
}
state fw_slave { # si on devient slave CARP
init {
run "ifconfig gif0 down"
}
if($fw_carp_init)
run "sleep 2"
if($fw_carp_up)
set-state fw_master
}
```
Avec cette astuce, l'esclave ne peut jamais transmettre la route (puisque l'interface sous-jacente est *down* systématiquement). Passons maintenant à la configuration de BIRD pour les passerelles VPN en question. On va être obligé dans cet exemple d'apprendre les routes « aliens » venant du noyau (puisqu'on ajoute/supprime des routes à la volée via `ifstated`). Il va donc falloir filtrer ces routes sinon, ça va tourner au grand nawak très rapidement.
```
log syslog { warning, error };
router id 192.168.0.251;
protocol kernel {
learn; # on force l'apprentissage des routes, import ne suffit pas
persist;
scan time 20;
import filter { ## un petit filtre des familles
if dest = RTD_UNREACHABLE then reject; # routes non-joignables
# c'est surtout utile en IPv6 où il y a des routes bannies dans OpenBSD par défaut
if net ~ [ 0.0.0.0/0 ] then reject; # passerelle par défaut
accept;
};
export all;
}
protocol device {
scan time 10;
}
protocol ospf {
import all;
export all;
area 0.0.0.0 {
interface "vio1" {
};
};
}
```
Et évidemment pareil en IPv6, mais avec une route par défaut qui a une tronche un peu différente.
Avec tout ce bazar, tout fonctionne très bien à présent, le routage est parfaitement opérationnel. Depuis *artr-1* :
```
$ ping -c3 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes
64 bytes from 192.168.1.1: icmp_seq=0 ttl=253 time=1.631 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=253 time=1.782 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=253 time=1.598 ms
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.598/1.670/1.782/0.086 ms
$ ping6 -c3 fddc:a021:7c20:1::1
PING6(56=40+8+8 bytes) fddc:a021:7c20::1 --fddc:a021:7c20:1::1
16 bytes from fddc:a021:7c20:1::1, icmp_seq=0 hlim=62 time=1.654 ms
16 bytes from fddc:a021:7c20:1::1, icmp_seq=1 hlim=62 time=2.075 ms
16 bytes from fddc:a021:7c20:1::1, icmp_seq=2 hlim=62 time=1.825 ms
--- fddc:a021:7c20:1::1 ping6 statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.654/1.851/2.075/0.173 ms
```
On peut même tester que les routes montent/descendent correctement sur *avpn-1/2* en jouant un peu avec `carpdemote`.
# Bon ben du coup, on chiffre ou bien
Ça vient jeune puceau, ça vient. Pour faire de la redondance dans l'IPSEC, il va nous falloir trois éléments :
* `ipsec` activé ;
* `isakmpd` pour génerer les sessions IPSEC ;
* `sasyncd` pour synchroniser les sessions en question entre chaque paire de routeurs.
Pour les deux premier, c'est extrêmement simple, il suffit d'ajouter ça dans `/etc/rc.conf.local` :
```
ipsec=YES
isakmpd_flags="-K -S"
```
Au passage, on peut tout de suite mettre :
```
sasyncd_flags=""
```
Et configurer `sasyncd` :
```
## donne le port d'écoute et l'adresse du copain
listen on 192.168.0.251
peer 192.168.0.252
# l'interface carp à surveiller pour savoir si on est maître ou esclave
interface carp0
# une clé partagée
sharedkey 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```
Ça, c'est la configuration pour ''avpn-1''. Il faut bien évidemment faire un symétrique pour ''avpn-2'' et le même genre de chose (mais avec une autre clé) pour *bvpn-1/2*.
Et finalement, il va falloir configurer la partie IPSEC en elle-même. Là, ça se passe dans `/etc/ipsec.conf` avec une configuration relativement simple (ici pour les deux routeurs du site A, la configuration est symétrique pour B) :
```
## macros de définition des deux sites
avpn="100.64.0.254"
bvpn="100.64.0.100"
## en une seule ligne, le protocole à transporter dans le tunnel et les différents chiffrements pour les phases 1 et 2 d'IPSEC
ike esp transport proto ipencap from $avpn to $bvpn local $avpn peer $bvpn main auth hmac-sha1 enc aes group modp1536 quick auth hmac-sha1 enc aes group modp1536 psk "pipopipo"
ike esp transport proto ipv6 from $avpn to $bvpn local $avpn peer $bvpn main auth hmac-sha1 enc aes group modp1536 quick auth hmac-sha1 enc aes group modp1536 psk "pipopipo"
```
L'astuce consiste ici à encapsuler dans IPSEC les protocoles IP `ipencap` et `ipv6` qui correspondent respectivement à IPv4 dans IPv4 et IPv6 dans IPv4. L'ensemble de ce qui passe dans le tunnel `gif` sera donc chiffré entre les sites A et B.
Une fois que c'est fait, il n'y a plus qu'à redémarrer le tout pour que tout soit pris en compte.
## TADAAAAA !!
Les flux IPSEC devraient normalement s'établir relativement vite entre les deux routeurs maîtres des deux côtés et les flux et les sessions vont assez vite se répliquer vers les routeurs esclaves des deux côtés :
```
$ ipsecctl -sf
flow esp in proto ipv6 from 100.64.0.100 to 100.64.0.254 peer 100.64.0.100 srcid 100.64.0.254/32 dstid 100.64.0.100/32 type use
flow esp out proto ipv6 from 100.64.0.254 to 100.64.0.100 peer 100.64.0.100 srcid 100.64.0.254/32 dstid 100.64.0.100/32 type require
flow esp in proto ipencap from 100.64.0.100 to 100.64.0.254 peer 100.64.0.100 srcid 100.64.0.254/32 dstid 100.64.0.100/32 type use
flow esp out proto ipencap from 100.64.0.254 to 100.64.0.100 peer 100.64.0.100 srcid 100.64.0.254/32 dstid 100.64.0.100/32 type require
```
> Note personnelle : pour une raison que j'ignore, le nombre de sessions actives quand on est en IKE actif/actif est nettemment plus important qu'en IKE actif/passif (avec un routeur qui initie la connexion donc). Si quelqu'un a une explication, je suis preneur.
On peut maintenant rebooter à loisir l'un ou l'autre des routeurs de n'importe quel côté et conserver la connexion et le chiffrement sans aucun souci.
Pour la prochaine fois, on va faire la version plus sophistiquée, en supposant qu'un des deux côtés à deux opérateurs au lieu d'un.

View File

@@ -0,0 +1,68 @@
+++
title = "OpenWRT et dhcp6c : la méthode Barrier Breaker"
date = 2015-02-02
aliases = [ "post/2015/02/02/OpenWRT-et-dhcp6c-:-la-méthode-Barrier-Breaker"]
[taxonomies]
tags = [ "système", "rézo" ]
+++
Parce que les tutos obsolètes, ça commence à bien faire !
<!-- more -->
Ayant eu récemment de très nombreux problèmes avec *pfSense*, j'ai finalement mis le truc au placard et l'ai remplacé par un truc d'homme avec des poils : *OpenWRT*.
# pfSense, atheros et instabilité
Depuis à peu près toutes les versions de pfSense, avec à peu près toutes les cartes *atheros*, j'ai toujours eu des emmerdes. La principale étant le message d'erreur suivant, répété en boucle dans les logs, rendant le Wi-Fi inutilisable :
```
kernel: ath0: stuck beacon; resetting (bmiss count 4)
```
J'ai eu beau essayé toutes les astuces du monde, sacrifié une chèvre au clair de lune dans un cercle de pierre avec trois nains unijambistes enroulés dans du jambon, rien à faire. De ce que j'ai compris, le problème provient de l'intégration du driver *ath* dans FreeBSD. Sur les séries 2.1.x de *pfSense*, impossible de faire tourner correctement une carte *atheros* « ancienne génération » (carte mini-PCI, compatible avec les Alix2).
Sur les PC-Engines APU, impossible de faire tourner les cartes mini-PCI Express avec des *atheros* plus récents.
Le souci, c'est qu'à un moment, j'ai quand même besoin de le faire marcher proprement môa, le Wi-Fi. En prime, les version Linux de *hostapd* permettent de faire tourner plusieurs SSID sur la même carte, ce que ne permettent pas, à ma connaissance, les versions FreeBSD.
Donc, fuck that et hop ! OpenWRT.
*Ami internaute, si au hasard d'un moteur de recherches quelconque tu tombes sur cet article en essayant de résoudre ton problème avec pfSense, tu es bien baisé.*
*Mais comme je suis d'un naturel généreux, je te donne une petite astuce : force le canal de la carte sur un nombre assez élevé. Ça suffit en général pour réduire considérablement les problèmes avec les cartes Atheros, sans toutefois les supprimer complètement.*
# Bon alors, tu l'envoies la pastille ?
Dans Barrier Breaker, nul besoin de faire des choses compliqué [comme ça](http://doc.rhizome-fai.net/doku.php?id=technique:materiel:ap_interieurs:openwrt_ipv6). C'est pas que ça marche pas, mais ça fait rajouter plein de paquets alors que nativement, OpenWRT est maintenant capable de gérer tout ça.
Comment ?
On commence par créer une interface virtuelle *wan6* dans */etc/config/network* :
```
config interface wan6
option ifname @wan
option proto dhcpv6
```
C'est une simple référence à l'interface existante, permettant de déclencher le *dhcp6c* sur la bonne interface.
Ensuite, sur chaque interface LAN, il suffit d'ajouter la taille du préfixe IPv6 (donc en général 64) ainsi que le sous-réseau concerné (pour un /56, indiquez l'ensemble du tuple entre le /48 et le /64). Par exemple :
```
config interface 'lan'
option ifname 'eth1'
option proto 'static'
option ipaddr '10.0.0.1'
option netmask '255.255.255.0'
option ip6assign '64'
option ip6hint '5e00'
```
Cette interface distribuera donc des IPv6 en : XXXX:XXXX:XXXX:5e00::/64, le préfixe étant récupéré via *dhcp6c*.
Je ne sais pas si ça pourrait fonctionner en indiquant moins d'information (toujours pareil en /56, pourrait ton simplement indiquer les bits entre /56 et /64 ?).
En attendant, cette méthode fonctionne parfaitement et permet de simplifier considérablement la configuration et les mises à jour d'OpenWRT !

View File

@@ -0,0 +1,260 @@
+++
title = "mdadm + LVM + ext4 vers BTRFS sans larme"
date = 2015-09-28
aliases = [ "post/2015/09/28/mdadm-LVM-ext4-vers-BTRFS-sans-larme"]
[taxonomies]
tags = [ "système", "lvm", "btrfs" ]
+++
ais avec un paquet de lubrifiant quand même…
<!-- more -->
Il y a quelques années, j'ai installé mon serveur personnel avec le top du top du *state of the art* de la folie du buzz des Interwebz : RAID logiciel avec `mdadm(8)`, du volume logique comme s'il en pleuvait avec `lvm(8)` et du système de fichiers de bobo en goguette avec `ext4(5)`. Bref, de la pure folie avec de la stabilité à tous les étages, avec de la farfeluterie complètement conformiste partout. Et du fun. Beaucoup de fun.
> Cette opération a été réalisée avec succès sur une Debian Jessie et 3 ArchLinux pas Jessie.
# La tronche de la babasse
> Il s'agit d'une reconstitution, toute ressemblance avec des personnes réelles seraient complètement faite exprès.
Du coup, ça donnait une machine goupillée à peu près comme ça :
```bash
# fdisk -l /dev/sd?
Disque /dev/sda : 20 GiB, 21474836480 octets, 41943040 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Type d'étiquette de disque : dos
Identifiant de disque : 0xc51f3b7e
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 41940991 41938944 20G fd Linux raid autodetect
Disque /dev/sdb : 20 GiB, 21474836480 octets, 41943040 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Type d'étiquette de disque : dos
Identifiant de disque : 0x67c9a5c7
Device Boot Start End Sectors Size Id Type
/dev/sdb1 2048 41940991 41938944 20G fd Linux raid autodetect
```
Donc une seule grosse partition et un RAID en `/dev/md0`. Sur lequel on va construire un magnifique LVM :
```
# lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
home vgroot -wi-ao---- 4,66g
opt vgroot -wi-ao---- 244,00m
root vgroot -wi-ao---- 952,00m
srv vgroot -wi-ao---- 4,66g
swap vgroot -wi-ao---- 952,00m
tmp vgroot -wi-ao---- 952,00m
usr vgroot -wi-ao---- 2,79g
var vgroot -wi-ao---- 2,79g
```
Voilà, ça te donne une petite idée de commet que c'était gaullé.
# À la batmobile Robin !
Quelques petits prérequis avant de commencer :
* tu peux tenter de faire tous tes changement sur les disques déjà présents dans la machine. Juste, si ça merde, t'as vraiment l'air con. Donc, tu vas utiliser un disque externe pour tout faire (ça ne changera pas grand chose au fait que si ça merde, t'auras l'air con, mais disons que tu améliores un peu tes chances).
* Ça marche mais à une condition : `btrfs-convert(8)` transforme les partitions ''ext4'' et partition ''btrfs'' mais seulement si le ''Block Size'' est à 4096 octets sur le système de fichiers d'origine. En dessous, ça marche pas. Si tu tombes sur une partoche comme ça, va falloir procéder autrement (et ça fait bien chier…).
* C'est ce que j'appellerais une migration à « tiède » (parce que c'est pas vraiment à chaud et c'est pas vraiment à froid non plus). Tu peux tout faire sur un SysRescueCD (mais c'est pas recommandé parce qu'il peut y avoir un écart de version ''btrfs'' entre SysRescCD et l'OS de base, qui produit des résultats… curieux…).
Bref, en route pour l'aventure. Première chose, faire les modifs sur les systèmes de fichiers qu'on peut faire à tiède (genre `/home` ou `/srv`) :
```
# umount /home
# btrfs-convert /dev/vgroot/home
creating btrfs metadata.
creating ext2fs image file.
cleaning up system chunk.
conversion complete.
# mount /dev/vgroot/home /home
# mount | grep btrfs
/dev/mapper/vgroot-home on /home type btrfs (rw,relatime,space_cache)
```
Il n'y a plus qu'à modifier le `/etc/fstab` pour refléter les changements. Tu peux faire pareil pour tout le reste (mon conseil personnel, c'est de débrancher le câble réeau et de passer en init 1 pour faire le reste). La seule partition qui reste à boucler, c'est donc `/` qui va exiger par contre de redémarrer sur un boot CD quelconque.
Une fois que tu as tout converti, je ne saurais que trop te conseiller de balancer un coup d'upgrade de `grub` histoire de pas te retrouver dans les choux (à adapter en fonction de la kernaille et des détails autour bien évidemment). Je te passe les détails parce que je pense que tu es un grand garçon maintenant et que tu peux faire ça tout seul, si, si, j'insiste.
# Résultat à l'étape de montagne
Si ça reboote correctement après le coup de SysRescCD, --tu peux aller faire un Loto-- tu devrais obtenir une machine parfaitement fonctionnelle avec du ''btrfs'' à tous les étages, mais toujours dans du LVM et toujours sur du RAID (ce qui, reconnaissons-le, est un peu con-con). T'obtiens un truc à peu près comme ça :
```
# mount | grep btrfs
/dev/mapper/vgroot-root on / type btrfs (ro,relatime,space_cache)
/dev/mapper/vgroot-usr on /usr type btrfs (rw,relatime,space_cache)
/dev/mapper/vgroot-srv on /srv type btrfs (rw,relatime,space_cache)
/dev/mapper/vgroot-tmp on /tmp type btrfs (rw,relatime,space_cache)
/dev/mapper/vgroot-home on /home type btrfs (rw,relatime,space_cache)
/dev/mapper/vgroot-var on /var type btrfs (rw,relatime,space_cache)
```
Du coup, on va transférer tout ça sur un autre disque. Là encore, je te recommande fortement de débrancher la machine parce que malheureusement, l'opération n'est pas complètement chaude (elle est tiède, humide et un peu poisseuse…).
Première étape : créer un snapshot en lecture seule de l'ensemble des partitions en questions. Ça ressemble vaguement à ça :
```
# btrfs subvolume snapshot -r /usr /usr/BACKUP
Create a readonly snapshot of '/usr' in '/usr/BACKUP'
```
Une fois, que c'est fait, il suffit de créer un gros volume ''btrfs'' sur un autre disque dur (ici `/dev/sdc`) :
```
# mkfs.btrfs -L ROOT /dev/sdc
Btrfs v3.17
See http://btrfs.wiki.kernel.org for more information.
Turning ON incompat feature 'extref': increased hardlink limit per file to 65536
fs created label ROOT on /dev/sdc
nodesize 16384 leafsize 16384 sectorsize 4096 size 20.00GiB
# mount LABEL=ROOT /mnt
```
Il ne reste maintenant qu'à faire du ''send/receive'' entre les disques du serveur et le disque externe :
```
# btrfs send /BACKUP | btrfs receive /mnt/
At subvol /BACKUP
At subvol BACKUP
# mv /mnt/BACKUP /mnt/__ro_root/
```
Et ainsi de suite pour toutes les autres partitions !
# Tu snap, tu snap, tu snap, tu snap à droite, tu snap et t'y es
Et à partir de ce moment, ça va être la fête du slip au snapshot. Tu dois donc avoir un truc de ce genre :
```
# btrfs sub list -a /mnt
ID 257 gen 10 top level 5 path __ro_root
ID 259 gen 17 top level 5 path __ro_usr
ID 260 gen 17 top level 5 path __ro_var
ID 261 gen 20 top level 5 path __ro_home
ID 262 gen 23 top level 5 path __ro_srv
```
Donc on va snapper comme des connards :
```
# cd /mnt
# btrfs sub snap __ro_root __root
Create a snapshot of '__ro_root' in './__root'
# rmdir __root/usr/
# btrfs sub snap __ro_usr __root/usr
Create a snapshot of '__ro_usr' in '__root/usr'
# btrfs sub snap __ro_var __root/var
Create a snapshot of '__ro_var' in '__root/var'
```
Pour `/home` et `/srv`, on va procéder un peu différemment, en faisant un snapshot à la racine du volume ''btrfs'' :
```
# btrfs sub snap __ro_home __home
Create a snapshot of '__ro_home' in './__home'
# btrfs sub snap __ro_srv __srv
Create a snapshot of '__ro_srv' in './__srv'
```
aintenant, nous allons maintenant nous arranger pour que `__root` soit le volume par défaut et modifier le `/etc/fstab` dans ce sous-volume :
```
# btrfs sub set-default 263 /mnt
# umount /mnt
# mount LABEL=ROOT /mnt
# mount -o subvol=__home LABEL=ROOT /mnt/home
# mount -o subvol=__srv LABEL=ROOT /mnt/srv
```
aintenant, tu peux chrooter dans `/mnt` (après avoir monté en bind les `/dev`, `/sys`, `/run` et `/proc`) et modifier le `/etc/fstab` :
```
# <file system> <mount point> <type> <options> <dump> <pass>
LABEL=ROOT / btrfs defaults 0 1
LABEL=ROOT /home btrfs defaults,subvol=__home 0 2
LABEL=ROOT /srv btrfs defaults,subvol=__srv 0 2
tmpfs /tmp tmpfs nodev,nosuid,size=256M 0 0
/dev/sr0 /media/cdrom0 udf,iso9660 user,noauto 0 0
```
Deux remarques avant de continuer : plus de `/tmp`, tu peux très bien te faire un petit sous-volume pour le gérer, mais franchement autant le mettre en RAM si ta machine le permet ; plus de swap non plus : il n'y a plus de partition sur le disque, on ne peut donc pas dédier une partition de swap. Les `swapfiles` ne sont pas gérés par ''btrfs'' pour le moment. Personnellement, je trouve qu'on s'en passe bien, à condition d'avoir suffisamment de RAM pour tenir la route bien entendu…
Une fois que tu as fait tout ça, tu peux remettre un coup de `grub-mkconfig` et `grub-install`, croiser les doigts et rebooter directement sur le disque externe. À partir de maintenant, la température de l'opération devrait remonter pas mal (plus besoin de reboot, plus besoin de limiter les lectures/écritures).
# C'est le mimi, c'est le rara, c'est le miracle !
La magie opère immédiatement :
```
# btrfs sub list -a /
ID 257 gen 27 top level 5 path <FS_TREE>/__ro_root
ID 259 gen 30 top level 5 path <FS_TREE>/__ro_usr
ID 260 gen 31 top level 5 path <FS_TREE>/__ro_var
ID 261 gen 33 top level 5 path <FS_TREE>/__ro_home
ID 262 gen 34 top level 5 path <FS_TREE>/__ro_srv
ID 263 gen 43 top level 5 path <FS_TREE>/__root
ID 266 gen 42 top level 263 path __root/usr
ID 267 gen 42 top level 263 path __root/var
ID 268 gen 42 top level 5 path <FS_TREE>/__home
ID 269 gen 42 top level 5 path <FS_TREE>/__srv
```
Il s'agit maintenant de tout rebasculer sur les disques nominaux. On va donc casser les petites fesses de LVM :
```
# vgchange -an
0 logical volume(s) in volume group "vgroot" now active
```
Et fister `mdadm` :
```
# mdadm --stop /dev/md0
mdadm: stopped /dev/md0
```
On va maintenant convertir à notre nouvelle religion les anciens disques (et en profiter pour remettre de la redondance) :
```
# btrfs device add -f /dev/sda / ## le -f sert à indiquer que oui, on va bien lui casser les fesses (d'où la lettre)
# btrfs device add -f /dev/sdb /
# btrfs balance start -dconvert=raid1 -mconvert=raid1 /
```
Après de longues minutes d'attente (tu peux jouer les voyeurs avec `btrfs balance status /`), tu vas obtenir un magnifique RAID1 avec 3 disques :
```
# btrfs fi df /
Data, RAID1: total=2.00GiB, used=895.21MiB
System, RAID1: total=32.00MiB, used=16.00KiB
etadata, RAID1: total=1.00GiB, used=55.45MiB
GlobalReserve, single: total=32.00MiB, used=0.00B
# btrfs fi sh /
Label: 'ROOT' uuid: 4cc2f675-d607-4674-8522-92de9065eff5
Total devices 3 FS bytes used 950.68MiB
devid 1 size 20.00GiB used 1.03GiB path /dev/sdc
devid 2 size 20.00GiB used 2.03GiB path /dev/sda
devid 3 size 20.00GiB used 3.00GiB path /dev/sdb
Btrfs v3.17
```
Et il suffira d'en retirer un :
```
# btrfs device del /dev/sdc /
```
Et voilà, la magie a opéré ! Même niveau de sécurité, mais plein de fonctionnalités en prime, et surtout, la classe ''btrfs'' à portée de groin.
Banzaï !

View File

@@ -0,0 +1,59 @@
+++
title = "Microsoft, je te chie dans la bouche"
date = 2016-02-15
aliases = [ "post/2016/02/15/Microsoft,-je-te-chie-dans-la-bouche"]
[taxonomies]
tags = [ "microsoft", "mail" ]
+++
Tu es le SIDA de l'Internet et j'ai envie de t'enculer le cerveau…
<!-- more -->
Cela fait plusieurs semaines que je suis incapable d'envoyer des messages à hotmail.com/hotmail.fr/live.com/outlook.fr/etc… Impossible pourquoi ? Jusqu'à présent cela ne posait pas de problème.
Premier sympôme : les SMTP de Microsoft me répondent qu'ils ne me connaissent pas et ne veulent donc pas me parler. Au moins, ça a le mérite d'être clair. Je m'adresse donc à mon fournisseur d'adresse IP (non, ce n'est en loccurrence pas mon fournisseur d'accès) pour lui demander de me whitelister (ou au moins de me faire connaître).
On m'indique alors un formulaire qui nécessite d'avoir un compte Outlook pour être rempli. On me dit au passage que Microsoft ne parle plus aux adresses IP qu'il ne connait pas… C'est là que je commence à me dire qu'il y a quelque chose qui ne tourne pas très rond chez eux (des adresses IP qu'ils ne connaissent pas, il y en a environ 4 milliards et je parle même pas d'IPv6, c'est beaucoup trop high-tech pour eux).
Bref, comme je suis très con, je m'exécute (et je crée le compte Outlook en question).
Je reçois alors un message comme quoi la confiance, ça prend du temps, que je suis bien gentil mais qu'il va falloir patienter. Je serais très curieux de savoir comme ça se passerait si de plus gros ESP comme Google répondait ça à Microsoft.
# Quelques semaines plus tard…
algré tous mes efforts, toujours impossible d'envoyer des messages. Alors certes, le SMTP ne me rejette plus brutalement, ce qui est déjà pas mal, mais mes messages atterrissent systématiquement en SPAM. Dans ce genre de cas, on commence par balayer devant sa porte et s'assurer que tout est parfaitement en ordre dans ces enregistrements SPF/DKIM/DMARC (tiens, un petit nouveau !).
Du coup, SPF -> check.
DKIM -> check.
DMARC -> check.
Que se passe putain de t-il donc de bordel de marde ?
Je reviens alors sur l'ensemble des rapports DMARC qui sont en ma possession pour voir ce qui se passe vraiment (c'est peut-être le seul truc positif dans DMARC, on peut vraiment savoir comment ça se passe)
icrosoft me dit alors : SPF -> temperror.
Comment ça ''temperror'' ? Sous prétexte que tu fais une erreur temporaire, tu rejettes mes mails ? C'est bien intelligent ça aussi. Mais je ne peux me satisfaire de cette réponse. Surtout quand elle dure depuis plusieurs semaines. En furetant un peu, je me rends compte que je ne suis pas le seul à essuyer les foudres de l'Ogre Microsoft concernant son système de messagerie. Plusieurs autres usagers du mail semblent ennuyer eux aussi et se posent à peu près les mêmes questions que moi : pourquoi mes messages sont rejetés ? Pourquoi faut-il tout valider des dizaines de fois ? Pourquoi faut-il un compte Outlook pour faire ça ? Pourquoi est-ce que je préfèrerais avaler une soupe aux prépus à la crême de cancrelas plutôt que d'avoir à faire avec des enculés de première et cons comme des chaises en prime ?
Je ne sais répondre qu'à la dernière question…
# Je te laisse une dernière chance, piéton !
Je finis donc par tomber sur deux choses. La première, il faut avoir du Sender-ID et non du SPF pour que Microsoft daigne prêter attention aux messages qu'on lui envoie. Je rappelle, à toutes fins utiles, que Sender-ID est le rejeton avorté et propriétaire de Microsoft pour remplacer le standard ouvert SPF. Tout cela est donc extrêmement intelligent (mais arrivé à ce point, ça semble presque logique en fait).
ais bon, soit, on peut toujours tenter quelque chose après tout, un enregistrement TXT ne coûte rien. Sauf que le générateur d'enregistrement Sender-ID de Microsoft renvoit une magnifique erreur 500 quand on essaie de s'adresser à lui. Même en touchant le fond, ils continuent de creuser, c'est impressionnant. Vous pouvez d'ailleurs admirer là, un magnifique exemple de perte de sens :
* tu fais la promotion d'un standard non-ouvert sur Internet
* quand tu vois que ça ne prend pas, tu forces les autres à l'utiliser en profitant de la position que tu occupes dans le domaine du standard en question
* plutôt que d'expliquer aux gens comment faire et leur donner les outils pour l'adopter, tu t'en branles…
\*clap clap clap\* toutes mes félicitations à Microsoft pour cette magnifique démonstration de non-sens.\
Après quelques pérégrinations supplémentaires, je finis par trouver la syntaxe de merde de ce protocole de merde pour discuter avec ce service de merde (oui, j'emploie beaucoup de gros mots, mais je suis vraiment colère…). Et… ça ne marche toujours pas.
Là, pour être très honnête, je me suis dit que j'aurais probablement plus vite fait d'aller directement à Redmond et faire un carton façon Columbine (tiens, n'est-ce pas étonnant d'ailleurs ? Pourquoi les massacres de ce type ont toujours lieu sur des campus et pas dans des grandes sociétés bien établies ?).
J'apprends alors que le système de vérification de Microsoft ne vérifie pas « en temps réel » les enregistrements SPF, mais les met en cache pour 12h (plus exactement les rafraîchit 2 fois par jour).
Purement par charité et amour pour mon prochain, je le dis donc : Microsoft, je te chie dessus et je te pète dans la bouche. Si tu ne veux pas accepter mes messages et bien tant pis, ne les accepte pas, je parlerai à des gens plus intelligents. Faut pas parler aux cons, ça les éduque, c'est bien connu.
> En tant que directeur de la publication de ce blog, je m'approuve moi-même dans cette démarche et j'assume l'entière responsabilité de ce qui a été écrit. Microsoft, si tu peux me lire, j'espère que tu crèveras en te noyant dans ton propre vomi.

View File

@@ -0,0 +1,343 @@
+++
title = "Z-Push, IMAP, sexe et NextCloud"
date = 2016-12-26
aliases = [ "post/2016/12/26/Z-Push,-IMAP,-sexe-et-NextCloud"]
[taxonomies]
tags = [ "mail", "système", "nextcloud" ]
+++
Peut-être pas de sexe finalement, vous n'avez pas été très sage cette année…
Plus sérieusement, je n'ai vu que très peu de ressources en français sur ce sujet, alors que je pense que c'est plutôt utile et rigolo à faire.
<!-- more -->
Tu auto-héberges tes messages chez toi. C'est bien. Tu auto-héberges aussi ton calendrier et ton carnet d'adresses via NextCloud. C'est bien. Et puis, c'est drôlement pratique, tu ne divulgues ainsi pas ta liste d'amis aux grands méchants de l'Internet. Tout cela est très zoli, très pratique et tout sauf que dès que tu veux configurer un ordiphone pour aller avec tout ça, et bien, ça devient nettement plus compliqué :
* sur un PommePhone, il faut entrer tes informations IMAP/SMTP puis ton login/pass ; ensuite, il faut entrer l'adresse du calendrier, puis ton login/pass ; enfin, il faut entrer l'adresse de ton carnet d'adresses puis ton login/pass.
* sur un RobotChelouPhone, il faut entrer tes informations IMAP/SMTP puis ton login/pass ; ensuite, il faut télécharger DavDroid, puis entrer l'URL de ton instance ownCloud/NextCloud puis entrer ton login/pass.
Alors, soyons bien d'accord : une fois que c'est fait, ça marche bien (même très bien) et c'est extrêmement pratique. Je n'ai personnellement jamais eu besoin d'y retoucher.
ais dès que l'on change d'appareil (ce qui arrive plus souvent qu'on ne le voudrait), il faut retaper toute la procédure ; en matière de *Single Sign-On*, on a vu mieux et enfin, Androïd n'est pas compatible nativement avec CardDav/CalDav, ce qui force à passer par un outil tiers (concernant DavDroid, il marche très très bien, je suis d'ailleurs content d'avoir fait un don au développeur).
On va donc essayer de réunir tout ça derrière un seul protocole : ActiveSync. Alors oui, ActiveSync est propriétaire, c'est moche toussa, mais au moins il marche très bien, il est compatible nativement avec pratiquement tous les ordiphones (Pomme, RobotChelou, je ne suis pas sûr pour RenardDeFeu et GentillesseHumaine) et si tu héberges quelqu'un d'autre chez toi (au hasard, ta femme), ce sera plus simple pour lui/elle de configurer un compte qui redescend ensuite toutes ses informations.
Sans compter le fait qu'on va avoir droit à deux ou trois bonus en prime.
# Authentification de NextCloud et IMAP sur la même base
Pour que tout cela puisse fonctionner efficacement, il va falloir avoir le même login/pass pour l'ensemble des applications concernées. C'est ce que l'on appelle du CSO, *Common Sign-On*.
Le plus simple dans la plupart des cas est de calquer l'authentification de l'ensemble des applis sur l'authentification IMAP. Il y a de très nombreux connecteurs permettant de faire cela sur un nombre considérable d'appli ([TinyTiny-RSS|https://tt-rss.org/|en|TT-RSS] par exemple) et au pire, il n'est pas très compliqué d'écrire son propre module d'authentification.
Personnellement, mon serveur de messagerie authentifie directement mes utilisateurs système, ownCloud, remplacé par NextCloud, s'authentifie directement sur le système aussi via [user_pwauth](https://apps.owncloud.com/content/show.php?content=148406).
J'ai maintenant le même login/pass sur NextCloud/ownCloud et sur mes serveurs IMAP et SMTP.
# Présentation et installation de Z-Push
[Z-Push](http://z-push.org/) est une application écrite intégralement en PHP permettant de faire un serveur ActiveSync à partir d'autres composants (*backends*). Pour l'installation, il suffit de suivre les instructions disponibles sur [cette page](https://wiki.z-hub.io/display/ZP/Installation). En gros, installer le nouveau dépôt, valider la clé GPG et c'est parti mon kiki !
Je vous recommande d'installer les paquets suivants :
* `z-push-backend-caldav`, `z-push-backend-carddav` et `z-push-backend-imap` : parce que sinon, ça va moyennement marcher ;
* `z-push-autodiscover` : permet de faire de l'auto-découverte de paramètres (et donc évite au toto-user de mal rentrer lui-même les paramètres) ;
* `z-push-state-sql`: permet de stocker les états ActiveSync dans une base de données plutôt que dans des fichiers plats ;
* `z-push-ipc-sharedmemory` : pour les grosses instances (quelques dizaines d'utilisateurs ?), cela va accélérer l'exécution du code.
Une fois que tout ce bazar, et ses dépendances, sont installés, il est nécessaire de changer les autorisations pour deux répertoires : `/var/lib/z-push` et `/var/log/z-push`. Il faut qu'ils appartiennent à l'utilisateur et au groupe faisant tourner le serveur Web (`www-data` pour Debian, `httpd` pour CentOS).
# Configuration
## NginX
Je ne vais pas tout expliquer en détail, mais voilà en gros, la configuration à appliquer pour que le truc fonctionne. C'est un peu la toutouille de cuisine après avoir subi un nombre conséquent d'essai/erreur.
```
server {
listen *:443 ssl;
listen [::]:443 ssl;
server_name z.mab.it autodiscover.mab.it; # le premier est l'URL de Z-Push, le second sert pour la découverte automatique
ssl_certificate /etc/letsencrypt/live/z.mab.it/fullchain.pem; ## vous savez vous servir de LetsEncrypt ? Et bien servez-vous en !
ssl_certificate_key /etc/letsencrypt/live/z.mab.it/privkey.pem;
root /usr/share/z-push;
index index.php;
client_max_body_size 4M;
client_body_buffer_size 128k;
location / { ## un grand classique en NginX pour tout rediriger vers index.php
try_files $uri $uri/ index.php;
}
location /Microsoft-Server-ActiveSync { ## c'est l'URL la plus important : le mobile va taper dessus pour toutes les opérations
rewrite ^(.*)$ /index.php last;
}
location ~* ^/autodiscover/autodiscover\.xml$ { ## cela permet de rediriger l'autodiscover (quelque soit la casse) vers le script PHP qui va bien)
rewrite ^(.*)$ /autodiscover/autodiscover.php last;
}
location ~* \.php$ { ## tout fichier PHP est ensuite interprété\
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
}
```
Normalement, avec ça, NginX devrait être capable de prendre en considération toutes les étapes du processus : auto-découverte, demande d'informations, etc… Si tu as la (mal)chance d'avoir de l'Apache, tu dois pouvoir arriver à faire un truc à peu près similaire sans trop de problème (mais je te laisse te démerder, je suis allergique à Apache).
## PHP-FPM
Ça, c'est la petite surprise du chef, qui fait plaisir aux papilles et qui réjouit les estomacs. Voilà une configuration qu'on ne trouve nul part et qui pourtant est indispensable : il faut ajouter la librairie `libawl-php`, installée dans les dépendances plus haut, dans le PATH d'exécution de PHP-FPM. Le PATH par défaut est `.:/usr/share/php`, il faut donc y ajouter `/usr/share/awl/inc` sous Debian (à adapter à votre distrib) dans `/etc/php5/fpm/php.ini` :
```
include_path = ".:/usr/share/php:/usr/share/awl/inc"
```
Au lieu de cela, tu pourrais créer un pool spécifique dans PHP-FPM contenant le bon PATH. Je n'en avais pas vraiment l'utilité, donc j'ai préféré procéder comme ça, mais le choix reste le tien.
## Z-Push
Normalement, l'installation a dû créer une chiée de fichiers de configuration dans `/etc/z-push`. Chaque fichier permet de configurer un morceau du logiciel.
# MySQL/MariaDB
Cet étape est facultative : tu peux très bien laisser le comportement par défaut qui consiste à créer les fichiers d'état de Z-Push dans `/var/lib/z-push`. Sinon, tu peux éditer le fichier `/etc/z-push/state-sql.conf.php` pour y entrer tes paramètres SQL : nom du serveur, utilisateur, mot de passe, base qu'il faudra créer au préalable. Il faut ensuite éditer le fichier `/etc/z-push/z-push.conf.php` pour l'informer d'où stocker les états :
```
/**********************************************************************************
* StateMachine setting
*
* These StateMachines can be used:
* FILE - FileStateMachine (default). Needs STATE_DIR set as well.
* SQL - SqlStateMachine has own configuration file. STATE_DIR is ignored.
* State migration script is available, more informations: https://wiki.z-hub.io/x/xIAa
*/
define('STATE_MACHINE', 'SQL');
define('STATE_DIR', '/var/lib/z-push/');
```
# Auto-découverte
On va modifier le fichier `/etc/z-push/autodiscover.conf.php` pour lui indiquer les paramètres suivants :
```
[…]
define('SERVERURL', 'https://z.mab.it/Microsoft-Server-ActiveSync'); ## permet de spécifier l'URL de push au terminal
[…]
define('USE_FULLEMAIL_FOR_LOGIN', false); ## dans mon cas, je n'utilise que la partie de gauche de mon courriel pour m'authentifier. Cela peut être différent si ton serveur IMAP ne fonctionne pas comme ça
[…]
define('BACKEND_PROVIDER', 'BackendCombined'); ## logiquement, ça devrait être détecté automatiquement, mais on est jamais trop prudent
```
# IMAP
Dans le fichier `/etc/z-push/imap.conf.php`, nous allons indiquer les paramètres IMAP. Cela va essentiellement consister à renseigner les paramètres de la boîte et notamment les dossiers d'envoi, de brouillons, etc…
```
## Donne les paramètres IMAP de base
define('IMAP_SERVER', 'localhost'); ## ou autre chose si c'est sur un autre serveur
define('IMAP_PORT', 143);
## Ce paramètre est super important : il donne la chaîne d'ouverture de la boîte par la librairie PHP-IMAP
## En gros, il faut obligatoirement du TLS, sans vérifier le certificat (inutile pour ce cas)
define('IMAP_OPTIONS', '/tls/novalidate-cert/norsh');
## Si tu ne mets pas ce paramètre à True, rien ne marche :)
define('IMAP_FOLDER_CONFIGURED', true);
```
Pour le reste, j'ai juste changé le répertoire des pourriels en `JUNK` et le répertoire des archives en `ARCHIVES`. Ce sont les paramètres par défaut que j'ai pour Dovecot, cela permet simplement de les calquer. Adapte à tes propres besoins (au pire, pour tester, ça va fonctionner correctement).
*Si tu te poses la question, à juste titre, de la configuration SMTP, il n'y en a pas. Z-Push utilise par défaut les fonctions d'envoi de message de PHP. Bien évidemment, c'est configurable :).*
# CalDav
Là, on arrive sur la partie un peu *touchy*. Il s'agit de configurer le fournisseur CalDav (NextCloud dans mon cas, ce peut être ownCloud évidemment ou tout autre serveur CalDav). L'idée est de fournir dans un premier temps le chemin vers la collection de calendriers, puis vers un calendrier par défaut. Sur tous les appareils où j'ai essayé, tous les calendriers étaient toujours détectés correctement, mais il se peut que tu ne voies que le calendrier par défaut.
Dans le fichier `/etc/z-push/caldav.conf.php` donc :
```
## Indique le nom, le protocole et le port pour le serveur CalDav
define('CALDAV_PROTOCOL', 'https');
define('CALDAV_SERVER', 'nextcloud.mab.it');
define('CALDAV_PORT', '443');
## L'URL de la collection. Le %u remplace le nom d'utilisateur. Pour ownCloud/NextCloud :
define('CALDAV_PATH', '/remote.php/dav/calendars/%u/');
## Le calendrier par défaut
define('CALDAV_PERSONAL', 'default');
## Apparemment, ownCloud/NextCloud ne supporte pas cette option (synchronisation de masse)
define('CALDAV_SUPPORTS_SYNC', false);
```
*Si tu as un NextCloud installé récemment, ton calendrier principal ne s'appellera `default` mais `calendar`. C'est l'héritage de ownCloud 6 que je trimballe…*
# CardDav
ême principe que pour CalDav : une collection, un carnet par défaut. Comme pour CalDav, il faut indiquer les bonnes URL aux bons endroits, avec néanmoins quelques variantes. Démonstration sur `/etc/z-push/carddav.conf.php` :
```
## Même principe que plus haut
define('CARDDAV_PROTOCOL', 'https');
define('CARDDAV_SERVER', 'nextcloud.mab.it');
define('CARDDAV_PORT', '443');
## le chemin vers la collection de carnets d'adresses. Attention / à la fin obligatoire !
define('CARDDAV_PATH', '/remote.php/dav/addressbooks/users/%u/');
## le carnet par défaut, / à la fin toujours obligatoire !
define('CARDDAV_DEFAULT_PATH', '/remote.php/dav/addressbooks/users/%u/default/');
## il faut commenter la GAL (Global Address List) : c'est une option Outlook qui n'existe évidemment pas avec CardDav
// define('CARDDAV_GAL_PATH', '/caldav.php/%d/GAL/');
## Pareil, non supporté
define('CARDDAV_SUPPORTS_SYNC', false);
## Si tu ne veux pas que ça merde, laisse cette option vide !! Ça force la recherche des fichiers VCF de la collection de carnet d'adresses en .vcf. Ce n'est pas le cas avec NextCloud
define('CARDDAV_URL_VCARD_EXTENSION', *);
```
Tu peux laisser les autres options par défaut.
*Si tu as un NextCloud installé récemment, ton carnet d'adresses principal ne s'appellera `default` mais `contacts`. C'est toujours l'héritage de ownCloud 6 que je trimballe…*
# Dernières configurations et test de l'ensemble
C'est là qu'on bascule du très scientifique au mystique. On va configurer le `BackendCombined` pour faire correspondre des fonctions ActiveSync à des backends (IMAP, CardDav ou CalDav donc). Ça se passe dans le fichier `/etc/z-push/combined.conf.php` :
```
class BackendCombinedConfig {
public static function GetBackendCombinedConfig() {
return array(
## Le nom des différents Backend et une lettre leur est associée
'backends' => array(
'i' => array(
'name' => 'BackendIMAP',
),
'd' => array(
'name' => 'BackendCardDAV',
),
'c' => array(
'name' => 'BackendCalDAV',
),
),
'delimiter' => '/',
## Et voilà les différentes fonctions ActiveSync
## pour chaque fonction on précise si ça doit aller dans IMAP (i), dans CardDav (c) ou dans CalDav (d)
## ci-dessous, un truc qui marche
'folderbackend' => array(
SYNC_FOLDER_TYPE_INBOX => 'i',
SYNC_FOLDER_TYPE_DRAFTS => 'i',
SYNC_FOLDER_TYPE_WASTEBASKET => 'i',
SYNC_FOLDER_TYPE_SENTMAIL => 'i',
SYNC_FOLDER_TYPE_OUTBOX => 'i',
SYNC_FOLDER_TYPE_TASK => 'c',
SYNC_FOLDER_TYPE_APPOINTMENT => 'c',
SYNC_FOLDER_TYPE_CONTACT => 'd',
SYNC_FOLDER_TYPE_NOTE => 'c',
SYNC_FOLDER_TYPE_JOURNAL => 'c',
SYNC_FOLDER_TYPE_OTHER => 'i',
SYNC_FOLDER_TYPE_USER_MAIL => 'i',
SYNC_FOLDER_TYPE_USER_APPOINTMENT => 'c',
SYNC_FOLDER_TYPE_USER_CONTACT => 'd',
SYNC_FOLDER_TYPE_USER_TASK => 'c',
SYNC_FOLDER_TYPE_USER_JOURNAL => 'c',
SYNC_FOLDER_TYPE_USER_NOTE => 'c',
SYNC_FOLDER_TYPE_UNKNOWN => 'i',
),
'rootcreatefolderbackend' => 'i',
);
}
}
```
La partie purement mystique est terminée. Si tu fais pointer ton butineur vers l'adresse Web de l'application Z-Push (`https://z.mab.it` dans mon cas), cela devrait te demander une authentification. Si tu arrives à t'authentifier avec un utilisateur IMAP, ça devrait te cracher une belle erreur comme quoi tu ne peux pas faire de GET sur cette page. C'est parfaitement normal, tu n'es pas un périphérique ActiveSync (on m'aurait menti ?).
En gros, maintenant, t'as plus qu'à tester avec un périphérique Androïd ou Apple : dans l'application Mail, il suffit de rentrer adresse électronique et mot de passe, l'auto-découverte devrait se charger du reste et commencer à synchroniser carnet(s) d'adresses, calendrier(s) et courriels. Petite astuce pour RobotChelouPhone : tu peux créer un second profil pour faire le test. Ça évitera de tout casser dans le tien et ça coûte pas grand-chose.
Tu peux vérifier les différents appareils connectés avec la commande `z-push-admin` :
```
# z-push-admin -a list
All synchronized devices
Device id Synchronized users
-----------------------------------------------------
androidcaaaaavava gibbon
```
On peut d'ailleurs récupérer tout un tas d'information sur le terminal, ce qui peut être plus ou moins pratique (Big Brother quelqu'un ?) :
```
# z-push-admin -a list -u gibbon
Synchronized devices of user: gibbon
-----------------------------------------------------
DeviceId: androidcaaaaavava
Device type: bqAquarisX
UserAgent: bqAquarisX5/6.0.1-EAS-2.0
Device Model: Aquaris X5
Device IMEI: XXXXXXXXXXXXXXX
Device friendly name: Aquaris X5
Device OS: Android 6.0.1
Device Operator: PourriOp (3333)
ActiveSync version: 14.0
First sync: 2016-12-27 09:04
Last sync: 2016-12-27 09:07
Total folders: 14
Short folder Ids: No
Synchronized folders: 6 (1 in progress)
Synchronized data: Emails(2) Calendars(3) Contacts
Synchronization progress:
Folder: Inbox Sync: Synchronizing Status: 22% (8/36)
Status: OK
WipeRequest on: not set
WipeRequest by: not set
Wiped on: not set
Policy name: default
Attention needed: No errors known
```
# BONUS!!§!
## Placer des paramètres de sécurité par défaut
Le truc pratique avec ActiveSync, c'est que ça permet de régler quelques paramètres par défaut pour le téléphone : on peut forcer le chiffrement du stockage, forcer un mot de passe de déverrouillage plus ou moins complexe, rendre impossible l'installation d'un second compte de messagerie, interdire l'utilisation de l'appareil photo, d'une carte SD, etc… Ça offre pas mal de possibilité et ça permet de s'assurer que l'appareil est un minimum sécurisé en tout temps (et sans rien rajouter d'autres).
Tout cela se passe dans le fichier `/etc/z-push/policies.ini`. La politique par défaut est assez laxiste, mais on peut assez facilement la renforcer un peu. Voici les quelques options que je vous conseille :
```
; mot de passe requis
devpwenabled = 1
; pas obligatoirement alphanumérique
alphanumpwreq = 0
; longueur minimum du mot de passe
mindevpwlenngth = 4
; chiffrement du terminal requis
reqdevenc = 1
```
Tu peux évidemment jouer avec les autres options : temps minimal avant que le terminal ne se verrouille, nombre d'essais de mot de passe avant de briquer le terminal, etc… Ce que je te propose ici est ce que je considère comme le minimum.
## Effacer un terminal à distance
On peut complètement effacer un terminal à distance grâce à ActiveSync. Attention, c'est hyper sérieux, le truc rigole pas du tout : __ÇA EFFACE VRAIMENT TOUT !!§!__. En plus de pouvoir faire une bonne blague pour le 1er avril, ça peut toujours être utile si le terminal est perdu ou volé : on s'assure que rien dessus ne sera retrouvé.
```
# z-push-admin -a wipe -d androidcaaaaavava
Are you sure you want to REMOTE WIPE device 'androidcaaaaavava' [y/N]:
```
On peut également effacer tous les terminaux liés à un utilisateur en particulier. Bref, c'est un joli petit bonus, je trouve, bien sympathique à l'utilisation.
# Conclusion
ActiveSync, c'est mal parce que c'est propriétaire. Malheureusement, pour le moment, c'est la seule solution fiable pour contrôler 99% des terminaux mobiles disponibles sur le marché. Alors, oui, c'est moche, mais en attendant une alternative viable, autant essayer d'en tirer le meilleur.
Et comme disent tous les connards : à l'année prochaine !
Addendum :
Si tu as une configuration de messagerie un peu chelou (genre ton `$myorigin` dans Postfix ne correspond pas à ton domaine de messagerie), n'oublie pas de mettre ce paramètre dans la configuration :
```
define('IMAP_DEFAULTFROM', '@mondomainedemessagerie.com');
```
Ça permet de s'assurer que le `Return-Path` et le `From` de l'email envoyé est correct.

View File

@@ -0,0 +1,30 @@
+++
title = "uBlock Origin : ne plus filtrer Piwik"
date = 2017-01-04
aliases = [ "post/2017/01/04/uBlock-Origin-:-ne-plus-filtrer-Piwik"]
[taxonomies]
tags = [ "web", "internet" ]
+++
Parce qu'autant je chie allègrement sur Google Analytics, autant Piwik, c'est le bien (y)
<!-- more -->
J'ai cherché pendant un moment comment faire pour blanc-lister :reverso: Piwik Analytics du bloqueur de publicitiés/malwares/spywares/NSAsucks que j'utilise actuellement, uBlock Origin.
En fait, depuis quelques versions, par défaut uBlock bloque vraiment beaucoup mais alors beaucoup de trucs. Déjà, il bloque Google Analytics par défaut (et ça, c'est bien), mais il bloque aussi par défaut Piwik (ou il est dans les listes que tu as vraiment envie d'activer).
Du coup, j'ai un peu tourné en rond pour comprendre comment je pourrais faire pour le réactiver proprement : j'ai envie que les sites que je visite et qui n'utilise pas Google Analytics puisse savoir que je les visite. J'ai envie de rentrer dans les statistiques des gens du bien. J'aurais bien envie que ça encourage certains sites à louer ou installer leur Piwik pour arrêter de nourrir le monstre Google.
Et en plus, je pense que certains blogueurs, notamment dans la sphère du libre, seraient fortement encouragés s'ils voyaient leurs statistiques monter, ne serait-ce qu'un peu.
Bref, c'est pas bien compliqué en fait, mais ça servira au moins de pense-bête. Dans l'onglet « Mes filtres », ajoute simplement la mention suivante :
```
@@/piwik.js
```
À partir de là, tous les sites que tu visites qui sont liés à un Piwik devrait fonctionner. Tu peux facilement vérifier ça en regardant le journal des requêtes de uBlock.
Voilà, je retourne faire la sieste, ça m'a épuisé ces conneries…

View File

@@ -0,0 +1,95 @@
+++
title = "Ma République est morte"
date = 2018-03-24
aliases = [ "post/2018/03/24/Ma-République-est-morte"]
[taxonomies]
tags = [ "démocratie" ]
+++
Plus la peine de tenter une réanimation, c'est juste fini…
<!-- more -->
La démocratie est déjà morte l'année dernière : quand 20% de la population décide pour 100% des gens, il y a un problème.
ais aujourd'hui, c'est bien plus grave : la République vient juste de mourir.
# Des étudiants tabassés
<iframe width="560" height="315" src="https://www.youtube.com/embed/SUFolph97Lo" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
Non, nous ne sommes pas en Italie dans les années 30. Nous ne sommes pas en Turquie. Nous ne sommes pas en Chine, ni en Corée du Nord. Nous sommes bien en France, vous êtes bien en 2018.
Des gens qui se font tabassés, il y en a tous les jours. Des cagoulés qui s'en prennent à des étudiants, c'est moins fréquent mais ça arrive probablement aussi…
C'est déjà assez dérangeant et perturbant en soi. Mais là, ça va beaucoup plus loin. Des étudiants se font tabasser dans leur propre université.
Par des gens cagoulés.
Par des gens armés.
Sous les yeux des vigiles.
Avec la complicité de l'administration.
La Police refuse d'intervenir.
Les médias s'en foutent.
Notre République vient juste de mourir. Sous nos yeux.
# La complicité de l'administration
C'est probablement ce qu'il y a de plus insoutenable et de plus insupportable. Dix individus cagoulés qui ont été introduits par l'administration, par le Doyen, pour aller casser la gueule à des étudiants qui n'avaient rien demandé.
Des étudiants trahis par leur administration. La même dont le rôle est quand même de les éduquer et des les protéger. Les voilà bien éduqués : ferme ta gueule ou on t'envoie nos skin-heads ; file droit ou on t'envoie à l'hosto.
Ce n'est même plus de l'indignation. C'est du dégoût. Et le Doyen de rajouter qu'en substance, il s'en tape. Pour lui tout va bien.
# Les médias s'en foutent
Tout le monde a été occupé avec d'autres choses aujourd'hui. Apparemment, des étudiants qui se font éclater la tronche avec des morceaux de bois par des individus cagoulés au sein d'une université avec la complicité, ça ne mérite pas un entre-filet dans le 20h.
Et pire, ce n'est même pas un fait divers. Un Doyen universitaire qui utilise des méthodes fascistes pour mater ses étudiants et nos chers représentants des médias trouvent que ça ne mérite pas la une. Que ça ne mérite même pas d'être montré ou reporté. Nan, on va aller demander au Doyen complice ce qu'il en pense. Ça, c'est la bonne chose à faire.
# Voilà, notre République est morte
La Police ne protège pas la population, elle la terrorise.
Les méthodes fascistes se multiplient.
Les médias ne couvrent que ce qui fait l'intérêt des puissants et plus ce qui est important.
On laisse des étudiants se faire tabasser. Pire, on prémédite le passage à tabac. Et on aide les fachos à taper sur les victimes.
# Il n'y aura pas de justice
Pendant que des innocents sont à l'hôpital, les mecs encagoulés n'ont pas été arrêtés.
Ils ne le seront pas. Jamais personne n'ira les chercher.
Le Doyen ne sera pas inquiété. On arrivera jamais à prouver qu'il a aidé les encagoulés.
Il n'y aura pas de justice. Les méchants ne seront jamais punis. Les coupables ne feront jamais face à leurs responsabilités. Il ne se passera rien.
# Ça commence comme ça
Les casseurs devraient déjà être en garde à vue. Ils devraient déjà être identifiés.
Tu te balades dans la rue avec une cagoule, tu tiens pas 5 minutes. Mais des mecs qui rentrent dans un lieu public avec des bâtons et des cagoules, aucun problème on les laisse faire, ils vont nous débarasser des gêneurs. On va même les aider.
Comment le journaliste qui a interrogé le doyen a fait pour ne pas lui sauter à la gorge ? Comment un individu normal peut se retrouver en face d'un déchet de l'humanité de ce type, d'un traître aux gens qu'il doit protéger, d'un fasciste de la sorte et ne rien faire ? Ne rien tenter ? Se retenir de lui coller son poing dans la figure ? Même simplement par réflexe ?
D'abord les doyens font tabasser leurs étudiants et après, c'est les médecins qui gazent leurs patients… Parce que c'est toujours comme ça que ça commence…
# T'es marrant, on fait quoi ?
On crie. On pleure. On s'indigne. On hurle. On n'essaie pas de comprendre parce que très rapidement, on a compris : des étudiants innoncents se font tabasser par des mecs encagoulés au sein de leur université avec la complicité de l'administration.
Cette simple phrase est déjà surréaliste. Aucune personne saine d'esprit ne devrait dire cette phrase dans un pays civilisé, dans une République, dans une Démocratie, en 2018.
Alors, on fait quoi ? Moi, j'écris. Parce que c'est tout ce que je sais faire. Parce que c'est cathartique. Parce que j'avais besoin d'exprimer mon indignation, mon dégoût, ma colère et mon impuissance.
Et on entre en résistance. Parce que, ça y est, notre République vient de mourir. L'état de droit vient de mourir. Quand les gens qui préméditent ce genre d'actes ne sont pas punis et que cela ne fait réagir personne, c'est que la partie est terminée.
Aux armes, Citoyen.

View File

@@ -0,0 +1,125 @@
+++
title = "LXD/LXC, Docker et autres joyeusetés (partie 1 sur beaucoup potentiellement…)"
date = 2018-08-06
aliases = [ "post/2018/08/06/LXD/LXC,-Docker-et-autres-joyeusetés-(partie-1-sur-beaucoup-potentiellement…)"]
[taxonomies]
tags = [ "système", "lxc", "docker" ]
+++
Et là, la marmotte, elle met le chocolat dans le papier d'alu.
<!-- more -->
# Point de départ
on serveur à ma maison (qui sert notamment à héberger ces lignes) commence à se faire vieux et surtout à être relativement surchargé :
* Messagerie avec Postfix/Dovecot/Spamassassin/OpenDKIM
* Messagerie instantannée avec Prosody (même s'il n'y a plus grand-monde dessus, il faut bien le reconnaître…)
* Listes de diffusion avec Mailman
* Plein d'applis PHP/MariaDB pour ma tronche (PrivateBin, TTRSS, Roundcube, Z-Push, NextCloud, etc…)
* Des applis PHP/MariaDB pour le [Dojo](https://www.nintendojo.fr/) (notamment un Wordpress et un forum phpBB)
* Une appli PHP/MariaDB pour [Pipoworld(https://forum.pipoworld.fr)
* Un serveur de conversation Teamspeak
* Un serveur de conversation Mumble (murmur pour les intimes)
* Un serveur de diffusion audio Icecast2 pour le [dojobar](https://www.nintendojo.fr/dojobar/)
* Un transmission et un Sonarr
* Une instance Mastodon dockérisé
Ça fait donc beaucoup beaucoup de bordel au même endroit, dans la même petite Debian.
Dans l'absolu, cette approche ne me pose pas tellement de souci : tous les logiciels sont installés proprement (NginX, PHP-FPM, etc…) et maintenus correctement par le projet Debian ou au pire par un dépôt tiers. Je n'ai pas vraiment de mouton à 3 têtes (sauf peut-être Mastodon, c'est bien pour ça qu'il est conteneurisé).
Au quotidien, le principal problème, c'est surtout que j'ai peur de tout casser : je fais les mises à jour régulières de sécurité sans trop me poser de question mais au moment de passer de *Jessie* à *Stretch*, je me suis fait quelques belles frayeurs. phpBB notamment ne supportait pas vraiment bien le passage de PHP 5 à PHP 7.
Fort heureusement, étant très prudent de base, c'est pratiquement le seul effet de bord que j'ai eu.
on second problème avec ce *setup*, c'est que je ne peux que difficilement déléguer : je peux donner des accès distants en SSH, je peux toujours faire des trucs avec du *sudoers*, mais ça devient assez vite limité s'il faut déléguer des droits plus fins ou plus permissifs.
L'objectif est donc de changer le fusil d'épaule et de passer à quelque chose de plus modulaire. Pour déléguer, mais aussi pour séparer un peu les choses.
# Comparaison des différentes solutions possibles
Je suis donc reparti sur le tableau blanc pour essayer d'imaginer les différentes alternatives et solutions qui pourraient s'offrir à moi.
## Touche pas, ça marche
La première consistait à reprendre tout tel quel. Ça ne simplifie pas forcément la migration (sic!) et en plus, je me retrouve avec le même bordel à l'arrivée. Garbage-in, garbage-out, j'ai éliminé très rapidement cette solution.
## VM partout, justice nulle part
En poussant un peu le curseur, je me suis dit que je pourrais aussi virtualiser tout : une machine virtuelle pour chaque grande famille d'applications, avec une adresse IPv6 publique. Bien entendu, il faudrait forcément rajouter des petites choses pour que tout se passe bien :
* un load-balancer (type HAProxy) pour la partie HTTP/HTTPS en IPv4. Je pensais à une solution qui ferait du load-balancing sur la partie HTTPS en utilisant l'entête SNI et en ouvrant ensuite la connexion vers le bon serveur en TCP. C'est con, mais j'ai testé, ça fonctionne correctement. Ça permettrait aussi de gérer proprement la partie gestion des certificats sur chaque VM.
* il faudra obligatoirement un orchestrateur pour gérer tout ça : SaltStack, Ansible ou approchant. Il va forcément y avoir beaucoup d'éléments de configuration qui vont être communs entre tous ces services.
Évidemment, le démarrage risque d'être le plus douloureux : il faut créer un socle de configuration commun à toutes les VMs (SSH, NTP, configuration générale de zsh, etc…) qui peut prendre pas mal de temps. Une fois le socle en place néanmoins, déployer une nouvelle VM devrait prendre un temps minimal.
A priori, ça peut être une bonne solution, mais il y a tout de même quelques réserves à émettre :
* le fait de gérer des VMs est de facto plus lourd que le fait de gérer des services. Par contre, ça peut permettre de séparer correctement les services, les utilisateurs, les administrateurs, etc…
* les ressources utilisées sont beaucoup plus importantes que sur une machine seule. En plus, il y a peu de mutualisation réelle de ressources possibles : 1 base MariaDB bien optimisée vaut-il mieux que 4 bases MariaDB pas du tout optimisées ?
* la complexité globale de la configuration risque d'être d'autant plus importante
Pas une mauvaise solution donc, mais il faut quand même rajouter quelques composants pour arriver à s'en sortir. En terme de ressources, ça n'est pas forcément à la portée de tout le monde non plus (faut une machine costaude).
## Conteneur LXD/LXC
LXC est une solution de conteneur permettant d'installer des « vrais » systèmes. Contrairement aux conteneurs Docker, l'objectif est ici d'avoir une gestion très proche des machines virtuelles, tout en ayant une empreinte mémoire bien plus légère qu'une machine virtuelle. On parle alors de VE (*Virtual Environment*) plutôt que de VM (*Virtual Machine*).
On peut donc y retrouver les mêmes principes de fonctionnement et avantages que pour la solution précédente, mais l'inconvénient de la ressource en moins. Cela oblige également à déployer les mêmes outils complémentaires dans la plupart des cas (orchestrateur, etc…).
LXD est l'évolution logique de LXC : plus simple, plus propre, plus facile, plus rigolo. Si on doit choisir entre les deux aujourd'hui, je pense qu'il vau mieux privilégier LXD. Le seul incovénient pour le moment, c'est que LXD ne tourne correctement que sous Ubunutu. L'intégrer correctement dans d'autres distributions est prévu mais pas encore fait pour le moment.
Un inconvénient majeur à retenir tout de même : si on peut faire à peu près n'importe quoi en terme d'image, c'est limité à Linux. Impossible d'installer un FreeBSD, un OpenBSD ou même (horreur !) un Windows. Par contre, on a accès à une large palette de Linux, d'Alpine à CentOS, en passant par Ubuntu, Archlinux et d'autres.
Il est même possible de faire du *nesting* pour faire du conteneur dans du conteneur (donc Docker dans LXC par exemple).
__C'est pour le moment la solution qui me semble la plus adaptée à mon besoin.__ Mais ça m'empêche pas de regarder ailleurs pour vérifier s'il y a pas plus marrant à faire.
## Conteneur Docker
Pour séparer correctement des applications, pourquoi ne pas utiliser Docker ? Après tout, c'est fait pour. Des dizaines d'image de conteneurs disponibles sur le [hub|https://store.docker.com/|en|Docker] directement faite par des gens kissiconèsse© et prêts à l'emploi.
Le souci, c'est que c'est compliqué de faire du Docker « seul ». Il faut obligatoirement l'outiller un peu aussi pour arriver à faire quelques chose. Pour déployer un ou deux conteneurs un peu à l'arrache, ça marche très bien, mais dès qu'il s'agit de gérer toute une batterie de conteneurs, il faut obligatoirement outiller l'ensemble avec un orchestrateur de conteneurs par exemple.
Il y a plusieurs possibilités pour ça :
* docker-compose permet de « scripter » un ensemble de conteneurs dans un fichier YAML. Il suffit donc de décrire l'ensemble des interactions entre des conteneurs et hop, le tour est joué
* Docker Swarm qui est un orchestrateur de conteneurs qui permet d'aller déployer les mêmes fichiers YAML mais à travers plusieurs machines (au moins 3 de ce que j'ai compris).
Du coup, pour rigoler, j'ai monté une petite maquette en Docker Swarm pour voir si ça pourrait correspondre au besoin :
* 3 machines ça fait beaucoup, mais au moins la redondance de l'ensemble est assurée.
* Docker prend en compte la redondance des conteneurs eux-mêmes (si l'on déploie un conteneur NginX, il sera téléchargé sur chaque nœud du cluster Swarm), mais pas la redondance des volumes de données. Il faut donc une solution complémentaire pour cela (GlusterFS, NAS, etc…)
* c'est nettement plus simple de déployer un conteneur qu'une VM et le conteneur a déjà une fonction spécialisée (nginx, php-fpm, mariadb, etc…)
* on déploie un peu la version de conteneur que l'on veut, sans trop se précoccuper de ce qu'il y a dedans. Envie d'un PHP5 ou lieu d'une PHP7 ? Rien de plus facile, il suffit de changer le tag du conteneur.
* on a forcément la dernière version stable ou même la dernière version tout court de n'importe quel logiciel. Les logiciels que l'on utilise n'ayant pas toujours les mêmes cycles de développement, cela peut se révéler très très utile dans certains cas.
Maintenant, Docker (et Docker Swarm) apporte aussi son lot de problème, que je vais essayer de résumer ci-après.
Le fontionnement de Swarm lui-même oblige à avoir des ports « publiable ». Je m'explique : vous faites tourner votre conteneur NginX et vous voulez publier le port 80. Swarm permet de le faire et à ce moment, le port 80 est accessible (en IPv4 et en IPv6) sur l'ensemble des nœuds du cluster Swarm. C'est cool mais ça veut aussi dire que pour avoir un second conteneur NginX, il faut obligatoirement un autre port. Il faut également prévoir un load-balancer pour balancer les requêtes entre les différents nœuds du cluster du coup : on ne peut joindre qu'un seul port 80 sur l'ensemble du cluster, mais si on redirige le port 80 vers un seul nœud et que ce dernier tombe, c'en est fini…
Une autre solution pourrait consister à utiliser Keepalived en frontal pour avoir une adresse VRRP partagée entre les nœuds du Swarm.
La logique de Docker veut qu'on fasse tourner un seul processus dans un conteneur. Sauf qu'avec un seul processus MariaDB, je peux faire tourner 10 bases de données différentes pour 10 applications différentes. Que faut-il que je fasse dans ce cas précis ? Rien n'est moins évident…
Dans le même ordre d'idée, un conteneur de type Sonarr, NextCloud ou Gitlab fait tourner bien plus qu'un seul processus et pourtant, ça ne choque personne. La documentation de [s6-overlay|https://github.com/just-containers/s6-overlay#the-docker-way|en|Github s6-overlay] précise que normalement faudrait avoir qu'un seul process par conteneur, mais qu'en fait on s'en bat les couilles. Et les mecs font des conteneurs super populaires, la logique, on repassera donc…
Il y a aussi quelques points de la mise en œuvre de Docker, spécifiquement pour les applications PHP, qui me semblent relativement discutables : hors de question de faire du Apache (faut pas déconner non plus !), il faut donc nécessaire un conteneur NginX et un conteneur PHP-FPM lié pour faire tourner une appli. Cela implique qu'il faut un volume partagé entre les deux. Jusque là, rien de choquant…
ais faut-il inclure les données non variables de l'application PHP (toute la partie « programme ») dans le conteneur également ? Dans un NextCloud par exemple, le dossier *config/* et *data/* sont les seuls à bouger en théorie. Pourquoi ne pas inclure tout le reste ?
La gestion de la configuration reste aussi un peu plus complexe : on peut faire quelques templates de configuration dans des volumes accessibles en lecture seule à plusieurs conteneurs, mais il y a souvent beaucoup de choses à inclure pour faire fonctionner certains composants. Typiquement, pour NginX : il faut un *nginx.conf* correctement configuré, il faut y adjoindre quelques autres fichiers de configuration (*mime.types*, *fastcgi_params*, etc…), le fichier de conf de l'appli elle-même, ainsi que le certificat et la clé privée.
Autant avec Salt, il suffit de déployer ces fichiers de conf correctement modélisés sur l'ensemble des nœuds ayant besoin de cette configuration. En  Docker Swarm, on peut certes faire la même chose, mais modifier la configuration de certains services à la volée est complexe.
Dans le même ordre d'idée, faire tourner certains types d'applications qui se basent sur de la communication inter-process ou sur le fait de lancer une commande d'une autre programme, peut rapidement tourner au cauchemar. Postfix lance la commande deliver de Dovecot pour livrer les mails : on fait comment quand tout est dans un conteneur ? Il faut forcément repenser les choses autrement.
on dilemme est donc à présent le suivant : il est certain que j'aurais du Docker d'une manière ou d'une autre (ne serait-ce que pour reprendre mon instance Mastodon). Tout mettre dans du Docker me permettrait éventuellement de me simplifier considérablement la vie dans certains cas, mais ça ne résout pas tout et surtout pas les soucis de délégation… Que faire donc ?
# Du coup, on fait quoi ?
Et bien on maquette mes loulous, on maquette.
onter une maquette LXD va être très simple. Il y a beaucoup de choses à tester, mais ça permet de se mettre rapidement le pied à l'étrier.
onter une maquette Swarm n'est pas beaucoup plus compliqué et cela permet au moins de vérifier comment on pourrait faire fonctionner le tout.
La suite au prochain numéro !

View File

@@ -0,0 +1,101 @@
+++
title = "LXD/LXC, Docker et autres joyeusetés (partie 2 sur beaucoup potentiellement…)"
date = 2018-08-16
aliases = [ "post/2018/08/16/LXD/LXC,-Docker-et-autres-joyeusetés-(partie-2-sur-beaucoup-potentiellement…)"]
[taxonomies]
tags = [ "lxc", "docker", "système" ]
+++
Où l'on met beaucoup d'ambiance dans cette boîte jaune…
<!-- more -->
# Docker, c'est bien, mais c'est quand même pas idéal pour tout
En y regardant de plus près, on se rend rapidement compte que Docker a avant tout été imaginé pour faire tourner des applications *stateless* (sans état), donc des trucs qu'on peut tuer et redémarrer à loisir.
Du coup, pour faire du *statefull* (base de données, applications persistentes, etc…), c'est déjà nettement plus compliqué (sans être impossible hein, mais disons que ça risque de galérer un peu). L'idée même du conteneur est d'être une structure jetable mais fournie avec les piles (toutes les librairies et une grande partie de la logique de fonctionnement).
# Des cas simples…
Il existe donc des cas d'usages finalement relativement simples :
* l'intégralité de l'application est un seul exécutable (avec un éventuel fichier de conf) :
* on peut mettre le fichier de conf dans un volume (en lecture seule même éventuellement)
* on peut faire un dérivé du conteneur « officiel » et y inclure le fichier de conf en question (moins souple, mais l'ensemble est monolithique)
* l'application peut s'exécuter à 100% dans le conteneur mais requiert un volume pour ces données :
* on peut très facilement lui coller un volume persistent
* en cas de mise à jour, le volume persistent ne bougera pas
* si l'application en question doit toucher à la structure des fichiers lors des mises à jour, ça va être très très compliqué pour revenir en arrière
* l'application peut s'exécuter à 100% dans le conteneur mais requiert une base de données :
* on peut héberger la base de données sur l'hôte (ça permet de séparer ce qui est *stateless* de ce qui est *statefull*)
* on peut héberger la base de données dans un autre conteneur à côté et lier simplement les deux conteneurs entre eux (comme ça, la base de données ne peut mécaniquement pas être accessible par autre chose que l'application)
* l'application consiste en plusieurs exécutables qui travaillent ensemble :
* on peut tous les inclure dans un conteneur, mais c'est super cracra
* on peut les séparer dans des conteneurs différents mais tous basés sur la même image, c'est plus propre mais nettement plus compliqué
Il y a probablement d'autres cas, mais ça résume, je trouve, assez bien le type d'applications que j'héberge actuellement.
# et des cas plus compliqués…
Le souci avec PHP, c'est qu'en gros, ça fait un peu tout ce qui a été cité avant : généralement, il faut une base de données, des fichiers persistents et des fichiers de configuration (en plus de la configuration « système » elle-même). Et l'exécution PHP requiert que le process naisse, interprète, exécute et meurt. Nous sommes loin de l'application dans un seul exécutable…
Du coup, plusieurs approches sont possibles :
* tout mettre dans un conteneur Apache + PHP en y incluant toute la conf PHP :
* les changements de configuration passent obligatoirement par une reconstruction (potentiellement lourde) du conteneur
* il faut quand même prévoir un ou plusieurs volumes persistents et en fonction des applications, cela peut être très compliqué (beaucoup d'applications PHP écrivent dans plusieurs répertoires)
* quoiqu'il arrive, il faut prévoir au moins un conteneur MariaDB par application PHP (pas gênant en soi, mais en terme de ressources, c'est un peu bourrin)
* faut faire du Apache et ça, ça donne vraiment envie de vomir
* tout mettre dans un conteneur FPM et mettre l'application PHP en elle-même dans un volume à part :
* il faut alors prévoir un conteneur supplémentaire NginX (par exemple) pour la partie présentation. Il faudra également que ce conteneur ait un accès en lecture seule au volume de données du conteneur FPM (sinon, ça ne marchera pas)
* il faut inclure les sources PHP du logiciel dans le conteneur : encore une fois, il faut le reconstruire à chaque version et prévoir un mécanisme pour mettre à jour les fichiers du volume persistent, sans écraser les configurations/données
(re)faire de l'Apache est honnêtement au-dessus de mes forces, je me suis donc penché sur les méthodes utilisées par quelques très grosses applications PHP : NextCloud et WordPress.
Les approches sont en générales assez similaires :
* fournir un conteneur très générique avec de l'Apache + PHP et tout ce qu'il faut dedans
* fournir un conteneur FPM contenant au moins le programme PHP et un moyen de mettre à jour le volume persistent créé par le conteneur
* y adjoindre systématiquement un conteneur de base de données
Dans tous les cas, ça marche très bien pour du lab. Le souci, c'est que ça fait des images relativement lourdes et surtout, c'est intéressant si l'on veut distribuer le logiciel en même temps que son environnement. Si on a déjà le logiciel (avec tout ce qu'il faut dedans), je ne vois pas bien l'intérêt.
D'autant plus qu'en cas de mise à jour, il faut de toutes manières aller écraser l'ensemble du contenu du volume persistent : la détection d'une nouvelle version me semble relativement hasardeuse en fonction des cas et surtout on perd la possibilité de faire un *rollback* en redémarrant simplement la version précédente du conteneur : vous démarrez NextCloud 13.0.2, un truc merde, vous ne pouvez plus revenir en arrière.
On perd du coup une grande partie de l'intérêt des conteneurs. Pareil pour les base de données : si le schéma ou les données sont touchées par la mise à jour, c'est en fini en cas de problème.
C'est un peu ce qui me fait dire que Docker et PHP ne sont peut-être pas vraiment fait pour s'entendre. Ça pourrait être bien pire, mais c'est loin, très loin d'être idéal.
# Retour à la case départ
Cela me ramène donc à mon idée d'origine :
* un dérivé d'un conteur *nginx:stable* qui embarquerait toute la configuration standard que j'ai l'habitude de mettre partout (ça représente 2 ou 3 fichiers en tout, c'est intéressant de les embarquer directement pour retrouver systématiquement la même structure) ;
* un dérivé d'un conteneur PHP-FPM contenant toutes les extensions nécessaires au bon fonctionnement de 99% des applis PHP (mysqli, gd, etc…), ainsi que toutes les configurations recommandées pour FPM (*opcache*, durée de vie, nombre de process, etc…). Des recettes pour faire le plus propre possible (en supprimant les outils de compilation à la fin par exemple) existe. Au pire des cas, NextCloud fournit un template très très complet auquel il ne faut pratiquement rien adjoindre.
* un conteneur *mariadb:latest* tout ce qu'il y a de plus standard (parce qu'a priori s'il y a un conteneur MariaDB par application, faire de l'optimisation ne va vraiment pas être nécessaire).
Cette approche permet d'avoir finalement une pile d'exécution PHP assez générique et qui convient à presque tous les usages : les conteneurs permettent d'avoir des environnements d'exécution bien séparés, les base de données sont clairement disjointes, les données persistentes sont bien rangées dans un coin.
Cela pose quand même quelques autres petits inconvénients :
* il est nécessaire de peupler le volume persistent avec le programme PHP avant de lancer le conteneur quoiqu'il arrive, on ne peut pas simplement se contenter de lancer le conteneur et le laisser se débrouiller ;
* une seule image PHP-FPM, cela veut dire toutes les librairies embarquées avec potentiellement des librairies complètement inutiles voire dangeureuses dedans.
ais au moins, de cette manière, pour faire tourner une application, est relativiment simple. Cela nécessite :
* deux volumes persistents, un pour les données de l'application PHP et un pour les données de la base de données MariaDB
* présenter le volume de données à un conteneur MariaDB
* présenter le volume applicatif à un conteneur PHP-FPM en écriture
* présenter le même volume à un NginX en lecture seule
* ajouter les configurations nécessaires pour NginX (1 fichier de conf pour l'appli, une clé privée et un certificat public pour la partie HTTPS) en lecture seule
* et évidemment rajouter toute la communication entre ces différents conteneurs
* le tout décrit dans un fichier YAML correctement commenté.
Bref, ça fait quand même beaucoup beaucoup de bordel pour pouvoir faire tourner chaque application. Je suis par contre complètement incapable de dire si c'est plus ou moins efficace en terme de maintenabilité :
* être systématiquement sur les toutes dernières version de NginX, MariaDB et PHP peut présenter des avantages considérables dans certains cas…
* … mais aussi faire bien chier dans d'autres. S'il faut que je maintienne 3 conteneurs PHP différents, ça va vite devenir très lourd…
* il faut construire (et donc reconstruire régulièrement) au moins deux conteneurs différents : un pour PHP et un pour NginX. Ce n'est jamais grand-chose, mais j'ignore si l'on peut automatiser ce processus (en mode quand PHP-FPM est mis à jour, va faire un *docker build*).
# Foutaise !
Si tu as bien tout suivi, tu as retenu au moins une chose : tout dockériser, c'est **surtout** maîtriser l'ensemble de la chaîne de montage de chaque image. Partir du principe que les images toutes faites vont convenir me semble hautement illusoire. Si l'on ne veut pas s'éparpiller, on a grandement intérêt à acquérir les bases logistiques nécessaires pour produire de l'image à tour de bras.
Finalement, peu importe qu'on le déploie avec simplement du Docker, avec du Docker-compose, du Docker Swarm, du Kubernetes, du RancherOS ou n'importe quel autre orchestrateur de conteneurs. Maîtriser le contenu des images est finalement plus important que dans maîtriser le contenant.
Au prochain numéro, on essaiera (tant bien que mal) de voir quelle solution sont adoptables pour faire du reverse-proxy.

View File

@@ -0,0 +1,103 @@
+++
title = "Passer ses utilisateurs IMAP du système vers Vmail (sans douleur si possible)"
date = 2018-08-20
aliases = [ "post/2018/08/20/Passer-ses-utilisateurs-IMAP-du-système-vers-Vmail-(sans-douleur-si-possible)"]
draft = true
[taxonomies]
tags = [ "système", "mail" ]
+++
Nan parce que c'est sympa et tout hein… mais tout le monde s'en fout !
<!-- more -->
Originellement, j'avais (bêtement) pensé que créer des utilisateurs système pour recevoir des courriels étaient une bonne idée. Dans les faits, il y a quelques années, c'était probablement vrai : tout le monde était propriétaire de ces mails au sens Unix, le dossier était clairement visible et ça permettait d'appliquer très très facilement des quotas.
Sauf qu'aujourd'hui, manager des utilisateurs système est plus un problème qu'autre chose :
1. les gens ne se connectent pratiquement plus à leur compte personnel… C'en est devenu un peu frustrant, mais à une époque proposer 1 ou 2 Gio de stockage accessible en SFTP avec un répertoire HTML public, c'était la classe, aujourd'hui, tout le monde s'en bat les couilles.
2. ça fait que le système et la messagerie sont très très étroitement liés, ce qui peut être problématique dans certains cas : changer de serveur peut poser un vrai problème, migrer sur un autre système également.
L'idéal serait donc d'avoir une configuration plus claire et surtout bien plus segmenté entre les différents services nécessaires :
* Postfix ne fait qu'acheminer les messages. Il n'a pas besoin de savoir qui sont les utilisateurs légitimes en local, ce n'est pas son problème.
* Dovecot devrait être capable d'authentifier les utilisateurs et de remettre les messages au bon endroit pour qu'il soit accessible. Tout devrait d'ailleurs repasser plus ou moins par lui.
On va donc voir ensemble comment on peut migrer ce bordel.
# Passage de Dovecot et Postfix en LMTP
À l'origine, j'avais une configuration très très simple pour la livraison des messages :
~~~
mailbox_command = /usr/lib/dovecot/deliver
~~~
L'idée est que Postfix fasse appel à la commande `deliver` disponible dans Dovecot. Ça marche très bien tant que tout est installé sur la même machine et avec les mêmes « versions » de logiciels. Si on commence à essayer de séparer les services sur des machines différentes, ça va commencer à sentir du cul.
Pour faire simple, lors d'une livraison locale (si Postfix détecte le domaine de messagerie dans le `mydestination` en gros), Postfix va évaluer les choses dans cet ordre :
* alias (en général dans `/etc/aliases`)
* fichiers `.forward`
* `mailbox_transport_maps`
* `mailbox_transport`
* `mailbox_command_maps`
* `mailbox_command`
* `home_mailbox`
* *(et d'autres après mais dont on se fout)*
À l'origine, j'utilisais `home_mailbox` pour que Postfix livre directement les messages dans le *Maildir* des utilisateurs. Postfix servait alors de MDA et de MTA. Au fur et à mesure que ma compréhension et mon expérience de Dovecot se sont améliorées, j'ai utilisé la commande `deliver` : Dovecot devient alors LDA (*Local Delivery Agent*).
On va garder le même état d'esprit mais en connectant Postfix à Dovecot via une socket au lieu d'une commande. On rend ainsi les deux beaucoup plus imperméables et moins interdépendants.
Pour Postfix, la configuration va être très très simple :
~~~
#mailbox_command = /usr/lib/dovecot/deliver
mailbox_transport = lmtp:inet:127.0.0.1:24
~~~
Et là normalement, tu devrais saigner du nez : « Putain ! Mais t'as mis une adresse IP en dur BORDAYLE DE MAYRDE !! *unfollow* ».
Oui, bah je sais plus pourquoi mais *localhost*, il avait du mal… Donc voilà, faites pas chier, vous vous adapterez et vous ferez les malins sur votre serveur Postfix à vous.
Dernière petite chose : il faut préciser à Postfix de ne pas accepter les messages avec des entêtes UTF-8. En effet, il se trouve que Dovecot en mode LMTP ne supporte pas les entêtes UTF-8. Si tu ne désactives pas cela, ça va vite partir en cacahouète du coup…
~~~
smtputf8_enable = no
~~~
Côté Dovecot, ça ne va pas être bien compliqué non plus. On ajoute donc le protocole LMTP aux protocoles servis par Dovecot :
~~~
protocols = imap lmtp
~~~
La liste des utilisateurs locaux transmis par Postfix à Dovecot contient l'adresse complète de la personne (vabien@teniquer.com par exemple). Il est donc nécessaire dans notre cas (authentification Unix pour le moment) de virer la partie courriel (donc la partie après le *@*) :
~~~
auth_username_format = %Ln
~~~
Enfin, il faut démarrer le service LMTP sur un port local :
~~~
service lmtp {
inet_listener ltmp {
address = 127.0.0.1 ::1
port = 24
}
}
protocol lmtp {
mail_location = maidir:~/Maildir
mail_plugins = sieve
}
~~~
*Note : il n'y a pas de port standard pour LMTP, tu es donc libre de choisir de ce que tu veux…*
On redémarre Postfix et Dovecot et on devrait normalement pouvoir continuer à recevoir des mails sans aucun souci (bon évidemment, vaut mieux tester hein, sinon, ça risque d'être un beau merdier !).
# Migration de la UserDB et de la PasswordDB

View File

@@ -0,0 +1,143 @@
+++
title = "Passer un disque fait pour le BIOS en UEFI avec BTRFS"
date = 2018-09-14
aliases = [ "post/2018/09/14/Passer-un-disque-fait-pour-le-BIOS-en-UEFI-avec-BTRFS"]
[taxonomies]
tags = [ "système", "btrfs" ]
+++
Ça a pas l'air compliqué comme ça, mais en fait, c'est hyper chiant…
<!-- more -->
J'ai changé un très vieux PC portable récemment (12 ans !) vers un truc un peu plus neuf (seulement 3 ans) et je ne voulais pas réinstaller le système au complet (flemme). Bon en vérité, j'aurais très bien pu tout réinstaller et je n'aurais pas mis tant de temps que ça mais je n'avais vraiment pas envie de me bouger.
Problème : cette nouvelle machine ne reconnaît pas mon disque. Pour une raison assez simple : il a été formaté avec une table de partitions standard et pas UEFI. Donc pas moyen de démarrer quoique ce soit, je suis obligé de booter sur une clé USB qui contient GRUB.
J'aurais pu en rester là, mais ça n'aurait pas été drôle. Mon disque ne contient même pas de partition en fait : c'est un système BTRFS directement installé sur `/dev/sda` (c'est moche, mais ça marche).
À partir de ce moment, il y a deux possibilités :
* soit on possède un autre disque, et on peut tenter un `btrfs replace` dans un sens, reformater le disque et faire un `btrfs replace` dans l'autre sens ;
* soit, et c'est mon cas, on ne dispose pas de second disque auquel cas on va utiliser les *snapshots* BTRFS et la fonction *send/receive* pour sauvegarder l'ensemble du disque sur une machine distante.
Et du coup, autant que je documente le processus, ça pourrait toujours servir à d'autres.
# Le bon système de fichiers, il snapshot…
Première étape essentiellement : faire des snapshots en lecture seule de l'ensemble des sous-volumes BTRFS. Pour cela rien de plus simple, il suffit de monter le disque BTRFS à la racine (si ton */* n'est pas à la racine) ou de créer carrément un sous-volume pour les stocker. Personnellement, mes sous-volumes ressemblent à ça :
```
# mount -t btrfs -o subvolid=0 /dev/sda /mnt
# btrfs sub list -a /mnt
ID 257 gen 202952 top level 5 path __root
ID 258 gen 202952 top level 5 path __home
ID 300 gen 202932 top level 257 path <FS_TREE>/__root/opt
ID 302 gen 202952 top level 257 path <FS_TREE>/__root/usr
ID 303 gen 202953 top level 257 path <FS_TREE>/__root/var
ID 801 gen 148907 top level 303 path <FS_TREE>/__root/var/lib/machines
ID 999 gen 194266 top level 303 path <FS_TREE>/__root/var/lib/portables
```
C'est donc pour moi assez simple : il faut créer un sous-volume cliché en __lecture seule__ pour chaque volume (sauf machines et portables qui ne me servent à rien et qui seront recréés par Docker/LXC) :
```
# btrfs subvolume snapshot -r /mnt/__home /mnt/__ro_home@$(date -I)
```
Opération à répéter pour chaque sous-volume. À noter qu'il faut impérativement tout mettre à la racine, même pour les sous-volumes *usr* et *var*.
À partir de ce moment-là, on peut commencer à le balancer sur une autre machine dans une autre partition BTRFS.
# Et un snapshot pour la 12 !
Alors évidemment (ou plutôt malheureusement), il va falloir être *root* sur les deux machines : impossible de créer un snapshot si l'on n'est pas root sur la machine cible et imposible de lire complètement un snapshot pour la même raison sur la machine source. Pour le reste, c'est relativement facile.
J'ai commencé par créer un sous-volume cible sur une autre machine (*baybay-ponay* en l'occurence) :
```
# btrfs subvolume create /home/pinkypie/
```
Depuis la machine source (*pinkypie*), j'ai ensuite envoyé chacun des snapshots :
```
# btrfs send /mnt/__ro_home@2018-09-14/ | ssh root@baybay-ponay btrfs receive /home/pinkypie/
# btrfs send /mnt/__ro_var@2018-09-14/ | ssh root@baybay-ponay btrfs receive /home/pinkypie/
[…]
```
> La date est complètement optionnelle hein, c'est juste pour se repérer plus facilement…
Avec un peu de patience, on obtient donc un transfert binaire absolument parfait de l'ensemble des partitions du système. On peut alors effacer complètement le disque d'origine, la sauvegarde du système est totalement réalisée.
# Reformatage du disque
À partir de ce moment-là, la suite des opérations se passent sur un disque de boot (Archlinux en l'occurence mais en théorie n'importe quel disque de boot doit faire l'affaire à condition que BTRFS et UEFI soit supporté dessus).
Comme ma machine cible est une machine UEFI 100%, nous allons utiliser `fdisk` pour la repartitionner en GPT (__ah ah ah ah__). Pour cela, on va se servir de l'option g dans le menu principal :
```
# fdisk /dev/sda
Bienvenue dans fdisk (util-linux 2.32.1).
Les modifications resteront en mémoire jusqu'à écriture.
Soyez prudent avant d'utiliser la commande d'écriture.
Commande (m pour l'aide) : g
Une nouvelle étiquette de disque GPT a été créée (GUID : LOLILOL).
L'ancienne signature iso9660 sera enlevée par une commande d'écriture.
```
Il suffit donc de créer une partition de type *EFI System* (256M doivent largement suffire, la bonne pratique recommande environ 100Mio), puis une seconde partition de type *Linux filesystem* pour le reste du disque.
On valide l'écriture et c'est parti mon kiki.
# Recréation d'un système de fichiers BTRFS
Là encore, c'est finalement relativement simple :
```
# mkfs.btrfs --label GG /dev/sda2
# mount LABEL=GG /mnt
```
On peut alors aller récupérer les données depuis *baybay-ponay* où on les avait entreposées bien au chaud :
```
# ssh root@baybay-ponay btrfs send /home/pinkypie/__ro_home@2018-09-14 | btrfs receive /mnt
```
On va alors récupérer un sous-volume en lecture seule. Pour le rendre inscriptible, rien de plus simple :
```
# btrfs subvolume snapshot /mnt/__ro_home@2018-09-14 /mnt/__home
# btrfs subvolume delete /mnt/__ro_home@2018-09-14
```
Et à ce moment, on récupère bien une partition parfaitement utilisable en écriture ! Il suffit donc de reprendre la même opération pour chaque sous-volume et de réorganiser ça bien gentiment.
# Réinstallation de ce qu'il faut sous Archlinux
Jusqu'à présent, nous avons fait des choses relativement universelles : BTRFS, partitionnement GPT (__ah ah ah ah__) et *send/receive*. Cette dernière partie est plus spécifique à Archlinux et est surtout là pour me servir de pense-bête.
Après avoir modifié correctement `/etc/fstab`, et monter toutes les partitions au bon endroit, on peut entamer la modification en profondeur du système pour le rendre compatible UEFI. Il faut donc supprimer le dossier `/boot@ de la racine du système cible, formater la partition *EFI System* précédemment créée en FAT32 et la monter au bon endroit :
```
# rm -rf /mnt/boot
# mkfs.vfat /dev/sda1
# mount /dev/sda1 /mnt/boot
```
Il faut ensuite balancer une commande *grub* bien spécifique pour créer tous les fichiers nécessaires au boot UEFI du système :
```
# grub-install --target=x86_64-efi --efi-directory=/mnt/boot --bootloader-id=GRUB
```
Voilà, maintenant il devrait y avoir tout ce qu'il faut au niveau de la carte mère pour arriver à booter correctement cette Archlinux. Néanmoins, comme nous avons supprimé la partition `/boot`, il va quand même falloir penser à réinstaller deux ou trois bricoles :
```
# for f in sys dev proc; do mount --bind /${f} /mnt/${f}
# chroot /mnt
# pacman -S linux
```
`pacman` va s'occuper de tout réinstaller bien gentiment à la bonne place avec les bonnes permissions et tout le toutim.
On redémarre, on va se chercher une bière bien méritée et on peut retourner glander tranquillement devant son PC en UEFI.

View File

@@ -0,0 +1,76 @@
+++
title = "Ne m'appelez plus privé !"
date = 2019-02-25
aliases = [ "post/2019/02/25/Ne-m-appelez-plus-privé-!"]
[taxonomies]
tags = [ "internet", "messagerie" ]
+++
Tu peux m'appeler Simone si tu veux…
<!-- more -->
Les repas chez ma belle-famille le Dimanche après-midi sont une occasion unique pour moi de me confronter à la population de néophytes en matière de réseau et d'informatique que constituent la majorités des gens. Mes beaux-parents sont un terrain d'expérimentation pratiquement illimité sur ce sujet : c'est un petit peu mes M. et Mme Michu à moi.
Comprenons-nous bien : ils ont un niveau d'éducation élevé et évoluent dans un milieu socio-professionnel élevé également. On est bien loin du « français moyen » (et j'ai une horreur toute particulière pour cette expression, mais elle aide à se figurer pas mal de choses).
Bref, ce sont des gens qui sont finalement ce que j'appelerais le « con moyen ». Face à un nouveau gadget informatique quelqu'il soit, le con moyen va avant tout voir ce que ça lui apporte avant même de se poser la question de ce que ça va lui coûter, soit en terme pécunier, soit en terme de vie privée.
## Bon, tu peux en venir au fait là ?
J'arrive jeune individu au sexe non-déterminé. Donc ma belle-mère ce Dimanche a commencé à partir en sucette à propos de Facebook : on y raconte n'importe quoi et il y a de plus en plus de violences dessus et de désinformation, etc…
Premier réflexe de votre serviteur : « Je ne comprends pas Belle-Maman : vous le critiquez et pourtant vous y êtes ». Stupeur à l'autre bout de la table : « Bah non, je ne suis pas sur Facebook ! ».
Je me permets alors de lui rappeler qu'elle a un compte Instagram. Pas de réaction. Je rajoute donc qu'Instagram appartient à 100% à Facebook et qu'être sur l'un ou l'autre ne change pas grand-chose : tout cela atterrit sur les mêmes serveurs, gérés par les mêmes gens (et dans les mêmes poches concernant les revenus publicitaires).
ême mon beau-père, pourtant généralement un peu poil mieux informé que ma belle-mère sur ce type de sujet, semble tomber des nues (tout est relatif hein, disons que ça l'a surpris…).
Je me suis donc retrouvé dans une position curieuse : je pensais sincèrement que les gens avaient entendu parler du rachat d'Instagram. Je pensais benoîtement que les gens étaient au courant que Facebook, Instagram et WhatsApp, c'était la même crêmerie (avec les mêmes pratiques douteuses d'ailleurs).
Donc, **non, les gens ne sont pas du tout au courant de la structure financière des services qu'ils utilisent gratuitement**. Donc, **non, les gens ne se posent pas la question de comment un truc avec 1 milliard d'utilisateurs peut fournir un service gratuitement**.
Et donc non, quand les gens finissent quand même par apprendre des scandales concernant la gestion des données personnelles sur Facebook, ils ne font pas du tout le rapprochement avec les autres boîtes appartenant au groupe Facebook (qui ne se présente d'ailleurs quasiment jamais comme ça !).
## Et donc le rapport avec « privé »
J'y viens. Donc mon beau-père d'enchaîner : « Mais de toutes manières, mon compte, il est privé ». J'ai dû avoir, bien involontairement, un rictus assez effroyable à ce moment-là car il s'est arrêté immédiatement. Puis il a repris en m'expliquant le plus naturellement du monde qu'il choisissait qui pouvait accéder à son compte.
Pardon ?
Un compte Instagram (ou Facebook, ou Google, ou Twitter, ou tout ce qui vous voulez comme service, privateur ou non d'ailleurs) n'est pas privé :
* les administrateurs du service peuvent accéder à n'importe quel compte
* les modérateurs du service peuvent accéder à n'importe quel compte
* les administrateurs des machines sur lequel est hébergé le service peuvent accéder à n'importe quel compte
* et probablement plein d'autres petites mains, administrateurs intermédiaires, hébergeurs, etc…
Des gens qui peuvent accéder à un compte Instagram/Facebook/Twitter, ça en fait quand même un paquet. Et je ne parle même pas ici des services des différents gouvernements qui mendient régulièrement des accès.
N'est **privé** que les données qui sont sur un disque dur que vous avez acheté et sur lequel vous faites tourner un service de traitement de ces données que vous maîtrisez, soit parce que vous pouvez auditer le code, soit parce que d'autres l'ont audité et que vous faites confiance dans le résultat dudit audit.
On peut évidemment ajouter à cela les données que vous avez chiffrées avec une clé dont vous êtes le seul détenteur.
Toutes les autres données sont publiques par définition, ou en tout cas sûrement pas privées.
## Restreint ? Invisible ? Non-public ?
Et c'est bien là qu'on voit toute la perfidie derrière les dénominations même utilisées par les grandes plateformes privées sur le Web : le terme privé est extrêmement trompeur. Il sous-entend que vous et vous seul avez les accès ou la capacité à donner des accès à certaines données alors que c'est par définition faux.
Non, un message privé sur Facebook n'est pas privé. Il peut être lu par un tiers. Non un compte Instagram privé n'est pas privé. Il peut être consulté par un tiers également.
Les messages directs sur Twitter ont au moins la décence de reconnaître cet état de fait : le message n'est pas privé. Mastodon utilise une terminologie similaire mais ajoute, en guise de précision, que les messages directs peuvent effectivement être lus par les administrateurs de l'instance qui envoit et de l'instance qui reçoit le message en question.
Reste donc deux questions ouvertes :
1. Peut-on trouver un terme qui serait plus parlant ? Vous l'avez lu plus haut : restreint, non-public, invisible, sur demande seulement ? Il va falloir trouver autre chose pour désigner ces comptes, ces messages ou ces photos « privés» parce que privé, ça ne veut strictement rien dire !
2. Peut-on éventuellement tenter d'attaquer ces plateformes pour propos mensonger lorsqu'elles désignent des choses comme étant privées ? Je ne suis malheureusement pas juriste et je suis à peu près certain que les plateformes en question ont utilisé des termes suffisamment alambiquées dans les CGV pour se dédouaner de ce « petite problème d'interface » qui appelle privé des choses qui ne le sont pas.
## Foutaises
Les pratiques des grandes plateformes de Web me dégoûtent toujours un peu plus tous les jours. J'ai ce qu'on appelle une angoisse gafamique : quand j'entends parler de la dernière connerie en date de Facebook ou du prochain projet orwellien de Google ou de la prochaine plateforme d'Amazon qui va encore un peu plus centraliser l'Internet, je commence à avoir une vraie, sincère et légitime angoisse à chaque fois.
Et voilà que maintenant, même les termes les plus basiques utilisées par ces gens ont pour simple et unique but de leurrer le grand public en leur faisant croire que leurs données sont bien à l'abri derrière un solide mur de pierre.
Facebook, fais-moi plaisir et va courir vers le mur le plus proche.

View File

@@ -0,0 +1,45 @@
+++
title = "XMPP et OMEMO sont dans un bâteau (1/2)"
date = 2019-03-12
aliases = [ "post/2019/03/12/XMPP-et-OMEMO-sont-dans-un-bâteau"]
[taxonomies]
tags = [ "internet", "messagerie" ]
+++
J'ai fait une rime \o/
<!-- more -->
La messagerie instantanée est un merdier sans nom depuis que je connais Internet. ICQ, MSN, Messenger, Jabber/XMPP, Facebook Messenger, WhatsApp, Wire, Telegram, Signal, c'est la jungle et ça l'a toujours été d'aussi loin que je me souvienne.\
Et la démocratisation de l'ordinateur de poche n'a finalement fait qu'empirer encore les choses : maintenant, tout le monde peut créer un compte sur n'importe quelle appli en quelques secondes et aller balancer l'ensemble de son carnet d'adresses chez une compagnie aux pratiques douteuses.
## Petit état des lieux…
Alors, je ne vais pas faire un bilan complet de toutes les applications de messagerie instantanée existantes. D'abord, parce que j'ai autre chose à foutre, ensuite parce qu'elles sont bien trop nombreuses pour les citer toutes (et je suis certain que d'autres l'ont fait bien mieux que moi) et enfin parce que ce n'est pas forcément ce qu'il y a de plus intéressant à faire.
Je vais néanmoins lister quelques critères qui me semblent très important concernant la messagerie instantanée :
* open source a minima, libre dans l'idéal côté serveur comme côté client ;
* la possibilité d'avoir des groupes de discussion (c'est probablement ce qui fait que le SMS est autant délaissé par les gens) ;
* la possibilité de chiffrer de bout en bout entre utilisateur mais également dans un groupe de discussion ;
* la possibilité de pouvoir accéder à toutes les conversations/contacts/groupe de discussion depuis plusieurs appareils (téléphone, ordinateur, etc…) ;
* la possibilité d'accéder le plus anonymement possible au service (pas besoin de mettre un courriel ou un numéro de téléphone ou d'autres informations personnelles) ;
* totalement décentralisé dans l'idéal.
À partir de ce moment-là, vous pouvez imaginer la note que récupérerait quelques services bien connus de messagerie instantanée :
* WhatsApp : centralisé, propriétaire, appartenant à Facebook, non-auditable (donc sécurité de bout en bout = 0). Pire choix possible donc.
* [Silence](https://f-droid.org/en/packages/org.smssecure.smssecure/) : chiffré, libre, basé sur du SMS (donc décentralisé dans une certaine mesure), mais malheureusement pas accessible depuis un ordinateur et pas de possibilité de faire des groupes (parce que SMS du coup). Je dirais que c'est une base qui peut être utile. En tout cas, ça ne mange pas de pain, mais il faut obligatoirement avoir autre chose en complément à mon humble avis.
* Telegram : open source côté client, un chiffrement qui semble solide au moins pour les dialogues 1:1, accessible depuis un PC. Bon par contre, pas de chiffrement pour les groupes, propriétaire côté serveur, centralisé et exige un numéro de téléphone valide pour s'inscrire. Donc mieux, mais pas top…
* Signal : open source côté client (mais Signal fait chier le monde concernant la distribution du code source et de la recompilation donc douteux), chiffrement solide, accessible depuis un PC. Par contre, se base essentiellement sur les infras de Google/Apple pour l'acheminement des messages, propriétaire côté serveur, centralisé et exige toujours un numéro de téléphone valide. Donc pareil, c'est mieux, mais c'est toujours pas top.
* Matrix/Riot : open source partout, accessible de partout, décentralisé. Mais malheureusement très bordélique à maintenir, et en fait, la décentralisation fait que le truc est lent pour délivrer des messages entre les instances (à ce qu'il paraît, je n'ai jamais pu le vérifier moi-même). On commence à toucher un truc pas trop mal là, mais j'ai personnellement le sentiment (sans avoir trempé dans le code ni rien) que ça ne sera pas vraiment utilisable à grande échelle.
Et donc, il nous reste le dernier (enfin dernier, on se comprend) candidat : XMPP avec OMEMO. XMPP, je pense que je n'ai pas vraiment besoin de le présenter : c'est LE protocole standard de messagerie instantanée sur Internet (oui, le seul…). Et OMEMO, c'est un standard supplémentaire qui permet de chiffrer de bout en bout mais pour plusieurs appareils et/ou plusieurs personnes. Les avantages de ce combo sont très nombreux :
* complètement décentralisé (c'est XMPP quoi) ;
* accessible depuis n'importe où : téléphone via [ChatSecure](https://itunes.apple.com/us/app/chatsecure-messenger/id464200063) sur iOS, [Conversations](https://f-droid.org/en/packages/eu.siacs.conversations/) sur Android, [Gajim](https://gajim.org/) sous Linux/Windows (j'ai un doute pour macOS, mais il doit bien exister quelque chose) ;
* chiffré de bout en bout, y compris pour les conversations de groupe et sur tous les appareils en même temps : c'est ça la grosse innovation d'OMEMO, pouvoir envoyer un message chiffré à destination de tous les appareils connectés du correspondant. Cela veut dire que l'on peut envoyer un message chiffré à la fois depuis un téléphone ou un PC mais aussi en **simultané** vers plusieurs téléphones, plusieurs PC, etc…
* c'est du XMPP donc ça ne nécessite en aucune manière un numéro de téléphone. Par contre, c'est un carnet d'adresses à part, en tout cas si tu ne partages pas ton roster XMPP avec ton carnet d'adresses habituel.
## :miam:
On verra donc une prochaine fois comment on peut s'installer un petit serveur XMPP tranquillou chez soi pour converser en toute sécurité avec d'autres barbus à poil dru ;)

View File

@@ -0,0 +1,75 @@
+++
title = "Résoudre l'erreur de mise à jour de TT-RSS (base 136 vers 137)"
date = 2019-03-13
aliases = [ "post/2019/03/13/Résoudre-l-erreur-de-mise-à-jour-de-TT-RSS-(base-136-vers-137)"]
[taxonomies]
tags = [ "web", "système" ]
+++
Nan parce que bon, c'est bon au bout d'un moment aussi…
<!-- more -->
Depuis quelques mois maintenant, Tiny-Tiny RSS ne maintient plus de version stable mais un [dépôt git](https://git.tt-rss.org/git/tt-rss/src/master) sur lequel il suffit de « puller » la dernière version. Et comme pour les versions antérieures, de temps en temps, il y a une mise à jour qui oblige à faire une mise à jour du schéma de la base.
En l'occurence, la mise à jour du 2019-03-10 contient une contrainte d'unicité supplémentaire sur une table.
Après la mise à jour du code, TT-RSS demande donc à mettre à jour la table en question.
Et là, c'est le drame :
![FAIL!!](/Screenshot_2019-03-13_Database_Updater.png)
## To the Batmobile !!
Bon évidemment, premier réflexe, aller vérifier que le problème n'est pas déjà répertorié ailleurs (tout l'avantage d'être un poil en retard sur ses mises à jour, c'est qu'on a nettement moins de chance d'être le premier à tomber sur un bogue).
[Le souci est donc connu](https://discourse.tt-rss.org/t/potential-problems-when-upgrading-to-schema-137/2122) mais en l'occurence la solution proposée ne fonctionne pas. Démonstration :
```sql
DELETE f1 FROM ttrss_feeds f1 INNER JOIN ttrss_feeds f2
WHERE f1.id < f2.id AND f1.feed_url = f2.feed_url
AND f1.owner_uid = f2.owner_uid;
Query OK, 0 rows affected (1.26 sec)
```
Il n'y a de toute évidence pas de lignes en doublon donc. Mais pourtant, l'index n'arrive pas à se créer.
Alors pourquoi qu'est-ce ?
## C'est pas la taille qui compte mais… on n'a pas déjà fait cette blague ?
Visiblement, ce n'est donc pas un souci de doublon mais un souci autre. À y regarder de plus près, l'index en question est créé avec une limite pour le champs `feed_url`. Et en fait, c'est parfaitement normal. Si l'on essaie de créer l'index sans longueur, MariaDB se fâche tout rouge :
```sql
alter table ttrss_feeds add constraint ttrss_feeds_feed_url_owner_uid_key unique (feed_url, owner_uid);
ERROR 1170 (42000): BLOB/TEXT column 'feed_url' used in key specification without a key length
```
En fait, un index texte doit forcément contenir une longueur fixe. Mais du coup, est-ce que ce ne serait pas ça le problème ? Retour sur MariaDB :
```sql
select count(*) from ttrss_feeds;
+----------+
| count(*) |
+----------+
| 589 |
+----------+
select count(distinct feed_url, owner_uid) from ttrss_feeds;
+-------------------------------------+
| count(distinct feed_url, owner_uid) |
+-------------------------------------+
| 589 |
+-------------------------------------+
select count(distinct substr(feed_url,1,255), owner_uid) from ttrss_feeds;
+---------------------------------------------------+
| count(distinct substr(feed_url,1,255), owner_uid) |
+---------------------------------------------------+
| 587 |
+---------------------------------------------------+
```
Twingo ! Le problème n'est donc pas l'unicité des flux, mais l'unicité des 255 premiers caractères des flux. Et en cherchant un peu le problème vient essentiellement de [Cheky](https://www.cheky.net/), le générateur de flux RSS à partir d'alerte [LeBonCoin](https://www.leboncoin.fr). Ce dernier génère de très très longs URLs avec beaucoup d'informations très similaires dedans.
Du coup, on fait quoi ? À part remonter le problème au développeur (ce que je vais m'empresser de faire), mais on ne peut malheureusement pas créer d'index plus long que 255 caractères.
Du coup, il faut simplement essaie de « distinguer » un peu plus les URLs en question (ouais, moi aussi, je voulais te proposer une solution bien plus poilue mais j'ai plus rien en stock).
Bon du coup, tu as pu faire ta mise à jour et tu es heureux. Tu peux maintenant prendre une bière bien méritée.

View File

@@ -0,0 +1,39 @@
+++
title = "LXD/LXC, Docker et autres joyeusetés (partie 3 sur beaucoup potentiellement…)"
date = 2019-06-30
draft = true
[taxonomies]
tags = [ "docker", "lxc", "système" ]
+++
Jaurais aussi pu appeler ça : 1 fille, 2 garçons, 3 possibilités. Ah, cest donc ça un titre putaclic…
<!-- more -->
Coucou mes rondoudous, cest Tonton Bingo.
Comme tu le sais probablement, ça fait un moment que je ~~tourne en rond~~ réfléchis à comment je vais faire évoluer mon auto-hébergement. Et même si je suis maintenant à peu près persuadé que la meilleure solution est LXC+Ansible, ça nempêche pas d'étudier les alternatives le plus sérieusement du monde. Et ça permet éventuellement de faire des choses intéressantes.
Aujourd'hui donc, je vais mattaquer à un problème autour duquel je tourne depuis [un bon moment|/post/2018/08/16/LXD/LXC%2C-Docker-et-autres-joyeuset%C3%A9s-%28partie-2-sur-beaucoup-potentiellement%E2%80%A6%29] : quelle est la meilleure solution pour faire tourner des applis PHP dans Docker ?
Alors, oui, jai déjà abordé le sujet, mais de façon assez générique. Je voulais voir ''concrètement'' comment on pourrait faire en prenant des exemples de la vie réelle. Et même si jai déjà dressé la liste des avantages/inconvénients de chaque méthode, je me suis dit que ce serait intéressant de les vérifier « pour de vrai ».
Donc, oui, il va y avoir de la redite, mais cest pour la bonne cause.
## Donc cest quoi en fait une appli PHP ?
Commençons par la base, à savoir comment cest gaullé une appli PHP. Généralement, il y a un serveur de présentation (dans mon cas, ça va être majoritairement du NginX), un serveur dexécution (PHP-FPM) et très souvent une base de données (MariaDB, mais ça peut être autre chose).
Les configurations à base dApache mélangent allègrement présentation et exécution, ce qui nest franchement pas ma tasse de thé (je trouve ça très crade et les conséquences en terme de sécurité et de performance peuvent réellement être inquiétantes). Mais le principe reste toujours le même : quand le client (le navigateur) demande une page '.php', le serveur de présentation va transmettre le chemin de la page en question à PHP-FPM, qui va exécuté lensemble de la pile PHP, puis renvoyer du texte (généralement du HTML ou du JS), après avoir éventuellement interagi avec la base de données.
Deux conséquences à cela :
1. NginX nest pas conscient quil y a une base de données (cest pas son boulot) ;
2. NginX et PHP-FPM doivent avoir les mêmes chemins sur le serveur local pour que cela fonctionne (cest le chemin qui est transmis par NginX vers PHP-FPM et pas le fichier à exécuter lui-même).
Il arrive également assez souvent que lapplication PHP ait besoin décrire dans des répertoires (fichiers temporaires, compilation de gabarits, téléversement de données, etc…).
Voilà, maintenant quon a clairement établi les règles du jeu, voyons comment on peut empaqueter une application PHP dans du Docker. Pour lexemple, on va utiliser phpBB. Il contient à peu près ce quon peut faire de pire en matière dapplications PHP (base de données, fichiers temporaires, fichiers persistents, etc…).
## Solution 1 : conteneurs génériques et répertoire de données
## Solution 2 : embarquer lapplication dans les conteneurs (ou solution NextCloud)
## Solution 3 : embarquer lapplication dans UN conteneur (ou solution Mastodon)

View File

@@ -0,0 +1,244 @@
+++
title = "Docker dans LXC, laventure"
date = 2019-09-01
aliases = [ "post/2019/09/01/Docker-dans-LXC,-laventure"]
draft = true
[taxonomies]
tags = [ "lxc", "docker", "système" ]
+++
((/39fmvg.jpg|))<!-- more -->Ceci ne fait pas exactement partie de la série sur mon auto-hébergement. Pour une raison assez simple : cet article a une portée un peu plus importante.
!!!Les conteneurs, cest bien, en abuser, ça craint !
LXC/LXD, ça devrait parler à tout le monde ici : cest un système permettant de faire des VE (''Virtual Environments''). A contrario des VM (''Virtual Machines''), les VE sont bien plus légers : ils ne se préoccupent pas de la couche de gestion du matériel.
Ainsi un Linux de base prendra entre 120 et 200 Mio de mémoire en VM, mais seulement 15 à 20 Mio de mémoire en VE.
Par contre, les VE sont bien plus limités : impossible de formater un système de fichiers, de monter un disque, etc… Ce nest pas fait pour ça. Cest fait essentiellement pour isoler un peu le système invité du système hôte, mais on continue à utiliser le noyau de lhôte (avec tous ces modules).
Docker aussi, ça devrait parler à tout le monde (au moins parce que cest la mode). Cest exactement pareil, mais lidée est ici de faire tourner un environnement qui ne fait lui-même fonctionner quun seul processus. Si le VE ressemble donc à une VM castré, Docker, cest simplement un moyen de distribuer un logiciel avec toutes ces librairies et dépendances dans un seul « pack ».
!!! LXC/LXD et les privilèges
Par défaut, LXC tourne en __non-privilégié__. En clair, ça veut dire que le ''root'' dans le conteneur est « squashé » vers un numéro bien plus haut au niveau de lhôte (généralement +100 000).
+++
title = "DMARC : toi aussi aide tes petits camarades à vérifier quils nont pas merdé (1/2)"
date = 2019-12-16 10:00:00
aliases = [ "post/2019/11/28/DMARC-:-toi-aussi-aide-tes-petits-camarades-à-vérifier-quils-nont-pas-merdé"]
+++
Parce que personne nest parfait, surtout pas toi…<!-- more -->DMARC, cest le machin qui est imposé maintenant par Google, Microsoft & Co dès que tu veux leur envoyer un message. Et en vrai, cest pas une si mauvaise idée que ça. Le principe de base est le suivant :
- on ta obligé à avoir du SPF, sinon on refuse tes messages. Mais le souci, cest que les redirections (fréquentes dans ce domaine), ça casse tout et on ne peut rien y faire ;
- on ta du coup obligé à avoir du DKIM. Ça signe tous les messages, et ça permet de sassurer queffectivement, ça vient bien de là où ça prétend venir.
On a donc, dune certaine manière, résolu une partie du problème : si le SPF merde pour une raison ou pour une autre, DKIM peut permettre de sassurer que les messages sont bien légitimes. Inversement, si DKIM merde (non-signature, message technique dun relais, etc…), on peut au moins sassurer que le message vient du bon endroit.
ais, il manque quand même un élément crucial à tout ça : que faire des messages « non-conformes » ? Qui va décider que les messages non-signés DKIM ou non-provenant de la bonne adresse SPF, faut les jeter à la poubelle ou pas ?
## DMARC to the rescue!!
Cest précisément là quintervient DMARC : lidée est de donner une politique générale sur ce quil faut faire si le message semble chelou. « Chelou » en DMARC, ça veut dire quil vient du mauvais endroit (donc viole la politique SPF) **ET** quil nest pas signé (donc viole la politique DKIM).
Et quand on y réfléchit bien, un tel message est effectivement tout sauf légitime : aucune raison daccepter un message non-signé qui vient dune IP russe^Wchinoise^Wde Google. En plus, DMARC permet de recevoir des rapports qui donnent des indications précieuses sur les messages qui ont été reçus par les destinataires.
Petite précision importante, cest bien un **ET** et pas un **OU** : le protocole prévoit le fait quun message peut être redirigé (donc violer sciemment la politique SPF, mais en conservant dans la plupart des cas la signature DKIM dorigine) ou que certains messages de service (abonné absent, mauvaise passerelle, etc…) peuvent ne pas être signés.
Tu vas donc pouvoir indiquer comment tu veux que tes messages soient traités en cas de souci, mais en plus, on va tinformer de ce qui a été fait et pourquoi. Ça peut aussi te permettre de repérer les ptits malins qui essaient de te barber au passage…
Et la politique DMARC en question, ça ressemble à quoi ? 3 formes possibles :
- ''none'' : tu fais rien, je veux juste avoir les rapports, cest tout
- ''quarantine'' : tu classes en pourriel
- ''reject'' : pas la peine daccepter les messages, visiblement, cest de la merde
Dans les deux derniers cas, tu peux même spécifier un pourcentage de message qui subiront ce traitement. Lidée est ici de permettre à de grosses organisations, qui envoient beaucoup de messages, de progressivement mettre en place le système et de permettre à leurs usagers de se rendre compte quils font de la merde.
## Bon, vas-y, tu me las vendue ta merde, comment je fais pour ça chez moi ?
La première chose à faire est de sassurer que tu peux recevoir les rapports DMARC des autres. Pour cela, cest relativement simple, il suffit dajouter un enregistrement de ce type dans ta zone :
```
_dmarc.tamere.lol. 3600\ IN\ TXT\ "v=DMARC1; p=none; rua=mailto:postmaster@tamere.lol;"\
```
Avec ça, tu précises :
- tu as une politique DMARC (doh!)
- tu précises quil ny a pas de politique pour le moment ''none''
- tu précises où tu veux recevoir les mails de rapport
Petite précision supplémentaire, si tu veux recevoir les rapports DMARC sur une adresse qui nest pas dans le domaine à protéger, tu dois rajouter dans le domaine en question un enregistrement de type TXT dans la zone de réception :
```
youplaboom.com._report._dmarc.tamere.lol. 21600 IN TXT "v=DMARC1;"
```
Dans cet exemple, à condition davoir posé un enregistrement ''_dmarc'' dans la zone `youplaboom.com`, tu peux recevoir les rapports avec une adresse en `@tamere.lol`.
Ces rapports viennent sous forme de fichiers XML zippés ou gzippés à ladresse indiquée. Ils ne sont pas bien compliqués à lire « à la main », mais il y a plein de logiciels en ligne qui permettent de visualiser de manière un peu plus simple les choses et de repérer plus facilement les erreurs de configuration.
Pour commencer, il est très fortement recommandé de laisser pendant quelques jours, voire quelques semaines, la politique à ''none''. Pour une raison très simple : ça permet de récupérer des données et de voir si ton serveur de courriels est correctement configuré. On se rend compte de belles conneries parfois en faisant ça : un relais bizarrement fait, des signatures DKIM qui ne correspondent pas, etc…
Une fois que tu es confiant dans ce que tu as dans ton serveur de messagerie, tu peux commencer à changer la politique et mettre quelque chose de plus restrictif. Tu peux même préciser un pourcentage de traitement (avec la commande `pct=<chiffre entre 0-100>`) pour dire que tu veux quun certain pourcentage de message soit traité de cette manière là. Lidée est toujours la même : aller le plus progressivement possible vers la politique la plus stricte possible, à savoir `p=reject;`.
## Protéger aussi les autres
Une bonne idée avant de fermer tout ça : si tu as des domaines dont tu ne te sers pas pour envoyer du courriel, tu as probablement grandement intérêt à préciser les élements suivants :
- SPF à `v=spf1 -all`
- DMARC à `v=DMARC1; p=reject; rua=mailto:postmaster@tamere.lol;`
Cela permet de préciser à tout ceux qui font du DMARC quil est **TOTALEMENT** anormal de recevoir des messages sur ce domaine. De cette manière, non seulement tu te protèges toi (ta réputation, les noms de domain que tu as acheté), mais tu protèges aussi les autres des cyber-squatteurs et autres enfoirés qui pourraient essayer de te barber…
## Conclusage
Voilà, tas déjà de quoi tamuser un moment et tu vas pouvoir voir non seulement si ta configuration est bonne dans tous les cas, mais en plus voir sil ny a pas des enfoirés qui essaient de te la mettre à lenvers.
Dans le prochain épisode, nous verrons comment faire pour envoyer des zolis rapports à tous nos correspondants histoire de rendre service à tout le monde en plus ;)
+++
title = "Journaliser les conversations qui passent par ses serveurs SMTP"
date = 2020-06-25 13:48:00
aliases = [ "post/2020/06/25/Journaliser-les-conversations-qui-passent-par-ses-serveurs-SMTP"]
+++
Parce que ça peut toujours servir<!-- more -->Récemment, je me suis un peu fritté avec Postfix et ElasticSearch et jai fini par me dire que ça pourrait en intéresser certains. Le challenge est assez simple en réalité : être capable de repérer très rapidement qui a parlé à qui et à quel moment.
Et en fait, cest pas aussi simple que ça.
## Au début, il y avait **syslog**
La base du problème nest finalement pas si compliqué que ça : quand on a un ou plusieurs serveurs SMTP, on peut commencer par balancer lensemble des logs dans **syslog**. Ça permet de les centraliser et ça permet également de faire des recherches croiser quand on en a plusieurs.
Jusque là, rien de bien sorcier. Je vous passe également le fait de balancer ces logs dans **Logstash**. Je ne dis pas que cest à la portée du premier con venu, mais ça dépasse quelque peu le cadre de cet article (je te rassure on va quand même en faire un peu, mais je ne détaillerai pas plus que ça).
Bref, je pars du principe que vous avez un **syslog** qui concentre tout et qui repasse le bébé à un **Logstash**.
## Ensuite, il fallu faire un peu le tri
alheureusement, comme souvent avec ELK, il va être nécessaire de faire le tri en amont pour ne pas se retrouver avec des grosses conneries un peu partout. Dans **Logstash**, il va falloir faire un **pipeline** et y appliquer des filtres. Donc, on va insérer ça dans le fichier `/etc/logstash/pipelines.yml` :
~~~
- pipeline.id: syslog
path.config: "/etc/logstash/syslog/conf.d/*.conf"
~~~
Ça permet de créer une configuration complètement à part pour **syslog** avec des entrées, des sorties et tout ce qui va bien, tout en faisant un truc à peu près propre. À partir de ce moment-là, on peut créer, dans `/etc/logstash/syslog/conf.d/`, deux fichiers `10_input.conf` et `90_output.conf` :
`10_input.conf` :
~~~
input {
udp {
port => 1234
type => "syslog"
}
}
~~~
`90_output.conf` :
~~~
output {
elasticsearch {
index => "syslog-%{+YYYY.MM.dd}"
template_name => "syslog-*"
}
}
~~~
**Si tu te poses la question, il faut que les fichiers soient lus dans le bon ordre pour faire les choses correctement, donc oui, je mets des chiffres en début de fichiers, cest moche mais au moins, je suis certain que la conf est lue dans le bon sens.**
Ça permet de créer une entrée (type **syslog**) et une sortie dans un **template** Elasticsearch. Jusque là, on na pas vraiment réinventé la mayonnaise, on prend juste des entrées et on les balance dans des sorties (et figurez-vous que ça fonctionne, cest un truc de gue-din). Bon, maintenant, on a certes des données, mais elles ne sont absolument pas triées.
En gros, on a une date et un message, mais cest à peu près tout. Et ça ne nous avance pas beaucoup. Donc, on va faire un premier filtre qui va permettre de récupérer les données **syslog** et de commencer à faire un premier tri. Tu peux faire un fichier intermédiaire (quon appellera `20_filter_10_begin.conf`) :
~~~
filter {
if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\\])?: %{GREEDYDATA:syslog_message}" }\
add_field => [ "received_at", "%{@timestamp}" ]
add_field => [ "received_from", "%{host}" ]
}
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
}
~~~
Voilà, maintenant, tu relances ton **Logstash** et tu devrais commencer à récupérer quelques données. Elles sont relativement brutes : tu as le serveur, la date et lheure, le nom du programme et cest à peu près tout. Ça peut déjà pas mal servir, mais on va quand même essayer daffiner un peu…
## Et après, on laisse affiner pendant quelques mois, comme pour le Beaufort
aintenant, tu vas [aller télécharger ces quelques fichiers](https://github.com/whyscream/postfix-grok-patterns) et les coller dans les bons répertoires (`/etc/logstash/syslog/patterns/` pour le fichier `.grok` et dans `conf.d` pour le reste, en faisant bien attention à le renommer pour quil soit lu après le reste, mais avant le **output**).
Une fois que tu as fait ça, tu peux de nouveau relancer **Logstash**. Ces modèles sont très très bien faits et permettent de vraiment trier complètement les différentes étapes de traitement de chaque message : mise en queue, traitement, livraison, nettoyage, etc…
Tu peux déjà tamuser dans linterface de Kibana pour essayer de retrouver certains messages, des destinataires, des expéditeurs et ainsi de suite :
![img](https://blog.libertus.eu/postfix_premier_tri.png)
Et rien quavec ça, tu peux déjà faire pas mal de merdes ! Tu peux faire des rapports permettant de voir le nombre de messages échangés (suffit de compter les **queueid** uniques), tu peux voir le volume moyen des messages échangés, etc…
Tout ça, cest très bien, mais à un moment, quelquun viendra forcément te poser la question : et comment je fais pour savoir qui a écrit à qui ?
## Ça a lair simple comme ça, mais en fait non…
Dit comme ça, ça a effectivement lair tout con et dans labsolu, tu te dis probablement que tu as la donnée, donc que ça ne devrait pas être si compliqué que ça à extraire. En fait, si. Le souci, cest que tu as effectivement la donnée : si tu tries en fonction du **queueid** de Postfix, tu peux effectivement savoir qui a écrit à qui. *Mais dun point de vue données, tu nas pas tout sur une seule ligne !*
Et Elasticsearch a ce côté un peu chiant : si tu nas pas toutes les infos dont tu as besoin sur la même ligne, dans le même document, dans le même enregistrement, alors faire des recoupements peut devenir extrêmement pénible. Alors évidemment, pour de la recherche manuelle, ce nest pas bien compliqué : tu cherches le destinataire, tu trouves le **queueid**, tu retries par **queueid** et tu finiras bien par tomber dessus.
ais à partir du moment où tu dois le confier à quelquun dautre ou simplement si tu veux faire des stats ou des rapports un peu plus poussés, bah ça va être très compliqué, voire impossible.
## Bah alors, on fait quoi du coup ? À part avoir lair con…
Jarrive mes petits bisous, jarrive. En fait, jai tourné en rond sur ce problème pendant un bon moment. Jai dabord cherché à voir si je pouvais faire des corrélations un peu complexes dans ELK lui-même. Ça na pas vraiment donné les résultats que jattendais (probablement pas impossible à faire, mais bien trop compliqué à mon goût). Il paraît que la version payant dELK sait faire pas mal de choses de ce point de vue là, mais je suis trop pauvre pour ça (pour un besoin pro, ça pourrait se justifier, pour un besoin perso, cest quand même plus compliqué).
Jai ensuite cherché comment faire du multiligne. Cest possible, et ça existe, il y a même des [plugins spéciaux](https://www.elastic.co/guide/en/logstash/current/plugins-codecs-multiline.html) pour ça. Il y a même [des gens qui lont déjà fait](https://github.com/whyscream/postfix-grok-patterns).
Le souci, cest que tout cela date un peu (beaucoup de fonctions ont changé) et que je ne suis jamais parvenu à le faire fonctionner correctement. Avec un peu de temps et defforts, cest peut-être possible de le faire fonctionner, mais sincèrement bon courage !
Bref, après avoir bien galéré sur le sujet pendant un moment, je me suis dit quil était peut-être possible denrichir les logs de Postfix avec dautres données. Si on narrive pas à traiter en aval, il vaut mieux revenir sur les données en amont pour essayer de pousser plus de choses ou de rendre les choses un peu différemment.
Première chose à faire donc, dire à Postfix de journaliser dautres informations sur les messages reçus. Par exemple, leur sujet : tous les messages ont un sujet, cela doit donc être possible de le journaliser. Et bien, oui et cest même relativement simple. Dans la configuration de Postfix (`main.cf`), il suffit dajouter cette ligne :
~~~
header_checks = regexp:/etc/postfix/header_checks
~~~
Puis de créer le fichier de **regexp** correspondant :
~~~
/^Subject:/ WARN
~~~
Là, on dit à Postfix de journaliser tout message contenant lentête **Suject** (donc tous les messages en gros) et de lui attribuer le niveau **WARNING**. On relance Postfix et là, voilà ce quon obtient :
~~~
Jun 25 16:32:57 smtp-1 postfix/cleanup[32674]: C9D27601B2: warning: header Subject: RE: I iz a subject from mail1.example.com[203.0.113.1]; from=<bidule@example.org> to=<truc@example.net> proto=ESMTP helo=<mail1.example.com>
~~~
\o/\
Non seulement Postfix a journalisé le sujet du message (ce qui est déjà très bien, ça nous permet de visualiser cela en une seule ligne), mais, coup de bol, il récupère aussi les autres informations du message en bonus. Et figure-toi que les filtres que tu as utilisé avant permettent de récupérer toutes ces informations directement dans ELK.
Dans Kibana, il suffit alors de mettre en place un petit filtre :
* `syslog_program: postfix`
* `postfix_message_level: warning`
* `postfix_to exists`
* `postfix_from exists`
Et tu vas récupérer exactement la liste de lensemble des messages qui sont passés par ton SMTP, avec le destinataire, lexpéditeur et le sujet du message :
![img](https://blog.libertus.eu/postfix_second_tri.png)
## Conclusage
Voilà, maintenant, tu as tout le nécessaire pour aller récupérer des données hyper précises sur tes SMTP et ça ne va pas être très compliqué de récupérer tous les messages dont machin est le destinataire ou tous les messages volumineux à destination de truc ou même faire des gros graphiques de ouf.

View File

@@ -0,0 +1,109 @@
+++
title = "Rust : jouer avec le typage fort"
date = 2019-10-24
aliases = [ "post/2019/04/24/RUST-:-jouer-avec-le-typage-fort"]
[taxonomies]
tags = [ "codaz", "rust" ]
+++
Pierre qui rouille… attendez, quoi ?!
<!-- more -->
Jai récemment appris Rust. Parce que le langage est marrant avec son concept dappartenance (*ownership*), sa gestion des erreurs très intelligentes, ses pointeurs intelligents (*smart pointers*). Parce que jaime bien apprendre des choses.
Et surtout parce que quand le shell nest plus suffisant, passer en python avait une certain tendance à me casser les noix : il faut des librairies installées ou il faut distribuer de grosses librairies avec certains programmes ou scripts alors quon peut simplement tout embarquer dans un exécutable Rust si on en a besoin.
Bref, cest cool.
Toutefois, Rust reste un langage fortement typé et quand on a lhabitude des langages faiblement typés (PHP, Python, etc…), cela peut nécessiter un sacré temps dadaptation pour sy remettre. Soyons clairs : jaime bien les langages fortement typés. Ça donne limpression de savoir ce que lon fait et ça permet déliminer un nombre substantiels de causes derreur. Mais cest aussi pas évident du tout à manipuler et ça prend nettement plus de temps pour arriver au même résultat quun langage faiblement typé.
![YAY](/.typing_m.png)
Je nai pas la prétention de vous apprendre à programmer en Rust. Même si jai commencé à faire des choses bien plus compliquées dans ce langage, jai débuté comme tout le monde. Et au moment de débuter, on bute toujours sur des choses stupides, comme ce que je vais vous présenter là et qui a, du coup, un rapport avec une particularité du langage, le typage fort.
Si vous êtes débutant en Rust, ou si vous ne connaissez pas Rust et que vous souhaitez lapprendre, ceci pourrait vous intéresser.
## Alors la marmotte, elle met le chocolat dans un entier 64 bits non-signé
Prenons un exemple tout con. On va faire une fonction dont le seul objectif est dafficher un nombre qui est donné en paramètre. Cette fonction pourrait sécrire comme ça :
```rust
fn print_number(a: u64) {
println!("{}", a);
}
```
Tu peux alors lutiliser de manières assez simple :
```rust
fn main() {
let a: u64 = 8;
print_number(a);
}
```
Basiquement, ça va fonctionner, mais cela pose quand même un gros souci : on ne peut passer à cette fonction que des `u64`. Or, `a` dans notre exemple pourrait très bien tenir dans un `u8`. Mais si lon fait, ça, ça ne fonctionnera pas :
```rust
fn main() {
let a: u8 = 8;
print_number(a);
}
error[E0308]: mismatched types
--> src/main.rs:9:18
|
9 | print_number(a);
| ^
| |
| expected u64, found u8
| help: you can convert an `u8` to `u64`: `a.into()`
```
Du point de vue de Rust, cest hyper cohérent : on lui dit quune fonction prend en paramètre un `u64`, on essaie de lui passer un `u8`, le compilateur gueule parce que ce nest pas normal. Ce nest pas le même type. Et même si dans labsolu un `u8` rentre parfaitement dans un `u64`, Rust sen moque : un type est un type et un type « compatible » reste un type différent.
Alors évidemment, tu peux tenter de caster systématiquement ta variable dans un `u64` (cest ce que propose le compilateur en fait). Pour cela, tu pourrais appeler la fonction de cette manière :
```rust
print_number(a as u64);
```
Ça règle effectivement le problème mais :
* ce nest pas forcément très élégant
* tu vas te retrouver à jongler avec des types alors que finalement, ça ne tintéresse pas des masses
Tu pourrais aussi imaginer de faire une fonction pour chaque type : `u64`, `u32`, `u16`, etc… mais franchement, ce serait hyper fastidieux.
Du coup, comment on fait ?
## Tu peux aller timplémenter Martine !!
En fait, on peut très facilement rendre cette fonction beaucoup plus universelle en remplaçant le type fixe par un ''Trait''. Lidée est ici de dire : finalement vu ce que je fais dans la fonction, ce qui mintéresse, cest que ce qui est passé en paramètre puisse safficher dans un `println!` pas tellement le fait que ce soit un nombre.
Et du coup, on peut réécrire cette fonction de la manière suivante :
```rust
fn print_number(a: impl std::fmt::Display) {
println!("{}", a);
}
```
À partir de ce moment, quelque soit ce quon passe dans la fonction (et même pas forcément un nombre !), tant que ce type implémente la fonction `fmt::Display`, ça fonctionnera !
Du coup, tu peux très bien imaginer de faire nawak :
```rust
fn main() {
let a: u8 = 8;
let b: u16 = 16;
let c: f64 = 3.1416;
print_number(a);
print_number(b);
print_number(c);
}
```
## Conclusage
Je voulais juste donner un tout petit exemple sur une problématique qui paraît hyper simple dans un langage faiblement typé, mais qui se révèle bien plus compliqué dès que le langage est fortement typé. Quand on commence à apprendre Rust, cest typiquement le genre de choses sur lequel on bute bêtement au départ parce que ce nest pas vraiment intuitif (en tout cas, pas tant quon a pas lu le chapitre sur le sujet dans le Book).
Quand on vient du C ou du PHP, cest le type de problématique qui paraît insurmontable au départ alors quen réalité, cest tout bête à résoudre. Et je trouve personnellement que Rust a une méthode particulièrement élégante pour le résoudre en prime et évidemment, on peut extrapoler cette méthode pour se donner bien plus de possibilités.

View File

@@ -0,0 +1,79 @@
+++
title = "DMARC : toi aussi aide tes petits camarades à vérifier quils nont pas merdé (1/2)"
date = 2019-12-16
aliases = [ "post/2019/11/28/DMARC-:-toi-aussi-aide-tes-petits-camarades-à-vérifier-quils-nont-pas-merdé"]
[taxonomies]
tags = [ "mail", "internet" ]
+++
Parce que personne nest parfait, surtout pas toi…
<!-- more -->
DMARC, cest le machin qui est imposé maintenant par Google, Microsoft & Co dès que tu veux leur envoyer un message. Et en vrai, cest pas une si mauvaise idée que ça. Le principe de base est le suivant :
- on ta obligé à avoir du SPF, sinon on refuse tes messages. Mais le souci, cest que les redirections (fréquentes dans ce domaine), ça casse tout et on ne peut rien y faire ;
- on ta du coup obligé à avoir du DKIM. Ça signe tous les messages, et ça permet de sassurer queffectivement, ça vient bien de là où ça prétend venir.
On a donc, dune certaine manière, résolu une partie du problème : si le SPF merde pour une raison ou pour une autre, DKIM peut permettre de sassurer que les messages sont bien légitimes. Inversement, si DKIM merde (non-signature, message technique dun relais, etc…), on peut au moins sassurer que le message vient du bon endroit.
ais, il manque quand même un élément crucial à tout ça : que faire des messages « non-conformes » ? Qui va décider que les messages non-signés DKIM ou non-provenant de la bonne adresse SPF, faut les jeter à la poubelle ou pas ?
## DMARC to the rescue!!
Cest précisément là quintervient DMARC : lidée est de donner une politique générale sur ce quil faut faire si le message semble chelou. « Chelou » en DMARC, ça veut dire quil vient du mauvais endroit (donc viole la politique SPF) **ET** quil nest pas signé (donc viole la politique DKIM).
Et quand on y réfléchit bien, un tel message est effectivement tout sauf légitime : aucune raison daccepter un message non-signé qui vient dune IP russe^Wchinoise^Wde Google. En plus, DMARC permet de recevoir des rapports qui donnent des indications précieuses sur les messages qui ont été reçus par les destinataires.
Petite précision importante, cest bien un **ET** et pas un **OU** : le protocole prévoit le fait quun message peut être redirigé (donc violer sciemment la politique SPF, mais en conservant dans la plupart des cas la signature DKIM dorigine) ou que certains messages de service (abonné absent, mauvaise passerelle, etc…) peuvent ne pas être signés.
Tu vas donc pouvoir indiquer comment tu veux que tes messages soient traités en cas de souci, mais en plus, on va tinformer de ce qui a été fait et pourquoi. Ça peut aussi te permettre de repérer les ptits malins qui essaient de te barber au passage…
Et la politique DMARC en question, ça ressemble à quoi ? 3 formes possibles :
- ''none'' : tu fais rien, je veux juste avoir les rapports, cest tout
- ''quarantine'' : tu classes en pourriel
- ''reject'' : pas la peine daccepter les messages, visiblement, cest de la merde
Dans les deux derniers cas, tu peux même spécifier un pourcentage de message qui subiront ce traitement. Lidée est ici de permettre à de grosses organisations, qui envoient beaucoup de messages, de progressivement mettre en place le système et de permettre à leurs usagers de se rendre compte quils font de la merde.
## Bon, vas-y, tu me las vendue ta merde, comment je fais pour ça chez moi ?
La première chose à faire est de sassurer que tu peux recevoir les rapports DMARC des autres. Pour cela, cest relativement simple, il suffit dajouter un enregistrement de ce type dans ta zone :
```
_dmarc.tamere.lol. 3600\ IN\ TXT\ "v=DMARC1; p=none; rua=mailto:postmaster@tamere.lol;"\
```
Avec ça, tu précises :
- tu as une politique DMARC (doh!)
- tu précises quil ny a pas de politique pour le moment ''none''
- tu précises où tu veux recevoir les mails de rapport
Petite précision supplémentaire, si tu veux recevoir les rapports DMARC sur une adresse qui nest pas dans le domaine à protéger, tu dois rajouter dans le domaine en question un enregistrement de type TXT dans la zone de réception :
```
youplaboom.com._report._dmarc.tamere.lol. 21600 IN TXT "v=DMARC1;"
```
Dans cet exemple, à condition davoir posé un enregistrement ''_dmarc'' dans la zone `youplaboom.com`, tu peux recevoir les rapports avec une adresse en `@tamere.lol`.
Ces rapports viennent sous forme de fichiers XML zippés ou gzippés à ladresse indiquée. Ils ne sont pas bien compliqués à lire « à la main », mais il y a plein de logiciels en ligne qui permettent de visualiser de manière un peu plus simple les choses et de repérer plus facilement les erreurs de configuration.
Pour commencer, il est très fortement recommandé de laisser pendant quelques jours, voire quelques semaines, la politique à ''none''. Pour une raison très simple : ça permet de récupérer des données et de voir si ton serveur de courriels est correctement configuré. On se rend compte de belles conneries parfois en faisant ça : un relais bizarrement fait, des signatures DKIM qui ne correspondent pas, etc…
Une fois que tu es confiant dans ce que tu as dans ton serveur de messagerie, tu peux commencer à changer la politique et mettre quelque chose de plus restrictif. Tu peux même préciser un pourcentage de traitement (avec la commande `pct=<chiffre entre 0-100>`) pour dire que tu veux quun certain pourcentage de message soit traité de cette manière là. Lidée est toujours la même : aller le plus progressivement possible vers la politique la plus stricte possible, à savoir `p=reject;`.
## Protéger aussi les autres
Une bonne idée avant de fermer tout ça : si tu as des domaines dont tu ne te sers pas pour envoyer du courriel, tu as probablement grandement intérêt à préciser les élements suivants :
- SPF à `v=spf1 -all`
- DMARC à `v=DMARC1; p=reject; rua=mailto:postmaster@tamere.lol;`
Cela permet de préciser à tout ceux qui font du DMARC quil est **TOTALEMENT** anormal de recevoir des messages sur ce domaine. De cette manière, non seulement tu te protèges toi (ta réputation, les noms de domain que tu as acheté), mais tu protèges aussi les autres des cyber-squatteurs et autres enfoirés qui pourraient essayer de te barber…
## Conclusage
Voilà, tas déjà de quoi tamuser un moment et tu vas pouvoir voir non seulement si ta configuration est bonne dans tous les cas, mais en plus voir sil ny a pas des enfoirés qui essaient de te la mettre à lenvers.
Dans le prochain épisode, nous verrons comment faire pour envoyer des zolis rapports à tous nos correspondants histoire de rendre service à tout le monde en plus ;)

View File

@@ -0,0 +1,150 @@
+++
title = "Journaliser les conversations qui passent par ses serveurs SMTP"
date = 2020-06-25
aliases = [ "post/2020/06/25/Journaliser-les-conversations-qui-passent-par-ses-serveurs-SMTP"]
[taxonomies]
tags = [ "mail", "système" ]
+++
Parce que ça peut toujours servir
<!-- more -->
Récemment, je me suis un peu fritté avec Postfix et ElasticSearch et jai fini par me dire que ça pourrait en intéresser certains. Le challenge est assez simple en réalité : être capable de repérer très rapidement qui a parlé à qui et à quel moment.
Et en fait, cest pas aussi simple que ça.
## Au début, il y avait **syslog**
La base du problème nest finalement pas si compliqué que ça : quand on a un ou plusieurs serveurs SMTP, on peut commencer par balancer lensemble des logs dans **syslog**. Ça permet de les centraliser et ça permet également de faire des recherches croiser quand on en a plusieurs.
Jusque là, rien de bien sorcier. Je vous passe également le fait de balancer ces logs dans **Logstash**. Je ne dis pas que cest à la portée du premier con venu, mais ça dépasse quelque peu le cadre de cet article (je te rassure on va quand même en faire un peu, mais je ne détaillerai pas plus que ça).
Bref, je pars du principe que vous avez un **syslog** qui concentre tout et qui repasse le bébé à un **Logstash**.
## Ensuite, il fallu faire un peu le tri
alheureusement, comme souvent avec ELK, il va être nécessaire de faire le tri en amont pour ne pas se retrouver avec des grosses conneries un peu partout. Dans **Logstash**, il va falloir faire un **pipeline** et y appliquer des filtres. Donc, on va insérer ça dans le fichier `/etc/logstash/pipelines.yml` :
~~~
- pipeline.id: syslog
path.config: "/etc/logstash/syslog/conf.d/*.conf"
~~~
Ça permet de créer une configuration complètement à part pour **syslog** avec des entrées, des sorties et tout ce qui va bien, tout en faisant un truc à peu près propre. À partir de ce moment-là, on peut créer, dans `/etc/logstash/syslog/conf.d/`, deux fichiers `10_input.conf` et `90_output.conf` :
`10_input.conf` :
~~~
input {
udp {
port => 1234
type => "syslog"
}
}
~~~
`90_output.conf` :
~~~
output {
elasticsearch {
index => "syslog-%{+YYYY.MM.dd}"
template_name => "syslog-*"
}
}
~~~
**Si tu te poses la question, il faut que les fichiers soient lus dans le bon ordre pour faire les choses correctement, donc oui, je mets des chiffres en début de fichiers, cest moche mais au moins, je suis certain que la conf est lue dans le bon sens.**
Ça permet de créer une entrée (type **syslog**) et une sortie dans un **template** Elasticsearch. Jusque là, on na pas vraiment réinventé la mayonnaise, on prend juste des entrées et on les balance dans des sorties (et figurez-vous que ça fonctionne, cest un truc de gue-din). Bon, maintenant, on a certes des données, mais elles ne sont absolument pas triées.
En gros, on a une date et un message, mais cest à peu près tout. Et ça ne nous avance pas beaucoup. Donc, on va faire un premier filtre qui va permettre de récupérer les données **syslog** et de commencer à faire un premier tri. Tu peux faire un fichier intermédiaire (quon appellera `20_filter_10_begin.conf`) :
~~~
filter {
if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\\])?: %{GREEDYDATA:syslog_message}" }\
add_field => [ "received_at", "%{@timestamp}" ]
add_field => [ "received_from", "%{host}" ]
}
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
}
~~~
Voilà, maintenant, tu relances ton **Logstash** et tu devrais commencer à récupérer quelques données. Elles sont relativement brutes : tu as le serveur, la date et lheure, le nom du programme et cest à peu près tout. Ça peut déjà pas mal servir, mais on va quand même essayer daffiner un peu…
## Et après, on laisse affiner pendant quelques mois, comme pour le Beaufort
aintenant, tu vas [aller télécharger ces quelques fichiers](https://github.com/whyscream/postfix-grok-patterns) et les coller dans les bons répertoires (`/etc/logstash/syslog/patterns/` pour le fichier `.grok` et dans `conf.d` pour le reste, en faisant bien attention à le renommer pour quil soit lu après le reste, mais avant le **output**).
Une fois que tu as fait ça, tu peux de nouveau relancer **Logstash**. Ces modèles sont très très bien faits et permettent de vraiment trier complètement les différentes étapes de traitement de chaque message : mise en queue, traitement, livraison, nettoyage, etc…
Tu peux déjà tamuser dans linterface de Kibana pour essayer de retrouver certains messages, des destinataires, des expéditeurs et ainsi de suite :
![img](/postfix_premier_tri.png)
Et rien quavec ça, tu peux déjà faire pas mal de merdes ! Tu peux faire des rapports permettant de voir le nombre de messages échangés (suffit de compter les **queueid** uniques), tu peux voir le volume moyen des messages échangés, etc…
Tout ça, cest très bien, mais à un moment, quelquun viendra forcément te poser la question : et comment je fais pour savoir qui a écrit à qui ?
## Ça a lair simple comme ça, mais en fait non…
Dit comme ça, ça a effectivement lair tout con et dans labsolu, tu te dis probablement que tu as la donnée, donc que ça ne devrait pas être si compliqué que ça à extraire. En fait, si. Le souci, cest que tu as effectivement la donnée : si tu tries en fonction du **queueid** de Postfix, tu peux effectivement savoir qui a écrit à qui. *Mais dun point de vue données, tu nas pas tout sur une seule ligne !*
Et Elasticsearch a ce côté un peu chiant : si tu nas pas toutes les infos dont tu as besoin sur la même ligne, dans le même document, dans le même enregistrement, alors faire des recoupements peut devenir extrêmement pénible. Alors évidemment, pour de la recherche manuelle, ce nest pas bien compliqué : tu cherches le destinataire, tu trouves le **queueid**, tu retries par **queueid** et tu finiras bien par tomber dessus.
ais à partir du moment où tu dois le confier à quelquun dautre ou simplement si tu veux faire des stats ou des rapports un peu plus poussés, bah ça va être très compliqué, voire impossible.
## Bah alors, on fait quoi du coup ? À part avoir lair con…
Jarrive mes petits bisous, jarrive. En fait, jai tourné en rond sur ce problème pendant un bon moment. Jai dabord cherché à voir si je pouvais faire des corrélations un peu complexes dans ELK lui-même. Ça na pas vraiment donné les résultats que jattendais (probablement pas impossible à faire, mais bien trop compliqué à mon goût). Il paraît que la version payant dELK sait faire pas mal de choses de ce point de vue là, mais je suis trop pauvre pour ça (pour un besoin pro, ça pourrait se justifier, pour un besoin perso, cest quand même plus compliqué).
Jai ensuite cherché comment faire du multiligne. Cest possible, et ça existe, il y a même des [plugins spéciaux](https://www.elastic.co/guide/en/logstash/current/plugins-codecs-multiline.html) pour ça. Il y a même [des gens qui lont déjà fait](https://github.com/whyscream/postfix-grok-patterns).
Le souci, cest que tout cela date un peu (beaucoup de fonctions ont changé) et que je ne suis jamais parvenu à le faire fonctionner correctement. Avec un peu de temps et defforts, cest peut-être possible de le faire fonctionner, mais sincèrement bon courage !
Bref, après avoir bien galéré sur le sujet pendant un moment, je me suis dit quil était peut-être possible denrichir les logs de Postfix avec dautres données. Si on narrive pas à traiter en aval, il vaut mieux revenir sur les données en amont pour essayer de pousser plus de choses ou de rendre les choses un peu différemment.
Première chose à faire donc, dire à Postfix de journaliser dautres informations sur les messages reçus. Par exemple, leur sujet : tous les messages ont un sujet, cela doit donc être possible de le journaliser. Et bien, oui et cest même relativement simple. Dans la configuration de Postfix (`main.cf`), il suffit dajouter cette ligne :
~~~
header_checks = regexp:/etc/postfix/header_checks
~~~
Puis de créer le fichier de **regexp** correspondant :
~~~
/^Subject:/ WARN
~~~
Là, on dit à Postfix de journaliser tout message contenant lentête **Suject** (donc tous les messages en gros) et de lui attribuer le niveau **WARNING**. On relance Postfix et là, voilà ce quon obtient :
~~~
Jun 25 16:32:57 smtp-1 postfix/cleanup[32674]: C9D27601B2: warning: header Subject: RE: I iz a subject from mail1.example.com[203.0.113.1]; from=<bidule@example.org> to=<truc@example.net> proto=ESMTP helo=<mail1.example.com>
~~~
\o/\
Non seulement Postfix a journalisé le sujet du message (ce qui est déjà très bien, ça nous permet de visualiser cela en une seule ligne), mais, coup de bol, il récupère aussi les autres informations du message en bonus. Et figure-toi que les filtres que tu as utilisé avant permettent de récupérer toutes ces informations directement dans ELK.
Dans Kibana, il suffit alors de mettre en place un petit filtre :
* `syslog_program: postfix`
* `postfix_message_level: warning`
* `postfix_to exists`
* `postfix_from exists`
Et tu vas récupérer exactement la liste de lensemble des messages qui sont passés par ton SMTP, avec le destinataire, lexpéditeur et le sujet du message :
![img](/postfix_second_tri.png)
## Conclusage
Voilà, maintenant, tu as tout le nécessaire pour aller récupérer des données hyper précises sur tes SMTP et ça ne va pas être très compliqué de récupérer tous les messages dont machin est le destinataire ou tous les messages volumineux à destination de truc ou même faire des gros graphiques de ouf.

View File

@@ -0,0 +1,43 @@
+++
title = "Passage à Zola"
date = 2020-11-03
[taxonomies]
tags = [ "méta" ]
+++
Les choses ne se passent pas toujours comme on le souhaite… Mais cest pas grave !
<!-- more -->
Comme tu as pu le remarquer, les choses nont pas tout-à-fait le même aspect depuis quelques jours : je suis en effet passé dun blog [Dotclear](https://dotclear.org/) à un site statique généré par [Zola](https://www.getzola.org/).
# Les sites statiques en fait, cest bien
Cela faisait déjà quelques temps que je gérais lensemble des articles en Markdown plutôt quavec le système déditeur Wiki de Dotclear ou en HTML brut (ce que jai fait pendant quelques temps dailleurs aussi…). Et il se trouve que le plugin Markdown pour Dotclear est loin de donner satisfaction :
* les commentaires ne marchaient pas
* écrire du code était toujours une galère entre ce quon pouvait échapper, ce quon ne pouvait pas échapper, ce quil fallait absolument échapper
Ça ma donc donné envie de prendre une autre direction et vu le peu dactivité, maintenir un blog complet en PHP est en fait assez lourd : je me suis retrouvé à un moment à avoir plus de mises à jour à faire sur le site que darticles dans les brouillons (et pourtant, il y en a plein des articles dans les brouillons !!).
Là, je peux écrire nativement mon article dans *vi*, tout en étant certain que [Zola](https://www.getzola.org/) soccupera de tout nativement : les espaces insécables sont pris en compte, le code est correctement mis en valeur, les images sont redimenssionnées à la volée, etc…
En terme de ressource, cest une sacrée économie : moins de processus PHP à faire tourner, une base de données en moins sur le serveur (évidemment, comme jai plein de bordels qui tournent à côté de cela, ce nest pas ultra significatif non plus, mais bon, cest toujours ça de pris).
Et surtout, la tranquilité : plus besoin de se prendre la tête avec les questions de mise à jour de sécurité, plus de crainte quun mot de passe fuite, quune page soit attaquée, etc…
# Et du coup, ça se passe comment en fait ?
Cest en réalité assez simple. La commande `zola init` permet de créer lensemble des répertoires nécessaires au bon fonctionnement du blog. Il suffit alors dajouter les fichiers Markdown dans les répertoires `content/` et le tour est joué ! Il y a évidemment quelques subtibilités supplémentaires, mais guère plus : ajouter un thème, voir comment on trie les articles **et cest à peu près tout !**
Une fois, les premiers articles écrits (ou importés manuellement depuis dautres sources), on peut très facilement visualiser le résultat avec la commande `zola serve`. Elle crée un petit serveur HTTP local qui permet daller surfer sur le blog comme si on y était (à une exception près : les flux RSS sont complètement pétés, ce qui ma fait me gratter la tête pendant un bon moment).
Quand on est satisfait du résultat (et en fait, ça consiste essentiellement à savoir si le thème colle bien, les dates sont bonnes et quon na pas fait derreur de syntaxe niveau Markdown), on peut brutalement compiler le site via `zola build` et à ce moment le répertoire `public/` devient le site près à être téléversé sur ton serveur.
*Et voilà* comme disent nos camarades anglophones…
# Mais tu perds les commentaires quand même dans lopération…
Oui. Et je me rends bien compte que cest un peu dommage : pouvoir réagir de suite et directement auprès de lauteur dun article est quelque chose que je trouve important. Mais comme pour le reste : vu le peu de commentaires que je reçois en réalité, cest largement plus rentable pour moi de laisser tomber. Si tu as des remarques à me faire, tu trouveras certainement un moyen de me contacter dune manière ou dune autre. Au pire, je ferai peut-être une page de contact statique, si je trouve le courage de la faire un jour.
Sinon, et bien, ça restera comme ça et cest pas bien grave. Je peux toujours écrire des conneries, me faire des pense-bêtes personnels ou parler de sujet qui me tiennent à cœur. Et cest là, le plus important.

View File

@@ -0,0 +1,75 @@
+++
title = "Mes deux centimes : changer de disque dur avec btrfs"
date = 2020-11-16
[taxonomies]
tags = [ "btrfs", "système" ]
+++
Si tu te souviens bien, il y a fort longtemps, on lavait fait avec LVM. Cétait drôle mais finalement ptet pas autant quavec btrfs.
<!-- more -->
Salut les campeurs et haut les cœurs !
Tu as tes données sur un disque `btrfs` qui commence à avoir du plomb dans laile ? Tu te demandes comment tu vas transférer tous ses GIGA de pr0n sans devoir arrêter ton ordinateur préféré pendant des heures et des heures ? Et bien, je suis là pour tout grand coquin ;)
# La réalité est triste et sans recours
Je suis moi aussi arrivé à ce constat amer : un disque dur, ça meurt et relativement vite. Idéalement, il faudrait le changer avant quil donne des signes de faiblesse mais sur unu machine de bureau, comme la mienne, ce nest pas toujours évident de sen rendre compte à temps.
Et donc mon disque de 3Tio commence à ramer sévère et je me dis quil va forcément péter un de ses 4. `smartctl` ma confirmé quil avait déjà un pied dans la tombe, il est donc temps de faire quelque chose avant quil ne lâche au plus mauvais moment possible.
Bref, pour commencer, regardons un peu létat des différents disques systèmes :
```
> lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 149,1G 0 disk /
sdb 8:16 0 2,7T 0 disk /home
sdc 8:32 0 3,6T 0 disk
sr0 11:0 1 1024M 0 rom
```
Le premier disque est mon SSD sur lequel se trouve ma partition système `btrfs` `/` et quelques autres bricoles (notamment `/srv` pour des questions de perf quand jai des trucs à tester). Le nouveau disque a bien été reconnu comme `/dev/sdc` après un redémarrage donc tout va bien pour le moment.
Le coupable de lenteur est `/dev/sdb` que nous allons nous empresser de changer. Regardons déjà létat dans lequel il est point de vue *filesystem* :
```
# btrfs filesystem show /home
Label: 'HOME' uuid: dabf1951-c21e-4322-91c2-959f9680a903
Total devices 1 FS bytes used 2.46TiB
devid 1 size 2.73TiB used 2.48TiB path /dev/sdb
```
# Bon, on y va ?
Oui, mon petit roudoudou, on y va ! Première et seule étape :
```
# btrfs replace start /dev/sdb /dev/sdc /home
```
Assez simple en principe : le `device` source, le `device` de destination et le système de fichiers que lon cherche à tranférer. La commande se lance en arrière-plan, il ny a donc pas à sinquiéter pour le transfert de données en lui-même.
On peut très facilement vérifier le statut du transfert avec la commande suivante :
```
# btrfs replace status /home
2.0% done, 0 write errs, 0 uncorr. read errs
```
Cette commande en revanche est interactive et il faudra donc sortir à base de `ctrl+c`.
# Oui donc, et après ?
Bah cest tout mes loulous. Après, il ny a quà patienter jusquà ce que le transfert soit complet (en ce qui me concerne ça a pris 18 heures). Toujours pour mon cas, javais une dernière chose à faire : je suis parti dun disque de 3Tio et lai remplacé par un disque de 4Tio, il a donc fallu que jagrandisse le système de fichiers `btrfs` a posteriori.
La commande est relativement simple aussi :
```
# btrfs filesystem resize max /home
Resize '/home' of 'max'
```
Et voilà, ça va encore plus vite que LVM en fait !

View File

@@ -0,0 +1,173 @@
+++
title = "Redimensionner un disque OpenBSD"
date = 2021-01-11
[taxonomies]
tags = [ "système", "openbsd" ]
+++
Le sujet étant assez peu ou mal documenté, je me suis dit que ce serait intéressant de te faire partager mes notes.
<!-- more -->
# La base
Pour lexpérience (et surtout parce que cétait à peu près mon cas dusage), jai donc installé un OpenBSD 6.8 tout frais avec un disque dur de 16Gio et le partitionnement par défaut. Ce dernier met la partition `/home` en dernier sur le disque. Cest la seule que tu pourras facilement redimensionner (ça tombe bien, cest ce que je comptais faire).
Bref, donc on ne peut pas redimensionner nimporte quelle partition, seulement la dernière du disque en gros (un peu comme du partitionnement plus classique pour Linux, mais ça fait quelques années que je nen ai pas fait).
La situation initiale ressemble donc à cela :
```
# df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/wd0a 413M 64.5M 328M 16% /
/dev/wd0k 2.6G 2.0K 2.4G 0% /home
/dev/wd0d 546M 6.0K 518M 0% /tmp
/dev/wd0f 1.9G 1.1G 787M 58% /usr
/dev/wd0g 530M 2.0K 504M 0% /usr/X11R6
/dev/wd0h 1.8G 216K 1.7G 0% /usr/local
/dev/wd0j 5.1G 2.0K 4.8G 0% /usr/obj
/dev/wd0i 1.3G 2.0K 1.3G 0% /usr/src
/dev/wd0e 782M 5.6M 737M 1% /var
```
Et cest donc surtout le `/home` qui va nous intéresser.
# Arrêt, redémarrage, toussa
Première étape donc, arrêter la machine et redimensionner le disque sous-jacent. Je lai passé à 64G pour les besoins de la démonstration, mais évidemment, ça pourrait fonctionner avec nimporte quelle valeur (enfin presque, je suppose que si on met un trillion de péta-octets, ça va ptet moins bien passer…).
Au redémarrage, à linvite de boot, il faut préciser `bsd.rd`. Il sagit du disque de secours qui est inclus à linstallation (sauf si tu las enlevé mais je pars du principe que tu nes pas un sauvage à ce point-là) :
```
>> OpenBSD/amd64 BOOT 3.52
boot> bsd.rd
```
Une fois démarré, choisis loption `(S)hell` pour tomber sur un shell standard (`/bin/sh`). À ce stade, le `/dev` de notre OpenBSD nest pas encore peuplé, il va donc au moins falloir y mettre le disque en question. Si cest un disque SATA, ce sera `sd0` si cest un disque IDE, ce sera `wd0` (sur une VM, il y a de bonnes chances que ce soit `wd0` du coup) :
```
# cd /dev
# sh MAKEDEV wd
```
# Je suis venu pour redimensionner des partitions et botter des culs
On va pouvoir vérifier les partitions MBR du disque :
```
# fdisk wd0
Disk: wd0 geometry: 8354/255/63 [134217728 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
*3: A6 0 1 2 - 2087 254 63 [ 64: 33543656 ] OpenBSD
```
Comme tu peux le constater la géométrie du disque est déjà à la bonne dimension en nombre de secteur. Mais la partition 3 (la partition OpenBSD) nest pas encore tout-à-fait à jour. On va donc forcer `fdisk` à faire ce quil faut :
```
# fdisk -iy wd0
Writing MBR at offset 0.
# fdisk wd0
Disk: wd0 geometry: 8354/255/63 [134217728 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
*3: A6 0 1 2 - 8353 254 63 [ 64: 134206946 ] OpenBSD
```
La quantité de secteur du disque a doublé : nous avons bien alloué de lespace supplémentaire à la partition OpenBSD. En toute logique, `disklabel` devrait être capable de retrouver rapidement ces petits :
```
# disklabel wd0
# /dev/rwd0c:
type: ESDI
disk: ESDI/IDE disk
label: VBOX HARDDISK
duid: 71abc1c5e7522ff0
flags:
bytes/sector: 512
sectors/track: 63
tracks/cylinder: 255
sectors/cylinder: 16065
cylinders: 2088
total sectors: 134217728
boundstart: 64
boundend: 33543720
drivedata: 0
```
Donc là, techniquement, on a la bonne taille de disque, mais pas encore la bonne taille de partition (regarde bien le champs `boundend` pour ten rendre compte).
On va donc donner les indications nécessaires à `disklabel` pour mettre la frontière au bon endroit et agrandir la dernière partition du disque :
```
# disklabel -E wd0
Label editor (enter '?' for help at any prompt)
wd0> b
Starting sector: [64]
Size ('*' for entire disk): [33543656] *
wd0*> c k
Partition k is currently 5535936 sectors in size, and can have a maximum
size of 106209952 sectors.
size: [5535936] *
wd0*> p
OpenBSD area: 64-134217728; size: 134217664; free: 30
# size offset fstype [fsize bsize cpg]
a: 880288 64 4.2BSD 2048 16384 6822
b: 1310050 880352 swap
c: 134217728 0 unused
d: 1162688 2190432 4.2BSD 2048 16384 9011
e: 1653888 3353120 4.2BSD 2048 16384 12921
f: 4218208 5007008 4.2BSD 2048 16384 12960
g: 1130272 9225216 4.2BSD 2048 16384 8760
h: 3816448 10355488 4.2BSD 2048 16384 12960
i: 2891616 14171936 4.2BSD 2048 16384 12960
j: 10944224 17063552 4.2BSD 2048 16384 12960
k: 106209952 28007776 4.2BSD 2048 16384 12960
wd0*> w
wd0> q
No label changes.
```
Pour le détail : on commence par changer la taille du disque (histoire de lui faire reconnaître ce quil y a en plus), puis on change la taille de la partition `k` (qui correspond donc au `/home` mais surtout qui est la dernière partition, la seule que lon peut redimensionner). À partir de là, il ne reste plus quà faire grandir le système de fichiers correspondant :
```
# growfs /dev/rwd0k
We strongly recommend you to make a backup before growing the Filesystem
Did you backup your data (Yes/No) ? Yes
new filesystem size is: 26552488 frags
Warning: 41632 sector(s) cannot be allocated.
growfs: 51840.0MB (106168320 sectors) block size 16384, fragment size 2048
using 256 cylinder groups of 202.50MB, 12960 blks, 25920 inodes.
super-block backups (for fsck -b #) at:
5806240, 6220960, 6635680, 7050400, 7465120, 7879840, 8294560, 8709280,
[…]
105339040, 105753760
```
Dernière étape avant de valider que tout est bon : faire un petit `fsck` des familles histoire de vérifier que tout est correct.
```
# fdisk /dev/rwd0k
```
# Conclusage
Au démarrage suivant, on constate bien quon a récupéré de lespace disque en plus :
```
# df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/wd0a 413M 84.4M 308M 22% /
/dev/wd0k 49.0G 2.0K 46.6G 0% /home
[…]
```
Bon évidemment, si on doit redimensionner autre chose que la dernière partition (`/var` au pif), ça va être nettement moins simple et jaurais tendance à dire que tauras plus vite fait de tout réinstaller. Néanmoins, le partition automatique étant pas trop mal foutu, il sarrange pour avoir suffisamment de place partout et garder la partition qui va probablement satûrer le plus vite à la fin.
Cest loin dêtre une science exacte mais jusquà présent, je lai plutôt bien vérifié dans la pratique.

View File

@@ -0,0 +1,189 @@
+++
title = "Lincroyable aventure de Pulseaudio et la carte Nvidia"
date = 2021-02-26
[taxonomies]
tags = [ "système" ]
+++
«Et le magicien changea alors la configuration dAlsa pour le rendre compatible avec cette sortie HDMI de merde! »
<!-- more -->
# Bon, cest quoi encore ton problème?
Depuis un certain temps, ma carte graphique Nvidia a décidé de ne plus fonctionner correctement en sortie audio: de temps en temps, à la sortie de veille, plus de son; à dautre moment, les sortie stéréo HDMI sinverse (il y a deux écrans branchés dessus et de temps en temps le son part sur lun, de temps en temps sur lautre).
En fait, jai découvert que cette conne de carte avait un comportement assez bizarre:
* elle affiche 7 sorties (!) alors quelle nen a que 4 (1 DisplayPort, un HDMI, 2 DVI);
* elle désactive les sorties audio lors de la sortie de veille du PC;
* elle inverse régulièrement les sorties en question.
Donc, sous Alsa, au lieu de voir 4 sorties avec des dénominations bien précises, jen vois un paquet qui ont des numéros et bien évidemment rien nest vraiment consistant:
```
$ aplay -l | grep -i carte
carte 0: PCH [HDA Intel PCH], périphérique 0: VT1708S Analog [VT1708S Analog]
carte 0: PCH [HDA Intel PCH], périphérique 2: VT1708S Alt Analog [VT1708S Alt Analog]
carte 0: PCH [HDA Intel PCH], périphérique 3: VT1708S Digital [VT1708S Digital]
carte 1: NVidia [HDA NVidia], périphérique 3: HDMI 0 [HDMI 0]
carte 1: NVidia [HDA NVidia], périphérique 7: HDMI 1 [HDMI 1]
carte 1: NVidia [HDA NVidia], périphérique 8: HDMI 2 [HDMI 2]
carte 1: NVidia [HDA NVidia], périphérique 9: HDMI 3 [HDMI 3]
carte 1: NVidia [HDA NVidia], périphérique 10: HDMI 4 [HDMI 4]
carte 1: NVidia [HDA NVidia], périphérique 11: HDMI 5 [HDMI 5]
carte 1: NVidia [HDA NVidia], périphérique 12: HDMI 6 [HDMI 6]
carte 3: Mic [Samson Meteor Mic], périphérique 0: USB Audio [USB Audio]
```
La carte 0 est une carte son intégrée à la carte mère que je ne souhaite pas forcément utiliser. Mon idée est de tout faire passer en HDMI pour pouvoir branche les enceintes/le casque sur lécran et donc brancher éventuellement dautres choses dessus (ma console, un autre PC, etc…) sans avoir à tout recâbler à chaque fois. La carte 2 (qui ne se voit pas ici) est une Webcam: cest donc seulement une entrée, pas une sortie, doù son absence ici.
La carte 3 est un micro USB qui se trouve aussi avoir une sortie audio.
Le problème vient donc de la carte 1. Pour une raison que jignore complètement, elle indique 7 sorties (comme marqué plus haut) alors quelle nen a physiquement que 4. Le plus étrange, cest que `xrandr` aussi mindique 6 sorties:
```
$ xrandr | grep connected
DVI-I-0 disconnected (normal left inverted right x axis y axis)
DVI-I-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 connected 1080x1920+0+0 left (normal left inverted right x axis y axis) 344mm x 194mm
DP-0 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
DVI-D-0 connected primary 1920x1080+1080+0 (normal left inverted right x axis y axis) 509mm x 286mm
```
Il y a donc des sorties factices ou des sorties internes (?) qui sont captés par Alsa et derrière par Pulseaudio comme étant des sorties réelles mais qui ne font en réalité rien du tout. Les deux écrans que jai (HDMI-0 et DVI-D-0) ont la capacité de faire sortir du son (via des petits hauts-parleurs et une sortie jack).
Voilà, le problème est posé: comment faire pour que cette putain de carte de merde sorte du son seulement sur lécran en DVI et y compris lors de la sortie de veille du PC et de manière consistante si possible?
# Lapproche par Pulseaudio
La vision côté Alsa, tu peux la voir plus haut. La vision côté Pulseaudio nest pas beaucoup plus complexe en réalité:
```
$ pacmd list-cards
4 card(s) available.
index: 0
name: <alsa_card.pci-0000_01_00.1>
driver: <module-alsa-card.c>
owner module: 5
properties:
[…]
profiles:
output:hdmi-stereo: Sortie Digital Stereo (HDMI) (priority 5900, available: unknown)
output:hdmi-stereo-extra1: Sortie Digital Stereo (HDMI 2) (priority 5700, available: unknown)
output:hdmi-stereo-extra2: Sortie Digital Stereo (HDMI 3) (priority 5700, available: no)
output:hdmi-surround-extra2: Sortie Digital Surround 5.1 (HDMI 3) (priority 600, available: no)
output:hdmi-surround71-extra2: Sortie Digital Surround 7.1 (HDMI 3) (priority 600, available: no)
output:hdmi-stereo-extra3: Sortie Digital Stereo (HDMI 4) (priority 5700, available: no)
output:hdmi-surround-extra3: Sortie Digital Surround 5.1 (HDMI 4) (priority 600, available: no)
output:hdmi-surround71-extra3: Sortie Digital Surround 7.1 (HDMI 4) (priority 600, available: no)
output:hdmi-stereo-extra4: Sortie Digital Stereo (HDMI 5) (priority 5700, available: no)
output:hdmi-surround-extra4: Sortie Digital Surround 5.1 (HDMI 5) (priority 600, available: no)
output:hdmi-surround71-extra4: Sortie Digital Surround 7.1 (HDMI 5) (priority 600, available: no)
output:hdmi-stereo-extra5: Sortie Digital Stereo (HDMI 6) (priority 5700, available: no)
output:hdmi-surround-extra5: Sortie Digital Surround 5.1 (HDMI 6) (priority 600, available: no)
output:hdmi-surround71-extra5: Sortie Digital Surround 7.1 (HDMI 6) (priority 600, available: no)
output:hdmi-stereo-extra6: Sortie Digital Stereo (HDMI 7) (priority 5700, available: no)
output:hdmi-surround-extra6: Sortie Digital Surround 5.1 (HDMI 7) (priority 600, available: no)
output:hdmi-surround71-extra6: Sortie Digital Surround 7.1 (HDMI 7) (priority 600, available: no)
off: Éteint (priority 0, available: unknown)
active profile: <alsa_output.pci-0000_01_00.1.hdmi-stereo>
sinks:
alsa_output.pci-0000_01_00.1.hdmi-stereo/#0: GM204 High Definition Audio Controller Digital Stereo (HDMI)
alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1/#3: GM204 High Definition Audio Controller Digital Stereo (HDMI 2)
alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2/#4: GM204 High Definition Audio Controller Digital Stereo (HDMI 3)
[…]
```
Il y a donc pas mal de profils disponibles: pour chaque sortie physique, un profil Surround, un profil Surround 7.1 et un profil Stéréo. Il y a également un profil `off` qui permet déteindre complètement la carte et dempêcher toute sortie son par là.
Lapproche naïve consiste donc à se dire: il suffit dutiliser un petit logiciel comme `pavucontrol` pour régler lensemble des sorties et entrées de la carte, déteindre les sorties qui ne nous intéressent pas, et le tour est joué. Et en effet, dans un premier temps, ça a marché: jai choisi le profil qui va bien pour sortir le son sur lécran principal et le tour était joué.
Ça na pas réglé mon souci de son absent en sortie de veille (on y reviendra), mais effectivement ça «marche». Sauf quau premier redémarrage, sans rien toucher physiquement évidemment, Pulseaudio semmêle les saucisses et la sortie HDMI3 devient la sortie HDMI2, la sortie HDMI4 devient la 1, etc…
**Impossible davoir un résultat probant avec cette méthode!!**
## Forcer le hardware
Ma première idée était donc de forcer une corrélation stricte entre le *hardware* (les sorties audio telle quelles sont vues par Alsa) et la partie Pulseaudio. Pour ce faire, on peut passer par le fichier `/etc/pulse/default.pa`. Première chose, repérer quelle sortie correspond à lécran. Pour cela, on utilise la commande `aplay` avec les bonnes options:
```
$ aplay -D plughw:1,3 /usr/share/sounds/alsa/Front_Right.wav
```
Voilà, tu fais ça avec chaque sortie (voire la sortie de la commande `aplay -l` plus haut) jusquà ce que tu trouves la sortie de ton écran. À partir de là, tu peux ajouter la commande suivante dans le fichier `/etc/pulse/default.pa`:
```
load-module module-alsa-sink device=hw:1,7
```
Ce fichier est le fichier de configuration par défaut de Pulseaudio, qui prend uniquement des commandes `pacmd`, et qui est lu par Pulseaudio à chaque fois quil crée un profil utilisateur. Ça, ça veut dire que cest juste une configuration par défaut et pas autre chose. Pour la prendre en charge, il faut donc arrêter `pulseaudio`, supprimer la configuration utilisateur et redémarrer `pulseaudio`. Ça se fait comme ça:
```
$ pulseaudio --kill; sleep 1; rm -rf .config/pulse; pulseaudio --start
```
Bien évidemment toute configuration personnalisée via `pavucontrol` ou autre sera perdue :niais:.
Je te coupe tout de suite le suspense: ça ne résiste pas à un redémarrage. Cest assez étonnant dailleurs parce que les indices hardware ne semblent pas particulièrement changer: Alsa détecte toujours la sortie 7 comme étant la sortie 7.
Mais bien sûr, côté Pulseaudio, il y a une sorte de renumérotation qui fait que `alsa_output.pci-0000_01_00.1.hdmi-stereo` va changer de sortie vis-à-vis dAlsa (et pareil pour `alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1` et ainsi de suite).
## Un simple problème de persmission
Visiblement, il traîne dans les archives du code de Pulseaudio un certain nombre de bêtises et notamment le fait quun groupe serait codé en dur dedans: si lutilisateur nest pas dans le groupe `audio`, ya plein de trucs qui ne marcheraient pas.
Bon, on ne va pas se cacher: ça ne marche pas forcément mieux.
## Premières conclusions
À partir de ce moment, jen ai conclu quil nétait pas possible de fixer véritablement la sortie audio: Pulseaudio ne détecte pas les sorties dans le bon ordre et même en le forçant, visiblement il fait un peu ce quil veut (soit au démarrage, soit en sortie de veille). Jaurais tendance à penser que le processus de détection nest malheureusement pas déterministe.
**Clairement, le souci vient de Pulseaudio cependant**, Alsa semble vraiment détecter les sorties audio toujours dans le même ordre. Il est aussi possible que le problème vienne des pilotes Nvidia qui ferait nimporte quoi avec la détection des sorties. Ou il est encore possible que ce soit une combinaison des deux. Quoiquil arrive, il est clair que je narriverai pas à solutionner le problème avec juste de la configuration.
# Et si on sortait le son partout sans réfléchir?
À partir de ce moment, je pense avoir essayé toutes les possibilités «rationnelles» sur ce problème. Et quand on a essayé toutes les possibilités rationnelles, il faut commencer à penser autrement. Je me suis donc dit: pourquoi ne pas sortir le son sur toutes les sorties HDMI en même temps? Finalement, ce serait le plus simple: quel que soit la configuration de Pulseaudio, la détection, lordre des sorties, etc… tout sera de toutes manières capables de sortir du son et il ny aura plus quà rendre les sorties inutiles muettes manuellement.
Sauf que par défaut, Pulseaudio na pas de profil pour sortir sur toutes les sorties HDMI en même temps.
## Profil Pulseaudio
Du coup, il faut aller dans le fichier `/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf`, à la toute fin, il y a un exemple impliquant un profil multi-sorties, cest donc de celui-ci quon va se servir:
```
[Profile forced-hdmi-stereo]
description = Foobar
output-mappings = hdmi-stereo hdmi-stereo-extra1 hdmi-stereo-extra2
input-mappings =
```
Cest assez bête basiquement: ce profil dit simplement que toutes les sorties Pulseaudio peuvent être utilisées en même temps. Maintenant, ce nest pas parce quelles peuvent être utilisées en même temps quelles le seront. En fait, si tu sélectionnes le profil `Foobar` dans longlet Configuration de `pavucontrol`, tu peux alors zapper entre les différentes sorties sans repasser par un profil particulier.
Sauf que pour le moment, cest tout ce que ça permet de faire: nous avons un profil qui peut interagir avec toutes les sorties mais pas sortir simultanément le son sur toutes ces sorties.
## Ajout dune sortie multiple
Pour arriver à faire une sortie multiple, il faut utiliser un autre module Pulseaudio: `combined-sink`. Dans Pulseaudio un `sink` est une sortie. Ce que nous avons fait précédemment, cest créer un profil disposant de plusieurs `sinks`. Il faut donc se débrouiller pour les combiner. Encore une fois, ça se passe dans le fichier `/etc/pulse/default.pa`:
```
set-card-profile 0 forced-hdmi-stereo
load-module module-combine-sink sink_name=combined slaves=alsa_output.pci-0000_01_00.1.hdmi-stereo,alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1,alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2
set-default-sink combined
```
La première ligne sert simplement à indiquer à Pulseaudio que la carte Nvidia (qui est détectée systématiquement comme carte 0 dans mon cas) est la carte part défaut. La seconde ligne crée un nouveau `sink` (une sortie donc) qui combine toutes les sorties de la carte (pour obtenir le nom des différents sorties, il suffit dutiliser la commande `pactl list-sinks`). De cette manière, si les sorties sont interverties par Pulseaudio, quelque soit la condition, toutes les sorties HDMI de la machine recevront du son. Voilà, cest à peu près aussi con que ça.
La dernière ligne sert juste à sélectionner cette sortie par défaut.
# Bon OK, tas du son mais est-ce que ten as toujours en sortie de veille?
Non toujours pas :/
Cétait un peu prévisible: jai deux problèmes en fait. Le premier est maintenant réglé, pour le second voilà ce quil en est… Pulseaudio semble refaire une détection complète à la sortie de veille (dans le cadre dun PC Portable, ça paraît logique en fait) et la carte Nvidia nest donc pas détectée suffisamment vite.
En fait, si à la sortie de veille, je vais dans `pavucontrol`, je peux très bien la rallumer là et elle marche parfaitement bien après. Le souci, cest que je nai pas du tout envie de faire ça.
De là, je me suis dit que ce nétait pas forcément un souci de bascule mais peut-être de détection détat: à la sortie de veille, la carte vidéo met peut-être un peu de temps à redétecter les écrans (en HDMI, ça ne serait pas surprenant) et Pulseaudio part donc du principe quil faut quil bascule les sources et les sorties ailleurs pour éviter de couper le son. Lintention est louable mais en loccurence, cest un peu inutile pour mon cas.
Jai donc simplement désactivé le module qui soccupe de ça, en loccurence `module-switch-on-port-available`. Pour le désactiver, il suffit de le commenter dans le fichier `/etc/pulse/default.pa`.
# Conclusage définitif
Putain, ça fait quand même beaucoup de bordel pour arriver à faire fonctionner les choses correctement. Je comprends le principe de faire de la détection dynamique systématiquement, je pense que cest une bonne chose dans labsolu, mais je pense quil faut aussi laisser la possibilité à lutilisateur de fixer un certain nombre de choses si ça foire ou si ça nest pas adapté à la situation.
Ce que jai fait là ressemble quand même largement plus à du hack quà de la vraie configuration (surtout la partie multiple sorties à vrai dire), mais je suis assez satisfait du résultat: ça fonctionne à tous les coups et sans faire des pieds et des mains ou sagiter dans la conf ou dans les menus ou commandes à chaque sortie de veille.

View File

@@ -0,0 +1,290 @@
+++
title = "Rust: faire joujou avec Async"
date = 2021-04-25
[taxonomies]
tags = [ "rust", "codaz" ]
+++
Quest-quon veut? *Maintenant!*
Et quand est-ce quon le veut? *De lasync!*
<!-- more -->
# Quest-ce que cest encore que ce machin?
Si tu fais un peu de Rust ces derniers temps, tu as du voir tout un tas de librairies se convertir à l`async` sans forcément comprendre pourquoi. Async/await, cest juste une manière de faire des choses de manières concurrentes. En gros, au lieu de chercher à exécuter le programme dans lordre, on va chercher à faire des actions un peu dans le désordre pour éviter de bloquer le thread principal.
Tu vas alors me dire: *mais on a déjà de la programmation parallèle pour ça non?* Et ça, je répondrais oui. Sauf quen fait, non. Dans des threads classiques, toutes les actions sont effectuées en **parallèle** (pense: plusieurs processeurs exécutant des actions complètement différentes). En **asynchrone**, lidée est dexécuter tout ça dans le même thread mais en profitant des temps morts à droite à gauche pour essayer de gagner du temps. Lexemple typique est celui de lécriture dans un fichier: cest une opération qui bloque le thread dexécution pendant longtemps pour rien : le processeur ne glande rien et attend juste le disque, donc autant lui faire exécuter dautres choses en attendant que ça passe et récupérer linformation sur lécriture du fichier un peu plus tard.
Bon, ça évidemment, cest la jolie théorie quon décrit un peu partout. Mais concrètement comment ça se passe?
# Mettre des choses dans Async/Await, bah en fait, cest pas si simple que ça…
Si je reprends lexemple donné sur le site de [tokio](https://tokio.rs/tokio/tutorial/hello-tokio) à part mettre des `await` partout, tu fais pas grand-chose dasynchrone là-dedans. Et en fait, cest vrai : basiquement, ça ne fait rien dasynchrone. En fait, `async/await` en Rust peut effectivement commencer à exécuter des trucs en asynchrone sans quon lui demande mais à un moment, on est bien obligé de récupérer le résultat.
Donc si ton programme fait juste des `await` toutes les 3 lignes, bah en fait, tu gagnes à peu près rien…
Ça commence bien…
# Allez, un exemple pour essayer
Comme tu peux éventuellement commencer à ten douter, jai passé un bon moment à me gratter la tête avec ses histoires dasync, sans vraiment comprendre lintérêt que ça avait (oui, on a déjà du parallèle si besoin donc bon…). Donc, voilà, je te propose un petit exemple qui permet de mettre en lumière certaines choses. Ça nest pas une réponse universelles à tout, ça nest peut-être même pas un bon exemple, mais disons que cest là que je suis arrivé après plusieurs heures de recherche et de tatônnement (jai aussi pu constater que les ressources disponibles sur la toile sur le sujet était soit obsolètes, soit pas vraiment claires).
## Bon admettons…
Donc, admettons quon ait 4 API en entrée (nous lappellerons *API entrante*) quon cherche à interroger. Ces 4 API vont renvoyer un nombre arbitraire de nombres et on va les envoyer vers une autre API (que nous appellerons *API sortante*) qui va déterminer si les nombres sont pairs ou impairs (oui, je sais cest complètement con, mais cest pour les besoins de lexercice). Pour bien se rendre compte du fonctionnement du truc, on va admettre que les API mettent énormément de temps à répondre (plusieurs secondes dans certains cas). Lidée est donc de voir si on peut optimiser et paralléliser.
Voici donc le code permettant de demandes des infos à lAPI entrante:
```rust
async fn poll() -> Vec<u32> {
let rand_waiter = rand::random::<u8>()/20; // on calcule un nombre aléatoire compris en 0 et 12
println!("poll: Waiting {}s for poll response…", rand_waiter);
sleep(Duration::from_secs(rand_waiter.into())).await; // on attend exactement ce nombre
// on génère autant de nombre sur 32 bits quon a attendu de secondes
let mut polling_res = vec![];
for _ in 0..rand_waiter {
let res = rand::random::<u32>();
polling_res.push(res);
}
println!("poll: Finished polling!");
polling_res
}
```
*Évidemment, nous utilisons ici les primitives `Duration` et `sleep` provenant de `tokio`.*
Notre API sortant va du coup ressembler à ça:
```rust
async fn treat(n: u32) -> bool {
let rand_waiter = rand::random::<u8>()/10; // on calcule un nombre aléatoire entre 25
println!("treat: Treating {}", n);
println!("treat: Waiting {}s for treatment response…", rand_waiter);
sleep(Duration::from_secs(rand_waiter.into())).await;
println!("treat: Finished treating {}", n);
n%2 == 0
}
```
## Approche naïve
Lapproche ultra de base consiste donc à mettre les 4 API entrantes dans un tableau, à les interroger une par une et ensuite pour chaque résultat reçu (tous les résultats arrivent en même temps en loccurence, on attend simplement que chaque API entrante réponde lensemble de ses résultats), on les balance dans lAPI sortante. On pourrait donc imaginer un code de ce type:
```rust
async fn main() {
let apis = vec!["A","B","C","D"];
for api in apis {
let numbers = poll().await;
for number in numbers {
let res = treat(number).await;
if res {
println!("main: {} was a good one, {}!", res, api);
} else {
println!("main: nah {} no good, better luck next time, {}", res, api);
}
}
}
}
```
Et quand on exécute, on constate effectivement que tout est parfaitement synchrone (et donc complètement con):
```
poll: Waiting 3s for poll response…
poll: Finished polling!
treat: Treating 4097894384
treat: Waiting 7s for treatment response…
treat: Finished treating 4097894384
main: true was a good one, A!
treat: Treating 4056821172
treat: Waiting 14s for treatment response…
treat: Finished treating 4056821172
main: true was a good one, A!
[…]
poll: Waiting 2s for poll response…
poll: Finished polling!
treat: Treating 4156474264
treat: Waiting 20s for treatment response…
treat: Finished treating 4156474264
main: true was a good one, D!
treat: Treating 1467559148
treat: Waiting 13s for treatment response…
treat: Finished treating 1467559148
main: true was a good one, D!
```
Ça asynchrone donc pas des masses :/. En fait, le souci vient du fait quon attend le résultat de chaque opération. Pour la première opération, on ne peut pas vraiment faire autrement (toutes les réponses arrivent en même temps de la partie de lAPI entrante). Par contre, pour le traitement, on pourrait imaginer de le faire en retirant le if derrière. Ça donnerait donc un truc de ce style:
```rust
for number in numbers {
treat(number);
}
```
Et là, tu te rends compte de la principale particularité de limplémentation d`async` dans Rust: si tu ne lui demandes rien, un `future` (donc une fonction `async` ici) ne sexécute pas. **En gros, si tu lattends pas, il fait rien ce con.**
## Bordel, mais comment on va résoudre ça???
Il va falloir mettre notre traitement dans une autre fonction qui va se charger de le faire avancer pendant quon fait autre chose. On ne peut pas le coller juste dans un block `async`, il faut le coller dans une fonction qui va le faire avancer (en gros). Donc on peut utiliser `tokio::spawn` dans la librairie `tokio` pour ce faire:
```rust
tokio::spawn(async move {
treat(number).await;
});
```
Là, ce nest plus gênant de déclencher lattente du résultat parce que finalement, le résultat est attendu dans une sorte de thread détaché du thread principal (mais pas vraiment un thread puisque cest pas du parallèle…). Et donc, là, ça fonctionne nettement mieux:
```
poll: Waiting 7s for poll response…
poll: Finished polling!
poll: Waiting 1s for poll response…
treat: Treating 1725139708
treat: Waiting 14s for treatment response…
treat: Treating 2961643129
treat: Waiting 22s for treatment response…
treat: Treating 3490534924
treat: Waiting 7s for treatment response…
treat: Treating 481316326
treat: Waiting 23s for treatment response…
treat: Treating 3335438155
treat: Waiting 9s for treatment response…
treat: Treating 2314014323
treat: Waiting 14s for treatment response…
treat: Treating 4156517114
treat: Waiting 0s for treatment response…
treat: Finished treating 4156517114
poll: Finished polling!
poll: Waiting 10s for poll response…
[…]
poll: Finished polling!
treat: Treating 279666034
treat: Waiting 22s for treatment response…
treat: Treating 945860350
treat: Waiting 23s for treatment response…
treat: Treating 1248492005
treat: Waiting 24s for treatment response…
```
Oups… On traite donc bien en parallèle, mais le souci cest quon attend pas vraiment que le traitement soit fini. **En gros, non seulement il faut le lancer dans un morceau de code à part, mais à un moment, il faut se poser la question de comment on attend que le morceau de code en question ait fini dexécuter.** Bon, en plus, en loccurence, on ne traite pas vraiment comme on le devrait. En fait, idéalement, il faudrait lancer la consultation de lAPI entrante en parallèle aussi (parce que là, ce nest pas fait), puis lancer le traitement de chaque résultat en parallèle, puis attendre que lensemble ait fini avant de sortir complètement.
# *craquement de doigts*
Une première approche consiste donc à lancer en parallèle lensemble des traitement initiaux sur lAPI entrante, de récupérer ses résultats et de les transmettre au thread principal pour quil puisse faire les traitements. De cette manière, ça ira un peu plus vite dans le sens où lon peut déjà mettre en attente notre volontairement-super-lente API entrante. Pour cela, il va falloir insérer un composant de communication entre ses différents thread. Dans les faits, ça marche exactement de la même manière que les threads classiques de Rust avec des transmetteurs, un récepteur et des messages (dans le cas présent, il existe bien sûr dautres types de messages et de mode de transmission). Si lon veut pouvoir préciser à quelle API on parle, il va aussi falloir créer une nouvelle structure qui va permettre de transmettre le nom de lAPI entrante en même temps que son résultat.
*Ce dernier point est juste cosmétique, on pourrait très bien faire sans, mais je trouve ça plus zoli avec…*
Voilà ce que ça va donner:
```rust
#[derive(Debug, Clone)]
struct ResAPI {
api: String,
id: u32,
}
#[tokio::main]
async fn main() {
let apis = vec!["A","B","C","D"];
let api_len = apis.len();
// on crée un canal de communication
let (tx, mut rx) = mpsc::channel(api_len);
for api in apis {
// quon clone pour chaque API entrante
let tx = tx.clone();
tokio::spawn(async move {
let vu32 = poll().await;
for v in vu32 {
let ra = ResAPI {
api: api.to_string(),
id: v,
};
// on envoie le résultat formé
tx.send(ra).await.unwrap();
}
});
}
// on détruit explicitement le canal de communication restant (celui qui na jamais été cloné
// en fait)
drop(tx);
while let Some(m) = rx.recv().await {
println!("main: Receiving {} from {}", m.id, &m.api);
let res = treat(m.id).await;
if res {
println!("main: {} was a good one, {}!", res, &m.api);
} else {
println!("main: nah {} no good, better luck next time, {}", res, &m.api);
}
}
}
```
Quand on exécute, ça fonctionne effectivement bien mieux. On constate bien que les 4 API entrantes sont consultées en même temps. On constate également que la première API qui a terminé va déclencher la suite des opérations. Mais… mais on a toujours un souci: on attend systématiquement le traitement de lAPI sortante et on perd du coup beaucoup de temps. Idéalement, il faudrait également rendre asynchrone le traitement des résultats.
# Où lon se rend compte que les grands principes de la science sont constants
Les mêmes causes produisant les mêmes effets, si lon ne fait que `spawn` des traitements de lAPI sortante, ça va effectivement paralléliser les traitements, mais on va en perdre au passage (tout traitement non terminé lorsque le thread principal meurt, mourra avec lui). On pourrait reprendre la même stratégie que précédemment mais dabord, ce ne serait pas drôle et ensuite, on est quand même obligé de déterminer la capacité dun canal de communication **avant** de louvrir. Dans notre cas, ça pourrait fonctionner puisquon connait à lavance le nombre maximum de résultalts, mais on pourrait imaginer des cas où lon en est pas capable (ou en tout cas, pas suffisamment précisément).
Donc, on va essayer une autre stratégie: lancer lensemble des threads de traitement dans un coin, stocker leur représentation dans un vecteur et simplement attendre jusquà ce que lensemble du vecteur ait terminé. Malheureusement `tokio` ne propose pas une telle fonction, il va donc falloir aller taper dans une autre librairie `futures` qui elle nous offre `join_all` permettant de faire exactement ce que lon veut. Voyons les modifs de code à partir de la boucle de réception:
```rust
// on crée un vecteur permettant de stocker tout ce petit monde
let mut tasks: Vec<_> = vec![];
while let Some(m) = rx.recv().await {
println!("main: Receiving {} from {}", m.id, &m.api);
// on lance des tâches comme précédemment
let task = tokio::spawn(async move {
println!("main: Beginning treatment for {} from {}", m.id, &m.api);
let t = treat(m.id).await;
if t {
println!("main: {}, that was a good one, {}!", m.id, &m.api);
} else {
println!("main: nah {}, better luck next time, {}!", m.id, &m.api);
}
});
// sauf quon met le résultat de cette tâche (le JoinHandle en fait) dans un tableau
tasks.push(task);
println!("main: Finished receiving");
}
println!("main: Back to main thread and were waiting for everyone now…");
// mainteant quon a tout reçu, on attend simplement que chaque thread se termine
join_all(tasks).await;
```
Et le tour est joué! Les traitement entrants sont bien asynchrones, les traitements sortants également, et le programme principal attend sagement que tout le monde ait terminé avant de lui-même séteindre bien gentiment.
# Conclusage
Ce nest certaiment pas le plus optimal et ce nest peut-être pas un modèle à suivre. Je suis certain par exemple quune fonction comme `join_all` doit poser des problèmes dans certains cas, ou ne peut pas accepter plus dun certain nombre de tâches, etc… Néanmoins, ça démontre bien un truc: faire de lasynchrone, cest bien joli, mais à moins davoir un cas assez spécifique ou de savoir ce que lon fait, ça ne servira clairement pas à tout le monde et clairement pas tout le temps.
Du coup, Messieurs les développeurs, essayer de proposer une alternative bloquante à vos librairies: ça mintéresse beaucoup davoir de lasynchrone (vraiment) mais les cas où je vais réellement en avoir besoin et réellement men servir, ce ne sera clairement pas tous les jours, surtout vu la complexité du truc.
Ah et dernière chose: quand je dis **bloquant**, cest vraiment **bloquant**. Oui, je te regarde `reqwest::blocking` qui en fait planque du `tokio` dedans…

View File

@@ -0,0 +1,153 @@
+++
title = "Ansible et la génération magique de variables"
date = 2022-09-08
[taxonomies]
tags = [ "ansible", "système" ]
+++
> Ansible, cest de la merde.
*Schtroumpf grognon, 2022*
<!-- more -->
Je naime pas _Ansible_. Venant à lorigine de _SaltStack_, je le trouve hyper limité et étriqué dans sa façon de fonctionner: ne favorise pas vraiment la réutilisabilité, les variables sont toutes mises dans le même sac à la fin, accéder à nimporte quelle variable est hyper confus, il ny a pas de système de signaux permettant déchanger avec le serveur.
Sans compter le fait que même lexécution des modules de base inclus dans _Ansible_ nécessite parfois linstallation de la moitié de _pypi_.
Mais il a néamoins deux ou trois trucs pour lui: pas dagent (jai du mal à classer ça dans les avantages, mais dans certaines circonstances, ne pas avoir à se préoccuper de lagent, cest plutôt une bonne chose) et surtout une courbe dapprentissage nettement moins raide que _SaltStack_ (où tu rotes du sang régulièrement…).
Bref, laisse-moi te conter la mésaventure qui mest arrivé au détour dun _role_ _Ansible_
# Les données du problème
Admettons que jai un _role_ dont le but est de tester le retour dun _GET_ HTTP. On lui done une liste avec pour chaque entrée:
* lURI
* lURN
* le résultat attendu
Et ce _role_ se charge daller faire la requête _GET_ et de vérifier que le mot-clé est bien dans le résultat. Un truc plutôt basique.
Tu pourrais imaginer de faire un truc comme ça:
```yaml
test_http:
- uri: "https://blog.libertus.eu"
urn: "/ne-m-appelez-plus-prive"
result: "LOLILOL"
- uri: "https://www.elysee.fr"
urn: "/emmanuel-macron"
result: "connard"
```
Le _role_ va donc faire une boucle simple (probablement avec `loop`) sur le contenu de la variable `test_http` et renvoyer à chaque fois le résultat dun `assert`.
Jusquici tout va bien. Mais admettons que je doive générer cette liste. Elle nest plus bêtement statique, je dois la générer. Mettons par exemple que je veuille vérifier que lensemble des serveurs dans un groupe donné sont bien enregistrés dans un `consul` ou un système de gestions de parc ou nimporte quoi dautres.
# This is *SALTSTACK*!!
Côté _SaltStack_, il ny a pas de souci particulier: tous les fichiers sont interprétés en _Jinja_ avant dêtre interprétés en _Yaml_. On peut donc finalement _templater_ un peu nimporte où, cela ne pose jamais de problème.
Pour résoudre un problème de ce type, on aurait probablement faire qqch du genre:
```yaml
test_http:
{% for host in hosts %} # on admettra ici que la variable hosts contient ce quil faut
- uri: "http://consul.server"
urn: "/v1/catalog/nodes"
result: "{{ host }}"
{% endfor %}
```
Tout ceci dans un _pillar_ quelconque avec le _state_ qui va bien derrière, rien de bien compliqué en soi.
Le très gros avantage davoir du _Jinja_ à tous les étages, cest quon peut finalement _templater_ un peu nimporte quoi, un peu nimporte où sans trop se préoccuper. Cest ainsi quil nexiste pas vraiment déquivalent de `loop` dans _SaltStack_: on te dira toujours te de débrouiller pour faire la même chose en _Jinja_ et puis voilà.
# Le Chad _SaltStack_ / Le Virgin _Ansible_
Dans _Ansible_, on ne peut pas coller du _Jinja_ nimporte où. Lensemble des fichiers que manipulent _Ansible_ (à lexception du `ansible.cfg` et des fichiers dinventaire qui peuvent être en _INI_), tout doit être du pur _Yaml_.
Doù un certain nombre daberrations genre `loop` et la tétrachiée de `filters` quil faut se trimballer pour arriver à lutiliser (jetez juste un œil à [cette page](https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#migrating-from-with-x-to-loop) si vous voulez vous en convaincre…).
Mais, ça ne veut pas dire quon ne peut pas du tout utiliser des _templates_ _Jinja_. On peut, simplement, il faut les limiter aux variables.
Une première approche consisterait donc à tenter de produire un _template_ pour essayer de générer du _Yaml_:
```yaml
hosts:
- ta
- mere
- lol
test_http: |-
{% for host in hosts %}
- uri: "http://consul.server"
urn: "/v1/catalog/nodes"
result: "{{ host }}"
{% endfor %}
```
Sauf que quand on essaie dafficher cette variable, on obtient juste une chaîne de caractères:
```bash
TASK [display var] **********
ok: [localhost] => {
"msg": "- uri: \"http://consul.server\"\n urn: \"/v1/catalog/nodes\"\n result: \"ta\"\n- uri: \"http://consul.server\"\n urn: \"/v1/catalog/nodes\"\n result: \"mere\"\n- uri: \"http://consul.server\"\n urn: \"/v1/catalog/nodes\"\n result: \"lol\"\n"
}
```
**PERDU !**
Si on remet les retours chariot un peu dans lordre, on se rend pourtant bien compte quon a effectivement généré ce quil faut, mais visiblement, _Ansible_ na pas vraiment envie de sen servir.
En vrai, il y a un astuce… Il ne faut pas _templater_ du _Yaml_, il faut _templater_ du _JSON_. Oui, cest vraiment un truc de clown…
Donc en fait, si tu fais **exactement** la même chose mais avec cette syntaxe:
```yaml
hosts:
- ta
- mere
- lol
test_http: |-
[
{% for host in hosts %}
{
"uri": "http://consul.server",
"urn": "/v1/catalog/nodes",
"result": "{{ host }}"
},
{% endfor %}
]
```
Et là, le miracle saccomplit:
```bash
TASK [display var] *********
ok: [localhost] => {
"msg": [
{
"result": "ta",
"uri": "http://consul.server",
"urn": "/v1/catalog/nodes"
},
{
"result": "mere",
"uri": "http://consul.server",
"urn": "/v1/catalog/nodes"
},
{
"result": "lol",
"uri": "http://consul.server",
"urn": "/v1/catalog/nodes"
}
]
}
```
Mais attention, hein! Il ne faut surtout pas oublier la moindre virgule et en particulier celle qui se trouve en toute fin de variable `},`, sinon tout est de nouveau interprété comme une chaîne de caractères générée.
# Foutaises
Donc, oui, on peut génerer des variables dans _Ansible_ mais juste, cest tellement bordélique quon peut quand même se poser des questions de pourquoi, cest foutu comme ça. Générer une variable _Yaml_ en passant par du _JSON_ _templaté_ en _Jinja_, cest pas vraiment le truc le plus instinctif de la planète…

View File

@@ -0,0 +1,74 @@
+++
title = "Réparer ou jeter, que faire?"
date = 2022-10-27
[taxonomies]
tags = [ "bricole-picole" ]
+++
> Bah moi, mon frigo, il a 40 ans et il tourne toujours.
Papy Mougeot
<!-- more -->
Je te livre ici une réflexion qui remonte à très très très loin. Ça fait un moment que je me pose la question et avec le contexte de crise climatique actuel, je pense que cette réflexion mûrit à point (en tout cas, jespère).
Je précise tout de suite que je ne suis pas en train de te dicter ton comportement. Ni en train de te dire que je vais sauver la planète (on en est même loin). Jessaie simplement de donner un contexte et quelques données qui permettront (peut-être) de prendre une décision assez simple : faut-il racheter (et donc jeter) ou réparer?
# *if it aint broken, dont fix it*
Tout dabord, le premier critère et certainement le plus important pour le moment: **sil fonctionne et quil remplit correctement sa fonction, il ny a aucune raison de remplacer un appareil**.
Voilà, cest dit, cest simple. Si ça marche, absolument aucune raison de changer un appareil. Ça a lair con comme ça, mais ça fait déjà une grosse différence: ta télé marche? Oui, alors tu la changes pas. Ton ordinateur marche? Oui, donc aucune raison de le changer.
Je mettrais tout de même une petite exception à cela: si lappareil est polluant ou particulièrement inefficace énergétiquement à lutilisation. Je donnerai un exemple plus bas, mais cest aussi pour illustrer le fait que rien nest absolu dans ce que je dis. Il faut y réfléchir pratiquement au cas par cas.
# La capacité de diagnostic
À partir du moment où un appareil est en panne, ou ne remplit plus complètement ses fonctions, la première chose est darriver à établir un diagnostic: quest-ce qui ne marche pas? Et surtout, pourquoi, ça ne marche pas?
Là, déjà, on a largué une bonne partie du commun des mortels. Et je ne parle même pas de questions délectronique ou dinformatique, le simple fait davoir ton lave-linge qui ne vidange plus, cest déjà presque trop pour la plupart des gens.
Ne négligeons pas cet aspect: même les informaticiens les plus chevronnés ne sont pas toujours capables de diagnostiquer correctement un souci purement électronique. Rien nest simple et rien nest évident.
Lespoir que jai dans ce domaine, cest que finalement les pannes sont aujourdhui de mieux en mieux documentées et beaucoup de sites web permettent dobtenir des diagnostics ou de les poser relativement facilement. Malheureusement, il y a pas mal de vendeurs dhuile de serpent qui profitent aussi de cela pour être en tête des réponses des moteurs de recherche avec dans lidée beaucoup un service à vendre quun réel bénéfice pour le consommateurs/utilisateurs.
Dans la capacité à diagnostiquer, il faut aussi inclure, et ce nest pas simple non plus, loutillage nécessaire. Autant certains diagnostics sont finalement assez simple à réaliser («est-ce que la pompe vidange? ») autant dautres sont nettement plus compliqués («Y a-t-il continuité entre le composant X et Y? »). Pareil, cest relativement crétin, mais avoir une caisse à outil avec ce quil faut pour démonter de lélectroménager, de lélectronique, avoir un multimètre, un petit compresseur, etc… et bien pour le moment, ce nest pas forcément donné à tout le monde.
Cest un effort déquipements, qui est dautant plus difficile à faire valoir, quil ne servira quen cas de panne (donc normalement relativement peu souvent). Je me suis personnellement servi de mon multimètre en tout et pour tout une demi-douzaine de fois depuis que je lai acheté. Alors, certes, cest pas cher, mais cest compliqué à justifier.
# La possibilité/capacité de réparer
Une fois de diagnostic posé (composant dessoudé, joint mort, pompe de vidange hors service, etc…), vient le moment de passer à la réparation. Là, encore une fois, on va perdre quelques personnes supplémentaires. Le jouet du gamin ne fait du bruit? Cest potentiellement juste un câble dessoudé mais encore faut-il avoir un fer à souder pour le réparer.
Fort heureusement, le kit du petit bricoleur standard (marteau, tourne-vis, etc…) suffit généralement amplement. Je ne saurais que trop conseiller à tout le monde de séquiper dun petit fer à souder. Ça ne coûte pratiquement rien et vous en aurez toujours lutiliser en réalité (ne serait que pour réparer un fil de lampe sans avoir à mettre un domino dégueux sur le fil en question).
En dehors de loutillage, il y a évidemment le souci des pièces. De ce point de vue, on peut remercier (sans ironie) lEurope qui a passé tout un tas de législation sur la réparabilité notamment de lélectro-ménager qui obligent les fabricants à garder des pièces disponibles. Cest un pas dans le bon sens, mais malheureusement cest assez insuffisant: ça nest pas valable pour lélectronique grand public (consoles, téléphones mobiles, etc… seule les télévisions sont dans cette obligation à ma connaissance); ça ne fixe pas un prix maximum raisonnable pour le consommateur qui souhaite réparer ou faire réparer (ce qui veut dire que le fabricant de lave-linges peut faire payer 30 fois le prix du lave-linge pour une vulgaire pièce sans rien risquer).
Ce nest donc toujours pas parfait, mais on na plus vraiment dexcuses pour ne pas tenter au moins une réparation ou ne pas tenter de faire réparer. À mon sens, il manque une incitation à réparer en dehors des périodes de garantie également. Les extensions de garantie ne sont que des produits financiers (et vendu comme tel) et sont généralement prohibitifs en terme de prix. Sans compter le fait quaucun appareil un tant soit peu bien construit ne connaît de vraies pannes dans ses 5 premières années dutilisation: lusure sur les pièces vient en général bien plus tard.
Je ne sais pas quelle forme pourrait prendre cette incitation (peut-être une intervention de lÉtat dune manière ou dune autre).
Bref, concernant le fait de réparer ou maintenir en bonne condition son appareil, on progresse. Encore une fois, rien nest parfait, rien nest définitif, rien nest évident, rien nest gagné. Mais on avance progressivement dans le bon sens.
**Pour moi, cest aussi à cet instant quentrent en jeu les équations économiques à prendre en compte.** La réparation ne devrait jamais coûter plus cher que lappareil neuf. Malheureusement, on sait que ce nest pas toujours le cas.
Et il y a aussi un autre facteur à prendre en compte: **lappareil en panne connaît-il en ce moment une rupture technologique qui permettrait de faire des économies énergétiques conséquentes ou daméliorer de manière conséquente lefficacité ou les performances de lappareil?**
Mettons que ton frigo tombe en panne. Cest une panne relativement conséquente. Depuis 6 mois, de nouveaux types de frigo sont disponibles. **Ils sont 100 fois plus efficaces énergétiquement** (chiffre sorti de mon cul bien évidemment). **Dans ce cas, et dans ce cas seulement**, je pense quil vaut mieux changer lappareil au complet. Certes, tu pourrais réparer ton frigo et le faire durer quelques années de plus. Mais pendant ce temps, il va consommer 100 fois plus que lappareil neuf. Ça nen vaut à mon sens plus la peine.
# Conclusage
Ya encore du boulot! Mais ya de lespoir aussi. Les informations concernant la réparation des appareils sont de plus en plus faciles à trouver, les diagnostics de plus en plus simple à établir, les bricoleurs du dimanche de plus en plus nombreux.
Mais il manque encore pas mal de choses. Il y a clairement des emplois de réparateurs à créer. Parce que tout le monde ne sait pas, parce que tout le monde ne peut pas. Mais il faut pour cela que ces réparateurs pratiquent des tarifs raisonnables. Ou alors il faut que le coût des appareils neufs augmentent considérablement (ce qui va peut-être finir par arriver par la force des choses).
Ça cétait le front de la réparation. Sur le front du recyclage, il me paraît évident quil va falloir mieux traiter les appareils dysfonctionnels dans le temps. Quun lave-linge soit déclaré économiquement irréparable, je le conçois parfaitement. Est-ce une raison pour jeter lensemble? Clairement, non. Il y a probablement encore de bonnes pièces dedans, quil faudrait extraire pour être utilisé sur les autres appareils compatibles.
Encore faut-il disposer de cette information (quel appareil utilise quoi, quelle chance pour que la pièce compatible soit disponible, etc…). Je ne sais pas qui doit faire leffort de ce point de vue. Jaurais tendance à dire les industriels: ils connaissent mieux les appareils quils vendent, ils savent les quantités produites et donc toujours en circulation, ils ont la capacité de produire et stocker les pièces. Mais peut-être faudrait-il penser à un système plus localisé.
Bref, il y a encore du boulot, là aussi.
Et concernant le fait de jeter? Et bien, comme je lai dit, en ce qui me concerne, cest si, et seulement si, il y a une rupture technologique qui permet de vraiment faire des économies dénergie ou des économies de CO₂ considérable derrière. Il y a quelques années, ma voiture à essence sest faite emboutir. Jusquà présent, je lavais toujours réparée même si le coût des réparations étaient importants. Elle a été déclarée économiquement irréparable (pour un montant un peu juste à mon goût, on pourrait en reparler). Jai décidé dacheter un véhicule électrique doccasion. Je ne suis pas un chantre de la voiture électrique (ça va être indispensable pour les transports du futur, mais ce nest clairement pas LA solution pour le moment), mais cest une rupture technologique majeure qui permet déconomiser de lénergie et du CO₂.
Cest pour moi lillustration quasi-parfaite de ce que je disais: si ce nest pas cassé, on ne change pas (ou alors on vend éventuellement, mais surtout on ne jète pas); si cest cassé, on répare, le plus possible; si cest cassé et quil y a une technologie bien meilleure disponible (pas plus performante, pas plus confortable, meilleure au sens énergétique et pollution), il faut remplacer.

View File

@@ -0,0 +1,161 @@
+++
title = "Ansible et la hiérarchie des groupes"
date = 2022-11-28
[taxonomies]
tags = [ "ansible", "système" ]
+++
> Le mauvais groupe, il est hiérarchique… le bon groupe… il est hiérarchique, mais cest pas pareil.
Le bon programmeur, apparemment…
<!-- more -->
Je suis récemment tombé sur une petite particularité (certains diront un *quirk*) dAnsible qui ma paru suffisamment intéressant pour tenter de lexpliquer, ou au moins den expliquer ma compréhension.
# Et là, la marmotte dAnsible, elle met le chocolat de lhéritage des groupes dans le papier du `group_vars`
Notre histoire commence un beau matin de printemps avec un playbook Ansible de bon alois qui se baladait innocemment dans la forêt enchantée du système dinformation. Ce playbook contenait de nombreuses variables et de nombreux hôtes. Toutes ces variables et ces hôtes formaient une hiérarchie complexe où beaucoup de variables se surchargeaient les unes les autres dans un ballet ininterrompu et franchement difficilement compréhensible.
Du point de vue du contrôleur (donc la machine qui fait tourner Ansible), les groupes en question servent essentiellement à organiser de manière logique les choses.
Mais du point de vue de la machine que se passe-t-il? Et du point de vue de lhôte, comment cest perçu?
Tu vas pouvoir constater que la réponse nest pas si simple que ça.
# Vu de lhôte, les groupes, on sen tape
Du point de vue lhôte (donc la machine sur laquelle se connecte le contrôleur Ansible), les groupes nont que peu dimportance: ils sont vus ni plus ni moins que comme des tags et rien de plus. Dailleurs, à ma connaissance, ils ne sont accessibles quà travers la variable Ansible `group_names` qui contient simplement une liste, à plat donc, de tous les groupes auquel appartient lhôte. On peut très facilement récupèrer cette liste comme suit:
```
---
- hosts: all
gather_facts: false
tasks:
- name: tamerelol
debug:
msg: "{{ group_names }}"
```
Ce qui donne ce résultat:
```
ok: [HP-elite-book-cuisine.buttse.cx] => {
"msg": [
"borg_client",
"borgbackup",
"webservers",
"disabled_syslog"
]
}
```
Cela peut être pratique dans certains cas:
* mettre à jour seulement les machines dans le groupe X
* exécuter une commande sur lensemble des machines dans le groupe Y
* filtrer dans un template Jinja2
Mais le fait est: du point de vue de lhôte, cest une inforamtion plate et absolument pas hiérarchisée.
**Donc si quelquun vous dit que les groupes Ansible sont juste des tags**, il a basiquement raison, mais seulement du point de vue de lhôte. Le contrôlleur, cest une toute autre histoire…
# Vu du contrôleur, les groupes, cest la vie
Du point de vue de contrôleur en revanche, les groupes ont une très grande importance: cest eux qui déterminent la façon dont les variables vont être déterminées et appliquées aux hôtes. En gros, du point de vue des `group_vars` de linventaire (et seulement de linventaire, je vous rappelle quil y a dautres priorités dans Ansible, en particulier concernant les variables dhôtes, les `group_vars` des playbooks, jen passe et des meilleurs, la résolution est la suivante:
* on commence par appliquer les variables qui sont dans le `all` (celui-ci a la priorité la plus basse donc);
* si elles sont surchargées par des variables dans le premier niveau de linventaire, ces dernières prennent la priorité (et donc surcharge logiquement le `all`)
* si des variables sont définies plus bas dans la hiérarchie des groupes, elles surchargeront logiquement le niveau de leur parent
* et seront surchargées par les variables définies au niveau de leurs enfants
Il ny a quune petite exception à cette règle, qui peut savérer très trompeuse dans certains cas, ce sont les variables qui sont définies au même niveau dans la hiérarchie des groupes. Dans ce cas, Ansible fait une résolution dans lordre alphabétique de définition des groupes (donc les groupes en `a*` seront surchargés par les groupes en `b*`, etc…).
Concrètement, si vous avez une hiérarchie de groupes qui ressemblent à ça:
```
> ansible-inventory -i production.yml --graph borgbackup
@borgbackup:
|--@borg_client:
| |--macbook-pro-chiottes.buttse.cx
| |--HP-elite-book-cuisine.buttse.cx
|--@borg_server:
| |--meinbackup.buttse.cx
```
Vous pouvez des variables « globales » (au niveau du groupe `borgbackup` donc) et les surcharger éventuellement au niveau du dessous. Petite illustration. Admettons que je définisse une variable `borg_vars` dans `group_vars/borgbackup.yml` comme suit:
```
> cat group_vars/borgbackup.yml
---
borg_vars: "je suis le niveau le plus en haut"
```
Et que je définisse la même variable un peu plus «bas» dans `group_vars/borg_client.yml`:
```
> cat group_vars/borg_client.yml
---
borg_vars: "je suis le niveau du client et je surcharge le serveur"
```
On va tenter de voir comment cest défini derrière:
```
> ansible-playbook -u root -i production.yml test.yml
PLAY [all] ********************************************************************************************************************
TASK [tamerelol] **************************************************************************************************************
lundi 28 novembre 2022 10:03:20 +0100 (0:00:00.032) 0:00:00.032 ********
ok: [meinbackup.buttse.cx] => {
"msg": "je suis le niveau le plus en haut"
}
ok: [HP-elite-book-cuisine.buttse.cx] => {
"msg": "je suis le niveau du client et je surcharge le serveur"
}
ok: [macbook-pro-chiottes.buttse.cx] => {
"msg": "je suis le niveau du client et je surcharge le serveur"
}
```
Comme tu peux le constater, la hiérarchie des groupes a bien été respectée pour surcharger la variable `borg_vars`. Évidemment, sil y avait eu un groupe en dessous de `borg_client`, on aurait aussi pu surchager la variable en question dedans aussi et ça aurait fait à peu près le même effet.
Ce dont il faut se méfier le plus, cest si des serveurs sont définis en même temps sur plusieurs niveaux. Dans ce cas, on revient au cas initiale (résolution dans lordre alphabéique). On peut le démontrer assez facilement dans notre cas en ajoutant lhôte `meinbackup.buttse.cx` directement dans le groupe `borg_client`. En faisant cela, on obtient bien:
```
ok: [meinbackup.buttse.cx] => {
"msg": "je suis le niveau du client et je surcharge le serveur"
}
```
Parce que, du point de vue dAnsible, `borg_client` est alphabétiquement avant `borgbackup`. Dailleurs, `borg_server` étant juste après `borg_client` mais avant `borgbackup`, si jajoute cette variable dans `group_vars/borg_server.yml`, elle surcharge bien celle de `group_vars/borg_client.yml`, on constate bien que cest le cas:
```
> cat group_vars/borg_server.yml
---
borg_vars: "normalement, je devrais avoir la priorité pour borg_server"
> ansible-playbook -u root -i production.yml test.yml
[…]
TASK [tamerelol] **************************************************************************************************************
lundi 28 novembre 2022 10:03:20 +0100 (0:00:00.032) 0:00:00.032 ********
ok: [meinbackup.buttse.cx] => {
"msg": "normalement, je devrais avoir la priorité pour borg_server"
}
ok: [HP-elite-book-cuisine.buttse.cx] => {
"msg": "je suis le niveau du client et je surcharge le serveur"
}
ok: [macbook-pro-chiottes.buttse.cx] => {
"msg": "je suis le niveau du client et je surcharge le serveur"
}
[…]
```
**Donc si quelquun vous dit que les groupes Ansible sont effectivement hiérarchiques**, il a basiquement raison, mais seulement du point de vue du contrôleur et de la définition de la précédence des variables.
# Conclusage
Cest un beau bordel mine de rien ces histoires de variables Ansible. Déjà que la précédence nest pas toujours simple (je vous mets au défi de me dire de tête qui est plus prioritaire entre une variable dinventaire, une variable de playbook et une variable de groupes), Ansible en rajoute une couche avec cette histoire de précédence dans les hiérarchies de groupes et de précédence dans les noms de groupe, histoire de bien simplifier les choses pour le quidam moyen.
Bref, jespère que ça taura permis de mieux comprendre la chose et de ne pas te faire surprendre. Et surtout, on noublie pas: quand deux personnes affirment des choses contradictoires, elles ne parlent peut-être simplement pas de la même chose ou du même point de vue.

View File

@@ -0,0 +1,190 @@
+++
title = "UTF-8, UTF-16, Rust et le formulaire Web magique"
date = 2022-12-05
[taxonomies]
tags = [ "utf-8", "utf-16", "rust" ]
+++
> Et là, la marmotte dUTF-16… attendez, je crois quon la déjà faite celle-là
Mon cerveau à deux heures du mat
<!-- more -->
Je suis tombé récemment sur une bizarrerie tellement étrange quil ma paru intéressant de tenter de la documenter (jai bien dit «tenter» parce que je ne suis pas certain que tout sera hyper précis dans ce que je vais dire…).
# Un peu de contexte…
Je suis en train de développer un petit programme Rust dont lobjectif est de convertir un profil Twitter en profil Mastodon. Il faut donc récupérer la photo de profil, la bannière, si elle existe, les liens, etc… mais surtout le nom affiché par Twitter (le *screen name* à lopposé du *name* qui est le truc au bout de lURL Twitter) doit correspondre, le mieux possible au *display name* Mastodon.
Or il se trouve que sur Twitter, le fameux *screen name* contient 50 «caractères» (oui, je mets des air-guillemets, je texpliquerais pourquoi plus loin) alors que sur Mastodon, le *display name* est limité à 30 caractères. Cest comme ça sur lAPI et cest comme ça sur le formulaire permettant de le changer.
Il va donc falloir couper une chaîne de caractères…
![Meme avec un chien découvrant quil va devoir couper une chaîne de caractères](/2022/12/736lvk.jpg)
# Première approche naïve…
Prenons une chaîne de caractères «normales» (pas de caractères chelous, que de lASCII tout ce quil y a de plus *vanilla*) et voyons un peu comment on peut faire. La fonction [`truncate`](https://doc.rust-lang.org/std/string/struct.String.html#method.truncate) de la librairie standard permet de faire ça, ça fonctionne comme suit:
```rust
let mut long_string = String::from("Je suis une longue phrase.");
long_string.truncate(15);
println!("{}", long_string);
```
Bon, là, ça «marche» dans ce cas précis et ça donne ça:
```bash
Je suis une lon
```
Sauf que la [doc](https://doc.rust-lang.org/std/string/struct.String.html#method.truncate) indique bien que la coupure va merder (`panic`, cest un peu la merde, on est bien daccord?) si par hasard on coupe au milieu dun caractère. Oui, parce que comme Rust fait de lUTF-8 partout, tout le temps, certains caractères sont sur plusieurs octets et en fait `truncate` ne coupe pas au caractère mais coupe à loctet… et panique. Essayons de le faire paniquer, tu vas voir que ce nest pas bien compliqué:
```rust
let mut long_string = String::from("Je suis une très longue phrase.");
long_string.truncate(15);
println!("{}", long_string);
```
Voilà, **perdu**, on a coupé au mauvais endroit, plus rien ne fonctionne correctement:
```bash
thread 'main' panicked at 'assertion failed: self.is_char_boundary(new_len)', /rustc/897e37553bba8b42751c67658967889d11ecd120/library/alloc/src/string.rs:1280:13
```
Bah oui `è` est en fait deux octets en UTF-8: `c3` et `a8`.
# Découpage par rapport aux caractères
Il se trouve que la bibliothèque standard de Rust est une petite merveille lorsquil sagit daller chercher des fonctions très chelous permettant de faire des trucs parfois très tordus. Nous allons voir ce quil est possible de faire. La fonction [`chars`](https://doc.rust-lang.org/std/primitive.str.html#method.chars) permet de récupérer un intérateur sur un `str` (et donc par extension `String`) au **caractère** et non à loctet. Elle semble donc toute indiquée pour tenter de résoudre le problème que nous avons là.
Une première approche pourrait justement consister à itérer sur ce `String` et sarrêter au quinzième caractère:
```rust
let long_string = String::from("Je suis une très longue phrase.");
let mut shorter_string = String::new();
for (i, c) in long_string.chars().enumerate() {
if i >= 15 {
break;
}
shorter_string.push(c);
}
println!("{}", shorter_string);
```
Bon là, ça marche effectivement:
```bash
Je suis une trè
```
Par contre, cest moyennement idiomatique, essayons de faire un peu mieux que ça en utilisant les fonctions [`take`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take) et [`collect`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect):
```rust
let long_string = String::from("Je suis une très longue phrase.");
let shorter_string = long_string.chars().take(15).collect::<String>();
println!("{}", shorter_string);
```
Même résultat, mais cest quand même un poil plus propre à la lecture et surtout, on évite davoir un `15` qui se balade dans le code sans quon puisse voir directement ce quil fait (et si taimes pas lopérateur *turbofish*, tu peux toujours préciser le type de variable au début, cest un peu comme tu le sens).
# Bon ben ça marche, pourquoi tu nous casses les noix alors?
Alors, dabord, tu vas rester poli si tu veux pas bouffer ton cul et ensuite, attend, ce nétait que la première partie de la grosse marade. Oui, lobjectif final, cest de faire manger cette chaîne de caractères à Mastodon, via lAPI, mais en réalité, on peut aussi le voir directement sur linterface Web correspondante (elle a, en gros, les mêmes «limitations»).
Et pour ça, on va devoir se poser une autre question: quand un formulaire Web te dit quil est limité à 30 caractères, cest quels caractères? 30 caractères ASCII? 30 caractères Unicode (UTF-8)? Ou encore autre chose?
Commençons par le commencement et nous poser la question: en Rust, cest quoi la taille dune chaîne de caractères? Et bien, cest déjà pas si simple que ça…
```rust
let long_string = String::from("Je suis une très longue phrase.");
println!(
"len: {}\ncount: {}",
long_string.len(),
long_string.chars().count()
);
```
Renvoie:
```bash
len: 32
count: 31
```
Et ça na rien de choquant: [`len`](https://doc.rust-lang.org/std/primitive.str.html#method.len) est supposé, encore une fois, donner la longueur en octets et non en caractères. Il faut donc encore une fois passer par [`chars`](https://doc.rust-lang.org/std/primitive.str.html#method.chars) et compter le nombre de caractères.
**Sauf que** (*NDLR*: le vrai fun commence maintenant), cest pas forcément le cas pour **tous** les caractères et ça dépend aussi de lencodage. Et là, normalement, tu dois commencer à saigner du nez: oui, si tu encodes en UTF-8 ou en UTF-16, bah, ça fait pas le même nombre de caractères…
Prenons des caractères plus exotiques, comme par exemple « 🇫🇷 ». Combien que ça fait de caractères ça, en Rust pur?
```rust
let drapeau = String::from("🇫🇷");
println!(
"{}\nlen: {}\t count: {}",
drapeau,
drapeau.len(),
drapeau.chars().count()
);
```
```bash
🇫🇷
len: 8 count: 2
```
*Dawat?* Donc, là, ça fait 2 caractères. Un seul caractère affiché mais deux caractères? En fait, cest «normal»: les drapeaux sont effectivement composés de deux caractères qui sont dans la table Unicode, `U+1F1EB` et `U+1F1F7`, identifiant chacun `F` et `R` respectivement. Cest parce que cest deux caractères sont côte à côte quun navigateur va lafficher comme un drapeau français. Sinon, il affichera simplement lidentifiant correspondant.
# `longueur_max = toto`
Et là, normalement, tu te dis que cest bon, que tout va bien, que tu vas arriver à quelque chose… mais est-ce que les formulaires Web font de lUTF-8? Lattribut `maxlength` représente-t-il vraiment le nombre de caractères?
Allons faire le test pour vérifier, équipé de notre vaillant 🇫🇷 :
![30 caractères, mon cul oui!](/2022/12/30_char_mon_cul.png)
Donc, cest supposément 30 caractères, on a vu quen UTF-8 le drapeau faisait 2 caractères et pourtant notre formulaire ne semble en accepter que 7 + le caractère `F` tout court à la fin. Quest-ce que la baise?
Et bien, en fait, les formulaires Web [encodent en utilisant de lUTF-16 et non de lUTF-8](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength) et ça donne effectivement plus de caractères. Essayons de voir comment retomber sur nos pieds en Rust. Pour cela, on va se servir de la fonction [`encode_utf16`](https://doc.rust-lang.org/std/primitive.str.html#method.encode_utf16) qui va elle aussi nous renvoyer un itérateur mais sur un tableau de `u16`:
```rust
let drapeau = String::from("🇫🇷");
println!(
"{}\nlen: {}\t count_utf-8: {}\tcount_utf-16: {}",
drapeau,
drapeau.len(),
drapeau.chars().count(),
drapeau.encode_utf16().count()
);
```
Résultat:
```bash
🇫🇷
len: 8 count_utf-8: 2 count_utf-16: 4
```
Bon, ça y est, on vient de tomber sur le bon chiffre, on peut partir du principe que ça suffira. Du coup, pour extraire les 30 premiers «caractères», en UTF-16, à destination dun formulaire, on pourra faire comme ceci:
```rust
let string_30 =
String::from_utf16_lossy(&string_50.encode_utf16().take(30).collect::<Vec<u16>>());
```
La fonction [`from_utf16_lossy`](https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf16_lossy) permettant de recréer un `String` Rust à partir de caractères UTF-16 en mettant un caractère invalide à chaque fois quelle ny arrive pas.
# Bon ben voilà, cétait pas si compliqué que ça!
/ragequit

View File

@@ -0,0 +1,138 @@
+++
title = "Rust: passer de clapv2 à clapv4"
date = 2022-12-09
[taxonomies]
tags = [ "rust", "clap" ]
+++
> Ah ouais, comme pour IPv4 et IPv6, tas zappé le 3?
>
> Ta gueule…
<!-- more -->
Récemment, je me suis rendu compte que certains des logiciels que javais développés par le passé, en Rust donc, utilisait une «assez vieille» version de la librairie [clap](https://epage.github.io/blog/), la v2 pour être précis.
En fait, jai complètement zappé la v3, parce quelle me semblait à ce moment nettement moins optimale, beaucoup plus brute, imposait tout un tas de choix discutable comme le fait de passer obligatoirement par des macros plutôt que davoir un moyen «programmatique» dappeler la librairie.
De ce point de vue, la v4 correspond bien plus à ce que jattends dun programme de ce type: le choix entre macros ou pas, la versatilité des options, les cas tordus quon na pas forcément vus au départ.
Voici un (petit) guide pour aider à passer de clapv2 à clapv4.
# `Command::new()` remplace `App:new()`
Voilà, la première des choses à faire, cest dinvoquer correctement clapv4. Pour cela, on utilise la fonction [`Command::new()`](https://docs.rs/clap/4.0.29/clap/builder/struct.Command.html#method.new) que lon doit invoquer à limport de `clap`.
`App` nexiste plus du tout, même si ce nest pas la première erreur que tu vas rencontrer a priori.
# `Arg::with_name` nexiste plus
Autre chose dont il va falloir se débarasser, `Arg::with_name` nexiste plus, il est remplacé par [`Arg::new()`](https://docs.rs/clap/4.0.29/clap/builder/struct.Arg.html#method.new). On peut ainsi facilement remplacer ce bout de code:
```rust
App::new()
.arg(Arg::with_name("host")
.short("H")
.long("host")
.value_name("HOST"));
```
Par:
```rust
Command::new()
.arg(Arg::new("host")
.short("H")
.long("host")
.value_name("HOST"));
```
Plus simple, plus lisible.
# `takes_value()` nexiste plus non plus
En fait `takes_value()` avait un peu un statut à la con à la lorigine: il permettait de spécifier quun argument devait prendre une valeur, mais il fallait une seconde fonction pour préciser le nombre de valeurs sil y en avait plus dune. Cest maintenant régler sur clapv4 où lon utilise systématiquement la fonction [`num_args()`](https://docs.rs/clap/4.0.29/clap/builder/struct.Arg.html#method.num_args). Il suffit de linvoquer avec la valeur `1` pour retrouver le comportement dorigine et lon verra un peu plus bas que ça a des conséquences assez positives sur le reste du code.
# `short()` prend maintenant un caractère et non une chaîne
Voilà un changement qui est le bienvenue: on ne peut plus écrire `short("b")`, il faut obligatoirement écrire `short('b')`. Et oui, en Rust, les chaînes de caractères sont entre *double quotes* alors que les caractères sont obligatoirement entre *single quotes* (un truc quon a beaucoup de mal à faire comprendre au départ à des gens qui font du PHP ou du Python dailleurs…).
# Dites aussi au revoir à `value_of()`
Bon alors là, je ne peux quapplaudir le progrès aussi. Au lieu dutiliser `value_of()` et de décapsuler léventuel résultat, il va maintenant sagir dutiliser [`get_one()`](https://docs.rs/clap/4.0.29/clap/parser/struct.ArgMatches.html#method.get_one) (ou son pendant [`get_many()`](https://docs.rs/clap/4.0.29/clap/parser/struct.ArgMatches.html#method.get_many)) pour récupérer les valeurs que lon souhaite. La (très) bonne nouvelle, cest que [`get_one()`](https://docs.rs/clap/4.0.29/clap/parser/struct.ArgMatches.html#method.get_one) permet de directement spécifier le type quon souhaite récupérer à la fin.
On peut donc parfaitement écrire:
```rust
let my_port = matches.get_one::<String>("host").unwrap_or("default");
```
Plutôt que:
```rust
let my_port = matches.value_of("host").unwrap().to_string();
```
Je trouve ça bien plus élégant dans le principe et même dans la pratique, dans la mesure où le *cast* est fait directement par la fonction et non en aval de celle-ci. On récupère donc bien plus directement les valeurs qui nous intéressent (et encore une fois, si vous naimez pas lopérateur *turbofish*, vous pouvez toujours venir me lécher le cul…).
Attention, toutefois! Vous ne récupérez pas la valeur directement mais un emprunt à cette valeur. Méfiez-vous donc pour les `String` par exemple. Ça veut aussi dire quil faudra de temps en temps se servir de lopérateur `*`, ce qui est relativement inhabituel en Rust.
# Les `flags` doivent maintenant être utilisés systématiquement
Avant pour préciser un *flag* (comprendre une valeur optionnelle sur la ligne de commande), il fallait préciser un argument sans `takes_value()` ou avec `takes_value(false)`. Désormais, il faut transformer cette argument en `flag` (un drapeau donc) avec une valeur par défaut (typiquement si faux, si vrai, etc…).
On passe donc de:
```rust
let matches = App::new()
.arg(
Arg::with_name("starttls")
.short("t")
.long("starttls"));
let starttls = matches.is_present("starttls");
```
À :
```rust
let matches = Command::new()
.arg(Arg::new("starttls")
.short('t')
.long("starttls")
.action(ArgAction::SetTrue));
let starttls = matches.get_flag("starttls");
```
Voilà, encore une fois un peu plus lourd à linstantiation, mais du coup bien plus «explicite» quand on lit juste la partie définition les arguments.
# Précisez les valeurs par défaut directement dans les arguments
Toujours dans le même état desprit, vous pouvez maintenant préciser une valeur par défaut (toujours sous la forme dune chaîne de caractères) directement dans les arguments. Donc au lieu de tenter des [`unwrap_or()`](https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or) un peu au pif dans le code, on peut directement préciser une valeur dans largument pris.
Cest, encore une fois, un changement qui va dans le bon sens, dautant plus quil est repris dans laide de la commande quon tape (entre crochet à côté de la valeur). Donc une très bonne option pour rendre les choses bien plus claires.
En gros, on peut maintenant faire:
```rust
let matches = Command::New()
.arg(Arg::new("port")
.short('p')
.long("port")
.num_args(1)
.default_value("993"));
let port = matches.get_one::<u16>("port").unwrap(); // va contenir 993 quoiquil arrive
```
# Sous-commandes
Et pour finir dans ce petit tour de clapv4, les `subcommand` (qui exigeaint à lorigine leur propres structures) sont maintenant simplement des [`Command::new()`](https://docs.rs/clap/4.0.29/clap/builder/struct.Command.html#method.new). Il ny a donc pas besoin davoir une commande spécifique pour les sous-commandes, on peut directement faire des commandes dans des commandes, en continuant dutiliser [`subcommand`](https://docs.rs/clap/4.0.29/clap/builder/struct.Command.html#method.subcommand).
Seul petit piège, [`subcommand` (celui du matches, pas lautre)](https://docs.rs/clap/4.0.29/clap/parser/struct.ArgMatches.html#method.subcommand) renvoie maintenant un tuple dans une Option au lieu de renvoyer une Option dans un tuple. Cest un changement assez minime dans labsolu, mais ça va forcément foutre en lair ta syntaxe dorigine, cest balot…
# Conclusation
Voilà, cétait un petit retour sur le passage de v2 à v4 de `clap`. `clap`, cest vraiment la librairie un peu indispensable dès quon veut mettre des options dans un programme Rust. Essayer de sen passer, cest un peu comme tenter de faire le Tour de France avec les petites roues en ayant mangé une choucroute avant de se lancer: ya moyen que tu te gamèles au premier virage avec pertes et fracas.
`clap`, cest bon, mangez-en!

View File

@@ -0,0 +1,63 @@
+++
title = "LXC: résoudre localement les noms des conteneurs"
date = 2022-12-23
[taxonomies]
tags = [ "lxc", "système", "dns" ]
+++
> Et je vais lappeler `systemd`. Je suis sûr que tout va bien se passer.
![Grand sourire](/2022/12/smile_loud.jpg)
<!-- more -->
Alors, je tarrête tout de suite, je nai pas dallergie chronique à `systemd`. Dans labsolu, je trouve même que cest pas si mal foutu que cela (et on aura loccasion dy revenir un peu plus tard). Alors, oui, il y a aussi des trucs à la con (et pas quun peu dailleurs) mais le simple fait que ça *normalise* énormément de choses est déjà en soi une bonne idée.
Bref, ce nétait pas tellement le sujet, je voulais surtout parler de LXC et de comment tu peux faire pour résoudre les noms de tes conteneurs directement depuis la machine hôte (pratique quand on doit faire un peu dexpérimentation, du Ansible, des choses comme ça).
# Donner un nom de domaine à résoudre
Généralement LXC sous Linux vient avec tout un tas de trucs pour simplifier les choses, et notamment un service qui se nomme `lxc-net`. Ce dernier est en réalité un «gros» script qui comprend:
* linitialisation de linterface de bridge qui va permettre aux conteneurs daccéder au monde extérieur (et dêtre accédé aussi)
* le démarrage dun `dnsmasq` pour assurer la partie DHCP/résolution de nom
Or il se trouve que ce `dnsmasq` peut aussi servir pour faire la résolution de nom des machines derrière le bridge en question (en gros, tu vas pouvoir joindre facilement tes conteneurs).
Pour cela, quelques manipulations sont nécessaires. Première chose, il faut aller éditer `/etc/default/lxc-net` (sous Debian et Archlinux, cest comme ça, je ne sais pas pour les autres). Il faut y ajouter la ligne suivante:
```
LXC_DOMAIN=lxc.buttse.cx.local
```
Ça peut être mis nimporte où évidemment. Cela permet dindiquer à `lxc-net` que lon souhaite que la résolution du service `dnsmasq` se fasse en `lxc.buttse.cx.local` (tu peux évidemment mettre nimporte quoi, il ny a pas de limitation particulière a priori).
En redémarrant le service, tu peux constater que tu peux résoudre ces noms en tapant directement sur linterface de bridge:
```
> dig +short @100.64.4.1 youpi.lxc.buttse.cx.local
100.64.4.94
```
*`100.64.4.1` étant ladresse de mon bridge LXC*
Voilà, tout ça, cest très bien mais comment tu fais pour que ton hôte (la grosse babasse là) puisse aussi faire cette résolution sans faire trop de contorsion.
# *Enter systemd*
Côté réseau `systemd`, cest un peu le bordel par contre. Personnellement, étant sous Archlinux, jutilise `netctl` pour la partie réseau et `systemd-resolved` pour faire la partie résolution de noms. Pas que je trouve ça particulièrement efficace, mais ça permet de gérer tout un tas de cas tordus à peu près automagiquement (changement de réseaux, VPN, etc…), donc je men contente pour le moment.
Bref, idéalement, on aimerait bien indiquer pour `lxc.buttse.cx.local`, il faut utiliser le `dnsmasq` local plutôt que le reste de la chaîne de résolution de noms. Bah il se trouve quon peut (et cest presque pas tordu, tu vas voir). En fait, avec lindicateur `DNS=` dans `/etc/systemd/resolved.conf`, on peut préciser plein de trucs: à quel serveur DNS sadresser, mais également sur quelle interface et pour quel domaine. La syntaxe fait un peu peur mais ça marche relativement bien:
```
[Resolve]
DNS=100.64.4.1%lxcbr0#lxc.buttse.cx.local
```
Évidemment, rien nempêche den ajouter dautres, séparés par un espace. En redémarrant le service en question, on peut alors faire la résolution sans préciser le serveur DNS:
```
> dig +short youpi.lxc.buttse.cx.local
100.64.4.94
```
Et en prime, ça ne perturbe rien dautres. Tu pourrais dailleurs très bien imaginer en ajouter dautres pour KVM, Docker, etc…
Voilà, cest gratuit, cest pour moi.

View File

@@ -0,0 +1,242 @@
+++
title = "Garage: le petit serveur S3 quil est bien pour sen servir"
date = 2023-01-02
[taxonomies]
tags = [ "système", "s3", "garage", "stockage" ]
+++
> Effectivement, ça sent un peu le cambouis
*Michel, utilisateur de garage*
<!-- more -->
# Cest quoi exactement S3?
Commençons par le commencement, S3 (pour *Simple Storage Service*) est un protocole de stockage objet, appuyé sur HTTP. Pensez NAS mais avec autre chose que du NFS/CIFS et plus orienté usage sur WAN plutôt que LAN (et oui, je sais quon *peut* faire du NFS/CIFS sur du WAN mais juste parce que tu peux ne veut pas dire que tu dois…).
Pourquoi est-ce que tu devrais ty intéresser? Parce quil y a tout un tas de logiciels qui lutilisent pour faire du stockage de fichiers et que ça peut du coup être bien pratique den avoir à disposition. On pensera, entre autres, à NextCloud, Peertube, Mastodon, Hugo, Restic, etc…
En plus, comme cest orienté Web, il est aussi possible de lutiliser directement pour servir un site Web statique, ou des éléments statiques sur un site Web (et donc techniquement économiser de la ressource sur ledit site Web).
# Ah et du coup, pourquoi `garage`?
Il y a plein de logiciels qui font du S3: [OpenIO](https://www.openio.io/), [Ceph](https://ceph.com/), [MinIO](https://min.io/) et probablement plein dautres. Mais ils ont tous (ou presque) en commun dêtre relativement compliqués à mettre en œuvre, soit en terme technique, soit en terme de ressource.
Cest là quintervient [`garage`](https://garagehq.deuxfleurs.fr/). Son objectif est de faire du S3 avec le moins de ressources possibles (en gros, ça peut tourner sur un Raspberry Pi, littéralement) tout en conservant un minimum de fonctionnalités propre à ce type de service: redondance des données, transfert optimisé, etc… Il est également conçu pour supporter des latences réseaux relativement élevées (de lordre de 200 ms au max), ce qui nest généralement pas le cas de ces concurrents.
Et en plus, cest écrit en Rust ❤️
# Bon OK, comment quon fait du coup?
On va partir sur une architecture volontairement un peu compliquée pour que tu puisses bien te rendre compte des possibilités offertes par `garage`. En fait, on va créer un cluster avec 6 nœuds au total (que jai appelé `garage{1..5}` et `garagegw1`), dans 3 zones différentes dont un nœud passerelle (jexpliquerai le pourquoi de la passerelle plus tard). Il faut savoir que `garage` est conçu pour assurer lui même lintégrité et la redondance des données, il nest donc généralement pas nécessaire davoir des systèmes super résilients sur les nœuds eux-mêmes (oubliez les RAIDs et autres systèmes de redondance, `garage` fait tout pour vous).
Jai donc monté 6 containers LXC pour ce faire, histoire de me simplifier la vie. Première chose, il faut aller télécharger lexécutable `garage` sur lensemble des 6 nœuds (dans `/usr/local/bin/garage` par exemple avec les bonnes permissions). Tu peux le compiler toi-même si tu as la patience, sinon tu peux le télécharger directement [ici](https://garagehq.deuxfleurs.fr/download/) (il sagit dun exécutable statique compilé avec `musl`, il est donc énorme, mais a lavantage de tourner nimporte où sans autre prérequis).
Petite précision: `garage` étant un exécutable sans aucune dépendance, jai choisi de le faire tourner directement sur les containers, mais on pouvait tout aussi bien le faire tourner dans un container Docker (si besoin, limage officielle est `dxflrs/garage`).
Bref, maintenant quon a nos exécutables sur chaque machine, on va pouvoir créer un service `systemd` pour les démarrer. `garage` supporte par défaut la fonction `DynamicUser` de `systemd` qui permet de démarrer un service avec un numéro dUID aléatoire à chaque fois (cest `systemd` qui recolle les morceaux), ce qui est bien pratique pour ne pas se prendre la tête avec les détails genre créer un utilisateur, une *home directory*, etc…
On peut donc créer, sur chaque nœud, le fichier `/etc/systemd/system/garage.service`:
```ini
[Unit]
Description=Garage Data Store
After=network-online.target
Wants=network-online.target
[Service]
Environment='RUST_LOG=garage=info' 'RUST_BACKTRACE=1'
ExecStart=/usr/local/bin/garage server
StateDirectory=garage
DynamicUser=true
ProtectHome=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
```
Les données seront alors stockées `/var/lib/private/garage/{meta,data}` de manière permanente avec un lien symbolique dans `/var/lib/garage` quand le service est démarré. Mais avant de faire cela, il va falloir créer un fichier de configuration spécifique à `garage`, `/etc/garage.toml`:
```toml
metadata_dir = "/var/lib/garage/meta" # obligatoire sinon rien ne va fonctionner
data_dir = "/var/lib/garage/data" # pareil
db_engine = "lmdb" # cest la DB recommandé par défaut pour stocker les métadonnées
replication_mode = "3" # le mode de réplication. Nous voulons ici une redondance max, ce sera donc 3.
# on peut mettre 2 ou même 1 en fonction de la redondance que lon souhaite.
compression_level = 2 # le degré de compression zstd, de 1 à 19, plus cest élevé, plus cest compressé
# plus ça consomme de ressources
rpc_bind_addr = "[::]:3901" # ladresse par défaut pour le protocole RPC de garage
rpc_public_addr = "garage1.lxc.arpa:3901" # son nom public
# ça, cest le secret RPC qui doit être partagé entre tous les nœuds,
# on peut le générer avec la commande `openssl rand -hex 32`
rpc_secret = "814297d51a3878c66ac4db89e20539b980dd70f43be35dd884d0c2f68aab6c90"
# ça, cest pour accéder à lAPI S3
[s3_api]
s3_region = "garage"
# concernant les nœuds de stockage, on peut éventuellement écouter sur [::1] si on a une passerelle
api_bind_addr = "[::]:3900"
root_domain = ".lxc.arpa"
```
Petite explication complémentaire: `garage` utilise un protocole chiffré qui lui est propre pour communiquer entre les nœuds (ça correspond aux indications `rpc_*`), parfaitement utilisable sur un réseau non-sûr comme Internet. Par contre, par défaut, le S3 est en HTTP et non en HTTPS. Donc si tu dois exposer lAPI S3, il faudra probablement rajouter un reverse proxy devant (ou utiliser une autre astuce que nous verrons un peu plus loin).
Bref, maintenant tu peux démarrer le service `garage` sur chaque nœud et faire un:
```bash
# garage status
==== HEALTHY NODES ====
ID Hostname Address Tags Zone Capacity
f1a78b6b674c054d garage1 127.0.1.1:3901 NO ROLE ASSIGNED
```
Comme tu peux le constater, pour le moment, il ne fait pas grand-chose…
# Fuuuuuuuuuuuuu-sion!
On va donc se dépêcher tinterconnecter tout ce beau monde. On va donc se connecter sur chaque nœud et récupérer son identifiant avec la commande suivante:
```bash
# garage node id -q
93506740b859618e6b5953092485b7717d186b3b15b61d9c59e20f685ee3122f@garage2:3901
```
Cet identifiant peut ensuite être utilisé sur le tout premier nœud pour faire linterconnection:
```bash
# garage node connect 93506740b859618e6b5953092485b7717d186b3b15b61d9c59e20f685ee3122f@garage2:3901
Success.
```
Et ainsi de suite pour lensemble des nœuds du cluster. À la fin, le statut devrait être le suivant:
```bash
# garage status
==== HEALTHY NODES ====
ID Hostname Address Tags Zone Capacity
32e482de67868f30 garage5 100.64.4.34:3901 NO ROLE ASSIGNED
93506740b859618e garage2 100.64.4.105:3901 NO ROLE ASSIGNED
fe8c6c5dd5730813 garagegw1 100.64.4.238:3901 NO ROLE ASSIGNED
38e9e413613a6afe garage4 100.64.4.217:3901 NO ROLE ASSIGNED
f1a78b6b674c054d garage1 127.0.1.1:3901 NO ROLE ASSIGNED
9bc75561e211a385 garage3 100.64.4.153:3901 NO ROLE ASSIGNED
```
Voilà, tous les nœuds se voient entre eux. Il va maintenant falloir les grouper dans des zones, leur donner des capacités et, optionnellement, désigner une paserelle (ce que nous allons en loccurence faire).
## Les zones
La zone ne donne, en soi, aucune indication particulière: cest simplement un moyen de grouper les serveurs entre eux. Derrière le rideau, `garage` va surtout se servir des zones pour multiplier les redondances de données.
Il faut savoir que si elles sont présentes, les zones serviront de base pour la redondance des données et non les serveurs présents dans chaque zone.
## La passerelle
Les passerelles sont simplement des nœuds `garage` qui ne stockent pas de données, mais permettent daccéder aux données via S3. Imaginons que tu ne souhaites pas exposer directemet lAPI S3 sur Internet mais tu ais besoin dun de tes services y accède. Tu peux donc simplement monter un `garage` sur la même machine que celle hébergeant le service en question, le faire écouter sur `localhost` sur ce même serveur et le grouper avec tous les autres. Comme ça, tu peux effectivement accéder à tes *buckets* S3 et leurs données, mais sans jamais exposer publiquement `garage` et sans copier les données sur le serveur susnommé.
## Les capacités
Alors là, ça se complique un tout petit peu. Les capacités sentendent au sens disque, mais en **relatif** dun nœud à lautre. Donc si tu mets 10/10/10 dans une configuration à 3 nœuds, tu indiques simplement que tous les nœuds peuvent héberger la même quantité de données (tu pourrais aussi bien mettre 1/1/1). Si tu mets 10/20/30, tu indiques que le nœud 30 a 3 fois plus de capacité que le nœud 10 et 50% de plus que le nœud 20. **Ça nindique donc pas vraiment la capacité, mais plus la façon dont il faut répartir les données.**
Au passage, une autre fonctionnalité intéressante de `garage`, la plupart des autres logiciels faisant du S3 que je connais obligent à avoir des nœuds de capacité identique, aussi bien dun point de vue stockage, que mémoire ou puissance de calcul.
## Allez, chauffe Marcel!
Allons mettre tout cela en musique. Nous avons 5 nœuds de stockage, nous allons donc les répartir en 3 zones: `maison` (qui simulera les babasses quil y a chez toi), `mere-grand` (qui sera le serveur que tas mis en loucedé chez ta grand-mère) et `boulot` (pour celui que tu as laissé dans le placard à balai du taf un jour que tétais bourré). Nous admettrons aussi que les serveurs `maison` sont identiques en terme de capa, les autres serveurs des autres zones seront 3 fois plus gros chacun (histoire davoir une copie complète de toutes les données dans chaque zone).
```bash
# garage layout assign -z maison -g fe8c6c5dd5730813
# garage layout assign -z maison -c 10 f1a78b6b674c054d
# garage layout assign -z maison -c 10 93506740b859618e
# garage layout assign -z maison -c 10 9bc75561e211a385
# garage layout assign -z mere-grand -c 30 38e9e413613a6afe
# garage layout assign -z boulot -c 30 32e482de67868f30
```
Voilà, avec ça on va avoir un `layout` relativement cohérent par rapport à lénoncé plus haut. Tu peux le voir avec `garage layout show` et lappliquer avec `garage layout --version 1`. Tout ceci est effectivement versionné en cas de besoin mais fais quand même attention: chaque fois que tu fais un changement significatif sur le design général, cela entraîne des mouvements de données pour répondre à ce besoin. Donc autant sur un cluster vide, comme cest le cas actuellement, les conséquences seront moindres, autant sur un cluster rempli ras la gueule, ça risque dêtre bien plus compliqué.
# Jfais quoi moi maintenant? Tu te casses!
Bon ben maintenant, on va pouvoir créer un *bucket*, une clé et téléverser quelques fichiers pour vérifier que tout va bien (et observer comment que ça marche derrière). Pour le client S3 à utiliser, je recommande personnellement `mc`, [le client MinIO](https://min.io/docs/minio/linux/reference/minio-mc.html).
Commençons par créer un *bucket*:
```bash
# garage bucket create potdechambre
Bucket potdechambre was created.
# garage key new --name caca
Key name: caca
Key ID: GK3fdf189892c2c7665049631d
Secret key: 0fd528f075d06ce05bf6e020e50d42bb37e288e1bb36d9a38076ef06f541958a
Can create buckets: false
Key-specific bucket aliases:
Authorized buckets:
```
Il suffit maintenant dassocier la clé en question au *bucket* précédent (on va se mettre tous les droits pour le moment, mais évidemment, on peut faire plus subtil):
```bash
# garage bucket allow potdechambre --owner --read --write --key GK3fdf189892c2c7665049631d
New permissions for GK3fdf189892c2c7665049631d on potdechambre: read true, write true, owner true.
```
Et voilà, il ne reste plus quà tester avec `mc`:
```bash
> mc alias set garage http://garagegw1.lxc.arpa:3900 <access_key> <secret_key>
> mc ls garage/
[2022-12-29 14:07:20 CET] 0B potdechambre/
```
Et donc maintenant, on peut déposer quelques fichiers et voir comment ça se comporte:
```bash
> mc mirror ./Téléchargements garage/potdechambre/
...ur-card.zip: 74.42 MiB / 74.42 MiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 56.43 MiB/s 1s
```
En comptant la compression, on voit bien quon a une répartition à peu près intelligente des données:
```bash
> for i in {1..5}; do echo garage${i}; ssh root@garage${i}.lxc.arpa du -hs /var/lib/private/garage/data; done
garage1
14M /var/lib/private/garage/data
garage2
29M /var/lib/private/garage/data
garage3
23M /var/lib/private/garage/data
garage4
65M /var/lib/private/garage/data
garage5
65M /var/lib/private/garage/data
```
Et bien sûr, on peut récupérer nimporte quel fichier depuis le S3 vers la machine locale:
```bash
> mc cp garage/potdechambre/rnamd /tmp/rnamd
...ambre/rnamd: 794.31 KiB / 794.31 KiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.51 MiB/s 0s
```
# Est-ce que cest vraiment résistant aux pannes?
Et ben, on va essayer!?! Éteignons le nœud 1 et voyons ce quil se passe:
```bash
> mc ls garage/potdechambre
[affiche toujours des trucs]
> mc mirror garage/potdechambre /tmp/Téléchargements
...ur-card.zip: 74.42 MiB / 74.42 MiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 151.55 MiB/s 0s
```
Aucun souci. En fait, on peut même éteindre tous les nœuds dune même zone et continuer daccéder aux données. Par contre, sil y a deux pannes dans deux zones différentes (même dun seul serveur pour le cas de `maison`), `garage` ne peut atteindre un quorum et les données ne sont plus accessibles (elles ne sont pas perdues pour autant, en tout cas pour le moment).
Donc en gros, si tu arrives à avoir 3 copies de tes données dans 3 zones différentes, tant que deux de ces zones sont accessibles, tu nauras pas de souci pour accéder aux données (cest ce que signifie le `replication_mode="3"`, 3 répliques sur lensemble du cluster en essayant de les répartir entre toutes les zones).
# Et ben, cétait pas mal tout ça…
Voilà, cétait un exemple un peu compliqué mais qui permet de bien voir toutes les possibilités offertes par `garage`. Dans le cadre dun auto-hébergement, on pourra bien sûr faire bien plus simple avec un seul nœud par exemple ou deux nœuds dans deux zones différentes, chez toi et chez un pote genre.
Et maintenant tu vois à quoi ça ressemble, et tu peux éventuellement commencer à imaginer ce que tu pourrais en faire. Dans un prochain article, on regardera comment on peut lutiliser comme moteur de stockage pour Nextcloud.

View File

@@ -0,0 +1,225 @@
+++
title = "Migrer Nextcloud dun stockage local à S3"
date = 2023-01-15
[taxonomies]
tags = [ "système", "s3", "garage", "stockage", "nextcloud" ]
+++
> Ah bah ça, ça tombe bien alors!
*La foule en délire*
<!-- more -->
**Avertissement**
Même si je suis certain de mon coup et que cette procédure a été testé et validé avec succès sur quelques instances Nextcloud, elle doit tout de même se faire à vos risques et périls: je ne peux offrir aucune garantie quant à sa réussite et je ne lai jamais testée sur des instances de grande ampleur (plusieurs dizaines, voire centaines de gigaoctets, avec des partages dans tous les sens). Voilà, tes prévenu, prends tes précautions.
# Introduction
Lidée de cette présente est de permettre à tout un chacun de migrer les données hébergées en local sur son instance Nextcloud vers du S3 (dans un [garage](https://blog.libertus.eu/tags/garage/), pourquoi pas). Cette procédure nétant absolument pas prévu par Nextcloud (aucun outil officiel, même pas une doc), elle a été faite à la main avec sueur et amour ❤️.
# Fais une maudite sauvegarde ostie de marde!
Alors, avant de commencer à rentrer dans le dur, deux ou trois précautions à prendre, quoiquil arrive:
* **Éjecte tous tes utilisateurs de linstance**. Tous. Mémé, son chien, ton petit frère, faut virer tout le monde. Cest pas des blagues hein, tu le fais, je tattends (Note: le plus simple est dactiver le mode maintenance en ligne de commande via `occ maintenance:mode --on`). Pour les clients de synchro et les clients Web, cela peut prendre jusquà une demi-heure pour que toutes les sessions soient bien mortes côté serveur, on y reviendra.
* **Fais une putain de sauvegarde de absolument tout**. Là, cest pareil, vas-y, je tattends. On se fout que ça prenne 10 minutes ou 2h, mais il faut impérativement que tu ais une sauvegarde de tout: base de données **et** données utilisateurs.
* Arme-toi de patience (et dune bière).
# La structure S3 sous Nextcloud
Les données ne vont pas être structurées de la même manière sur S3 et sur ton disque local. En fait, quand tu navigues dans ton Nextcloud, tu navigues essentiellement dans la structure de la base de données. En gros, tout est virtuel.
Quand tu passes en S3, cette structure, cette hiérarchie de fichiers qui était si familière, va être complètement explosée: pour des questions doptimisations, le greffon S3 stocke tous les fichiers à plat à la racine du seau S3 en les identifiant avec leur `fileid` (extrait directement de la base de données).
Il va donc falloir **transformer** la structure de tes dossiers et fichiers en une structure mangeable par Nextcloud S3. Pour cela et pour éviter de faire des copies dans tous les sens, on va **recréer** la structure en question avec des liens symboliques dans un dossier de transfert et ensuite, on va faire une synchro complète vers S3.
# Première étape: nettoyer la base de données
Cest loin dêtre une obligation mais malheureusement, Nextcloud est extrêmement mauvais pour faire le ménage dans ses propres tables de fichiers et de cache. On va sintéresser à deux tables essentiellement: `oc_filecache` (qui contient en fait le cache de tous les fichiers, leurs propriétés de base, etc…) et `oc_storages` (qui contient en fait les différents stockages des différents utilisateurs et le stockage de linstance elle-même).
La première chose à vérifier est assez simple: **il faut impérativement sassurer quaucune entrée dans `oc_filecache` ne fasse référence un stockage inexistant dans `oc_storages`**. Les deux tables sont liés par lattribut `oc_filecache.storage = oc_storages.numeric_id`.
On peut facilement vérifier le nombre dentrées avec la commande SQL suivante:
```sql
SELECT storage, count(storage)
FROM oc_filecache
GROUP BY storage;
SELECT * FROM oc_storages;
```
Logiquement, la colonne `storage` de la première requête devrait correspondre exactement à la sortie de la seconde requête. Si ce nest pas le cas, **vérifie quil ny a pas une cagade entre les deux**. Sur ma vieille instance, mes fichiers les plus anciens étaient en double dans pratiquement toute la table `oc_filecache` (je suppose que cest une mise à jour qui sest mal passée).
En toute logique, la table `oc_storages` ne devrait contenir que:
* une entrée `local::<chemin vers le stockage de ton nextcloud>`
* plusieurs entrées `home::<utilisateur>`
Si tu vois des utilisateurs en double, des `oc_filecache.storage` qui ne correspondent plus à rien, il est temps de faire le ménage.
*Note: il est très certainement possible de faire une requête SQL des familles pour accomplir cela en une fois, je nai pas creusé, un truc de plus à faire si besoin…*
Toujours dans le même état desprit, cela peut être intéressant de purger les corbeilles et les versions de fichiers (ça fera toujours ça de moins à transférer). À noter quil est nécessaire pour cela de sortir du mode maintenance. Donc, si tu as prévu une migration un jour donné, ça peut être intéressant de commencer à faire le ménage *in vivo* quelques jours ou quelques semaines avant.
# Deuxième étape: préparer les données
Bon voilà, maintenant que tu as mis tout en maintenance, que tu as fait une grosse sauvegarde de tout et que ta base de données est nettoyée (autant que faire se peut en tout cas), on va pouvoir attaquer le vif du sujet.
Donc, on va commencer par établir une liste exhaustive de tous les fichiers des utilisateurs et les mettre dans un fichier assez simple avec une association `urn:oid:<numéro de fichiers>` vers le chemin actuel.
Pour cela, un petit coup de shell/sql:
```bash
mysql -B --disable-column-names -D <ta base de données> << EOF > user_file_list
SELECT CONCAT('urn:oid:', fileid, ' ', '<chemin vers les données Nextcloud>', SUBSTRING(id from 7), '/', path)
FROM oc_filecache JOIN oc_storages
ON storage = numeric_id
WHERE id LIKE 'home::%'
ORDER BY id;
EOF
```
Tu prendras évidemment soin de remplacer le nom de la base de données ainsi que le chemin où se trouve les données. Il sagit bien du répertoire de données; par défaut, il sagit du répertoire `data` à la racine de linstallation, mais techniquement, ça peut être nimporte où.
Logiquement, tu devrais obtenir un fichier dont les lignes ressemblent à ça(cest, au passage, le bon moment de vérifier que les chemins en question sont bons):
```
urn:oid:120795 /srv/nextcloud/user1/files/Documents/Welcome to Nextcloud Hub.docx
```
De la même manière, on va établir la liste des métadonnées (les données exploitées par Nextcloud lui-même et qui sont stockées dordinaire dans le répertoire `appdata`):
```bash
mysql -B --disable-column-names -D <ta base de données> << EOF > meta_file_list
SELECT CONCAT('urn:oid:', fileid, ' ', SUBSTRING(id from 8), path)
FROM oc_filecache JOIN oc_storages
ON storage = numeric_id
WHERE id LIKE 'local::%'
ORDER BY id;
EOF
```
# Troisième étape: transférer les données
Lidée générale est de créer des liens symboliques vers les «vraies» données pour éviter de faire une copie en local avant de balancer une copie vers le S3. Comme on vient détablir la liste des données pour les métadonnées et les données utilisateurs, ce nest pas bien compliqué:
```bash
mkdir s3_files
cd s3_files
while read target source ; do
if [ -f "$source" ] ;
then
ln -sv "$source" "$target"
fi
done < ../user_file_list
while read target source ; do
if [ -f "$source" ] ;
then
ln -sv "$source" "$target"
fi
done < ../meta_file_list
```
En toute logique, tu devrais te retrouver avec un répertoire contenant quelques dizaines/centaines/milliers de liens symboliques.
Une bonne idée pour vérifier que rien na merdé, cest de compter les liens en question:
```bash
ls -l | wc -l
find <chemin vers les données Nextcloud> -type f | wc -l
```
Tu peux aussi vérifier la taille de lensemble:
```bash
du -Lhsc *
```
Si tu es confort avec les chiffres que tu vois (quau moins ils ont le bon ordre de grandeur), tu peux passer à la suite.
*Note: à ce stade, je pars du principe que tu as créé le bucket S3 et la clé correspondante qui va bien dans `garage`.*
Pour la partie transfert en elle-même, jai essayé [le client MinIO](https://min.io/download) sans grand succès à chaque fois. Je ne sais pas pourquoi, mais ça a toujours merdé avec ce client.
Du coup, je me suis décidé à passer par `awscli` pour éviter les emmerdes. Peu importe comment tu linstalles (via le gestionnaire de paquets ou depuis `pip`), limportant cest quil soit configuré correctement: utilise la même clé et le même secret dans `~/.aws/credentials` et noublie pas de mettre la région `garage` dans `~/.aws/config`. Logiquement, cest tout ce dont tu auras besoin.
On se place ensuite dans le répertoire `s3_files` contenant lensemble des liens symboliques et on sarme dune autre bière et de beaucoup de patience:
```bash
aws --endpoint-url http://<adresse de garage>:3900 s3 sync . s3://nextcloud
```
Évidemment, la commande est à adapter à la situation. Si un transfert merdouille, le simple fait de la relancer doit suffire à retransférer les fichiers qui ont merdé. On peut également surveiller ce quil se passe côté `garage` pour être certain que rien ne foire de manière hyper évidente.
**En toute logique, si tu relances la commande et quil ne se passe plus rien, cest que le transfert est intégralement terminé.**
On va donc pouvoir passer au nettoyage/adaptation de la base.
# Quatrième étape: adaptation de la base de données
Les données sont transférées mais la base de données à toujours les anciennes références, il faut donc les adapter. Première chose donc, modifier les `storages` utilisateur:
```sql
UPDATE oc_storages
SET id = CONCAT('object::user:', SUBSTRING(id from 7))
WHERE id LIKE 'home::%';
```
Tu transformes ici les références `home::<utilisateur>` en référence `object::user:<utilisateur>` (note que le `:` seul à la fin est normal). Il faut ensuite transformer le stockage de Nextcloud lui-même:
```sql
UPDATE oc_storages
SET id = 'object::store:amazon::<nom du bucket S3>'
WHERE id LIKE 'local::%';
```
**Normalement**, tu ne devais avoir quun seul `local::*` dans cette table. Si ce nest pas le cas, cest peut-être quun truc est resté allumé malgré la maintenance ou quil sest passé autre chose. Quoiquil arrive, ça vaut le coup dêtre vérifié avant de continuer.
**Normalement**, à partir de cette étape, on ne touchera plus à la table `oc_storages` et elle ne devrait donc contenir que deux types dentrées:
* une entrée `object::user:<utilisateur>` pour chaque utilisateur
* une entrée `object::storage:amazon::<nom du bucket S3>`
La casse, les `:` ou `::`, le nom des utilisateurs, le nom du bucket S3, **tout cela a une importance**, donc il vaut mieux vérifier 3 fois pour être sûr.
Dernière adaptation, il est nécessaire de faire des changements dans la table `oc_mounts` (qui gère notamment comment les points de montage comme les partages vont être gérés par Nextcloud):
```sql
UPDATE oc_mounts
SET mount_provider_class = 'OC\\Files\\Mount\\ObjectHomeMountProvider';
```
Cette table devrait normalement contenir:
* une entrée par utilisateur
* une entrée par partage
La colonne `mount_provider_class` devrait maintenant être uniforme avec la valeur `OC\Files\Mount\ObjectHomeMountProvider` (comme dhabitude avec les caractères déchappement de type `\`, se méfier du résultat).
# Dernière étape: configurer Nextcloud et vérifier lensemble
Bon, on a bien transpiré, il est temps de finir la configuration. Dans le fichiers `config/config.php` de Nextcloud, tu peux maintenant ajouter ton instance `garage` avec les mêmes informations que tu avais configuré dans `awscli` directement dans lobjet `$CONFIG`:
```php
'objectstore' => [
'class' => '\\OC\\Files\\ObjectStore\\S3',
'arguments' => [
'bucket' => '<nom du bucket S3>',
'autocreate' => false,
'key' => '<clé>',
'secret' => '<secret>',
'hostname' => '<adresse de garage>'
'port' => <port de garage>,
'use_ssl' => false, // à adapter si tu as un reverse proxy avec du TLS
'region' => 'garage',
'use_path_style' => true
],
],
```
Tu peux maintenant virer la maintenance, te reconnecter sur linterface Web et vérifier que tout fonctionne correctement. Mon conseil: passer dans quelques partages, ouvrir des fichiers que tu na pas accédé depuis un petit moment. Ça peut être le bon moment aussi pour relancer une synchro complète sur un client vierge histoire de vérifier que tous les accès sont bons.
Dès que tu es suffisamment confiant dans ta migration, tu peux virer les répertoires de données dorigine et virer le répertoire contenant les liens symboliques.
# Conclusation
Et ben, on va pouvoir prendre des vacances bien méritées avec ça…

View File

@@ -0,0 +1,179 @@
+++
title = "Passer une borne Ubiquiti sous OpenWRT"
date = 2023-02-05
[taxonomies]
tags = [ "système", "réseau", "openwrt", "ubiquiti" ]
+++
![img](/2023/02/openwrt.jpg)
<!-- more -->
# Ce quUniFi était
Il était une fois un bon logiciel. Il permettait à ses utilisateurs de gérer une flotte de bornes Wi-Fi avec facilité, de les grouper en site, dutiliser des fonctionnalités avancées. En plus, il ne bloquait pas ses utilisateurs dans lécosystème, leur permettait dinstaller ce logiciel sur une Debian ou une Ubuntu, était relativement peu cher à lachat, ne nécessitait pas dabonnement.
Bref, ce logiciel était bien et la vie était belle. Et ce logiciel sappelait *UniFi*.
Tout allait bien dans le meilleur des mondes mais ensuite, est venu le pire ennemi du logiciel. Le temps…
Le temps passait, les composants du logiciel devenaient vieux et fatigués. Le logiciel ne pouvait par exemple pas supporter une version de MongoDB supérieur à 3.6 (alors de que cette dernière est en fin de vie depuis longtemps) mais demandait une version de Java au moins égale à 11. Ce qui faisait que les utilisateurs étaient obligés soit dutiliser une Ubuntu Focal Fossa spécifiquement, soit une Debian Stretch mais en utilisant la version *backports* de Java.
Et les utilisateurs commençèrent à en avoir plein le cul (enfin, surtout un), surtout pour gérer une seule et unique borne.
Plutôt que de faire évoluer le logiciel pour que ces briques de base puissent supporter des versions plus modernes de MongoDB ou de Java, ou de rendre son installation plus simple, le logiciel a choisi de continuer de grossir et doffrir toujours plus de fonctionnalités gadgets dont personne ne se sert.
Alors le petit utilisateur en eut marre et décida que niquez-vous, vais le faire moi-même, marchera mieux…
# La borne elle-même
Alors, en fait, si tu te connectes à une borne *UniFi* (jai une UAP-AC-Lite, mais je suppose que cest à peu près pareil pour les autres modèles), tu pourras constater que cest déjà une *OpenWRT* (!). En fait, pour être précis, il sagit dune *LEDE* un fork du projet *OpenWRT* destiné justement à le moderniser et qui a depuis refusionner avec ce dernier, en Janvier 2018:
```
enbarr-BZ.6.2.49# cat /etc/openwrt_release
DISTRIB_ID='LEDE'
DISTRIB_RELEASE='17.01.6'
DISTRIB_REVISION='r3979-2252731af4'
DISTRIB_CODENAME='reboot'
DISTRIB_TARGET='ar71xx/ubnt'
DISTRIB_ARCH='mips_24kc'
DISTRIB_DESCRIPTION='LEDE Reboot 17.01.6 r3979-2252731af4'
DISTRIB_TAINTS='no-all mklibs busybox'
```
Précision pour pouvoir se connecter directement à la borne Wi-Fi: sous *UniFi*, allez dans les options, puis *System*, puis *Advanced* et tout en bas, vous pouvez révéler le mot de passe `admin` de lensemble des bornes. Il suffit ensuite de sy connecter en SSH.
Ce qui est intéressant avec cette borne, cest quelle était fraîchement mise à jour de quelques heures au moment décrire ces lignes. Donc, on a bien une version très largement obsolète de *LEDE Project* redevenu *OpenWRT*.
# Downgrade, upgrade, degrade
[Pour cette partie, je ne fais que suivre la documentation officielle, tu peux directement ty référer.](https://openwrt.org/toh/ubiquiti/unifiac) Jai donc utilisé la méthode non-invasive qui me paraissait la plus simple. Première chose à faire, remettre un [vieux firmware sur la borne](https://www.ui.com/download/). Il suffit daller télécharger le bon et le forcer via la console *UniFi*.
Une fois, la borne redémarrée, on peut de nouveau sy connecter en SSH, aller dans `/tmp`, créer un dossier `flash` et y télécharger les éléments suivants:
* [le firmware OpenWRT](https://downloads.openwrt.org/releases/22.03.3/targets/ath79/generic/openwrt-22.03.3-ath79-generic-ubnt_unifiac-lite-squashfs-sysupgrade.bin) (à adapter, voire le lien au-dessus)
* [lutilitaire `mtd`](https://downloads.openwrt.org/releases/21.02.0/targets/ath79/generic/packages/mtd_26_mips_24kc.ipk)
* [la librairie C qui va avec](https://downloads.openwrt.org/releases/21.02.0/targets/ath79/generic/packages/libc_1.1.24-3_mips_24kc.ipk)
* [une autre librairie complémentaire](https://downloads.openwrt.org/releases/21.02.0/packages/mips_24kc/base/libubox20210516_2021-05-16-b14c4688-2_mips_24kc.ipk)
Tu remarqueras que les versions ne correspondent pas. Cest à peu près normal, rien dinquiétant. Une fois quon a fait tout ça, on peut tranquillement décompresser le tout:
```bash
tar -xzOf libc_1.1.24-3_mips_24kc.ipk ./data.tar.gz | tar -xvz
tar -xzOf mtd_26_mips_24kc.ipk ./data.tar.gz | tar -xvz
tar -xzOf libubox20210516_2021-05-16-b14c4688-2_mips_24kc.ipk ./data.tar.gz | tar -xvz
```
Tu as maintenant tout ce quil faut pour *flasher* la borne, il suffit de sy mettre en deux étapes:
```bash
LD_LIBRARY_PATH=${PWD}/lib lib/ld-musl-mips-sf.so.1 sbin/mtd write openwrt-*.bin kernel0
LD_LIBRARY_PATH=${PWD}/lib lib/ld-musl-mips-sf.so.1 sbin/mtd erase kernel1
```
On vient de mettre à jour le `kernel0` de `mtd` (je ne sais pas exactement à quoi ça correspond, mais je suppose que cest du stockage avec plusieurs entrée de démarrage, façon OPNSense), et effacer le `kernel1`. Il suffit alors dindiquer à `mtd` que lon souhaite démarrer sur le `kernel0`:
```bash
grep bs /proc/mtd # cette commande doit renvoyer bs quelque part
mtd7: 00020000 00010000 "bs"
dd if=/dev/zero bs=1 count=1 of=/dev/mtd4 # écrase le premier octet de mtd4 avec 0x00
hexdump -C /dev/mtd4 | head # devrait indiquer 0x00 pour le premier octet
```
Une fois les opérations effectuées, tu peux redémarrer la borne (soit un `reboot`, soit on débranche). Au bout dune grosse minute, la borne doit redémarrer avec ladresse par défaut `192.168.1.1/24` et devrait être joignable sur le réseau local.
Première chose à faire: dans LuCI (linterface Web d*OpenWRT*), dans *Network » Interfaces » LAN » DHCP Server » General Setup*, désactive le DHCP pour cette interface (ça fera du bordel en moins). Tant que ty es, tu peux aussi complètement démonétiser le *router advertisement* dans les mêmes paramètres sous *IPv6 Settings*.
À partir de ce moment, il suffit de remettre une adresse correcte à la machine (en DHCP ou une adresse fixe) et cest en gros terminé. Tu peux recréer ton SSID et cest réglé. Tu remarqueras quil y a deux interfaces `radio0` et `radio1` pour le Wi-Fi (une antenne fait du 2,4Ghz, une autre du 5Ghz). Cest normal, *UniFi* présentait tout de manière « unifiée » (LOLOLOLOL) mais *OpenWRT* te laisse le choix de ce que tu veux mettre où. Si tu ne veux pas te casser la tête, tu peux simplement remettre les mêmes paramètres sur les deux et laisser le périphérique qui se connecte choisir la meilleure connexion pour lui (de manière générale, ça ne devrait pas poser plus de problème que ça).
Mais si tu avais une configuration un peu plus compliquée, je tinvite à continuer de lire.
# Un poil plus compliqué
Originellement, javais une configuration un poil plus compliqué: en fait, la borne était certes adressable sur un bout de réseau mais faisait plusieurs ponts SSID/VLAN (deux pour être précis). Lidée était davoir la borne isolée sur le réseau où se trouvait le serveur *UniFi* dun côté, mais davoir mon VLAN domestique et mon VLAN IoT disponibles depuis la borne de lautre.
Doù cette configuration « un peu » compliquée. La borne étant restée branchée de la même manière, elle a donc toujours nativement accès au VLAN isolé et les deux autres VLANs sont « taggés » via le switch derrière.
Il va donc falloir faire avaler cette configuration à *OpenWRT*. Absolument tout ce qui suit peut-être fait en ligne de commande (et je vais mettre les fichiers de configuration résultant à la fin), mais jécris ce petit guide pour linterface Web (parce quil y en a une plutôt claire, donc autant en profiter). Évidemment, il faudra adapter à ton cas spécifique si nécessaire.
Première chose à faire: vider complètement les règles de firewall. Elles sont conçues pour un routeur faisant du NAT, elles risquent donc te compliquer la vie dans un premier temps. Rien ne tempêche den remettre après si tu le souhaites, mais pour un point daccès Wi-Fi débile, je pense que cest complètement inutile.
Donc, dans *Network » Firewall » Traffic Rules*, tu peux tout virer. Tout.
Deuxième chose: il va falloir dire à *OpenWRT* que le *bridge* par défaut a des VLANs à disposition. Pour cela, il faut se rendre dans *Network » Interfaces » Devices* et cliquer sur *Configure…* pour le *bridge* en question.
Dans le dernier onglet, on voit une option *Bridge VLAN filtering*. Il suffit de cocher la case et ensuite, tu peux entrer les VLANs qui sont taggés, natifs, etc… Pour le VLAN natif (celui qui est *untagged* justement sur linterface de la borne), je te recommande de mettre le VLAN 1 et les options *Egress untagged* **et** *Primary VLAN ID*. Jai essayé dautres combinaisons sans beaucoup de succès et je ne sais pas si le souci vient d*OpenWRT* ou de la façon dont le switch fonctionne (jen doute…).
Bref, avec cette configuration, tu devrais avoir accès à des VLANs depuis le *bridge*. **Surtout napplique pas tout de suite la configuration, sinon tu perdras la main et il faudra tout recommencer!**
Donc, une fois rendu là, il faut créer de nouveaux *devices*. Tu peux donc procéder pour en créer au moins un `br-lan.1` qui correspondra au fameux *VLAN 1* de tout à lheure. Au moment de créer ces *devices*, tu vas voir un menu déroulant dans *Existing device* et pourvoir choisir *SoftwareVLAN: "br-lan.1" (lan)*, cest celui quil faut choisir.
Tu peux créer les autres VLANs dont tu as besoin de la même manière (juste attention, il y a une limite au nombre de SSID sur chaque interface, donc créer 20 000 VLANs nest probablement pas possible).
Une fois que tout cela est fait, retourne dans *Network » Interfaces* et associe linterface *LAN* (créée par défaut) à `br-lan.1`. À partir de ce moment-là, tu peux appliquer, tu ne devrais pas perdre la main.
Et il ne reste plus quà créer les autres *Interfaces* pour *OpenWRT* selon les VLANs que tu as fait. Tu peux te contenter de les créer et des associer aux interfaces VLAN créées précédemment (avec un nom un peu explicite pour que ce soit plus simple), il ny a rien dautres à configurer.
Une fois tout ceci fait, tu peux créer tes SSID et y associer systématiquement la bonne interface. Le SSID se contentera alors de faire le passe-plat entre Wi-Fi et filaire, tout simplement.
Une petit aperçu de ce que ça donne dans les fichiers de conf derrière, comme promis:
```
config device
option name 'br-lan'
option type 'bridge'
list ports 'eth0'
config interface 'lan'
option proto 'static'
option netmask '255.255.255.0'
option gateway '100.64.0.1'
option ipaddr '100.64.0.100'
option device 'br-lan.1'
list dns '100.64.0.1'
list dns_search 'buttse.cx'
option delegate '0'
option ip6gw 'dead:beef::1'
list ip6addr 'dead:beef::100/64'
config bridge-vlan
option device 'br-lan'
option vlan '200'
list ports 'eth0:t' # ça correspond à un VLAN taggé
config bridge-vlan
option device 'br-lan'
option vlan '406'
list ports 'eth0:t'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'eth0:u*' # et là, le VLAN natif non-taggé
config interface 'vlan200'
option proto 'none'
option device 'br-lan.200'
config interface 'vlan406'
option proto 'none'
option device 'br-lan.406'
```
Évidemment, tout ceci peut encore une fois être fait 100% en ligne de commande, juste il faut tout faire dun coup et sans se louper (donc cest pas si simple).
# Conclusement
Voilà, tu as fièrement passer ta borne en *OpenWRT*, tu as supprimé une VM ou un conteneur bien encombrant et boursoufflé de partout et qui ne servait pas à grand-chose tout en gardant les mêmes fonctionnalités quavant. Et en bonus, tu vas pouvoir faire des trucs en plus sur la borne maintenant que son système est complètement libérée.
Bravo, tu peux te mettre une bonne tape sur lépaule et retourner dormir.
*Màj du 2023-03-08*
Conclusion de la conclusion: ça peut être interéssant de désactiver complètement le routage IPv4 et IPv6 comme la borne ne « route » à proprement parler rien.
Pour cela, on pourra ajouter les lignes suivantes dans le `/etc/rc.local`:
```shell
sysctl net.ipv4.conf.all.forwarding=0
sysctl net.ipv6.conf.all.forwarding=0
```

View File

@@ -0,0 +1,165 @@
+++
title = "Configurer un relais SMTP (proprement)"
date = 2023-04-18
[taxonomies]
tags = [ "système", "smtp", "tem" ]
+++
> Ah oué, cest tant si simple que ça en fait…
*Michel, désespéré…*
<!-- more -->
Coucou mes ptits Rondoudous, ça faisait un moment.
Il mest arrivé récemment une aventure magique avec des relais SMTP et je me suis dit que ce serait la bonne occasion de faire un petit résumé de ce quil faut faire pour gérer des cas un peu tordus.
Lidée est donc de configurer un relais SMTP dabord simple avec un serveur SMTP que tu maîtrises à 100% et qui est dans le même réseau que toi, puis de sattaquer à un relais SMTP un peu plus compliqué, passant par un TEM (_**T**ransactional **E**-**M**ail_). En loccurence, cest celui de *Scaleway* mais tous ont un fonctionnement remarquablement similaire, donc ça devrait pouvoir sappliquer à tous.
Et comme cest un peu le standard, on va faire tout ça avec du *Postfix*, je suppose quon peut aussi avoir des configurations similaires avec dautres serveur SMTP, mais cest ce que jai sous la main (et cest fort probable que toi aussi).
# Commençons par le pourquoi
Dabord, tu pourrais te demander: mais pourquoi diable devrais-je me crever le cul à installer un relais SMTP qui envoient des messages tout propres depuis mes serveurs?
Bah, cest tout con: quand il y a des soucis, le serveur a tendance à le signaler par courriel. Et il y a encore beaucoup de services (*cron* est lexemple le plus frappant) qui informe les utilisateurs par courriel de ce qui se passe.
Ça permet également dans le cas où un logiciel est installé sur le serveur de le faire sappuyer sur le SMTP du relais plutôt que sur dobscures configurations hétérogènes par toujours facile à manipuler.
Alors évidemment, si tu nas ni *cron*, ni *NUT*, ni quoique ce soit qui serait susceptible denvoyer des courriels depuis ton serveur, tu peux parfaitement ten passer. Mais lexpérience montre que ça arrive bien plus souvent quon ne le souhaiterait.
# Relais SMTP avec_Submission_
Un des trucs importants à comprendre dans *Postfix*, cest quil y a une différence fondamentale entre *SMTP* et *SMTPD*: le premier est la partie «cliente» de SMTP, le second est la partie «serveur». Oui, parce que dans certains cas, *Postfix* reçoit du courrier et sarrange pour le traiter correctement (le transmettre à un _**M**ail **D**elivery **A**gent_ par exemple) et dans dautres cas, il doit aller le transmettre à un autre serveur (un autre _**M**ail **T**ransport **A**gent_).
Pour faire tout ça, il y a des ports standards:
* le port 25 est le port SMTP standard quand on ne chiffre pas la communication (ce qui nempêche de la chiffrer _a posteriori_ avec une commande `STARTTLS`);
* le port 465 est le port SMTP standard mais avec chiffrement _a priori_ (donc pas de `STARTTLS`, on est déjà chiffré quand on arrive);
* le port 587 est le port de soumission pour **demander** gentiment à *Postfix* de livrer le message suggéré. Il est chiffré par défaut et demande généralement une authentification (pas forcément complexe mais voilà).
Donc, voilà, pour cette partie, je vais supposer que tu as déjà un serveur SMTP correctement configuré et que tu as accès au port 587 de ce dernier pour balancer tes mails.
En toute logique, cest un cas assez standard et plutôt simple à mettre en œuvre.
Donc côté «client», on peut donc commencer par un `main.cf` de ce type:
```postfix
# des trucs génériques dont on se fout un peu
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
# !!! TRÈS IMPORTANT
append_dot_mydomain = no
myorigin = buttse.cx # il faut mettre ici le domaine dorigine du message,
# sinon, ça risque de poser pas mal de souci
# ça cest les trucs génériques de Postfix
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
# la destination, cest important: cest un relais, donc il nest pas
# supposé recevoir de messages autrement que pour les utilisateurs
# locaux
mydestination = $myhostname, localhost.$mydomain, localhost
# cest là que la magie opère
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
relayhost = [smtp.buttse.cx]:587
# notre serveur ne doit écouter quen local, jamais ailleurs
mynetworks = 127.0.0.0/8
inet_interfaces = loopback-only
smtpd_relay_restrictions = permit_mynetworks,defer_unauth_destination,reject
```
Et donc effectivement, toute la magie tient dans les variables `smtp_sasl_*` et notamment le fichier `/etc/postfix/sasl_passwd`, qui contient lutilisateur/mot de passe pour le serveur SMTP:
```postfix
[smtp.buttse.cx]:587 utilisateur:mot_de_passe
```
Il faut un fichier de hash, donc un petit coup de `postmap` sur le fichier en question et hop!, tout fonctionne! Dès que le serveur veut envoyer un message, il passera par le SMTP local, il aura la bonne adresse source et devrait sappuyer sur le relais qui a été donné.
Ça cétait le cas le plus simple.
# _Submission_failed!
Mais alors que se passe-t-il si ton fournisseur ou ton hébergeur ne te laisse pas accéder au port 587? Généralement, la plupart des TEM ont une config alternatives pour ce cas-là: le port 2525 pour certains, ou comme *Scaleway* le port 2465 (mais le problème est le même pour le port 465). Lidée, cest donc dutiliser le port *SMTPS* avec *Postfix*.
Sauf que loption `relayhost` de *Postfix* ne supporte pas les connexions *SMTPS*. Parce que bien sûr que non.
Du coup, il va falloir ruser pour convaincre *Postfix* de faire comme dhabitude. Et cette ruse, elle nest pas si compliquée que ça, elle sappelle `stunnel`.
Sous Debian/Ubuntu, tu peux donc installer le paquet `stunnel4` et faire une petite configuration de ce type, dans `/etc/stunnel/smtp-wrapper.conf`:
```stunnel
[smtp-tls-wrapper]
accept = 10465
client = yes
connect = smtp.tem.buttse.cx:465
```
Et du coup, lorsque lon démarre le service `stunnel4`, on voit effectivement quil écoute sur le port 10465. Il suffit alors de reconfigurer notre petit *Postfix* comme suit pour sadapter:
```postfix
relayhost = [localhost]:10465
```
Et évidemment, changer également le login/pass/host dans `/etc/postfix/sasl_passwd`:
```sasl_passwd
[localhost]:10465 login:pass
```
Et boum!, ça fait des chocapics!
# Bonus 1: configurer Postfix pour réécrire ladresse cible
Quand il sagit de *cron*, le service a tendance à envoyer les messages à lutilisateur local concerné. Donc si le *cron* `www-data`, qui gère les services Web, se vautre complètement, il enverra un mail à `www-data`, lutilisateur local. Cest super intéressant, mais en loccurence, on préfèrerait largement quil lenvoie à un autre destinataire, jai nommé ladministrateur du système.
Pour cela, on peut forcer *Postfix* à faire une réécriture de tous les messages de destination pour les envoyer ailleurs. Ce nest pas bien compliqué, il suffit dajouter la ligne suivante dans le `main.cf`:
```postfix
recipient_canonical_maps = regexp:/etc/postfix/recipient_canonical
```
Et le fichier `/etc/postfix/recipient_canonical` se présentera comme suit:
```postfix
/.+/ admin@buttse.cx
```
Évidemment, petit malin, rien nempêche daller mettre dautres choses dans ce fichier. Là, tout sera réécrit vers `admin@buttse.cx`, ce nest pas forcément le comportement souhaité _in fine_.
# Bonus 2: configurer Postfix pour réécrire les adresses sources
_Normalement™_, le simple fait davoir un `myorigin` dans le `main.cf` devrait suffire à containdre les adresses locales à utiliser un nom de domaine bien précis.
Malheureusement pour certaines configurations, ce nest pas toujours le cas. Et notamment le `From` et le `Envelope-From` SMTP ne sont pas toujours identiques. Pis, il peut arriver que le TEM ou le relais SMTP refuse le message sil ny a pas correspondance entre ces deux entêtes dans le courriel. Cest en réalité parfaitement logique, lobjectif étant déviter que le message ne se retrouve perdu pour non-respect de SPF/DKIM par exemple.
Mais du coup, ça fait un peu chier…
Heureusement, encore une fois *Postfix* à la rescousse! On peut aussi forcer ce dernier a réécrire les adresses (tous les champs entêtes) pour y mettre une adresse donc on est certain de la conformité.
Pour cela, il va falloir ajouter quelques lignes dans notre `main.cf`:
```postfix
sender_canonical_classes = envelope_sender, header_sender
sender_canonical_maps = regexp:/etc/postfix/sender_canonical_maps
smtp_header_checks = regexp:/etc/postfix/header_check
```
Et on produit donc les deux *maps* permettant de changer les deux entêtes à la fois dans `/etc/postfix/sender_canonical_maps` et `/etc/postfix/header_check` (cest exactement le même fichier):
```postfix
/.+/ youplaboom@mondomainesour.ce
```
Et voilà, tous les courriels vont maintenant venir de `youplaboom@mondomainesour.ce`!
# Conclusation
Bah voilà, maintenant, il ny a plus dexcuses pour ne pas mettre un relais SMTP tout propre sur tes serveurs histoire davoir une idée de ce qui se passe sur le système.

View File

@@ -0,0 +1,260 @@
+++
title = "Grapher les seaux Garage dans Munin"
date = 2023-05-07
[taxonomies]
tags = [ "système", "garage", "munin", "graphe", "auto-hébergement" ]
+++
> Tu vois, cest là quelle a triché la fille…
*Christophe, regardant ses magnifiques graphes Munin*
<!-- more -->
Munin, cest loin dêtre parfait, mais ça a un avantage non négligeable: cest ultra léger. Si tu as besoin de sortir des métriques rapidement pour un système auto-hébergé, ça évite de sortir la très grosse artillerie tout produisant des données utilisables et utiles, pour une complexité relativement réduite.
Il y a quelques jours, je me suis demandé comment je pourrais produire des graphes pour Garage (parce que `garage`, cest bien, mangez-en) histoire davoir des infos que je considère comme essentielles: quantité de données par *bucket*, nombre dobjets et éventuellement téléversements foireux.
Toutes ces données sont accessibles via la commande `garage bucket info <bucket>`, mais je cherchais a pouvoir grapher lensemble de manière un peu plus efficace.
# Métriques dans le code de Garage
Première piste suivie: regarder ce qui est existe déjà dans `garage`. Étant un jeune logiciel, il intègre par défaut tout un tas de protocoles et de standards modernes pour interagir avec dautres éléments assez communs dans les Systèmes dInformation modernes: `consul`, `kubernetes`, etc… Ce qui nous intéresse ici est la partie métrique qui est disponible au format [OpenTelemetry](https://opentelemetry.io/).
Pour lactiver, rien de plus simple, dans `/etc/garage.toml`, il suffit dajouter deux lignes pour activer lAPI `admin`:
```toml
[admin]
api_bind_addr = "[::1]:3903"
```
On redémarre `garage` et on peut alors accéder directement aux métriques via lURL [http://[::1]:3903/metrics](http://[::1]:3903/metrics).
Alors, cest cool, on peut y trouver pas mal de métriques internes à `garage`, mais malheureusement, absolument rien qui concerne les *buckets* eux-mêmes. Dans un contexte de supervision, [la documentation officielle](https://garagehq.deuxfleurs.fr/documentation/reference-manual/monitoring/) donne même les indicateurs quil faut suivre en priorité, et les éventuels soucis quils peuvent représenter.
Sauf que cest pas du tout ce quon veut, on veut simplement grapher lusage.
# Le code source de Garage?
`garage` est écrit en Rust, jai donc jeté un petit coup dœil dans le code. Autant le dire tout de suite: cest bien au-dessus de mes compétences et si jai réussi, à force dessais et derreurs, à introduire des données dans les métriques déjà supportées, en ajouter de nouvelles est une autre paire de manches!
Sans aller jusquà dire que cest impossible (encore une fois, je ne connais que très peu le code et il est dune complexité relativement élevée pour moi), je dirais que ce nest pas forcément la bonne approche.
Après avoir fait le tour de ça, je suis donc revenu à la base: quand on tape `garage bucket info <bucket>`, on obtient bien les données que lon cherche, elles doivent donc bien être disponibles quelque part…
Évidemment la première option, ce serait de parser le résultat de cette commande, mais elle nest visiblement pas vraiment faite pour être parsée (en tout cas, pas facilement), petit exemple:
```
# garage bucket info tarace
Bucket: cdfe[…]28f0
Size: 43.6 GiB (46.8 GB)
Objects: 374678
Unfinished multipart uploads: 1497
Website access: true
Global aliases:
tarace
ta.mere.lol
Key-specific aliases:
Authorized keys:
RW GKXXXX tarace-key
```
Il y a des espaces partout et il est a priori impossible dobtenir les unités brutes pour le stockage (elles sont systématiquement calculées en MiB/GiB/TiB avant dêtre affichées). On peut évidemment, et jai vérifié, bricoler le code source pour lui faire afficher ce que lon souhaite, mais une modif juste pour ça me paraît très largement au-dessus de la nécessité.
Et cest là que je me suis souvenu que `garage` avait aussi une API dadministration sur laquelle on pouvait trouver pas mal de trucs…
# API admin, quand tu nous tiens
Activer lAPI admin est relativement simple, il suffit dajouter un *token* dans `/etc/garage.toml`:
```toml
[admin]
api_bind_addr = "[::1]:3903"
admin_token = "tamerelol"
```
À partir de ce moment, on peut interroger lAPI avec un petit `curl` des familles, tout simplement (lentête doit juste contenir `Bearer <admin_token>` dans un champs `Authorization` et le tour est joué):
```bash
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/status"
{
"node": "153d5ffd1f2be62c1eec4f318ba862e4aefcb28be6bc778422d22ada69cef904",
"garageVersion": "git:v0.8.1-209-g1ecd88c-modified",
"garageFeatures": [
"k2v",
"sled",
"metrics",
"bundled-libs"
],
[]
}
```
Et donc, il suffit dutiliser le *endpoint* `bucket` pour récupérer les infos dont on a besoin:
```bash
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/bucket"
[
{
"id": "dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2",
"globalAliases": [
"tamerelol"
],
"localAliases": []
}
]
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/bucket?id=dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2"
{
"id": "dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2",
"globalAliases": [
"tamerelol"
],
"websiteAccess": false,
"websiteConfig": null,
"keys": [
{
"accessKeyId": "GK94ecde83f07248fb33b81d34",
"name": "Unnamed key",
"permissions": {
"read": true,
"write": true,
"owner": true
},
"bucketLocalAliases": []
}
],
"objects": 24,
"bytes": 19420604,
"unfinishedUploads": 12,
"quotas": {
"maxSize": null,
"maxObjects": null
}
}
```
À partir de ce moment-là, cest essentiellement une question de rédiger un script correct pour Munin.
# Chargez les Munin-tions (oui, cest très mauvais)
Le prérequis pour faire un script Munin est assez minimaliste en réalité. Il faut:
* avoir un morceau de script capable de renvoyer les caractéristiques des graphes si on lui ajout lentrée `config`;
* être capable de sortir un chiffre par ligne dans un format assez basique.
Cest donc simplement une affaire de récupérer les bonnes données, les mettre en forme et merci kiki.
On va ici simplement sattaquer à la donnée qui concerne les objets dans les *buckets*. Les autres graphes auront exactement le même script et la même forme, il sagit simplement de changer quelques données dedans à chaque fois (jexpliquerai un peu plus loin quelques subtilités).
Histoire davoir un truc présentable, on va aussi essayer de faire afficher à Munin les noms des *buckets* plutôt que leur identifiant. À noter quavec lAPI admin de `garage`, il est impossible dappeler le *endpoint* `bucket` avec un nom. Seuls les identifiants sont acceptés. On va donc utiliser du `bash` avec un tableau associatif (histoire de pouvoir la translation identifiant/nom plus facilement).
Première chose à faire donc, stocker en « sécurité » notre `admin_token` pour quil soit accessible depuis tous nos scripts Munin. Pour cela, on peut aller lajouter comme une variable denvironnement pour les scripts commençant par `garage*` dans `/etc/munin/plugin-conf.d/garage`:
```
[garage*]
env.BEARER tamerelol
```
La variable shell `${BEARER}` contiendra systématiquement ce *token* quand on lexécute dans lenvironnement de Munin. Voilà le résultat après une grosse heure de script:
```bash
#!/bin/bash
HEADER="Authorization: Bearer ${BEARER}" # on forme lentête un peu en avance,
# on va sen resservir souvent
declare -A BUCKETS=() # on déclare le tableau associatif
# on va aller récupérer lensemble des buckets via lAPI.
# On utilise jq pour interpréter les résultats et les stocker sous forme:
# <id>,<name>
# un par ligne
API_BUCKETS_JSON=$(curl -s -H "${HEADER}" "http://[::1]:3903/v0/bucket" | jq -r '.[] | .id + "," + .globalAliases[0]')
# on va peupler notre tabeau associatif en interprétant bêtement chaque ligne
for bucket in ${API_BUCKETS_JSON}
do
BUCKETS+=([$(echo ${bucket} | cut -d ',' -f 1)]="$(echo ${bucket} | cut -d ',' -f 2)")
done
# Ceci sert uniquement à la partie configuration:
# on va y poser les labels, le type de graphe, ainsi que le nom de chaque
# courbe. Ce nom sera donc automatiquement le nom du bucket S3.
case $1 in
config)
cat << 'EOM'
graph_title Objects by Bucket
graph_vlabel Number of objects
graph_args --base 1000 -l 0
graph_category garage
EOM
for i in "${!BUCKETS[@]}"
do
echo "${BUCKETS[${i}]}.label ${BUCKETS[${i}]}"
done
exit 0;;
esac
# ceci est un exécution normale du script
# on y refait un appel à lAPI mais avec lidentifiant du bucket cette fois-ci
# comme ça, on peut récupérer la donnée qui nous intéresse, toujours via jq
# et lafficher proprement!
for i in "${!BUCKETS[@]}"
do
OBJECTS=$(curl -s -H "${HEADER}" "http://[::1]:3903/v0/bucket?id=${i}" | jq -r '.objects')
echo "${BUCKETS[${i}]}.value ${OBJECTS}"
done
```
Dans le cas dune exécution avec configuration, on obtient donc ceci:
```bash
> ./garage_bucket_objects config
graph_title Objects by Bucket
graph_vlabel Number of objects
graph_args --base 1000 -l 0
graph_category garage
tamerelol.label tamerelol
```
Et avec une exécution standard:
```bash
> ./garage_bucket_objects
tamerelol.value 24
```
Si lon ajoute un nouveau *bucket*, `youpi`, il est automatiquement pris en compte:
```bash
> ./garage_bucket_objects
youpi.value 0
tamerelol.value 24
```
On peut alors tester le script en condition réelle sur le serveur `garage`, en le plaçant dans `/etc/munin/plugins`, via:
```bash
munin-run garage_bucket_objects
```
Si tout se passe bien, il suffit alors de redémarrer le service `munin-node` et le tour est joué!
# Et pour… le reste?
Si tu souhaite grapher dautres données, cest à peu près aussi simple: lors de second appel, sans rien modifier dautres, tu peux simplement demander le champs `unfinishedUploads` au lieu du champs `objects` pour obtenir les téléversements non terminés.
Si tu veux obtenir la taille de chaque *bucket*, cest globalement la même chose à un petit détail près: non seulement il faut effectivement demander le champs `bytes` lors du second appel à lAPI, mais il faut aussi demander au graphe de passer dune base 1000 à une base 1024 histoire dafficher des données cohérentes.
Mais voilà, cest à peu près tout, ça fait des jolis graphes et tout le monde est content \o/
# Bonus: grapher le total
Petit bonus de dernière minute: pour grapher un total (somme de toutes les courbes), il suffit dajouter ce qui suit à la `config`.
```
graph_total Total
```
Pour du disque, ça peut être pertinent…

View File

@@ -0,0 +1,101 @@
+++
title = "Réduire un cluster Garage"
date = 2023-07-18
[taxonomies]
tags = [ "système", "garage" ]
+++
![Jaime vivre dangereusement](/jaime_vivre_dangereusement.jpg)
<!-- more -->
Des fois, tu fais des choix douteux. Et des fois, ces choix douteux tamènent à avoir un cluster Garage bien trop gros par rapport à ton besoin réel.
# La situation de départ
La recommandation pour Garage est davoir au moins 3 réplicats à travers un cluster. Autrement dit, quelques soient les machines utilisées (puisquun cluster peut avoir des machines complètement hétérogènes), il faut au minimum un `replication_mode` à 3 et du coup 3 machines, idéalement dans 3 zones différentes.
Donc si tu suis la recommandation, tu as monté 3 machines (ou 3 VMs, ou 3 conteneurs LXC, etc…) et comme tu es quelquun de prudent, tu en as même rajouté une quatrième qui va servir de _gateway_ pour lensemble du cluster. Quand on a effectivement un peu de trafic HTTP, ça peut être intéressant davoir une passerelle pour accéder à lensemble de données du cluster. Non seulement, ça décharge les machines qui soccupent du stockage, mais ça permet de bien séparer les rôles stockage et accès.
Et tout le souci est là: 3 machines, ça fait beaucoup et en réalité, 3 VMs ou 3 conteneurs LXC sur la même machine, ça ne sert pas à grand-chose. Donc oui, cest loin de la recommandation, mais quand on a déjà de la redondance en dessous (NAS, RAID, etc…), ça ne sert pas à grand-chose davoir autant de réplicats.
Sauf que passer de la situation décrite à une autre situation nest pas forcément ce quil y a de plus simple à faire non plus.
# Et au commencement était la redondance
Première étape, il va falloir arrêter le cluster complet. Ça paraît évident, mais ça mérite dêtre rappelé également: il ne vaut mieux pas quil y ait des données inscrites dans le cluster au moment où on larrête ou où lon change sa topologie.
Pour commencer, il faut idéalement lancer un _scrub_ sur lensemble des données, au moins sur le nœud qui est supposé rester à la fin. Ça se lance assez facilement avec cette commande:
```
garage repair --yes scrub start
```
On peut surveiller létat de cette tâche en allant récupérer le `TID` correspondant et en regardant derrière la tâche en question:
```
# garage worker list | grep scrub
5 Busy Block scrub worker 4 4.07% - - -
# garage worker info 5
Task id: 5
Worker name: Block scrub worker
Worker state: Busy (throttled, paused for 0.012s)
Tranquility: 4
Total errors: 0
Consecutive errs: 0
Progress: 4.31%
Persistent errors: 0
```
Il faut savoir également que cette opération est extrêmement lente et demande pas mal de ressources disques. En effet, lobjectif est bien de vérifier lintégrité de chaque bloc sur le disque, cela peut donc potentiellement prendre des heures, voire même des jours. Tu peux également voir un facteur amusant là-dedans, la _tranquilité_ de Garage: plus elle est forte, plus Garage va bourrer comme un âne sur les disques.
Bref, quoiquil arrive, il vaut mieux le laisser terminer, cela permet de sassurer que toutes les données sont saines. Si des blocs posent problème, ils seront récupérés depuis dautres nœuds du cluster, opération bien entendu impossible a posteriori.
# Attention Chérie, ça va trancher
Bon, tu ten doutais mais à un moment, va bien falloir couper. Si tu as un NginX devant la passerelle ou si tu as plusieurs nœuds qui servent de passerelles avec un autre serveur Web devant, larrêter permet de simplifier un peu lopération.
Ça laisse le cluster actif, sans pour autant autoriser les accès en lecture ou en écriture.
Si tu as un processus de sauvegarde de Garage (et tu as grandement intérêt à une avoir un), ça pourrait être une bonne idée de le déclencher maintenant parce quon va rapidement passer aux choses sérieuses.
Maintenant suivre une procédure pas vraiment officielle, pour mettre un cluster dans un état pas vraiment recommandé, le tout sans sauvegarde, comment dire… Faut aimer vivre dangereusement!
# Cest pas le moment davoir les mains qui tremblent
Si lon en croit la [documentation officielle](https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#replication-mode), on peut changer le mode de réplication mais il faut pour cela complètement supprimer le _layout_ courant de tous les nœuds.
La première étape va donc consister à arrêter lensemble des nœuds et à supprimer le fichier `/var/lib/private/garage/meta/cluster_layout`. Le cluster va alors retourner dans son état dorigine, pas en terme de données, mais en terme dorganisation.
Autrement dit, les données ne bougent pas, les métadonnées ne bougent pas (ce qui va permettre à Garage de récupérer toutes ses fonctionnalités sans souci), mais lensemble des mécanismes de synchronisation du cluster, de réplication de données entre les membres va être fortement impacté.
Avant de redémarrer le cluster dans son ensemble, nous allons procéder à la modification du `replication_mode`. Il faut **impérativement** le changer sur lensemble des nœuds du cluster **avant** de redémarrer. Sous peine davoir une situation particulièrement instable.
Dans le fichier `/etc/garage.toml`, on peut donc passer:
```toml
replication_mode = "1"
```
Une fois la modif faite, on peut redémarrer lensemble du cluster, qui sera alors dans son état initial: tous les nœuds se connaissent, ils sont connectés, mais ils ne savent pas quel rôle est le leur.
Du coup, on peut tranquillement assigner un rôle unique à un unique serveur Garage:
```
# garage layout assign -c 5 -t nouveau -z zone1 2XXXXXXXXXXXXXX # la capacité na plus aucune importance ici
# garage layout apply --version 1
```
Normalement, une fois le _layout_ appliqué, lensemble des données devrait de nouveau être disponible sur le seul et unique nœud disponible.
Les autres nœuds disparaîtront quand tu les éteindras, tout simplement.
Cest le moment de vérifier si toutes les données sont effectivement disponibles via S3 (peu importe la méthode, je vous recommande personnellement [minio-client](https://min.io/download#/linux) et de faire une synchro complète dun bucket avant dattaquer le reste).
# Conclusage
Bah alors, elle est pas belle et frugale la vie?

View File

@@ -0,0 +1,46 @@
+++
title = "Threadivers: est-ce que ça peut marcher?"
date = 2023-07-20
[taxonomies]
tags = [ "fédiverse", "threadiverse", "lemmy", "mastodon" ]
+++
![Why cant I hold all those lemmy?](/6bf7ab41-b09f-43c6-8849-cee9c5884201.webp)
<!-- more -->
Jobserve depuis quelques jours de plus près le Threadiverse, terme désormais consacré pour parler du (peut-être) successeur au (mal en point) Reddit: [Lemmy](https://join-lemmy.org/). Et si les alternatives sont toujours intéressantes, surtout quand elles sont libres et décentralisées, jai quand même un doute sur celle-ci en particulier.
Du coup, au lieu de faire un thread géant sous Mastodon, je vais essayer dexposer ma pensée ici.
# Micro-blog, cest comme du blog, mais en plus petit
Mastodon/Pleroma/dautres trucs (les puristes mexcuseront, je ne connais pas tous les logiciels de micro-blog existants dans le Fédiverse) sont centrés sur des personnes: vous suivez tel journaliste, tel artiste, telle personne. Certes, vous pouvez suivre des mots-dièse (putain que je kiffe cette francisation complètement forcée et pas du tout naturel!), certes il y a des instances par centre dintérêt mais globalement, ça reste centré sur lindividu.
Cest dailleurs à ce titre que les micro-instances relativement spécialisées ont du sens: on peut avoir une instance pour la rédaction dun journal, on peut avoir une instance pour un partie politique, une institution, une entreprise. Tout cela a, je trouve, parfaitement du sens. Et dailleurs, avoir des instances relativement spécialisées en fonction des personnes que lon peut y trouver est probablement la meilleure marche à suivre pour lavenir du Fédiverse: quune instance Coca-Cola puisse parler à une instance Les Républicains qui elle-même puisse parler à une instance Mediapart, je trouve que tout cela a du sens.
Dautant plus quun journaliste de linstance Mediapart pourrait très bien avoir un compte privé ailleurs dans des instances non-spécialisées, sans thème précis, comme il en existe déjà pas mal.
# Reddit est-il vraiment adapté à ce type de publication?
Reddit est plus une sorte de forum universel. Il est organisé par centres dintérêt: [r/gaming](https://reddit.com/r/gaming), [r/nintendo](https://reddit.com/r/nintendo), etc… On ny suit pas des individus mais des sujets de conversation. Et cest à ce titre **pas** un réseau social selon moi, la composante dinteraction individuelle étant extrêmement limitée, sans être complètement inexistante: on peut débattre de ce qui est publié ou le commenter, on peut y avoir des réactions et des interactions intéressantes, mais encore une fois, tout est centré autour de sujets particulier.
Vraiment comme un forum pour le coup. On peut y dévier, on peut avoir des conversations avec dautres membres, mais lessentiel reste quand même centré autour de sujets assez précis.
Du coup, je ne vois pas bien quelle forme finale pourrait prendre le Threadiverse. Si tu as un site web, nas-tu pas plus intérêt à avoir un simple forum? Si tu héberges une instance Lemmy, elle sera forcément centrée autour de quelques points dintérêts puisque cest comme ça que le logiciel le présente et lorganise.
> Remarque à moi-même: on pourrait discuter longtemps du fait que la façon de présenter les choses déforme lusage des choses en question. Mais cest probablement un débat pour un autre moment.
Second souci: Reddit est également un aggrégateur de liens, un endroit où lon peut trouver classé par intérêt (nombre de haut-votes pour un sujet précis) et par centre dintérêt des liens qui vont de toutes manières ailleurs. Cest dailleurs à la fois toute la qualité et tout le défaut de Reddit: il est la _front page of the Internet_, le premier truc que tas intérêt à ouvrir, mais pour aller complètement ailleurs de toutes manières.
> Remarque à moi-même: là encore, on pourrait questionner la pertinence dun site web à but lucratif qui ne peut que difficilement gérer de la rétention, mais encore une fois, cest un débat pour un autre moment.
Lemmy pose alors aussi le souci du centre dintérêt: on ne peut pas suivre tous les `gaming@<instance>` de toutes les instances. Du coup, le côté aggrégation, regroupement de tous les liens liés à un certain sujet et classés par une communauté de passionnés dudit sujet, on le perd également. Dit autrement, à partir du moment où il y a deux *r/nintendo*, il faut soit suivre les deux, soit les fusionner pour que cette composante aggrégateur retrouve de lintérêt.
Donc en terme de topologie logique du Threadiverse, soit on va finir par avoir des instances tellement spécialisées que ça va recréer des centres (une instance spécialisée dans les sujets liés à Nintendo par exemple, une autre sur la politique, etc… mais bien une seule à chaque fois), soit il faudra une sorte de méta-annuaire ou une sorte de méta-aggrégateur pour tout retrouver facilement (et lintérêt risque davoir tout en décentralisé en prend la aussi un coup).
# Conclusation dans ton fion
Voilà, je ne sais pas ce que va donner Lemmy. Parce que de manière générale, je suis assez mauvais pour les prédictions, surtout quand elles concernent le futur. Mais je vois déjà quelques soucis de base avec la façon dont il est organisé qui peuvent se résumer ainsi: un aggrégateur de liens décentralisés, ce nest plus vraiment un aggrégateur; une organisation peut fournir des comptes individuels identifiables sur le Fédiverse, peut ouvrir un forum pour des conversations plus centrées sur des sujets, je les vois mal ouvrir un Lemmy pour remplacer ça.
Bon après, ya du shitpost de qualitay, donc voilà *glory to u/spez* et vive le Threadiverse!

View File

@@ -0,0 +1,144 @@
+++
title = "Véhicules électriques et EVSE"
date = 2023-08-12
[taxonomies]
tags = [ "véhicule électrique", "evse", "électricité" ]
+++
![img](/2023/08/explain_meme.jpg)
<!-- more -->
Petit essai sur les véhicules électriques et les EVSE ou comment je me suis rendu compte que les constructeurs automobiles ne se mettent pas du tout à la place de leurs clients, loin sen faut.
Pour les néophytes sur le sujet (ou les gens qui ont lintention dacheter un véhicule électrique ou les gens qui en ont acheté un mais qui «ny connaissent rien»), je vais commencer par un petit récapitulatif des connaissances nécessaires pour la charge dun véhicule électrique.
Cest aussi pour poser un peu de vocabulaire et quelques bases sur la façon dont cest normé. Et cest pas inintéressant de voir et de comprendre les choix qui sont faits dans ce cadre.
# De quoi quon parle
Pour charger une voiture électrique, il y a quelques prérequis:
* il faut un câble Type 2 côté véhicule;
* il faut également un appareil spécial pour communiquer avec la voiture, un EVSE: **E**lectric **V**ehicle **S**upply **E**quipment. Contrairement à ce que lon dit couramment, il ne sagit pas dun «chargeur» mais dun interrupteur un poil intelligent, plus de détails après.
Ce dernier est relié au réseau électrique (on verra comment plus tard).
Je ne parlerai pas ici (ou très peu) de charge en courant continu. Dabord parce que chaque constructeur a décidé dutiliser un vocabulaire hyper marketing pour en parler (*optimum charge*, *fast charging*, *super charging*, etc…) et ensuite parce quon lutilise excessivement rarement. Ce nest pas comme ça quon charge un véhicule électrique au quotidien et ce nest pas comme ça quon devrait le charger de toutes manières sinon nous allons recréer le problème infrastructurel des stations service, à savoir recréer des monopoles locaux sur les carburants.
## Câbles Type 2
Je parlerai ici exclusivement de câble Type 2. Cest le câble qui ressemble à ça:
![img](/2023/08/t2t2.webp)
Cest le câble standard en Europe: à gauche sur limage côté véhicule et à droite côté borne de rechargement AC (courant alternatif, autrement dit les bornes que lon trouve en ville, dans les bureaux, etc…).
Le câblage interne est indiqué sur la prise de droite:
* `PP` est le câble de proximité, il permet de couper le jus si lon enlève le câble, cest une mesure de sécurité.
* `CP` est le câble de contrôle, il permet au véhicule électrique de communiquer avec la borne.
* `PE`, cest la masse.
* `N`, `L{1,3}` représente le neutre et les (potentielles) phases.
Oui parce que les véhicules électriques en Europe doivent supporter la charge sur 3 phases dans le cas où ils seraient branchés sur une borne tri-phasée. La plupart des particuliers ont de lélectricité mono-phasée chez eux, il y a aura donc seulement `L1` qui sera utilisée, `L2` et `L3` ne seront pas branchées.
> Note: en Amérique du Nord, on utilises un câble [J1772](https://en.wikipedia.org/wiki/SAE_J1772) (dit aussi Type 1), qui est mono-phasé par défaut, donc ne contient que 3 prises plus les deux câbles de contrôle/sécurité, mais le principe est exactement le même.
> Note: le CCS, ou Combo DC, utilise la terre et les deux lignes de contrôle, mais comprend également deux énormes tiges en dessous de la prise pour charger directement la batterie en courant continu. Mais le principe reste grosso modo le même.
Nous avons là un premier souci concernant le câble: certains fabricants de câble et certains constructeurs automobiles ne livrent pas au client le câble le plus capacitif possible. En effet, un câble Type 2 peut avoir les caractéristiques suivantes:
* il peut être 16A ou 32A: les deux formats sont supportés. Pour avoir la charge maximale sur un véhicule électrique, il faut avoir un câble 32A.
* il peut être mono-phasé ou tri-phasé: il existe des câbles sans `L2` ou `L3`.
Évidemment la capacité de charge du véhicule dépend de lensemble des paramètres, au moins-disant: capacité supportée par le véhicule, capacité de lEVSE/borne **et capacité du câble**. Et ça fait une grosse différence, comme tu peux le voir ci-dessous:
| Capacité de charge | 16A | 32A |
|--------------------|-------|-------|
| Mono | 3,7kW | 7,4kW |
| Tri | 11kW | 22kW |
> Lecture du tableau: un câble mono-phasé 16A ne pourra jamais excéder une charge de 3,7kW. Un câble tri-phasé 32A pourra supporter jusquà 22kW de charge.
Autrement dit, il vaut mieux avoir un câble 32A tri-phasé. Il sera certes plus rigide et forcément un peu moins long, mais au moins, il permettra de charger à la capacité maximale du véhicule ou de lEVSE. **Qui peut le plus peut le moins**, il pourra toujours servir dans les autres situations.
Jai vu pas mal de néo-propriétaires de véhicule électrique se poser la question de pourquoi leur voiture était très lente à charger alors quils avaient pourtant une borne très capacitive. Et la réponse était presque systématiquement le câble.
> Note: si tu veux calculer le temps de charge dun véhicule électrique, je te conseille [cet outil](https://www.automobile-propre.com/simulateur-temps-de-recharge-voiture-electrique/).
## EVSE
Côté EVSE, cest bien plus simple: un EVSE est simplement un gros interrupteur (un peu) intelligent. Il ne fait aucune conversion sur le courant, il permet simplement de vérifier que le véhicule est dans les bonnes conditions et que le câble de proximité est branché.
Il est néanmoins obligatoire pour charger un véhicule électrique: on ne peut pas simplement connecter le courant sur une prise Type 2 en reliant les câbles, ça ne fontionnera pas.
## Prises électriques
Là, je parle bien de prise électrique domestique. Encore une fois, simplement que nous ayons tous le même vocabulaire.
Une prise Schuko (ou prise Type E et Type F, les deux étant compatibles entre elles) est une prise électrique standard en France. Cest les prises que tu as chez toi en gros, rien de plus rien de moins. Elles ne peuvent néanmoins par supporter plus de 16A et sont mono-phasées.
![img](/2023/08/Steckdose.jpg)
Si tu as bien suivi jusquà maintenant, ça veut dire que sur une prise domestique standard avec une EVSE portable (celui que ton constructeur te fournit généralement), tu peux charger à 3,7kW maximum.
Pour les prises électriques domestiques plus capacitives (tri-phasées 16A, mono-phasées 32A, tri-phasées 32A), il faut se diriger vers [les prises camping](https://fr.wikipedia.org/wiki/CEI_60309), de ce type:
![img](/2023/08/16A-plug.jpg)
# La charge dun véhicule électrique au quotidien
Voilà, maintenant que le décor est posé, que faut-il pour charger un véhicule électrique au quotidien? Cest en fait assez simple: généralement on charge à la maison, donc on a le fameux câble fourni par le constructeur quon branche sur une prise Schuko.
![img](/2023/08/cable-de-recharge-pour-prise-domestique.jpg)
Mais en fait, ce nest pas vraiment un câble. Cest un **EVSE** avec une prise Schuko dun côté et une prise Type 2 de lautre, le tout attaché ensemble et indissociable.
Quand on commence à se servir de son véhicule un peu plus souvent, on peut éventuellement avoir besoin de charger *plus vite*. Ce câble supporte effectivement 16A mono-phasé, donc cest 3,7kW max si les prises le permettent, autrement dit une charge relativement lente pour un véhicule électrique (mais largement supportable pour 80% des gens).
Quand on a besoin de plus, on peut installer un EVSE en dur chez soi. Jai personnellement acheté et monté un [OpenEVSE](https://www.openevse.com/), version 100% Open Source et Open Hardware, qui me permet de charger mon véhicule électrique en 32A mono-phasé, autrement dit la plus grosse charge possible pour un particulier (hors tri-phasé, mais je considère que cest plus une exception que la règle, en France néanmoins).
À noter qu[OpenEVSE](https://www.openevse.com/) est monté par défaut sur un câble 6mm² (comme ton four) côté réseau, pas sur une prise électrique et quil est monté de lautre avec une prise Type 2 **câblée en dur**.
# OK, je vois, mais du coup, de quoi on a besoin quand on part en vadrouille
Et cest là que je voulais en venir et là que je trouve tous les systèmes actuels un peu stupides.
Autant pour faire des trajets autour de chez soi, on ne se pose généralement pas la question: on charge chez soi et voilà. Mais dès quon est en vadrouille un peu plus loin, il faut pouvoir se charger sur une infrastructure publique. Et donc là, il ny a plusieurs possibilités:
* les charges DC (courant continu) que je mets un peu à part parce que les câbles sont toujours attachés directement à la borne pour des raisons de sécurité, donc pas de souci de ce côté;
* pour se charger sur des bornes AC publiques, **il faut obligatoirement un câble Type 2** et de préférence tri-phasé 32A histoire de profiter de la charge la plus rapide possible (voire plus haut);
* si on na pas de borne publique disponible, **il faut obligatoirement son câble constructeur avec un EVSE dessus**, pour pouvoir se brancher sur nimporte quelle prise électrique.
Donc le propriétaire standard de véhicule électrique se retrouve souvent dans une situation débile où il faut trimballer un câble Schuko/EVSE/Type 2 et un câble Type 2/Type 2.
Cest **débilissime**.
En fait, je ne comprends vraiment pas pourquoi tout nest pas en kit.
Ce serait tellement plus simple davoir:
* un câble Type 2/Type 2 (tri-phasé, 32A, voilà toussa, TMTC);
* un EVSE portable avec une capacité 32A (pas bien compliqué en fait, un OpenEVSE devrait pouvoir se glisser dans un petit boîtier dans problème) et qui embarquerait une prise Type 2 femelle;
* un adaptateur Schuko, mais quon pourrait très bien remplacer par un adapteur camping ou pour dautres prises finalement, ça na que peu dimportance.
Ça fait un seul câble Type 2 à se trimballer. Et un EVSE miniaturisé autant que possible avec des prises électriques interchangeables. Ça permettrait même à la plupart des possesseurs de véhicules électriques de se passer complètement dEVSE à la maison: il suffirait de le remplacer par une prise camping pour profiter une charge maximale…
# Et le pire, cest que certains y ont pensé mais pas jusquau bout
Si tu regardes la [boutique de Telsa](https://shop.tesla.com/fr_fr/product/connecteur-mobile), ils ont un connecteur mobile (donc un EVSE) avec un adaptateur permettant de se brancher sur autre chose quune prise Schuko.
Ils ont des [prises anglaises](https://shop.tesla.com/fr_fr/product/adaptateur-pour-le-royaume-uni), des [prises suisses](https://shop.tesla.com/fr_fr/product/adaptateur-suisse), des [prises italiennes](https://shop.tesla.com/fr_fr/product/adaptateur-italie) et même des [prises camping 32A](https://shop.tesla.com/fr_fr/product/adaptateur-bleu---16a_32a).
Mais côté véhicule, tout est toujours câblé en dur. Donc, le posseseur de ce *connecteur mobile* va quand même être obligé de se trimballer avec un câble Type 2 à côté pour se charger sur les infrastructures publiques.
Encore une fois, **débilissime**.
# Conclusage (enfin presque)
Voilà, je nai trouvé personne que ça choque. Personne à qui ça pose problème. Je suis là seul comme un con à me dire que cest mal branlé et ça rend ouf de voir que personne na pris le temps de comprendre ou danalyser ça pour en déduire que cest pas comme ça quil faudrait faire et quil y a probablement un produit à sortir.
Donc voilà, je cherche. Je cherche lEVSE miracle qui ferait tout ça. Ou au pire, ladaptateur qui permettrait de transformer un OpenEVSE en ce que je cherche.
Jai déjà personnellement posé une prise Type 2 femelle sur mon OpenEVSE pour éviter davoir encore un câble qui traîne attaché. Donc ce nest pas compliqué à faire.
Par contre, côté réseau électrique, je ne vois pas du tout. Telsa utilise une sorte de connecteur propriétaire, je chercherais plutôt un connecteur standard, mais relativement compact. Je ne sais pas si ça existe.
Dès que je trouve, crois bien que je vais commencer à le construire cet EVSE parfait.

View File

@@ -0,0 +1,97 @@
+++
title = "MariaDB: comment fonctionne la gestion mémoire?"
date = 2023-09-01
[taxonomies]
tags = [ "mariadb", "mysql", "sysadmin", "système" ]
+++
![img](/2023/09/calculating_meme.jpg)
<!-- more -->
Je traîne un souci depuis un bon moment maintenant: jai quelques conteneurs LXC avec une base de données MariaDB. Ce nest pas la configuration par défaut, mais cest une configuration que je porte de machine en machine depuis quelques années et qui nest pas forcément spécifiquement faite pour faire de la performance.
Mais là nest pas (encore) la question. De temps en temps, de manière apparemment aléatoire, MariaDB se prend un `oom-kill` par le système, plante et évidemment ne redémarre pas tout seul. Dans une configuration un peu ric-rac en terme de mémoire (on parle de conteneur LXC qui démarre par défaut en utilisant un peu moins de 10Mio de RAM), cest quand même assez étonnant.
Dautant plus que de prime abord, tout va bien: lhyperviseur donne des consommations de RAM (en moyenne comme en pic) tout-à-fait raisonnable, pareil pour les autres métriques ramassées autrement.
Il semblerait donc bien que ce soit un problème extrêmement ponctuel et à moins davoir le nez dessus quand ça arrive, ça risque dêtre relativement compliqué à diagnostiquer.
Ce qui mamène donc au sujet du jour: comment MariaDB gère-t-il la mémoire? Ya-t-il un minimum? Un maximum? Quelles valeurs de configuration peut-on manipuler pour arriver à le limiter sans quil se prenne les pieds dans le tapis?
# Le pool commun de mémoire
Il va falloir distinguer deux types de consommation mémoire pour MariaDB. Je vais partir du principe quon va pour linstant rester sur la configuration «par défaut» sans chercher à faire quoique ce soit.
Tu le sors de la boîte donc, tu le poses sur le système, tu le démarres et bim! 200 Mio de mémoire prise alors que tas rien fait de particulier.
Alors, il y a peut-être (probablement même), dautres paramètres, mais en gros, la mémoire de ce bout-là va être décomposé comme suit:
* le `key_buffer_size` (utilisé par *MyISAM*) prend 128Mio de mémoire par défaut
* le `query_cache` (il y a plusieurs variables pour le gérer, elles commencent toutes par `query_cache_*`) prend 1Mio
* les différents cache d*InnoDB* `innodb_buffer_pool_size` et `innodb_log_buffer_size` occupent respectivement 128Mio et 16Mio
Bien évidemment, par défaut, je suppose que ces caches sont complètement vides mais ça donne une petite idée de la consommation « à vide» du serveur MariaDB. Il nira jamais en dessous de ces valeurs, à moins quon ne ly force.
Premières constations donc:
* sans aucun ajustement, cest, en théorie, 273Mio incompressible
* si lon a pas du tout de tables *InnoDB* ou pas du tout de table *MyISAM*, on peut commencer à bricoler
La [documentation officielle](https://mariadb.com/kb/en/mariadb-memory-allocation/) nous dit:
* aucune table *InnoDB* -> régler `key_buffer_size` à 20% de la RAM disponible et mettre `innodb_buffer_pool_size` à 0
* aucune table *MyISAM* -> régler ` innodb_buffer_pool_size` à 70% de la RAM disponible et mettre `key_buffer_size` à une faible valeur mais pas en dessous de 10M
Ça donne déjà une bonne idée de ce que lon peut faire en auto-hébergement: si lon peut se passer de lun ou lautre des types de base, ça simplifie déjà pas mal la configuration. **Tu peux économiser immédiatement entre 118 et 128Mio de RAM en faisant juste ça!**
Comme tu peux ten douter, je me suis donc empressé de convertir toutes les tables de toutes les DB de tous les conteneurs en *InnoDB* et jai rapidement déployé une configuration plus optimale. Ça permet de souffler, mais tu vas voir que ce nest malheureusement pas tout.
# La mémoire par connexion
En effet, MariaDB va aussi allouer de la mémoire à chaque nouvelle connexion. Et quand tu as essentiellement des applis en PHP, tu peux considérer grosso-modo que chaque client qui va aller faire un rendu sur une page, va aller générer une connexion côté base de données (plusieurs si le code nest pas bien optimisé, mais cest un autre souci).
Avec notre configuration par défaut, MariaDB a un maximum de 151 connexions (150 configurées + 1 permanente pour `root`). Mais combien de mémoire exactement consomme une connexion?
Cela va dépendre essentiellement de quelques paramètres de *buffer* et de cache attribués à chaque connexion:
* `{sort,read,read_rnd,join}_buffer_size`, pour un total de 2,625Mio
* `thread_stack`, 0,285Mio (cest la mémoire allouée à chaque *thread*)
* `binlog_cache_size`, 0.031Mio
* `tmp_table_size` prend à lui seul **16Mio**
Donc en fait, avec la configuration par défaut, MariaDB va attribuer un peu moins de **20Mio** par connexion. Si lon reprend les 151 connexions par défaut, **ça veut dire quon peut monter jusquà 3Gio tout carrossés.**
Bon évidemment, cest **le pire scénario possible**. On peut bien évidemment supposer que cela narrivera jamais, mais cela reste un potentiel.
On se rend aussi rapidement compte que la principale consommation de mémoire ne vient donc pas du tout des caches et de la configuration de base de MariaDB (ou alors de manière assez marginale) mais essentiellement des connexions, et particulièrement la variable `tmp_table_size`.
Se pose alors la question: cest quoi bordel `tmp_table_size`? En fait, cela correspond à la taille maximale que MariaDB va allouer en mémoire pour former des tables temporaires (lorsque lon fait des jointures ou des groupements par exemple). Si cette valeur est dépassée pour une raison ou pour une autre, MariaDB allouera une table temporaire sur le disque (avec ce que lon peut imaginer comme pénalité pour les performances).
Jignore si lon peut vraiment jouer sur ce paramètre pour essayer de gratter de la RAM. Il y a une variable MariaDB qui garde le nombre de tables temporaires formées en mémoire et le débordement correspondant sur disque:
```sql
SHOW STATUS LIKE 'Created_tmp_%tables';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
| Created_tmp_tables | 0 |
+-------------------------+-------+
```
Pour linstant et comme illustré ci-dessus, jy ai toujours vu 0. Je ne sais donc pas trop quoi en conclure (peut-être quon pourrait réduire cette valeur à 1Mio, peut-être pas…).
Par contre, je sais que lon peut regarder la quantité de connexion que subit MariaDB sur une certaine période et la limiter. Dans le pire des cas, le client aura une erreur comme quoi le nombre de connexions est dépassé, mais au moins, ça ne plantera pas tout le process. Et en plus labaque est relativement simple: environ 20Mio/connexion.
Mais comment fait-on pour connaître le nombre maximal de connexions? Et bien, il suffit de regarder dans le statut correspondant:
```sql
SHOW STATUS WHERE Variable_name='Max_used_connections';
```
Ça te donnera une idée de lusage depuis le dernier redémarrage. Ce nest évidemment pas parfait puisque si le serveur plante à cause dun surplus de connexion, ça ne taidera pas beaucoup. Néanmoins, ça peut permettre dajuster la quantité de RAM vers le haut ou au contraire vers le bas.
# Conclusage
Bon ben finalement, cétait pas si compliqué: il y a une certain quantité de mémoire qui est attribué à lensemble des processus MariaDB (273Mio par défaut que lon peut assez facilement réduire en unifiant les moteurs de DB utilisés) et il y a la mémoire utilisée pour chacune des connexions.
On peut donc pré-calculer la quantité de RAM nécessaires en fonction du nombre de connexions. À lévidence un serveur avec beaucoup de trafics et relativement peu de caches de page va multiplier les connexions à la DB et donc consommer bien plus de RAM. Dans le cas dun serveur non-dédié (avec un NginX, un PHP, etc… en plus dessus),il faut bien évidemment prendre en compte le fait que les autres services vont aussi consommer de la RAM.
Mais voilà, avec ces premiers chiffres en tête, ça peut donner une petite idée de la quantité maximale théorique que va consommer MariaDB et ça peut permettre dajuster le cas échéant.

View File

@@ -0,0 +1,105 @@
+++
title = "Migrer MinIO de service à plugin dans TrueNAS"
date = 2023-12-08
[taxonomies]
tags = [ "truenas", "minio", "s3", "sysadmin", "système" ]
+++
> La mauvaise doc, tu la lis… tu comprends rien! La bonne doc, tu la lis… bon tu comprends rien non plus, mas cest une bonne doc!
<!-- more -->
Parce que les emmerdes, ça vole souvent en escadrille, TrueNAS ne maintiendra plus le plugin S3 à partir de la version 13.1. Les utilisateurs sont priés de migrer vers le [plugin S3](https://www.truenas.com/docs/core/13.0/coretutorials/jailspluginsvms/plugins/minioplugin/).
Sauf que cest bien gentil, mais ce nest pas particulièrement bien documenté (que ce soit pour migrer ou même simplement pour installer et utiliser ledit plugin).
Guide non-officiel donc.
# Installation du plugin MinIO
Le service S3 de TrueNAS repose sur MinIO, cest également le cas du plugin. Avant de faire quoique ce soit, on peut donc aller simplement activer le plugin en question. Pour cela, le plus simple est de se rendre sur linterface Web (oui, je sais, cest caca™), daller dans la section `Plugins`. On te propose alors dindiquer sur quel volume il faut installer les `jails` (par défaut, ce sera dans un dossier `iocage`). Tu peux mettre le volume par défaut, ça ne gène en rien.
À la fin de linstallation, TrueNAS te donne quelques informations, notamment les informations de connexion, que tu pourras réafficher en cliquant sur la flèche au bout de la ligne correrpondant à MinIO puis dans les `Post install notes` (lécran saffiche, mais si tas loupé un truc, cest pas bien grave donc).
Une fois que cela est fait, je tinvite à stopper le plugin lui-même, parce quil va falloir aller faire pas mal de bricolage maintenant.
## Création dun dataset dédié
Comme pour le service S3, il faut un dataset dédié (et malheureusement distinct du dataset dorigine) et avec les **bonnes ACLs**. Et cest généralement là quon commence à bien rigoler…
Donc, rends-toi dans `Storage > Pools` pour créer le dataset dédié. Le nom a peu dimportance, ce qui est important en revanche, ce sont les ACLs.
Dans lécran pour régler les permissions voici donc ce quil faut faire pour que ça marche correctement:
* pour le *preset*, tu peux sélectionner `Restricted` qui correspond plutôt pas mal au cas dusage (on ne veut pas que quoique ce soit dautres que MinIO vienne écrire dans ce dataset)
* au niveau utilisateur, **il faut** sélectionner `minio`
* puis **il faut appliquer récursivement** les changements (via la coche ` Apply permissions recursively `)
De cette manière, ton dataset appartient bien à lutilisateur `minio` et à lui seul.
## Association du dataset au plugin
Une fois tout ceci fait, tu devrais avoir un plugin MinIO à larrêt et un dataset avec les bonnes permissions.
Le plugin lui-même va placer lensemble de ces données dans `<point de montage zfs>/iocage/jails/<nom de la jail>/root/var/db/minio`. Il faut donc idéalement aller monter le volume que lon vient de créer là. **Sauf que** le dossier `/var/db/minio` dans la `jail` nest pas vide (ce serait trop simple).
En fait, au premier démarrage MinIO peuple ce dossier avec quelques fichiers de configuration ce qui empêche complètement dy associer le dataset.
On va donc bouger un dossier cacher, `.minio.sys/` de ce dossier vers la racine du dataset que lon a créé:
```
mv <point de montage zfs>/iocage/jails/<nom de la jail>/root/var/db/minio/.minio.sys/ <point de montage zfs>/<dataset minio>/
```
À partir de là, dans linterface des plugins, tu peux aller triturer les points de montage. Il suffit dassocier ton dataset avec le dossier `/var/db/minio` dans la `jail` et ça devrait cesser de gueuler.
## Ajouter la partie réseau
Une `jail`, ça fonctionne grosso merdo comme un conteneur Docker (en tout cas du point de vue de lutilisateur). Il va donc falloir lui associer des ports sur lhôte. Normalement, MinIO utilise deux ports:
* 9000, pour S3
* 9001, pour ladministration
Par défaut le plugin démarre sur le premier port libre, donc généralement le 9002. On peut aller changer cela dans le menu `Jails`. Il suffit de cliquer de nouveau sur la flèche de droite puis daller dans `Edit` pour changer les ports sous `Network Properties`.
Une fois tout ceci fait, on peut redémarrer une dernière fois la `jail`, elle devrait écouter sur le nouveau port.
# Recréation de la configuration
Parce que la nature est bien fait, il est impossible de migrer les métadonnées des buckets S3 de MinIO service vers MinIO plugin. Ça veut dire quil faut effectivement tout recréer (ou trouver une bricole avec lAPI que je nai pas trop cherchée: avec un seul bucket et une seule clé, ça nen valait pas la peine dans mon cas) des buckets aux clés en passant par les utilisateurs et les permissions.
Amuse-toi bien… Peut-être quil existe des scripts précuits ou des recettes de cuisine, peut-être même fournies par le projet MinIO, mais jai eu la flemme de chercher plus que ça.
# Migration des données
Comme je le disais auparavant, la façon dont MinIO stocke les données à changer entre la version service et la version plugin (essentiellement parce que la version service est relativement ancienne maintenant). Il est donc impossible de charger simplement les données, le seul moyen est de passer par un miroir via `mc` (*minio client* ou *mcli* dans certaines distributions).
Il va donc falloir passer par une machine tierce, de préférence sur le même réseau pour éviter latence et désaagrément, pour faire un miroir complement **pour chaque bucket**. Là encore, bon courage si tu en as plusieurs ou que tu as plusieurs dizaines de téraoctets de données sur MinIO.
Donc, il faut créer une clé ayant a minima les droits de lecture sur tous les buckets côté source et évidemment une clé ayant les droits décriture sur tous les buckets côté destination. Cela peut être créer dans linterface dadmin de chaque MinIO, je ne détaille pas (cest trop dépendant des versions et la doc de MinIO est éventuellement là pour aider).
Sur la machine tierce, une fois `mc` installé par la méthode de ton choix (il y a un paquet Debian qui semble fonctionner correctement cela dit, sous le nom `mcli`), il suffi denregistrer les deux alias qui vont bien:
```
mcli alias set service http://truenas:9000 cle_lecture_seule secret_lecture_seule
mcli alias set plugin http://truenas:10000 cle_lecture_écriture secret_lecture_écriture
```
Tant que tu ne tes pas emmêlé les saucisses entre les ports/s3 source et destination et les clés, ça devrait bien se passer. `mc` indique en général tout de suite si quelque chose ne va pas, cest déjà ça de pris.
Pour le transfert lui-même, il faut sarmer de patience (et de `tmux`) et se lancer:
```
mcli mirror --preserve service/<bucketA> plugin/<bucketB>
```
Une fois le transfert effectué, on peut simplement vérifier que lon a le bon nombre dobjets. La taille devrait être «comparable» mais pas forcément similaire (la méthode de calcul peut différer entre les deux versions).
À ce moment, on peut simplement stopper la version service de MinIO, remplacer les ports de la version plugin, puis éventuellement effacer les données.
Bien entendu, il faudra quand même sassurer que les données en question ont bien été transférées, bon courage là-dessus aussi.
# Conclusage
Un service qui sarrête, ce sont des choses qui arrivent. Ça ne fait plaisir à personne, mais des fois, il ny a simplement pas le choix (obsolescence, sécurité, maintenabilité, les raisons sont multiples).
Par contre, quand cest le cas, le minimum cest de prévenir bien à lavance et de fournir le plus possible aux usagers de la solution la possibilité de migrer de la manière la plus simple et la plus efficace possible.
Là, je suis franchement déçu par TrueNAS sur ce point. Non seulement la documentation officielle est remplie de trous (doù ce billet dailleurs), mais en plus elle est loin dêtre efficace. Je navais quun seul téraoctet de données en S3 et heureusement suffisamment de place à côté. Je nimagine même pas la galère les utilisateurs un peu justes en terme despace ou avec des volumétries bien plus importantes.

View File

@@ -0,0 +1,90 @@
+++
title = "Plomber un nom de domaine"
date = 2024-05-27
[taxonomies]
tags = [ "dns", "sécurité", "sysadmin", "système" ]
+++
Tu viens davoir une idée géniale pour un nom de domaine et tu as un projet super intéressant et qui va changer la vie de millions de gens mais que tu nécriras jamais… Cest quoi le minimum à faire pour préserver le nom de domaine en question et éviter quil se fasse pourrir/quon envoie des mails à ta place/que quelquun essaie de prendre ta précieuse virginité?
Cest ce quon va voir maintenant!
<!-- more -->
Tu viens donc dacheter un nom de domaine dont tu ne te serviras probablement jamais. Mais il ne sagirait pas que des gens commencent à envoyer des mails avec, tentent de commander des certificats en ton nom, etc…
Du coup, quest-ce qui faut faire?
# Pour la partie mail
Dans tous les cas, si un nom de domaine nest pas supposé envoyer de mail, jaurais tendance à le plomber par défaut. Cest tellement facile quand il ny a aucun contrôle daller envoyer des mails à tout va sur un domaine qui vient dêtre enregistré et ce à linsu complète de son propriétaire.
## Réception
Première étape donc, on commence par rendre la réception de messages impossible. Pour cela, deux possibilités, soit ton hébergeur DNS supporte MX nul (ou tu le fais toi-même) et dans ce cas, il suffit dajouter un enregistrement DNS qui ressemble à ça:
```
> dig +short buttse.cx mx
0 .
```
Soit, ce nest pas supporté et dans ce cas le plus simple est de créer un enregistrement A vers `localhost` puis de faire pointer le MX dessus:
```
> dig +short buttse.cx mx
1 local.buttse.cx.
> dig +short local.buttse.cx. a
127.0.0.1
```
Voilà, impossible de recevoir un message, au pire, ça bouclera gentiment. Dailleurs, je me permets la remarque ici: OVH, ce serait vraiment bien du supporter les MX nul, ça éviterait ce genre de manip à la con.
## Envoi
Là, cest assez simple et universel, mais il y a beaucoup denregistrements à ajouter. On commence donc pas SPF v1 et v2. Ce sont de simples enregistrements `TXT` à la racine du domaine qui permettent dindiquer qui est autorisé à envoyer des mails pour le domaine en question.
Pour les plomber, cest assez simple, il suffit de dire que personne ny est autorisé:
```
dig +short buttse.cx txt
"v=spf1 -all"
"spf2.0/mfrom -all"
```
Pour info, SPFv2 nest utilisé par personne… sauf Microsoft :/
Donc si tu ne veux pas avoir demmerdes avec eux, bah faut sy plier, ya pas le choix.
Il faut ensuite préciser au niveau DMARC, que faire en cas de réception dun message depuis ce domaine. En loccurence, il faut absolument tout rejeter, quelque soit le message. Absolument aucun message ne devrait être envoyé et cest exactement ce quon va faire dire à DMARC:
```
> dig +short _dmarc.buttse.cx txt
"v=DMARC1;p=reject;pct=100;sp=reject;aspf=s;"
```
Ça peut éventuellement être intéressant de rajouter un `rua` à lenregistrement. En gros, ça correspond à une adresse mail (pas sur ce domain donc) sur laquelle tu peux recevoir les rapports. Comme en théorie, absolument aucun message ne doit être envoyé (et donc reçu), si tu reçois un truc, ça doit immédiatement faire sonner une alerte quelques parts.
# Pour la partie certificat
En théorie pour arriver à commander un certificat, il faut quand même pas mal de prérequis: confirmation par mail, enregistrement qui pointe vers une IP précise, etc… Néanmoins, le fait dindiquer clairement aux organismes émettant des certificats quil ne faut justement pas en émettre pour le domaine est une question de bon sens. Et si jamais un certificat apparaît avec le nom de domaine en question, ça permet potentiellement au navigateur de se dire que quelque chose cloche.
Bref, même si ça semble un peu bizarre, cest loin dêtre idiot.
Donc, tout cela se faire avec lenregistrement `CAA` (pour _Certificate Authority Authorization_) et il y a justement un cas prévu pour «rien»:
```
> dig +short buttse.cx caa
0 issue ";"
```
À linstar du DMARC, ça peut être intéressant de coller une adresse mail pour être prévenu si jamais un certificat est demandé pour ce domaine:
```
> dig +short buttse.cx caa
0 issue ";"
0 iodef "mailto:nique@exemple.com"
```
# Conclusation
Voilà, une fois que tu as fait tout ça, tu es certain que ton domaine, qui pour linstant est en veille, ne sera pas squatté/détourné/utilisé à mauvais escient. Une fois que ton projet sortira de terre (LOL), tu pourras toujours aller faire les modifs nécessaires pour remettre en service ce qui est nécessaire.

View File

@@ -0,0 +1,72 @@
+++
title = "Que peut-il se passer le 8 juillet?"
date = 2024-06-26
[taxonomies]
tags = [ "politique", "fiction", "république" ]
+++
_Disclaimer_: je ne suis pas un expert en constitution. Je ne suis pas un expert en politique. Je ne suis pas un expert en quoique ce soit dans ce domaine. Mais là, je suis stressé par la situation et jai donc besoin dextérioriser. Donc plutôt que de faire un TRÈÈÈÈÈÈS long poste dans Mastodon, je vais y préférer écrire ici ce que jen pense, ce qui est possible, ce que je devine, ce que jintuite. Cest à la fois de la prospective et un truc cathartique.
Donc rien de ce qui est écrit ici nest à prendre au premier degré. Encore une fois, je ne suis pas un expert en quoique ce soit, jessaie juste de comprendre la situation et de me projeter à laprès 8 juillet avec les petits moyens qui sont à ma disposition. Je vais aussi donner une sorte de probabilité que chaque scénario se produise. Encore une fois, cest du pif 100%, je ne suis pas assez câlé, jessaie juste dorganiser ma pensée.
<!-- more -->
# Élections législatives
Donc, le 30 juin se déroule le premier tour des élections législatives anticipées et le 7 juillet, le second tour. Dans notre mode délection moisi en France, il ny a pas une élection, mais 577 micro-élections avec à chaque fois des luttes internes, des luttes locales et tout un tas dévénements qui peuvent faire basculer une élection dans un sens ou dans lautre. Ce système me saoûle souverainement, mais je ne vais pas revenir dessus, ce nest pas lobjet.
Les résultats sont en théorie connus aux environ de 20h pour la plupart des circonscriptions de France (bureau de vote fermant à 17h) mais peuvent se prolonger jusque tard dans la nuit. Jai déjà vu des bureaux de vote remonter au bureau central des résultats jusquà 23h30. Il suffit parfois de pas grand-chose pour faire recompter une énième fois lintégralité des bulletins pour être sûr. Et soyons clairs, sur une élection aussi serrée dans certaines circonscriptions, ce nest pas forcément un mal de prendre le temps.
Bref, nous sommes le matin du 8 juillet, nous devons normalement avoir tous les résultats et sauf très grosse cagade ou contestation, les députés de chaque circonscription sont connus.
# Les hypothèses
## Hypothèse 1: le Rassemblement National arrive en tête avec une majorité absolue
Jordan Bardella se retrouve Premier Ministre et on na pas fini de danser. Les acquis sociaux vont voler en éclat, les racistes/fascistes/connards vont se lâcher comme jamais (ça a, en vérité, déjà commencé).
Devant cette débâcle, notre Président de la République pourrait démissionner. Cela provoquerait des élections présidentielles anticipées qui permettraient de mener Marine Le Pen à la Présidence au mois de Septembre. Dans cette hypothèse, je suppose que le Président du Sénat, Gérard Larcher, assure lintérim pendant les Jeux Olympiques (ah oui, parce quil ne faudrait pas les oublier ceux-là non plus). Je ne connais pas la durée légale de cet intérim.
Le dernier cas que nous avons eu est la mort de Georges Pompidou où Alain Poher, alors Président du Sénat, a assuré 1 mois et 25 jours la Présidence de la République. Cela nous mènerait à des élections tout début Septembre.
**Quelle est la probabilité pour que cela se produise?** Pour arriver à avoir 289 sièges à lAssemblée Nationale (le seuil pour la majorité absolue), il faudrait que le RN ramasse 200 circonscriptions de plus quen 2022. Malgré les scores aux Européennes, lauto-sabotage et les renoncements démarrés il y a une grosse semaine me font dire quils vont avoir plus de sièges, mais je doute fortement que ce soit autant. Après, je ne suis pas dans la tête des gens. Les sursauts, ça arrive et pas toujours dans le sens quon espère…
Quant à la démission du Président de la République, je pense quelle est peu probable dans ce cas. Il sera bien trop heureux de pouvoir taper sur son Premier Ministre à longueur de journée et il saccomoderait très bien du programme du RN. Sans compter le fait quil pourrait se replier sur le «domaine réservé», un truc qui nexiste pas, mais quil pourrait très bien réactiver. Bref, cest quand même le scénario du pire pour beaucoup de gens, mais passons…
## Hypothèse 2: le Nouveau Front Populaire arrive en tête avec une majorité absolue
Quelquun se retrouve Premier Ministre. Comme personne ne peut blairer Jean-Luc «Moi je suis de gauche mais quand même, cest pas possible» Mélenchon, ce nest pas lui qui est Premier Ministre. Faure, Tondelier, qui vous voulez mais pas lui… Bref, on sen fout.
Le Nouveau Front Populaire gouverne donc, le Rassemblement National est la principale force dopposition et ça leur va finalement très bien. Comme Jordan est élu européen, on en entend plus parler et cest tant mieux. Comme le programme nest pas vraiment du goût des marchés financiers, la dette de la France est attaquée et au bout de même pas 6 mois, la note de la France se dégrade fortement et cest le retour des politiques daustérité. Voilà, ça cest le côté pessimiste, mais en vrai non, ça pourrait tout aussi bien se passer correctement.
Quant au Président de la République, il pourrait aussi décider de démissionner. Personne à gauche ne voudrait se laisser faire et malgré lalliance au Parlement, tout le monde partirait en désordre de bataille pour cette présidentielle anticipée. Au final, cest nimporte-qui™ qui se retrouve élu et ça ne change rien, sauf sil est du Nouveau Front Populaire auquel cas, on pourra partir vers une VIème République, pépouze, ce qui serait une bonne idée parce que visiblement la nôtre nest plus adaptée à tout cela.
**Quelle est la probabilité pour que cela se produise?**La NUPES, cétait péniblement 151 sièges en 2022, dont certains ont été gagnés à quelques dizaines de voix près, pour ainsi dire avec les dents. Entre deux, Bolloré a continué à faire marcher la machine à propagande à fond les ballons et la droite républicaine est tombée en miettes. Le résultat dépend donc à mon sens fortement du comportement de ces électeurs traditionnels de droite et des appeurés qui nauraient pas voté en 2022 mais voteront en urgence ici. Quoiquil en soit, il faut gagner 138 circonscriptions en subissant un *bashing* permanent de la télé sur la Gauche Unie/Nouveau Front Populaire/LFI, etc…
Donc la progression me paraît possible, mais dans la douleur et avec une détermination de tous les instants. Tout le monde y croit peut-être sur le terrain, mais le soutien me paraît bien faible au regard de tout le *shitstorm* qui est pris par la gauche dans les médias traditionnels. Donc, jai envie dy croire mais sincèrement, jen doute. Jespère avoir tort, vraiment.
## Hypothèse 3: Aucune coalition na la majorité absolue, aucune majorité relative ne se détache
Là, ça devient un peu plus «intéressant». Le Président ne peut pas prononcer une nouvelle dissolution avant un an. Donc nous avons ce parlement avec 3 blocs (NFP et RN à peu près à égalité et Renaissance en chute libre) mais dont aucun ne veut céder un pouce de terrain à lautre.
Sauf quils sont vissés au Parlement pour un an minimum. Dordinaire, la dissolution est une épée de Damoclès au-dessus de la tête du Parlement pour le discipliner ou le punir. Si le gouvernement est si peu censuré au cours de la Vème République (en comparaison avec le IIIème et la IVème), cest justement parce quune censure peut amener à une dissolution et donc une redistribution des cartes. Il est donc absolument nécessaire pour lAssemblée Nationale de bien peser les choses avant de censurer le gouvernement.
Mais là? Et bien là, cest un peu la fête du slip pendant un an au minimum: si un gouvernement NFP est désigné par le Président, le reste de lAssemblée peut le censurer à loisir; pareil côté RN; pareil côté Renaissance. Même un gouvernement «technique» (sans majorité, juste des technocrates qui gèrent le quotidien) pourrait parfaitement se faire censurer à la moindre occasion. Et je ne vois pas très bien ce qui pourrait retenir nos chers députés. On pourrait avoir une censure par semaine sil le fallait. Ce serait le retour à la fameuse instabilité gouvernementale de la IIIème République.
En soi, je ny vois pas de problème. Cest un retour en force du Parlement dans la vie politique et ce nest pas plus mal. Mais notre Vème République nest pas vraiment conçue pour cela et on arriverait, de fait, à un blocage institutionnel. Que faire donc? Potentiellement admettre que notre République est à bout de souffle et proposer une VIème. Ça, ce serait la réponse la plus sensée. Peut-être que lAssemblée pourrait démissionner en entier. Ou aider à ce processus.
Bref, il faudra une solution, je ne sais pas laquelle (du tout). Ça peut être un tremplin (VIème République), comme cela peut-être le plongeon dans le chaos (installabilité gouvernementale non-maîtrisée).
Que se passerait-il alors si le Président de la République démissionnait? Cest là que je suis un peu plus dans le flou: le nouveau Président de la République pourrait-il prononcer une dissolution? Ou est-ce quil devrait de toutes manières se taper un *shitstorm* pendant un an minimum (et donc difficilement convaincre tout le monde de voter pour son parti un an plus tard)?
## Hypothèse 3bis: Aucune coalition na la majorité absolue, mais une majorité relative se détache
On revient sur un scénario qui est potentiellement «gérable». Cest comme ça que lon vit depuis 2 ans et globalement, le Parlement est en capacité de travailler. Mais toujours pareil que dans lhypothèse précédente: le gouvernement serait en permanence en situation de fragilité. Cela forcerait peut-être Renaissance à faire ami-ami avec le RN ou ami-ami avec le Front Populaire (ou inversement). En gros, il faudrait reprendre le bâton de pélerin et aller chercher les majorités et des compromis.
Entre nous, ce serait potentiellement une bonne chose pour nos chers partis politiques: ça permettrait dinstaller une culture du compromis. Ça pourrait ouvrir la porte à une Assemblée de compromis. Ça pourrait nous sortir de lornière dans la situation présente: nous aurions alors un modèle qui est celui de 95% des démocraties modernes à savoir une élection proportionnelle avec une coalition de fait au pouvoir.
Maintenant, on peut aussi craindre que ça ne mène quà un nouveau serrage de vis dans lequel on sassurera que quoiquil arrive, aucun compromis ne puisse être trouvé mais que nous ayons de nouveau un gouvernement par fait majoritaire. Si lon fait une proportionnelle avec une prime majoritaire de 30% par exemple…
# Conclusage
Bah, jen sais rien. Mais ça fait du bien dimaginer ce qui pourrait se passer… Parce que franchement, cest le merdier dans ma tête et ça fait 2-3 jours que ça tourne. Là, au moins, cest fixé, cest figé.

View File

@@ -0,0 +1,116 @@
+++
title = "Monter son propre NAS"
date = 2024-10-29
[taxonomies]
tags = [ "système", "nas", "zfs", "nfs" ]
+++
![This is brilliant meme: TrueNAS, this is brilliant. Debian, but I like this](/2024/truenas_i_like_this.jpg)
Il y a de nombreuses lunes, javais un NAS TrueNAS Core. Cétait du FreeBSD, il ne faisait pas grand-chose à part du S3 et du NFS et tout allait pour le mieux.
Il se trouve que TrueNAS est en train de petit à petit laisser tomber TrueNAS Core (certains tickets ouverts sur des bogues sont corrigés en TrueNAS Scale mais pas en TrueNAS Core par exemple).
Bref, bienvenue dans les aventures de TrueNAS Scale.
<!-- more -->
# TrueNAS Core, TrueNAS Scale, cest quoi le truc?
TrueNAS Core, cest une base de FreeBSD (qui commence à dater un peu dailleurs). TrueNAS Scale, cest une base de Debian. Les deux font du ZFS, les deux font du NFS, les deux font du CIFS.
La grosse différence, cest donc essentiellement la «fraîcheur» du système et sa maintenabilité.
Maintenant, dans un cas comme dans lautre, mes vieux démons me rattrappent: il mest impossible dintégrer ça proprement nulle part. Ajouter du Munin? Impossible! Faire la conf via Ansible? Impossible! Vous voulez intégrer [acme.sh](https://github.com/acmesh-official/acme.sh)? Impossible!
Certes, il y a une API. Mais faut encore se taper tout le bordel de lAPI en question alors que jai déjà des choses toutes prêtes à côté.
Et le [MinIO S3](https://min.io/) sur TrueNAS Scale est maintenant disponible dans un [K3s](https://k3s.io/) donc en terme de simplicité et de ressources, cest pas ouf… Même si ça fonctionne tout-à-fait correctement, je tiens à le préciser.
Mais comme cest une putain de Debian avec finalement pas grand-chose dessus, est-ce que cest si compliqué que ça à faire soi-même « à la main»? Ben non en fait…
# Installation Debian
Je ne vais pas te mentir, cest presque la partie la plus compliquée. Linstallation de TrueNAS est hyper simple: tout le partitionnement, toute la mécanique derrière, est 100% automatique, tu ne vois rien, tu ne sais rien, tu ne toccupes de rien.
Tout sinstalle aussi sur du ZFS. Bon installer Debian sur du ZFS à la main, cest pas [infaisable mais vlà la race quoi…](https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html). Donc il vaut mieux partir sur plus simple: `mdadm` avec du LVM par dessus.
Jai fini par tomber daccord avec moi-même sur une configuration de ce type: une première partition EFI sur chaque disque système (en loccurence pour moi 2 SSDs de 120Gio recyclés), puis une seconde partition pour `mdadm`.
Une fois, `mdadm` installé, LVM vient après avec une configuration finalement assez classique: `swap`, une partition `/`, une partition `/home` plus petite (il ne devrait pas y avoir dutilisateurs dans mon cas) et une partition `/var` un peu conséquente.
Pareil, je pars aussi du principe que la partie réseau nest pas vraiment un problème: soit tu as une adresse fixe, soit cest du DHCP, mais en gros, tu sais te débrouiller là-dessus et tu na pas de config cheloue avec des interfaces agrégées qui partent dans tous les sens.
# Installation dOpenZFS
Là où ça se complique un peu, cest pour létape daprès: il faut aller importer les informations ZFS existantes.
La première étape, cest donc dinstaller ZFS. Pour éviter un *downgrade*, opération pas toujours bien supportée par ZFS, jai choisi de ne baser sur OpenZFS disponible dans les *backports* de Debian 12. Si tu commences juste avec ZFS et que tu nas créé aucun pool, tu peux très bien démarrer avec les dépôts `contrib` standard.
```bash
echo "deb http://deb.debian.org/debian bookworm-backports main contrib
deb-src http://deb.debian.org/debian bookworm-backports main contrib" > /etc/apt/sources.list.d/bookworm-backports.list
echo "Package: src:zfs-linux
Pin: release n=bookworm-backports
Pin-Priority: 990" > /etc/apt/preferences.d/90_zfs
apt update && apt install dpkg-dev linux-headers-generic linux-image-generic zfs-dkms zfsutils-linux
```
À partir de ce moment, tu devrais avoir un ZFS parfaitement fonctionnel. Encore une fois, sil sagit dune récup depuis un ZFS existant, faut suivre la suite. Sil sagit dune première installation, faut créer les volumes, la doc en ligne devrait te suffire.
Généralement, TrueNAS monte lensemble des volumes dans `/mnt`. Pour refaire la même, il faut faire deux ou trois petites manips côté ZFS:
```bash
zpool import -fR /mnt <mon volume>
zfs set mountpoint=/mnt/<mon volume> <mon volume>
```
Ces deux jolies lignes permettent dimporter le volume existant (en forçant, sinon ZFS nest pas content), de le monter au même endroit que ce que faisait TrueNAS et de mettre ce point de montage en dur dans ZFS.
Ça permet de sassurer quon retrouvera les mêmes chemins ce qui est très important pour NFS, nettement moins pour les autres services.
Pour permettre au démon ZFS de monter tout ce petit monde au démarrage du serveur, il faut aller fixer un fichier de cache du côté de la configuration dudit démon.
```bash
zpool set cachefile=/etc/zfs/zpool.cache <mon volume>
```
Une fois rendu là, tu devrais avoir des points de montage exactement similaire à ce que fait TrueNAS. Et les mêmes volumes. Tu peux dailleurs en profiter pour faire un peu de ménage et supprimer les datasets qui ne seront plus utiles de toutes manières.
```bash
zfs destroy <mon volume>/.system
```
# Installation de NFS
Bon là, cest un peu con comme la mort, on va pas se mentir.
```bash
apt install nfs-kernel-server && systemctl start --enable nfs-server.service
```
Cest à peu près tout. Bon, idéalement, il faut récupérer le fichier `/etc/exports` qui existait à lorigine sur le TrueNAS dorigine. Si ce nest pas ton cas, il faudra aller le recréer, mais la syntaxe nest pas bien compliqué.
Une fois arrivé là, tu as déjà 90% de TrueNAS, sauf évidemment si tu nas aucun self-respect et que tu fais du CIFS. Mais seuls les déments font ça, donc je suppose que cest bon.
# SMART
Histoire de compléter, tu peux installer SMART. Là, aussi cest à peu près très con:
```bash
apt install smartmontools && systemctl start --enable smartd.service
```
# Le reste
Une fois que tout cela est fait, tu fais… bah ce que tu veux! Tas besoin de rajouter du S3, tu peux faire du [garage](/tags/garage/). Tas besoin dhéberger du Bittorent, tu peux faire du transmission, du déluge, etc…
Si ton TrueNAS ne te servait vraiment que de stockage, là, tu nas que du stockage, point.
# Conclusage
Oui, cest déjà fini. En fait, avec ça, tu as un TrueNAS maison. Évidemment, il faudra potentiellement rajouter un relais SMTP, de la supervision, etc… Mais je suppose ici que tu as déjà ça à côté et que tu sais faire en gros.
TrueNAS, ça se limite à peu près à ça: gérer du ZFS, gérer du partage, gérer des disques.
Manque linterface clicaconvy, mais qui a vraiment envie de tout faire en passant par ça ou par une API imbitable?

4
content/_index.md Normal file
View File

@@ -0,0 +1,4 @@
+++
sort_by = "date"
+++

BIN
static/.39fmvg_m.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
static/.39fmvg_s.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/.39fmvg_sq.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
static/.39fmvg_t.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
static/.N64_schema_s.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
static/.N64_schema_sq.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/.N64_schema_t.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Some files were not shown because too many files have changed in this diff Show More