NixDaemon

NixOS pentest workstation as one flake — IceBreaker's successor
git clone https://git.daemon-sec.xyz/NixDaemon.git
Log | Files | Refs | README

commit 7f4371bbd34381c9353b92739e917b297084ffe0
parent d6d42a863f25d11b6f10aa624b4a053beec9389f
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Sat, 10 Oct 2026 11:50:44 +0100

feat(ssd): add secure-vault LUKS container on the SSD

- Add a `secure-vault` script that creates, opens, locks, and reports on a 100 GB LUKS2 container stored as a file on /mnt/ssd, with a `busy` subcommand to list processes holding the vault mount.
- Serialize vault operations with a lock, and refuse to act when the SSD mount source, the container path, or the mapper's backing file is not the expected one.
- Install cryptsetup, btrfs-progs, e2fsprogs, keepassxc, age, restic, and the vault script system-wide.
- Declare /mnt/vault as a noauto, nofail ext4 mount with nosuid, nodev, and noexec, and create its root-owned tmpfiles directory.

Commit-Date: 2026-10-10T11:50:52+01:00
Commit-Host: daemonsec@nixos

Diffstat:
Mmodules/hosts/laptop/ssd.nix | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 128 insertions(+), 2 deletions(-)

diff --git a/modules/hosts/laptop/ssd.nix b/modules/hosts/laptop/ssd.nix @@ -14,12 +14,138 @@ { ... }: { flake.nixosModules.laptop-ssd = - { ... }: - { + { pkgs, user, ... }: + let + vault = pkgs.writeShellApplication { + name = "secure-vault"; + runtimeInputs = with pkgs; [ cryptsetup util-linux coreutils e2fsprogs psmisc gawk ]; + text = '' + export LC_ALL=C + case "''${1:-help}" in + init|open|lock|status|busy) ;; + *) echo 'Usage: secure-vault {init|open|lock|status|busy}'; exit 0 ;; + esac + if (( EUID != 0 )); then + exec /run/wrappers/bin/sudo "$0" "$@" + fi + image=/mnt/ssd/.secure-vault/vault.luks + pending=/mnt/ssd/.secure-vault/vault.luks.building + mapper=netrunner-private + target=/mnt/vault + owner=${user} + fail() { echo "$*" >&2; exit 1; } + # Serialize lifecycle operations. Nothing here manages the outer ssd mapper. + exec 9>/run/lock/secure-vault.lock + flock -n 9 || fail 'Another vault operation is running.' + require_ssd() { + mountpoint -q /mnt/ssd || fail 'The existing SSD must be mounted at /mnt/ssd first.' + local source + source=$(findmnt -nro SOURCE --mountpoint /mnt/ssd) + [[ "$source" == /dev/mapper/ssd || "$source" == /dev/mapper/ssd\[* ]] || + fail 'Unexpected SSD mount source; refusing to create/open a container.' + } + verify_mapper() { + local loop backing + loop=$(cryptsetup status "$mapper" | awk '$1 == "device:" {print $2}') + [[ "$loop" == /dev/loop* ]] || fail 'Vault mapper is not backed by a loop device.' + backing=$(losetup --noheadings --raw --output BACK-FILE "$loop") + [[ "$backing" == "$image" || ( "$backing" == "$pending" && ! -e "$image" ) ]] || fail 'Vault mapper belongs to a different container; refusing.' + } + verify_mount() { + local source + source=$(findmnt -nro SOURCE --mountpoint "$target") + [[ "$(readlink -f "$source")" == "$(readlink -f "/dev/mapper/$mapper")" ]] || + fail 'Unexpected filesystem at /mnt/vault; refusing to unmount it.' + } + case "$1" in + init) + require_ssd + [[ ! -e "$image" && ! -L "$image" && ! -e "$pending" && ! -L "$pending" ]] || + fail 'Container or unfinished creation already exists; refusing to overwrite it.' + [[ ! -e "/dev/mapper/$mapper" ]] || fail 'Vault mapper already exists.' + ! mountpoint -q "$target" || fail 'Vault mountpoint is already occupied.' + # 100 GB decimal, plus 5 GB spare space for the outer filesystem. + available=$(df --output=avail -B1 /mnt/ssd | tail -n 1) + (( available >= 105000000000 )) || fail 'Need at least 105 GB free on the SSD.' + [[ ! -L /mnt/ssd/.secure-vault ]] || fail 'Container directory must not be a symlink.' + install -d -m 0700 -o root -g root /mnt/ssd/.secure-vault + umask 077 + ( set -o noclobber; : > "$pending" ) + fallocate -l 100000000000 "$pending" + echo 'Creating a NEW 100 GB container. Choose a long, unique vault passphrase.' + echo 'This does not format or close the existing SSD.' + cryptsetup luksFormat --type luks2 --pbkdf argon2id --iter-time 3000 "$pending" + # Opening a regular file uses a cryptsetup-managed loop device. + cryptsetup open --type luks "$pending" "$mapper" + cleanup_init() { + if mountpoint -q "$target"; then + echo 'Initialization stopped with vault mounted; close apps and run secure-vault lock.' >&2 + else + cryptsetup close "$mapper" || true + fi + } + trap cleanup_init EXIT + mkfs.ext4 -m 0 -L PrivateVault "/dev/mapper/$mapper" + install -d -m 0700 -o root -g root "$target" + mount -o nosuid,nodev,noexec "/dev/mapper/$mapper" "$target" + install -d -m 0700 -o "$owner" -g "$(id -gn "$owner")" "$target/Private" + umount "$target" + cryptsetup close "$mapper" + trap - EXIT + mv -T "$pending" "$image" + echo '100 GB vault created and locked. Use secure-vault open.' + ;; + open) + require_ssd + [[ -f "$image" && ! -L "$image" ]] || fail 'No vault container. Run secure-vault init once.' + [[ ! -e "/dev/mapper/$mapper" ]] || fail 'Vault mapper is already open; check status.' + ! mountpoint -q "$target" || fail 'Vault mountpoint is already occupied.' + cryptsetup open --type luks "$image" "$mapper" + if ! mount "$target"; then + cryptsetup close "$mapper" + fail 'Mount failed; vault mapping closed.' + fi + echo 'Vault open: /mnt/vault/Private' + ;; + lock) + if cryptsetup status "$mapper" >/dev/null 2>&1; then + verify_mapper + if mountpoint -q "$target"; then + verify_mount + umount "$target" + fi + cryptsetup close "$mapper" + else + ! mountpoint -q "$target" || fail 'Vault mountpoint occupied but mapper absent; inspect manually.' + fi + echo 'Private vault locked. Existing SSD remains available.' + ;; + status) + cryptsetup status "$mapper" || true + findmnt --mountpoint "$target" || true + ;; + busy) + mountpoint -q "$target" || fail 'Vault is not mounted.' + fuser -vm "$target" + ;; + esac + ''; + }; + in { + environment.systemPackages = with pkgs; [ cryptsetup btrfs-progs e2fsprogs keepassxc age restic vault ]; + environment.etc.crypttab.text = '' ssd UUID=8cd17d0f-adf3-430f-9d95-d17ef42df9b0 /etc/secrets/ssd.key luks,nofail ''; + # The new inner container is independent of the existing SSD unlock. + fileSystems."/mnt/vault" = { + device = "/dev/mapper/netrunner-private"; + fsType = "ext4"; + options = [ "noauto" "nofail" "nosuid" "nodev" "noexec" ]; + }; + systemd.tmpfiles.rules = [ "d /mnt/vault 0700 root root -" ]; + fileSystems."/mnt/ssd" = { device = "/dev/mapper/ssd"; fsType = "btrfs";