Skip to content

Security modules

Cross-cutting complementary measures β€” Annex A (C1–C12). (wip)

These measures have no ANSSI number but are essential to the guide’s spirit. Covers linux-hardened patches (C1), Lockdown LSM (C2), USBGuard (C3), nftables deny-by-default (C4), hardened SSH (C5), LUKS2 (C6), NTP/NTS (C7), secure DNS (C8), disabled core dumps (C9), PAM anti brute-force (C10), legal banners (C11), and cron/at restriction (C12).

  • enable bool Enable complementary ANSSI measures (C1–C12).
  • lockdownLevel enum [ "none" "integrity" "confidentiality" ] Lockdown LSM level (C2)
  • lsmStack listOf str LSM stack order (C2, R11, R20). Modifies boot.kernelParams lsm=…
  • ntpServers listOf str NTP/NTS servers (C7).
  • useNts bool Enable NTS (Network Time Security) for NTP authentication (C7).
  • sshBanner str SSH banner displayed before authentication (C11).
  • cronAllowedUsers listOf str Users allowed to schedule cron jobs (C12).
  • atAllowedUsers listOf str Users allowed to schedule at jobs (C12). Mirrors cronAllowedUsers by default.
  • egressAllowlist listOf str IPv4/IPv6 addresses or CIDRs allowed outbound under the C4 reinforced egress deny-by-default policy. Empty list keeps the policy but only loopback and established/related traffic pass (proxy required).
darkone.security.complement = {
enable = false;
lockdownLevel = "integrity";
lsmStack = [ ];
ntpServers = [ ];
useNts = true;
sshBanner = ''
╔═════════════════════════════════════════════════════════╗
β•‘ DNF NETWORK NODE β•‘
β•‘ Restricted and monitored access. β•‘
β•‘ Unauthorized access may result in criminal prosecution β•‘
β•‘ under Articles 323-1 to 323-8 of the French Penal Code. β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
'';
cronAllowedUsers = [ ];
atAllowedUsers = [ ];
egressAllowlist = [ ];
};

Partitioning, file tree, and file permissions (R28–R29, R50–R57). (wip)

Covers secure mount options (R28), restriction of /boot (R29), permissions of sensitive files (R50), passwords kept out of the store (R51), sockets (R52), orphan files (R53), sticky bit (R54), per-user temporary directories (R55), and setuid (R56, R57).

  • enable bool Enable ANSSI filesystem hardening (R28–R29, R50–R57).
  • allowedSetuid listOf str Allowlist of tolerated setuid/setgid binaries (R56, R57).
  • tmpfs bool Whether R28 owns /tmp as a hardened tmpfs sized on boot.tmp.tmpfsSize. Set to false to leave /tmp on the filesystem it already lives on β€” a host keeping several GB of persistent scratch there loses both the data and the RAM otherwise.
  • tmpNoexec bool Whether the R28 /tmp carries noexec. noexec blocks direct execution and dlopen, so it breaks any runtime unpacking native libraries there (Bun, Electron, agent tooling) and anything compiling then running in TMPDIR. Only meaningful when tmpfs is true.
  • extraMountHardening listOf str Mount points to harden with nosuid,nodev,noexec (R28). Each listed path MUST be a separate partition declared elsewhere (hardware- configuration / disko); the options are merged into its existing fileSystems entry. An explicit list (rather than a global flag) avoids guessing the layout and never creates phantom mounts. Do not list /home if users run scripts there β€” noexec would break them.
darkone.security.filesystem = {
enable = false;
allowedSetuid = [ ];
tmpfs = true;
tmpNoexec = true;
extraMountHardening = [ ];
};

Filesystem integrity β€” HIDS (R76–R77). (wip)

Covers sealing and integrity verification via AIDE (R76) and protection of the sealed database with GPG signature plus remote copy (R77).

  • enable bool Enable ANSSI HIDS integrity β€” AIDE (R76–R77).
  • aideRemoteCopy nullOr submodule Remote copy of the GPG-signed AIDE database (R77). null = disabled.
    • host str Remote host for the AIDE database copy.
    • sshKeyFile path Path to the private SSH key for the remote copy.
darkone.security.integrity = {
enable = false;
aideRemoteCopy = null;
aideRemoteCopy.host = null;
aideRemoteCopy.sshKeyFile = null;
};

Logging and auditd (R71–R73). (wip)

Covers persistent logging with TLS forwarding (R71), per-service dedicated journals (R72), and auditd with ANSSI rules (R73).

  • enable bool Enable ANSSI logging and auditd (R71–R73).
darkone.security.journaling.enable = false;

Static kernel configuration β€” requires recompilation (R15–R27). (wip)

These rules require a custom kernel via boot.kernelPackages. NixOS allows this through structuredExtraConfig. All these rules carry the kernel-recompile tag: they are skipped if that tag is in excludes.

  • enable bool Enable the ANSSI kernel recompilation module (R15–R27).
darkone.security.kernel-build.enable = false;

Dynamic kernel configuration: boot parameters and sysctls (R8–R14). (wip)

Rules applicable without recompiling the kernel. Covers memory options (R8), system sysctls (R9), disabling module loading (R10), Yama/ptrace (R11), IPv4 network sysctls (R12), IPv6 disabling (R13), and filesystem sysctls (R14).

  • enable bool Enable ANSSI dynamic kernel parameters (R8–R14).
  • restrictUserns bool R9: forbid unprivileged user namespaces (user.max_user_namespaces). Breaks Flatpak, bubblewrap and rootless Podman, so it stays opt-in and belongs on servers that run none of them.
  • disableSmt bool R8: turn hyperthreading off. Three parameters carry it; false falls each back to its SMT-preserving form and leaves the other fourteen alone. Note l1tf=full drops SMT even without force β€” it only leaves the sysfs knob behind β€” so the fallback there is flush.
  • memoryDebug bool R8: the allocator extras β€” slub_debug=FZP, page_poison=on and slab_nomerge=yes. None of the three is in the ANSSI R8 list, and page_poison displaces init_on_alloc/init_on_free, which are: the kernel says so at boot, and honours the debug facility instead. Turning it off restores the specified wipe and gives back 7% of a compile (95s to 88s, ms-a2, SMT on).
  • disableIpv6 bool R13: disable IPv6 entirely. Off by default β€” ANSSI itself prefers hardening IPv6 over dropping it, and a public host generally needs it.
darkone.security.kernel-params = {
enable = false;
restrictUserns = false;
disableSmt = true;
memoryDebug = true;
disableIpv6 = false;
};

Mandatory Access Control β€” MAC (R37, R45–R49). (wip)

R37 is a meta rule: valid if at least R45 (AppArmor) or R46 (SELinux) is active. SELinux (R46–R49) is not supported on NixOS and is excluded by default via exceptions. AppArmor (R45) is partially supported.

  • enable bool Enable ANSSI MAC module β€” AppArmor/SELinux (R37, R45–R49).
darkone.security.mac.enable = false;

Local mail handling (R74–R75). (wip)

Covers the local MTA hardened to loopback-only (R74) and mail aliases routed to the administrator’s address (R75). These rules only apply if an MTA service is active (Postfix or OpenSMTPD).

  • enable bool Enable ANSSI MTA hardening (R74–R75).
darkone.security.mta.enable = false;

Network services β€” isolation and supervision (R78–R80). (wip)

Covers isolation of network services (R78: server category), hardening and monitoring of exposed services (R79: fail2ban, HTTP headers, ANSSI TLS) and reduction of the network surface (R80).

  • enable bool Enable ANSSI network hardening (R78–R80).
  • exposedServices listOf str Public services to jail with fail2ban (R79). Each entry MUST resolve to a filter β€” one shipped by fail2ban, or one declared in filters β€” or the assertion refuses the build.
  • filters attrs Filters usable in exposedServices, on top of those fail2ban ships. Caddy, Synapse and Kanidm need theirs written against real journal output β€” declare them here once the patterns are confirmed.
    • journalmatch str Journal match for the jail (fail2ban runs on the systemd backend).
    • failregex str Failure pattern, written to filter.d/<name>.conf. Empty when fail2ban already ships the filter and only the match is missing.
  • publicListeners listOf str Services allowed to listen on 0.0.0.0 / :: (R80).
  • httpsHeaders bool Adds ANSSI HTTP security headers in Nginx/Caddy (R79).
  • tlsCiphers listOf str ANSSI TLS cipher suite for Nginx (R79).
darkone.security.network = {
enable = false;
exposedServices = [ ];
filters.journalmatch = "_SYSTEMD_UNIT=forgejo.service";
filters.failregex = "";
publicListeners = [ ];
httpsHeaders = true;
tlsCiphers = [ ];
};

NSS β€” Remote user databases (R69–R70). (wip)

Rules applicable only if an external NSS is active (SSSD, nslcd). Not currently relevant for DNF; see what can be done with Kanidm + PAM.

Covers hardening of remote databases (R69: TLS mandatory) and separation of system and directory accounts (R70).

  • enable bool Enable ANSSI NSS hardening (R69–R70).
darkone.security.nss.enable = false;

Package management and updates (R58–R61). (wip)

Covers installing only what is strictly necessary (R58), trusted repositories (R59), hardened repositories (R60: linux_hardened), and regular updates (R61).

  • enable bool Enable ANSSI package management (R58–R61).
  • trustedSubstituters listOf str Allowlist of authorized Nix binary caches (R59).
  • trustedPublicKeys listOf str Public keys for authorized binary caches (R59).
  • allowedUris listOf str Prefix allowlist of URIs fetchable during evaluation (R59). Restricts builtins.fetch* / flake inputs to internal mirrors. Empty list disables the restriction.
  • maxSystemPackages nullOr int Soft threshold on the number of system packages (R58). Emits an evaluation warning when exceeded; null disables the check.
darkone.security.packages = {
enable = false;
trustedSubstituters = [ ];
trustedPublicKeys = [ ];
allowedUris = [ ];
maxSystemPackages = 200;
};

PAM β€” Authentication and password storage (R67–R68). (wip)

Covers secure remote PAM authentication (R67: SSSD, Kerberos, pam_faillock) and encrypted password storage (R68: yescrypt).

  • enable bool Enable ANSSI PAM module β€” authentication and passwords (R67–R68).
  • faillock.deny ints.positive Failed attempts before pam_faillock locks the account (R67).
  • faillock.unlockTime ints.positive Seconds an account stays locked (R67). Unlock early with faillock --reset.
darkone.security.pam = {
enable = false;
faillock.deny = 3;
faillock.unlockTime = 900;
};

Hardware configuration and secure boot (R1–R7). (wip)

Covers UEFI Secure Boot (R3), lanzaboote, bootloader password (R5), signed UKIs (R6), and IOMMU (R7). R1 and R2 (hardware/firmware) are out of NixOS scope and only produce a note in the report.

  • enable bool Enable ANSSI secure boot β€” Secure Boot, IOMMU (R1–R7).
darkone.security.preboot.enable = false;

Hardening of systemd services (R62–R66). (wip)

Covers disabling unnecessary services (R62), reducing functionality via systemd security options (R63), privilege restriction (R64), isolation (R65), and hardening of containerization components (R66).

  • enable bool Enable ANSSI hardening of systemd services (R62–R66).
  • rootServicesAllowed listOf str systemd services allowed to run as root without CapabilityBoundingSet (R64).
  • hardenedUnits listOf str systemd units to which the ANSSI hardening baseline is applied (R52 RuntimeDirectoryMode, R55 PrivateTmp, R63 full sandbox via dnfLib.mkHardenedServiceConfig). Empty by default: blanket sandboxing can break services that need write access or extra syscalls, so units are opt-in. List framework-owned units known to tolerate confinement; per-unit serviceConfig overrides still win.
darkone.security.services = {
enable = false;
rootServicesAllowed = [ ];
hardenedUnits = [ ];
};

sudo hardening (R38–R44). (wip)

Covers the dedicated sudo group (R38), hardened sudo directives (R39), restriction of non-root targets (R40), NOEXEC limitation (R41), forbidding negations (R42), explicit argument specification (R43), and use of sudoedit (R44).

  • enable bool Enable ANSSI sudo hardening (R38–R44).
  • allowedRootRules listOf str sudo rules allowed to target root (R40). Others must target a service account.
darkone.security.sudo = {
enable = false;
allowedRootRules = [ ];
};

User accounts and authentication (R30–R36). (wip)

Covers unused accounts (R30), password policy (R31), inactivity locking (R32), admin accountability (R33), service accounts (R34), unique service accounts (R35), and umask (R36).

  • enable bool Enable ANSSI account management (R30–R36).
  • idleTimeout ints.unsigned Seconds before an idle login shell exits (R32, TMOUT). 0 drops the TTY timeout entirely; the GNOME idle lock is unaffected. Workstations holding long-lived terminals (build farm, agent tooling) set 0.
darkone.security.users = {
enable = false;
idleTimeout = 600;
};