← All case studies

Case Study

Implementing the NixOS STIG in a Regulated Environment

A regulated organization made a US DoD hardening baseline part of its NixOS releases. Those releases now ship with compliance evidence, and tests show that key rules both pass and work.

Industry
Regulated environment
Service
Secure Software Supply Chain
Scope
All 103 rules of the NixOS STIG
Stack
NixOS modules · NixOS integration tests · auditd · PAM · FIPS-capable kernel
Talks
Implementing the NixOS STIG 🔗 · Alexander Foremny · NixCon 2026, Kraków

The goal

Our client built and operates secure infrastructure with NixOS in a regulated environment. They chose NixOS years ago in an evaluation we did together. Stakeholders valued NixOS’s declarative nature because audit-related documentation maps more easily to declarative NixOS configs than to Ansible scripts. Every machine is described in code, making it easy to map a line of NixOS config to the regulation it satisfies.

Government auditors expect detailed proof: The client must show that every machine follows a government-grade hardening baseline with a formal description. The client needs to implement the NixOS STIG (Security Technical Implementation Guide), published by DISA (US Department of Defense) in 2024 and revised in 2025 as part of its STIG library. 103 rules cover auditing, cryptography, SSH, kernel, and PAM. Each rule expresses/checks the requirements in NIST SP 800-53. Rules consist of check text, the command an assessor runs, and fix text (which, for NixOS, is usually a line of Nix). Because a failed audit can hold up operations or accreditation, this baseline must hold on every machine and in every release.

Passing != working

At first, this work seems like a plain translation of fix texts into NixOS configuration values. This is not completely wrong, but most of the effort turned out to be elsewhere, as the following examples show: The STIG is young/unpolished, and for some rules, the fix text and check text don’t work well together right away. Several rules require domain knowledge to implement them. Good examples include PAM account lockout and audit daemon configuration. Many text rules are about runtime state like file modes and ownership, which a NixOS configuration doesn’t describe or enforce over a system’s lifetime.

The four time-consuming parts: STIG immaturity, checks run by hand, programs that have to be learned, and rules about state rather than configuration.
Where the time actually went. (Slide from the NixCon 2026 talk)

A passing configuration check shows that a setting is present, but does this imply that the setting works at runtime? Passing an audit only at build time is meaningless.

Diagram: the operator must pass compliance and must hold security, and the slide asks whether compliance and security are the same thing.
Operators ship the product, compliance needs the checklist satisfied, security needs the system to hold. (Slide from the NixCon 2026 talk)

Integration

An audit is evidence for one moment only: The state of a running machine can drift away from its initial configuration. Since the client already treated infrastructure as code, compliance checks became code, too. Each STIG rule is one NixOS module, addressed by its STIG ID. A STIG rule contains:

  • the configuration change
  • a verifier that runs the check text as a small program on the machine
  • rule metadata for the compliance report
Diagram: the STIG's metadata and discussion become a compliance report, the check text becomes a runtime verifier, and the fix text becomes a NixOS module.
Each part of a STIG rule becomes a part of the system. (Slide from the NixCon 2026 talk)

The community has already done valuable hardening work:

When we compared them, none combined all the parts our client needed: single rules that can be switched off, a compliance report, a runtime verifier, and tests of actual security behavior.

We left the check texts exactly as the STIG defines them to stick to the audit-relevant outputs. Where a check alone proves too little, we added checks and behavioral NixOS integration tests.

Slide comparing check texts from the STIG with an additional check and a behavioral test that fails three logins.
Keep the STIG's checks as they are, and add checks and behavioral tests where a check alone proves too little. (Slide from the NixCon 2026 talk)

Findings of the extra tests

Following the STIG to the letter would have produced green checks in places where the machine was not doing what the rule intended, so the examples below show why extra tests matter. Let’s look at examples:

Account locking

Rule ANIX-00-000040 requires locking an account after 3 failed logins. In the NixOS configuration, the fix text’s PAM configuration and the default NixOS password check don’t mix well, so the result is that after 3 failed login attempts, login still works because the default password check comes first in the PAM config. After 3 failed login attempts, login still works because the default password check comes first in the PAM config. Only an integration test catches and rules out such behavior. The rule now uses the NixOS PAM options with an explicit order.

Test script: three logins with a wrong password, then a login with the right password that is expected to fail but succeeds.
The VM test that caught it: after three wrong passwords, the right one still worked. (Slide from the NixCon 2026 talk)

FIPS mode activation

The high-severity rule ANIX-00-001840 requires FIPS 140-validated cryptography. Even if the fix text adds fips=1 to the kernel command line, this check is worthless if the kernel ignores it at runtime with a message like

Unknown kernel command line parameters "fips=1", will be passed to user space

The default kernel configuration doesn’t enable FIPS mode and ignores the parameter, so the check passes while FIPS is off. This means that the kernel configuration also needs to be checked.

The fix text adds fips=1 to the kernel parameters, the check text searches the kernel command line for it, and the kernel log reports the parameter as unknown.
Fix text and check text agree, and the kernel log shows that the parameter has no effect. (Slide from the NixCon 2026 talk)

auditd configuration

Multiple rules’ fix texts, such as ANIX-00-000420, write the auditd configuration file directly, although NixOS generates that file from its options. Although the machine would behave correctly at runtime, reports derived from the configuration would still show the old configuration. Setting all values through NixOS options synchronizes report and machine config at runtime.

Firewall rules

The fix text of rule ANIX-00-000010 enables the firewall. It also requires firewall-panic tools that cut all remote access in an emergency. The fix text does not assert their installation. With those tools added, the rule check passes and works as intended.

Luckily, NixOS, the Linux kernel, modules, etc. are fully open source, so these rules and checks were easy to fix.

Result

The client’s NixOS releases now implement all 103 STIG rules, and each release ships with compliance reports as build artifacts. Compliance reports are just a build artifact of releases. Behavioral integration tests with VMs quickly uncover regressions before deployment, which keeps maintenance cycles short.

Because drift may occur after system updates or bad configuration changes, we run verifiers in CI and on deployed machines.

Open source

The implementation is client-only and not public today. We’re working on a release, but it has to clear multiple bureaucratic hurdles.

In the meantime, watch our NixCon 2026 talk!

Value Proposition

  • The hardening works, including in places where following the STIG to the letter would not have been enough, and the releases ship with compliance evidence.
  • Easy audit generation with every build.
  • Drift detection: In the CI and on the deployed machines.
  • Porting effort for 103 rules is a one-time investment that pays off for all future deployments.
  • In practice, our client’s vision of mapping NixOS configuration lines to regulatory requirements proved feasible: all hardening settings map to their STIG rules.

Facing a similar problem?

Talk to our engineers