Compare commits

...
45 Commits
Author SHA1 Message Date
DamienandClaude Sonnet 5 9512f781fd README : retire les prenoms des postes nommes, repo public
thomas/clara/jade/oreo/oreo-i3 apparaissaient en clair dans le
README public (Etape 4B, section "NixOS deja installe", etc.) --
ce sont des prenoms reels d'utilisateurs, n'ont rien a faire dans
un repo public sans authentification. Remplace par des mentions
generiques ("profil nomme", "<profil>"), la liste reelle reste
privee cote Nixos_Users.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:59:39 +02:00
DamienandClaude Sonnet 5 64e7112093 README : ajoute un index et la section "NixOS deja installe"
Nouvel index en tete de fichier (3 chemins d'installation + reference/dev/git).
Nouvelle section top-level "Installer depuis un NixOS deja installe" :
clone + nixos-rebuild switch --flake .#nixos-existing/-existing --impure,
pour flotte et poste nomme, plus le netrc a la main (pas d'install.sh
ici pour l'ecrire) et la maintenance qui va avec.

Simplifie l'Etape 8 de "Installer le serveur PXE" (redondant avec le
deroule de "Installer un PC", ne garde qu'un renvoi).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:56:07 +02:00
DamienandClaude Sonnet 5 8cca5d3ebb Externalise le module disque (comme nixos_users) + ajoute nixos-existing
hosts/nixos/configuration.nix importait diskos/plain.nix en dur --
deplace vers flake.nix (injection externe), meme pattern que
nixos_users/flake.nix (mkHost). Necessaire pour ajouter
nixos-existing : meme profil logiciel que "nixos", mais avec le
hardware-configuration.nix reel de la machine a la place de
diskos/plain.nix -- pour appliquer cette config sur un NixOS deja
installe, sans toucher au partitionnement existant (meme principe
que pxe-server, --impure requis).

Deplace aussi system.autoUpgrade.flake hors de hosts/common/default.nix
(etait fixe sur #nixos pour tous les profils) vers une injection par
attribut dans flake.nix : sans ca, une machine nixos-existing aurait
bascule sur l'attribut "nixos" (disko) a son prochain auto-upgrade,
avec un layout disque potentiellement incompatible avec l'existant.

Verifie : nix flake check --no-build passe pour "nixos" ; "nixos-existing"
echoue nur pour la meme raison attendue que pxe-server
(hardware-configuration.nix absent hors du vrai serveur/poste), pas
une regression. Dry-run build de "nixos" toujours complet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:53:32 +02:00
DamienandClaude Sonnet 5 3be4747eb8 README : previent que le build ISO est long, explique la difference avec l'ISO nixos.org
mksquashfs (compression du store pour l'ISO) est l'etape la plus
lente et n'affiche aucune progression -- peut donner l'impression
d'un plantage alors que ca tourne normalement (10-30+ min).

Precise aussi ce qui distingue cette ISO de celle de nixos.org :
paquets/clavier/locale du parc, et surtout install.sh qui se lance
seul au lieu d'un shell nu a completer a la main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:34:34 +02:00
DamienandClaude Sonnet 5 18b0156db3 README : remet Linux et Windows complets dans l'Etape 0, un seul endroit
Les allers-retours entre Etape 0 et la section WSL (a 550 lignes de
distance) rendaient le parcours introuvable -- la dedup poussee trop
loin cassait la lisibilite. Etape 0 contient maintenant les deux
chemins complets (Linux : clone/build/dd : Windows : clone/build/cp
+Rufus), sans avoir besoin d'aller chercher ailleurs. Les sections
Referentce/WSL ne gardent qu'un pointeur d'une ligne vers l'Etape 0
plutot que de re-expliquer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:17:03 +02:00
DamienandClaude Sonnet 5 04d6a564ef README : la section Windows ne repete plus le clone/build, deja dans l'Etape 0
Construire l'ISO est identique a Linux, meme commande nix build --
aucune raison de le repeter sous un titre "depuis Windows". Ne
garde que la vraie difference (la gravure, Rufus au lieu de dd) et
renvoie a l'Etape 0 pour le reste. Retire aussi le doublon dd/usbipd
qui se repetait deux fois dans la meme section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:14:34 +02:00
DamienandClaude Sonnet 5 ab3f3eba51 README : condense la section ISO Windows en etapes commande-par-commande
Trop de justifications intercalees entre les commandes (pourquoi le
lien symbolique, pourquoi WSL ne voit pas la cle...) rendaient le
parcours illisible. Reduit aux commandes dans l'ordre (clone, build,
cp, Rufus, retour a l'etape 1), les explications passent en une
seule ligne de contexte a la fin. Corrige aussi l'ordre clone/cd qui
etait invalide (cd vers un dossier pas encore clone).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:13:25 +02:00
DamienandClaude Sonnet 5 1ccc467964 README : rend "Installer un PC" lineaire de A a Z (clone -> ISO -> boot -> install)
Avant : "Installer un PC" commencait directement au boot PXE, en
supposant le support d'installation deja pret. Le clone+build+dd
vivait seulement sous Referme > "Graver la cle USB", deconnecte --
personne ne tombait sur le chemin complet en partant de zero.

Ajoute une Etape 0 (USB : clone/build/dd -- PXE : rien a faire) au
debut de "Installer un PC", et adapte l'Etape 2 pour couvrir les
deux supports (Network Boot ou Boot from USB, meme menu ensuite).
Degonfle "Graver la cle USB" en Reference pour ne garder que le
contexte/pourquoi, les commandes ne vivent plus qu'a un seul endroit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:11:17 +02:00
DamienandClaude Sonnet 5 6d6aa7a397 Ajoute .gitignore, corrige README : result ne s'ouvre pas depuis Windows
Verifie en pratique : le lien result (pointe dans /nix/store, pur
Linux) apparait comme un objet 0 Ko depuis Windows/Explorer, meme
via \wsl$\... -- la mention precedente ("accessible aussi depuis
Windows via \wsl$") etait fausse. Seule une vraie copie (cp, pas le
lien) produit un fichier utilisable depuis Windows. Recommande de
copier directement dans le dossier du repo (deja visible, deja
ignore par git).

.gitignore n'existait pas du tout dans ce repo -- ajoute (result,
result-*, *.iso) pour eviter qu'un lien Nix ou une ISO de plusieurs
Go finisse committee par un futur `git add -A`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:04:44 +02:00
DamienandClaude Sonnet 5 46e66ba487 README : oreo-i3 manquait dans les etapes 4/4B, ajoute le lien manquant depuis Graver la cle USB
install.sh liste oreo-i3 dans sa liste blanche depuis un moment
deja, mais les etapes 4/4B du README (choix du profil au boot)
n'avaient jamais ete mises a jour -- oreo-i3 y etait invisible.

Ajoute aussi le vrai manque signale : "Graver la cle USB" dit que
c'est la meme ISO pour tout le monde mais ne renvoie jamais vers le
deroule reel (etape 4B) -- d'ou la confusion "comment je build l'ISO
jade/oreo-i3" alors qu'il n'y a qu'une seule commande de build, le
choix se fait au boot, pas a la construction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:54:51 +02:00
DamienandClaude Sonnet 5 ff5db5f054 README : ajoute un renvoi vers la section Windows depuis "Graver la cle USB"
Le contenu Windows/WSL existait deja mais loin de "Graver la cle
USB" (tout en bas, dans la section WSL) -- personne ne tombe dessus
en lisant la section principale. Ajoute un pointeur explicite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:50:24 +02:00
DamienandClaude Sonnet 5 911f191998 README/AGENTS.md : documente la construction + gravure de l'ISO depuis Windows
Generalise pour n'importe quelle machine Windows qui a suivi la
section WSL du README, pas pour la configuration particuliere de
cette session. Construire l'ISO marche normalement sous WSL ; la
graver est une autre affaire (WSL2 n'a pas d'acces natif aux
disques amovibles, source Microsoft Learn) -- deux options
documentees : copier le .iso vers Windows + outil natif (Rufus...),
ou usbipd-win pour rattacher la cle USB a WSL et dd normalement.

AGENTS.md : meme mise en garde cote agent, avec consigne explicite
de ne pas improviser un contournement si usbipd n'est pas deja en
place -- construire l'ISO puis la remettre a l'operateur humain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:46:31 +02:00
DamienandClaude Sonnet 5 c94c870c82 AGENTS.md : ajoute le chemin ISO USB et les sources officielles
L'agent doit pouvoir se concentrer soit sur l'ISO soit sur le
serveur PXE -- ajoute la section ISO (meme flake, meme install.sh,
sans dnsmasq/caddy a operer) avec la regle dd explicite (irreversible,
confirmation humaine obligatoire sur /dev/sdX).

Ajoute aussi une section Sources (nixos manual, search.nixos.org,
code source nixpkgs pour iso-image.nix/netboot.nix, wiki.nixos.org,
disko, home-manager, ipxe, dnsmasq, caddy, systemd.timer, policies
Firefox) pour que l'agent verifie au lieu de deviner quand il bute
sur un mecanisme qu'il ne connait pas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:42:01 +02:00
DamienandClaude Sonnet 5 04d73b16ea AGENTS.md : clarifie la regle pull/push, formulation precedente contradictoire
"pull-only... qui ne font jamais git pull" melangeait deux sens de
"pull" (role consommateur vs commande git litterale) et se lisait
comme contradictoire. Regle simple a la place : Hermes pull toujours
permis (necessaire a l'entretien du serveur PXE), push jamais, sans
exception -- signaler un changement a pousser plutot que le pousser.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:37:22 +02:00
DamienandClaude Sonnet 5 ea22c08f57 Ajoute AGENTS.md : contexte pour un agent (Hermes) dedie serveur PXE
Fichier distinct du README humain -- couvre installation, entretien
et depannage du serveur PXE pour un agent charge de l'operer
(dnsmasq/caddy/pxe-publish), avec les incidents deja rencontres
cette session (service absent avant rebuild du serveur, clone perdu,
IP statique pas appliquee au premier switch, git pull sans nouveau
commit) et les regles de securite specifiques a un agent autonome
(jamais de push sans instruction, verifier hostname/pwd avant toute
commande distante, confirmer avant une action a impact sur le parc
en prod).

Note en passant : le README ne liste que 3 des 4 valeurs a adapter
dans pxe-server/configuration.nix (internetInterface manquait) --
corrige dans AGENTS.md, pas encore dans le README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:30:44 +02:00
DamienandClaude Sonnet 5 217d258ffd README : documente comment changer username/password du compte flotte
Precise les deux fichiers a toucher (hosts/nixos/configuration.nix
+ home-manager.users dans hosts/common/default.nix), et surtout la
limite : initialPassword ne s'applique qu'a la creation du compte,
jamais retroactivement sur une machine deja installee (sudo passwd
a la main sinon).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:27:11 +02:00
DamienandClaude Sonnet 5 2efdcaeecb Unifie le compte generique flotte : usagers -> nixos (compte, attribut, dossier)
Renomme partout (flake.nix, hosts/usagers -> hosts/nixos,
install.sh, autoUpgrade.flake, home-manager.users, README) pour
que compte systeme, attribut de flake et mot de passe portent tous
le meme nom "nixos" -- un seul mot a retenir sur un poste partage.

Remplace aussi hosts/nixos/dconf.ini (jusque-la un seul reglage,
le fond d'ecran) par le dconf complet d'oreo (panneau, themes,
reglages GTK/Nemo...), chemins de wallpaper adaptes vers
hosts/common/wallpaper (partage, pas par-profil). Warpinator
connect-id retire du dconf copie : deja exclu du bureau Cinnamon
sur les trois profils (fleet/thomas-clara-jade/oreo) via
environment.cinnamon.excludePackages, une valeur figee partagee
par tout le parc aurait de toute facon casse l'appairage si le
paquet avait ete present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:23:18 +02:00
DamienandClaude Sonnet 5 8f99c20ec5 README : documente comment forcer une mise a jour immediate
Trois options (timer, systemctl start nixos-upgrade.service, ou
git pull + rebuild manuel sur place) au lieu d'attendre le prochain
passage hebdomadaire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 13:52:54 +02:00
DamienandClaude Sonnet 5 73c93f3aac README : corrige le mot de passe fleet et documente les commandes nix verifiees
Etape 6 annoncait a tort un changement de mot de passe force pour
"PC normal" -- usagers garde nixos stable sans expiration, seuls les
postes nominatifs (nixos_users) le forcent. Ajoute comment reactiver
ce mecanisme sur la fleet si besoin un jour.

Avant-derniere section : commandes nix desormais reellement testees
(WSL), plus --no-build/--dry-run pour rester rapides, avec le
perimetre exact de ce qu'elles verifient.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 13:27:34 +02:00
DamienandClaude Sonnet 5 9f4fc59709 Firefox : active la locale fr et desactive le widget meteo
RequestedLocales manquait -- le langpack fr etait deja installe
(languagePacks) mais jamais active comme locale par defaut.
FirefoxHome.Weather=false (ajoute en Firefox 152 / ESR 140.12)
retire le widget meteo (demande la geolocalisation) de la page
d'accueil.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 13:27:27 +02:00
DamienandClaude Sonnet 5 a1762f2922 install.sh : ajoute oreo-i3 a la liste blanche des utilisateurs nommes
Sans ca, taper 'oreo-i3' a l'install repondait 'Utilisateur inconnu' --
la liste etait en dur, pas derivee du repo nixos_users.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 12:11:55 +02:00
DamienandClaude Sonnet 5 6d9ec09ecb README : documente le workflow dconf dump/load pour personnaliser le bureau
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 11:18:49 +02:00
DamienandClaude Sonnet 5 ccae2984bc usagers : dconf.settings typé -> fichier dconf.ini brut chargé par home-manager
hosts/usagers/dconf.ini (nouveau) : même clé qu'avant (fond d'écran),
mais sous forme de fichier ini mis à jour par 'dconf dump' + commit
depuis une machine réelle, pas en retranscrivant à la main -- même
mécanisme que oreo. Prépare le terrain pour enrichir ce fichier plus
tard sans toucher au .nix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 11:17:52 +02:00
DamienandClaude Sonnet 5 618db31477 README : section installer/déboguer WSL + Nix sous Windows
Couvre l'installation WSL2/Nix, le fait que les flakes sont déjà
activées par défaut (2.35+), le symptôme classique 'ping IP marche
mais résolution DNS time out' (fix: networkingMode=mirrored dans
.wslconfig), et comment appeler nix depuis un outil qui pilote WSL en
non-interactif (wsl.exe -e bash -lc, pas wsl.exe nix directement).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 11:08:45 +02:00
DamienandClaude Sonnet 5 980eb9d71b home-manager : remplace la base dconf système + activation script par du per-user
- flake.nix : ajoute l'input home-manager (release-26.05, même branche
  que nixpkgs, confirmé qu'elle existe), importe
  home-manager.nixosModules.home-manager sur le profil usagers
- hosts/common/default.nix : le fond d'écran par défaut passe par
  home-manager.users.usagers.dconf.settings au lieu de
  environment.etc."dconf/db/local.d/..." + system.activationScripts
  dconfUpdate -- home-manager gère lui-même le contexte utilisateur et
  le rechargement dconf
- useGlobalPkgs = true : partage le pkgs du système, pas de deuxième
  évaluation nixpkgs (RAM, PC à 4 Go)
- backupFileExtension : évite un échec d'activation si un fichier
  existant est dans le chemin (migration sur machine déjà installée)

ATTENTION : flake.lock est maintenant périmé (nouvel input non
verrouillé) -- nix flake lock à relancer avant déploiement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 09:07:17 +02:00
DamienandClaude Sonnet 5 2cb72503d5 Wallpapers : symlink un fichier précis, pas tout le dossier (préparation multi-source)
Évite un conflit si un profil ajoute son propre XML dans
/usr/share/cinnamon-background-properties à côté du commun (cas réel
côté nixos_users : oreo). Sans effet fonctionnel ici (un seul profil
sur ce repo), appliqué pour la cohérence entre les deux repos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 08:58:01 +02:00
DamienandClaude Sonnet 5 3459a89720 README: corrige la cadence de mise à jour (weekly, pas daily)
Doc restée à 'tous les jours' après le passage à weekly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 08:54:04 +02:00
DamienandClaude Sonnet 5 83d4bed8c4 Corrige (vraiment) le sélecteur de fond d'écran Cinnamon : mauvais mécanisme la première fois
Le précédent correctif visait gnome-background-properties (XDG_DATA_DIRS)
-- mais Cinnamon ne suit pas ce standard. Vérifié dans le vrai source du
sélecteur (linuxmint/cinnamon, cs_backgrounds.py) :
properties_dir = "/usr/share/cinnamon-background-properties", un
chemin ABSOLU EN DUR, jamais résolu via XDG_DATA_DIRS. Notre XML avait
beau être au bon endroit dans le store, Cinnamon ne le trouvait jamais.

Fix : le XML est maintenant généré sous cinnamon-background-properties
(bon nom), et systemd.tmpfiles.rules force un symlink
/usr/share/cinnamon-background-properties -> notre dérivation --
seul moyen d'atteindre ce chemin en dur sur NixOS (pas de /usr/share
FHS classique). Réappliqué à chaque switch, pas besoin de reboot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 20:26:57 +02:00
DamienandClaude Sonnet 5 972f9c8d3c Firefox flotte : reprend la config oreo (page de bienvenue + pubs), DuckDuckGo, extensions réduites
Le config Firefox précédente ne coupait que showSponsored -- pas
aboutwelcome (page de config au premier lancement), pas les autres
sources de contenu sponsorisé/promo (FirefoxHome TopSites/Highlights/
Pocket/Snippets, FirefoxSuggest, UserMessaging...). Reprend la config
complète d'oreo (nixos_users/hosts/oreo/configuration.nix), avec deux
changements : SearXNG (perso/privé) -> DuckDuckGo (public, intégré,
pas besoin d'Add) ; ExtensionSettings réduit à uBlock Origin + Privacy
Badger (oreo en a une quinzaine, hors sujet pour un poste générique).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 20:19:49 +02:00
DamienandClaude Sonnet 5 9c8847f3d4 Corrige le sélecteur de fond d'écran Cinnamon vide (mint-artwork + notre wallpaper)
Root cause identifiée : Cinnamon découvre les fonds d'écran
sélectionnables via un XML gnome-background-properties (même
mécanisme que nixos-artwork.wallpapers dans nixpkgs, vérifié dans son
source). mint-artwork (déjà installé par défaut avec Cinnamon) ne
fournit PAS ce XML -- ses images existent bien dans le store mais sont
invisibles au clic droit > changer le fond d'écran. On génère ce XML
nous-mêmes au build, à partir des fichiers réellement présents dans
mint-artwork (pas de liste en dur) + notre propre wallpaper par défaut.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 19:42:34 +02:00
DamienandClaude Sonnet 5 54803f2f92 Explicite system.autoUpgrade.persistent = true
Déjà le défaut du module NixOS (nixos/modules/tasks/auto-upgrade.nix) :
si la machine est éteinte au moment prévu, systemd rattrape la maj au
prochain démarrage (Persistent= côté timer). Aucun changement de
comportement, juste documenté en dur plutôt que de dépendre d'un défaut
invisible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 18:36:52 +02:00
DamienandClaude Sonnet 5 6a42052663 Simplification : fusion base.nix+desktop.nix+accounts.nix -> hosts/common/default.nix
Aucun changement fonctionnel, juste moins de fichiers à gérer côté
admin (structure inspirée de modules/anssi/ : un rôle clair par fichier,
mais un seul profil ici donc un seul fichier commun suffit).
hosts/usagers/configuration.nix importe désormais juste `../common`
(default.nix résolu automatiquement) + le layout disko.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 18:33:46 +02:00
DamienandClaude Sonnet 5 bd77248935 Cadence weekly (format oreo) + paquets/Flatpak communs sur toutes les machines
- system.autoUpgrade.dates : daily -> weekly, même cadence que oreo
- hosts/common/desktop.nix : ajoute la liste de paquets demandée (System
  tools, Office, Communication/Media) + Flatpak (repo/install/update) +
  AppImage, déjà présents sur oreo, maintenant sur usagers aussi

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 18:24:40 +02:00
DamienandClaude Sonnet 5 0a1bcd18c0 Bluetooth commun (même config que oreo) sur toutes les machines
hardware.bluetooth.enable + powerOnBoot=false + blueman, déplacé dans
hosts/common/base.nix -- avant ça n'existait que sur le profil oreo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:53:46 +02:00
DamienandClaude Sonnet 5 ec562a3655 Firefox flotte : uBlock+Privacy Badger forcés, DuckDuckGo, DNT, anti-pub écran d'accueil
- ExtensionSettings : ajoute Privacy Badger (uBlock Origin déjà présent),
  même id AMO que oreo/nixos_cinnamon
- SearchEngines.Default = DuckDuckGo (moteur intégré, pas besoin d'Add,
  confirmé via firefox-admin-docs.mozilla.org)
- FirefoxHome.Sponsored{TopSites,Pocket} + FirefoxSuggest.SponsoredSuggestions
  = false : retire les pubs du nouvel onglet / barre d'adresse
  (showSponsored seul ne couvrait que l'ancien mécanisme)
- privacy.donottrackheader.enabled = true : DNT n'a plus d'UI dans les
  Firefox récents mais le pref existe toujours et envoie l'en-tête DNT=1
- https_only_mode déjà réglé, rien à changer là-dessus

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:47:17 +02:00
DamienandClaude Sonnet 5 fddcca0e51 Comments: style plus concis (calé sur modules/anssi/, écrit par des pros)
Aucun changement fonctionnel -- juste les commentaires : essais de
justification en plusieurs paragraphes remplacés par des notes courtes,
type/quoi/pourquoi en une ou deux lignes. Corrige au passage un
commentaire obsolète dans installer-base.nix qui décrivait encore l'usage
de disko-install (abandonné depuis pour disko + nixos-install séparés).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:42:51 +02:00
DamienandClaude Sonnet 5 bfd7ed4eed install.sh: ne garde sur /etc/nixos que ce qui sert (flakes, hosts, modules, diskos)
Retire installer/ (script d'install, obsolète une fois fait) et
pxe-server/ (config d'un autre host) du clone copié sur la machine
installée -- inutiles pour un poste client, ne concernent que le
serveur PXE / le moment de l'install. Nix ne lit ces fichiers que si on
build l'attribut correspondant (jamais le cas pour un poste installé),
donc rien ne casse. N'affecte que la branche fleet : nixos_users n'a ni
installer/ ni pxe-server/, le rm -rf ne trouve rien à supprimer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:39:11 +02:00
DamienandClaude Sonnet 5 8735b8336c Firmware au maximum : enableAllFirmware au lieu de enableRedistributableFirmware
Vérifié dans nixpkgs (nixos/modules/hardware/all-firmware.nix) :
enableAllFirmware inclut déjà enableRedistributableFirmware (son défaut
est config.hardware.enableAllFirmware) et ajoute en plus
broadcom-bt-firmware (Bluetooth), b43Firmware (wifi Broadcom),
xone-dongle-firmware, facetimehd-firmware. Nécessaire pour coller à un
parc de PC inconnu à l'avance (logique Linux Mint : firmware non-libre
inclus par défaut). allowUnfree déjà réglé, rien d'autre à changer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:18:42 +02:00
DamienandClaude Sonnet 5 ae3461b085 Garde l'hyperthreading (exception R8 explicite, pas de contournement silencieux)
R8 (mitigations CPU) coupe le SMT via mds=full,nosmt. Désactivé via
security.anssi.exceptions.R8 (mécanisme prévu par le module), puis
boot.kernelParams réaffirme le reste de R8 à l'identique du texte
source (modules/anssi/kernel-options.nix), sauf mds=full,nosmt ->
mds=full (valeur alternative documentée du même paramètre, mitige MDS
sans couper l'hyperthreading).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:03:12 +02:00
DamienandClaude Sonnet 5 6bcb16b8ab Durcissement ANSSI (module SécurixOS) sur toutes les machines de la flotte
- vendor modules/anssi/ depuis SécurixOS (DINUM), copy-paste explicitement
  supporté par son propre README, licence MIT
- hosts/common/base.nix: security.anssi.enable, level=intermediary,
  category=client -- remplace le bloc sysctl qui n'existait que sur le
  profil oreo (nixos_users), même valeurs, rien de plus fort
- garde systemd.coredump.enable=false + apparmor (pas couvert par le
  module ANSSI, exception R45 explicite côté Sécurix)
- ip_forward=0 (R12) n'affecte pas pxe-server, qui n'importe pas ce fichier

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 17:00:17 +02:00
DamienandClaude Sonnet 5 77b089bc45 Persiste la config sur /etc/nixos à l'install, firmware wifi, wallpaper par défaut
- install.sh: copie le repo (clone substitué) vers /mnt/etc/nixos après
  nixos-install, sinon la machine installée n'a aucun fichier à éditer
- base.nix: hardware.enableRedistributableFirmware pour les cartes wifi
  nécessitant un firmware non-libre
- desktop.nix: fond d'écran NixOS par défaut via dconf (n'existait pas)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 16:53:08 +02:00
Damien 096e534e91 fix swapfile btrfs: chattr +C avant ecriture, ajout e2fsprogs 2026-09-09 13:20:15 +02:00
Damien 7523532dfd install.sh: clone+sed pour la substitution disque (pas de mecanisme --disk cote disko seul) 2026-09-09 13:08:50 +02:00
Damien 3bbe301b38 installe en 3 étapes avec swap sur disque cible, pour les PC à 4 Go de RAM 2026-09-09 12:52:33 +02:00
Damien d725d633cd console.keyMap fr sur pxe-server 2026-09-09 12:32:44 +02:00
24 changed files with 2292 additions and 289 deletions
+6
View File
@@ -0,0 +1,6 @@
# Liens produits par `nix build` (pointent dans /nix/store, inutiles à committer)
result
result-*
# ISO construites localement (voir README : "Construire l'ISO et la graver")
*.iso
+170
View File
@@ -0,0 +1,170 @@
# AGENTS.md — contexte pour agent (Hermes et autres)
Ce fichier s'adresse à un agent (Hermes ou tout autre harness compatible AGENTS.md) chargé de produire le média d'installation de la flotte, **soit la création de l'ISO USB, soit la gestion du serveur PXE** -- les deux couvrent le même besoin (fournir l'installeur à un PC), pas à la fois sur la même tâche sauf demande explicite. Le [README.md](README.md) reste la référence humaine (technicien qui installe des PC un par un) ; ce fichier-ci est pour l'agent.
Repo public, sans authentification, jamais chiffré. Ne contient aucune donnée nominative (voir [Nixos_Users](../nixos_users), repo privé séparé, hors périmètre de cet agent sauf mention contraire de l'opérateur humain).
## Règles avant toute action
- **`git pull` oui, `git push` jamais.** Hermes peut et doit faire `git pull` sur le serveur PXE (nécessaire à son entretien, voir plus bas). Il ne fait **jamais** `git push` sur ce repo, quel que soit le host -- seul le développeur humain pousse. Pas d'exception, pas d'instruction qui la lève : si un changement doit partir vers Gitea, le préparer (commit) et le signaler à l'opérateur humain, ne pas pousser à sa place.
- **Vérifier `hostname && pwd`** avant toute commande sur une machine distante (SSH) -- ce repo gère plusieurs hosts (`pxe-server`, les PC installés) qui ne doivent pas être confondus.
- **Ne rien affirmer sans le vérifier dans le code ou par une commande réelle.** Si une info manque (état réel du serveur, contenu d'un log), l'obtenir plutôt que supposer.
- **Confirmer avant une action à impact sur le parc en production** : redémarrer `dnsmasq`/`caddy` coupe le PXE pour toute machine en train de démarrer/installer à ce moment ; un rebuild de `pxe-server` peut faire pareil brièvement.
- **`dd` est irréversible.** Avant tout `dd if=... of=/dev/sdX`, faire confirmer par l'opérateur humain le chemin exact (`lsblk` d'abord, toujours) -- une mauvaise lettre écrase silencieusement un disque, système ou données, sans avertissement. Ne jamais deviner `/dev/sdX` à partir d'une supposition ("probablement la clé").
- Commits : `Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>` en pied de message (adapter le nom de modèle si l'agent qui commit n'est pas Claude).
## Ce que sert le serveur PXE
Un seul flake, deux modes choisis à l'écran de démarrage par [installer/install.sh](installer/install.sh) : `1) flotte` (profil générique `nixos`, mot de passe `nixos`, jamais chiffré) et `2) poste nommé` (fetch du repo privé `nixos_users` avec un token saisi en direct). Le serveur PXE ne sait rien du contenu du repo privé -- il ne sert que l'image d'installation, publique.
Architecture ([pxe-server/configuration.nix](pxe-server/configuration.nix)) :
- `services.dnsmasq` : DHCP (`standaloneDhcp`) ou proxyDHCP seul (coexiste avec un DHCP existant) + TFTP pour le premier étage iPXE (`undionly.kpxe` / `ipxe.efi`).
- `services.caddy` : sert `netboot.ipxe` + kernel/initrd en HTTP (TFTP seul est trop lent pour ces tailles).
- `systemd.services.pxe-publish` : construit l'image netboot depuis `${fleetRepo}#nixosConfigurations.installer-netboot` et publie les fichiers dans `webroot`/`tftpRoot`. Se relance au boot (`wantedBy = multi-user.target`) et **quotidiennement** (`systemd.timers.pxe-publish`, `OnCalendar = "daily"` -- ne pas confondre avec `system.autoUpgrade` des PC installés, qui est hebdomadaire).
- Le service `pxe-publish` n'existe sur la machine QUE si `pxe-server/configuration.nix` a déjà été appliqué (`nixos-rebuild switch --flake .#pxe-server`) avec une version du repo qui le contient. L'ajouter au repo ne suffit pas : il faut rebuild le serveur lui-même.
4 valeurs à adapter dans `pxe-server/configuration.nix` (le `let` en tête de fichier) -- le README n'en liste que 3, `internetInterface` en plus :
- `interface` : interface réseau réelle du serveur (`ip a`).
- `serverIp` : IP fixe de cette interface (le script iPXE pointe dessus en dur).
- `standaloneDhcp` : `true` si ce serveur doit être l'unique DHCP du segment PXE ; `false` pour proxyDHCP seul (coexiste avec un DHCP existant -- cas le plus probable).
- `internetInterface` : seulement utilisée si `standaloneDhcp = true` (sortie NAT du segment PXE vers Internet).
## Alternative : créer l'ISO USB (sans serveur PXE)
Même flake, même `install.sh`, pas de dnsmasq/Caddy à opérer -- juste un fichier `.iso` à graver. Utile en secours ou pour tester sans monter tout le PXE. Un seul ISO pour la flotte ET les postes nommés : le choix (`1) flotte` / `2) poste nommé`) se fait à l'écran de démarrage, pas à la gravure.
```bash
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage
lsblk # identifier la clé USB, PAS le disque système
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress conv=fsync
```
`/dev/sdX` doit être confirmé par l'opérateur humain avant d'exécuter le `dd` (voir règle plus haut) -- ce n'est pas négociable, une erreur ici écrase un disque en silence.
**Si l'agent tourne sous WSL (Windows)** : `lsblk` n'y verra la clé USB QUE si elle a été explicitement rattachée via `usbipd attach --wsl` ([doc officielle](https://learn.microsoft.com/en-us/windows/wsl/connect-usb), détails dans le [README.md](README.md)). Par défaut WSL2 n'a aucun accès aux disques amovibles. Si ce n'est pas déjà en place, ne pas improviser un contournement : construire l'ISO (ça, ça marche normalement sous WSL), remettre le `.iso` produit à l'opérateur humain, et le laisser le graver avec un outil Windows natif (Rufus...).
Définition de l'image : [installer/iso.nix](installer/iso.nix). Attribut de flake : `nixosConfigurations.installer-iso`.
## Installer le serveur PXE (depuis zéro)
```bash
# 1. NixOS installé (ISO officielle), mot de passe root défini.
nix-shell -p git
git clone https://gitea.miniclic.net/Damien/Nixos_Fleet.git /etc/nixos-fleet
cd /etc/nixos-fleet
# 2. Fichier matériel (hors repo, jamais committé)
sudo nixos-generate-config
# 3. Éditer les 4 valeurs ci-dessus dans pxe-server/configuration.nix
# 4. Build -- --impure obligatoire (lit /etc/nixos/hardware-configuration.nix, hors flake)
sudo nixos-rebuild switch --flake .#pxe-server --impure
# 5. Publier l'image (première fois : plusieurs minutes)
sudo systemctl start pxe-publish
sudo systemctl status pxe-publish # doit finir en "success"
```
Vérification post-install :
```bash
sudo systemctl status dnsmasq caddy
ls /var/lib/tftpboot # undionly.kpxe, ipxe.efi
ls /var/www/pxe # bzImage, initrd, netboot.ipxe
curl -I http://<serverIp>/netboot.ipxe
systemctl is-enabled pxe-publish.timer
```
Puis tester sur 1 seul PC (BIOS et UEFI séparément si le parc a les deux) avant tout déploiement de masse.
## Entretien courant
```bash
cd /etc/nixos-fleet # ou l'emplacement réel du clone sur ce serveur
git pull
git log --oneline -3 # le dernier commit doit correspondre à ce qui est attendu
```
Republier l'image après une modif de `installer/` :
```bash
sudo systemctl start pxe-publish
```
Republier après une modif de `pxe-server/configuration.nix` (rebuild complet, pas juste `pxe-publish`) :
```bash
sudo nixos-rebuild switch --flake .#pxe-server --impure
```
Logs :
```bash
journalctl -u dnsmasq -f
journalctl -u caddy -f
journalctl -u pxe-publish -f
```
## Dépannage -- incidents déjà rencontrés
**`Unit pxe-publish.service not found`** : le système du serveur n'a jamais été rebuild depuis que ce service a été ajouté à `pxe-server/configuration.nix`. `git pull` seul ne suffit pas côté serveur -- il faut ensuite `sudo nixos-rebuild switch --flake .#pxe-server --impure`.
**Le dossier repo a disparu du serveur** (`/etc/nixos-fleet` ou équivalent absent) : repo public, sans authentification -- reclone directement, n'importe où :
```bash
git clone https://gitea.miniclic.net/Damien/Nixos_Fleet.git
```
Le `hardware-configuration.nix` est hors du repo (`/etc/nixos/`), donc indépendant de l'emplacement du clone.
**Un fix censé être appliqué ne change rien** : vérifier `git log --oneline -3` avant toute autre piste -- le `git pull` n'a peut-être rien ramené de neuf (mauvaise branche, push pas fait côté dev). Ne jamais supposer qu'un rebuild "prend" le dernier commit distant sans l'avoir vérifié.
**`curl http://<serverIp>/netboot.ipxe` reste bloqué** : l'IP statique de l'interface PXE ne s'est parfois pas appliquée toute seule au tout premier `switch` sur une install neuve. Vérifier :
```bash
ip a show <interface> # cherche "inet <serverIp>/24"
```
Absente → forcer une fois :
```bash
sudo systemctl start network-addresses-<interface>.service
```
Un reboot du serveur applique aussi ça correctement au démarrage suivant.
**Écran PXE ne trouve rien** : revérifier `serverIp` et `interface` dans `pxe-server/configuration.nix` contre la réalité du serveur (`ip a`).
**Boucle infinie au démarrage d'un PC client** : `journalctl -u dnsmasq` pendant le boot du PC concerné.
**UEFI ou BIOS ne boote pas** (l'un des deux seulement) : vérifier que les deux fichiers existent dans `/var/lib/tftpboot` (`undionly.kpxe` pour BIOS, `ipxe.efi` pour UEFI) -- l'un des deux `install -m 0644 ... || true` dans `pxe-publish` peut avoir silencieusement échoué (noms de sortie du paquet `pkgs.ipxe` à revérifier si la version change).
**IP qui changent tout le temps sur le segment PXE** : un autre serveur DHCP tourne déjà sur ce réseau -- repasser `standaloneDhcp` à `false` (proxyDHCP seul).
## Valider une modif avant de la pousser
```bash
nix flake check --no-build # échoue sur pxe-server hors du vrai serveur (attendu, lit /etc/nixos)
nix flake check --no-build --impure # à lancer depuis le serveur PXE réel uniquement
nix build .#nixosConfigurations.nixos.config.system.build.toplevel --dry-run
```
Environnement de dev sans accès à un vrai serveur PXE : voir "Environnement de dev sous Windows (WSL + Nix)" dans le [README.md](README.md) -- `pxe-server` ne s'évalue qu'avec `--impure` **sur la machine réelle**, c'est normal ailleurs.
## Sources -- pour vérifier au lieu de deviner
En cas de doute sur le comportement réel d'une option ou d'un mécanisme, chercher ici avant de supposer :
- NixOS manual : https://nixos.org/manual/nixos/stable/
- Recherche d'options NixOS (n'importe quel module : `services.dnsmasq`, `services.caddy`, `system.autoUpgrade`, `programs.firefox`...) : https://search.nixos.org/options
- Recherche de paquets nixpkgs : https://search.nixos.org/packages
- Code source nixpkgs (la vérité de dernier recours sur le comportement réel d'un module) : https://github.com/NixOS/nixpkgs
- Module image ISO (`installer-iso`) : https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/cd-dvd/iso-image.nix
- Module image netboot (`installer-netboot`) : https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/netboot/netboot.nix
- NixOS Wiki (officiel, `wiki.nixos.org` -- pas `nixos.wiki`, non-officiel et moins à jour) :
- Netboot : https://wiki.nixos.org/wiki/Netboot
- Creating a NixOS live CD : https://wiki.nixos.org/wiki/Creating_a_NixOS_live_CD
- disko (partitionnement déclaratif) : https://github.com/nix-community/disko -- Quickstart : https://github.com/nix-community/disko/blob/master/docs/quickstart.md -- Référence : https://github.com/nix-community/disko/blob/master/docs/reference.md
- home-manager : https://home-manager.dev/manual/
- iPXE (premier étage du boot réseau, `undionly.kpxe`/`ipxe.efi`) : https://ipxe.org/
- dnsmasq (DHCP/TFTP) : https://thekelleys.org.uk/dnsmasq/doc.html
- Caddy (serveur HTTP) : https://caddyserver.com/docs/
- systemd.timer (`pxe-publish.timer`) : https://www.freedesktop.org/software/systemd/man/systemd.timer.html
- Policies Firefox (`RequestedLocales`, `FirefoxHome`...) : https://firefox-admin-docs.mozilla.org/reference/policies/
+286 -43
View File
@@ -1,13 +1,70 @@
# Nixos_Fleet # Nixos_Fleet
Déploiement en masse (PXE ou USB) du profil générique `usagers`. Déploiement en masse (PXE ou USB) du profil générique `nixos`.
Repo public. `git pull` sans mot de passe. Repo public. `git pull` sans mot de passe.
Ne contient aucune donnée des utilisateurs nommés. Ça, c'est dans [Nixos_Users](../nixos_users), repo privé séparé. Ne contient aucune donnée des utilisateurs nommés. Ça, c'est dans [Nixos_Users](../nixos_users), repo privé séparé.
## Index
- [Installer depuis un NixOS déjà installé](#installer-depuis-un-nixos-déjà-installé) -- appliquer cette config sans réinstaller, partitionnement existant préservé
- [Installer un PC](#installer-un-pc) -- clé USB ou PXE, depuis zéro (reformate le disque)
- [Installer le serveur PXE](#installer-le-serveur-pxe)
- [Référence](#référence)
- [Environnement de dev sous Windows (WSL + Nix)](#environnement-de-dev-sous-windows-wsl--nix)
- [Travailler avec Git](#travailler-avec-git)
--- ---
# Installer depuis un NixOS déjà installé
Pour appliquer cette config (flotte ou poste nommé) sur une machine où NixOS tourne déjà -- sans réinstaller, **sans toucher au partitionnement existant**. Utilise `nixos-existing` (flotte, ce repo) ou `<profil>-existing` (poste nommé, [Nixos_Users](../nixos_users)) : même profil logiciel que l'install normale, mais basé sur le `hardware-configuration.nix` réel de la machine plutôt que sur disko.
Pour repartir de zéro (reformater le disque) à la place : voir "Installer un PC" plus bas.
## Flotte
```bash
git clone https://gitea.miniclic.net/Damien/Nixos_Fleet.git /etc/nixos-fleet
cd /etc/nixos-fleet
sudo nixos-rebuild switch --flake .#nixos-existing --impure
```
`--impure` : nécessaire pour lire `/etc/nixos/hardware-configuration.nix`, hors du flake.
## Poste nommé
Repo privé -- voir "Token Gitea" dans [Nixos_Users](../nixos_users) pour en obtenir un, et la liste des profils disponibles (pas listée ici, repo public).
```bash
git clone https://<token>@gitea.miniclic.net/Damien/Nixos_Users.git /etc/nixos-users
cd /etc/nixos-users
sudo nixos-rebuild switch --flake .#<profil>-existing --impure
```
Pour que les mises à jour automatiques continuent de fonctionner ensuite (repo privé), créer le netrc à la main -- normalement écrit par `install.sh`, absent ici puisqu'on ne passe pas par l'installeur :
```bash
sudo mkdir -p /etc/nix
{
echo "machine gitea.miniclic.net"
echo "login <token>"
echo "password x-oauth-basic"
} | sudo tee /etc/nix/netrc
sudo chmod 600 /etc/nix/netrc
```
## Maintenance
Identique au reste du repo (voir "Mises à jour de la flotte" en Référence), avec `nixos-existing` (ou `<profil>-existing`) à la place de l'attribut normal :
```bash
cd /etc/nixos-fleet # ou /etc/nixos-users
sudo git pull
sudo nixos-rebuild switch --flake .#nixos-existing --impure
```
# Installer un paquet sans le mettre "en dur" # Installer un paquet sans le mettre "en dur"
Pour tester un outil sans toucher à `configuration.nix`. Pour tester un outil sans toucher à `configuration.nix`.
@@ -38,9 +95,45 @@ Aucune de ces méthodes ne survit à un reboot ni à un `nixos-rebuild`. Pour qu
Une étape à la fois. Ne saute pas d'étape. Une étape à la fois. Ne saute pas d'étape.
## Étape 0 — Support d'installation
Deux supports possibles, choisis-en un.
**PXE (serveur déjà monté)** : rien à préparer ici. Va à l'étape 1.
**Ce n'est pas l'ISO de nixos.org.** Celle-ci est construite depuis ce repo (`installer/iso.nix`) : paquets propres au parc (git, outils de partitionnement...), clavier/locale FR par défaut, et surtout `install.sh` qui se lance seul à l'ouverture de session -- l'ISO officielle démarre sur un shell nu, sans rien de tout ça, il faudrait tout faire à la main (partitionnement, `nixos-install`...).
Le `nix build` ci-dessous est **long** (10 à 30+ minutes) et n'affiche presque rien pendant la majeure partie du temps -- l'étape la plus lente (`mksquashfs`, compression de tout le store Nix pour l'ISO) ne montre aucune progression. Normal, pas figé : vérifier avec `top`/`htop` qu'un processus consomme du CPU avant de penser à interrompre.
**Clé USB, sur Linux :**
```bash
git clone https://gitea.miniclic.net/Damien/Nixos_Fleet.git
cd Nixos_Fleet
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage
lsblk # repérer la clé -- pas ton disque système
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress conv=fsync
```
Vérifier `/dev/sdX` avant de lancer `dd`.
**Clé USB, sur Windows** (dans un terminal WSL2 -- voir "Installer WSL2" plus bas si pas encore fait) :
```bash
cd "/mnt/c/Users/<toi>/Desktop" # ou un autre dossier Windows de ton choix
git clone https://gitea.miniclic.net/Damien/Nixos_Fleet.git
cd Nixos_Fleet
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage
cp result/iso/*.iso ./nixos-installer.iso
```
`dd` ne fonctionne pas depuis WSL2 (pas d'accès à la clé USB par défaut, [source](https://learn.microsoft.com/en-us/windows/wsl/connect-usb)). `nixos-installer.iso` apparaît dans l'explorateur Windows, dans le dossier du repo -- le graver avec [Rufus](https://rufus.ie/) (ou balenaEtcher, Win32DiskImager).
Une fois la clé prête (Linux ou Windows) : va à l'étape 1.
## Étape 1 — Brancher ## Étape 1 — Brancher
Branche le câble réseau. Branche le câble réseau (PXE) ou la clé USB gravée à l'étape 0 (USB).
Branche le PC au secteur. Branche le PC au secteur.
@@ -56,7 +149,7 @@ HP → F9
Lenovo → F12 Lenovo → F12
Choisis : Network Boot. Choisis : Network Boot (PXE) ou Boot from USB (clé) -- le menu qui suit est identique dans les deux cas.
## Étape 3 — Attendre ## Étape 3 — Attendre
@@ -70,7 +163,7 @@ Il y a deux cas. Choisis-en un seul.
PC normal → va à l'étape 4A. PC normal → va à l'étape 4A.
PC pour thomas, clara, jade ou oreo → va à l'étape 4B. PC pour une personne (profil nommé) → va à l'étape 4B.
## Étape 4A — PC normal ## Étape 4A — PC normal
@@ -90,7 +183,7 @@ Tape : `2`
Appuie sur Entrée. Appuie sur Entrée.
Tape le prénom (thomas, clara, jade ou oreo). Tape le prénom du profil (liste donnée à part, pas dans ce guide public).
Appuie sur Entrée. Appuie sur Entrée.
@@ -122,17 +215,19 @@ N'y touche pas.
PC normal : PC normal :
Utilisateur : `usagers` Utilisateur : `nixos`
Mot de passe : `nixos` Mot de passe : `nixos`
Utilisateur et mot de passe identiques, volontairement (un seul mot à retenir sur un poste partagé). Stable, ne change jamais tout seul et n'est jamais redemandé (voir "Réactiver un changement de mot de passe forcé" en référence si ça doit changer un jour).
PC pour une personne : PC pour une personne :
Utilisateur : le prénom tapé à l'étape 4B Utilisateur : le prénom tapé à l'étape 4B
Mot de passe : `nixos` Mot de passe : `nixos`
Un nouveau mot de passe est demandé tout de suite. Le choisir maintenant. Un nouveau mot de passe est demandé tout de suite (changement forcé au premier login, seulement sur les postes nominatifs -- voir [Nixos_Users](../nixos_users)). Le choisir maintenant.
## Étape 7 — Vérifier ## Étape 7 — Vérifier
@@ -238,13 +333,7 @@ Un reboot du serveur applique aussi ça correctement au démarrage suivant.
## Étape 8 — Tester sur 1 PC ## Étape 8 — Tester sur 1 PC
Démarrer un seul PC en PXE. Suivre "Installer un PC" (Network Boot au lieu de Boot from USB) sur une seule machine avant d'en lancer 20. Tester BIOS et UEFI séparément si le parc a les deux.
Vérifier qu'il arrive sur l'écran de choix.
Si le parc a du BIOS et de l'UEFI, tester les deux.
Ne pas lancer 20 PC avant que cette étape fonctionne.
## Après l'installation ## Après l'installation
@@ -311,7 +400,7 @@ IP qui changent tout le temps → un autre DHCP tourne déjà, repasser `standal
## Contenu du repo ## Contenu du repo
`hosts/usagers/` → le seul profil du repo. Jamais chiffré. `hosts/nixos/` → le seul profil du repo. Jamais chiffré.
`diskos/plain.nix` → layout disko (disque `main`, GPT+ESP+btrfs). `diskos/plain.nix` → layout disko (disque `main`, GPT+ESP+btrfs).
@@ -319,45 +408,115 @@ IP qui changent tout le temps → un autre DHCP tourne déjà, repasser `standal
`pxe-server/` → le serveur PXE (dnsmasq + Caddy). `pxe-server/` → le serveur PXE (dnsmasq + Caddy).
## Personnaliser le bureau (dconf)
`hosts/nixos/dconf.ini` contient les réglages Cinnamon (thème, fond d'écran, panneau...), chargés par home-manager à chaque rebuild.
Pour changer un réglage : le faire en direct dans l'interface Cinnamon sur une machine, puis exporter :
```bash
dconf dump /org/cinnamon/ > dconf.ini
```
Remplacer `hosts/nixos/dconf.ini` par ce fichier, commit, push. Rien à changer dans le `.nix`.
## Changer le nom d'utilisateur ou le mot de passe
Deux fichiers à toucher, dans `hosts/nixos/configuration.nix` :
```nix
users.users.nixos = {
...
initialPassword = "nixos";
};
```
Renommer `nixos` (l'attribut `users.users.nixos`) change le compte système. Changer la valeur d'`initialPassword` change le mot de passe.
Si le nom d'utilisateur change, mettre à jour aussi la référence dans `hosts/common/default.nix` (`home-manager.users.nixos = ...` doit cibler le même nom, sinon home-manager configure un compte qui n'existe plus).
**`initialPassword` ne s'applique qu'à la création du compte.** Sur une machine déjà installée, changer cette valeur dans le repo + laisser l'auto-upgrade tourner ne change RIEN au mot de passe réel -- NixOS ne réécrit jamais un mot de passe déjà défini. Ça ne s'applique qu'aux **nouvelles installations** à partir de ce moment-là.
Pour changer le mot de passe sur une machine déjà déployée : à la main, sur place ou à distance (SSH/console) :
```bash
sudo passwd nixos
```
Pareil pour renommer le compte sur une machine déjà installée : `usermod -l` ne suffit pas à tout migrer proprement (home directory, permissions) -- plus simple de considérer que le renommage ne s'applique qu'aux futures installations, pas de le faire à chaud sur le parc existant.
## Réactiver un changement de mot de passe forcé
Par défaut, le compte `nixos` garde `nixos` comme mot de passe stable, sans expiration (`hosts/nixos/configuration.nix`). Voulu : poste partagé, pas nominatif -- forcer un changement casserait le mot de passe commun affiché sur l'étiquette/le guide remis aux gens.
`nixos_users` force ce changement au premier login pour les postes nominatifs, via un script d'activation one-shot (`hosts/common/accounts.nix` de ce repo-là) :
```nix
system.activationScripts.forcePasswordChangeOnce.text = ''
marker=/var/lib/nixos-force-pwchange-done
if [ ! -e "$marker" ]; then
${pkgs.shadow}/bin/chage -d 0 nixos
mkdir -p /var/lib
touch "$marker"
fi
'';
```
Pour réactiver la même chose sur `nixos`, reprendre ce bloc tel quel dans `hosts/nixos/configuration.nix` (ou `hosts/common/default.nix` si ça doit s'appliquer à un futur deuxième profil de flotte). Le marqueur est nécessaire : sans lui, `chage -d 0` s'appliquerait à nouveau à chaque `nixos-rebuild switch`, redemandant un mot de passe à chaque redémarrage.
## Pourquoi deux repos ## Pourquoi deux repos
Une machine de la flotte pull uniquement ce repo. Une machine de la flotte pull uniquement ce repo.
Les comptes thomas/clara/jade/oreo vivent dans un autre repo, privé. Les comptes nominatifs vivent dans un autre repo, privé.
La flotte ne voit jamais ce second repo. La flotte ne voit jamais ce second repo.
## Graver la clé USB ## Graver la clé USB
Marche sans le serveur PXE. Utile pour tester avant de monter le PXE, ou en secours. Marche sans le serveur PXE. Utile pour tester avant de monter le PXE, ou en secours. Commandes : voir "Étape 0" au tout début de ce README.
NixOS permet nativement de fabriquer une image d'installation sur mesure : un ISO avec nos propres paquets et notre propre script (`install.sh`) qui se lance seul au démarrage. C'est une fonctionnalité standard de NixOS, pas un bricolage — `installer/iso.nix` dans ce repo est la définition de cette image. NixOS permet nativement de fabriquer une image d'installation sur mesure : un ISO avec nos propres paquets et notre propre script (`install.sh`) qui se lance seul au démarrage. C'est une fonctionnalité standard de NixOS, pas un bricolage — `installer/iso.nix` dans ce repo est la définition de cette image.
C'est le même ISO pour la flotte et les postes nommés — pas deux images différentes. Le choix se fait à l'écran au démarrage (`1) flotte` / `2) poste nommé`), pas au moment de la gravure. C'est le même ISO pour la flotte et les postes nommés — pas deux images différentes, **pas de commande de build spécifique par profil nommé**. Le choix se fait à l'écran au démarrage, une fois la clé branchée sur le PC cible (voir étapes 4/4B).
Le fichier `.iso` n'existe pas tant qu'on ne l'a pas construit : Depuis Windows : mêmes commandes, dans WSL -- voir "Étape 0", section Windows.
```bash
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage
```
Repérer la clé USB avant de graver — bien vérifier le chemin, pas ton disque système :
```bash
lsblk
```
```bash
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress conv=fsync
```
Vérifier `/dev/sdX` avant de lancer `dd`.
## Mises à jour de la flotte ## Mises à jour de la flotte
Chaque PC tire ses mises à jour directement depuis ce repo, tous les jours. Chaque PC tire ses mises à jour directement depuis ce repo, chaque semaine (`system.autoUpgrade`, `persistent = true` : se rattrape au boot suivant même si le PC était éteint au moment prévu).
Pas de `git pull` manuel sur les PC. Juste `git push` ici. Pas de `git pull` manuel sur les PC en fonctionnement normal. Juste `git push` ici.
### Appliquer un changement immédiatement (sans attendre la semaine)
1. Pousser le changement depuis le poste de dev :
```bash
git add -A
git commit -m "description"
git push
```
2. Sur le PC cible, trois options, du plus lent au plus immédiat :
**Rien faire** : le timer hebdomadaire s'en charge tout seul.
**Forcer maintenant, sans y toucher physiquement** (ex. à distance via SSH) : lancer le même service que le timer, il fetch tout seul depuis l'URL du repo, pas besoin que `/etc/nixos` soit à jour :
```bash
sudo systemctl start nixos-upgrade.service
```
**Rebuild manuel sur place** (typique en test, devant la machine) :
```bash
cd /etc/nixos
sudo git pull
sudo nixos-rebuild switch --flake .#nixos
```
Le `git pull` est obligatoire ici : contrairement à `nixos-upgrade.service`, ce rebuild lit les fichiers locaux de `/etc/nixos`, pas l'URL distante.
## Avant la première vague de 100 PC ## Avant la première vague de 100 PC
@@ -373,21 +532,26 @@ Boot BIOS et boot UEFI, sur le parc réel.
Test sur 1-2 PC réels d'abord. Test sur 1-2 PC réels d'abord.
PC à seulement 4 Go de RAM : le zram (`installer-base.nix`) doit suffire pour installer `usagers` (Cinnamon + Firefox + LibreOffice + VLC), mais pas testé en dessous de 12 Go pour l'instant. Si ça échoue encore sur du 4 Go réel : alléger la liste de paquets de `usagers` plutôt que d'insister sur le zram seul. PC à seulement 4 Go de RAM : le zram (`installer-base.nix`) doit suffire pour installer `nixos` (Cinnamon + Firefox + LibreOffice + VLC), mais pas testé en dessous de 12 Go pour l'instant. Si ça échoue encore sur du 4 Go réel : alléger la liste de paquets de `nixos` plutôt que d'insister sur le zram seul.
```bash ```bash
nix flake check nix flake check --no-build # évalue nixos/installer-netboot/installer-iso
nix build .#nixosConfigurations.usagers.config.system.build.toplevel nix flake check --no-build --impure # inclut pxe-server (lit /etc/nixos, réel sur le serveur seulement)
nix build .#nixosConfigurations.installer-iso nix build .#nixosConfigurations.nixos.config.system.build.toplevel --dry-run
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage --dry-run
``` ```
Vérifié (WSL, testé le 2026-09-12) : `nixos` s'évalue et résout entièrement sa fermeture de paquets sans erreur. `pxe-server` échoue sans `--impure` en dehors du vrai serveur PXE (attendu, lit `/etc/nixos/hardware-configuration.nix`).
**Ce que ces commandes ne couvrent pas** : elles vérifient que la config s'évalue et que les paquets existent, pas que le poste installé fonctionne réellement (Wi-Fi, wallpaper, LightDM, avatar...). Seul un test sur machine réelle (étape 8 plus haut) le confirme.
## Connexion Wi-Fi ## Connexion Wi-Fi
Pour un PC sans câble réseau (après l'installation, ou en dépannage). Pour un PC sans câble réseau (après l'installation, ou en dépannage).
### Option 1 — NetworkManager ### Option 1 — NetworkManager
Déjà actif par défaut dans ce repo (`hosts/common/base.nix`). Rien à ajouter dans `configuration.nix`. Déjà actif par défaut dans ce repo (`hosts/common/default.nix`). Rien à ajouter dans `configuration.nix`.
```nix ```nix
networking.networkmanager.enable = true; networking.networkmanager.enable = true;
@@ -426,7 +590,86 @@ Aucun paquet à installer manuellement : `enable = true` suffit dans les deux ca
Après édition : Après édition :
```bash ```bash
sudo nixos-rebuild switch --flake .#usagers sudo nixos-rebuild switch --flake .#nixos
```
---
# Environnement de dev sous Windows (WSL + Nix)
Pour tester `nix flake check` / `nix build` depuis un poste Windows. Nix ne tourne pas nativement sur Windows : il faut un vrai noyau Linux, donc WSL2.
## Installer WSL2
PowerShell **en administrateur** :
```powershell
wsl --install
```
Installe WSL2 + Ubuntu par défaut. Redémarrer si demandé, puis laisser Ubuntu créer l'utilisateur Linux (indépendant du compte Windows).
## Installer Nix
Dans le terminal Ubuntu, commande officielle (nixos.org) :
```bash
curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install | sh -s -- --daemon
```
Fermer/rouvrir le terminal ensuite. Vérifier :
```bash
nix --version
```
Les flakes sont activées par défaut sur les versions récentes de Nix (2.35+) — rien à configurer en plus. Si `nix flake check` répond une erreur `experimental-features`, ajouter dans `/etc/nix/nix.conf` :
```
experimental-features = nix-command flakes
```
## Utiliser les repos depuis WSL
Les fichiers Windows sont accessibles depuis WSL sous `/mnt/c/...` — pas besoin de cloner une deuxième fois :
```bash
cd "/mnt/c/Users/<toi>/Desktop/Claude/Nixos/nixos_fleet"
nix flake check --no-build
```
`pxe-server` échouera à `nix flake check` (sans `--impure`) : il lit `/etc/nixos/hardware-configuration.nix`, qui n'existe que sur le vrai serveur PXE. Normal, pas une régression.
## Si "pas de réseau" dans WSL
Symptôme typique : `ping 8.8.8.8` répond, mais `curl https://nixos.org` (ou tout domaine) time out à la résolution — le tunnel DNS de WSL2 est cassé (fréquent avec VPN/antivirus). Vérifier :
```bash
curl -m 5 https://1.1.1.1 # IP brute : doit marcher
curl -m 5 https://nixos.org # domaine : si ça time out, c'est le DNS
```
Correctif (côté Windows, PowerShell, pas besoin d'être admin) : créer `%USERPROFILE%\.wslconfig` avec :
```
[wsl2]
networkingMode=mirrored
```
Puis `wsl --shutdown` et rouvrir Ubuntu. Ce mode partage directement la pile réseau de Windows à WSL, au lieu de passer par le NAT + proxy DNS interne.
## Construire l'ISO et la graver, depuis Windows
Commandes complètes : voir "Étape 0 — Support d'installation" tout en haut de ce README.
Alternative avancée à Rufus (rattacher la clé USB à WSL et faire `dd` comme sur Linux) : [usbipd-win](https://github.com/dorssel/usbipd-win).
## Appeler `nix` depuis un outil qui pilote WSL sans shell interactif
`wsl.exe nix --version` échoue avec `nix: command not found` : une commande non-interactive ne source pas le profil qui ajoute `nix` au PATH. Forcer un shell de login :
```bash
wsl.exe -e bash -lc "nix --version"
``` ```
--- ---
Generated
+22
View File
@@ -20,6 +20,27 @@
"type": "github" "type": "github"
} }
}, },
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1789182507,
"narHash": "sha256-9nt4W0HNNP84B+b37yRs9Zxat1ZR5guJxEZDEjAgex4=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "b1d1b60084970f9d1e2b72662639dab6d039be71",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-26.05",
"repo": "home-manager",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1788807765, "lastModified": 1788807765,
@@ -39,6 +60,7 @@
"root": { "root": {
"inputs": { "inputs": {
"disko": "disko", "disko": "disko",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
} }
+33 -3
View File
@@ -7,17 +7,47 @@
url = "github:nix-community/disko"; url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# Même branche release-26.05 que nixpkgs (confirmé : la branche existe
# bien sur nix-community/home-manager). Gère les réglages Cinnamon par
# utilisateur (dconf, fichiers dans $HOME) à la place des scripts
# d'activation système à la main qu'on avait avant.
home-manager = {
url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = outputs =
{ self, nixpkgs, disko, ... }: { self, nixpkgs, disko, home-manager, ... }:
let
repoUrl = "git+https://gitea.miniclic.net/Damien/Nixos_Fleet.git";
in
{ {
nixosConfigurations = { nixosConfigurations = {
usagers = nixpkgs.lib.nixosSystem { nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
modules = [ modules = [
disko.nixosModules.disko disko.nixosModules.disko
./hosts/usagers/configuration.nix home-manager.nixosModules.home-manager
./hosts/nixos/configuration.nix
./diskos/plain.nix
{ system.autoUpgrade.flake = "${repoUrl}#nixos"; }
];
};
# Même profil logiciel que "nixos", mais sans disko : pour une
# machine où NixOS est déjà installé (partitionnement existant,
# quel qu'il soit, à ne pas toucher) -- utilise le
# hardware-configuration.nix réel de cette machine à la place de
# diskos/plain.nix, comme pxe-server. --impure obligatoire au
# nixos-rebuild (lit un chemin hors du flake).
nixos-existing = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
home-manager.nixosModules.home-manager
./hosts/nixos/configuration.nix
/etc/nixos/hardware-configuration.nix
{ system.autoUpgrade.flake = "${repoUrl}#nixos-existing"; }
]; ];
}; };
-23
View File
@@ -1,23 +0,0 @@
# Compte admin des machines de la flotte : root, mot de passe "nixos"
# STABLE (pas d'expiration forcée ici, contrairement à nixos_users) —
# ces machines sont génériques/partagées, un mot de passe connu et
# constant sur tout le parc est voulu pour l'administration courante.
{ pkgs, ... }:
{
users.mutableUsers = true;
users.users.root = {
initialPassword = "nixos";
};
# Login root direct autorisé nativement dès qu'un mot de passe est
# défini (console/tty). SSH reste sur le défaut NixOS (clé uniquement) :
# le mot de passe "nixos" n'ouvre pas la porte à distance, seulement en
# local sur la machine.
# `initialPassword` n'est appliqué par NixOS qu'à la création du compte,
# donc un rebuild/auto-upgrade ultérieur ne le réécrit jamais — mais ici
# on ne force pas non plus de changement : "nixos" doit rester valide
# indéfiniment sur la flotte.
}
-76
View File
@@ -1,76 +0,0 @@
# Socle commun à toutes les machines de la flotte (repo public).
{ pkgs, ... }:
{
# Toutes les machines de la flotte portent le même nom : pas
# d'identification individuelle pour l'instant (voir README du repo
# racine). À revoir plus tard via le numéro de série DMI si un vrai
# besoin d'identification apparaît.
networking.hostName = "Nixos_Fleet";
networking.networkmanager.enable = true;
time.timeZone = "Europe/Paris";
i18n.defaultLocale = "fr_FR.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "fr_FR.UTF-8";
LC_IDENTIFICATION = "fr_FR.UTF-8";
LC_MEASUREMENT = "fr_FR.UTF-8";
LC_MONETARY = "fr_FR.UTF-8";
LC_NAME = "fr_FR.UTF-8";
LC_NUMERIC = "fr_FR.UTF-8";
LC_PAPER = "fr_FR.UTF-8";
LC_TELEPHONE = "fr_FR.UTF-8";
LC_TIME = "fr_FR.UTF-8";
};
services.xserver.xkb = {
layout = "fr";
variant = "";
};
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.configurationLimit = 10;
services.openssh.enable = true;
networking.firewall.enable = true;
# Certains PC du parc n'ont que 4 Go de RAM (Cinnamon + Firefox +
# LibreOffice + VLC peuvent solliciter la mémoire) — swap compressé en
# RAM, marche identiquement quelle que soit la machine.
zramSwap = {
enable = true;
memoryPercent = 50;
};
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nixpkgs.config.allowUnfree = true;
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
# Convergence : chaque machine de la flotte tire ses mises à jour du
# même repo public, sans intervention humaine (pas de netrc nécessaire
# ici, contrairement au repo nixos_users). Voir README pour adapter
# l'URL réelle du repo.
system.autoUpgrade = {
enable = true;
dates = "daily";
flake = "git+https://gitea.miniclic.net/Damien/Nixos_Fleet.git#usagers";
};
environment.systemPackages = with pkgs; [
git
curl
wget
htop
nixfmt
];
system.stateVersion = "26.05";
}
+538
View File
@@ -0,0 +1,538 @@
# Socle commun à toutes les machines de la flotte (repo public) : compte
# système, réseau/durcissement, bureau Cinnamon/Firefox. Un seul fichier
# plutôt que plusieurs petits (base/desktop/accounts) -- chaque option
# n'a qu'un seul endroit où la chercher.
{ pkgs, lib, ... }:
let
# PREMIER ESSAI FAUX (corrigé ici) : Cinnamon ne suit PAS le mécanisme
# GNOME standard (gnome-background-properties via XDG_DATA_DIRS).
# Vérifié dans le vrai source du sélecteur, cs_backgrounds.py
# (linuxmint/cinnamon, module cinnamon-settings) :
# `properties_dir = "/usr/share/cinnamon-background-properties"`, un
# chemin ABSOLU EN DUR, jamais résolu via XDG_DATA_DIRS -- même en
# mettant le bon XML au bon endroit dans le store, Cinnamon ne
# regarde jamais là. mint-artwork (déjà installé par défaut avec
# Cinnamon) ne fournit de toute façon aucun XML de ce genre -- ses
# images sont sur le disque mais invisibles dans le sélecteur.
#
# Solution : générer le XML nous-mêmes (à partir de ce que
# mint-artwork contient réellement, pas une liste en dur -- chemin
# confirmé dans nixpkgs/pkgs/by-name/mi/mint-artwork/package.nix qui
# sed-remplace littéralement usr/share/backgrounds/linuxmint/*.jpg),
# PUIS forcer /usr/share/cinnamon-background-properties à pointer
# dessus via systemd-tmpfiles (seul moyen d'atteindre un chemin en
# dur sur un système qui n'a pas de /usr/share FHS classique).
cinnamonDefaultWallpapers = pkgs.runCommand "cinnamon-default-wallpapers-properties" { } ''
mkdir -p $out/share/cinnamon-background-properties
{
echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd">'
echo '<wallpapers>'
for f in ${pkgs.mint-artwork}/share/backgrounds/linuxmint/*.jpg \
${pkgs.mint-artwork}/share/backgrounds/linuxmint/*.png \
${./wallpaper/Nix/13977413.png}; do
[ -e "$f" ] || continue
name=$(basename "$f")
echo " <wallpaper deleted=\"false\">"
echo " <name>$name</name>"
echo " <filename>$f</filename>"
echo " <options>zoom</options>"
echo " </wallpaper>"
done
echo '</wallpapers>'
} > $out/share/cinnamon-background-properties/nixos-fleet-defaults.xml
'';
in
{
imports = [ ../../modules/anssi ];
# L+ crée aussi les dossiers parents manquants (/usr, /usr/share,
# confirmé dans la doc systemd-tmpfiles.d) -- pas besoin de les créer
# à la main. Réappliqué à chaque nixos-rebuild switch (pas seulement
# au boot), donc pas besoin de reboot pour que ça prenne effet.
#
# Symlink un FICHIER précis dans le dossier, pas le dossier entier :
# /usr/share/cinnamon-background-properties reste alors un vrai
# dossier (auto-créé par L+) où d'autres XML peuvent être ajoutés à
# côté (ex: hosts/oreo/configuration.nix côté nixos_users) sans
# conflit -- symlinker le dossier entier depuis deux endroits
# différents écraserait l'un des deux.
systemd.tmpfiles.rules = [
"L+ /usr/share/cinnamon-background-properties/common-defaults.xml - - - - ${cinnamonDefaultWallpapers}/share/cinnamon-background-properties/nixos-fleet-defaults.xml"
];
# Nom uniforme, pas d'identification individuelle pour l'instant (voir
# README). À revoir via le numéro de série DMI si le besoin apparaît.
networking.hostName = "Nixos_Fleet";
networking.networkmanager.enable = true;
# Couverture matérielle maximale (wifi/Bluetooth/PCI) sur un parc
# hétérogène inconnu à l'avance. Inclut enableRedistributableFirmware.
hardware.enableAllFirmware = lib.mkDefault true;
# Durcissement ANSSI (modules/anssi/, vendored depuis SécurixOS).
# level "intermediary" : couvre déjà tout ce qui était réglé à la main
# sur le profil oreo (R9/R11/R12/R14). category "client" : postes de
# travail, pas des serveurs.
#
# R12 force net.ipv4.ip_forward = 0 : sans impact sur des postes
# clients. Ne pas importer ce module sur pxe-server/configuration.nix
# (a besoin du forwarding inverse).
security.anssi = {
enable = true;
level = "intermediary";
category = "client";
# R8 (mitigations CPU) coupe l'hyperthreading via "mds=full,nosmt" :
# trop coûteux sur le matériel bas de gamme du parc. Désactivé via
# exception plutôt que contourné en silence ; réappliqué ci-dessous
# sans le "nosmt".
exceptions.R8.rationale = "SMT conservé : impact perf trop lourd sur le parc (matériel bas de gamme, parfois 4 Go de RAM). Mitigations réappliquées ci-dessous sans 'nosmt'.";
};
# R8 (modules/anssi/kernel-options.nix), sauf mds=full,nosmt -> mds=full.
boot.kernelParams = [
"l1tf=full,force"
"page_poison=on"
"pti=on"
"slab_nomerge=yes"
"slub_debug=FZP"
"spec_store_bypass_disable=seccomp"
"spectre_v2=on"
"mds=full" # sans ",nosmt" : hyperthreading conservé
"mce=0"
"page_alloc.shuffle=1"
"rng_core.default_quality=500"
];
# Pas couvert par le module ANSSI (R45 en exception : AppArmor
# partiellement supporté sur NixOS).
systemd.coredump.enable = false;
security.apparmor.packages = [ pkgs.apparmor-profiles ];
security.apparmor.enableCache = true;
# Bluetooth (même config que le profil oreo) : pas d'allumage auto au
# boot, blueman pour la gestion depuis Cinnamon.
hardware.bluetooth = {
enable = true;
powerOnBoot = false;
};
services.blueman.enable = true;
time.timeZone = "Europe/Paris";
i18n.defaultLocale = "fr_FR.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "fr_FR.UTF-8";
LC_IDENTIFICATION = "fr_FR.UTF-8";
LC_MEASUREMENT = "fr_FR.UTF-8";
LC_MONETARY = "fr_FR.UTF-8";
LC_NAME = "fr_FR.UTF-8";
LC_NUMERIC = "fr_FR.UTF-8";
LC_PAPER = "fr_FR.UTF-8";
LC_TELEPHONE = "fr_FR.UTF-8";
LC_TIME = "fr_FR.UTF-8";
};
services.xserver.xkb = {
layout = "fr";
variant = "";
};
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.configurationLimit = 10;
services.openssh.enable = true;
networking.firewall.enable = true;
# Swap compressé en RAM : couvre le bas du parc (4 Go, desktop complet).
zramSwap = {
enable = true;
memoryPercent = 50;
};
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nixpkgs.config.allowUnfree = true;
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
# Convergence automatique depuis le repo public, sans netrc. Cadence
# weekly (même format que oreo, plutôt que daily). persistent = true
# (déjà la valeur par défaut de ce module -- explicité ici) : si la
# machine est éteinte au moment prévu, la maj se lance dès le
# prochain démarrage plutôt que d'attendre la semaine suivante.
# `flake` n'est PAS fixé ici : il dépend de l'attribut réellement
# utilisé pour cette machine (nixos vs nixos-existing, disko ou pas)
# -- injecté par flake.nix, comme dans nixos_users. Le fixer ici
# ferait basculer une machine "nixos-existing" vers l'attribut
# "nixos" (disko) à son prochain auto-upgrade, avec un layout disque
# potentiellement incompatible avec l'existant.
system.autoUpgrade = {
enable = true;
persistent = true;
dates = "weekly";
};
# Compte admin de la flotte : root, mot de passe "nixos" STABLE (pas
# d'expiration forcée) -- machines génériques/partagées, un mot de
# passe connu et constant sur tout le parc est voulu pour
# l'administration courante. `initialPassword` n'est appliqué qu'à la
# création du compte : un rebuild/auto-upgrade ultérieur ne le réécrit
# jamais, mais on ne force pas non plus de changement -- "nixos" doit
# rester valide indéfiniment sur la flotte. Login root direct autorisé
# en local (console/tty) ; SSH reste sur le défaut NixOS (clé
# uniquement), ce mot de passe n'ouvre pas la porte à distance.
users.mutableUsers = true;
users.users.root.initialPassword = "nixos";
# --- Bureau Cinnamon + Firefox --------------------------------------
services.xserver.enable = true;
services.xserver.displayManager.lightdm.enable = true;
services.xserver.displayManager.lightdm.background = "/etc/nixos/hosts/common/wallpaper/Nix/13977413.png";
services.xserver.desktopManager.cinnamon.enable = true;
services.accounts-daemon.enable = true;
environment.cinnamon.excludePackages = with pkgs; [
celluloid
warpinator
];
# dconf par utilisateur via home-manager, chargé depuis un fichier ini
# brut (hosts/nixos/dconf.ini) plutôt que du dconf.settings typé --
# même mécanisme que oreo (nixos_users), pour la même raison : ça se
# met à jour par un simple `dconf dump /org/cinnamon/ > dconf.ini` +
# commit depuis une machine réelle, pas en retranscrivant chaque clé à
# la main en Nix. Repris tel quel du dconf d'oreo (panneau, thèmes,
# réglages GTK/Nemo...), sauf le chemin du fond d'écran (commun,
# pas par-profil) et l'ID Warpinator (retiré : une valeur fixe
# partagée par tout le parc casserait l'appairage).
home-manager.useGlobalPkgs = true; # partage le pkgs du système, pas de deuxième évaluation (RAM)
home-manager.backupFileExtension = "hm-backup"; # évite un échec si un fichier existe déjà (migration)
home-manager.users.nixos =
{ pkgs, lib, ... }:
{
home.stateVersion = "26.05";
# DBUS_SESSION_BUS_ADDRESS / dbus-run-session : même logique que le
# module home-manager dconf.nix officiel (dconf a besoin d'un bus
# D-Bus pour écrire, on en lance un temporaire s'il n'y en a pas
# déjà un actif).
home.activation.nixosDconf = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
if [[ -v DBUS_SESSION_BUS_ADDRESS ]]; then
DCONF_DBUS_RUN_SESSION=""
else
DCONF_DBUS_RUN_SESSION="${pkgs.dbus}/bin/dbus-run-session --dbus-daemon=${pkgs.dbus}/bin/dbus-daemon"
fi
run $DCONF_DBUS_RUN_SESSION ${pkgs.dconf}/bin/dconf load / < ${../nixos/dconf.ini}
'';
};
# Reprend la config Firefox d'oreo (nixos_users/hosts/oreo/configuration.nix)
# -- c'est elle qui tue la page d'accueil/configuration au premier
# lancement (aboutwelcome, newtabpage...) et bloque les pubs/contenu
# sponsorisé bien plus largement que l'ancienne version ici (qui ne
# coupait que le strict showSponsored). Deux choses changées par
# rapport à oreo, volontairement : SearXNG (perso, privé) remplacé par
# DuckDuckGo (public, intégré à Firefox) ; ExtensionSettings réduit à
# uBlock Origin + Privacy Badger (oreo en a une quinzaine, trop pour
# un poste générique de flotte).
programs.firefox = {
enable = true;
languagePacks = [ "fr" ];
preferences = {
"browser.startup.homepage" = "https://duckduckgo.com";
"privacy.resistFingerprinting" = true;
"network.trr.mode" = 3;
"network.trr.uri" = "https://dns.quad9.net/dns-query";
"network.trr.fallback-resolver" = "https://mozilla.cloudflare-dns.com/dns-query";
"browser.newtabpage.enabled" = false;
"browser.newtabpage.activity-stream.enabled" = false;
"browser.startup.page" = 1;
# Page de configuration/bienvenue au premier lancement -- désactivée.
"browser.aboutwelcome.enabled" = false;
"browser.newtabpage.pinned" = "[]";
"browser.newtabpage.section.discovery" = false;
"places.history.enabled" = false;
"privacy.clearOnShutdown.history" = true;
"privacy.clearOnShutdown.cache" = true;
"privacy.clearOnShutdown.downloads" = true;
"privacy.clearOnShutdown.cookies" = false;
"privacy.clearOnShutdown.sessions" = false;
"privacy.clearOnShutdown.formdata" = false;
"privacy.clearOnShutdown.offlineApps" = true;
# Onglets verticaux à gauche par défaut
"sidebar.revamp" = true;
"sidebar.verticalTabs" = true;
# Virer "Recent browsing" (Firefox View)
"browser.tabs.firefox-view" = false;
"browser.tabs.firefox-view-next" = false;
# Virer "List all tabs" (bouton chevron dropdown)
"browser.tabs.tabmanager.enabled" = true;
# Virer suggestions de recherche (y compris tendances)
"browser.search.suggest.enabled" = false;
"browser.urlbar.suggest.searches" = false;
"browser.urlbar.suggest.trending" = false;
"browser.newtabpage.activity-stream.showSponsored" = false;
# HTTPS obligatoire en permanence
"dom.security.https_only_mode" = true;
"dom.security.https_only_mode_ever_enabled" = true;
# Désactiver WebRTC (fuite d'IP réelle même via VPN)
"media.peerconnection.enabled" = false;
# Désactiver la géolocalisation
"geo.enabled" = false;
# Désactiver DRM (Widevine)
"media.eme.enabled" = false;
# Désactiver le prefetching (DNS/liens) — fuite d'intention de navigation
"network.dns.disablePrefetch" = true;
"network.prefetch-next" = false;
"network.predictor.enabled" = false;
"network.http.speculative-parallel-limit" = 0;
# Désactiver Safe Browsing distant (envoie des hachages d'URL à Google)
"browser.safebrowsing.malware.enabled" = false;
"browser.safebrowsing.phishing.enabled" = false;
# Désactiver la détection de portail captif (requête réseau vers Mozilla)
"network.captive-portal-service.enabled" = false;
"network.connectivity-service.enabled" = false;
# Isolation par premier-parti (empêche le cross-site tracking via cache/cookies)
"privacy.firstparty.isolate" = true;
# Désactiver WebGL (vecteur de fingerprinting majeur)
"webgl.disabled" = true;
# Désactiver l'API batterie (fingerprinting)
"dom.battery.enabled" = false;
# Bloquer l'autoplay média
"media.autoplay.default" = 5;
# Référent minimal envoyé aux sites
"network.http.referer.XOriginPolicy" = 2;
# Do Not Track : plus d'UI depuis les versions récentes de Firefox,
# mais le pref existe encore et envoie bien l'en-tête DNT=1.
"privacy.donottrackheader.enabled" = true;
};
policies = {
# Langpack fr téléchargé via languagePacks ci-dessus ; sans
# RequestedLocales, Firefox reste quand même en anglais (le
# langpack est juste installé, pas activé).
RequestedLocales = [
"fr"
"en-US"
];
DisableFirefoxStudies = true;
DisableFirefoxAccounts = false;
DisableFirefoxScreenshots = true;
DisableForgetButton = true;
DisableMasterPasswordCreation = true;
DisableProfileImport = true;
DisableProfileRefresh = true;
DisableSetDesktopBackground = true;
DisablePocket = true;
DisableTelemetry = true;
DisableFormHistory = true;
DisablePasswordReveal = true;
SanitizeOnShutdown = false;
GenerativeAI.Enable = false;
FirefoxSuggest = {
WebSuggestions = false;
SponsoredSuggestions = false;
ImproveSuggest = false;
};
FirefoxHome = {
SponsoredTopSites = false;
SponsoredPocket = false;
TopSites = false;
Highlights = false;
Pocket = false;
Snippets = false;
# Widget météo (demande la géolocalisation) -- ajouté à
# FirefoxHome en Firefox 152 / ESR 140.12.
Weather = false;
Locked = true;
};
UserMessaging = {
WhatsNew = false;
MoreFromMozilla = false;
};
EnableTrackingProtection = {
Value = true;
Locked = true;
Cryptomining = true;
Fingerprinting = true;
};
BlockAboutConfig = false;
BlockAboutProfiles = false;
BlockAboutSupport = false;
DontCheckDefaultBrowser = true;
HardwareAcceleration = true;
OfferToSaveLogins = false;
DefaultDownloadDirectory = "$HOME/Downloads";
PromptForDownloadLocation = true;
DisplayBookmarksToolbar = "never";
Preferences = {
"privacy.globalprivacycontrol.enabled" = {
Value = true;
Status = "locked";
};
"privacy.globalprivacycontrol.functionality.enabled" = {
Value = true;
Status = "locked";
};
};
# DuckDuckGo est un moteur intégré à Firefox, pas besoin de l'Add.
Homepage = {
URL = "https://duckduckgo.com";
Locked = true;
StartPage = "homepage";
};
SearchEngines = {
Default = "DuckDuckGo";
PreventInstalls = true;
};
"browser.aboutConfig.showWarning" = {
Value = false;
Status = "locked";
};
NewTabPage = false;
ExtensionSettings = {
"uBlock0@raymondhill.net" = {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi";
installation_mode = "force_installed";
};
"jid1-MnnxcxisBPnSXQ@jetpack" = {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/privacy-badger17/latest.xpi";
installation_mode = "force_installed";
};
};
};
};
environment.systemPackages = with pkgs; [
cinnamonDefaultWallpapers
git
curl
wget
htop
nixfmt
# === Office/Notes ===
libreoffice
pdfarranger
simple-scan
# === System tools ===
btop
unzip
screenfetch
tmux
fwupd
fwupd-efi
ncdu
gdu
hplip
gutenprint
cups
inxi
ntfs3g
# === Communication/Media ===
thunderbird
ffmpeg
vlc
mpv
strawberry
];
# Flatpak
services.flatpak.enable = true;
xdg.portal.enable = true;
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
systemd.services.flatpak-repo = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
path = [ pkgs.flatpak ];
script = ''
flatpak remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
'';
};
systemd.services.flatpak-install = {
wantedBy = [ "multi-user.target" ];
after = [
"flatpak-repo.service"
"network-online.target"
];
wants = [ "network-online.target" ];
requires = [ "flatpak-repo.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
path = [ pkgs.flatpak ];
script = ''
flatpak install -y flathub \
net.waterfox.waterfox \
eu.betterbird.Betterbird
'';
};
systemd.services.flatpak-update = {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.flatpak ];
script = ''
flatpak update -y
'';
};
programs.appimage = {
enable = true;
binfmt = true;
};
system.stateVersion = "26.05";
}
-57
View File
@@ -1,57 +0,0 @@
# Bureau Cinnamon générique pour la flotte. Adapté (pas importé) depuis
# nixos_cinnamon/hosts/cinnamon/desktop-cinnamon.nix : les deux repos
# restent indépendants au `git pull`.
{ pkgs, ... }:
{
services.xserver.enable = true;
services.xserver.displayManager.lightdm.enable = true;
services.xserver.desktopManager.cinnamon.enable = true;
services.accounts-daemon.enable = true;
environment.cinnamon.excludePackages = with pkgs; [
celluloid
warpinator
];
# Réglages de confidentialité génériques (pas de SearXNG/DNS personnels
# comme sur le poste perso — cette config est destinée à être publique).
programs.firefox = {
enable = true;
languagePacks = [ "fr" ];
preferences = {
"privacy.resistFingerprinting" = true;
"browser.newtabpage.activity-stream.showSponsored" = false;
"browser.search.suggest.enabled" = false;
"dom.security.https_only_mode" = true;
"media.peerconnection.enabled" = false;
"geo.enabled" = false;
"network.captive-portal-service.enabled" = false;
};
policies = {
DisableTelemetry = true;
DisablePocket = true;
DisableFirefoxStudies = true;
OfferToSaveLogins = false;
DontCheckDefaultBrowser = true;
EnableTrackingProtection = {
Value = true;
Locked = true;
};
ExtensionSettings = {
"uBlock0@raymondhill.net" = {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi";
installation_mode = "force_installed";
};
};
};
};
environment.systemPackages = with pkgs; [
libreoffice
thunderbird
vlc
];
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

+22
View File
@@ -0,0 +1,22 @@
# Profil générique "nixos" : poste non-nominatif de la flotte.
# Le module disque (diskos/plain.nix pour une install neuve, ou le
# hardware-configuration.nix réel d'une machine déjà installée) est
# injecté par flake.nix, pas ici -- ce fichier ne connaît pas le
# layout disque, comme dans nixos_users.
{ ... }:
{
imports = [
../common
];
# Compte de session non-nominatif, mot de passe "nixos" stable, sans
# expiration forcée (voir hosts/common). Nom d'utilisateur "nixos"
# aussi, pour unifier avec le mot de passe -- un seul mot à retenir.
users.users.nixos = {
isNormalUser = true;
description = "Nixos";
extraGroups = [ "networkmanager" ];
initialPassword = "nixos";
};
}
+199
View File
@@ -0,0 +1,199 @@
[org/cinnamon]
enabled-applets=['panel1:left:0:menu@cinnamon.org:0', 'panel1:left:1:separator@cinnamon.org:1', 'panel1:left:2:grouped-window-list@cinnamon.org:2', 'panel1:right:0:systray@cinnamon.org:3', 'panel1:right:1:xapp-status@cinnamon.org:4', 'panel1:right:2:notifications@cinnamon.org:5', 'panel1:right:3:printers@cinnamon.org:6', 'panel1:right:4:removable-drives@cinnamon.org:7', 'panel1:right:5:keyboard@cinnamon.org:8', 'panel1:right:6:favorites@cinnamon.org:9', 'panel1:right:7:network@cinnamon.org:10', 'panel1:right:8:sound@cinnamon.org:11', 'panel1:right:9:power@cinnamon.org:12', 'panel1:right:10:calendar@cinnamon.org:13', 'panel1:right:11:cornerbar@cinnamon.org:14']
next-applet-id=15
[org/cinnamon/desktop/background]
picture-uri='file:///etc/nixos/hosts/common/wallpaper/Nix/13977413.png'
[org/cinnamon/desktop/background/slideshow]
delay=15
image-source='directory:///etc/nixos/hosts/common/wallpaper/Nix'
[org/cinnamon/desktop/input-sources]
sources=[('xkb', 'fr')]
[org/cinnamon/desktop/interface]
cursor-size=24
cursor-theme='Bibata-Modern-Classic'
gtk-theme='Mint-Y-Dark-Blue'
icon-theme='Mint-Y-Blue'
toolkit-accessibility=false
[org/cinnamon/desktop/peripherals/mouse]
double-click=400
drag-threshold=8
speed=0.0
[org/cinnamon/desktop/peripherals/touchpad]
natural-scroll=false
speed=0.0
[org/cinnamon/desktop/screensaver]
layout-group=0
[org/cinnamon/desktop/sound]
event-sounds=false
[org/cinnamon/gestures]
swipe-down-2='PUSH_TILE_DOWN::end'
swipe-down-3='TOGGLE_OVERVIEW::end'
swipe-down-4='VOLUME_DOWN::end'
swipe-left-2='PUSH_TILE_LEFT::end'
swipe-left-3='WORKSPACE_NEXT::end'
swipe-left-4='WINDOW_WORKSPACE_PREVIOUS::end'
swipe-right-2='PUSH_TILE_RIGHT::end'
swipe-right-3='WORKSPACE_PREVIOUS::end'
swipe-right-4='WINDOW_WORKSPACE_NEXT::end'
swipe-up-2='PUSH_TILE_UP::end'
swipe-up-3='TOGGLE_EXPO::end'
swipe-up-4='VOLUME_UP::end'
tap-3='MEDIA_PLAY_PAUSE::end'
[org/cinnamon/settings-daemon/plugins/color]
night-light-enabled=true
night-light-last-coordinates=(48.866667, 2.3333330000000001)
night-light-schedule-mode='always'
night-light-temperature=uint32 5400
[org/cinnamon/theme]
name='Mint-Y-Dark-Blue'
[org/gnome/desktop/a11y/applications]
screen-reader-enabled=false
[org/gnome/desktop/a11y/mouse]
dwell-click-enabled=false
dwell-threshold=10
dwell-time=1.2
secondary-click-enabled=false
secondary-click-time=1.2
[org/gnome/desktop/input-sources]
sources=[('xkb', 'fr')]
xkb-options=['terminate:ctrl_alt_bksp']
[org/gnome/desktop/interface]
can-change-accels=false
clock-show-date=false
clock-show-seconds=false
cursor-blink=true
cursor-blink-time=1200
cursor-blink-timeout=10
cursor-size=24
cursor-theme='Bibata-Modern-Classic'
enable-animations=true
font-name='Ubuntu 10'
gtk-color-palette='black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90'
gtk-color-scheme=''
gtk-enable-primary-paste=true
gtk-im-module=''
gtk-im-preedit-style='callback'
gtk-im-status-style='callback'
gtk-key-theme='Default'
gtk-theme='Mint-Y-Dark-Blue'
gtk-timeout-initial=200
gtk-timeout-repeat=20
icon-theme='Mint-Y-Blue'
menubar-accel='F10'
menubar-detachable=false
menus-have-tearoff=false
scaling-factor=uint32 0
text-scaling-factor=1.0
toolbar-detachable=false
toolbar-icons-size='large'
toolbar-style='both-horiz'
toolkit-accessibility=false
[org/gnome/desktop/peripherals/mouse]
accel-profile='default'
double-click=400
drag-threshold=8
left-handed=false
middle-click-emulation=false
natural-scroll=false
speed=0.0
[org/gnome/desktop/privacy]
disable-camera=false
disable-microphone=false
disable-sound-output=false
old-files-age=uint32 30
recent-files-max-age=7
remember-recent-files=true
remove-old-temp-files=false
remove-old-trash-files=false
[org/gnome/desktop/sound]
event-sounds=false
input-feedback-sounds=false
theme-name='LinuxMint'
[org/gnome/desktop/wm/preferences]
action-double-click-titlebar='toggle-maximize'
action-middle-click-titlebar='lower'
action-right-click-titlebar='menu'
audible-bell=false
auto-raise=false
auto-raise-delay=500
button-layout=':minimize,maximize,close'
disable-workarounds=false
focus-mode='click'
focus-new-windows='smart'
mouse-button-modifier='<Alt>'
num-workspaces=4
raise-on-click=true
resize-with-right-button=true
theme='Mint-Y'
titlebar-font='Ubuntu Medium 10'
titlebar-uses-system-font=false
visual-bell=false
visual-bell-type='fullscreen-flash'
workspace-names=@as []
[org/gnome/file-roller/dialogs/extract]
height=800
recreate-folders=true
skip-newer=false
width=1000
[org/gnome/file-roller/file-selector]
show-hidden=false
sidebar-size=300
sort-method='name'
sort-type='ascending'
window-size=(-1, -1)
[org/gnome/file-roller/listing]
list-mode='as-folder'
name-column-width=57
show-path=false
sort-method='name'
sort-type='ascending'
[org/gnome/file-roller/ui]
sidebar-width=200
window-height=480
window-width=600
[org/gtk/settings/file-chooser]
date-format='regular'
location-mode='path-bar'
show-hidden=false
show-size-column=true
show-type-column=true
sidebar-width=176
sort-column='name'
sort-directories-first=true
sort-order='ascending'
type-format='category'
window-position=(202, 73)
window-size=(1096, 822)
[org/nemo/window-state]
geometry='800x550+349+125'
maximized=false
sidebar-bookmark-breakpoint=3
start-with-sidebar=true
[org/x/apps/portal]
color-scheme='prefer-dark'
-20
View File
@@ -1,20 +0,0 @@
# Profil générique "usagers" : poste non-nominatif de la flotte.
{ ... }:
{
imports = [
../common/base.nix
../common/accounts.nix
../common/desktop.nix
../../diskos/plain.nix
];
# Compte de session non-nominatif, mot de passe "nixos" stable, sans
# expiration forcée (voir hosts/common/accounts.nix).
users.users.usagers = {
isNormalUser = true;
description = "Usagers";
extraGroups = [ "networkmanager" ];
initialPassword = "nixos";
};
}
+108 -32
View File
@@ -1,38 +1,113 @@
# Pas de shebang ici : ce fichier est injecté comme corps de script par # Injecté comme corps de script par pkgs.writeShellScriptBin
# pkgs.writeShellScriptBin dans installer-base.nix (qui ajoute juste # (installer-base.nix) : pas de mode strict par défaut, d'où le `set`
# `#!...`, PAS de mode strict — d'où le `set` explicite ci-dessous : sans # explicite ci-dessous.
# lui, une erreur (ex: DNS indisponible) n'arrête pas le script, qui
# continuait jusqu'au reboot final en masquant l'échec.
set -euo pipefail set -euo pipefail
# Installeur unique pour les deux repos : # Installeur unique pour les deux repos :
# - branche "flotte" -> nixos_fleet (public, jamais chiffré) # - branche "flotte" -> nixos_fleet (public, jamais chiffré)
# - branche "utilisateur nommé" -> nixos_users (privé, chiffrement au choix) # - branche "utilisateur nommé" -> nixos_users (privé, chiffrement au choix)
# Ce script ne contient aucun secret : le token du repo privé est saisi en # Le token du repo privé est saisi en direct, jamais stocké dans ce
# direct, jamais stocké dans ce fichier ni dans un repo git. # fichier ni dans un repo git.
FLEET_REPO="git+https://gitea.miniclic.net/Damien/Nixos_Fleet.git" FLEET_REPO="git+https://gitea.miniclic.net/Damien/Nixos_Fleet.git"
USERS_REPO_HOST="gitea.miniclic.net" USERS_REPO_HOST="gitea.miniclic.net"
USERS_REPO_PATH="Damien/Nixos_Users.git" USERS_REPO_PATH="Damien/Nixos_Users.git"
# Le script se lance dès le login (autologin), potentiellement avant que # Le script se lance dès le login (autologin), potentiellement avant que
# NetworkManager ait fini de négocier le DHCP. On attend une route par # NetworkManager ait fini de négocier le DHCP.
# défaut avant de continuer (jusqu'à 30s), plutôt que d'échouer trop tôt.
echo "Attente du réseau..." echo "Attente du réseau..."
for _ in $(seq 1 30); do for _ in $(seq 1 30); do
ip route | grep -q '^default' && break ip route | grep -q '^default' && break
sleep 1 sleep 1
done done
# 4 étapes séparées (clone -> partition -> swap sur le disque cible ->
# build) plutôt qu'un seul `disko-install` : ce dernier construit tout
# dans le tmpfs RAM de l'environnement live, qui sature sur les PC à
# 4 Go avant même de toucher le disque cible. Le swapfile posé sur /mnt
# donne accès au disque comme mémoire virtuelle pendant le build.
#
# Le clone local est nécessaire : `disko` seul (contrairement à
# `disko-install`) n'a aucun mécanisme de substitution de disque nommé.
# On substitue donc le vrai disque dans une copie locale du fichier
# disko (sed), puis `disko` et `nixos-install` pointent tous les deux
# sur ce même clone.
do_install() {
local remote_url="$1" # URL flake (git+https://...), pour cloner
local attr="$2"
local disko_rel_path="$3" # diskos/plain.nix ou diskos/luks.nix
local repo_dir="/tmp/install-repo"
local clone_url="${remote_url#git+}" # git clone ne comprend pas le préfixe git+
echo "Récupération de la config ($attr)..."
rm -rf "$repo_dir"
git clone --depth 1 "$clone_url" "$repo_dir"
echo "Substitution du disque cible dans la config disko..."
sed -i "s#/dev/placeholder#$DEVICE#" "$repo_dir/$disko_rel_path"
echo "Partitionnement de $DEVICE..."
# --yes-wipe-all-disks : évite la confirmation interactive, un install
# de flotte ne doit dépendre de personne au clavier.
nix run --no-write-lock-file --refresh 'github:nix-community/disko' -- \
--mode destroy,format,mount \
--yes-wipe-all-disks \
"$repo_dir/$disko_rel_path"
echo "Swap temporaire sur le disque cible (RAM limitée sur certains PC du parc)..."
# btrfs : chattr +C (NOCOW) doit précéder toute écriture dans le
# fichier, sinon swapon échoue avec "Invalid argument".
truncate -s 0 /mnt/.install-swapfile
chattr +C /mnt/.install-swapfile
fallocate -l 4G /mnt/.install-swapfile
chmod 600 /mnt/.install-swapfile
mkswap /mnt/.install-swapfile
swapon /mnt/.install-swapfile
echo "Construction et installation du système (peut prendre plusieurs minutes)..."
# --no-root-passwd : le mot de passe root est déjà déclaré
# (initialPassword) côté cible, on évite juste un prompt interactif.
# --flake pointe sur le clone local (disque déjà substitué).
nixos-install --root /mnt --flake "${repo_dir}#${attr}" --no-channel-copy --no-root-passwd
swapoff /mnt/.install-swapfile
rm -f /mnt/.install-swapfile
# Persiste la config sur le disque cible : sans ça, le clone ne vit
# que dans /tmp du live (RAM), effacé au reboot -- plus rien à éditer
# ni à `git pull` ensuite. system.autoUpgrade continue de fonctionner
# sans ça (il pointe sur l'URL distante), mais un technicien n'a alors
# aucun accès local.
echo "Installation de la configuration dans /mnt/etc/nixos..."
rm -rf /mnt/etc/nixos
mkdir -p /mnt/etc
cp -a "$repo_dir" /mnt/etc/nixos
# installer/ (script d'install, obsolète une fois fait) et
# pxe-server/ (config d'un autre host) ne servent à rien sur un poste
# client -- ne concerne que nixos_fleet, ces rm sont des no-op côté
# nixos_users.
rm -rf /mnt/etc/nixos/installer /mnt/etc/nixos/pxe-server
# Committé en local (jamais poussé) pour que `git status` reste propre
# sur la machine installée.
git -C /mnt/etc/nixos \
-c user.name="Installeur Nixos_Fleet" \
-c user.email="installeur@nixos-fleet.local" \
commit -aqm "Config locale : disque substitué ($DEVICE), installer/pxe-server retirés (inutiles sur ce poste)"
INSTALLED_ATTR="$attr"
}
echo "=== Installeur Nixos_Fleet ===" echo "=== Installeur Nixos_Fleet ==="
echo "1) PC de la flotte (générique)" echo "1) PC de la flotte (générique)"
echo "2) Poste utilisateur nommé" echo "2) Poste utilisateur nommé"
read -rp "Choix [1/2] : " CHOICE read -rp "Choix [1/2] : " CHOICE
# --- Détection automatique du disque cible ------------------------------- # --- Détection automatique du disque cible -------------------------------
# On exclut les disques amovibles (la clé USB de boot elle-même) et les # Exclut les disques amovibles (la clé USB de boot) et les loop/rom.
# loop/rom. S'il en reste plusieurs (rare), on prend le plus gros et on le # S'il en reste plusieurs, on prend le plus gros et on le signale.
# signale clairement dans les logs plutôt que de bloquer sur un menu.
echo echo
echo "Détection du disque cible..." echo "Détection du disque cible..."
mapfile -t CANDIDATES < <( mapfile -t CANDIDATES < <(
@@ -56,20 +131,16 @@ fi
case "$CHOICE" in case "$CHOICE" in
1) 1)
# --- Branche flotte : rien d'autre à saisir -----------------------
echo echo
echo "Installation du profil générique 'usagers' sur $DEVICE..." echo "Installation du profil générique 'nixos' sur $DEVICE..."
nix run --no-write-lock-file --refresh 'github:nix-community/disko#disko-install' -- --write-efi-boot-entries \ do_install "$FLEET_REPO" "nixos" "diskos/plain.nix"
--flake "${FLEET_REPO}#usagers" \
--disk main "$DEVICE"
;; ;;
2) 2)
# --- Branche utilisateur nommé ------------------------------------- read -rp "Utilisateur (thomas/clara/jade/oreo/oreo-i3) : " USER_PROFILE
read -rp "Utilisateur (thomas/clara/jade/oreo) : " USER_PROFILE
USER_PROFILE=$(echo "$USER_PROFILE" | tr '[:upper:]' '[:lower:]' | xargs) USER_PROFILE=$(echo "$USER_PROFILE" | tr '[:upper:]' '[:lower:]' | xargs)
case "$USER_PROFILE" in case "$USER_PROFILE" in
thomas|clara|jade|oreo) ;; thomas|clara|jade|oreo|oreo-i3) ;;
*) *)
echo "Utilisateur inconnu : $USER_PROFILE" >&2 echo "Utilisateur inconnu : $USER_PROFILE" >&2
exec bash exec bash
@@ -85,26 +156,26 @@ case "$CHOICE" in
read -rsp "Token Gitea (repo privé Nixos_Users) : " TOKEN read -rsp "Token Gitea (repo privé Nixos_Users) : " TOKEN
echo echo
# Le token n'est utilisé qu'en mémoire/ligne de commande pour ce fetch # Utilisé seulement en mémoire/ligne de commande pour ce fetch
# ponctuel : il n'est jamais écrit dans un fichier suivi par git. # ponctuel, jamais écrit dans un fichier suivi par git.
AUTHED_URL="git+https://${TOKEN}@${USERS_REPO_HOST}/${USERS_REPO_PATH}" AUTHED_URL="git+https://${TOKEN}@${USERS_REPO_HOST}/${USERS_REPO_PATH}"
echo echo
echo "Installation du profil '$ATTR' sur $DEVICE..." echo "Installation du profil '$ATTR' sur $DEVICE..."
DISKO_REL_PATH="diskos/plain.nix"
if [[ "$ATTR" == *-luks ]]; then if [[ "$ATTR" == *-luks ]]; then
echo "Disque chiffré : disko va demander une passphrase LUKS pendant le partitionnement." echo "Disque chiffré : disko va demander une passphrase LUKS pendant le partitionnement."
DISKO_REL_PATH="diskos/luks.nix"
fi fi
nix run --no-write-lock-file --refresh 'github:nix-community/disko#disko-install' -- --write-efi-boot-entries \ do_install "$AUTHED_URL" "$ATTR" "$DISKO_REL_PATH"
--flake "${AUTHED_URL}#${ATTR}" \
--disk main "$DEVICE"
# Le système installé doit pouvoir continuer à tirer ses mises à jour # Netrc laissé pour que le système installé continue de tirer ses
# du repo privé tout seul : on lui laisse un netrc, jamais commité, # mises à jour du repo privé tout seul (nix.settings.netrc-file
# jamais dans le Nix store (nix.settings.netrc-file dans le repo # pointe vers ce même chemin côté nixos_users). Jamais commité,
# nixos_users pointe vers ce même chemin). # jamais dans le Nix store.
# #
# ATTENTION : le format exact attendu par Gitea (login=token vs # ATTENTION : le format exact attendu par Gitea (login=token vs
# login+password séparés) dépend de la version/config du serveur — # login+password séparés) dépend de la version/config du serveur --
# à vérifier sur l'instance réelle avant le déploiement pilote. # à vérifier sur l'instance réelle avant le déploiement pilote.
mkdir -p /mnt/etc/nix mkdir -p /mnt/etc/nix
{ {
@@ -122,6 +193,11 @@ case "$CHOICE" in
esac esac
echo echo
echo "Installation terminée. Redémarrage dans 10s (Ctrl-C pour annuler et inspecter /mnt)..." echo "Installation terminée."
echo "Config installée dans /etc/nixos (attribut : ${INSTALLED_ATTR})."
echo "Pour ajouter/enlever un paquet après le premier démarrage :"
echo " 1. éditer les fichiers dans /etc/nixos"
echo " 2. nixos-rebuild switch --flake /etc/nixos#${INSTALLED_ATTR}"
echo "Redémarrage dans 10s (Ctrl-C pour annuler et inspecter /mnt)..."
sleep 10 sleep 10
reboot reboot
+17 -35
View File
@@ -1,7 +1,7 @@
# Environnement live commun aux deux formes d'image (PXE netboot / ISO # Environnement live commun aux deux formes d'image (PXE netboot / ISO
# USB). Contient juste de quoi partitionner et lancer disko-install ; la # USB). Contient juste de quoi partitionner et installer ; la config
# vraie configuration cible est toujours récupérée depuis git au moment # cible est toujours récupérée depuis git au moment de l'install (repo
# de l'install (repo public nixos_fleet ou repo privé nixos_users). # public nixos_fleet ou repo privé nixos_users).
{ pkgs, lib, ... }: { pkgs, lib, ... }:
let let
@@ -13,22 +13,13 @@ in
"flakes" "flakes"
]; ];
# netboot.nix définit DEUX tmpfs distincts, ni l'un ni l'autre avec une # netboot.nix définit "/" et "/nix/.rw-store" (overlay où atterrissent
# taille explicite -> plafonnés à 50% de la RAM chacun par défaut : # les paquets téléchargés/construits pendant l'install) sans taille
# - "/" : la racine du système live lui-même (petite) # explicite -> 50% de la RAM chacun par défaut. .rw-store sature en
# - "/nix/.rw-store" : la vraie zone où atterrissent tous les nouveaux # premier sur un système complet (Cinnamon + Firefox + LibreOffice +
# paquets téléchargés/construits pendant l'install # VLC), pas "/". mkForce nécessaire : netboot.nix utilise
# (overlay au-dessus du squashfs .ro-store en # mkImageMediaOverride (priorité 60), une assignation simple (100) est
# lecture seule). C'est CELLE-LÀ qui sature en # ignorée ; mkForce remplace toute la liste, d'où mode=0755 remis ici.
# premier sur un système complet (Cinnamon +
# Firefox + LibreOffice + VLC...), pas "/".
#
# netboot.nix définit ces deux options via mkImageMediaOverride, qui vaut
# mkOverride 60 — une simple assignation ici (priorité 100 par défaut)
# est donc entièrement ignorée, pas fusionnée (constaté : le premier
# essai n'a eu aucun effet). Il faut lib.mkForce (priorité 50, plus
# fort que 60) pour vraiment gagner — et comme mkForce REMPLACE toute la
# liste plutôt que de la compléter, on remet mode=0755 dedans aussi.
fileSystems."/".options = lib.mkForce [ fileSystems."/".options = lib.mkForce [
"mode=0755" "mode=0755"
"size=20%" "size=20%"
@@ -38,11 +29,9 @@ in
"size=90%" "size=90%"
]; ];
# Certains PC du parc n'ont que 4 Go de RAM. Le disque cible ne peut pas # Certains PC du parc n'ont que 4 Go de RAM. Le disque cible ne peut
# servir de secours (disko-install l'efface entièrement en une seule # pas servir de secours ici (pas encore partitionné à ce stade) : swap
# opération, un swap posé dessus avant serait détruit en cours de # compressé en RAM (zram), identique en PXE et en USB.
# route) : on utilise du swap compressé en RAM (zram) à la place, qui
# marche pareil en PXE et en USB sans dépendre d'un périphérique externe.
zramSwap = { zramSwap = {
enable = true; enable = true;
memoryPercent = 100; # jusqu'à 1x la RAM en swap compressé (~2-3x effectif) memoryPercent = 100; # jusqu'à 1x la RAM en swap compressé (~2-3x effectif)
@@ -52,26 +41,19 @@ in
git git
parted parted
gptfdisk gptfdisk
util-linux # lsblk util-linux # lsblk, fallocate
e2fsprogs # chattr (NOCOW pour le swapfile sur btrfs)
installScript installScript
]; ];
# disko-install n'est pas préinstallé : install.sh l'invoque via
# `nix run github:nix-community/disko#disko-install`, ce qui évite de
# dépendre d'un nom de paquet nixpkgs précis (pas de package `disko`
# garanti tel quel côté nixpkgs) et reste conforme à l'usage documenté
# du projet.
networking.networkmanager.enable = true; networking.networkmanager.enable = true;
services.xserver.xkb.layout = "fr"; services.xserver.xkb.layout = "fr";
console.keyMap = "fr"; console.keyMap = "fr";
# Root autologin sur tty1, l'installeur se lance automatiquement au # Root autologin sur tty1, l'installeur se lance automatiquement au
# login. S'il échoue ou qu'on le quitte, on retombe sur un shell root # login. Échec ou sortie -> shell root normal pour dépanner.
# normal pour dépanner à la main — pas de piège sans issue.
# mkForce : le profil installeur officiel (installation-device.nix) # mkForce : le profil installeur officiel (installation-device.nix)
# définit aussi cette option (autologin "nixos"), même priorité que la # définit aussi cette option, même priorité -> conflit sinon.
# nôtre sans mkForce -> conflit sinon.
services.getty.autologinUser = lib.mkForce "root"; services.getty.autologinUser = lib.mkForce "root";
users.users.root.password = lib.mkForce ""; users.users.root.password = lib.mkForce "";
+14
View File
@@ -0,0 +1,14 @@
<!--
SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
SPDX-License-Identifier: MIT
-->
# ANSSI compliance module
This is a self-contained NixOS implementation of <https://messervices.cyber.gouv.fr/guides/recommandations-de-securite-relatives-un-systeme-gnulinux>.
It can be copy-pasted outside of Sécurix for your own needs.
> Vendored depuis [SécurixOS](https://github.com/cloud-gouv/securix)
> (`modules/anssi/`, alpha), 2026-09-09. Ne pas éditer directement --
> reprendre depuis Sécurix en cas de correctif.
+15
View File
@@ -0,0 +1,15 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{
# Default exceptions for MACs.
security.anssi.exceptions = {
R37.rationale = "No MAC implementation is available on NixOS at the moment.";
R45.rationale = "AppArmor is partially supported on NixOS.";
R46.rationale = "SELinux is unsupported on NixOS.";
R47.rationale = "SELinux is unsupported on NixOS.";
R48.rationale = "SELinux is unsupported on NixOS.";
R49.rationale = "SELinux is unsupported on NixOS.";
};
}
+10
View File
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{
imports = [
./options.nix
./base.nix
];
}
+112
View File
@@ -0,0 +1,112 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{
# rulesets: a set of rules, e.g., { R1 = {...}; R2 = {...}; ... }
generateOptionHierarchy =
rulesets:
let
levelMapping = {
"minimal" = 0;
"intermediary" = 1;
"reinforced" = 2;
"high" = 3;
};
# helper to generate one rule's option set
makeRuleOption =
{
lib,
config,
pkgs,
...
}:
ruleId: rule:
{
anssiRef = lib.mkOption {
type = lib.types.str;
default = rule.anssiRef;
readOnly = true;
description = "ANSSI reference for rule ${rule.name}";
};
enable = lib.mkOption {
type = lib.types.bool;
default =
config.security.anssi.enable
&& levelMapping.${config.security.anssi.level} >= levelMapping.${rule.severity}
&& (rule.category == "base" || rule.category == config.security.anssi.category)
&& lib.all (t: !(lib.elem t config.security.anssi.excludes)) (rule.tags or [ ])
&& (
((rule.architectures or [ ]) != [ ])
-> lib.elem pkgs.stdenv.hostPlatform.linuxArch rule.architectures
)
&& !config.security.anssi.exceptions ? ${ruleId};
description = ''
Enable rule ${rule.name}.
${rule.description}
ANSSI reference: ${rule.anssiRef}.
'';
};
exceptionRationale = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default =
if config.security.anssi.exceptions ? ${ruleId} then
config.security.anssi.exceptions.${ruleId}.rationale
else
null;
readOnly = true;
description = "Optional rationale for disabling or modifying rule ${rule.name}";
};
checkScript = lib.mkOption {
type = lib.types.path; # TODO: or string if we store the script inline
default = rule.checkScript pkgs;
description = "Check script for rule ${rule.name}";
};
}
// lib.optionalAttrs (rule ? implementations) {
implementation = lib.mkOption {
type = lib.types.enum (lib.attrNames rule.implementations);
description = "Chosen implementation for this rule.";
};
};
# map each rule in rulesets to the generated option hierarchy
ruleOptions =
moduleArgs: moduleArgs.lib.mapAttrs (ruleId: rule: makeRuleOption moduleArgs ruleId rule) rulesets;
ruleEnabled = config: ruleId: config.security.anssi.rules.${ruleId}.enable;
enabledRuleConfigs =
moduleArgs:
moduleArgs.lib.mapAttrsToList (
ruleId: rule:
let
complexConfigTerm =
cfg:
moduleArgs.lib.mkMerge (
moduleArgs.lib.mapAttrsToList (
implName: _:
moduleArgs.lib.mkIf (moduleArgs.config.security.anssi.rules.${ruleId}.implementation == implName) (
rule.implementations.${implName}.config cfg
)
) rule.implementations
);
simpleConfigFn = rule.config;
configFn = if rule ? implementations then complexConfigTerm else simpleConfigFn;
in
moduleArgs.lib.mkIf (ruleEnabled moduleArgs.config ruleId) (configFn moduleArgs)
) rulesets;
in
{
lib,
config,
pkgs,
...
}@moduleArgs:
{
options.security.anssi.rules = ruleOptions moduleArgs;
config = lib.mkMerge (enabledRuleConfigs moduleArgs);
};
}
+324
View File
@@ -0,0 +1,324 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
let
mkSysctlChecker =
lib: attrs:
let
# Normalize whitespace (tabs to spaces, collapse runs, trim edges)
# so that sysctl output like "32768\t65535" matches Nix value "32768 65535".
normalizeWhitespace = "tr -s '[:space:]' ' ' | sed 's/^ //;s/ $//' ";
mkCheckSingularSysctl = attr: expectedValue: ''
# Check for sysctl '${attr}'
actual_value=$(sysctl -n "${attr}" | ${normalizeWhitespace}) || {
echo "Check failed for ${attr}: unable to read sysctl value"
exit 1
}
expected_value=$(echo "${toString expectedValue}" | ${normalizeWhitespace})
if [[ "$actual_value" != "$expected_value" ]]; then
echo "Check failed for ${attr}: expected $expected_value, got $actual_value"
exit 1
else
echo "Check passed for ${attr}"
fi
'';
in
''
${lib.concatStringsSep "\n" (
map (name: mkCheckSingularSysctl name attrs.${name}) (lib.attrNames attrs)
)}
'';
in
{
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 = _: {
boot.kernelParams = [
"l1tf=full,force" # Enable full mitigation for L1 Terminal Fault (L1TF), including disabling SMT
"page_poison=on" # Enable poisoning of freed pages to detect leaks
"pti=on" # Force use of Page Table Isolation (PTI) for Meltdown vulnerability
"slab_nomerge=yes" # Disable slab cache merging to complicate heap overflow attacks
"slub_debug=FZP" # Enable slab cache debugging with specific tests
"spec_store_bypass_disable=seccomp" # Use seccomp mitigation for Spectre v4
"spectre_v2=on" # Force Spectre v2 mitigation
"mds=full,nosmt" # Enable full mitigation for MDS and disable SMT (requires microcode update)
"mce=0" # Force kernel panic on uncorrected Machine Check exceptions
"page_alloc.shuffle=1" # Enable page allocator randomization
"rng_core.default_quality=500" # Set HWRNG quality for TPM-based CSPRNG initialization
];
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R8" ''
# The kernel boot parameters we expect to check
EXPECTED_PARAMS=(
"l1tf=full,force"
"page_poison=on"
"pti=on"
"slab_nomerge=yes"
"slub_debug=FZP"
"spec_store_bypass_disable=seccomp"
"spectre_v2=on"
"mds=full,nosmt"
"mce=0"
"page_alloc.shuffle=1"
"rng_core.default_quality=500"
)
# Get the current kernel parameters from /proc/cmdline
ACTUAL_PARAMS=$(cat /proc/cmdline)
# Check if each expected parameter is in the actual kernel parameters
for param in "''${EXPECTED_PARAMS[@]}"; do
if echo "$ACTUAL_PARAMS" | grep -q "$param"; then
echo "Kernel parameter '$param' is correctly set."
else
echo "WARNING: Kernel parameter '$param' is NOT set."
exit 1
fi
done
# Additionally, check specific parameters from /proc/cpuinfo (for cpu-related parameters)
echo "Checking CPU-specific parameters..."
# Check if SMT (Hyper-Threading) is disabled, as required by "mds=full,nosmt"
if grep -q "nosmt" <<< "$ACTUAL_PARAMS"; then
if grep -q "siblings" /proc/cpuinfo && ! grep -q "HT" /proc/cpuinfo; then
echo "SMT is disabled (as expected for mds=full,nosmt)."
else
echo "WARNING: SMT is still enabled, but 'mds=full,nosmt' was passed."
fi
fi
# Check if Page Table Isolation (PTI) is enabled (from /proc/cpuinfo)
if grep -q "pti" <<< "$ACTUAL_PARAMS"; then
if grep -q "pti" /proc/cpuinfo; then
echo "PTI is enabled (as expected for pti=on)."
else
echo "WARNING: PTI is not enabled, despite pti=on being passed."
fi
fi
exit 0
'';
};
R9 =
let
sysctls = {
# Restrict access to dmesg buffer
"kernel.dmesg_restrict" = "1";
# Hide kernel addresses in /proc and other interfaces, even for privileged users
"kernel.kptr_restrict" = "2";
# Set process ID maximum
"kernel.pid_max" = "1048576"; # 2^20
# Restrict perf subsystem usage
"kernel.perf_cpu_time_max_percent" = "1";
"kernel.perf_event_max_sample_rate" = "1";
# Restrict access to perf_event_open system call
"kernel.perf_event_paranoid" = "2";
# Enable Address Space Layout Randomization (ASLR)
"kernel.randomize_va_space" = "2";
# Disable Magic SysRq key (prevents magical system requests)
"kernel.sysrq" = "0";
# Restrict use of BPF (Berkeley Packet Filter) to privileged users
"kernel.unprivileged_bpf_disabled" = "1";
# Kernel panic on unexpected kernel behavior (e.g., oops)
"kernel.panic_on_oops" = "1";
};
in
{
name = "R9_KernelOptions";
anssiRef = "R9 – Paramétrer les options de configuration du noyau";
description = "Tune kernel configuration options";
severity = "intermediary";
category = "base";
config = _: { boot.kernel.sysctl = sysctls; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R9" ''
${mkSysctlChecker pkgs.lib sysctls}
'';
};
R10 = {
name = "R10_DisableKernelModuleLoading";
anssiRef = "R10 – Désactiver le chargement des modules noyau";
description = "Disable loading of kernel modules";
severity = "reinforced";
category = "base";
tags = [ "disable-kernel-module-loading" ];
config = _: { boot.kernel.sysctl."kernel.modules_disabled" = "1"; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R10" ''
${mkSysctlChecker pkgs.lib { "kernel.modules_disabled" = 1; }}
'';
};
R11 = {
name = "R11_ConfigureYamaLSM";
anssiRef = "R11 – Activer et configurer le LSM Yama";
description = "Enable and configure the Yama Linux Security Module";
severity = "intermediary";
category = "base";
config = _: { boot.kernel.sysctl."kernel.yama.ptrace_scope" = "1"; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R11" ''
${mkSysctlChecker pkgs.lib { "kernel.yama.ptrace_scope" = 1; }}
'';
};
R12 =
let
ipv4_sysctls = {
# Harden the JIT compiler for BPF
"net.core.bpf_jit_harden" = "2";
# Disable IP forwarding (no routing between interfaces)
"net.ipv4.ip_forward" = "0";
# Reject packets with source address 127/8 (loopback network)
"net.ipv4.conf.all.accept_local" = "0";
# Disable ICMP redirects (prevents potential traffic redirection)
"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";
# Disable shared media detection (useful for preventing misconfiguration)
"net.ipv4.conf.all.shared_media" = "0";
"net.ipv4.conf.default.shared_media" = "0";
# Disable kernel ARP management across interfaces (security for high-availability setups)
"net.ipv4.conf.all.arp_filter" = "1";
# Respond to ARP requests only if source and destination are on the same network
"net.ipv4.conf.all.arp_ignore" = "2";
# Refuse routing of packets with loopback addresses as source or destination
"net.ipv4.conf.all.route_localnet" = "0";
# Drop Gratuitous ARP (prevents ARP poisoning attacks)
"net.ipv4.conf.all.drop_gratuitous_arp" = "1";
# Perform Reverse Path Filtering (checks if the source is reachable via the interface it came from)
"net.ipv4.conf.default.rp_filter" = "1";
"net.ipv4.conf.all.rp_filter" = "1";
# Disable sending ICMP redirects (normal for routers but unnecessary for end hosts)
"net.ipv4.conf.default.send_redirects" = "0";
"net.ipv4.conf.all.send_redirects" = "0";
# Ignore bogus ICMP error responses (RFC 1122)
"net.ipv4.icmp_ignore_bogus_error_responses" = "1";
# Increase the range for ephemeral ports (for better security)
"net.ipv4.ip_local_port_range" = "32768 65535";
# Enable RFC 1337 compliance (prevents TCP timestamp attacks)
"net.ipv4.tcp_rfc1337" = "1";
# Enable SYN cookies (prevents SYN flood attacks)
"net.ipv4.tcp_syncookies" = "1";
};
in
{
name = "R12_IPv4Networking";
anssiRef = "R12 – Paramétrer les options de configuration du réseau IPv4";
description = "Configure IPv4 network options";
severity = "intermediary";
category = "base";
config = _: { boot.kernel.sysctl = ipv4_sysctls; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R12" ''
${mkSysctlChecker pkgs.lib ipv4_sysctls}
'';
};
R13 =
let
sysctls = {
# Disable IPv6 on all interfaces by default
"net.ipv6.conf.default.disable_ipv6" = "1";
# Disable IPv6 on all interfaces
"net.ipv6.conf.all.disable_ipv6" = "1";
};
in
{
name = "R13_DisableIPv6";
anssiRef = "R13 – Désactiver le plan IPv6";
description = "Disable IPv6";
severity = "intermediary";
category = "base";
tags = [ "no-ipv6" ];
config = _: { boot.kernel.sysctl = sysctls; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R13" ''
${mkSysctlChecker pkgs.lib sysctls}
'';
};
R14 =
let
sysctls = {
# Disable coredumps for setuid executables
"fs.suid_dumpable" = "0";
# Prevent opening FIFOs and regular files not owned by the user in sticky directories
"fs.protected_fifos" = "2";
"fs.protected_regular" = "2";
# Restrict the creation of symbolic links to files owned by the user
"fs.protected_symlinks" = "1";
# Restrict the creation of hard links to files owned by the user
"fs.protected_hardlinks" = "1";
};
in
{
name = "R14_FilesystemConfig";
anssiRef = "R14 – Paramétrer les options de configuration des systèmes de fichiers";
description = "Tune filesystem configuration options";
severity = "intermediary";
category = "base";
config = _: { boot.kernel.sysctl = sysctls; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R14" ''
${mkSysctlChecker pkgs.lib sysctls}
'';
};
}
+209
View File
@@ -0,0 +1,209 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{
pkgs,
config,
lib,
...
}:
let
inherit (lib) mkEnableOption mkOption types;
cfg = config.security.anssi;
rulesets = import ./ruleset.nix { inherit lib; };
levelMapping = {
"minimal" = 0;
"intermediary" = 1;
"reinforced" = 2;
"high" = 3;
};
# rules.* must follow the format used earlier:
# {
# name = "...";
# category = "client" / "server" / "base";
# severity = "minimal" / "intermediary" / "reinforced" / "high";
# tags = [ "kernel" ... ];
# config = f: { ... };
# checkScript = pkgs: pkgs.writeShellScript ...
# }
# Look at all rules that are not enabled.
# Determine why they are not enabled:
# - if there's an exception for it, use it.
# - if it's excluded via a tag, use it.
# - if it's excluded via architecture, use it.
# - if it's out of the scope for the category, use it.
# - if it's out of the scope for the level, use it.
# - otherwise, mark it unknown.
# Determine why a rule is not enabled.
determineExclusionReason =
ruleId: rule:
if !cfg.rules.${ruleId}.enable then
# Check if it's excluded via a clear mechanism.
let
tagExclusions = lib.filter (tag: lib.elem tag (rule.tags or [ ])) cfg.excludes;
categoryExclusion = rule.category != "base" && rule.category != cfg.category;
levelExclusion = levelMapping.${rule.severity} > levelMapping.${cfg.level};
architectureExclusion =
if (rule.architectures or [ ]) != [ ] then
!lib.elem pkgs.stdenv.hostPlatform.linuxArch rule.architectures
else
false;
in
if tagExclusions != [ ] then
{
reason = "excluded by tag ${lib.concatStringsSep ", " tagExclusions}";
via = "tag";
exclusionTags = tagExclusions;
}
else if categoryExclusion then
{
reason = "out of scope for category '${cfg.category}' (${rule.category})";
via = "category";
requiredCategory = rule.category;
}
else if levelExclusion then
{
reason = "out of scope for level ${cfg.level} < ${rule.severity}";
via = "level";
requiredLevel = rule.severity;
}
else if architectureExclusion then
{
reason = "out of scope for architecture '${pkgs.stdenv.hostPlatform.linuxArch}'";
via = "architecture";
requiredArchitectures = rule.architectures or [ ];
}
else if lib.hasAttr ruleId cfg.exceptions then
{
reason = "excluded by explicit exception: ${cfg.exceptions.${ruleId}.rationale}";
via = "exception";
rationale = cfg.exceptions.${ruleId}.rationale;
}
else
{
reason = "could not understand why this rule was excluded, the user must have set the `enable = false;` forcibly via the module system bypassing the governance system. This is a non-compliant.";
via = "unknown";
}
else
null;
generateRuleEntry =
ruleId: rule:
{
enabled = cfg.rules.${ruleId}.enable;
anssiRef = cfg.rules.${ruleId}.anssiRef;
knownCheckScript = rule.checkScript pkgs;
}
// lib.optionalAttrs (cfg.exceptions ? ruleId) {
declaredExceptionRationale = cfg.exceptions.${ruleId}.rationale;
}
// lib.optionalAttrs (rule ? implementations) {
implementation = cfg.rules.${ruleId}.implementation;
};
complianceReport = {
"org.securix.anssi-compliance.v1" = {
guideVersion = "2.0";
rules = lib.mapAttrsToList (ruleId: rule: generateRuleEntry ruleId rule) rulesets;
targetLevel = cfg.level;
hostname = config.networking.hostName;
domain = config.networking.domain or null;
targetSystemCategory = cfg.category;
exclusions = lib.mapAttrs determineExclusionReason (
lib.filterAttrs (ruleId: _: !cfg.rules.${ruleId}.enable) rulesets
);
};
};
in
{
imports = [
# This will generate `security.anssi.rules.RXX.{anssiRef,enable,checkScript}` options and so on.
((import ./generator.nix).generateOptionHierarchy rulesets)
];
options.security.anssi = {
enable = mkEnableOption "ANSSI compliance for GNU/Linux systems (v2.0)";
level = mkOption {
type = types.enum [
"minimal"
"intermediary"
"reinforced"
"high"
];
default = "minimal";
description = "Select desired ANSSI compliance level.";
};
category = mkOption {
type = types.enum [
"base"
"client"
"server"
];
default = "base";
description = "Rule category to activate.";
};
excludes = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Tags to exclude (e.g. [ \"kernel\" \"no-ipv6\" ])";
};
exceptions = mkOption {
type = types.attrsOf (
types.submodule (_: {
options.rationale = mkOption { type = types.lines; };
})
);
default = { };
};
};
config = lib.mkIf cfg.enable {
system.build.complianceReport = complianceReport;
system.build.complianceReportDocument = pkgs.writers.writeJSON "anssi-compliance.json" complianceReport;
system.build.complianceCheckScript =
let
mkCheckSingularRule = ruleId: rule: ''
# Check for rule ${ruleId}
if [ -n "${rule.checkScript}" ]; then
result=$(bash -c "${rule.checkScript}")
results["${ruleId}"]=$result
else
results["${ruleId}"]="Check script not defined: cannot be checked."
fi
'';
in
pkgs.writeShellScriptBin "anssi-nixos-compliance-check" ''
declare -A results
${lib.concatStringsSep "\n" (
lib.mapAttrsToList mkCheckSingularRule (
lib.filterAttrs (_: rule: rule.enable) config.security.anssi.rules
)
)}
# Output the results in a matrix format
echo "ANSSI Compliance Check Results:"
for rule in "''${!results[@]}"; do
result="''${results[$rule]}"
if [[ "$result" =~ .*TODO.* ]]; then
printf "\033[33m%-40s : %s\033[0m\n" "$rule" "$result"
elif [[ "$result" =~ (fail|WARNING|DIVERGENCE|UNSET|error|not\ completed|incomplete) ]]; then
printf "\033[31m%-40s : %s\033[0m\n" "$rule" "$result"
else
printf "\033[32m%-40s : %s\033[0m\n" "$rule" "$result"
fi
done
'';
environment.systemPackages = [ config.system.build.complianceCheckScript ];
};
}
+181
View File
@@ -0,0 +1,181 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{
# TODO: these two next rules requires special system integration that depends on the hardware manufacturer.
# We implement no specific check or implementation here.
R1 = {
name = "R1_ChooseHardware";
anssiRef = "R1 – Choisir et configurer son matériel";
description = "Choosing and configuring hardware";
severity = "high";
category = "base";
config = _: {
# TODO: hardware-level config (maybe BIOS settings, firmware)
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R1" ''
echo "TODO: check hardware configuration for R1"
exit 0
'';
};
R2 = {
name = "R2_BIOS_UEFI";
anssiRef = "R2 – Configurer le BIOS/UEFI";
description = "Configure BIOS / UEFI";
severity = "intermediary";
category = "base";
config = _: {
# TODO: set NixOS options for UEFI / BIOS security
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R2" ''
echo "TODO: check BIOS / UEFI secure configuration"
exit 0
'';
};
R3 = {
name = "R3_UEFI_SecureBoot";
anssiRef = "R3 – Activer le démarrage sécurisé UEFI";
description = "Enable UEFI Secure Boot";
severity = "intermediary";
category = "base";
config = _: {
# TODO: NixOS bootloader secure-boot config
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R3" ''
# Check if the system is running in UEFI mode
if [ ! -d "/sys/firmware/efi" ]; then
echo "Secure Boot cannot be checked. System is not in UEFI mode."
exit 1
fi
# Check the Secure Boot status
secure_boot_file="/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c"
if [ -f "$secure_boot_file" ]; then
# Extract the value of SecureBoot
secure_boot_status=$(xxd -p -c 4 "$secure_boot_file")
# Check if Secure Boot is enabled
if [[ "$secure_boot_status" =~ 01 ]]; then
echo "Secure Boot is enabled."
exit 0
else
echo "fail: Secure Boot is not enabled."
exit 1
fi
else
echo "fail: Secure Boot status file not found."
exit 1
fi
'';
};
R4 = {
name = "R4_ReplacePreloadedKeys";
anssiRef = "R4 – Remplacer les clés préchargées";
description = "Replace preloaded UEFI keys";
severity = "high";
category = "base";
config = _: { };
checkScript =
pkgs:
pkgs.writeShellScript "check-R4" ''
echo "TODO: check all loaded certificates"
exit 0
'';
};
R5 = {
name = "R5_BootloaderPassword";
anssiRef = "R5 – Configurer un mot de passe pour le chargeur de démarrage";
description = "Set a password on the bootloader";
severity = "intermediary";
category = "base";
implementations = {
secureboot = {
checkScript = { };
depends = [ "R3" ];
config = _: { };
};
grub = {
checkScript = "";
config = _: { };
};
};
config = _: {
# TODO: grub or systemd-boot password
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R5" ''
echo "R5 depends on R3."
exit 0
'';
};
R6 = {
name = "R6_ProtectKernelCmdline";
anssiRef = "R6 – Protéger les paramètres de ligne de commande du noyau et l’initramfs";
description = "Protect kernel cmdline and initramfs";
severity = "high";
category = "base";
# implementations.secureboot = { checkScript = {}; depends = [ "R3" ]; };
config = _: {
# TODO
};
checkScript =
pkgs:
pkgs.writeShellScript "check-R6" ''
echo "R6 depends on R3."
'';
};
R7 = {
name = "R7_EnableIOMMU";
anssiRef = "R7 – Activer l’IOMMU";
description = "Enable IOMMU";
severity = "reinforced";
category = "base";
config = _: { boot.kernelParams = [ "iommu=force" ]; };
checkScript =
pkgs:
pkgs.writeShellScript "check-R7" ''
#!/bin/sh
IOMMU_DIR="/sys/class/iommu"
if [ -d "$IOMMU_DIR" ] && [ "$(ls -A $IOMMU_DIR)" ]; then
echo "IOMMU is enabled. Files found: $(ls $IOMMU_DIR | xargs)"
else
echo "No IOMMU detected or not enabled."
exit 1
fi
exit 0
'';
};
}
+23
View File
@@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2026 Ryan Lahfa <ryan.lahfa@numerique.gouv.fr>
#
# SPDX-License-Identifier: MIT
{ lib, ... }:
let
loadRules = files: lib.mergeAttrsList (map import files);
in
loadRules [
./preboot.nix
./kernel-options.nix
# ./kernel.nix
# ./vfs.nix
# ./users.nix
# ./apparmor.nix
# ./selinux.nix
# ./journaling.nix
# ./runtime-minimization.nix
# ./hardening.nix
# ./nss-hardening.nix
# ./integrity.nix
# ./mta.nix
]
+3
View File
@@ -41,6 +41,9 @@ in
]; ];
i18n.defaultLocale = "fr_FR.UTF-8"; i18n.defaultLocale = "fr_FR.UTF-8";
# i18n.defaultLocale ne règle pas le clavier de la console — option
# séparée, absente jusqu'ici pour ce host (vérifié : wiki.nixos.org/wiki/Keyboard_Layout_Customization).
console.keyMap = "fr";
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;