Add NBDE channel modules

This commit is contained in:
2026-03-27 15:12:40 +01:00
parent b7ed287cd0
commit 9274b5a870
6 changed files with 505 additions and 1 deletions

View File

@@ -1 +1,26 @@
# Tribes Guix Channel ## NBDE Channel
This repository provides the Guix-side pieces for network-bound disk
encryption:
- `nbde/packages/crypto.scm`
Package definitions for `luksmeta`, `tang`, and `clevis`.
- `nbde/services/tang.scm`
A standalone Tang service for Guix systems.
- `nbde/system/mapped-devices.scm`
A Clevis-backed mapped-device kind with manual `cryptsetup` fallback.
- `nbde/system/initrd.scm`
A helper around `raw-initrd` for early-boot Clevis support.
- `examples/phase0-system.scm`
Minimal reference system using the Clevis-backed mapped-device kind and
custom initrd.
Current development status:
1. `luksmeta`, `tang`, and `clevis` build successfully on `pguix`.
2. A disposable Tang + LUKS smoke test passes.
3. A QEMU Phase-0 system with encrypted root now boots unattended through
Clevis/Tang and reaches a login prompt.
For pinned bootstrap usage, generate a `channels.scm` that combines upstream
Guix with this repository's current commit.

View File

@@ -0,0 +1,48 @@
(use-modules (gnu)
(gnu services base)
(gnu services networking)
(gnu services ssh)
(gnu system mapped-devices)
(nbde system initrd)
(nbde system mapped-devices))
;; Phase-0 reference system for NBDE development. The device names and
;; interface name are intentionally simple defaults for an x86_64 QEMU guest.
(operating-system
(host-name "nbde-phase0")
(timezone "Etc/UTC")
(locale "en_US.UTF-8")
(keyboard-layout (keyboard-layout "us"))
(kernel-arguments (list "console=ttyS0"))
(initrd
(lambda (file-systems . rest)
(apply clevis-initrd file-systems
#:network (nbde-network-configuration
(interface "eth0")
(timeout 20))
rest)))
(bootloader
(bootloader-configuration
(bootloader grub-bootloader)
(targets (list "/dev/vda"))))
(mapped-devices
(list
(mapped-device
(source "/dev/vda2")
(target "cryptroot")
(type clevis-luks-device-mapping))))
(file-systems
(cons (file-system
(mount-point "/")
(device "/dev/mapper/cryptroot")
(type "ext4"))
%base-file-systems))
(services
(append
(list (service dhcpcd-service-type)
(service agetty-service-type
(agetty-configuration
(tty "ttyS0")
(term "vt100")))
(service openssh-service-type))
%base-services)))

180
nbde/packages/crypto.scm Normal file
View File

@@ -0,0 +1,180 @@
(define-module (nbde packages crypto)
#:use-module ((guix licenses) #:prefix license:)
#:use-module (guix build-system gnu)
#:use-module (guix build-system meson)
#:use-module (guix download)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix utils)
#:use-module (gnu packages admin)
#:use-module (gnu packages autotools)
#:use-module (gnu packages bash)
#:use-module (gnu packages base)
#:use-module (gnu packages build-tools)
#:use-module (gnu packages compression)
#:use-module (gnu packages curl)
#:use-module (gnu packages cryptsetup)
#:use-module (gnu packages documentation)
#:use-module (gnu packages hardware)
#:use-module (gnu packages jose)
#:use-module (gnu packages linux)
#:use-module (gnu packages ninja)
#:use-module (gnu packages networking)
#:use-module (gnu packages password-utils)
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages tls)
#:use-module (gnu packages web)
#:export (luksmeta tang clevis))
(define-public luksmeta
(package
(name "luksmeta")
(version "10")
(source
(origin
(method url-fetch)
(uri (string-append "https://github.com/latchset/luksmeta/archive/refs/tags/v"
version
".tar.gz"))
(sha256
(base32 "18mkb5xl2aln61gnqf9v2245akcmh8cbmmhs3p8v5qysgk1qns3d"))))
(build-system gnu-build-system)
(native-inputs
(list autoconf automake libtool pkg-config asciidoc))
(inputs
(append
(list cryptsetup
eudev)
(libcryptsetup-propagated-inputs)))
(arguments
(list
#:phases
#~(modify-phases %standard-phases
(add-before 'configure 'bootstrap
(lambda _
(invoke "autoreconf" "-vfi"))))))
(home-page "https://github.com/latchset/luksmeta/")
(synopsis "Metadata helper for LUKS headers")
(description
"LUKSMeta stores small pieces of metadata in the LUKS header. Clevis
uses it to manage bindings on LUKS1 devices.")
(license license:lgpl2.1+)))
(define-public tang
(package
(name "tang")
(version "15")
(source
(origin
(method url-fetch)
(uri (string-append "https://github.com/latchset/tang/archive/refs/tags/v"
version
".tar.gz"))
(sha256
(base32 "1abx1ajmakhyi8697lwhsbmn0zvhrlnwi79b208wqv73rnkfmfhb"))))
(build-system meson-build-system)
(native-inputs
(list asciidoc curl iproute meson ninja pkg-config))
(inputs
(list http-parser jansson jose openssl zlib))
(arguments
(list
#:phases
#~(modify-phases %standard-phases
(add-after 'unpack 'patch-http-parser-detection
(lambda _
(substitute* "meson.build"
(("inc_dir = meson.get_external_property\\('inc_dir', '-I/usr/local/include'\\)")
"inc_dir = ''")
(("lib_dir = meson.get_external_property\\('lib_dir','/usr/local/lib'\\)")
"lib_dir = ''")
(("if compiler.has_header\\('llhttp.h', args: inc_dir\\)")
"if compiler.has_header('llhttp.h')")
(("if not compiler.has_header\\('http_parser.h', args: inc_dir\\)")
"if not compiler.has_header('http_parser.h')")
(("http_parser = compiler.find_library\\(http_lib, dirs: \\[lib_dir\\]\\)")
"http_parser = compiler.find_library(http_lib)"))))
(add-after 'install 'wrap-tools
(lambda _
(use-modules (guix build utils))
(let ((jose-bin (string-append #$jose "/bin"))
(out #$output))
(wrap-program (string-append out "/bin/tang-show-keys")
`("PATH" ":" prefix (,jose-bin)))
(wrap-program (string-append out "/libexec/tangd-keygen")
`("PATH" ":" prefix (,jose-bin)))
(wrap-program (string-append out "/libexec/tangd-rotate-keys")
`("PATH" ":" prefix (,jose-bin)))))))))
(home-page "https://github.com/latchset/tang")
(synopsis "Server for binding data to network presence")
(description
"Tang is a stateless server for network-bound encryption. It provides
the server side of the Clevis and Tang NBDE workflow and can run in standalone
mode without systemd.")
(license license:gpl3+)))
(define-public clevis
(package
(name "clevis")
;; Nixpkgs currently packages v21. Bump once the v22 release hash is
;; confirmed in the channel workflow.
(version "21")
(source
(origin
(method url-fetch)
(uri (string-append "https://github.com/latchset/clevis/archive/refs/tags/v"
version
".tar.gz"))
(sha256
(base32 "04q0xzi4c3b8nhlgdwdm7v0wh33763543az1k2g7jyik7028z8qb"))))
(build-system meson-build-system)
(native-inputs
(list asciidoc meson ninja pkg-config))
(inputs
(append
(list bash-minimal
coreutils
cryptsetup
curl
eudev
grep
jansson
jose
libpwquality
luksmeta
openssl
sed
tpm2-tools
zlib)
(libcryptsetup-propagated-inputs)))
(arguments
(list
#:phases
#~(modify-phases %standard-phases
(add-after 'unpack 'patch-absolute-cat
(lambda _
(substitute* (find-files "src" ".*")
(("/bin/cat") (string-append #$coreutils "/bin/cat")))))
(add-after 'install 'wrap-clevis
(lambda _
(use-modules (guix build utils))
(let ((out #$output))
(wrap-program (string-append out "/bin/clevis")
`("PATH" ":" prefix
(,(string-append out "/bin")
,(string-append #$coreutils "/bin")
,(string-append #$cryptsetup "/bin")
,(string-append #$cryptsetup "/sbin")
,(string-append #$curl "/bin")
,(string-append #$grep "/bin")
,(string-append #$jose "/bin")
,(string-append #$luksmeta "/bin")
,(string-append #$sed "/bin")
,(string-append #$tpm2-tools "/bin"))))))))))
(home-page "https://github.com/latchset/clevis")
(synopsis "Automated decryption framework")
(description
"Clevis is a pluggable framework for automated decryption. It can bind
LUKS devices to Tang, TPM2, and other pins, and provides the client-side
commands needed for unattended unlock.")
(license license:gpl3+)))

63
nbde/services/tang.scm Normal file
View File

@@ -0,0 +1,63 @@
(define-module (nbde services tang)
#:use-module (gnu services)
#:use-module (gnu services shepherd)
#:use-module (guix gexp)
#:use-module (guix records)
#:use-module (nbde packages crypto)
#:export (tang-configuration
tang-configuration?
tang-configuration-package
tang-configuration-port
tang-configuration-key-directory
tang-service-type))
(define-record-type* <tang-configuration>
tang-configuration make-tang-configuration
tang-configuration?
(package tang-configuration-package
(default tang))
(port tang-configuration-port
(default 7654))
(key-directory tang-configuration-key-directory
(default "/var/lib/tang")))
(define (tang-activation config)
#~(begin
(use-modules (guix build utils))
(let ((key-directory #$(tang-configuration-key-directory config))
(keygen (string-append
#$(tang-configuration-package config)
"/libexec/tangd-keygen")))
(mkdir-p key-directory)
(when (<= (length (scandir key-directory)) 2)
(invoke keygen key-directory)))))
(define (tang-shepherd-service config)
(list
(shepherd-service
(documentation "Run Tang in standalone mode.")
(provision '(tang))
(requirement '(networking))
(start #~(make-forkexec-constructor
(list (string-append
#$(tang-configuration-package config)
"/libexec/tangd")
"-l"
"-p" #$(number->string
(tang-configuration-port config))
#$(tang-configuration-key-directory config))))
(stop #~(make-kill-destructor))
(respawn? #f))))
(define tang-service-type
(service-type
(name 'tang)
(extensions
(list (service-extension activation-service-type tang-activation)
(service-extension shepherd-root-service-type
tang-shepherd-service)))
(default-value (tang-configuration))
(description
"Run a standalone Tang server and initialize its key directory during
system activation.")))

94
nbde/system/initrd.scm Normal file
View File

@@ -0,0 +1,94 @@
(define-module (nbde system initrd)
#:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages base)
#:use-module (gnu packages curl)
#:use-module (gnu packages cryptsetup)
#:use-module (gnu packages jose)
#:use-module (gnu packages linux)
#:use-module (gnu system linux-initrd)
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix records)
#:use-module (nbde packages crypto)
#:export (nbde-network-configuration
nbde-network-configuration?
nbde-network-configuration-interface
nbde-network-configuration-timeout
clevis-initrd-helper-packages
clevis-initrd-network-pre-mount
clevis-initrd))
(define-record-type* <nbde-network-configuration>
nbde-network-configuration make-nbde-network-configuration
nbde-network-configuration?
(interface nbde-network-configuration-interface
(default "eth0"))
(timeout nbde-network-configuration-timeout
(default 20)))
(define (clevis-initrd-helper-packages)
(list bash-minimal
coreutils
cryptsetup-static
curl
dhcpcd
e2fsck/static
grep
iproute
jose
sed
clevis))
(define (clevis-initrd-network-pre-mount config)
"Return a pre-mount gexp that performs a minimal DHCP-based network bring-up
for initrds that need Tang."
#~(let ((ip-bin #$(file-append iproute "/sbin/ip"))
(dhcpcd-bin #$(file-append dhcpcd "/sbin/dhcpcd"))
(interface #$(nbde-network-configuration-interface config))
(timeout #$(number->string
(nbde-network-configuration-timeout config))))
(mkdir-p "/run")
(mkdir-p "/run/dhcpcd")
(mkdir-p "/var")
(mkdir-p "/var/db")
(mkdir-p "/var/db/dhcpcd")
(mkdir-p "/var/run")
(mkdir-p "/var/run/dhcpcd")
(mkdir-p "/etc")
(unless (file-exists? "/etc/resolv.conf")
(call-with-output-file "/etc/resolv.conf"
(lambda (_) #t)))
(invoke ip-bin "link" "set" "dev" interface "up")
(invoke dhcpcd-bin "-w" "-t" timeout interface)
(invoke ip-bin "-4" "addr" "show" "dev" interface)
(invoke ip-bin "-4" "route" "show")))
(define* (clevis-initrd file-systems
#:key
linux
(linux-modules '())
(mapped-devices '())
keyboard-layout
(helper-packages '())
network
qemu-networking?
volatile-root?
(on-error 'debug))
"Build an initrd with the helper packages needed for Clevis/Tang based root
unlock. NETWORK is an optional @code{<nbde-network-configuration>} record used
to request a minimal DHCP pre-mount hook."
(raw-initrd
file-systems
#:linux linux
#:linux-modules linux-modules
#:mapped-devices mapped-devices
#:keyboard-layout keyboard-layout
#:helper-packages
(append (clevis-initrd-helper-packages) helper-packages)
#:pre-mount
(and network
(clevis-initrd-network-pre-mount network))
#:qemu-networking? qemu-networking?
#:volatile-root? volatile-root?
#:on-error on-error))

View File

@@ -0,0 +1,94 @@
(define-module (nbde system mapped-devices)
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (gnu packages bash)
#:use-module (gnu system mapped-devices)
#:use-module (gnu system uuid)
#:autoload (gnu packages cryptsetup) (cryptsetup-static)
#:use-module (ice-9 match)
#:use-module (nbde packages crypto)
#:export (clevis-luks-device-mapping))
(define* (open-clevis-luks-device source targets
#:key
(clevis-package clevis)
key-file
allow-discards?
(extra-options '()))
"Return a gexp that first tries to unlock SOURCE using Clevis and falls back
to interactive cryptsetup when that fails. The fallback path intentionally
keeps a manual recovery slot available."
(with-imported-modules (source-module-closure
'((gnu build file-systems)
(guix build utils)))
(match targets
((target)
#~(let ((source #$(if (uuid? source)
(uuid-bytevector source)
source))
(keyfile #$key-file))
(mkdir-p "/run/cryptsetup/")
(let* ((partition
(if (bytevector? source)
(or (let loop ((tries-left 10))
(and (positive? tries-left)
(or (find-partition-by-luks-uuid source)
(begin
(sleep 1)
(loop (- tries-left 1))))))
(error "LUKS partition not found" source))
source))
(shell-bin #$(file-append bash-minimal "/bin/sh"))
(clevis-bin #$(file-append clevis-package "/bin/clevis"))
(cryptsetup-bin #$(file-append cryptsetup-static
"/sbin/cryptsetup"))
(cryptsetup-flags
(append
(list "open" "--type" "luks")
(if #$allow-discards?
'("--allow-discards")
'())
'#$extra-options
(list partition #$target))))
(or (zero? (system* shell-bin "-c"
(string-append
"attempt=0; "
"while :; do "
"attempt=$((attempt + 1)); "
"echo \"nbde: clevis unlock attempt ${attempt} "
partition " -> " #$target "\" >/dev/console; "
"'" clevis-bin "' luks list -d '" partition
"' >/dev/console 2>&1 || true; "
"if '" clevis-bin "' luks unlock"
" -d '" partition "'"
" -n '" #$target "'"
" >/dev/console 2>&1; then "
"exit 0; "
"fi; "
"if [ \"$attempt\" -ge 5 ]; then "
"exit 1; "
"fi; "
"sleep 2; "
"done")))
(and keyfile
(zero? (apply system*/tty cryptsetup-bin
"--key-file" keyfile
cryptsetup-flags)))
(zero? (apply system*/tty cryptsetup-bin
cryptsetup-flags)))))))))
(define* (close-clevis-luks-device source targets #:rest _)
(match targets
((target)
#~(zero? (system* #$(file-append cryptsetup-static "/sbin/cryptsetup")
"close" #$target)))))
(define clevis-luks-device-mapping
(mapped-device-kind
(open open-clevis-luks-device)
(close close-clevis-luks-device)
(modules '((rnrs bytevectors)
((gnu build file-systems)
#:select (find-partition-by-luks-uuid system*/tty))
((guix build utils)
#:select (mkdir-p))))))