Copier tout le contenu d'un fichier dans le presse-papier en ligne de commande : xsel -b < <fichier>
Qui n'a pas déjà voulu qu'une commande GNU/Linux enregistre son résultat dans le fichier d'origine, genre sort MonFichier > MonFichier
?
Évidemment, ça ne fonctionne pas car, à cause de la redirection (« > »), le shell vide le fichier « MonFichier » puis l'ouvre avec l'identifiant « 1 » (stdout) dans la table des descripteurs de fichiers du processus, puis il se duplique avec l'appel système « fork() » (un fork() conserve la table des descripteurs de fichiers) puis remplace le code de sa copie par celui du programme demandé (sort, dans notre exemple) avec la famille d'appels système « exec() », ce qui en lance l'exécution. Donc, dès le début, le fichier d'origine est vidé.
La plupart des commandes permettent d'écrire leur résultat dans un fichier genre sort -o <fichier> <fichier>
. Mais pas toutes.
C'est là que sponge
intervient : cette commande attend la fin de l'écoulement des données sur stdin puis elle ouvre le fichier passé en argument et enregistre tout ce qu'elle a reçu depuis stdin.
Pratique quand une commande ne permet pas d'enregistrer son travail dans un fichier (genre jq
) et qu'on ne veut pas créer xxx fichiers temporaires (genre « commande fichierN > fichierN+1 »).
Exemple d'utilisation : jq '.' monFichier | sponge monFichier
.
Voir mon introduction à la prise en main de jq
, le logiciel de manipulation de JSON en ligne de commande.
Soit le fichier JSON africa-coast-to-europe-ace.json.
Avec jq
version 1.5.1 Debian GNU/Linux Buster, je veux récupérer la valeur de l'attribut « owners » sous la forme d'une unique chaîne de caractère. Je voudrais que la séparation entre chaque propriétaire ne soit plus « , », mais « \n * » (afin de produire une liste à puces).
Facile, il suffit d'utiliser la fonction gsub(), greedy sub(), qui permet de remplacer des bouts de chaînes de caractères. Sauf que…
$ jq -r '.owners | " * \(gsub(", ";"\n *"))"' africa-coast-to-europe-ace.json
* Orange
* Dolphin Telecom
* Cote d’Ivoire Telec
*
* Gambia Submarine Cable Company
* MTN
[…]
Hum, il y a un raté pour Cote d'Ivoire Telecom.
Je t'épargne l'identification du problème avec hexdump -C
et jq -a
(afficher le code séquence d'un caractère non-ASCII) : ce modèle d'apostrophe n'est pas un caractère ASCII mais Unicode.
Normalement, ça ne devrait pas poser de problème puisque jq
travaille, par défaut, en UTF-8 (d'après son manuel). De plus, file -i
est formel, le fichier utilisé est bien encodé en UTF-8. Même si j'utilise iconv -t UTF-8 africa-coast-to-europe-ace.json -o africa-coast-to-europe-ace.json
pour m'en assurer, ça change rien.
J'ai essayé avec d'autres caractères Unicode : ça fonctionne avec « » » et des caractères Hangûl, mais pas avec le caractère « ⚧ »…
J'en déduis que la fonction gsub() de jq
foire avec certains caractères Unicode.
J'ai rien trouvé de mieux que de remplacer l'aspostrophe problématique par l'apostrophe ASCII avec sed -i "s/’/'/" africa-coast-to-europe-ace.json
avant son traitement par jq
…
Ce bug semble être corrigé dans la version 1.6 Debian GNU/Linux de jq
.
TL;DR : carte OpenStreetMap des câbles sous-marins d'Internet / Internet submarine cables map OpenStreetMap. Ci-dessous, pourquoi et comment construire une telle carte.
ÉDIT DU 29/08/2023 : depuis le 15/11/2022, TeleGeography a supprimé son dépôt git et ne diffuse plus les données géographiques (« We no longer maintain and update a repository on GitHub for our data or our source code » dans l'infobulle « about » sur leur site web). :( Cette carte n'est donc plus à jour. Dernière version des données géographiques. FIN DE L'ÉDIT.
Loin d'être virtuel, Internet repose sur des infrastructures physiques dont des câbles sous-marins en fibre optique (qu'il faut entretenir contre l'usure, les événements naturels ou humains, les sabotages, etc.).
La liste de ces câbles, leurs stations d'atterrissement et leur tracé est environ public (environ car le tracé ne l'est pas de manière précise). Nous disposons de l'excellent site web Submarine Cable Map.
Néanmoins, cet existant, cette carte, me pose deux problèmes :
Pour l'heure, les données (le tracé des câbles et les coordonnées géographiques des stations d'atterrissement) de la carte « Submarine Cable Map » sont disponibles dans un dépôt git public sous une licence environ libre (CC BY-NC-SA).
Et si on les utilisait pour construire une carte basée sur OpenStreetMap, l'outil de cartographie libre et communautaire (OSM) ?
C'est chose faite : carte OpenStreetMap des câbles sous-marins d'Internet / Internet submarine cables map OpenStreetMap.
J'actualiserai cette carte au fil de l'eau, en me basant sur le dépôt git sus-cité.
Ci-dessous, un retour sur la façon dont j'ai procédé pour construire cette carte.
Pour réaliser cette carte, il faut deux choses.
D'un côté, il faut pouvoir créer une carte personnalisée en utilisant un fond de carte OpenStreetMap, c'est-à-dire une carte sur laquelle on peut représenter ce que l'on veut : un itinéraire, un parcours de trail, un marqueur sur un lieu avec une éventuelle infobulle pour donner des infos, plusieurs marqueurs afin d'identifier plusieurs lieux (pour un festival décentralisé, par exemple), etc.
Le site web officiel d'OpenStreetMap permet de tracer des itinéraires et de partager une carte avec un marqueur sur un lieu (sans infobulle, sans marqueur personnalisé), mais ça s'arrête là.
Le logiciel web umap pemet tout le reste sus-cité. On peut installer sa propre instance ou utiliser celles qui existent. Exemples : instance umap OSM France, instance umap Framasoft.
Vu mon maigre besoin, j'ai décidé d'utiliser une instance existante. J'ai choisi celle de Framasoft, une association française qui œuvre pour des services numériques éthiques et la culture libre.
Pour débuter, je recommande vivement cet exellent tutoriel umap. D'autres tutoriels, et même des tutoriels vidéos sont disponibles sur le wiki officiel OSM France.
D'un autre côté, il faut retravailler les données géographiques disponibles :
On pourrait faire ça à la main, mais de nouveaux câbles entrent régulièrement en service pendant que d'autres rendent leur tablier. Une mise à jour à la main ne serait pas gérable. Il faut donc qu'un programme informatique fasse cette transformation des fichiers d'origine prévus pour Google Maps en fichiers compréhensibles par OpenStreetMap en y ajoutant, au passage, nos desiderata de mise en forme.
Dès que l'on évoque la manipulation de JSON, on pense forcément à la commande jq
. J'ai écrit un tutoriel pour jq qui est rien de plus que le retour de l'expérience acquise pour construire cette carte des câbles sous-marins d'Internet.
Voici le script que j'ai pondu : transform_carte_cables_sous-marins.sh.
git
et jq
doivent être installés ;Une fois que l'on a exécuté ce script afin de traiter les données, comment créer la carte et y importer les données ?
Créer une carte sur une instance umap ;
Dans l'entête, à côté de « Carte sans nom », il y a une icône en forme de crayon. Cliquer dessus :
Limites géographiques (pour limiter les déplacements sur la carte aux endroits où il y a des câbles afin d'éviter les fausses manips) :
Cliquer sur le bouton « Enregistrer ». Garder impérativement le lien de modification (tant que le cookie est conservé par ton navigateur web, ça va, mais sinon il constitue le seul moyen de pouvoir modifier la carte) ;
Cliquer sur le bouton « calques » :
Supprimer le calque 1 ;
Ajouter un calque :
Description = « Landing stations » ;
Propriétés de la forme :
Ajouter un calque :
Description = « submarine cables » ;
Propriétés de la forme :
Cliquer sur le bouton « Enregistrer » ;
Cliquer sur le bouton « importer des données » :
Je note quelques limites d'umap :
#<niveau_de_zoom>/<latitude>/<longitude>
). Dit autrement : je n'ai pas trouvé un moyen de faire un lien vers un objet. Sur OSM, les objets (le tracé d'une ligne de bus, par exemple) ont un identifiant interne public, donc on peut construire une URL de la forme « /relation/<identifiant>
». Avec umap, je n'ai pas trouvé d'identifiant, y compris dans l'export ;Le script présenté plus haut permettant de traiter les données géographiques avant importation dans umap n'est pas le premier que j'ai écrit. Le premier, c'est celui-ci : transform_carte_cables_sous-marins.original.sh.
Quelles différences ?
jq
peut mettre le contenu d'un (ou plusieurs) fichier secondaire dans une (ou des) variable afin d'enrichir le fichier principal avec des informations du (des) fichier secondaire ;jq
. Tout filtre ajouté à la chaîne sera exécuté autant de fois qu'il y a d'éléments dans le tableau parcouru. Cela signifie aussi que l'on perd tout le contenu JSON qui était autour de ce tableau. Oui, c'est aussi idiot que de se plaindre qu'avec un grep | xargs <maCommande>
, maCommande n'accède pas à ce qui n'a pas été retenu par grep
. Mais, du coup, ça me fait perdre l'entête et le pied du fichier JSON d'origine, donc ça produit un JSON invalide. Si je n'itère pas sur le tableau, je ne peux pas accéder aux attributs de chaque élément du tableau, donc il m'est impossible, par exemple, de transvaser la couleur d'un câble dans un attribut compréhensible par umap (genre jq '.features[].properties += { "_umap_options": { "color": .color } }' cables.json
, ça ne fonctionne pas). Comme je n'avais pas compris que jq
permet de travailler sur plusieurs fichiers, je n'avais pas perçu que l'on peut reconstruire le fichier JSON d'origine ;Ces trois points sont détaillés dans mon shaarli dédié à la prise en main de jq par des exemples pratiques.
En conséquence, je faisais une boucle « for » avec mon shell. La fin de l'itération était le nombre d'éléments dans le tableau - 1. Ainsi, j'avais un indice de tableau. Donc je pouvais accéder à l'élément que je voulais dans le tableau, récupérer son identifiant (permettant de récupérer ses infos dans le fichier JSON séparé) et le modifier.
jq
(récupérer l'identifiant d'un élément, récupérer les infos dans le deuxième fichier, modifier le JSON avec tout ça) ‒ ce qui implique des fork() et des exec() en pagaille ‒, que j'écrivais autant de fichiers temporaires qu'il y a d'éléments parcourus, et que jq renvoi l'intégralité du JSON à chaque passe. Tout ça consomme du CPU, de la RAM et des IO. Ce premier jet s'exécute en 1 minute 54 (moyenne sur 3 exécutions, récupération du dépôt git exclue). Et, encore, je suis en RAM (tmpfs).jq
distincte, je peux appliquer très facilement un grep -v 'null'
afin de dégager de la sortie les attributs JSON non-remplis (genre un câble qui n'a pas de site web associé, par exemple). EDIT DU 21/08/2021 : pour afficher un texte pré-défini quand la valeur d'un attribut JSON est null, jq dispose d'un opérateur de remplacement, « // ». FIN DE L'ÉDIT.printf()
genre « Working on cables: 001/469… […] 142/469… ». Ça fait bien longtemps que je n'avais pas écrit un code générant un affichage aussi kikoo. :DLa version actuelle du script utilise une seule commande jq
(mais avec des filtres supplémentaires) afin de réaliser l'itération, l'enrichissement du JSON et la modification du flux JSON.
jq
commence à devenir touffue et très compliquée à comprendre, donc à maintenir dans le temps. Oui, je pourrais virer des variables, mais guère plus, je pense.jq
mais au prix de la lisibilité. EDIT DU 21/08/2021 : pour afficher un texte pré-défini quand la valeur d'un attribut JSON est null, jq dispose d'un opérateur de remplacement, « // ». FIN DE L'ÉDIT.
Merci Johndescs pour la relecture et la correction des scripts.
Autoriser l'amplification du volume audio sur la version Android de VLC : menu -> Preferences -> Video -> Audio boost.
Durant la lecture d'un média, il suffira d'augmenter le volume comme d'habitude (glisser bas vers haut du côté droit), mais, cette fois-ci, il pourra dépasser 100 %, comme sur la version PC GNU/Linux de VLC.
Attention, pour ce faire, il faut au moins la version 3.0 de VLC (source).
Si vous avez installé VLC avec l'apk distribué sur le site web officiel (car VLC n'était plus disponible sur F-Droid à une époque, par exemple), F-Droid dira que le logiciel est à jour, ce qui n'est pas le cas. Il faut désinstaller VLC puis l'installer avec F-Droid.
jq
(paquet logiciel du même nom) est un outil en ligne de commande pour manipuler du JSON (quelle idée !).
Il m'apparaît que le meilleur moyen d'apprendre à utiliser jq
, c'est de pratiquer. C'est ce que nous allons faire dans ce shaarli.
Fichiers utilisés dans les exemples ci-dessous : cables.json et cablesDefs.json.
L'usage le plus simple de jq
est de rendre lisible (indentation, coloration syntaxique, etc.) un bout de JSON qui ne l'est pas (on dit aussi prettifyer, forrmatter) : jq '.' cables.json
.
Afficher la valeur / le contenu de l'attribut « features » de l'entité / objet racine : jq '.features' cables.json
.
jq '.type' cables.json
, le retour serait une unique chaîne de caractères.
Afficher uniquement l'attribut « slug » de l'attribut « properties » de chaque élément du tableau features « features » : jq '.features[].properties.slug' cables.json
.
grep
.
Afficher l'attribut « name » de l'attribut « properties » de chaque élément du tableau « features ». Afficher « n/a » (not available) si l'attribut n'existe pas ou a « null » comme valeur : jq '.features[].properties.name // "n/a"'
.
Notons que jq
propose des paramètres intéressants.
Ce que nous avons mis entre apostrophes dans les exemples précédents se nomme un filtre. Il est possible d'enchaîner les filtres. Pour ce faire, deux opérateurs existent :
jq '.features[].properties.slug , .features[].properties.color' cables.json
;jq '.features[].properties.slug | .' cables.json
.
Tout peut être stocké dans une variable à tout moment : jq '.features[].properties as $props | .' cables.json
.
$props | <traitement_ici>
ou $props.<nom_attribut>
(exemple : « $props.color »).
Si un filtre retourne plusieurs résultats et qu'un ou plusieurs filtres lui sont chaînés, ces derniers filtres s'appliqueront / seront exécutés pour chaque résultat retourné par le premier filtre. Il est donc possible de travailler sur chaque élément d'un tableau. Nous le faisons depuis le début : jq '.features[].properties.slug' cables.json
= jq '.features[] | .properties.slug' cables.json
. Il est donc possible d'exécuter un traitement sur chaque élément du tableau, comme une sorte de grep | xargs
.
jq '.features[0] | .properties.slug' cables.json
(« [0] » demande à travailler sur le premier élément du tableau) avant de le généraliser ;jq '.features[] | .
, c'est-à-dire « affiche-moi chaque élément du tableau « features » », on se rend compte que l'on perd l'entête (« { "type": "FeatureCollection", "features": [ ») et le pied (« ] } ») de notre fichier. Il faut comprendre qu'on les retrouvera jamais, qu'on peut jamais remonter au niveau supérieur. Ce serait comme utiliser grep essai | xargs <maCommande>
et vouloir que maCommande accède à ce qui n'a pas été grep-é (retenu par grep
). Si l'on tente d'enregistrer la racine dans une variable (« . as $racine ») et que l'on tente de l'afficher dans une itération (exemple : jq '. as $racine | .features[] | .properties.slug | $racine' cables.json
), l'ensemble du fichier JSON sera affiché autant de fois qu'il y a d'éléments dans le tableau « features », ce qui n'est pas ce que l'on veut. Bref, n'essaye pas. On ne sort pas d'une itération, tout enchaînement de filtres après un filtre qui émet plusieurs éléments JSON sera exécuté autant de fois qu'il y a d'éléments ;
Recherche sur la valeur d'un attribut / afficher l'élément du tableau « features » dont la valeur de l'attribut « slug » est « adria-1 » : jq '.features[] | select(.properties.slug=="adria-1")' cables.json
.
jq '.features[] | select(.properties.slug=="adria-1") | .properties.color' cables.json
ou, plus simplement : jq '.features[] | select(.properties.slug=="adria-1").properties.color' cables.json
.jq '.features[] | select(.properties.slug | test("adria-1"))
. Il est possible de positionner des drapeaux pour des recherches gourmandes (greedy), pour des recherches insensibles à la case, etc. Exemple concret d'utilisation.
On peut mettre en forme /formater la sortie (afficher plusieurs attributs, ajouter du texte libre, etc.). Pour chaque élément du tableau « features », je veux afficher une ligne de la forme \*\*SLUG\*\*: <slug> ~ \*COLOR\*: <color>.
. Les « * » sont du formatage Markdown (**gras**, *italique*). Le texte en majuscule, le caractère « ~ » et le caractère final (« . ») sont des bouts de texte arbitraires que je souhaite afficher. jq '.features[] | "**SLUG**: \(.properties.slug) ~ *COLOR*: \(.properties.color)."' cables.json
.
jq
d'interpréter une expression située au milieu d'une chaîne de caractères.
Et si je veux utiliser le contenu d'une variable shell dans un filtre ?
jq
entre apostrophes afin que le shell n'interprète pas la chaîne de caractères. Mais, on peut aussi l'écrire entre guillemets afin quil l'interpète. Exemple : maVariable='{ "test": "toto" }' ; jq ".features[].properties += $maVariable" cables.json
. Cela signifie aussi que tu vas devoir échapper tout ce que le shell pourrait interpréter : le « $ » d'une variable interne à jq
, les guillemets, etc. Pour peu que ton enchaînement de filtres jq
soit un peu costaud, ça va le rendre illisible. Je déconseille ;jq
propose les arguments --arg <nom_variable_interne> <contenu>
(le contenu sera enregistré comme une chaîne de caractères) et --argjson <nom_variable_interne> <bout_de_JSON>
(attention : le bout de JSON doit avoir une syntaxe valide !). Reprise de l'exemple précédent : maVariable='{ "test": "toto" }' ; jq --argjson varInterne "$maVariable" '.features[].properties += $varInterne' cables.json
.
Et si l'on veut modifier le flux JSON ? Il existe les opérateurs « |= » et « += ». Le premier modifie la valeur d'un attribut en écrasant l'exisant. Le second ajoute un élément dans la valeur d'un attribut.
jq '.features[] | .properties.color |= "yellow"' cables.json
;jq '.features[] | .properties += { "toto": "titi" } ' cables.json
;jq '.features[] | .properties += { "toto": .properties.color } ' cables.json
;jq '.features[] | .properties += { "toto": { "color": .properties.color } }' cables.json
.
Normalement, si tu suis, tu me traites de tricheur : les deux dernières commandes du point précédent n'ont pas renommé / déplacé l'attribut, mais l'ont simplement copié. Pour finir le travail, jq
propose la fonction native « del() » qui, comme son nom l'indique, permet de supprimer un attribut. Démo sur les deux exemples précédents : jq '.features[] | .properties += { "toto": .properties.color } | del(.properties.color)' cables.json
et jq '.features[] | .properties += { "toto": { "color": .properties.color } } | del(.properties.color)' cables.json
.
jq
ne propose pas un argument (genre « -o ») permettant d'enregistrer le résultat de son traitement dans un fichier. Donc, si l'on veut enregistrer le résultat dans le fichier source / d'origine, il faut utiliser l'outil sponge (une redirection shell, >
, viderait le fichier avant d'exécuter jq
).
Il est possible de travailler sur plusieurs fichiers JSON en même temps. Soit en les concaténant (cat fichier1 fichier2 | jq '.'
). Oui, ça va générer un fichier JSON invalide (plusieurs entités racines…), mais jq
est tolérant. Soit en ayant un fichier principal et des fichiers secondaires accessibles via des variables. Cette deuxième possibilité est très pratique quand on veut agrémenter / enrichir un fichier principal avec des informations disponibles dans un autre fichier (sans polluer le fichier principal avec une concaténation, donc).
jq --slurpfile maVariable cablesDefs.json '.features[] | . as $cable | $maVariable[] | select(.id==$cable.properties.slug).name as $cableName | $cable | .properties += { "name": $cableName }' cables.json
.
Plus haut, j'écris que si l'on itère sur le contenu d'un attribut avec jq
, alors on perd les attributs qui entourent celui sur lequel on itère. Avec ce "mode multi-fichiers", on peut reconstruire le JSON d'origine (sauf si trop d'élements sont passés à la trappe, bien entendu). Exemple (je reprends le cas cité ci-dessus malgré son absence de représentativité) :
jq '.features[] | .' cables.json > /tmp/json.tmp # dans json.tmp, on aurait donc uniquement les éléments du tableau « features », tout le reste aura disparu
cat <<EOF > /tmp/minJSONStruct # dans ce fichier, on met l'entête et le pied du fichier d'origine (cables.json), précisement ce qui a disparu
{
"type": "FeatureCollection",
"features": [
]
}
EOF
jq --slurpfile featuresList /tmp/json.tmp '.features += $featuresList' /tmp/minJSONStruct > cables.json # on ajoute les ex-éléments du tableau « features » au tableau « features » de l'entête/pied JSON, donc on reconstitue notre fichier cables.json d'origine.
Oui, je préfère passer par un fichier temporaire que par une variable (avec « --arg ») afin d'éviter les problèmes d'échappement de caractères pour le shell.
On peut aussi préserver la structure JSON autour du tableau. Pour ce faire, il faut contenir l'itération sur le tableau au sein d'une affectation. Exemple : pour renommer l'attribut « properties.color » d'un objet membre d'un tableau en « properties.toto », il faut que j'itère sur ce tableau sinon je ne peux pas récupérer la valeur de l'attribut afin de la transvaser. Pour que cette opération ne soit pas destructrice, je l'encadre dans une affectation : jq '. as $racine | .featuresDup |= $racine.features | del(.features) | .features += [ .featuresDup[] | .properties += { "toto": .properties.color } | del(.properties.color) ] | del(.featuresDup)' cables.json
. Si je n'avais pas dupliqué l'attribut « features », alors ce tableau contiendrait deux fois chaque élément : l'original et la version modifiée. Cette copie me permet de vider l'attribut « features » et d'utiliser sa copie pour itérer dessus et remplir « features » avec les objets modifiés.
Appliquer le même traitement à tous les éléments d'un tableau. Pour chaque élément du fichier cablesDefs.json, je veux récupérer une unique chaîne de caractères contenant tous les éléments du tableau « landing_points » sous la forme * <nom1>: <latitude+longitude>\n * <nom2>: <latitude+longitude>\n […]
. Premier essai : jq '.landing_points[] | " * \(.name): \(.latlon)"' cablesDefs.json
. Bien vu, mais on ne peut pas obtenir une unique chaîne avec join() puisque ce qui ressort du filtre, c'est des chaînes de caractères et qu'un join() ne peut être appliqué sur ce type d'objets.
jq '.landing_points | map(" * \(.name) :\(.latlon)") | join("\n")' cablesDefs.json
.
Il reste encore beaucoup de choses à découvrir : les boucles, les conditions, les fonctions persos, etc. Voir le manuel officiel de jq. Pour ma part, j'ai pu réaliser ce que je voulais (construire une carte des câbles sous-marins d'Internet) donc je m'arrête là.
Sur mon ordinateur de poche Android, j'utilise le pare-feu Android AFWall+ (en fait, c'est juste une interface graphique pour le pare-feu natif du noyau Linux, Netfilter).
Ça me permet de contrôler quelles applications peuvent émettre et sur quel(s) réseau(x) (Wi-Fi, 4G, VPN) elles le peuvent.
J'utilise également un VPN de confiance (avec le logiciel Android OpenVPN For Android qui n'est pas l'implémentation officielle du projet OpenVPN) afin de me protéger des craderies de mon opérateur mobile lorsque je suis en 4G (on a vu Bouygues Telecom modifier les réponses DNS, on a vu SFR modifier les réponses web, etc.) et des points d'accès Wi-Fi malveillants (notamment ceux tenus par des potes farceurs). Mais également pour ne pas dévoiler ma vie privée à des gens en qui je n'ai pas confiance (opérateurs mobiles, fournisseurs de Wi-Fi ouverts, etc.).
Lorsqu'on utilise un VPN dans un tel contexte, le minimum est d'interdire tout trafic qui voudrait sortir en dehors du VPN, sauf le VPN lui-même et DHCP.
De même, lorsqu'un VPN est établi, il convient d'interdire tout trafic DNS sortant vers les serveurs DNS récursifs de l'opérateur mobile / fournisseur Wi-Fi, car certaines applications les utilisent au lieu de ceux proposés par le service de VPN, ce qui fait fuiter une partie de la vie privée.
AFWall+ permet uniquement d'autoriser / bloquer des logiciels. Le premier besoin est satisfait (bloquer tout en sortie, autoriser quelques logiciels via le VPN), pas le deuxième. Pour le satisfaire, il faut utiliser des scripts personnalisés.
On peut saisir un script perso directement dans l'interface d'AFWall+, sauf que c'est pénible et long. Je choisis donc de l'écrire dans un fichier séparé. Voici mon script pour bloquer le trafic à destination des serveurs DNS récursifs de Numericable :
#!/system/bin/sh
# Necessary at the beginning of each script!
IPTABLES=/system/bin/iptables
# Now add your own rules...
$IPTABLES -I "afwall-vpn" -d 89.2.0.1/32 -j "afwall-reject"
$IPTABLES -I "afwall-vpn" -d 89.2.0.2/32 -j "afwall-reject"
# Don't
# exit 0
Quelques commentaires :
su
) Android (j'utilise le classique Terminal Emulator pour y accéder) pour voir les chaînes disponibles à un instant T ;set -e
(voir ici) ne semble pas être disponible. Dommage. Plus surprenant, un « exit 0 » en fin de script pour signifier que tout s'est bien passé conduit AFWall+ à déclarer que l'application des règles de filtrage a échoué…
Avec le gestionnaire de fichiers Android Ghost commander, je déplace ce script depuis ma carte SD vers le dossier /storage/emulated/0/afwall
. Pourquoi ? 1) Je n'ai pas trouvé de dossier nommé à peu près afwall dans /data/data ; 2) C'est ici qu'AFWall+ stocke ses exportations de règles (menu -> « Export ») donc, c'est lui qui a créé le dossier, donc, en l'utilisant, je me prémunis de problèmes de droits d'accès.
Plutôt qu'une carte SD, j'aurais pu utiliser adb
, oui.
Ne pas laisser ce script sur la carte SD, car, si elle n'est pas insérée lors d'un démarrage de l'ordiphone, alors les règles de filtrage ne seront pas appliquées, donc le pare-feu sera désactivé.
Dans le menu d'AFWall+ -> « Set custom script » -> « Enter custom script below », je saisis . /storage/emulated/0/afwall/<nom_script>
. Attention au point au début !
Menu AFWall+ -> « Apply ». C'est terminé. \o/