Sécurix est un modèle de poste de travail sécurisé construit sur NixOS par le département Opérateur de Produits Interministériels (OPI) de la DINUM. Il applique les recommandations de l’ANSSI pour les systèmes GNU/Linux et sert de base déclarative pour des postes multi-profils : administration de production, poste multi-agent, poste multi-niveaux, poste intranet-only. Le projet est en alpha, sous licence MIT, et publié pour que toute équipe puisse s’en inspirer ou le forker.
Cette page analyse le dépôt pour en faire ressortir le travail de sécurité concret : modules NixOS implémentés, choix d’architecture, patterns réutilisables. Je ne rejoue pas le tutoriel d’installation officiel — le but est de vous donner des points d’entrée dans le code pour apprendre et éventuellement piocher.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre le modèle de menace et la cible de Sécurix par rapport à un NixOS vanilla
- Identifier les 7 briques de sécurité principales du dépôt
- Lire le module ANSSI comme framework de compliance as code
- Savoir où regarder pour chaque besoin : durcissement noyau, FIDO2, Secure Boot, auditd, VPN
- Décider si Sécurix s’adresse à votre contexte ou si vous devez en extraire des morceaux
Pourquoi ce projet mérite attention
Section intitulée « Pourquoi ce projet mérite attention »Construire un poste d’administration conforme aux Recommandations de sécurité relatives à un système GNU/Linux — le guide ANSSI-BP-028, publié par l’ANSSI — est un travail de plusieurs mois pour une équipe. Le guide compte plus d’une centaine de règles classées en quatre niveaux : minimal, intermédiaire, renforcé, élevé.
Sécurix propose une implémentation NixOS directement utilisable et surtout auditable règle par règle. Chaque règle ANSSI devient un module Nix versionné dans Git, avec :
- une configuration déclarative qui applique la règle ;
- un script de vérification exécutable à la demande ;
- des métadonnées (sévérité, catégorie, tags) qui permettent l’inclusion ou l’exclusion en fonction du profil.
C’est l’un des rares exemples publics, dans la fonction publique française, de compliance as code appliqué à un OS complet. Pour une équipe qui découvre NixOS dans un contexte sécurisé, c’est un gisement d’idées.
Modèle de menace visé
Section intitulée « Modèle de menace visé »Sécurix s’adresse à un poste physique d’administrateur qui doit accéder à des environnements de production ou à des systèmes sensibles. Le modèle de menace implicite comprend :
- le vol ou la perte du poste (chiffrement complet du disque, déchiffrement par clé matérielle) ;
- la compromission applicative (durcissement noyau contre Spectre / Meltdown / MDS, isolation des montages) ;
- la compromission d’un utilisateur (FIDO2 comme auth principale, auditd R33, bastion mode) ;
- la dérive de configuration (NixOS : tout est déclaratif et régénéré à chaque déploiement) ;
- la chaîne de boot (Secure Boot avec PK/KEK custom, initrd signé).
Ce qu’il ne vise pas explicitement (à ce stade) : environnement multi-tenant type terminal partagé, ou postes nomades avec contraintes hors-ligne prolongées.
Les sept briques de sécurité
Section intitulée « Les sept briques de sécurité »En parcourant le dossier modules/, sept familles ressortent.
1. Module ANSSI — compliance as code
Section intitulée « 1. Module ANSSI — compliance as code »Le dossier modules/anssi/ contient le cœur de la démarche. Chaque règle ANSSI est définie comme une entrée Nix avec ce schéma :
R8 = { name = "R8_MemoryBootOptions"; anssiRef = "R8 – Paramétrer les options de configuration de la mémoire"; description = "Set memory options at boot"; severity = "intermediary"; category = "base"; config = _: { /* options NixOS qui appliquent la règle */ }; checkScript = pkgs: pkgs.writeShellScript "check-R8" '' # vérification runtime '';};Le fichier options.nix expose un framework complet :
- Niveaux :
minimal,intermediary,reinforced,high— mappés sur un ordre numérique (0 à 3). - Catégories :
base,client,server. - Exclusions documentées via
security.anssi.exceptions.<RuleID>.rationale. Les règlesR37(pas de MAC sous NixOS),R45-R49(SELinux/AppArmor non supportés) sont explicitement écartées avec motif, pas silencieusement ignorées. - Traçabilité des règles non appliquées : le framework classe chaque règle non active en
tag,category,level,architectureouunknown.
C’est exactement le genre de cadre qu’un auditeur veut voir : non pas « tout est bon », mais « voici ce qui est appliqué, ce qui est écarté, et pourquoi ».
2. Durcissement noyau (R8)
Section intitulée « 2. Durcissement noyau (R8) »Le fichier modules/anssi/kernel-options.nix applique onze paramètres noyau au boot :
boot.kernelParams = [ "l1tf=full,force" # L1 Terminal Fault, désactive SMT "page_poison=on" # empoisonne les pages libérées pour détecter les fuites "pti=on" # Page Table Isolation (Meltdown) "slab_nomerge=yes" # complique les heap overflows "slub_debug=FZP" # debug allocateur slab "spec_store_bypass_disable=seccomp" # Spectre v4 "spectre_v2=on" "mds=full,nosmt" # MDS + désactivation SMT "mce=0" # kernel panic sur Machine Check non corrigé "page_alloc.shuffle=1" # randomisation allocateur pages "rng_core.default_quality=500" # initialisation CSPRNG via TPM];Chaque paramètre est commenté avec sa motivation sécurité. Le checkScript associé relit /proc/cmdline et vérifie la présence effective — utile pour détecter une dérive après installation manuelle.
3. Chiffrement complet du disque (LUKS + FIDO2)
Section intitulée « 3. Chiffrement complet du disque (LUKS + FIDO2) »Le fichier modules/filesystems/securix_v2.nix pilote le partitionnement via disko. Structure clé :
- ESP (
/boot) : 1 Go FAT32. - Recovery (
/recovery) : 2 Go FAT32 — partition de secours. - LUKS : reste du disque, avec :
enrollFido2 = true: déchiffrement au boot par clé FIDO2 (Yubikey) ;enrollRecovery = true: clé de secours générée à l’installation ;allowDiscards = true: support TRIM pour SSD.
- Btrfs avec sous-volumes
/home,/var,/nixet options de montage durcies :nosuid,nodev,noatime,compress=zstd.
Le choix nosuid,nodev sur /home bloque l’exécution de binaires setuid et l’accès aux device nodes depuis les données utilisateur — une défense classique mais trop souvent oubliée.
4. Authentification sans mot de passe (FIDO2)
Section intitulée « 4. Authentification sans mot de passe (FIDO2) »Le fichier modules/pam/u2f.nix configure PAM pour que la clé matérielle remplace le mot de passe comme auth principale :
security.pam.u2f = { enable = true; control = "sufficient"; # U2F suffit, pas de mot de passe requis settings = { inherit (cfg) origin; appid = cfg.appId; authfile = "/etc/u2f-mappings"; cue = true; # avertit l'utilisateur de toucher la clé };};Le contrôle sufficient est le détail intéressant : la clé FIDO2 seule suffit pour ouvrir la session. Le mot de passe reste disponible uniquement comme mécanisme de secours, pas comme auth normale. L’appId et l’origin sont paramétrables pour que les clés restent liées à un parc identifié (pam://acme-corp-workstations).
Complément côté Yubikey : modules/security-keys.nix embarque yubikey-manager, yubikey-personalization, pcscd et yubikey-agent pour SSH.
5. Secure Boot avec PK/KEK custom
Section intitulée « 5. Secure Boot avec PK/KEK custom »Le README mentionne l’enrôlement centralisé pour Secure Boot avec gestion PK/KEK. Dans la pratique, cela signifie que les clés de signature UEFI ne sont pas celles de Microsoft : l’organisation génère sa propre Platform Key et Key Exchange Key, signe son initrd et son kernel, et configure l’UEFI du poste pour ne reconnaître que cette chaîne.
C’est la seule façon crédible de garantir que l’initrd chargé au boot — celui qui déchiffre le LUKS avec FIDO2 — n’a pas été remplacé entre deux démarrages.
6. Audit (R33)
Section intitulée « 6. Audit (R33) »Le fichier modules/auditd.nix active auditd avec une politique de rotation et d’alerte :
space_left = 10%space_left_action = ignoreadmin_space_left = 5%admin_space_left_action = emailaction_mail_acct = <adminEmail>num_logs = 10max_log_file = 100max_log_file_action = rotateLa règle audit par défaut (-a exit,always -F arch=b64 -S execve) trace tous les execve — utile comme baseline. Le commentaire TODO: dans le code liste les prochains traçages prévus : accès à l’audit lui-même, mounts, clés USB, modules noyau, kexec, interfaces réseau, Thunderbolt.
7. VPN, bastion, modes et profils
Section intitulée « 7. VPN, bastion, modes et profils »Au-delà du durcissement, Sécurix isole le poste dans des modes d’usage :
modules/vpn/: trois implémentations VPN au choix — IPsec, WireGuard, Netbird — avec leurs configurations NetworkManager dédiées.modules/bastion/: mode bastion, accès restreint à l’infrastructure.modules/admins/etmodules/superadmins/: séparation admin / superadmin, avec privilèges gradués.modules/developer-mode/: mode développeur, activé explicitement, avec les compromis assumés.modules/http-proxy/: auto-détection du proxy sortant avec bascule, utile pour un poste nomade.
Le module modules/self.nix gère la notion de selfDescriptionType : un profil peut être "user", "machine" ou "both", ce qui découple l’identité de la machine de l’identité de l’agent qui l’utilise. C’est ce qui permet le poste multi-agent.
Tester Sécurix localement
Section intitulée « Tester Sécurix localement »Le dossier tests/ expose deux suites bâties sur pkgs.testers.nixosTest, qui boote une VM QEMU et y exécute un script Python. Leur contenu est minimal :
securix.wait_for_unit("default.target")securix.succeed("cat /etc/os-release | grep securix")Ce sont des smoke tests : le système boote, l’identité Sécurix est présente. Les checkScript déclarés par chaque règle ANSSI ne sont pas appelés automatiquement — ils sont faits pour être lancés à la main sur une machine installée. Retenez-le avant de lire « test ANSSI passed ».
Prérequis
Section intitulée « Prérequis »J’ai validé la procédure dans une VM Debian 12 + Nix 2.34, cohérente avec le setup du lab NixOS Lab 1.
- CPU : 4 cœurs. 2 fonctionnent, mais allongent le build à 25-30 min.
- RAM : 8 GB conseillés. 4 GB swappent dès la phase de fish-completions.
- Disque : 25 GB libres — le
/nix/storeatteint ~19 GB au premier run. - KVM :
/dev/kvmaccessible, nested virt activée côté hyperviseur (/sys/module/kvm_intel/parameters/nested=Y).
Commandes
Section intitulée « Commandes »git clone https://github.com/cloud-gouv/securix.gitcd securix
# Toute la suitenix-build -A tests
# Ou un test précis, plus rapide à itérernix-build -A tests.minimal -j 4 --cores 4nix-build -A tests.anssi-minimal -j 4 --cores 4Au premier run, Nix télécharge le channel nixos-25.11 et construit la closure complète (kernel durci, Sway, auditd, pam, luks…). Les runs suivants sont à chaud.
Lire le résultat
Section intitulée « Lire le résultat »ls -la result# → result -> /nix/store/<hash>-vm-test-run-minimal
cat result/test.log | tail -30Le test.log contient dmesg, journald, et la trace de chaque succeed/fail du script Python. Sur un échec, l’assertion ratée apparaît en clair avec les logs kernel qui la précèdent.
Ce que j’ai mesuré
Section intitulée « Ce que j’ai mesuré »Trois configurations exécutées dans la même VM (4 vCPU / 8 GB / /nix/store initialement vide) :
| Config | Test script | Boot systemd | Kernel params R8 appliqués |
|---|---|---|---|
minimal (sans ANSSI) | 70.49 s | 66 s (2.8 + 22.3 + 40.8) | ❌ aucun |
anssi-minimal (level = "minimal") | 67.21 s | 63 s (2.7 + 20.9 + 39.6) | ❌ aucun (R8 filtré) |
anssi-intermediary (level = "intermediary", patch maison) | 124.44 s | 119 s (3.5 + 41.2 + 74.1) | ✅ les 11 params |
Trois constats tirés de ces mesures.
Le nom de test anssi-minimal est piégeur. R8 a severity = "intermediary" ; la logique de options.nix (levelExclusion = severity > level, mappés 0..3) exclut tout ce qui dépasse minimal. Résultat : le cmdline kernel de ce test contient juste les défauts NixOS (console=…, lsm=landlock,yama,bpf), pas le moindre l1tf=, page_poison=, spectre_v2=, etc. Un système level = "minimal" n’applique quasiment rien des règles ANSSI.
Le framework de niveaux fonctionne. En patchant tests/anssi-minimal.nix en level = "intermediary", le kernel-params du système contient bien les 11 paramètres R8 attendus. Passer du minimal au durcissement réel demande donc une seule ligne de profil : security.anssi.level = "intermediary" ou "reinforced".
Le durcissement a un vrai coût de boot. intermediary quasi-double le temps de démarrage (66 s → 119 s). Le surcoût est concentré sur l’initrd (+19 s) et le userspace (+33 s) — le kernel lui-même reste rapide. Cause principale : le SMT désactivé par mds=full,nosmt réduit de moitié le parallélisme systemd. Sur un SSD NVMe réel, le ratio sera moins brutal, mais comptez 1 à 2 min de boot sur une machine ANSSI-durcie, c’est assumé par le modèle de menace.
Lire le code pour apprendre
Section intitulée « Lire le code pour apprendre »Sécurix est un excellent corpus d’étude pour qui vient de découvrir NixOS via les labs de cette section. Points d’entrée selon le sujet :
| Sujet | Fichier à lire en premier |
|---|---|
| Framework de compliance | modules/anssi/options.nix |
| Règles ANSSI déclaratives | modules/anssi/preboot.nix, kernel-options.nix |
| LUKS + FIDO2 + recovery | modules/filesystems/securix_v2.nix |
| PAM U2F | modules/pam/u2f.nix |
| auditd minimal | modules/auditd.nix |
| Profil par défaut | modules/self.nix |
| Variantes matériel | hardware/ (ThinkPad X13/X280/T14, EliteBook, Latitude…) |
| Tests NixOS | tests/anssi-minimal.nix |
Verdict — utilisable en l’état ?
Section intitulée « Verdict — utilisable en l’état ? »Non pour un remplacement direct de poste de production, oui comme base à forker et comme gisement de patterns. Le projet est en alpha et le README est explicite : aucun support n’est proposé.
Par brique
Section intitulée « Par brique »| Brique | Verdict | Commentaire |
|---|---|---|
Module ANSSI (modules/anssi/) | ✅ Réutilisable | Framework autoportant, copy-pastable selon le README. |
| Kernel hardening (R8) | ✅ Fonctionne | Les 11 params s’appliquent dès level = "intermediary". |
| PAM U2F / FIDO2 | ✅ Mature | ~50 lignes, control = "sufficient" bien pensé. |
| LUKS + FIDO2 + recovery | ✅ Mature | Basé sur disko, montages btrfs durcis (nosuid,nodev). |
| auditd (R33) | ⚠️ Partiel | Rotation + mail OK, mais règles additionnelles en TODO. |
| Ruleset ANSSI complet | ⚠️ Quasi vide | Sur ~100 règles BP-028, une dizaine codée. Le reste est commenté dans ruleset.nix. |
| Test suite | ⚠️ Smoke tests | Boot + branding, pas la compliance runtime. |
| MAC (SELinux / AppArmor) | ❌ Non applicable | NixOS ne supporte pas SELinux ; R37, R45-R49 écartées avec motif. |
| Profils matériel | ⚠️ Limités | 7-8 modèles de laptop, seul x280 testé par CI. |
| Onboarding / inventory | ❌ Annoncé | phone home en développement. |
Le vrai blocage
Section intitulée « Le vrai blocage »Il n’est pas technique. C’est la combinaison taux de complétion du ruleset + absence de validation runtime en CI. Tant que les checkScript individuels ne tournent pas dans le test, impossible de prouver la conformité à un auditeur — or c’est précisément sur cette preuve que repose la promesse « NixOS conforme ANSSI ».
Ce que je recommanderais
Section intitulée « Ce que je recommanderais »- Extraire le module ANSSI pour l’intégrer dans votre propre profil NixOS. C’est la partie la plus mature et la seule vraiment plug-and-play.
- Forker pour construire un poste d’admin interne, en acceptant de compléter
ruleset.nixrègle par règle selon votre cible de conformité. - Ne pas déployer
maintel quel en production — attendre une release stable ou avoir une équipe pour suivre le projet.
Trois extensions transformeraient la test suite en outil réellement exploitable en CI : appeler les checkScript dans testScript, tester tous les variants matériels, tester au moins un VPN. La structure de tests/default.nix (un simple attrset) rend l’ajout trivial — une entrée = un scénario.
À retenir
Section intitulée « À retenir »- Sécurix est un poste NixOS open source de la DINUM, aligné BP-028, en alpha sous MIT.
- Le vrai apport est le module ANSSI : compliance as code avec sévérité, catégories, exceptions motivées et
checkScript— réutilisable hors Sécurix. - Côté durcissement effectif : R8 (11 params kernel Meltdown/Spectre/L1TF/MDS), FIDO2 comme auth principale (
pam.u2f.control = "sufficient"), LUKS + FIDO2 + recovery viadisko, auditd R33 avec mail et trace desexecve. - Piège du nom
anssi-minimal:level = "minimal"filtre R8 et tout ce qui estintermediary. Pour un durcissement effectif, passer à"intermediary"— et doubler le temps de boot (66 s → 119 s mesurés en VM, SMT désactivé oblige). - Limites structurelles : ~10 règles BP-028 réellement codées sur ~100, test suite en smoke tests, pas de MAC (NixOS).
- Verdict : base à forker et compléter, pas produit prêt pour la prod.