Modules système
⚙ darkone.system.core
Section intitulée « ⚙ darkone.system.core »Le module DNF principal.
Met en place :
- systemd-boot et le noyau LTS (dernier mainline via
enableLatestKernel); - JetBrains Mono Nerd Font, avec
kmsconpour le TTY; - optimisation nightly du store, GC hebdomadaire avec rétention de 30 jours (keep-last-N via
gcKeepGenerationssur les hôtes au disque contraint); - le pare-feu et SSH à clé uniquement;
- le terminfo
xterm-ghosttyde Ghostty, pour que chaque hôte accepte le terminal; - la politique de veille (
disableSuspendpour les serveurs); - les règles polkit laissant
wheelarrêter/redémarrer; - un groupe/utilisateur
common-filespartagé pour les dossiers médias inter-services.
- enable
boolSystème principal du framework Darkone (activé par défaut) - enableSystemdBoot
boolActiver le chargeur de démarrage par défaut - enableLatestKernel
boolUtiliser le dernier noyau mainline (linuxPackages_latest) au lieu du LTS - enableFstrim
boolOptimisation SSD avec fstrim - enableFirewall
boolActiver le pare-feu (true par défaut) - enableSops
boolActiver le module dnf sops (true par défaut) - enableFlatpak
boolActiver la configuration DNF flatpak (uniquement pour les environnements graphiques) - enableKmscon
boolActiver la nerd font pour le TTY - gcKeepGenerations
intSi > 0, limiter le profil système aux N dernières générations avant chaque collecte de déchets, et collecter quotidiennement sans rétention basée sur l’âge. Pour les hôtes avec une partition racine étroite (passerelles). - enableBoost
boolActiver l’overclocking, corectl - enableAutoSuspend
boolActiver la veille automatique (pour les portables, ignoré si disableSuspend est true) - disableSuspend
boolDésactivation complète de la veille (pour les serveurs) - enableCommonFilesUser
boolActiver l’utilisateur common-files utilisé par plusieurs services
darkone.system.core = { enable = true; enableSystemdBoot = true; enableLatestKernel = false; enableFstrim = true; enableFirewall = true; enableSops = true; enableFlatpak = true; enableKmscon = true; gcKeepGenerations = 5; enableBoost = false; enableAutoSuspend = false; disableSuspend = false; enableCommonFilesUser = false;};⚙ darkone.system.dnf-release
Section intitulée « ⚙ darkone.system.dnf-release »Estampille la version du framework sur l’hôte : /etc/dnf-release et l’étiquette d’amorçage.
Répond à la question « quelle version de DNF fait tourner cette machine ? » sans journal de déploiement, sur une flotte où les hôtes sont mis à jour un par un et où une restauration peut laisser un nœud à plusieurs versions en arrière.
⚙ darkone.system.documentation
Section intitulée « ⚙ darkone.system.documentation »Documentation (man) pour les développeurs et les administrateurs.
- enable
boolActiver la documentation technique utile (man, nixos)
darkone.system.documentation.enable = false;⚙ darkone.system.hardware
Section intitulée « ⚙ darkone.system.hardware »Améliorations de la configuration matérielle.
Tous les firmwares sont chargés (hardware.enableAllFirmware), sauf sur une
machine virtuelle : just detect-hw la détecte (systemd-detect-virt --vm) et
définit darkone.system.hardware.virtual dans hardware/detected-hardware.nix.
Un invité permet aussi à Zed de tourner sur son GPU émulé (ZED_ALLOW_EMULATED_GPU).
- enable
boolActiver les optimisations matérielles - enableIntel
boolActiver les mises à jour de microcode intel - enableAmd
boolActiver les mises à jour de microcode amd - virtual
boolMachine virtuelle, aucun firmware chargé. Défini parjust detect-hw
darkone.system.hardware = { enable = false; enableIntel = false; enableAmd = false; virtual = false;};⚙ darkone.system.i18n
Section intitulée « ⚙ darkone.system.i18n »Configuration de la localisation et de la langue.
- enable
boolActive l’internationalisation avec la configuration de la zone réseau par défaut - locale
strMatching dnfLib.localeRegexLocale réseau, doit respecter la formexx_YY.UTF-8. - timeZone
strFuseau horaire réseau - keyboard
rawClavier de la locale : mappageconsole, dispositionxkbet variantexkbVariant.
darkone.system.i18n = { enable = false; locale = "fr_FR.UTF-8"; timeZone = "Europe/Paris"; keyboard = if cfg.enable then dnfLib.keyboardFromLocale cfg.locale else { console = config.console.keyMap; xkb = config.console.keyMap; xkbVariant = ""; };};⚙ darkone.system.luks
Section intitulée « ⚙ darkone.system.luks »Politique de mots de passe LUKS et déverrouillage à distance : mots de passe partagés et par hôte, SSH en initrd.
Complète modules/system/yubikey.nix (emplacements de clés FIDO2) avec la partie mots de passe de la politique LUKS du parc. Chaque volume chiffré dispose exactement de deux mots de passe gérés, ainsi que d’un emplacement de clé par YubiKey enregistré :
| Keyslot | Secret | Audience |
|---|---|---|
| shared | sops luks-passphrase | l’administrateur du parc. Autorise également les opérations sur les emplacements (luksAddKey / luksKillSlot) pour les unités ci-dessous, et est la clé utilisée par disko pour formater le disque. |
| per-host | sops luks/<host>/passphrase | l’utilisateur de cette machine uniquement, sans donner accès au parc entier. |
| FIDO2 | registry usr/secrets/yubikeys.json | un emplacement par clé, sur chaque hôte chiffré. |
Deux mots de passe gérés plutôt qu’un seul, car ils s’adressent à des publics différents ; et parce que la rotation de l’un peut toujours être autorisée par l’autre, le service de synchronisation n’est jamais laissé sans autorisateur (il ajoute le nouvel emplacement avant de supprimer l’ancien).
Il n’existe pas de « mot de passe d’installation » distinct : just install formate le volume via nixos-anywhere --disk-encryption-keys avec le mot de passe partagé, de sorte que l’emplacement de clé de formatage est l’emplacement partagé. Sur le premier hôte (darkone new, just install-here), le mot de passe saisi dans l’assistant devient ce mot de passe.
just luks <host> provisionne un hôte (idempotent, également appelé par just configure) ; sa phase pre s’exécute hors ligne et est appelée par just install avant la compilation. Elle enregistre l’hôte dans le manifeste public usr/secrets/luks.json (committé), stocke le mot de passe par hôte et la clé hôte SSH initrd dans sops, et s’assure que le luks-passphrase partagé existe. just luks <host> show affiche la table des emplacements de clés et vérifie le déverrouillage à distance. Le module assure ensuite la convergence à chaque application :
- keyslots : un service oneshot synchronise les mots de passe partagé et par hôte dans l’en-tête LUKS2 de chaque volume déclaré par disko. Toute modification d’une valeur dans sops fait tourner l’emplacement correspondant (ancien supprimé, nouveau ajouté). Les opérations sur les emplacements sont autorisées par le mot de passe géré qui déverrouille encore le volume ; l’un d’eux doit donc rester valide. Les emplacements hors de son registre (FIDO2, enrôlements manuels) ne sont jamais modifiés.
- remote unlock : sshd en initrd sur un port dédié (2222), avec sa propre clé hôte persistante (
/var/lib/luks-initrd/), connexion root autorisée avec la clé de déploiementnix(usr/secrets/nix.pub).just unlock <host>répond à l’invite de mot de passe sans intervention humaine (via l’assistantdnf-unlockinclus dans l’initrd), ou interactivement depuis un terminal si aucun mot de passe stocké ne fonctionne. En cas d’échec de la route VPN, il se replie sur l’IP WAN enregistrée dans le manifeste. Les hôtes classiques obtiennent leur IP par DHCP sur les interfaces filaires (un portable en Wi-Fi n’a pas de réseau en initrd et se replie sur le déverrouillage par console) ; les passerelles de zone répliquent leur configuration de production : IP LAN statique sur le pont lan0, DHCP côté WAN, car elles sont elles-mêmes le serveur DHCP et l’initrd attendrait sinon.
- enable
boolPolitique de mots de passe LUKS du parc (inactive tant quejust luks <host>n’a pas provisionné l’hôte) - sshPort
portPort sshd en initrd ; distinct de 22 afin que la clé hôte initrd ne entre jamais en conflit avec celle du système - volumes
listOf strVolumes LUKS déclarés par la configuration disko de l’hôte.
darkone.system.luks = { enable = true; sshPort = 2222; volumes = [ ];};⚙ darkone.system.microvm.guest
Section intitulée « ⚙ darkone.system.microvm.guest »Côté invité MicroVM : hyperviseur, lien routé, partages, clé hôte persistante.
⚙ darkone.system.microvm.host
Section intitulée « ⚙ darkone.system.microvm.host »Côté hyperviseur MicroVM : invités, taps routés, isolation.
⚙ darkone.system.microvm.whisper
Section intitulée « ⚙ darkone.system.microvm.whisper »Côté hyperviseur MicroVM : serveur whisper.cpp pour les invités qui le demandent.
⚙ darkone.system.microvm
Section intitulée « ⚙ darkone.system.microvm »Invités MicroVM (microvm.nix, cloud-hypervisor) sur un hôte hyperviseur DNF.
Un invité est un hôte DNF ordinaire de l’inventaire, marqué de la
fonctionnalité microvm-guest:<hypervisor> dans etc/config.yaml. Il garde son IP
de zone, ses services et sa supervision ; l’hyperviseur l’exécute et le déploie.
- hostname: "space1" zone: "cp:1.40" # fixed IP: tap + MAC are derived from it profile: "ai-space" features: ["microvm-guest:ms-a2", "monitoring-node"] # monitored like any host- Côté invité (
microvm/guest.nix) : partages virtiofs sous<stateDir>/<guest>/{persist,var-lib,home}, store de l’hôte en lecture seule, clé de l’hôte sur/persist, lien statique/32routé par l’hyperviseur. - Côté hyperviseur (
microvm/host.nix) : un tap routé par invité (pas de pont), tout l’hôte sur networkd avec la passerelle de zone comme résolveur statique, proxy ARP sur le LAN, isolation nftables (tablednf-microvm),<stateDir>fermé aux utilisateurs de l’hyperviseur (0770) et monténosuid,nodev(fichiers setuid, nœuds de périphérique d’un invité inertes). - Parole vers texte (
microvm/whisper.nix) : un serveur whisper.cpp sur l’hyperviseur pour les invités avecdarkone.system.microvm.guest.whisper(notes vocales OpenClaw), démarré et ouvert pour eux seuls ;whisper.model,whisper.threadsle règlent.
- hypervisor
nullOr strHyperviseur exécutant cet hôte comme invité microVM, null sur machine physique. - stateDir
strRépertoire de l’hyperviseur contenant un sous-répertoire de partages par invité. - guest.vcpu
ints.positiveProcesseurs virtuels de l’invité. - guest.mem
ints.positiveRAM de l’invité, en MiB. - guest.whisper
boolUtiliser le serveur whisper.cpp de l’hyperviseur (parole vers texte) : démarré et ouvert à cet invité. - guestInputRules
linesRègles nftables de la chaîne d’entréednf-microvmde l’hyperviseur, avant le drop du trafic des invités. - whisper.model
pathModèle ggml whisper.cpp du serveur de l’hyperviseur, chargé une fois pour tous les invités. - whisper.threads
ints.positiveThreads du serveur whisper.cpp de l’hyperviseur, une transcription à la fois.
darkone.system.microvm = { hypervisor = dnfLib.getMicrovmHypervisor hosts host; stateDir = "/var/lib/microvms"; guest.vcpu = 2; guest.mem = 4096; guest.whisper = false; guestInputRules = ""; whisper.model = pkgs.fetchurl { url = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin"; sha256 = "1fc70f774d38eb169993ac391eea357ef47c88757ef72ee5943879b7e8e2bc69"; }; whisper.threads = 8;};⚙ darkone.system.security
Section intitulée « ⚙ darkone.system.security »Durcissement système selon l’ANSSI BP-028 v2.0 (GNU/Linux). (wip)
Module recommandé, activé dans certains profils hôte ou manuellement selon les besoins. Applique progressivement les recommandations ANSSI selon le niveau choisi et la catégorie de machine. Chaque profil hôte définit son niveau et sa catégorie :
darkone.system.security = { level = "intermediary"; # minimal | intermediary | reinforced | high category = "server"; # base | client | server};Les règles incompatibles avec l’environnement sont exclues par tag :
darkone.system.security.excludes = [ "needs-jit" "needs-hibernation" ];Une règle spécifique peut être contournée avec une justification obligatoire :
darkone.system.security.exceptions = { R9.rationale = "Docker rootless required during development.";};- enable
boolActive le module de durcissement ANSSI BP-028 v2.0. - level
enum [ "minimal" "intermediary" "reinforced" "high" ]Niveau de durcissement ANSSI ciblé. - category
enum [ "base" "client" "server" ]Catégorie de machine qui sélectionne les sous-ensembles de règles. - excludes
listOf strTags qui désactivent des groupes entiers de règles. - exceptions
attrsExceptions par règle avec justification obligatoire. - adminMailbox
strAdresse e-mail de l’administrateur (sudo R39, alias MTA R75). - useHardenedKernel
boolUtilise linuxPackages_hardened (R60, C1) au lieu du noyau par défaut. - allowedActiveUsers
listOf strComptes humains autorisés à s’authentifier (R30). Par défaut, tout utilisateur déclaré dans config.yaml (host.users) plus le compte de déploiement du frameworknix. Les comptes désactivés (pas de credential mot de passe) et les comptes de service (pas des utilisateurs normaux) n’ont pas besoin d’être listés.
darkone.system.security = { enable = false; level = "minimal"; category = "base"; excludes = [ ]; exceptions = { }; adminMailbox = "admin@exemple.fr"; useHardenedKernel = false; allowedActiveUsers = [ ];};⚙ darkone.system.services.caddy
Section intitulée « ⚙ darkone.system.services.caddy »Services DNF : reverse proxy Caddy. Doc : en-tête de ../services.nix.
⚙ darkone.system.services.oauth2-proxy
Section intitulée « ⚙ darkone.system.services.oauth2-proxy »Services DNF : SSO oauth2-proxy. Doc : en-tête de ../services.nix.
⚙ darkone.system.services
Section intitulée « ⚙ darkone.system.services »Registre des services DNF : chaque module de service s’y enregistre, et le registre est résolu par rapport à la topologie réseau.
Sous-modules (services/) :
caddy.nix: vhosts du reverse proxy, TLS, pare-feu HTTP(S) ;oauth2-proxy.nix: SSO Kanidm des services protégés.
Reverse proxy (caddy.nix)
Section intitulée « Reverse proxy (caddy.nix) »Un vhost Caddy par service résolu, plus le pare-feu HTTP(S) des passerelles de zone et du HCS.
SSO (oauth2-proxy.nix)
Section intitulée « SSO (oauth2-proxy.nix) »oauth2-proxy devant les services protégés d’une zone, adossé au client
OAuth2 internal-service de Kanidm.
- enable
boolActiver le gestionnaire de services DNF pour enregistrer et exposer les services - service
attrsConfiguration globale des services <name>- enable
boolActiver le proxy du service - defaultParams
submoduleCes options sont calculées par dnfLib.srv.extractServiceParams- domain
strNom de domaine du service - title
strNom affiché sur la page d’accueil - description
strDescription du service pour la page d’accueil - icon
strNom de l’icône pour la page d’accueil 🡕 - global
boolLe service global est accessible sur Internet - noRobots
boolEmpêcher les robots de scanner si global est vrai - fqdn
strFQDN calculé du service avant le reverse proxy - href
strURL calculée du service avant le reverse proxy - ip
strIP calculée pour contacter le service
- domain
- displayOnHomepage
boolAfficher un lien sur la page d’accueil - reverseProxy
boolAtteint via le reverse proxy de la passerelle de zone (le DNS pointe sur l’IP LAN de la passerelle) - uniquePerZone
boolAu plus une instance autorisée par zone (validation du générateur) - externalAccess
boolService www-zone joignable depuis le LAN via une IP hôte fixe (ex. headscale, turn) - persist.dirs
listOf strRépertoires persistants du service - persist.files
listOf strFichiers persistants du service - persist.dbDirs
listOf strRépertoires persistants du service avec base(s) de données - persist.dbFiles
listOf strFichier(s) de base de données du service - persist.varDirs
listOf strFichiers secondaires variables (log, cache, etc.) - persist.mediaDirs
listOf strRépertoires médias du service (images, vidéos, gros fichiers) - proxy.enable
boolCréer la configuration virtualHost (false pour les services qui gèrent le leur) - proxy.isProtected
boolService protégé par Oauth2 - proxy.allowedGroups
listOf strGroupes Kanidm autorisés sur ce service protégé (vide = tout utilisateur authentifié) - proxy.spaceMembersOnly
boolRestreindre aux membres de l’espace IA de l’hôte de l’instance (groupe Kanidmspace-<host>) ; implique isProtected - proxy.isInternal
boolLier le service sur l’interface interne uniquement (pas accessible depuis Internet) - proxy.hasReverseProxy
boolC’est un reverse proxy (ou une autre configuration virtualhost via extraConfig) - proxy.defaultService
boolEst le service par défaut - proxy.servicePort
nullOr portPort interne du service - proxy.preExtraConfig
linesConfiguration caddy virtualHost supplémentaire (préfixe) - proxy.extraConfig
linesConfiguration caddy virtualHost supplémentaire - proxy.extraGlobalConfig
linesConfiguration caddy supplémentaire - proxy.scheme
strSchéma du service interne (http / https)
- enable
- resolved
rawRegistre des services résolus.
darkone.system.services = { enable = false; service.enable = false; service.defaultParams.domain = ""; service.defaultParams.title = ""; service.defaultParams.description = ""; service.defaultParams.icon = ""; service.defaultParams.global = false; service.defaultParams.noRobots = true; service.defaultParams.fqdn = ""; service.defaultParams.href = ""; service.defaultParams.ip = ""; service.displayOnHomepage = true; service.reverseProxy = true; service.uniquePerZone = false; service.externalAccess = false; service.persist.dirs = [ ]; service.persist.files = [ ]; service.persist.dbDirs = [ ]; service.persist.dbFiles = [ ]; service.persist.varDirs = [ ]; service.persist.mediaDirs = [ ]; service.proxy.enable = true; service.proxy.isProtected = false; service.proxy.allowedGroups = [ ]; service.proxy.spaceMembersOnly = false; service.proxy.isInternal = false; service.proxy.hasReverseProxy = true; service.proxy.defaultService = false; service.proxy.servicePort = null; service.proxy.preExtraConfig = ""; service.proxy.extraConfig = ""; service.proxy.extraGlobalConfig = ""; service.proxy.scheme = "https"; resolved = { inherit services hasProtectedServices authHost; };};⚙ darkone.system.sops
Section intitulée « ⚙ darkone.system.sops »sops DNF, gestion des mots de passe et des secrets.
Câble sops-nix sur usr/secrets/secrets.yaml et le déverrouille avec la
clé SSH hôte (ssh_host_ed25519_key) plus la clé age d’infrastructure
dédiée (/etc/sops/age/infra.key). Déclare un secret
user/<login>/password-hash par utilisateur hôte, avec
neededForUsers = true pour que le hash soit disponible avant la création
des comptes utilisateurs.
Rien d’autre n’est matérialisé ici : un secret appartient aux hôtes qui le consomment, déclaré par le module qui le lit.
- enable
boolActiver la configuration automatisée sops pour DNF - isolated
boolFichier de secrets propre, sans clé infra, sans hash de mots de passe
darkone.system.sops = { enable = false; isolated = false;};⚙ darkone.system.srv-dirs
Section intitulée « ⚙ darkone.system.srv-dirs »Service de gestion des répertoires partagés.
- enable
boolActiver les répertoires partagés, créer le répertoire racine (défaut /srv) - enableNfs
boolActiver les chemins du service NFS (nfs/common, nfs/homes) - enableMedias
boolActiver les chemins des services média (medias/[videos|music|incomming/…]) - enableStk
boolActiver le chemin de partage des pistes SuperTuxKart (nfs/stk-tracks) - root
strRépertoire racine des données persistantes (/srv) - nfs
strRépertoire racine NFS (/srv/nfs) - homes
strRépertoire des domiciles partagés (/srv/nfs/homes) - common
strRépertoire commun partagé (/srv/nfs/common lié à ~/Public) - stkTracks
strRépertoire des pistes SuperTuxKart partagées (/srv/nfs/stk-tracks) - medias
strRépertoire racine des médias (/srv/medias) - music
strRépertoire des fichiers musicaux partagés (/srv/medias/music) - videos
strRépertoire des fichiers vidéo partagés (/srv/medias/videos) - incoming
strRépertoire des fichiers entrants partagés (/srv/medias/incoming accès en écriture) - incomingMusic
strRépertoire des fichiers entrants partagés (/srv/medias/incoming/music accès en écriture) - incomingVideos
strRépertoire des fichiers entrants partagés (/srv/medias/incoming/videos accès en écriture)
darkone.system.srv-dirs = { enable = cfg.enableNfs || cfg.enableMedias || cfg.enableStk; enableNfs = false; enableMedias = false; enableStk = false; root = "/srv"; nfs = null; homes = null; common = null; stkTracks = null; medias = null; music = null; videos = null; incoming = null; incomingMusic = null; incomingVideos = null;};⚙ darkone.system.testing
Section intitulée « ⚙ darkone.system.testing »Mode test autonome pour le NixOS Test Driver.
Quand activé, neutralise les parties irréductiblement externes/exécution afin qu’un nœud puisse être testé dans une VM : headscale/tailscale deviennent des no-op et un certificat TLS fixe peut simuler ACME. Permet également à la configuration propre à workDir (nix.pub, harmonia.pub) d’être ignorée par core/nix-cache. sops reste RÉEL (haute fidélité).
- standalone
boolMode test autonome — ignore la configuration propre à workDir (nix.pub, harmonia.pub) et neutralise les services externes - tlsCert
nullOr pathCertificat auto-signé (PEM) fourni par le harnais de test pour simuler ACME. Jamais un chemin tests/ intégré dans le framework. - tlsKey
nullOr pathClé privée (PEM) associée à tlsCert.
darkone.system.testing = { standalone = false; tlsCert = null; tlsKey = null;};⚙ darkone.system.yubikey
Section intitulée « ⚙ darkone.system.yubikey »Authentification forte par YubiKey : PAM U2F à l’échelle du parc + déchiffrement LUKS déclaratif par FIDO2.
Un seul enrôlement physique par (utilisateur, clé) sur l’hôte admin : just yubikey <user> [key] [action] alimente :
- un registre public
usr/secrets/yubikeys.jsondans l’espace de travail consommateur : l’identifiant de l’attestation pamu2fcfg, l’id du credential FIDO2 hmac-secret et le sel (inutilisables sans la clé physique, donc committables) ; - un secret sops
yubikey/<user>/<key>/luks-secret(le mot de passe LUKS dérivé de FIDO2) ainsi que leluks-passphrasepartagé (le mot de passe saisi à l’installation disko, requis pour autoriser de nouveaux keyslots).
Le module propage ensuite tout déclarativement (just apply), sans jamais
brancher la clé sur les hôtes cibles :
- connexion / sudo / greeter : pam_u2f comme alternative au mot de
passe (
sufficient) : toucher la clé connecte, le mot de passe sops reste le repli automatique. Un seul credential est valable sur tout le parc grâce à l’origine fixe (pam://<network.domain>). - hôtes chiffrés : un service oneshot auto-enrôle chaque clé déclarée
dans chaque en-tête LUKS2 déclaré par disko (keyslot + jeton
systemd-fido2, le format exact écrit parsystemd-cryptenroll) et retire les révoquées. La clé physique n’est nécessaire qu’au démarrage, pour déverrouiller le disque.
- enable
boolAuthentification par YubiKey (défaut : activée si usr/secrets/yubikeys.json existe) - origin
strOrigine/appid pam_u2f fixe : un seul enrôlement est valable sur tout le parc - luks.enable
boolDéverrouillage FIDO2 des volumes LUKS de l’hôte (inerte sans LUKS disko + clés enrôlées)
darkone.system.yubikey = { enable = hasRegistry; origin = "pam://${network.domain}"; luks.enable = true;};