# Nix Configuration Security Audit Audit date: 2026-08-14 Scope: Nix and NixOS configuration under this repository. The audit did not open or inspect any `wireguard.nix` file because those files contain private keys. Findings below are based on configuration review, not on a live host inspection. Remediation status: configuration changes from the follow-up pass are marked below. The installer test image still has a root SSH test-key exception because `tests/iso-test.sh` requires it; the installed laptop no longer authorizes that key for root. ## Findings ### 1. NUR community package supply-chain risk (Medium) **Status:** Mitigated by removing the NUR input, overlay, and NUR-only Firefox add-on packages. **Why it matters:** NUR packages are community-maintained and do not receive the same centralized review, reproducibility expectations, or provenance assurance as the nixpkgs packages selected by this repository. Avoiding them reduces exposure to unvetted package changes and potential supply-chain or malware risks. This is a risk-management decision, not a claim that NUR itself is malicious. **Verification:** `nix flake check --no-build` passes and no NUR references remain in the flake, lock file, or Nix modules. **If Firefox add-ons are restored:** use exact-version XPI files from official Mozilla Add-ons releases, fetch them with Nix `fetchurl` and a committed content hash, review the extension manifest and requested permissions, and install them through Home Manager's `extensions.packages`. Do not use NUR packages or `addons.mozilla.org/.../latest.xpi` runtime URLs. Mozilla signing helps establish publisher authenticity; the Nix hash provides reproducibility and prevents silent content changes until an explicit reviewed update. ### 2. Insecure Electron package is explicitly allowed (High) **Status:** Fixed by removing both insecure-package exceptions from `flake.nix`. **Location:** Previously in both desktop configurations. **What:** Both desktop configurations previously allowed an insecure Electron package through `permittedInsecurePackages`. **Why it matters:** This bypasses nixpkgs' insecure-package rejection. Any application using that Electron runtime inherits known upstream security risk, and the exception applies to the whole system evaluation rather than one carefully isolated package. **Recommended action:** Keep the exception absent and identify any package evaluation failure that appears after future input updates before reintroducing an explicit, time-bounded exception. ### 3. Root SSH access is baked into the laptop and installer (High) **Status:** Partially fixed. Root SSH is disabled on the installed laptop. The live installer retains its test-only root key until the ISO test harness is migrated to non-root access. **Locations:** `13-inch-thin-cannon/configuration.nix:196-198` and `13-inch-thin-cannon/installer.nix:85-95` **What:** The same tracked public test key was authorized for root SSH access on the installed laptop and the live installer. The installed laptop now sets `PermitRootLogin = "no"` and no longer configures a root key; the installer still uses the key for its SSH-based test harness. **Why it matters:** Anyone who obtains the corresponding private test key gets root access to both environments. The installer is especially sensitive: it boots a privileged root environment and can rewrite the target disk. A key named and stored as a test key is also easy to leave shared or insufficiently rotated. **Recommended action:** Keep the installed-host root restriction. Migrate `tests/iso-test.sh` to console or non-root test access, then remove the installer's root key as well. ### 4. Wi-Fi password is configured in a Nix expression (High if replaced) **Status:** Fixed. The profile now uses NetworkManager's agent-owned password flag and does not place a password in the flake. **Location:** `13-inch-thin-cannon/configuration.nix:74-80` **What:** The NetworkManager profile previously contained a literal `password` attribute. It now sets `password-flags = 1`, so NetworkManager prompts for the credential instead. **Why it matters:** Values embedded in Nix configuration can be copied into the world-readable Nix store and may also be present in build logs or system closures. Replacing the placeholder with a real 802.1X password would expose that password to users who can read the store or configuration generations. **Recommended action:** Keep using NetworkManager's agent-owned credential storage. Never commit the real value or interpolate it into a derivation. ### 5. LUKS and TPM hardening options are currently no-ops (High) **Status:** Partially fixed. `hardened.luks.enable` now asserts that an initrd LUKS device exists, and the unused `hardened.luks.tpm` option was removed. TPM enrollment itself remains a separate follow-up. **Locations:** `modules/luks.nix:8-14`, `harden/configuration.nix:30-37`, and `13-inch-thin-cannon/configuration.nix:200-205` **What:** These options previously suggested that enabling them enforced LUKS and TPM-backed unlocking without doing so. The LUKS option now has an initrd assertion; TPM enrollment is no longer exposed as a non-functional option. **Why it matters:** This creates a false security guarantee for callers of the shared module. The current hosts happen to define LUKS through their disko layouts, but changing a layout or adding a host can silently produce an unencrypted system while the hardening option remains enabled. **Recommended action:** Keep the LUKS assertion and test the generated `fileSystems`, initrd crypt configuration, and unlock policy for every host. Implement TPM enrollment separately before enabling it on physical hardware. ### 6. Main laptop does not enable the shared kernel/AppArmor hardening (Medium) **Status:** Fixed. The laptop now enables both shared AppArmor and kernel hardening settings. **Locations:** `13-inch-thin-cannon/configuration.nix:200-206` and `modules/hardened.nix:8-25` **What:** The laptop previously left `hardened.kernel.hardened` and `hardened.apparmor.enable` unset. Both are now enabled alongside Secure Boot, LUKS, and impermanence. **Why it matters:** Secure Boot protects boot-chain integrity, but it does not replace runtime isolation. This host also enables libvirt, USB redirection, desktop applications, and a webcam module, so runtime attack surface is material. **Recommended action:** Verify the generated AppArmor profiles and service behavior on the physical laptop; enabling the framework alone is not a complete policy. ### 7. TPM-backed agenix is not enabled on the laptop (Medium) **Status:** Partially fixed. The user-home identity fallback was removed from the configured identity list. TPM-backed agenix remains disabled until a TPM identity is enrolled and tested on the physical host. **Locations:** `modules/secrets.nix:7-20` and `13-inch-thin-cannon/configuration.nix:200-205` **What:** The shared secret declarations now use only the host SSH key. The laptop does not enable `hardened.agenix`, so TPM-backed decryption is not yet configured by the shared hardening module. **Why it matters:** A host SSH key is still a high-value decryption identity, and it does not provide TPM-bound protection against offline disk theft. **Recommended action:** Enroll and test a TPM identity before enabling TPM-backed agenix. Until then, keep the host SSH key protected and verify its permissions and persistence; never place private identities in the repository or Nix store. ### 8. Disk discard is enabled for the encrypted laptop volume (Low) **Status:** Fixed by removing `allowDiscards` from the LUKS layout. **Location:** `13-inch-thin-cannon/disko.nix:22-29` **What:** LUKS was previously configured with `allowDiscards = true`. **Why it matters:** Discards can reveal information about filesystem block usage and timing to a storage device or a party observing its behavior. This is usually an availability/performance tradeoff, not a remote vulnerability, but it weakens the metadata-hiding properties expected from encrypted storage. **Recommended action:** Keep discards disabled unless SSD space reclamation is explicitly required and the information leak is accepted. ### 9. Kubernetes artifacts are downloaded without integrity verification (High) **Status:** Open. **Locations:** `kubernetes/ansible/playbooks/tasks/cilium.yml` and `kubernetes/ansible/playbooks/tasks/rook-ceph.yml`. **What:** The deployment downloads the Cilium Helm archive and Rook manifests from `helm.cilium.io` and `raw.githubusercontent.com` at deployment time. The URLs contain version strings, but the downloads are not checked against SHA-256 digests, signatures, or repository-committed copies. **Why it matters:** A compromised upstream account, repository release, CDN, DNS path, or control machine could supply modified YAML or a modified Helm chart. Kubernetes manifests run with cluster-admin-level effects, and Helm charts can install privileged workloads. **Recommended action:** Vendor the exact chart/manifests or pin and verify checksums before applying them. Prefer signed release artifacts and verify their signatures in the deployment workflow. Treat a version tag as a selector, not as an integrity guarantee. ### 10. Container image is tag-pinned but not digest-pinned (High) **Status:** Open. **Location:** `kubernetes/ansible/playbooks/tasks/rook-ceph.yml:146`. **What:** Ceph is configured as `quay.io/ceph/ceph:v19.2.0`, which identifies a version tag but not an immutable image manifest digest. **Why it matters:** Registry tags can be moved. A later deployment or node image pull could receive different bytes under the same tag, bypassing the intended review and reproducibility boundary for a privileged storage component. **Recommended action:** Pin the image to `quay.io/ceph/ceph@sha256:`, record how the digest was verified against the intended release, and repeat the same policy for every externally pulled Kubernetes image. ### 11. Moving flake references rely on lock-file discipline (Medium) **Status:** Partially mitigated. The committed lock files pin content by revision and nar hash, but nested flake declarations still use moving channels or refs such as `nixos-unstable`, `latest`, and `master`. **Locations:** `flake.nix`, `harden/flake.nix`, and `kubernetes/flake.nix`. **Why it matters:** A normal evaluation uses the lock file, but an unlocked checkout, an intentional lock update, or an unreviewed generated lock-file change can pull new upstream code into the build. This is a supply-chain review boundary, especially for bootloader, disk, secret-management, and Kubernetes components. **Recommended action:** Keep lock files committed, review every lock-file diff, avoid `latest` in source declarations, and update one dependency at a time with `nix flake check --no-build` followed by the full relevant end-to-end test before deployment. ## Items Requiring Verification - The installer enables SSH in `installer.nix:85-91` but does not define an explicit firewall policy. Verify from a booted ISO that SSH is unreachable unless the intended network and key controls are present. - `networking.firewall.checkReversePath = "loose"` is required by the WireGuard policy-routing design (`configuration.nix:118-124`) but weakens reverse-path anti-spoofing. Keep the nftables input policy restrictive and verify that spoofed or unsolicited inbound traffic is rejected. - Both desktop hosts enable libvirt and SPICE USB redirection. This is a deliberate local virtualization feature, but it expands the privileged device and VM attack surface. Review whether USB redirection is needed on every host and limit access to trusted local users. - The repository uses `nixos-unstable` in `flake.nix:5`. The lock file pins the current inputs, but the `nix flake update` shell alias updates them without a review gate. Review lock-file changes before deploying system generations. ## Positive Controls Observed - The laptop uses full-disk LUKS in its disko layout and has Secure Boot via Lanzaboote enabled. - The laptop's WireGuard kill-switch drops ordinary output when `wg0` is down; its policy-routing exception is documented in the configuration. - Password authentication is disabled for the configured SSH services, and the root password is locked in `modules/user.nix` when that module is enabled. - The laptop's root filesystem is rolled back with impermanence, while selected state and SSH host keys are intentionally persisted. ## Useful Links - [NixOS package security options](https://nixos.org/manual/nixos/stable/#sec-pkgs-configuration) - insecure package handling and exceptions - [NixOS SSH option reference](https://search.nixos.org/options?show=services.openssh.settings) - OpenSSH service settings - [NixOS impermanence guidance](https://github.com/nix-community/impermanence) - persistence and rollback model - [systemd-cryptenroll](https://www.freedesktop.org/software/systemd/man/latest/systemd-cryptenroll.html) - TPM-backed LUKS enrollment