first commit
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
public/
|
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "themes/terminimal"]
|
||||||
|
path = themes/terminimal
|
||||||
|
url = https://github.com/pawroman/zola-theme-terminimal.git
|
46
config.toml
Normal 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"},
|
||||||
|
]
|
72
content/2009-12-11_ipv6-dans-la-pratique-partie-1.md
Normal 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ï !
|
84
content/2009-12-17_ipv6-dans-la-pratique-partie-2.md
Normal 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…
|
83
content/2009-12-25_IPv6-dans-la-pratique-(partie-3).md
Normal 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).
|
@@ -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…
|
@@ -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…
|
@@ -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…
|
@@ -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.
|
@@ -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.
|
67
content/2010-07-09_IPv6,-tunnelbroker-et-table-forward.md
Normal 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.
|
@@ -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.
|
@@ -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…
|
@@ -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`.
|
@@ -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.
|
@@ -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.
|
@@ -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.
|
@@ -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…
|
||||||
|
|
@@ -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 :
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
74
content/2010-10-11_Organiser-des-volumes-mirroirs-LVM.md
Normal 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/
|
@@ -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…
|
@@ -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.
|
145
content/2010-11-26_Du-routage-à-moteur-dans-ton-tunnel-IPSEC.md
Normal 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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
ê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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
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.
|
@@ -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 :
|
||||||
|
|
||||||
|

|
@@ -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é.
|
@@ -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.
|
@@ -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.
|
@@ -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.
|
@@ -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…
|
96
content/2011-08-30_Faire-mentir-un-serveur-DNS.md
Normal 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…
|
@@ -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 !!!§§!
|
69
content/2011-09-24_Multiples-BSSID-avec-hostapd.md
Normal 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 !
|
@@ -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.
|
56
content/2011-12-04_Dépucelage-DNSSEC-(1).md
Normal 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.
|
@@ -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…
|
89
content/2012-05-09_Dépucelage-DNSSEC-(2).md
Normal 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 [là](http://dnssec-debugger.verisignlabs.com/) ou [là](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à…
|
@@ -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 !
|
@@ -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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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 !
|
122
content/2012-09-14_nginx-:-ne-pas-faire-de-la-merde-avec-PHP.md
Normal 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.
|
@@ -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 :).
|
@@ -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.
|
128
content/2013-02-15_DNSSEC-:-méthode-BIND9.9.md
Normal 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.
|
110
content/2013-07-12_OpenBSD-en-lecture-seule.md
Normal 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 ?
|
@@ -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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
É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.
|
@@ -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 !
|
260
content/2015-09-28_mdadm-LVM-ext4-vers-BTRFS-sans-larme.md
Normal 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ï !
|
59
content/2016-02-15_Microsoft,-je-te-chie-dans-la-bouche.md
Normal 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 l’occurrence 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.
|
343
content/2016-12-26_Z-Push,-IMAP,-sexe-et-NextCloud.md
Normal 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.
|
30
content/2017-01-04_uBlock-Origin-:-ne-plus-filtrer-Piwik.md
Normal 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…
|
95
content/2018-03-24_Ma-République-est-morte.md
Normal 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.
|
@@ -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 !
|
@@ -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.
|
@@ -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
|
@@ -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.
|
76
content/2019-02-25_Ne-m-appelez-plus-privé-!.md
Normal 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.
|
45
content/2019-03-12_XMPP-et-OMEMO-sont-dans-un-bâteau.md
Normal 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 ;)
|
@@ -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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
@@ -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" ]
|
||||||
|
+++
|
||||||
|
J’aurais aussi pu appeler ça : 1 fille, 2 garçons, 3 possibilités. Ah, c’est donc ça un titre putaclic…
|
||||||
|
<!-- more -->
|
||||||
|
Coucou mes rondoudous, c’est 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 n’empê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 m’attaquer à 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, j’ai 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 j’ai 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 c’est pour la bonne cause.
|
||||||
|
|
||||||
|
## Donc c’est quoi en fait une appli PHP ?
|
||||||
|
|
||||||
|
Commençons par la base, à savoir comment c’est 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 d’exécution (PHP-FPM) et très souvent une base de données (MariaDB, mais ça peut être autre chose).
|
||||||
|
|
||||||
|
Les configurations à base d’Apache mélangent allègrement présentation et exécution, ce qui n’est 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é l’ensemble 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 n’est pas conscient qu’il y a une base de données (c’est pas son boulot) ;
|
||||||
|
2. NginX et PHP-FPM doivent avoir les mêmes chemins sur le serveur local pour que cela fonctionne (c’est 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 l’application PHP ait besoin d’écrire dans des répertoires (fichiers temporaires, compilation de gabarits, téléversement de données, etc…).
|
||||||
|
|
||||||
|
Voilà, maintenant qu’on a clairement établi les règles du jeu, voyons comment on peut empaqueter une application PHP dans du Docker. Pour l’exemple, on va utiliser phpBB. Il contient à peu près ce qu’on peut faire de pire en matière d’applications 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 l’application dans les conteneurs (ou solution NextCloud)
|
||||||
|
|
||||||
|
## Solution 3 : embarquer l’application dans UN conteneur (ou solution Mastodon)
|
244
content/2019-09-01-Docker-dans-LXC,-l’aventure.md
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
+++
|
||||||
|
|
||||||
|
title = "Docker dans LXC, l’aventure"
|
||||||
|
date = 2019-09-01
|
||||||
|
aliases = [ "post/2019/09/01/Docker-dans-LXC,-l’aventure"]
|
||||||
|
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, c’est bien, en abuser, ça craint !
|
||||||
|
|
||||||
|
LXC/LXD, ça devrait parler à tout le monde ici : c’est 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 n’est pas fait pour ça. C’est fait essentiellement pour isoler un peu le système invité du système hôte, mais on continue à utiliser le noyau de l’hôte (avec tous ces modules).
|
||||||
|
|
||||||
|
Docker aussi, ça devrait parler à tout le monde (au moins parce que c’est la mode). C’est exactement pareil, mais l’idée est ici de faire tourner un environnement qui ne fait lui-même fonctionner qu’un seul processus. Si le VE ressemble donc à une VM castré, Docker, c’est 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 l’hôte (généralement +100 000).
|
||||||
|
+++
|
||||||
|
|
||||||
|
title = "DMARC : toi aussi aide tes petits camarades à vérifier qu’ils n’ont 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-qu’ils-n’ont-pas-merdé"]
|
||||||
|
+++
|
||||||
|
Parce que personne n’est parfait, surtout pas toi…<!-- more -->DMARC, c’est le machin qui est imposé maintenant par Google, Microsoft & Co dès que tu veux leur envoyer un message. Et en vrai, c’est pas une si mauvaise idée que ça. Le principe de base est le suivant :
|
||||||
|
|
||||||
|
- on t’a obligé à avoir du SPF, sinon on refuse tes messages. Mais le souci, c’est que les redirections (fréquentes dans ce domaine), ça casse tout et on ne peut rien y faire ;
|
||||||
|
- on t’a du coup obligé à avoir du DKIM. Ça signe tous les messages, et ça permet de s’assurer qu’effectivement, ça vient bien de là où ça prétend venir.
|
||||||
|
|
||||||
|
On a donc, d’une 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 s’assurer que les messages sont bien légitimes. Inversement, si DKIM merde (non-signature, message technique d’un relais, etc…), on peut au moins s’assurer 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!!
|
||||||
|
|
||||||
|
C’est précisément là qu’intervient DMARC : l’idée est de donner une politique générale sur ce qu’il faut faire si le message semble chelou. « Chelou » en DMARC, ça veut dire qu’il vient du mauvais endroit (donc viole la politique SPF) **ET** qu’il n’est 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 d’accepter un message non-signé qui vient d’une 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, c’est bien un **ET** et pas un **OU** : le protocole prévoit le fait qu’un message peut être redirigé (donc violer sciemment la politique SPF, mais en conservant dans la plupart des cas la signature DKIM d’origine) 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 t’informer de ce qui a été fait et pourquoi. Ça peut aussi te permettre de repérer les p’tits 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, c’est tout
|
||||||
|
- ''quarantine'' : tu classes en pourriel
|
||||||
|
- ''reject'' : pas la peine d’accepter les messages, visiblement, c’est de la merde
|
||||||
|
|
||||||
|
Dans les deux derniers cas, tu peux même spécifier un pourcentage de message qui subiront ce traitement. L’idé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 qu’ils font de la merde.
|
||||||
|
|
||||||
|
## Bon, vas-y, tu me l’as vendue ta merde, comment je fais pour ça chez moi ?
|
||||||
|
|
||||||
|
La première chose à faire est de s’assurer que tu peux recevoir les rapports DMARC des autres. Pour cela, c’est relativement simple, il suffit d’ajouter 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 qu’il n’y 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 n’est 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 d’avoir 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 à l’adresse 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 qu’un certain pourcentage de message soit traité de cette manière là. L’idé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 qu’il 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à, t’as déjà de quoi t’amuser un moment et tu vas pouvoir voir non seulement si ta configuration est bonne dans tous les cas, mais en plus voir s’il n’y a pas des enfoirés qui essaient de te la mettre à l’envers.
|
||||||
|
|
||||||
|
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 j’ai 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, c’est pas aussi simple que ça.
|
||||||
|
|
||||||
|
## Au début, il y avait **syslog**
|
||||||
|
|
||||||
|
La base du problème n’est finalement pas si compliqué que ça : quand on a un ou plusieurs serveurs SMTP, on peut commencer par balancer l’ensemble 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 c’est à 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, c’est 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 n’a 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, c’est 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 c’est à 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 (qu’on 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 l’heure, le nom du programme et c’est à peu près tout. Ça peut déjà pas mal servir, mais on va quand même essayer d’affiner 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 qu’il 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à t’amuser dans l’interface de Kibana pour essayer de retrouver certains messages, des destinataires, des expéditeurs et ainsi de suite :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Et rien qu’avec ç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, c’est très bien, mais à un moment, quelqu’un viendra forcément te poser la question : et comment je fais pour savoir qui a écrit à qui ?
|
||||||
|
|
||||||
|
## Ça a l’air simple comme ça, mais en fait non…
|
||||||
|
|
||||||
|
Dit comme ça, ça a effectivement l’air tout con et dans l’absolu, 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, c’est 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 d’un point de vue données, tu n’as pas tout sur une seule ligne !*
|
||||||
|
|
||||||
|
Et Elasticsearch a ce côté un peu chiant : si tu n’as 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 n’est 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 à quelqu’un d’autre 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 l’air con…
|
||||||
|
|
||||||
|
J’arrive mes petits bisous, j’arrive. En fait, j’ai tourné en rond sur ce problème pendant un bon moment. J’ai d’abord cherché à voir si je pouvais faire des corrélations un peu complexes dans ELK lui-même. Ça n’a pas vraiment donné les résultats que j’attendais (probablement pas impossible à faire, mais bien trop compliqué à mon goût). Il paraît que la version payant d’ELK 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, c’est quand même plus compliqué).
|
||||||
|
|
||||||
|
J’ai ensuite cherché comment faire du multiligne. C’est 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 l’ont déjà fait](https://github.com/whyscream/postfix-grok-patterns).
|
||||||
|
|
||||||
|
Le souci, c’est 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 d’efforts, c’est 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 qu’il était peut-être possible d’enrichir les logs de Postfix avec d’autres données. Si on n’arrive 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 d’autres 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 c’est même relativement simple. Dans la configuration de Postfix (`main.cf`), il suffit d’ajouter 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 l’entête **Suject** (donc tous les messages en gros) et de lui attribuer le niveau **WARNING**. On relance Postfix et là, voilà ce qu’on 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 l’ensemble des messages qui sont passés par ton SMTP, avec le destinataire, l’expéditeur et le sujet du message :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
109
content/2019-10-24_RUST-:-jouer-avec-le-typage-fort.md
Normal 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 -->
|
||||||
|
J’ai récemment appris Rust. Parce que le langage est marrant avec son concept d’appartenance (*ownership*), sa gestion des erreurs très intelligentes, ses pointeurs intelligents (*smart pointers*). Parce que j’aime bien apprendre des choses.
|
||||||
|
|
||||||
|
Et surtout parce que quand le shell n’est 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 qu’on peut simplement tout embarquer dans un exécutable Rust si on en a besoin.
|
||||||
|
|
||||||
|
Bref, c’est cool.
|
||||||
|
|
||||||
|
Toutefois, Rust reste un langage fortement typé et quand on a l’habitude des langages faiblement typés (PHP, Python, etc…), cela peut nécessiter un sacré temps d’adaptation pour s’y remettre. Soyons clairs : j’aime bien les langages fortement typés. Ça donne l’impression de savoir ce que l’on fait et ça permet d’éliminer un nombre substantiels de causes d’erreur. Mais c’est aussi pas évident du tout à manipuler et ça prend nettement plus de temps pour arriver au même résultat qu’un langage faiblement typé.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Je n’ai pas la prétention de vous apprendre à programmer en Rust. Même si j’ai commencé à faire des choses bien plus compliquées dans ce langage, j’ai 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 l’apprendre, 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 d’afficher 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 l’utiliser 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 l’on 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, c’est hyper cohérent : on lui dit qu’une fonction prend en paramètre un `u64`, on essaie de lui passer un `u8`, le compilateur gueule parce que ce n’est pas normal. Ce n’est pas le même type. Et même si dans l’absolu un `u8` rentre parfaitement dans un `u64`, Rust s’en 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` (c’est 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 n’est pas forcément très élégant
|
||||||
|
* tu vas te retrouver à jongler avec des types alors que finalement, ça ne t’inté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 t’implémenter Martine !!
|
||||||
|
|
||||||
|
En fait, on peut très facilement rendre cette fonction beaucoup plus universelle en remplaçant le type fixe par un ''Trait''. L’idée est ici de dire : finalement vu ce que je fais dans la fonction, ce qui m’intéresse, c’est que ce qui est passé en paramètre puisse s’afficher 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 qu’on 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, c’est typiquement le genre de choses sur lequel on bute bêtement au départ parce que ce n’est pas vraiment intuitif (en tout cas, pas tant qu’on a pas lu le chapitre sur le sujet dans le Book).
|
||||||
|
|
||||||
|
Quand on vient du C ou du PHP, c’est le type de problématique qui paraît insurmontable au départ alors qu’en réalité, c’est 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.
|
@@ -0,0 +1,79 @@
|
|||||||
|
+++
|
||||||
|
|
||||||
|
title = "DMARC : toi aussi aide tes petits camarades à vérifier qu’ils n’ont pas merdé (1/2)"
|
||||||
|
date = 2019-12-16
|
||||||
|
aliases = [ "post/2019/11/28/DMARC-:-toi-aussi-aide-tes-petits-camarades-à-vérifier-qu’ils-n’ont-pas-merdé"]
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "mail", "internet" ]
|
||||||
|
+++
|
||||||
|
Parce que personne n’est parfait, surtout pas toi…
|
||||||
|
<!-- more -->
|
||||||
|
DMARC, c’est le machin qui est imposé maintenant par Google, Microsoft & Co dès que tu veux leur envoyer un message. Et en vrai, c’est pas une si mauvaise idée que ça. Le principe de base est le suivant :
|
||||||
|
|
||||||
|
- on t’a obligé à avoir du SPF, sinon on refuse tes messages. Mais le souci, c’est que les redirections (fréquentes dans ce domaine), ça casse tout et on ne peut rien y faire ;
|
||||||
|
- on t’a du coup obligé à avoir du DKIM. Ça signe tous les messages, et ça permet de s’assurer qu’effectivement, ça vient bien de là où ça prétend venir.
|
||||||
|
|
||||||
|
On a donc, d’une 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 s’assurer que les messages sont bien légitimes. Inversement, si DKIM merde (non-signature, message technique d’un relais, etc…), on peut au moins s’assurer 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!!
|
||||||
|
|
||||||
|
C’est précisément là qu’intervient DMARC : l’idée est de donner une politique générale sur ce qu’il faut faire si le message semble chelou. « Chelou » en DMARC, ça veut dire qu’il vient du mauvais endroit (donc viole la politique SPF) **ET** qu’il n’est 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 d’accepter un message non-signé qui vient d’une 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, c’est bien un **ET** et pas un **OU** : le protocole prévoit le fait qu’un message peut être redirigé (donc violer sciemment la politique SPF, mais en conservant dans la plupart des cas la signature DKIM d’origine) 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 t’informer de ce qui a été fait et pourquoi. Ça peut aussi te permettre de repérer les p’tits 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, c’est tout
|
||||||
|
- ''quarantine'' : tu classes en pourriel
|
||||||
|
- ''reject'' : pas la peine d’accepter les messages, visiblement, c’est de la merde
|
||||||
|
|
||||||
|
Dans les deux derniers cas, tu peux même spécifier un pourcentage de message qui subiront ce traitement. L’idé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 qu’ils font de la merde.
|
||||||
|
|
||||||
|
## Bon, vas-y, tu me l’as vendue ta merde, comment je fais pour ça chez moi ?
|
||||||
|
|
||||||
|
La première chose à faire est de s’assurer que tu peux recevoir les rapports DMARC des autres. Pour cela, c’est relativement simple, il suffit d’ajouter 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 qu’il n’y 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 n’est 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 d’avoir 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 à l’adresse 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 qu’un certain pourcentage de message soit traité de cette manière là. L’idé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 qu’il 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à, t’as déjà de quoi t’amuser un moment et tu vas pouvoir voir non seulement si ta configuration est bonne dans tous les cas, mais en plus voir s’il n’y a pas des enfoirés qui essaient de te la mettre à l’envers.
|
||||||
|
|
||||||
|
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 ;)
|
@@ -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 j’ai 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, c’est pas aussi simple que ça.
|
||||||
|
|
||||||
|
## Au début, il y avait **syslog**
|
||||||
|
|
||||||
|
La base du problème n’est finalement pas si compliqué que ça : quand on a un ou plusieurs serveurs SMTP, on peut commencer par balancer l’ensemble 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 c’est à 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, c’est 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 n’a 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, c’est 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 c’est à 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 (qu’on 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 l’heure, le nom du programme et c’est à peu près tout. Ça peut déjà pas mal servir, mais on va quand même essayer d’affiner 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 qu’il 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à t’amuser dans l’interface de Kibana pour essayer de retrouver certains messages, des destinataires, des expéditeurs et ainsi de suite :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Et rien qu’avec ç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, c’est très bien, mais à un moment, quelqu’un viendra forcément te poser la question : et comment je fais pour savoir qui a écrit à qui ?
|
||||||
|
|
||||||
|
## Ça a l’air simple comme ça, mais en fait non…
|
||||||
|
|
||||||
|
Dit comme ça, ça a effectivement l’air tout con et dans l’absolu, 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, c’est 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 d’un point de vue données, tu n’as pas tout sur une seule ligne !*
|
||||||
|
|
||||||
|
Et Elasticsearch a ce côté un peu chiant : si tu n’as 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 n’est 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 à quelqu’un d’autre 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 l’air con…
|
||||||
|
|
||||||
|
J’arrive mes petits bisous, j’arrive. En fait, j’ai tourné en rond sur ce problème pendant un bon moment. J’ai d’abord cherché à voir si je pouvais faire des corrélations un peu complexes dans ELK lui-même. Ça n’a pas vraiment donné les résultats que j’attendais (probablement pas impossible à faire, mais bien trop compliqué à mon goût). Il paraît que la version payant d’ELK 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, c’est quand même plus compliqué).
|
||||||
|
|
||||||
|
J’ai ensuite cherché comment faire du multiligne. C’est 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 l’ont déjà fait](https://github.com/whyscream/postfix-grok-patterns).
|
||||||
|
|
||||||
|
Le souci, c’est 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 d’efforts, c’est 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 qu’il était peut-être possible d’enrichir les logs de Postfix avec d’autres données. Si on n’arrive 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 d’autres 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 c’est même relativement simple. Dans la configuration de Postfix (`main.cf`), il suffit d’ajouter 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 l’entête **Suject** (donc tous les messages en gros) et de lui attribuer le niveau **WARNING**. On relance Postfix et là, voilà ce qu’on 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 l’ensemble des messages qui sont passés par ton SMTP, avec le destinataire, l’expéditeur et le sujet du message :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
43
content/2020-11-03_Passage-à-zola.md
Normal 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 c’est pas grave !
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Comme tu as pu le remarquer, les choses n’ont pas tout-à-fait le même aspect depuis quelques jours : je suis en effet passé d’un blog [Dotclear](https://dotclear.org/) à un site statique généré par [Zola](https://www.getzola.org/).
|
||||||
|
|
||||||
|
# Les sites statiques en fait, c’est bien
|
||||||
|
|
||||||
|
Cela faisait déjà quelques temps que je gérais l’ensemble des articles en Markdown plutôt qu’avec le système d’éditeur Wiki de Dotclear ou en HTML brut (ce que j’ai fait pendant quelques temps d’ailleurs 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 qu’on pouvait échapper, ce qu’on ne pouvait pas échapper, ce qu’il fallait absolument échapper
|
||||||
|
|
||||||
|
Ça m’a donc donné envie de prendre une autre direction et vu le peu d’activité, 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 d’articles 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/) s’occupera 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, c’est une sacrée économie : moins de processus PHP à faire tourner, une base de données en moins sur le serveur (évidemment, comme j’ai plein de bordels qui tournent à côté de cela, ce n’est pas ultra significatif non plus, mais bon, c’est 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 qu’un mot de passe fuite, qu’une page soit attaquée, etc…
|
||||||
|
|
||||||
|
# Et du coup, ça se passe comment en fait ?
|
||||||
|
|
||||||
|
C’est en réalité assez simple. La commande `zola init` permet de créer l’ensemble des répertoires nécessaires au bon fonctionnement du blog. Il suffit alors d’ajouter 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 c’est à peu près tout !**
|
||||||
|
|
||||||
|
Une fois, les premiers articles écrits (ou importés manuellement depuis d’autres 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 d’aller surfer sur le blog comme si on y était (à une exception près : les flux RSS sont complètement pétés, ce qui m’a 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 qu’on n’a pas fait d’erreur 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 l’opération…
|
||||||
|
|
||||||
|
Oui. Et je me rends bien compte que c’est un peu dommage : pouvoir réagir de suite et directement auprès de l’auteur d’un 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é, c’est largement plus rentable pour moi de laisser tomber. Si tu as des remarques à me faire, tu trouveras certainement un moyen de me contacter d’une manière ou d’une 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 c’est 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 c’est là, le plus important.
|
||||||
|
|
@@ -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 l’avait fait avec LVM. C’était drôle mais finalement ptet pas autant qu’avec 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 l’aile ? 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 qu’il donne des signes de faiblesse mais sur unu machine de bureau, comme la mienne, ce n’est pas toujours évident de s’en rendre compte à temps.
|
||||||
|
|
||||||
|
Et donc mon disque de 3Tio commence à ramer sévère et je me dis qu’il va forcément péter un de ses 4. `smartctl` m’a confirmé qu’il avait déjà un pied dans la tombe, il est donc temps de faire quelque chose avant qu’il 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 j’ai 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 l’on cherche à tranférer. La commande se lance en arrière-plan, il n’y a donc pas à s’inquié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 c’est tout mes loulous. Après, il n’y a qu’à patienter jusqu’à ce que le transfert soit complet (en ce qui me concerne ça a pris 18 heures). Toujours pour mon cas, j’avais une dernière chose à faire : je suis parti d’un disque de 3Tio et l’ai remplacé par un disque de 4Tio, il a donc fallu que j’agrandisse 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 !
|
||||||
|
|
173
content/2021-01-11-Redimensionner-un-disque-openbsd.md
Normal 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 l’expérience (et surtout parce que c’était à peu près mon cas d’usage), j’ai 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. C’est la seule que tu pourras facilement redimensionner (ça tombe bien, c’est ce que je comptais faire).
|
||||||
|
|
||||||
|
Bref, donc on ne peut pas redimensionner n’importe 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 n’en 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 c’est 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 l’ai passé à 64G pour les besoins de la démonstration, mais évidemment, ça pourrait fonctionner avec n’importe 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, à l’invite de boot, il faut préciser `bsd.rd`. Il s’agit du disque de secours qui est inclus à l’installation (sauf si tu l’as enlevé mais je pars du principe que tu n’es pas un sauvage à ce point-là) :
|
||||||
|
|
||||||
|
```
|
||||||
|
>> OpenBSD/amd64 BOOT 3.52
|
||||||
|
boot> bsd.rd
|
||||||
|
```
|
||||||
|
Une fois démarré, choisis l’option `(S)hell` pour tomber sur un shell standard (`/bin/sh`). À ce stade, le `/dev` de notre OpenBSD n’est pas encore peuplé, il va donc au moins falloir y mettre le disque en question. Si c’est un disque SATA, ce sera `sd0` si c’est 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) n’est pas encore tout-à-fait à jour. On va donc forcer `fdisk` à faire ce qu’il 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 l’espace 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 t’en 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 qu’il 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 l’on 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 qu’on a récupéré de l’espace 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 j’aurais tendance à dire que t’auras plus vite fait de tout réinstaller. Néanmoins, le partition automatique étant pas trop mal foutu, il s’arrange pour avoir suffisamment de place partout et garder la partition qui va probablement satûrer le plus vite à la fin.
|
||||||
|
|
||||||
|
C’est loin d’être une science exacte mais jusqu’à présent, je l’ai plutôt bien vérifié dans la pratique.
|
@@ -0,0 +1,189 @@
|
|||||||
|
+++
|
||||||
|
title = "L’incroyable aventure de Pulseaudio et la carte Nvidia"
|
||||||
|
date = 2021-02-26
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|
« Et le magicien changea alors la configuration d’Alsa pour le rendre compatible avec cette sortie HDMI de merde ! »
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# Bon, c’est 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 ; à d’autre moment, les sortie stéréo HDMI s’inverse (il y a deux écrans branchés dessus et de temps en temps le son part sur l’un, de temps en temps sur l’autre).
|
||||||
|
|
||||||
|
En fait, j’ai découvert que cette conne de carte avait un comportement assez bizarre :
|
||||||
|
* elle affiche 7 sorties (!) alors qu’elle n’en 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, j’en vois un paquet qui ont des numéros et bien évidemment rien n’est 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 d’autres 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 : c’est donc seulement une entrée, pas une sortie, d’où 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 j’ignore complètement, elle indique 7 sorties (comme marqué plus haut) alors qu’elle n’en a physiquement que 4. Le plus étrange, c’est que `xrandr` aussi m’indique 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 j’ai (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 ?
|
||||||
|
|
||||||
|
# L’approche par Pulseaudio
|
||||||
|
|
||||||
|
La vision côté Alsa, tu peux la voir plus haut. La vision côté Pulseaudio n’est 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 d’empêcher toute sortie son par là.
|
||||||
|
|
||||||
|
L’approche naïve consiste donc à se dire : il suffit d’utiliser un petit logiciel comme `pavucontrol` pour régler l’ensemble 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é : j’ai choisi le profil qui va bien pour sortir le son sur l’écran principal et le tour était joué.
|
||||||
|
|
||||||
|
Ça n’a pas réglé mon souci de son absent en sortie de veille (on y reviendra), mais effectivement ça « marche ». Sauf qu’au premier redémarrage, sans rien toucher physiquement évidemment, Pulseaudio s’emmêle les saucisses et la sortie HDMI3 devient la sortie HDMI2, la sortie HDMI4 devient la 1, etc…
|
||||||
|
|
||||||
|
**Impossible d’avoir 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 qu’elles 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 qu’il crée un profil utilisateur. Ça, ça veut dire que c’est 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. C’est assez étonnant d’ailleurs 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 d’Alsa (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 qu’un groupe serait codé en dur dedans : si l’utilisateur n’est pas dans le groupe `audio`, y’a 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, j’en ai conclu qu’il 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 qu’il veut (soit au démarrage, soit en sortie de veille). J’aurais tendance à penser que le processus de détection n’est 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 n’importe quoi avec la détection des sorties. Ou il est encore possible que ce soit une combinaison des deux. Quoiqu’il arrive, il est clair que je n’arriverai 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, l’ordre des sorties, etc… tout sera de toutes manières capables de sortir du son et il n’y aura plus qu’à rendre les sorties inutiles muettes manuellement.
|
||||||
|
|
||||||
|
Sauf que par défaut, Pulseaudio n’a 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, c’est donc de celui-ci qu’on va se servir :
|
||||||
|
```
|
||||||
|
[Profile forced-hdmi-stereo]
|
||||||
|
description = Foobar
|
||||||
|
output-mappings = hdmi-stereo hdmi-stereo-extra1 hdmi-stereo-extra2
|
||||||
|
input-mappings =
|
||||||
|
```
|
||||||
|
|
||||||
|
C’est assez bête basiquement : ce profil dit simplement que toutes les sorties Pulseaudio peuvent être utilisées en même temps. Maintenant, ce n’est pas parce qu’elles peuvent être utilisées en même temps qu’elles le seront. En fait, si tu sélectionnes le profil `Foobar` dans l’onglet Configuration de `pavucontrol`, tu peux alors zapper entre les différentes sorties sans repasser par un profil particulier.
|
||||||
|
|
||||||
|
Sauf que pour le moment, c’est 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 d’une 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, c’est 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 d’utiliser 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à, c’est à peu près aussi con que ça.
|
||||||
|
|
||||||
|
La dernière ligne sert juste à sélectionner cette sortie par défaut.
|
||||||
|
|
||||||
|
# Bon OK, t’as du son mais est-ce que t’en as toujours en sortie de veille ?
|
||||||
|
|
||||||
|
Non toujours pas :/
|
||||||
|
|
||||||
|
C’était un peu prévisible : j’ai deux problèmes en fait. Le premier est maintenant réglé, pour le second voilà ce qu’il en est… Pulseaudio semble refaire une détection complète à la sortie de veille (dans le cadre d’un PC Portable, ça paraît logique en fait) et la carte Nvidia n’est 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, c’est que je n’ai 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 qu’il faut qu’il bascule les sources et les sorties ailleurs pour éviter de couper le son. L’intention est louable mais en l’occurence, c’est un peu inutile pour mon cas.
|
||||||
|
|
||||||
|
J’ai donc simplement désactivé le module qui s’occupe de ça, en l’occurence `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 c’est une bonne chose dans l’absolu, mais je pense qu’il faut aussi laisser la possibilité à l’utilisateur de fixer un certain nombre de choses si ça foire ou si ça n’est pas adapté à la situation.
|
||||||
|
|
||||||
|
Ce que j’ai 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 s’agiter dans la conf ou dans les menus ou commandes à chaque sortie de veille.
|
290
content/2021-04-25-Rust-faire-joujou-avec-async.md
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
+++
|
||||||
|
title = "Rust: faire joujou avec Async"
|
||||||
|
date = 2021-04-25
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "rust", "codaz" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|
Qu’est-qu’on veut ? *Maintenant !*
|
||||||
|
Et quand est-ce qu’on le veut ? *De l’async !*
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# Qu’est-ce que c’est 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, c’est juste une manière de faire des choses de manières concurrentes. En gros, au lieu de chercher à exécuter le programme dans l’ordre, 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 qu’en 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**, l’idée est d’exécuter tout ça dans le même thread mais en profitant des temps morts à droite à gauche pour essayer de gagner du temps. L’exemple typique est celui de l’écriture dans un fichier : c’est une opération qui bloque le thread d’exécution pendant longtemps pour rien : le processeur ne glande rien et attend juste le disque, donc autant lui faire exécuter d’autres choses en attendant que ça passe et récupérer l’information sur l’écriture du fichier un peu plus tard.
|
||||||
|
|
||||||
|
Bon, ça évidemment, c’est la jolie théorie qu’on décrit un peu partout. Mais concrètement comment ça se passe ?
|
||||||
|
|
||||||
|
# Mettre des choses dans Async/Await, bah en fait, c’est pas si simple que ça…
|
||||||
|
|
||||||
|
Si je reprends l’exemple donné sur le site de [tokio](https://tokio.rs/tokio/tutorial/hello-tokio) à part mettre des `await` partout, tu fais pas grand-chose d’asynchrone là-dedans. Et en fait, c’est vrai : basiquement, ça ne fait rien d’asynchrone. En fait, `async/await` en Rust peut effectivement commencer à exécuter des trucs en asynchrone sans qu’on 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 à t’en douter, j’ai passé un bon moment à me gratter la tête avec ses histoires d’async, sans vraiment comprendre l’inté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 n’est pas une réponse universelles à tout, ça n’est peut-être même pas un bon exemple, mais disons que c’est là que je suis arrivé après plusieurs heures de recherche et de tatônnement (j’ai 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 qu’on ait 4 API en entrée (nous l’appellerons *API entrante*) qu’on 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 c’est complètement con, mais c’est pour les besoins de l’exercice). 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). L’idée est donc de voir si on peut optimiser et paralléliser.
|
||||||
|
|
||||||
|
Voici donc le code permettant de demandes des infos à l’API 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 qu’on 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
|
||||||
|
|
||||||
|
L’approche 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 l’occurence, on attend simplement que chaque API entrante réponde l’ensemble de ses résultats), on les balance dans l’API 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 qu’on 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 l’API 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 l’implémentation d’`async` dans Rust : si tu ne lui demandes rien, un `future` (donc une fonction `async` ici) ne s’exécute pas. **En gros, si tu l’attends 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 qu’on 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 n’est plus gênant de déclencher l’attente 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 c’est 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 c’est qu’on 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 d’exécuter.** Bon, en plus, en l’occurence, on ne traite pas vraiment comme on le devrait. En fait, idéalement, il faudrait lancer la consultation de l’API entrante en parallèle aussi (parce que là, ce n’est pas fait), puis lancer le traitement de chaque résultat en parallèle, puis attendre que l’ensemble ait fini avant de sortir complètement.
|
||||||
|
|
||||||
|
# *craquement de doigts*
|
||||||
|
|
||||||
|
Une première approche consiste donc à lancer en parallèle l’ensemble des traitement initiaux sur l’API entrante, de récupérer ses résultats et de les transmettre au thread principal pour qu’il puisse faire les traitements. De cette manière, ça ira un peu plus vite dans le sens où l’on 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 d’autres types de messages et de mode de transmission). Si l’on 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 l’API 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 {
|
||||||
|
// qu’on 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 n’a 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 l’API sortante et on perd du coup beaucoup de temps. Idéalement, il faudrait également rendre asynchrone le traitement des résultats.
|
||||||
|
|
||||||
|
# Où l’on se rend compte que les grands principes de la science sont constants
|
||||||
|
|
||||||
|
Les mêmes causes produisant les mêmes effets, si l’on ne fait que `spawn` des traitements de l’API 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 d’abord, ce ne serait pas drôle et ensuite, on est quand même obligé de déterminer la capacité d’un canal de communication **avant** de l’ouvrir. Dans notre cas, ça pourrait fonctionner puisqu’on connait à l’avance le nombre maximum de résultalts, mais on pourrait imaginer des cas où l’on en est pas capable (ou en tout cas, pas suffisamment précisément).
|
||||||
|
|
||||||
|
Donc, on va essayer une autre stratégie : lancer l’ensemble des threads de traitement dans un coin, stocker leur représentation dans un vecteur et simplement attendre jusqu’à ce que l’ensemble 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 l’on 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 qu’on 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 we’re waiting for everyone now…");
|
||||||
|
|
||||||
|
// mainteant qu’on 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 n’est certaiment pas le plus optimal et ce n’est peut-être pas un modèle à suivre. Je suis certain par exemple qu’une fonction comme `join_all` doit poser des problèmes dans certains cas, ou ne peut pas accepter plus d’un certain nombre de tâches, etc… Néanmoins, ça démontre bien un truc : faire de l’asynchrone, c’est bien joli, mais à moins d’avoir un cas assez spécifique ou de savoir ce que l’on 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 m’intéresse beaucoup d’avoir de l’asynchrone (vraiment) mais les cas où je vais réellement en avoir besoin et réellement m’en servir, ce ne sera clairement pas tous les jours, surtout vu la complexité du truc.
|
||||||
|
|
||||||
|
Ah et dernière chose : quand je dis **bloquant**, c’est vraiment **bloquant**. Oui, je te regarde `reqwest::blocking` qui en fait planque du `tokio` dedans…
|
||||||
|
|
@@ -0,0 +1,153 @@
|
|||||||
|
+++
|
||||||
|
title = "Ansible et la génération magique de variables"
|
||||||
|
date = 2022-09-08
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "ansible", "système" ]
|
||||||
|
+++
|
||||||
|
> Ansible, c’est de la merde.
|
||||||
|
|
||||||
|
*Schtroumpf grognon, 2022*
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Je n’aime pas _Ansible_. Venant à l’origine 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 à n’importe quelle variable est hyper confus, il n’y a pas de système de signaux permettant d’échanger avec le serveur.
|
||||||
|
|
||||||
|
Sans compter le fait que même l’exécution des modules de base inclus dans _Ansible_ nécessite parfois l’installation de la moitié de _pypi_.
|
||||||
|
|
||||||
|
Mais il a néamoins deux ou trois trucs pour lui : pas d’agent (j’ai du mal à classer ça dans les avantages, mais dans certaines circonstances, ne pas avoir à se préoccuper de l’agent, c’est plutôt une bonne chose) et surtout une courbe d’apprentissage nettement moins raide que _SaltStack_ (où tu rotes du sang régulièrement…).
|
||||||
|
|
||||||
|
Bref, laisse-moi te conter la mésaventure qui m’est arrivé au détour d’un _role_ _Ansible_…
|
||||||
|
|
||||||
|
# Les données du problème
|
||||||
|
|
||||||
|
Admettons que j’ai un _role_ dont le but est de tester le retour d’un _GET_ HTTP. On lui done une liste avec pour chaque entrée :
|
||||||
|
* l’URI
|
||||||
|
* l’URN
|
||||||
|
* le résultat attendu
|
||||||
|
|
||||||
|
Et ce _role_ se charge d’aller 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 d’un `assert`.
|
||||||
|
|
||||||
|
Jusqu’ici tout va bien. Mais admettons que je doive générer cette liste. Elle n’est plus bêtement statique, je dois la générer. Mettons par exemple que je veuille vérifier que l’ensemble des serveurs dans un groupe donné sont bien enregistrés dans un `consul` ou un système de gestions de parc ou n’importe quoi d’autres.
|
||||||
|
|
||||||
|
# This is *SALTSTACK*!!
|
||||||
|
|
||||||
|
Côté _SaltStack_, il n’y 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 n’importe 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 qu’il 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 d’avoir du _Jinja_ à tous les étages, c’est qu’on peut finalement _templater_ un peu n’importe quoi, un peu n’importe où sans trop se préoccuper. C’est ainsi qu’il n’existe 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_ n’importe où. L’ensemble des fichiers que manipulent _Ansible_ (à l’exception du `ansible.cfg` et des fichiers d’inventaire qui peuvent être en _INI_), tout doit être du pur _Yaml_.
|
||||||
|
|
||||||
|
D’où un certain nombre d’aberrations genre `loop` et la tétrachiée de `filters` qu’il faut se trimballer pour arriver à l’utiliser (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 qu’on 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 d’afficher 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 l’ordre, on se rend pourtant bien compte qu’on a effectivement généré ce qu’il faut, mais visiblement, _Ansible_ n’a pas vraiment envie de s’en servir.
|
||||||
|
|
||||||
|
En vrai, il y a un astuce… Il ne faut pas _templater_ du _Yaml_, il faut _templater_ du _JSON_. Oui, c’est 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 s’accomplit :
|
||||||
|
|
||||||
|
```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, c’est tellement bordélique qu’on peut quand même se poser des questions de pourquoi, c’est foutu comme ça. Générer une variable _Yaml_ en passant par du _JSON_ _templaté_ en _Jinja_, c’est pas vraiment le truc le plus instinctif de la planète…
|
||||||
|
|
74
content/2022-10-27-Réparer,-ou-jeter,-que-faire.md
Normal 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, j’espè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). J’essaie 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 ain’t broken, don’t fix it*
|
||||||
|
|
||||||
|
Tout d’abord, le premier critère et certainement le plus important pour le moment : **s’il fonctionne et qu’il remplit correctement sa fonction, il n’y a aucune raison de remplacer un appareil**.
|
||||||
|
|
||||||
|
Voilà, c’est dit, c’est simple. Si ça marche, absolument aucune raison de changer un appareil. Ça a l’air 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 l’appareil est polluant ou particulièrement inefficace énergétiquement à l’utilisation. Je donnerai un exemple plus bas, mais c’est aussi pour illustrer le fait que rien n’est 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 d’arriver à établir un diagnostic : qu’est-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 d’informatique, le simple fait d’avoir ton lave-linge qui ne vidange plus, c’est 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 n’est simple et rien n’est évident.
|
||||||
|
|
||||||
|
L’espoir que j’ai dans ce domaine, c’est que finalement les pannes sont aujourd’hui de mieux en mieux documentées et beaucoup de sites web permettent d’obtenir des diagnostics ou de les poser relativement facilement. Malheureusement, il y a pas mal de vendeurs d’huile de serpent qui profitent aussi de cela pour être en tête des réponses des moteurs de recherche avec dans l’idée beaucoup un service à vendre qu’un réel bénéfice pour le consommateurs/utilisateurs.
|
||||||
|
|
||||||
|
Dans la capacité à diagnostiquer, il faut aussi inclure, et ce n’est pas simple non plus, l’outillage nécessaire. Autant certains diagnostics sont finalement assez simple à réaliser (« est-ce que la pompe vidange ? ») autant d’autres sont nettement plus compliqués (« Y a-t-il continuité entre le composant X et Y ? »). Pareil, c’est relativement crétin, mais avoir une caisse à outil avec ce qu’il 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 n’est pas forcément donné à tout le monde.
|
||||||
|
|
||||||
|
C’est un effort d’équipements, qui est d’autant plus difficile à faire valoir, qu’il ne servira qu’en 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 l’ai acheté. Alors, certes, c’est pas cher, mais c’est 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 ? C’est 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 d’un petit fer à souder. Ça ne coûte pratiquement rien et vous en aurez toujours l’utiliser 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 l’outillage, il y a évidemment le souci des pièces. De ce point de vue, on peut remercier (sans ironie) l’Europe 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. C’est un pas dans le bon sens, mais malheureusement c’est assez insuffisant : ça n’est 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 n’est donc toujours pas parfait, mais on n’a plus vraiment d’excuses 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 qu’aucun appareil un tant soit peu bien construit ne connaît de vraies pannes dans ses 5 premières années d’utilisation : l’usure 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 d’une manière ou d’une autre).
|
||||||
|
|
||||||
|
Bref, concernant le fait de réparer ou maintenir en bonne condition son appareil, on progresse. Encore une fois, rien n’est parfait, rien n’est définitif, rien n’est évident, rien n’est gagné. Mais on avance progressivement dans le bon sens.
|
||||||
|
|
||||||
|
**Pour moi, c’est aussi à cet instant qu’entrent en jeu les équations économiques à prendre en compte.** La réparation ne devrait jamais coûter plus cher que l’appareil neuf. Malheureusement, on sait que ce n’est pas toujours le cas.
|
||||||
|
|
||||||
|
Et il y a aussi un autre facteur à prendre en compte : **l’appareil en panne connaît-il en ce moment une rupture technologique qui permettrait de faire des économies énergétiques conséquentes ou d’améliorer de manière conséquente l’efficacité ou les performances de l’appareil ?**
|
||||||
|
|
||||||
|
Mettons que ton frigo tombe en panne. C’est 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 qu’il vaut mieux changer l’appareil 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 l’appareil neuf. Ça n’en vaut à mon sens plus la peine.
|
||||||
|
|
||||||
|
# Conclusage
|
||||||
|
|
||||||
|
Y’a encore du boulot ! Mais y’a de l’espoir 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 qu’il va falloir mieux traiter les appareils dysfonctionnels dans le temps. Qu’un lave-linge soit déclaré économiquement irréparable, je le conçois parfaitement. Est-ce une raison pour jeter l’ensemble ? Clairement, non. Il y a probablement encore de bonnes pièces dedans, qu’il 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 l’effort de ce point de vue. J’aurais tendance à dire les industriels : ils connaissent mieux les appareils qu’ils 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 l’ai dit, en ce qui me concerne, c’est 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 s’est faite emboutir. Jusqu’à présent, je l’avais 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). J’ai décidé d’acheter un véhicule électrique d’occasion. Je ne suis pas un chantre de la voiture électrique (ça va être indispensable pour les transports du futur, mais ce n’est clairement pas LA solution pour le moment), mais c’est une rupture technologique majeure qui permet d’économiser de l’énergie et du CO₂.
|
||||||
|
|
||||||
|
C’est pour moi l’illustration quasi-parfaite de ce que je disais : si ce n’est pas cassé, on ne change pas (ou alors on vend éventuellement, mais surtout on ne jète pas) ; si c’est cassé, on répare, le plus possible ; si c’est cassé et qu’il y a une technologie bien meilleure disponible (pas plus performante, pas plus confortable, meilleure au sens énergétique et pollution), il faut remplacer.
|
161
content/2022-11-28-Ansible-et-la-hiérarchie-des-groupes.md
Normal 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 c’est pas pareil.
|
||||||
|
|
||||||
|
Le bon programmeur, apparemment…
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Je suis récemment tombé sur une petite particularité (certains diront un *quirk*) d’Ansible qui m’a paru suffisamment intéressant pour tenter de l’expliquer, ou au moins d’en expliquer ma compréhension.
|
||||||
|
|
||||||
|
# Et là, la marmotte d’Ansible, elle met le chocolat de l’hé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 d’information. 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 l’hôte, comment c’est perçu ?
|
||||||
|
|
||||||
|
Tu vas pouvoir constater que la réponse n’est pas si simple que ça.
|
||||||
|
|
||||||
|
# Vu de l’hôte, les groupes, on s’en tape
|
||||||
|
|
||||||
|
Du point de vue l’hôte (donc la machine sur laquelle se connecte le contrôleur Ansible), les groupes n’ont que peu d’importance : ils sont vus ni plus ni moins que comme des tags et rien de plus. D’ailleurs, à 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 l’hô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 l’ensemble des machines dans le groupe Y
|
||||||
|
* filtrer dans un template Jinja2
|
||||||
|
|
||||||
|
Mais le fait est : du point de vue de l’hôte, c’est une inforamtion plate et absolument pas hiérarchisée.
|
||||||
|
|
||||||
|
**Donc si quelqu’un vous dit que les groupes Ansible sont juste des tags**, il a basiquement raison, mais seulement du point de vue de l’hôte. Le contrôlleur, c’est une toute autre histoire…
|
||||||
|
|
||||||
|
# Vu du contrôleur, les groupes, c’est la vie
|
||||||
|
|
||||||
|
Du point de vue de contrôleur en revanche, les groupes ont une très grande importance : c’est 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 l’inventaire (et seulement de l’inventaire, je vous rappelle qu’il y a d’autres priorités dans Ansible, en particulier concernant les variables d’hôtes, les `group_vars` des playbooks, j’en 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 l’inventaire, 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 n’y a qu’une petite exception à cette règle, qui peut s’avé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 l’ordre 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 c’est 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, s’il 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, c’est si des serveurs sont définis en même temps sur plusieurs niveaux. Dans ce cas, on revient au cas initiale (résolution dans l’ordre alphabéique). On peut le démontrer assez facilement dans notre cas en ajoutant l’hô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 d’Ansible, `borg_client` est alphabétiquement avant `borgbackup`. D’ailleurs, `borg_server` étant juste après `borg_client` mais avant `borgbackup`, si j’ajoute cette variable dans `group_vars/borg_server.yml`, elle surcharge bien celle de `group_vars/borg_client.yml`, on constate bien que c’est 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 quelqu’un 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
|
||||||
|
|
||||||
|
C’est un beau bordel mine de rien ces histoires de variables Ansible. Déjà que la précédence n’est pas toujours simple (je vous mets au défi de me dire de tête qui est plus prioritaire entre une variable d’inventaire, 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, j’espère que ça t’aura permis de mieux comprendre la chose et de ne pas te faire surprendre. Et surtout, on n’oublie 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.
|
@@ -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 d’UTF-16… attendez, je crois qu’on l’a déjà faite celle-là
|
||||||
|
|
||||||
|
Mon cerveau à deux heures du mat’
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Je suis tombé récemment sur une bizarrerie tellement étrange qu’il m’a paru intéressant de tenter de la documenter (j’ai 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 l’objectif 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* à l’opposé du *name* qui est le truc au bout de l’URL 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 t’expliquerais pourquoi plus loin) alors que sur Mastodon, le *display name* est limité à 30 caractères. C’est comme ça sur l’API et c’est comme ça sur le formulaire permettant de le changer.
|
||||||
|
|
||||||
|
Il va donc falloir couper une chaîne de caractères…
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Première approche naïve…
|
||||||
|
|
||||||
|
Prenons une chaîne de caractères « normales » (pas de caractères chelous, que de l’ASCII tout ce qu’il 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`, c’est un peu la merde, on est bien d’accord ?) si par hasard on coupe au milieu d’un caractère. Oui, parce que comme Rust fait de l’UTF-8 partout, tout le temps, certains caractères sont sur plusieurs octets et en fait `truncate` ne coupe pas au caractère mais coupe à l’octet… et panique. Essayons de le faire paniquer, tu vas voir que ce n’est 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 lorsqu’il s’agit d’aller chercher des fonctions très chelous permettant de faire des trucs parfois très tordus. Nous allons voir ce qu’il 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 à l’octet. 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 s’arrê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, c’est 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 c’est quand même un poil plus propre à la lecture et surtout, on évite d’avoir un `15` qui se balade dans le code sans qu’on puisse voir directement ce qu’il fait (et si t’aimes pas l’opérateur *turbofish*, tu peux toujours préciser le type de variable au début, c’est un peu comme tu le sens).
|
||||||
|
|
||||||
|
# Bon ben ça marche, pourquoi tu nous casses les noix alors ?
|
||||||
|
|
||||||
|
Alors, d’abord, 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, l’objectif final, c’est de faire manger cette chaîne de caractères à Mastodon, via l’API, mais en réalité, on peut aussi le voir directement sur l’interface 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 qu’il est limité à 30 caractères, c’est 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, c’est quoi la taille d’une chaîne de caractères ? Et bien, c’est 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 n’a 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), c’est pas forcément le cas pour **tous** les caractères et ça dépend aussi de l’encodage. 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, c’est « 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. C’est parce que c’est deux caractères sont côte à côte qu’un navigateur va l’afficher comme un drapeau français. Sinon, il affichera simplement l’identifiant correspondant.
|
||||||
|
|
||||||
|
# `longueur_max = toto`
|
||||||
|
|
||||||
|
Et là, normalement, tu te dis que c’est bon, que tout va bien, que tu vas arriver à quelque chose… mais est-ce que les formulaires Web font de l’UTF-8 ? L’attribut `maxlength` représente-t-il vraiment le nombre de caractères ?
|
||||||
|
|
||||||
|
Allons faire le test pour vérifier, équipé de notre vaillant 🇫🇷 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Donc, c’est supposément 30 caractères, on a vu qu’en 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. Qu’est-ce que la baise ?
|
||||||
|
|
||||||
|
Et bien, en fait, les formulaires Web [encodent en utilisant de l’UTF-16 et non de l’UTF-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 d’un 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 qu’elle n’y arrive pas.
|
||||||
|
|
||||||
|
# Bon ben voilà, c’était pas si compliqué que ça !
|
||||||
|
|
||||||
|
/ragequit
|
138
content/2022-12-09-Rust-:-passer-de-clapv2-à-clapv4.md
Normal 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, t’as zappé le 3 ?
|
||||||
|
>
|
||||||
|
> − Ta gueule…
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Récemment, je me suis rendu compte que certains des logiciels que j’avais 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, j’ai complètement zappé la v3, parce qu’elle 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 d’avoir un moyen « programmatique » d’appeler la librairie.
|
||||||
|
|
||||||
|
De ce point de vue, la v4 correspond bien plus à ce que j’attends d’un programme de ce type : le choix entre macros ou pas, la versatilité des options, les cas tordus qu’on n’a 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, c’est d’invoquer 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 l’on doit invoquer à l’import de `clap`.
|
||||||
|
|
||||||
|
`App` n’existe plus du tout, même si ce n’est pas la première erreur que tu vas rencontrer a priori.
|
||||||
|
|
||||||
|
# `Arg::with_name` n’existe plus
|
||||||
|
|
||||||
|
Autre chose dont il va falloir se débarasser, `Arg::with_name` n’existe 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()` n’existe plus non plus
|
||||||
|
|
||||||
|
En fait `takes_value()` avait un peu un statut à la con à la l’origine : il permettait de spécifier qu’un argument devait prendre une valeur, mais il fallait une seconde fonction pour préciser le nombre de valeurs s’il y en avait plus d’une. C’est maintenant régler sur clapv4 où l’on 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 l’invoquer avec la valeur `1` pour retrouver le comportement d’origine et l’on 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 qu’on a beaucoup de mal à faire comprendre au départ à des gens qui font du PHP ou du Python d’ailleurs…).
|
||||||
|
|
||||||
|
# Dites aussi au revoir à `value_of()`
|
||||||
|
|
||||||
|
Bon alors là, je ne peux qu’applaudir le progrès aussi. Au lieu d’utiliser `value_of()` et de décapsuler l’éventuel résultat, il va maintenant s’agir d’utiliser [`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 l’on souhaite. La (très) bonne nouvelle, c’est 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 qu’on 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 n’aimez pas l’opé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 qu’il faudra de temps en temps se servir de l’opé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 à l’instantiation, 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 d’esprit, vous pouvez maintenant préciser une valeur par défaut (toujours sous la forme d’une 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 l’argument pris.
|
||||||
|
|
||||||
|
C’est, encore une fois, un changement qui va dans le bon sens, d’autant plus qu’il est repris dans l’aide de la commande qu’on 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 quoiqu’il arrive
|
||||||
|
```
|
||||||
|
|
||||||
|
# Sous-commandes
|
||||||
|
|
||||||
|
Et pour finir dans ce petit tour de clapv4, les `subcommand` (qui exigeaint à l’origine leur propres structures) sont maintenant simplement des [`Command::new()`](https://docs.rs/clap/4.0.29/clap/builder/struct.Command.html#method.new). Il n’y a donc pas besoin d’avoir une commande spécifique pour les sous-commandes, on peut directement faire des commandes dans des commandes, en continuant d’utiliser [`subcommand`](https://docs.rs/clap/4.0.29/clap/builder/struct.Command.html#method.subcommand).
|
||||||
|
|
||||||
|
Seul petit piège, [`subcommand` (celui du matches, pas l’autre)](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. C’est un changement assez minime dans l’absolu, mais ça va forcément foutre en l’air ta syntaxe d’origine, c’est balot…
|
||||||
|
|
||||||
|
# Conclusation
|
||||||
|
|
||||||
|
Voilà, c’était un petit retour sur le passage de v2 à v4 de `clap`. `clap`, c’est vraiment la librairie un peu indispensable dès qu’on veut mettre des options dans un programme Rust. Essayer de s’en passer, c’est un peu comme tenter de faire le Tour de France avec les petites roues en ayant mangé une choucroute avant de se lancer : y’a moyen que tu te gamèles au premier virage avec pertes et fracas.
|
||||||
|
|
||||||
|
`clap`, c’est bon, mangez-en !
|
@@ -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 l’appeler `systemd`. Je suis sûr que tout va bien se passer.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Alors, je t’arrête tout de suite, je n’ai pas d’allergie chronique à `systemd`. Dans l’absolu, je trouve même que c’est pas si mal foutu que cela (et on aura l’occasion d’y revenir un peu plus tard). Alors, oui, il y a aussi des trucs à la con (et pas qu’un peu d’ailleurs) 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 d’expé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 :
|
||||||
|
* l’initialisation de l’interface de bridge qui va permettre aux conteneurs d’accéder au monde extérieur (et d’être accédé aussi)
|
||||||
|
* le démarrage d’un `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, c’est 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 n’importe où évidemment. Cela permet d’indiquer à `lxc-net` que l’on souhaite que la résolution du service `dnsmasq` se fasse en `lxc.buttse.cx.local` (tu peux évidemment mettre n’importe quoi, il n’y 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 l’interface de bridge :
|
||||||
|
```
|
||||||
|
> dig +short @100.64.4.1 youpi.lxc.buttse.cx.local
|
||||||
|
100.64.4.94
|
||||||
|
```
|
||||||
|
|
||||||
|
*`100.64.4.1` étant l’adresse de mon bridge LXC*
|
||||||
|
|
||||||
|
Voilà, tout ça, c’est 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`, c’est un peu le bordel par contre. Personnellement, étant sous Archlinux, j’utilise `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 m’en 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 qu’on peut (et c’est presque pas tordu, tu vas voir). En fait, avec l’indicateur `DNS=` dans `/etc/systemd/resolved.conf`, on peut préciser plein de trucs : à quel serveur DNS s’adresser, 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 n’empêche d’en ajouter d’autres, 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 d’autres. Tu pourrais d’ailleurs très bien imaginer en ajouter d’autres pour KVM, Docker, etc…
|
||||||
|
|
||||||
|
Voilà, c’est gratuit, c’est pour moi.
|
@@ -0,0 +1,242 @@
|
|||||||
|
+++
|
||||||
|
title = "Garage : le petit serveur S3 qu’il est bien pour s’en servir"
|
||||||
|
date = 2023-01-02
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système", "s3", "garage", "stockage" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|
> Effectivement, ça sent un peu le cambouis
|
||||||
|
|
||||||
|
*Michel, utilisateur de garage*
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# C’est 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 qu’on *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 t’y intéresser ? Parce qu’il y a tout un tas de logiciels qui l’utilisent pour faire du stockage de fichiers et que ça peut du coup être bien pratique d’en avoir à disposition. On pensera, entre autres, à NextCloud, Peertube, Mastodon, Hugo, Restic, etc…
|
||||||
|
|
||||||
|
En plus, comme c’est orienté Web, il est aussi possible de l’utiliser 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 d’autres. 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.
|
||||||
|
|
||||||
|
C’est là qu’intervient [`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 l’ordre de 200 ms au max), ce qui n’est généralement pas le cas de ces concurrents.
|
||||||
|
|
||||||
|
Et en plus, c’est écrit en Rust ❤️
|
||||||
|
|
||||||
|
# Bon OK, comment qu’on 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 j’ai appelé `garage{1..5}` et `garagegw1`), dans 3 zones différentes dont un nœud passerelle (j’expliquerai le pourquoi de la passerelle plus tard). Il faut savoir que `garage` est conçu pour assurer lui même l’intégrité et la redondance des données, il n’est donc généralement pas nécessaire d’avoir 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).
|
||||||
|
|
||||||
|
J’ai donc monté 6 containers LXC pour ce faire, histoire de me simplifier la vie. Première chose, il faut aller télécharger l’exécutable `garage` sur l’ensemble 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 s’agit d’un exécutable statique compilé avec `musl`, il est donc énorme, mais a l’avantage de tourner n’importe où sans autre prérequis).
|
||||||
|
|
||||||
|
Petite précision : `garage` étant un exécutable sans aucune dépendance, j’ai choisi de le faire tourner directement sur les containers, mais on pouvait tout aussi bien le faire tourner dans un container Docker (si besoin, l’image officielle est `dxflrs/garage`).
|
||||||
|
|
||||||
|
Bref, maintenant qu’on 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 d’UID aléatoire à chaque fois (c’est `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" # c’est 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 l’on souhaite.
|
||||||
|
|
||||||
|
compression_level = 2 # le degré de compression zstd, de 1 à 19, plus c’est élevé, plus c’est compressé
|
||||||
|
# plus ça consomme de ressources
|
||||||
|
|
||||||
|
rpc_bind_addr = "[::]:3901" # l’adresse par défaut pour le protocole RPC de garage
|
||||||
|
rpc_public_addr = "garage1.lxc.arpa:3901" # son nom public
|
||||||
|
# ça, c’est 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, c’est pour accéder à l’API 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 l’API 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 t’interconnecter 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 l’interconnection :
|
||||||
|
```bash
|
||||||
|
# garage node connect 93506740b859618e6b5953092485b7717d186b3b15b61d9c59e20f685ee3122f@garage2:3901
|
||||||
|
Success.
|
||||||
|
```
|
||||||
|
|
||||||
|
Et ainsi de suite pour l’ensemble 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 l’occurence faire).
|
||||||
|
|
||||||
|
## Les zones
|
||||||
|
|
||||||
|
La zone ne donne, en soi, aucune indication particulière : c’est 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 d’accéder aux données via S3. Imaginons que tu ne souhaites pas exposer directemet l’API S3 sur Internet mais tu ais besoin d’un 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 s’entendent au sens disque, mais en **relatif** d’un nœud à l’autre. 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 n’indique 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 d’un 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 qu’il y a chez toi), `mere-grand` (qui sera le serveur que t’as 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 d’avoir 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 l’appliquer 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 c’est le cas actuellement, les conséquences seront moindres, autant sur un cluster rempli ras la gueule, ça risque d’être bien plus compliqué.
|
||||||
|
|
||||||
|
# J’fais 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 d’associer 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 qu’on 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 n’importe 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 c’est vraiment résistant aux pannes ?
|
||||||
|
|
||||||
|
Et ben, on va essayer !?! Éteignons le nœud 1 et voyons ce qu’il 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 d’une même zone et continuer d’accéder aux données. Par contre, s’il y a deux pannes dans deux zones différentes (même d’un 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 n’auras pas de souci pour accéder aux données (c’est ce que signifie le `replication_mode="3"`, 3 répliques sur l’ensemble 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 d’un 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 l’utiliser comme moteur de stockage pour Nextcloud.
|
225
content/2023-01-15-Migrer-Nextcloud-dun-stockage-local-a-S3.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
+++
|
||||||
|
title = "Migrer Nextcloud d’un 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 l’ai jamais testée sur des instances de grande ampleur (plusieurs dizaines, voire centaines de gigaoctets, avec des partages dans tous les sens). Voilà, t’es prévenu, prends tes précautions.
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
L’idé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, quoiqu’il arrive :
|
||||||
|
* **Éjecte tous tes utilisateurs de l’instance**. Tous. Mémé, son chien, ton petit frère, faut virer tout le monde. C’est pas des blagues hein, tu le fais, je t’attends (Note : le plus simple est d’activer 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à, c’est pareil, vas-y, je t’attends. 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 d’une 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 d’optimisations, 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
|
||||||
|
|
||||||
|
C’est 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 s’inté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 l’instance elle-même).
|
||||||
|
|
||||||
|
La première chose à vérifier est assez simple : **il faut impérativement s’assurer qu’aucune entrée dans `oc_filecache` ne fasse référence un stockage inexistant dans `oc_storages`**. Les deux tables sont liés par l’attribut `oc_filecache.storage = oc_storages.numeric_id`.
|
||||||
|
|
||||||
|
On peut facilement vérifier le nombre d’entré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 n’est pas le cas, **vérifie qu’il n’y 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 c’est une mise à jour qui s’est 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 n’ai pas creusé, un truc de plus à faire si besoin…*
|
||||||
|
|
||||||
|
Toujours dans le même état d’esprit, cela peut être intéressant de purger les corbeilles et les versions de fichiers (ça fera toujours ça de moins à transférer). À noter qu’il 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 s’agit bien du répertoire de données ; par défaut, il s’agit du répertoire `data` à la racine de l’installation, mais techniquement, ça peut être n’importe où.
|
||||||
|
|
||||||
|
Logiquement, tu devrais obtenir un fichier dont les lignes ressemblent à ça(c’est, 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 d’ordinaire 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
|
||||||
|
|
||||||
|
L’idé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 n’est 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 n’a merdé, c’est 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 l’ensemble :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
du -Lhsc *
|
||||||
|
```
|
||||||
|
|
||||||
|
Si tu es confort avec les chiffres que tu vois (qu’au 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, j’ai 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 l’installes (via le gestionnaire de paquets ou depuis `pip`), l’important c’est qu’il soit configuré correctement : utilise la même clé et le même secret dans `~/.aws/credentials` et n’oublie pas de mettre la région `garage` dans `~/.aws/config`. Logiquement, c’est tout ce dont tu auras besoin.
|
||||||
|
|
||||||
|
On se place ensuite dans le répertoire `s3_files` contenant l’ensemble des liens symboliques et on s’arme d’une 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 qu’il 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 qu’il ne se passe plus rien, c’est 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 qu’un seul `local::*` dans cette table. Si ce n’est pas le cas, c’est peut-être qu’un truc est resté allumé malgré la maintenance ou qu’il s’est passé autre chose. Quoiqu’il 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 d’entré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 d’habitude avec les caractères d’échappement de type `\`, se méfier du résultat).
|
||||||
|
|
||||||
|
# Dernière étape : configurer Nextcloud et vérifier l’ensemble
|
||||||
|
|
||||||
|
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 l’objet `$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 l’interface Web et vérifier que tout fonctionne correctement. Mon conseil : passer dans quelques partages, ouvrir des fichiers que tu n’a 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 d’origine et virer le répertoire contenant les liens symboliques.
|
||||||
|
|
||||||
|
# Conclusation
|
||||||
|
|
||||||
|
Et ben, on va pouvoir prendre des vacances bien méritées avec ça…
|
179
content/2023-02-05-Passer-une-borne-Ubiquiti-sous-OpenWRT.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
+++
|
||||||
|
title = "Passer une borne Ubiquiti sous OpenWRT"
|
||||||
|
date = 2023-02-05
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système", "réseau", "openwrt", "ubiquiti" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# Ce qu’UniFi é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, d’utiliser des fonctionnalités avancées. En plus, il ne bloquait pas ses utilisateurs dans l’écosystème, leur permettait d’installer ce logiciel sur une Debian ou une Ubuntu, était relativement peu cher à l’achat, ne nécessitait pas d’abonnement.
|
||||||
|
|
||||||
|
Bref, ce logiciel était bien et la vie était belle. Et ce logiciel s’appelait *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 d’utiliser 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 d’offrir 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* (j’ai une UAP-AC-Lite, mais je suppose que c’est à peu près pareil pour les autres modèles), tu pourras constater que c’est déjà une *OpenWRT* (!). En fait, pour être précis, il s’agit d’une *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 l’ensemble des bornes. Il suffit ensuite de s’y connecter en SSH.
|
||||||
|
|
||||||
|
Ce qui est intéressant avec cette borne, c’est qu’elle é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 t’y référer.](https://openwrt.org/toh/ubiquiti/unifiac) J’ai 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 d’aller télécharger le bon et le forcer via la console *UniFi*.
|
||||||
|
|
||||||
|
Une fois, la borne redémarrée, on peut de nouveau s’y 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)
|
||||||
|
* [l’utilitaire `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. C’est à peu près normal, rien d’inquiétant. Une fois qu’on 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 qu’il faut pour *flasher* la borne, il suffit de s’y 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 c’est du stockage avec plusieurs entrée de démarrage, façon OPNSense), et effacer le `kernel1`. Il suffit alors d’indiquer à `mtd` que l’on 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 d’une grosse minute, la borne doit redémarrer avec l’adresse par défaut `192.168.1.1/24` et devrait être joignable sur le réseau local.
|
||||||
|
|
||||||
|
Première chose à faire : dans LuCI (l’interface 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 t’y 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 c’est en gros terminé. Tu peux recréer ton SSID et c’est réglé. Tu remarqueras qu’il y a deux interfaces `radio0` et `radio1` pour le Wi-Fi (une antenne fait du 2,4Ghz, une autre du 5Ghz). C’est 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 t’invite à continuer de lire.
|
||||||
|
|
||||||
|
# Un poil plus compliqué
|
||||||
|
|
||||||
|
Originellement, j’avais 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). L’idée était d’avoir la borne isolée sur le réseau où se trouvait le serveur *UniFi* d’un côté, mais d’avoir mon VLAN domestique et mon VLAN IoT disponibles depuis la borne de l’autre.
|
||||||
|
|
||||||
|
D’où 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 l’interface Web (parce qu’il 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 t’empêche d’en remettre après si tu le souhaites, mais pour un point d’accès Wi-Fi débile, je pense que c’est 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 l’interface de la borne), je te recommande de mettre le VLAN 1 et les options *Egress untagged* **et** *Primary VLAN ID*. J’ai essayé d’autres 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 (j’en doute…).
|
||||||
|
|
||||||
|
Bref, avec cette configuration, tu devrais avoir accès à des VLANs depuis le *bridge*. **Surtout n’applique 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 à l’heure. Au moment de créer ces *devices*, tu vas voir un menu déroulant dans *Existing device* et pourvoir choisir *Software VLAN: "br-lan.1" (lan)*, c’est celui qu’il 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 n’est probablement pas possible).
|
||||||
|
|
||||||
|
Une fois que tout cela est fait, retourne dans *Network » Interfaces* et associe l’interface *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 n’y a rien d’autres à 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 d’un coup et sans se louper (donc c’est 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 qu’avant. 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
|
||||||
|
```
|
165
content/2023-04-18-Configurer-un-relais-SMTP-(proprement).md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
+++
|
||||||
|
title = "Configurer un relais SMTP (proprement)"
|
||||||
|
date = 2023-04-18
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système", "smtp", "tem" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|
> Ah oué, c’est tant si simple que ça en fait…
|
||||||
|
|
||||||
|
*Michel, désespéré…*
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Coucou mes p’tits Rondoudous, ça faisait un moment.
|
||||||
|
|
||||||
|
Il m’est 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 qu’il faut faire pour gérer des cas un peu tordus.
|
||||||
|
|
||||||
|
L’idée est donc de configurer un relais SMTP d’abord simple avec un serveur SMTP que tu maîtrises à 100% et qui est dans le même réseau que toi, puis de s’attaquer à un relais SMTP un peu plus compliqué, passant par un TEM (_**T**ransactional **E**-**M**ail_). En l’occurence, c’est celui de *Scaleway* mais tous ont un fonctionnement remarquablement similaire, donc ça devrait pouvoir s’appliquer à tous.
|
||||||
|
|
||||||
|
Et comme c’est un peu le standard, on va faire tout ça avec du *Postfix*, je suppose qu’on peut aussi avoir des configurations similaires avec d’autres serveur SMTP, mais c’est ce que j’ai sous la main (et c’est fort probable que toi aussi).
|
||||||
|
|
||||||
|
# Commençons par le pourquoi
|
||||||
|
|
||||||
|
D’abord, 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, c’est 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 l’exemple 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 s’appuyer sur le SMTP du relais plutôt que sur d’obscures configurations hétérogènes par toujours facile à manipuler.
|
||||||
|
|
||||||
|
Alors évidemment, si tu n’as ni *cron*, ni *NUT*, ni quoique ce soit qui serait susceptible d’envoyer des courriels depuis ton serveur, tu peux parfaitement t’en passer. Mais l’expérience montre que ça arrive bien plus souvent qu’on ne le souhaiterait.
|
||||||
|
|
||||||
|
# Relais SMTP avec _Submission_
|
||||||
|
|
||||||
|
Un des trucs importants à comprendre dans *Postfix*, c’est qu’il 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 s’arrange pour le traiter correctement (le transmettre à un _**M**ail **D**elivery **A**gent_ par exemple) et dans d’autres 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 n’empê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, c’est 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 d’origine du message,
|
||||||
|
# sinon, ça risque de poser pas mal de souci
|
||||||
|
|
||||||
|
# ça c’est les trucs génériques de Postfix
|
||||||
|
alias_maps = hash:/etc/aliases
|
||||||
|
alias_database = hash:/etc/aliases
|
||||||
|
# la destination, c’est important : c’est un relais, donc il n’est pas
|
||||||
|
# supposé recevoir de messages autrement que pour les utilisateurs
|
||||||
|
# locaux
|
||||||
|
mydestination = $myhostname, localhost.$mydomain, localhost
|
||||||
|
|
||||||
|
# c’est 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 qu’en 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 l’utilisateur/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 s’appuyer 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). L’idée, c’est donc d’utiliser le port *SMTPS* avec *Postfix*.
|
||||||
|
|
||||||
|
Sauf que l’option `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 d’habitude. Et cette ruse, elle n’est pas si compliquée que ça, elle s’appelle `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 l’on démarre le service `stunnel4`, on voit effectivement qu’il écoute sur le port 10465. Il suffit alors de reconfigurer notre petit *Postfix* comme suit pour s’adapter :
|
||||||
|
|
||||||
|
```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 l’adresse cible
|
||||||
|
|
||||||
|
Quand il s’agit de *cron*, le service a tendance à envoyer les messages à l’utilisateur local concerné. Donc si le *cron* `www-data`, qui gère les services Web, se vautre complètement, il enverra un mail à `www-data`, l’utilisateur local. C’est super intéressant, mais en l’occurence, on préfèrerait largement qu’il l’envoie à un autre destinataire, j’ai nommé l’administrateur du système.
|
||||||
|
|
||||||
|
Pour cela, on peut forcer *Postfix* à faire une réécriture de tous les messages de destination pour les envoyer ailleurs. Ce n’est pas bien compliqué, il suffit d’ajouter 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 n’empêche d’aller mettre d’autres choses dans ce fichier. Là, tout sera réécrit vers `admin@buttse.cx`, ce n’est pas forcément le comportement souhaité _in fine_.
|
||||||
|
|
||||||
|
# Bonus 2 : configurer Postfix pour réécrire les adresses sources
|
||||||
|
|
||||||
|
_Normalement™_, le simple fait d’avoir 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 n’est 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 s’il n’y a pas correspondance entre ces deux entêtes dans le courriel. C’est en réalité parfaitement logique, l’objectif é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` (c’est 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 n’y a plus d’excuses pour ne pas mettre un relais SMTP tout propre sur tes serveurs histoire d’avoir une idée de ce qui se passe sur le système.
|
260
content/2023-05-07-Grapher-les-seaux-Garage-dans-munin.md
Normal 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, c’est là qu’elle a triché la fille…
|
||||||
|
|
||||||
|
*Christophe, regardant ses magnifiques graphes Munin*
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Munin, c’est loin d’être parfait, mais ça a un avantage non négligeable : c’est 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`, c’est bien, mangez-en) histoire d’avoir des infos que je considère comme essentielles : quantité de données par *bucket*, nombre d’objets 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 l’ensemble 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 d’autres éléments assez communs dans les Systèmes d’Information 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 l’activer, rien de plus simple, dans `/etc/garage.toml`, il suffit d’ajouter deux lignes pour activer l’API `admin` :
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[admin]
|
||||||
|
api_bind_addr = "[::1]:3903"
|
||||||
|
```
|
||||||
|
|
||||||
|
On redémarre `garage` et on peut alors accéder directement aux métriques via l’URL [http://[::1]:3903/metrics](http://[::1]:3903/metrics).
|
||||||
|
|
||||||
|
Alors, c’est 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 qu’il faut suivre en priorité, et les éventuels soucis qu’ils peuvent représenter.
|
||||||
|
|
||||||
|
Sauf que c’est pas du tout ce qu’on veut, on veut simplement grapher l’usage.
|
||||||
|
|
||||||
|
# Le code source de Garage ?
|
||||||
|
|
||||||
|
`garage` est écrit en Rust, j’ai donc jeté un petit coup d’œil dans le code. Autant le dire tout de suite : c’est bien au-dessus de mes compétences et si j’ai réussi, à force d’essais et d’erreurs, à 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 c’est impossible (encore une fois, je ne connais que très peu le code et il est d’une complexité relativement élevée pour moi), je dirais que ce n’est 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 l’on 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 n’est 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 d’obtenir 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 j’ai vérifié, bricoler le code source pour lui faire afficher ce que l’on souhaite, mais une modif juste pour ça me paraît très largement au-dessus de la nécessité.
|
||||||
|
|
||||||
|
Et c’est là que je me suis souvenu que `garage` avait aussi une API d’administration sur laquelle on pouvait trouver pas mal de trucs…
|
||||||
|
|
||||||
|
# API admin, quand tu nous tiens
|
||||||
|
|
||||||
|
Activer l’API admin est relativement simple, il suffit d’ajouter un *token* dans `/etc/garage.toml` :
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[admin]
|
||||||
|
api_bind_addr = "[::1]:3903"
|
||||||
|
admin_token = "tamerelol"
|
||||||
|
```
|
||||||
|
|
||||||
|
À partir de ce moment, on peut interroger l’API avec un petit `curl` des familles, tout simplement (l’entê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 d’utiliser 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à, c’est essentiellement une question de rédiger un script correct pour Munin.
|
||||||
|
|
||||||
|
# Chargez les Munin-tions (oui, c’est 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 l’entrée `config` ;
|
||||||
|
* être capable de sortir un chiffre par ligne dans un format assez basique.
|
||||||
|
|
||||||
|
C’est donc simplement une affaire de récupérer les bonnes données, les mettre en forme et merci kiki.
|
||||||
|
|
||||||
|
On va ici simplement s’attaquer à 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 s’agit simplement de changer quelques données dedans à chaque fois (j’expliquerai un peu plus loin quelques subtilités).
|
||||||
|
|
||||||
|
Histoire d’avoir un truc présentable, on va aussi essayer de faire afficher à Munin les noms des *buckets* plutôt que leur identifiant. À noter qu’avec l’API admin de `garage`, il est impossible d’appeler 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 qu’il soit accessible depuis tous nos scripts Munin. Pour cela, on peut aller l’ajouter comme une variable d’environnement 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 l’exécute dans l’environnement de Munin. Voilà le résultat après une grosse heure de script :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
HEADER="Authorization: Bearer ${BEARER}" # on forme l’entête un peu en avance,
|
||||||
|
# on va s’en resservir souvent
|
||||||
|
|
||||||
|
declare -A BUCKETS=() # on déclare le tableau associatif
|
||||||
|
|
||||||
|
# on va aller récupérer l’ensemble des buckets via l’API.
|
||||||
|
# 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 à l’API mais avec l’identifiant du bucket cette fois-ci
|
||||||
|
# comme ça, on peut récupérer la donnée qui nous intéresse, toujours via jq
|
||||||
|
# et l’afficher 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 d’une 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 l’on 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 d’autres données, c’est à peu près aussi simple : lors de second appel, sans rien modifier d’autres, 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*, c’est globalement la même chose à un petit détail près : non seulement il faut effectivement demander le champs `bytes` lors du second appel à l’API, mais il faut aussi demander au graphe de passer d’une base 1000 à une base 1024 histoire d’afficher des données cohérentes.
|
||||||
|
|
||||||
|
Mais voilà, c’est à 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 d’ajouter ce qui suit à la `config`.
|
||||||
|
|
||||||
|
```
|
||||||
|
graph_total Total
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour du disque, ça peut être pertinent…
|
101
content/2023-07-18-Réduire-un-cluster-garage.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
+++
|
||||||
|
title = "Réduire un cluster Garage"
|
||||||
|
date = 2023-07-18
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système", "garage" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Des fois, tu fais des choix douteux. Et des fois, ces choix douteux t’amènent à avoir un cluster Garage bien trop gros par rapport à ton besoin réel.
|
||||||
|
|
||||||
|
# La situation de départ
|
||||||
|
|
||||||
|
La recommandation pour Garage est d’avoir au moins 3 réplicats à travers un cluster. Autrement dit, quelques soient les machines utilisées (puisqu’un 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 quelqu’un de prudent, tu en as même rajouté une quatrième qui va servir de _gateway_ pour l’ensemble du cluster. Quand on a effectivement un peu de trafic HTTP, ça peut être intéressant d’avoir une passerelle pour accéder à l’ensemble de données du cluster. Non seulement, ça décharge les machines qui s’occupent 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, c’est loin de la recommandation, mais quand on a déjà de la redondance en dessous (NAS, RAID, etc…), ça ne sert pas à grand-chose d’avoir autant de réplicats.
|
||||||
|
|
||||||
|
|
||||||
|
Sauf que passer de la situation décrite à une autre situation n’est pas forcément ce qu’il 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 qu’il y ait des données inscrites dans le cluster au moment où on l’arrête ou où l’on change sa topologie.
|
||||||
|
|
||||||
|
Pour commencer, il faut idéalement lancer un _scrub_ sur l’ensemble 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, l’objectif est bien de vérifier l’inté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, quoiqu’il arrive, il vaut mieux le laisser terminer, cela permet de s’assurer que toutes les données sont saines. Si des blocs posent problème, ils seront récupérés depuis d’autres nœuds du cluster, opération bien entendu impossible a posteriori.
|
||||||
|
|
||||||
|
# Attention Chérie, ça va trancher
|
||||||
|
|
||||||
|
Bon, tu t’en 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, l’arrêter permet de simplifier un peu l’opé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 qu’on 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 !
|
||||||
|
|
||||||
|
# C’est pas le moment d’avoir les mains qui tremblent
|
||||||
|
|
||||||
|
Si l’on 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 l’ensemble des nœuds et à supprimer le fichier `/var/lib/private/garage/meta/cluster_layout`. Le cluster va alors retourner dans son état d’origine, pas en terme de données, mais en terme d’organisation.
|
||||||
|
|
||||||
|
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 l’ensemble 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 l’ensemble des nœuds du cluster **avant** de redémarrer. Sous peine d’avoir 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 l’ensemble 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é n’a plus aucune importance ici
|
||||||
|
# garage layout apply --version 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Normalement, une fois le _layout_ appliqué, l’ensemble 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.
|
||||||
|
|
||||||
|
C’est 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 d’un bucket avant d’attaquer le reste).
|
||||||
|
|
||||||
|
# Conclusage
|
||||||
|
|
||||||
|
Bah alors, elle est pas belle et frugale la vie ?
|
||||||
|
|
46
content/2023-07-20-Threadivers-est-ce-ça-peut-marcher.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
+++
|
||||||
|
title = "Threadivers : est-ce que ça peut marcher ?"
|
||||||
|
date = 2023-07-20
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "fédiverse", "threadiverse", "lemmy", "mastodon" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
J’observe 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, j’ai 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 d’exposer ma pensée ici.
|
||||||
|
|
||||||
|
# Micro-blog, c’est comme du blog, mais en plus petit
|
||||||
|
|
||||||
|
Mastodon/Pleroma/d’autres trucs (les puristes m’excuseront, 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 d’intérêt mais globalement, ça reste centré sur l’individu.
|
||||||
|
|
||||||
|
C’est d’ailleurs à ce titre que les micro-instances relativement spécialisées ont du sens : on peut avoir une instance pour la rédaction d’un journal, on peut avoir une instance pour un partie politique, une institution, une entreprise. Tout cela a, je trouve, parfaitement du sens. Et d’ailleurs, avoir des instances relativement spécialisées en fonction des personnes que l’on peut y trouver est probablement la meilleure marche à suivre pour l’avenir du Fédiverse : qu’une 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.
|
||||||
|
|
||||||
|
D’autant plus qu’un journaliste de l’instance 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 d’intérêt : [r/gaming](https://reddit.com/r/gaming), [r/nintendo](https://reddit.com/r/nintendo), etc… On n’y suit pas des individus mais des sujets de conversation. Et c’est à ce titre **pas** un réseau social selon moi, la composante d’interaction 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 d’autres membres, mais l’essentiel 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, n’as-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 d’intérêts puisque c’est comme ça que le logiciel le présente et l’organise.
|
||||||
|
|
||||||
|
> Remarque à moi-même : on pourrait discuter longtemps du fait que la façon de présenter les choses déforme l’usage des choses en question. Mais c’est probablement un débat pour un autre moment.
|
||||||
|
|
||||||
|
Second souci : Reddit est également un aggrégateur de liens, un endroit où l’on peut trouver classé par intérêt (nombre de haut-votes pour un sujet précis) et par centre d’intérêt des liens qui vont de toutes manières ailleurs. C’est d’ailleurs à la fois toute la qualité et tout le défaut de Reddit : il est la _front page of the Internet_, le premier truc que t’as 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 d’un site web à but lucratif qui ne peut que difficilement gérer de la rétention, mais encore une fois, c’est un débat pour un autre moment.
|
||||||
|
|
||||||
|
Lemmy pose alors aussi le souci du centre d’inté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 l’inté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 l’intérêt risque d’avoir 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 n’est 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, y’a du shitpost de qualitay, donc voilà *glory to u/spez* et vive le Threadiverse !
|
144
content/2023-08-12-Véhicules-électriques-et-EVSE.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
+++
|
||||||
|
title = "Véhicules électriques et EVSE"
|
||||||
|
date = 2023-08-12
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "véhicule électrique", "evse", "électricité" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- 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 s’en faut.
|
||||||
|
|
||||||
|
Pour les néophytes sur le sujet (ou les gens qui ont l’intention d’acheter un véhicule électrique ou les gens qui en ont acheté un mais qui « n’y connaissent rien »), je vais commencer par un petit récapitulatif des connaissances nécessaires pour la charge d’un véhicule électrique.
|
||||||
|
|
||||||
|
C’est aussi pour poser un peu de vocabulaire et quelques bases sur la façon dont c’est normé. Et c’est pas inintéressant de voir et de comprendre les choix qui sont faits dans ce cadre.
|
||||||
|
|
||||||
|
# De quoi qu’on 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 l’on dit couramment, il ne s’agit pas d’un « chargeur » mais d’un 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. D’abord parce que chaque constructeur a décidé d’utiliser un vocabulaire hyper marketing pour en parler (*optimum charge*, *fast charging*, *super charging*, etc…) et ensuite parce qu’on l’utilise excessivement rarement. Ce n’est pas comme ça qu’on charge un véhicule électrique au quotidien et ce n’est pas comme ça qu’on 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. C’est le câble qui ressemble à ça :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
C’est le câble standard en Europe : à gauche sur l’image côté véhicule et à droite côté borne de rechargement AC (courant alternatif, autrement dit les bornes que l’on 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 l’on enlève le câble, c’est 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`, c’est 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 l’ensemble des paramètres, au moins-disant : capacité supportée par le véhicule, capacité de l’EVSE/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 l’EVSE. **Qui peut le plus peut le moins**, il pourra toujours servir dans les autres situations.
|
||||||
|
|
||||||
|
J’ai 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 qu’ils 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 d’un véhicule électrique, je te conseille [cet outil](https://www.automobile-propre.com/simulateur-temps-de-recharge-voiture-electrique/).
|
||||||
|
|
||||||
|
## EVSE
|
||||||
|
|
||||||
|
Côté EVSE, c’est 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. C’est 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# La charge d’un véhicule électrique au quotidien
|
||||||
|
|
||||||
|
Voilà, maintenant que le décor est posé, que faut-il pour charger un véhicule électrique au quotidien ? C’est en fait assez simple : généralement on charge à la maison, donc on a le fameux câble fourni par le constructeur qu’on branche sur une prise Schuko.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Mais en fait, ce n’est pas vraiment un câble. C’est un **EVSE** avec une prise Schuko d’un côté et une prise Type 2 de l’autre, 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 c’est 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. J’ai 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 c’est 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 qu’il est monté de l’autre 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 c’est 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 qu’on est en vadrouille un peu plus loin, il faut pouvoir se charger sur une infrastructure publique. Et donc là, il n’y 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 n’a pas de borne publique disponible, **il faut obligatoirement son câble constructeur avec un EVSE dessus**, pour pouvoir se brancher sur n’importe 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.
|
||||||
|
|
||||||
|
C’est **débilissime**.
|
||||||
|
|
||||||
|
En fait, je ne comprends vraiment pas pourquoi tout n’est pas en kit.
|
||||||
|
|
||||||
|
Ce serait tellement plus simple d’avoir :
|
||||||
|
* 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 qu’on pourrait très bien remplacer par un adapteur camping ou pour d’autres prises finalement, ça n’a que peu d’importance.
|
||||||
|
|
||||||
|
Ç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 d’EVSE à la maison : il suffirait de le remplacer par une prise camping pour profiter une charge maximale…
|
||||||
|
|
||||||
|
# Et le pire, c’est que certains y ont pensé mais pas jusqu’au 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 qu’une 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 n’ai trouvé personne que ça choque. Personne à qui ça pose problème. Je suis là seul comme un con à me dire que c’est mal branlé et ça rend ouf de voir que personne n’a pris le temps de comprendre ou d’analyser ça pour en déduire que c’est pas comme ça qu’il faudrait faire et qu’il y a probablement un produit à sortir.
|
||||||
|
|
||||||
|
Donc voilà, je cherche. Je cherche l’EVSE miracle qui ferait tout ça. Ou au pire, l’adaptateur qui permettrait de transformer un OpenEVSE en ce que je cherche.
|
||||||
|
|
||||||
|
J’ai déjà personnellement posé une prise Type 2 femelle sur mon OpenEVSE pour éviter d’avoir encore un câble qui traîne attaché. Donc ce n’est 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.
|
||||||
|
|
@@ -0,0 +1,97 @@
|
|||||||
|
+++
|
||||||
|
title = "MariaDB : comment fonctionne la gestion mémoire ?"
|
||||||
|
date = 2023-09-01
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "mariadb", "mysql", "sysadmin", "système" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Je traîne un souci depuis un bon moment maintenant : j’ai quelques conteneurs LXC avec une base de données MariaDB. Ce n’est pas la configuration par défaut, mais c’est une configuration que je porte de machine en machine depuis quelques années et qui n’est pas forcément spécifiquement faite pour faire de la performance.
|
||||||
|
|
||||||
|
Mais là n’est 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), c’est quand même assez étonnant.
|
||||||
|
|
||||||
|
D’autant plus que de prime abord, tout va bien : l’hyperviseur 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 d’avoir le nez dessus quand ça arrive, ça risque d’être relativement compliqué à diagnostiquer.
|
||||||
|
|
||||||
|
Ce qui m’amène donc au sujet du jour : comment MariaDB gère-t-il la mémoire ? Y’a-t-il un minimum ? Un maximum ? Quelles valeurs de configuration peut-on manipuler pour arriver à le limiter sans qu’il 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 qu’on va pour l’instant 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 t’as rien fait de particulier.
|
||||||
|
|
||||||
|
Alors, il y a peut-être (probablement même), d’autres 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 n’ira jamais en dessous de ces valeurs, à moins qu’on ne l’y force.
|
||||||
|
|
||||||
|
Premières constations donc :
|
||||||
|
* sans aucun ajustement, c’est, en théorie, 273Mio incompressible
|
||||||
|
* si l’on 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 l’on peut faire en auto-hébergement : si l’on peut se passer de l’un ou l’autre 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 t’en douter, je me suis donc empressé de convertir toutes les tables de toutes les DB de tous les conteneurs en *InnoDB* et j’ai rapidement déployé une configuration plus optimale. Ça permet de souffler, mais tu vas voir que ce n’est 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 n’est pas bien optimisé, mais c’est 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 (c’est 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 l’on reprend les 151 connexions par défaut, **ça veut dire qu’on peut monter jusqu’à 3Gio tout carrossés.**
|
||||||
|
|
||||||
|
Bon évidemment, c’est **le pire scénario possible**. On peut bien évidemment supposer que cela n’arrivera 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 : c’est 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 l’on 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 l’on peut imaginer comme pénalité pour les performances).
|
||||||
|
|
||||||
|
J’ignore si l’on 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 l’instant et comme illustré ci-dessus, j’y ai toujours vu 0. Je ne sais donc pas trop quoi en conclure (peut-être qu’on pourrait réduire cette valeur à 1Mio, peut-être pas…).
|
||||||
|
|
||||||
|
Par contre, je sais que l’on 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 l’abaque 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 l’usage depuis le dernier redémarrage. Ce n’est évidemment pas parfait puisque si le serveur plante à cause d’un surplus de connexion, ça ne t’aidera pas beaucoup. Néanmoins, ça peut permettre d’ajuster 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é à l’ensemble des processus MariaDB (273Mio par défaut que l’on 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 d’un 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 d’ajuster le cas échéant.
|
@@ -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 c’est 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 c’est bien gentil, mais ce n’est 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, c’est é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 l’interface Web (oui, je sais, c’est caca™), d’aller dans la section `Plugins`. On te propose alors d’indiquer 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 l’installation, 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 s’affiche, mais si t’as loupé un truc, c’est pas bien grave donc).
|
||||||
|
|
||||||
|
Une fois que cela est fait, je t’invite à stopper le plugin lui-même, parce qu’il va falloir aller faire pas mal de bricolage maintenant.
|
||||||
|
|
||||||
|
## Création d’un dataset dédié
|
||||||
|
|
||||||
|
Comme pour le service S3, il faut un dataset dédié (et malheureusement distinct du dataset d’origine) et avec les **bonnes ACLs**. Et c’est généralement là qu’on commence à bien rigoler…
|
||||||
|
|
||||||
|
Donc, rends-toi dans `Storage > Pools` pour créer le dataset dédié. Le nom a peu d’importance, ce qui est important en revanche, ce sont les ACLs.
|
||||||
|
|
||||||
|
Dans l’écran pour régler les permissions voici donc ce qu’il faut faire pour que ça marche correctement :
|
||||||
|
* pour le *preset*, tu peux sélectionner `Restricted` qui correspond plutôt pas mal au cas d’usage (on ne veut pas que quoique ce soit d’autres 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 à l’utilisateur `minio` et à lui seul.
|
||||||
|
|
||||||
|
## Association du dataset au plugin
|
||||||
|
|
||||||
|
Une fois tout ceci fait, tu devrais avoir un plugin MinIO à l’arrêt et un dataset avec les bonnes permissions.
|
||||||
|
|
||||||
|
Le plugin lui-même va placer l’ensemble 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 l’on vient de créer là. **Sauf que** le dossier `/var/db/minio` dans la `jail` n’est 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 d’y associer le dataset.
|
||||||
|
|
||||||
|
On va donc bouger un dossier cacher, `.minio.sys/` de ce dossier vers la racine du dataset que l’on 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 l’interface des plugins, tu peux aller triturer les points de montage. Il suffit d’associer 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 l’utilisateur). Il va donc falloir lui associer des ports sur l’hôte. Normalement, MinIO utilise deux ports :
|
||||||
|
* 9000, pour S3
|
||||||
|
* 9001, pour l’administration
|
||||||
|
|
||||||
|
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 d’aller 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 qu’il faut effectivement tout recréer (ou trouver une bricole avec l’API que je n’ai pas trop cherchée : avec un seul bucket et une seule clé, ça n’en 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 qu’il existe des scripts précuits ou des recettes de cuisine, peut-être même fournies par le projet MinIO, mais j’ai 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 l’interface d’admin de chaque MinIO, je ne détaille pas (c’est 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 d’enregistrer 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 t’es 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, c’est déjà ça de pris.
|
||||||
|
|
||||||
|
Pour le transfert lui-même, il faut s’armer 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 l’on a le bon nombre d’objets. 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 s’assurer que les données en question ont bien été transférées, bon courage là-dessus aussi.
|
||||||
|
|
||||||
|
# Conclusage
|
||||||
|
|
||||||
|
Un service qui s’arrête, ce sont des choses qui arrivent. Ça ne fait plaisir à personne, mais des fois, il n’y a simplement pas le choix (obsolescence, sécurité, maintenabilité, les raisons sont multiples).
|
||||||
|
|
||||||
|
Par contre, quand c’est le cas, le minimum c’est de prévenir bien à l’avance 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 (d’où ce billet d’ailleurs), mais en plus elle est loin d’être efficace. Je n’avais qu’un seul téraoctet de données en S3 et heureusement suffisamment de place à côté. Je n’imagine même pas la galère les utilisateurs un peu justes en terme d’espace ou avec des volumétries bien plus importantes.
|
90
content/2024-05-27-Plomber-un-nom-de-domaine.md
Normal 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 d’avoir 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… C’est quoi le minimum à faire pour préserver le nom de domaine en question et éviter qu’il se fasse pourrir/qu’on envoie des mails à ta place/que quelqu’un essaie de prendre ta précieuse virginité ?
|
||||||
|
|
||||||
|
C’est ce qu’on va voir maintenant !
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
Tu viens donc d’acheter un nom de domaine dont tu ne te serviras probablement jamais. Mais il ne s’agirait pas que des gens commencent à envoyer des mails avec, tentent de commander des certificats en ton nom, etc…
|
||||||
|
|
||||||
|
Du coup, qu’est-ce qui faut faire ?
|
||||||
|
|
||||||
|
# Pour la partie mail
|
||||||
|
|
||||||
|
Dans tous les cas, si un nom de domaine n’est pas supposé envoyer de mail, j’aurais tendance à le plomber par défaut. C’est tellement facile quand il n’y a aucun contrôle d’aller envoyer des mails à tout va sur un domaine qui vient d’être enregistré et ce à l’insu 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 d’ajouter un enregistrement DNS qui ressemble à ça :
|
||||||
|
|
||||||
|
```
|
||||||
|
> dig +short buttse.cx mx
|
||||||
|
0 .
|
||||||
|
```
|
||||||
|
|
||||||
|
Soit, ce n’est 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. D’ailleurs, 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à, c’est assez simple et universel, mais il y a beaucoup d’enregistrements à ajouter. On commence donc pas SPF v1 et v2. Ce sont de simples enregistrements `TXT` à la racine du domaine qui permettent d’indiquer qui est autorisé à envoyer des mails pour le domaine en question.
|
||||||
|
|
||||||
|
Pour les plomber, c’est assez simple, il suffit de dire que personne n’y est autorisé :
|
||||||
|
|
||||||
|
```
|
||||||
|
dig +short buttse.cx txt
|
||||||
|
"v=spf1 -all"
|
||||||
|
"spf2.0/mfrom -all"
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour info, SPFv2 n’est utilisé par personne… sauf Microsoft :/
|
||||||
|
|
||||||
|
Donc si tu ne veux pas avoir d’emmerdes avec eux, bah faut s’y plier, y’a pas le choix.
|
||||||
|
|
||||||
|
Il faut ensuite préciser au niveau DMARC, que faire en cas de réception d’un message depuis ce domaine. En l’occurence, il faut absolument tout rejeter, quelque soit le message. Absolument aucun message ne devrait être envoyé et c’est exactement ce qu’on 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` à l’enregistrement. 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 d’indiquer clairement aux organismes émettant des certificats qu’il 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, c’est loin d’être idiot.
|
||||||
|
|
||||||
|
Donc, tout cela se faire avec l’enregistrement `CAA` (pour _Certificate Authority Authorization_) et il y a justement un cas prévu pour « rien » :
|
||||||
|
|
||||||
|
```
|
||||||
|
> dig +short buttse.cx caa
|
||||||
|
0 issue ";"
|
||||||
|
```
|
||||||
|
|
||||||
|
À l’instar 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 l’instant 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.
|
@@ -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 j’ai donc besoin d’extérioriser. Donc plutôt que de faire un TRÈÈÈÈÈÈS long poste dans Mastodon, je vais y préférer écrire ici ce que j’en pense, ce qui est possible, ce que je devine, ce que j’intuite. C’est à la fois de la prospective et un truc cathartique.
|
||||||
|
|
||||||
|
Donc rien de ce qui est écrit ici n’est à prendre au premier degré. Encore une fois, je ne suis pas un expert en quoique ce soit, j’essaie juste de comprendre la situation et de me projeter à l’aprè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, c’est du pif 100%, je ne suis pas assez câlé, j’essaie juste d’organiser 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 n’y 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 l’autre. Ce système me saoûle souverainement, mais je ne vais pas revenir dessus, ce n’est pas l’objet.
|
||||||
|
|
||||||
|
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. J’ai 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 l’intégralité des bulletins pour être sûr. Et soyons clairs, sur une élection aussi serrée dans certaines circonscriptions, ce n’est 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 n’a 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 l’intérim pendant les Jeux Olympiques (ah oui, parce qu’il 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 à l’Assemblée Nationale (le seuil pour la majorité absolue), il faudrait que le RN ramasse 200 circonscriptions de plus qu’en 2022. Malgré les scores aux Européennes, l’auto-sabotage et les renoncements démarrés il y a une grosse semaine me font dire qu’ils 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 qu’on espère…
|
||||||
|
|
||||||
|
Quant à la démission du Président de la République, je pense qu’elle est peu probable dans ce cas. Il sera bien trop heureux de pouvoir taper sur son Premier Ministre à longueur de journée et il s’accomoderait très bien du programme du RN. Sans compter le fait qu’il pourrait se replier sur le « domaine réservé », un truc qui n’existe pas, mais qu’il pourrait très bien réactiver. Bref, c’est 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
|
||||||
|
|
||||||
|
Quelqu’un se retrouve Premier Ministre. Comme personne ne peut blairer Jean-Luc « Moi je suis de gauche mais quand même, c’est pas possible » Mélenchon, ce n’est pas lui qui est Premier Ministre. Faure, Tondelier, qui vous voulez mais pas lui… Bref, on s’en fout.
|
||||||
|
|
||||||
|
Le Nouveau Front Populaire gouverne donc, le Rassemblement National est la principale force d’opposition et ça leur va finalement très bien. Comme Jordan est élu européen, on en entend plus parler et c’est tant mieux. Comme le programme n’est 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 c’est le retour des politiques d’austérité. Voilà, ça c’est 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é l’alliance au Parlement, tout le monde partirait en désordre de bataille pour cette présidentielle anticipée. Au final, c’est n’importe-qui™ qui se retrouve élu et ça ne change rien, sauf s’il 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 n’est 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 n’auraient pas voté en 2022 mais voteront en urgence ici. Quoiqu’il 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, j’ai envie d’y croire mais sincèrement, j’en doute. J’espère avoir tort, vraiment.
|
||||||
|
|
||||||
|
## Hypothèse 3 : Aucune coalition n’a 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 à l’autre.
|
||||||
|
|
||||||
|
Sauf qu’ils sont vissés au Parlement pour un an minimum. D’ordinaire, 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), c’est justement parce qu’une censure peut amener à une dissolution et donc une redistribution des cartes. Il est donc absolument nécessaire pour l’Assemblée Nationale de bien peser les choses avant de censurer le gouvernement.
|
||||||
|
|
||||||
|
Mais là ? Et bien là, c’est 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 l’Assemblé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 s’il le fallait. Ce serait le retour à la fameuse instabilité gouvernementale de la IIIème République.
|
||||||
|
|
||||||
|
En soi, je n’y vois pas de problème. C’est un retour en force du Parlement dans la vie politique et ce n’est pas plus mal. Mais notre Vème République n’est 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 l’Assemblé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 ? C’est 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 qu’il 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 n’a la majorité absolue, mais une majorité relative se détache
|
||||||
|
|
||||||
|
On revient sur un scénario qui est potentiellement « gérable ». C’est comme ça que l’on vit depuis 2 ans et globalement, le Parlement est en capacité de travailler. Mais toujours pareil que dans l’hypothè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 d’installer une culture du compromis. Ça pourrait ouvrir la porte à une Assemblée de compromis. Ça pourrait nous sortir de l’orniè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 s’assurera que quoiqu’il arrive, aucun compromis ne puisse être trouvé mais que nous ayons de nouveau un gouvernement par fait majoritaire. Si l’on fait une proportionnelle avec une prime majoritaire de 30% par exemple…
|
||||||
|
|
||||||
|
# Conclusage
|
||||||
|
|
||||||
|
Bah, j’en sais rien. Mais ça fait du bien d’imaginer ce qui pourrait se passer… Parce que franchement, c’est le merdier dans ma tête et ça fait 2-3 jours que ça tourne. Là, au moins, c’est fixé, c’est figé.
|
116
content/2024-10-29-Monter-son-propre-NAS.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
+++
|
||||||
|
title = "Monter son propre NAS"
|
||||||
|
date = 2024-10-29
|
||||||
|
[taxonomies]
|
||||||
|
tags = [ "système", "nas", "zfs", "nfs" ]
|
||||||
|
+++
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Il y a de nombreuses lunes, j’avais 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, c’est quoi le truc ?
|
||||||
|
|
||||||
|
TrueNAS Core, c’est une base de FreeBSD (qui commence à dater un peu d’ailleurs). TrueNAS Scale, c’est une base de Debian. Les deux font du ZFS, les deux font du NFS, les deux font du CIFS.
|
||||||
|
|
||||||
|
La grosse différence, c’est donc essentiellement la « fraîcheur » du système et sa maintenabilité.
|
||||||
|
|
||||||
|
Maintenant, dans un cas comme dans l’autre, mes vieux démons me rattrappent : il m’est impossible d’inté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 l’API en question alors que j’ai 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, c’est pas ouf… Même si ça fonctionne tout-à-fait correctement, je tiens à le préciser.
|
||||||
|
|
||||||
|
Mais comme c’est une putain de Debian avec finalement pas grand-chose dessus, est-ce que c’est si compliqué que ça à faire soi-même « à la main » ? Ben non en fait…
|
||||||
|
|
||||||
|
# Installation Debian
|
||||||
|
|
||||||
|
Je ne vais pas te mentir, c’est presque la partie la plus compliquée. L’installation 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 t’occupes de rien.
|
||||||
|
|
||||||
|
Tout s’installe aussi sur du ZFS. Bon installer Debian sur du ZFS à la main, c’est pas [infaisable mais v’là 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.
|
||||||
|
|
||||||
|
J’ai fini par tomber d’accord avec moi-même sur une configuration de ce type : une première partition EFI sur chaque disque système (en l’occurence 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 d’utilisateurs dans mon cas) et une partition `/var` un peu conséquente.
|
||||||
|
|
||||||
|
Pareil, je pars aussi du principe que la partie réseau n’est pas vraiment un problème : soit tu as une adresse fixe, soit c’est du DHCP, mais en gros, tu sais te débrouiller là-dessus et tu n’a pas de config cheloue avec des interfaces agrégées qui partent dans tous les sens.
|
||||||
|
|
||||||
|
# Installation d’OpenZFS
|
||||||
|
|
||||||
|
Là où ça se complique un peu, c’est pour l’étape d’après : il faut aller importer les informations ZFS existantes.
|
||||||
|
|
||||||
|
La première étape, c’est donc d’installer ZFS. Pour éviter un *downgrade*, opération pas toujours bien supportée par ZFS, j’ai choisi de ne baser sur OpenZFS disponible dans les *backports* de Debian 12. Si tu commences juste avec ZFS et que tu n’as 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, s’il s’agit d’une récup depuis un ZFS existant, faut suivre la suite. S’il s’agit d’une première installation, faut créer les volumes, la doc en ligne devrait te suffire.
|
||||||
|
|
||||||
|
Généralement, TrueNAS monte l’ensemble 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 d’importer le volume existant (en forçant, sinon ZFS n’est 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 s’assurer qu’on 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 d’ailleurs 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à, c’est un peu con comme la mort, on va pas se mentir.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apt install nfs-kernel-server && systemctl start --enable nfs-server.service
|
||||||
|
```
|
||||||
|
|
||||||
|
C’est à peu près tout. Bon, idéalement, il faut récupérer le fichier `/etc/exports` qui existait à l’origine sur le TrueNAS d’origine. Si ce n’est pas ton cas, il faudra aller le recréer, mais la syntaxe n’est pas bien compliqué.
|
||||||
|
|
||||||
|
Une fois arrivé là, tu as déjà 90% de TrueNAS, sauf évidemment si tu n’as aucun self-respect et que tu fais du CIFS. Mais seuls les déments font ça, donc je suppose que c’est bon.
|
||||||
|
|
||||||
|
# SMART
|
||||||
|
|
||||||
|
Histoire de compléter, tu peux installer SMART. Là, aussi c’est à 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 ! T’as besoin de rajouter du S3, tu peux faire du [garage](/tags/garage/). T’as besoin d’héberger du Bittorent, tu peux faire du transmission, du déluge, etc…
|
||||||
|
|
||||||
|
Si ton TrueNAS ne te servait vraiment que de stockage, là, tu n’as que du stockage, point.
|
||||||
|
|
||||||
|
# Conclusage
|
||||||
|
|
||||||
|
Oui, c’est 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 l’interface clicaconvy, mais qui a vraiment envie de tout faire en passant par ça ou par une API imbitable ?
|
4
content/_index.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
+++
|
||||||
|
sort_by = "date"
|
||||||
|
+++
|
||||||
|
|
BIN
static/.39fmvg_m.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
static/.39fmvg_s.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/.39fmvg_sq.jpg
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
static/.39fmvg_t.jpg
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
static/.N64_schema_s.jpg
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
static/.N64_schema_sq.jpg
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
static/.N64_schema_t.jpg
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
static/.Screenshot_2019-03-13_Database_Updater_m.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
static/.Screenshot_2019-03-13_Database_Updater_s.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
static/.Screenshot_2019-03-13_Database_Updater_sq.png
Normal file
After Width: | Height: | Size: 820 B |
BIN
static/.Screenshot_2019-03-13_Database_Updater_t.png
Normal file
After Width: | Height: | Size: 972 B |