- Nix 100%
| examples | add substituters abstraction | |
| .gitignore | add target/ to gitignore | |
| age.nix | add pkgsSandboxed and pkgsUnsandboxed | |
| common.nix | add pkgsSandboxed and pkgsUnsandboxed | |
| default.nix | add pkgsSandboxed and pkgsUnsandboxed | |
| flake.lock | flake.lock: Update | |
| flake.nix | add envsubst abstraction | |
| README.md | add initrd secrets use case | |
| substituters.nix | fix substituters module | |
taintnix: Generic impurity management framework for NixOS
taintnix is a framework for generating and running Bash scripts that handle impurities (such as secrets) outside of the Nix sandbox. It's essentially an overengineered makefile generator, except that it doesn't use GNU Make but instead does dependency graph resolution within Nix.
Motivation
In NixOS development, we often encounter problems involving impurities such as:
- Secrets management. All Nix store paths are world-readable, so we need some
out-of-band mechanism to provision and manage secrets.
- Once we have secrets files lying around, we also often want some way to interpolate the secrets into config files at runtime.
- Cryptographic signing of builds. To guarantee the authenticity of software, some systems (such as Android APKs or UEFI Secure Boot) make use of code signing. However, inside the Nix sandbox, we can neither access private keys outside the Nix store nor external HSMs like YubiKeys for that purpose.
- Intentionally nondeterministic builds. For instance, you may want to build disk images with unique, pseudo-random UUIDs.
Apart from some lost souls who have no problem with introducing impurities into the Nix sandbox ([1]), the standard solution to this kind of problem is to do as much as possible within the Nix sandbox, and make the Nix build produce a script that does the necessary remaining steps - such as signing - outside of the Nix sandbox. Some examples of this design pattern include:
- Various NixOS modules using
envsubstto interpolate secrets into config files in systemdExecPreStartdirectives. - NixOS initrd secrets.
- Agenix using system activation scripts.
- The robotnix release signing scripts.
Features
The basic building block of a taintnix setup is a target, which is essentially the same as a GNU Make target. taintnix can automatically generate build scripts, which can then be run outside of the Nix sandbox to produce the desired output. In particular, taintnix supports:
- Dependency management: You can specify multiple targets that depend on
each other, and taintnix will build them in the right order.
- Example: decrypting a secret and then interpolating it into some config files.
- State management: taintnix does not require you to build all of the
targets at once or on the same machine.
- Example: generating a UEFI Secure Boot signature on a build server with
access to the Secure Boot signing keys, and then deploying it on another
server while running
nixos-rebuild. - If required, taintnix can make sure that the target outputs of different NixOS generations do not mix by placing them in separate, per-generation directories, similar to agenix generations.
- Example: generating a UEFI Secure Boot signature on a build server with
access to the Secure Boot signing keys, and then deploying it on another
server while running
- Environment filtering: Taintnix will clear all env vars before running
the target generation scripts by default to reduce error surface for
user-by-user non-reproducibilities by e.g. improperly declared dependencies.
- You can specify that some specific env vars should be passed through to the build scripts.
- Fallback to Nix sandbox: For R&D/debug/testing purposes, it is often desirable to spare yourself the hassle and use "placeholder impurities" which live inside the Nix store. In such cases, taintnix automatically infers which parts of the dependency tree can be built inside the Nix sandbox - for instance, if we use placeholder secrets inside the Nix store, config file secrets interpolation can also happen inside the Nix store.