Quelques bricoles pour renforcer Linux : ASLR, lockdown, PTI (Spectre/Meltdown)...

Linux a lui aussi droit à son confinement ! Nous allons voir quelques paramètres non-exhaustifs pour renforcer sa sécurité.

Quelques bricoles pour renforcer Linux : ASLR, lockdown, PTI (Spectre/Meltdown)...

Avertissement

Je préfère prévenir avant de commencer :

  • Cet article n'a pas pour ambition de se substituer aux documentations officielles, ni de se suffire à lui-même. J'y partage simplement quelques mesures non-exhaustives à compléter et dont leur impact est à mesurer individuellement.
  • Tous les paramètres (ainsi que linux-hardened) sont à tester un par un, idéalement sur une machine d'essai (pourquoi pas une VM) avant de les mettre en place sur vos machines personnelles.
  • Enfin, ces "bricoles" sont ce qu'elles sont, rien de plus : bien qu'elles pourraient augmenter drastiquement la protection de Linux contre d'éventuels exploits, elles ne "réparent" pas ses déficiences.

Des bricoles ne remplacent pas la robustesse d'un modèle de sécurité. Elles sont toutefois bienvenues pour rendre plus difficile certaines exploitations.

Contexte

Il raconte sa vie, passez si c'est trop long...

Il n'y a pas si longtemps, j'évoquais l'insécurité du modèle Linux desktop en insistant parfois sur la surface d'attaque que représente Linux. Pour résumer (en vrac) : trop de privilèges (laissant place à une guerre philosophique), noyau monolithique, attaques possibles liées à la mémoire, etc.

Récemment, j'ai expliqué comment mettre en place des solutions d'isolation particulières telles qu'avec gVisor. Si vous pensiez que ce n'était réservé qu'aux conteneurs, détrompez-vous : gVisor (sous réserve de maturation du projet) pourrait tout à fait subvenir aux besoins d'applications du quotidien. GrapheneOS vise à utiliser gVisor dans une optique de moyen terme.

Fuchsia, perçu comme le remplaçant d'Android et qui utilisera un microkernel (kind of), utilisera sans doute une couche de compatibilité similaire à gVisor : une émulation de l'API Linux dans l'espace utilisateur. On pourrait peut-être y voir dans gVisor les fondations de ce travail...

Je ne suis personnellement plus vraiment utilisateur de Linux sur desktop, mais j'ai entendu les critiques de certains par rapport à mon premier article (qui a été mis à jour continuellement). Parmi elles, j'ai vu que "je ne proposais rien" : en effet, je suggère implicitement de passer sur un OS avec un vrai modèle de sécurité, mais je laisse la porte ouverte à une amélioration future.

Malgré tout, j'utilise Linux sur mes serveurs (et mon smartphone Android), et je ne vois pas comment je pourrais m'en passer. Donc non, je ne m'en fous pas ; et oui, j'ai quelques "solutions" à proposer dont je ne suis que le médiateur, je n'ai rien inventé ici.

linux-hardened

C'est quoi ?

Pour les plus téméraires d'entre vous, vous pouvez simplement utiliser linux-hardened. C'est un projet qui vise à fournir des patchs relativement légers à Linux et renforcent sa sécurité sans non plus sacrifier toutes les performances. Si vous aviez lu mes articles, ce n'est pas la première fois que je mentionne linux-hardened : en effet, ce projet émane directement de GrapheneOS qui l'utilise.

anthraxx/linux-hardened
Minimal supplement to upstream Kernel Self Protection Project changes. Features already provided by SELinux + Yama and archs other than multiarch arm64 / x86_64 aren't in scope. Only tags have ...

Voyez linux-hardened comme une alternative à grsecurity/PaX.

Ah, vous vous souvenez de grsecurity ? Malheureusement, leurs patchs sont devenus payants.

"Hardened", comment ça ?

Vous allez rire mais il n'y a pas vraiment de page qui recense toutes les améliorations apportées, et c'est bien dommage ; les mainteneurs réfèrent souvent à l'historique des commits.

J'ai fait un petit travail de recensement et voici ce que vous pouvez noter :

  • Beaucoup d'améliorations notables à ASLR (Address Space Layout Randomization), une technique de défense largement employée qui place aléatoirement les adresses mémoires d'un processus pour rendre plus difficile les attaques de type buffer overflow.
  • Restrictions d'accès par les capabilities
  • Renforcement de l'allocation Slab
  • Compilation avec des paramètres sûrs : FORTIFY_SOURCE et Stack Smashing Protection pour limiter des attaques de buffer overflow.
  • Le projet suggère toutefois d'être compilé avec clang plutôt que GCC pour bénéficier de CFI/ShadowCallStack, dont seul Google tire à ce jour profit pour les kernels des Google Pixel.
  • Des paramètres par défauts considérés comme robustes et sûrs, dont la plupart sera vue ultérieurement.
A noter pour CFI : son support sera intégré upstream pour la version 5.13. Cela devrait rendre son utilisation bien plus simple !

Au fond, ça reste un patch assez minimaliste qui ne doit pas être utilisé tout seul. Par exemple, le projet vous suggère de mettre en place vous-même votre MAC (mandatory access control) tel que AppArmor ou SELinux.

Cela dit, c'est un point d'entrée robuste !

Stable ou LTS (long term support) ?

linux-hardened propose des patchs pour les versions stables et LTS de Linux.

  • Les versions stables sont plus souvent mises à jour en termes de fonctionnalité (parfois de sécurité) : elles peuvent introduire, outre la surface d'attaque supplémentaire, des bugs et donc des failles potentielles.
  • Les LTS sont moins souvent mises à jour, mais vous avez des fonctionnalités stables et souvent éprouvées.

C'est une question de compromis, mais j'aurais tendance à suggérer les LTS sur des machines qui doivent rester "stables" justement, et les versions "stable" pour tout le reste. Dans tous les cas, ne négligez pas les mises à jour de votre kernel (surtout sur serveur, je vous vois avec vos concours d'uptime).

Bonus : lien intéressant sur le sujet.

Installation

AVERTISSEMENT : linux-hardened peut "casser" des logiciels que vous utilisez donc il faut absolument tester en amont, et prévoir un downgrade en cas de besoin.

Si vous utilisez Arch Linux, c'est votre jour de chance : linux-hardened y est proposé comme paquet. Autrement, on va devoir compiler.

Pour ce tutoriel très bref, j'utilise Debian Buster à jour configuré pour recevoir des paquets des backports. Ces derniers vous permettent de bénéficier de certains paquets à jour et de la même qualité que les paquets standards, donc je vous suggère très fortement de l'utiliser.

Commençons par installer les prérequis à la compilation de Linux (avec permissions administrateur) :

apt install build-essential xz-utils libncurses-dev bison flex libssl-dev libelf-dev

Maintenant petit point particulier, la version de pahole sur Debian Buster est trop vieille pour compiler des versions récentes. Donc on va l'installer depuis les backports :

apt install -t buster-backports dwarves

Désormais les permissions administrateur ne seront plus nécessaires excepté pour l'installation proprement dite. Maintenant vous pouvez créer un dossier de travail, y télécharger les sources de la version souhaitée et l'extraire tout en vérifiant son authenticité :

VERSION_LINUX=5.11.1 # à remplacer
mkdir linux-hardened && cd linux-hardened
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${VERSION_LINUX}.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${VERSION_LINUX}.tar.sign
xz -d -v linux-${VERSION_LINUX}.tar.xz
gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org
gpg2 --verify linux-${VERSION_LINUX}.tar.sign
tar xvf linux-${VERSION_LINUX}.tar

VERSION_LINUX est à changer selon vos besoins et la disponibilité des patchs de linux-hardened (cf. les releases sur Github). Tant qu'à faire, téléchargeons le patch correspondant et appliquons-le :

wget https://github.com/anthraxx/linux-hardened/releases/download/${VERSION_LINUX}-hardened1/linux-hardened-${VERSION_LINUX}-hardened1.patch
cd linux-${VERSION_LINUX}
patch -p1 < ../linux-hardened-${VERSION_LINUX}-hardened1.patch

Enfin, il ne reste que des étapes classiques pour entamer la compilation proprement dite. Vous pouvez partir d'une .config pré-existante (facultatif) :

cp -v /boot/config-$(uname -r) .config

Puis lancer menuconfig, apporter vos modifications si nécessaires, enregistrer puis quitter :

make menuconfig

Si vous avez utilisé un .config de kernel Debian, pensez à commenter deux lignes dans ce fichier sans quoi la compilation ne commencera pas :

CONFIG_MODULE_SIG_KEY
CONFIG_SYSTEM_TRUSTED_KEYS

Au lieu de fournir un certificat qui n'existera pas à moins de l'importer manuellement, vous allez en générer un vous-même.

La compilation proprement dite peut être lancée :

make -j x

"x" à remplacer par le nombrer de cœurs CPU que vous souhaitez dédier à la compilation.

Le temps de se faire un thé vert (ou deux), la compilation devrait se finir sans obstacles et vous devez passer en administrateur pour installer le kernel :

make modules_install
make install

Normalement GRUB (ou apparenté) devrait se configurer tout seul, mais si ce n'est pas le cas, passez un coup de update-grub. Enfin, on redémarre et on regarde si le kernel est bien installé :

$ uname -a
Linux xxxxx 5.11.11-hardened1 #1

Bien, c'est fait !

Linux vanilla : quoi faire ?

Personnellement, je n'utilise pas linux-hardened (sauf sur GrapheneOS du coup) même si je trouve le projet très intéressant. Mais c'est un peu overkill pour moi d'autant plus que je fais très attention à la surface que j'expose à mes applications.

Je suis aussi un gros flemmard qui n'a pas envie de compiler à chaque mise à jour et je préfère faire confiance à ma distribution pour cela. Idéalement, je vous conseille quand même de compiler !

Un noyau récent

Tout d'abord je vous conseille d'utiliser une version LTS/stable récente, par exemple si vous utilisez Debian Buster, passez par les backports :

apt install -t buster-backports linux-image-amd64

Je vous réfère au paragraphe plus haut concernant le compromis stable/LTS.

Les kernel proposés par les distributions sont généralement de bonne qualité et disposent d'une maintenance dans l'ordre du possible. Seulement le système de CVE n'est pas infaillible, y compris pour Linux, ce qui peut brouiller le travail de backports et c'est ce que je tentais d'expliquer dans un article précédent. C'est pour cette raison que je privilégie des kernel récents.

Compiler ou pas ?

Vous pouvez tout à fait reprendre les instructions précédentes, exclure l'étape du patch et compiler votre propre kernel. Cela a plusieurs avantages :

  • Vous pouvez réduire la surface d'attaque en configurant un kernel minimal avec seulement ce dont vous avez besoin.
  • Certains types d'attaques exploitent des emplacements connus de pointeurs : en compilant votre propre kernel, vous aurez des pointeurs uniques ce qui rend ces attaques reposant sur des emplacements définis plus difficiles.
  • Ne pas dépendre d'un mainteneur pour mettre à jour son kernel (il faut), mais à voir si l'effort vaut la peine pour vous.

Si vous décidez de compiler, cette page vous indiquera des options de configuration intéressantes à mettre en place.

Linux Security Modules & MAC

LSM (Linux Security Modules) est un framework de Linux qui permet par exemple aux MAC (AppArmor, SELinux, TOMOYO) de fonctionner. Ce sont des outils puissants qui demandent d'être particulièrement intégrés au kernel, et sont également très complexes, si bien que ce ne sera pas dans l'intérêt de ce billet d'aller dans le détail.

Un MAC permet de restreindre un programme à ses accès spécifiques (chemins, ports, etc.) permettant ainsi d'éviter une catastrophe en cas de compromission dudit programme.

Sachez que SELinux permet un contrôle bien plus précis, mais il est aussi bien plus complexe à maîtriser. AppArmor est plus simple, mais moins précis. Pour le moment, je conseille d'utiliser une distribution qui s'intègre avec l'un des deux : Debian Buster utilise AppArmor par exemple.

Je vous réfère en conséquence à la documentation de votre distribution.

Pour savoir quels LSM sont utilisés :

# cat /sys/kernel/security/lsm
lockdown,capability,yama,apparmor,tomoyo

Linux se fait aussi confiner (lockdown mode)

Depuis sa version 5.4, Linux embarque une nouvelle fonctionnalité : le mode lockdown. Proposé par l'ingénieur de Google Matthew Garett, ce mode a mis un bon bout de temps avant d'être intégré upstream. Et pour cause, il s'attaque à un problème philosophique de taille : les pleins privilèges.

Traditionnellement, la frontière entre le kernel et l'espace utilisateur est mince. Beaucoup d'informations sensibles s'échangent de l'un à l'autre, et en particulier, l'espace utilisateur a un un pouvoir de modification puissant sur le kernel notamment par l'utilisateur root.

Mais est-ce que root doit vraiment avoir les "pleins pouvoirs", même sur Linux proprement dit ?

Et c'est Linus Torvalds lui-même qui était très critique de l'idée de verrouiller des aspects du kernel, avant de céder après des années. Ce mode a été plus ou moins implémenté auparavant par certaines distributions.

Un des facteurs qui a poussé à l'inclusion de ce mode est l'avènement de l'UEFI Secure Boot. En effet, son modèle de sécurité n'a que très peu de sens si l'userspace peut à ce point altérer en profondeur le système et, dans le cas d'un malware, gagner facilement la persistance au démarrage.

Entre autres, le mode lockdown agit de plusieurs façons en désactivant des fonctionnalités qui peuvent altérer le noyau :

  • Intransigeance sur la signature des modules utilisés (potentiel conflit avec dkms, je vous préviens d'avance)
  • Accès restreint à /dev/mem, /dev/kmem et /dev/port
  • Verrouillage du DMA (Direct Memory Addressing) par l'insertion d'appareils (PCI par exemple), etc.

Tout ce qu'il faut retenir, c'est que le lockdown empêche généralement à l'userspace (même root) d'altérer le fonctionnement du kernel.

lockdown possède deux modes :

  • integrity : c'est le niveau d'implémentation de base qui restreint l'accès de l'userspace au kernel (bye Kexec, BPF)
  • confidentiality : c'est un superset du mode précédent qui empêche même root d'accéder à des informations sensibles (comme des clés EVM)

Le lockdown peut être activé dans un paramètre de démarrage (par exemple GRUB_CMDLINE_LINUX_DEFAULT dans /etc/default/grub):

lockdown=integrity

A l'issue d'un redémarrage, on peut vérifier que le lockdown est actif :

# cat /sys/kernel/security/lockdown
none [integrity] confidentiality

Attention, donc : le mode lockdown empêche de charger des modules qui ne sont pas signés, certaines fonctionnalités de fonctionner (Kexec, BPF), mais apparemment les capacités d'hibernation également. C'est une option intéressante à activer au cas par cas.

Autres paramètres de démarrage

Ces paramètres peuvent être appliqués de la même façon que le lockdown.

Cette liste est inspirée par celle gracieusement fournie par madaidan, chercheur en sécurité et développeur chez Whonix. Il fournit davantage de paramètres, mais j'en ai sélectionné quelques uns seulement qui n'impactent pas autant les performances.

slab_nomerge

Allow slab_nomerge to be set at build time (openwall.com)

vsyscall=none

Obsolètes et remplacés par vDSO

slub_debug=FZ

Permet de prévenir des corruptions liées à slab (des pages contiguës en mémoire utilisés dans des opérations de cache).

init_on_alloc=1 init_on_free=1

Permet de prévenir des fuites d'information sensibles, et des vulnérabilités Use After Free (qui permettent des executions de code arbitraire via des pointeurs qui ne pointent pas vers un objet propre).

page_alloc.shuffle=1

Randomise l'allocation de pages en mémoire, peut aussi bénéficier en performances.

debugfs=off

Désactive debugfs qui expose des informations sensibles.

pti=on

PTI (anciennement KAISER) est l'acronyme de Page-Table Isolation, une stratégie de mitigation de la fameuse vulnérabilité Meltdown, et qui plus généralement protège des bypass de l'ASLR mentionné plus haut. Cette mitigation a cependant un coût théorique en performance (variable selon l'architecture CPU et la tâche, donner un % n'a pas vraiment de sens).

Je vous conseille par ailleurs (chez Debian en tous cas) l'excellent outil spectre-meltdown-checker qui permet d'avoir une vue globale de toutes les vulnérabilités liées à Spectre/Meltdown et vos mitigations mises en place.

Exemple de PTI en action

Une fois ces paramètres ajoutés, vous pouvez redémarrer et tester par exemple avec cet utilitaire (qui vous permettra de tester lockdown par la même occasion car il vous hurlera dessus sur certains points...).

Réduction de la surface d'attaque

Il faut éviter autant que possible la présence de modules. Même si ceux-ci ne sont pas chargés, vous n'êtes pas à l'abris d'une possibilité où un utilisateur peut causer indirectement le chargement d'un module vulnérable.

Si vous compilez votre propre kernel, vous êtes plus qu'encouragé à le délester d'autant de modules inutiles que possible. Une alternative (valable pour ceux qui ne compilent pas leur kernel) est de les blacklister grâce à des fichiers de configuration qui seront lus dans /etc/modprobe.d :

install module /bin/false
/etc/modprobe.d/blacklist.conf

Voici plusieurs exemples (fournis par madaidan) :

install dccp /bin/false
install sctp /bin/false
install rds /bin/false
install tipc /bin/false
install n-hdlc /bin/false
install ax25 /bin/false
install netrom /bin/false
install x25 /bin/false
install rose /bin/false
install decnet /bin/false
install econet /bin/false
install af_802154 /bin/false
install ipx /bin/false
install appletalk /bin/false
install psnap /bin/false
install p8023 /bin/false
install p8022 /bin/false
install can /bin/false
install atm /bin/false
/etc/modprobe.d/blacklist-net.conf

Cette liste bloque des protocoles réseau peu communs.

install cramfs /bin/false
install freevxfs /bin/false
install jffs2 /bin/false
install hfs /bin/false
install hfsplus /bin/false
install squashfs /bin/false
install udf /bin/false
install cifs /bin/true
install nfs /bin/true
install nfsv3 /bin/true
install nfsv4 /bin/true
install gfs2 /bin/true
/etc/modprobe.d/blacklist-fs.conf

Cette liste bloque des systèmes de fichier rarement utilisés.

install vivid /bin/false
/etc/modprobe.d/blacklist.conf

Le module vivid est lui aussi rarement utilisé.

install bluetooth /bin/false
install btusb /bin/false
/etc/modprobe.d/blacklist.conf

Désactivez le Bluetooth si vous n'en avez pas besoin !

install uvcvideo /bin/false
/etc/modprobe.d/blacklist.conf

Désactive la webcam.

Paramètres sysctl

sysctl permet de modifier certains paramètres du kernel. C'est un outil à utiliser avec précaution. Une valeur peut être changée à chaud ainsi :

sysctl -w $parametre = $valeur

Pour que la modification soit permanente, elle doit être ajoutée dans /etc/sysctl.conf ou dans un fichier dans /etc/sysctl.d.

Cette liste s'inspire de celle de madaidan mais aussi des recommandations de l'ANSSI. Je choisis d'ajuster quelques paramètres et d'en omettre quelques uns pour atteindre un compromis plus personnel. Libre à vous de vous documenter sur chacune de ces modifications (et vous devriez !).

Protections du kernel

Ces paramètres sont à ranger dans /etc/sysctl.d/51-kernel.conf.

kernel.kptr_restrict=1

Ce paramètre limite les fuites des pointeurs du kernel. Ces derniers, si leurs emplacements sont connus, permettent de faciliter des exploitations.

kernel.dmesg_restrict=1

Ce paramètre limite les données sensibles exposées par le kernel à dmesg, son outil de log.

kernel.printk=3 3 3 3

Idem que ci-dessus, mais cette fois-ci concernant les messages durant la séquence de démarrage.

kernel.unprivileged_bpf_disabled=1
net.core.bpf_jit_harden=2

Ces paramètres restreignent eBPF, et adopte la mitigation du constant blinding traditionnellement adoptée par les JIT (compilateurs à la volée) qui consiste (très succinctement) à obfusquer des valeurs dans le code compilé avec des valeurs aléatoires.

kernel.sysrq=4

SysRq (System Request) est une combinaison de touches qui permettent de mener des opérations telles que le redémarrage du système, ou d'autres opérations potentiellement dangereuses qui sont ainsi exposées à des utilisateurs non-privilégiés. C'est un problème qui ne concerne pas seulement les attaquants physiques.

Il convient donc de le restreindre au maximum. La valeur 4 désactivera la globalité des opérations possibles excepté la séquence Secure Attention Key qui permet d'accéder à root de façon sécurisée dans certaines circonstances. Si cela n'est même pas nécessaire pour vous, la valeur 0 désactivera toutes les fonctionnalités liées à SysRq.

kernel.kexec_load_disabled=1

kexec() est un appel système qui permet de démarrer un autre kernel sans redémarrage. Cette fonctionnalité peut être détournée pour charger du code malveillant, il est préférable de la désactiver.

kernel.unprivileged_userns_clone=0

Les user namespaces sont abordés dans cet article. Cette fonctionnalité est intéressante, mais expose une surface d'attaque : elle peut ainsi être restreinte à la capacité CAP_SYS_ADMIN. D'autres outils de sandboxing sont recommandés en cas d'environnements non-privilégiés.

kernel.perf_event_paranoid=3

Ce paramètre restreint l'usage de l'outil perf qui permet des mesures de performances. Utile en cas de debug, il doit être restreint autrement : ici à la capacité CAP_PERFMON.

vm.unprivileged_userfaultfd=0

userfaultfd() est un appel système qui peut être abusé également, ce paramètre le restreint à la capacité CAP_SYS_PTRACE.

Protections réseau

Ces paramètres sont à ranger dans /etc/sysctl.d/51-network.conf.

net.ipv4.tcp_syncookies=1

Ce paramètre protège contre des attaques réseaux de type SYN flood.

net.ipv4.tcp_rfc1337=1

Ce paramètre protège des time-wait assassinations.

net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1

Ces paramètres protègent des IP spoofing en validant la source des paquets qui proviennent de toutes les interfaces.

net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv6.conf.all.accept_redirects=0
net.ipv6.conf.default.accept_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0

Ces paramètres désactivent l'ICMP redirect, qui peut être utilisé pour des attaques man-in-the-middle et dans des fuites d'informations.

net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv6.conf.all.accept_source_route=0
net.ipv6.conf.default.accept_source_route=0

Idem que ci-dessus, mais cette fois-ci concernant le source routing.

net.ipv6.conf.all.accept_ra=0
net.ipv6.conf.default.accept_ra=0

Idem : ces paramètres désactivent la prise en charge des router advertisements.

net.ipv4.tcp_sack=0
net.ipv4.tcp_dsack=0
net.ipv4.tcp_fack=0

Ces paramètre désactivent TCP SACK (Selective Acknowledgments) qui peut être exploité, donc devrait être désactivé si non-utilisé.

net.ipv4.icmp_echo_ignore_all=1

Attention : ce paramètre configure le système pour ignorer toutes les requêtes ICMP. Ce n'est logiquement pas idéal pour un serveur.

Protections espace utilisateur

Ces protections sont à ranger dans /etc/sysctl.d/51-userspace.conf.

kernel.yama.ptrace_scope=2

ptrace() est un appel système utilisé pour contrôler un autre processus, sa mémoire, etc. Il est pratique pour debug, mais son usage devrait être autrement restreint à la capacité CAP_SYS_PTRACE.

vm.mmap_rnd_bits=32
vm.mmap_rnd_compat_bits=16

Ces paramètres renforcent ASLR en augmentant ses bits d'entropie. Il est toujours suggéré d'utiliser linux-hardened pour aller plus loin.

Les valeurs données sont compatibles avec une architecture 64-bits.

fs.protected_symlinks=1
fs.protected_hardlinks=1

Ces paramètres protègent les symlinks et hardlinks contre les bugs time-of-check to time-of-use.

fs.protected_fifos=2
fs.protected_regular=2

Ces paramètres empêchent la création de fichiers dans des environnements potentiellement contrôlés par un attaquant et ouverts en écriture à tout le monde (data spoofing).

Fin et documentation

Vous pouvez creuser le sujet davantage à ces adresses :

Je vous réfère également à mes précédents articles qui distillent d'autres informations, je voulais parfois éviter la redite. Celui-ci devait être un petit article sans prétention à la base, mais j'ai encore dérapé sur la longueur à certains endroits, veuillez m'excuser de mon manque de concision.

Stay safe!