1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.boot.initrd.clevis;
9 systemd = config.boot.initrd.systemd;
10 supportedFs = [
11 "zfs"
12 "bcachefs"
13 ];
14in
15{
16 meta.maintainers = with lib.maintainers; [
17 julienmalka
18 camillemndn
19 ];
20 meta.doc = ./clevis.md;
21
22 options = {
23 boot.initrd.clevis.enable = lib.mkEnableOption "Clevis in initrd";
24
25 boot.initrd.clevis.package = lib.mkPackageOption pkgs "clevis" { };
26
27 boot.initrd.clevis.devices = lib.mkOption {
28 description = "Encrypted devices that need to be unlocked at boot using Clevis";
29 default = { };
30 type = lib.types.attrsOf (
31 lib.types.submodule ({
32 options.secretFile = lib.mkOption {
33 description = "Clevis JWE file used to decrypt the device at boot, in concert with the chosen pin (one of TPM2, Tang server, or SSS).";
34 type = lib.types.path;
35 };
36 })
37 );
38 };
39
40 boot.initrd.clevis.useTang = lib.mkOption {
41 description = "Whether the Clevis JWE file used to decrypt the devices uses a Tang server as a pin.";
42 default = false;
43 type = lib.types.bool;
44 };
45
46 };
47
48 config = lib.mkIf cfg.enable {
49
50 # Implementation of clevis unlocking for the supported filesystems are located directly in the respective modules.
51
52 assertions = (
53 lib.attrValues (
54 lib.mapAttrs (device: _: {
55 assertion =
56 (lib.any (
57 fs:
58 fs.device == device && (lib.elem fs.fsType supportedFs)
59 || (fs.fsType == "zfs" && lib.hasPrefix "${device}/" fs.device)
60 ) config.system.build.fileSystems)
61 || (lib.hasAttr device config.boot.initrd.luks.devices);
62 message = ''No filesystem or LUKS device with the name ${device} is declared in your configuration.'';
63 }) cfg.devices
64 )
65 );
66
67 warnings =
68 if
69 cfg.useTang && !config.boot.initrd.network.enable && !config.boot.initrd.systemd.network.enable
70 then
71 [ "In order to use a Tang pinned secret you must configure networking in initrd" ]
72 else
73 [ ];
74
75 boot.initrd = {
76 extraUtilsCommands = lib.mkIf (!systemd.enable) ''
77 copy_bin_and_libs ${pkgs.jose}/bin/jose
78 copy_bin_and_libs ${pkgs.curl}/bin/curl
79 copy_bin_and_libs ${pkgs.bashNonInteractive}/bin/bash
80
81 copy_bin_and_libs ${pkgs.tpm2-tools}/bin/.tpm2-wrapped
82 mv $out/bin/{.tpm2-wrapped,tpm2}
83 cp {${pkgs.tpm2-tss},$out}/lib/libtss2-tcti-device.so.0
84
85 copy_bin_and_libs ${cfg.package}/bin/.clevis-wrapped
86 mv $out/bin/{.clevis-wrapped,clevis}
87
88 for BIN in ${cfg.package}/bin/clevis-decrypt*; do
89 copy_bin_and_libs $BIN
90 done
91
92 for BIN in $out/bin/clevis{,-decrypt{,-null,-tang,-tpm2}}; do
93 sed -i $BIN -e 's,${pkgs.bashNonInteractive},,' -e 's,${pkgs.coreutils},,'
94 done
95
96 sed -i $out/bin/clevis-decrypt-tpm2 -e 's,tpm2_,tpm2 ,'
97 '';
98
99 secrets = lib.mapAttrs' (
100 name: value: lib.nameValuePair "/etc/clevis/${name}.jwe" value.secretFile
101 ) cfg.devices;
102
103 systemd = {
104 extraBin = lib.mkIf systemd.enable {
105 clevis = "${cfg.package}/bin/clevis";
106 curl = "${pkgs.curl}/bin/curl";
107 };
108
109 storePaths = lib.mkIf systemd.enable [
110 cfg.package
111 "${pkgs.jose}/bin/jose"
112 "${pkgs.curl}/bin/curl"
113 "${pkgs.tpm2-tools}/bin/tpm2_createprimary"
114 "${pkgs.tpm2-tools}/bin/tpm2_flushcontext"
115 "${pkgs.tpm2-tools}/bin/tpm2_load"
116 "${pkgs.tpm2-tools}/bin/tpm2_unseal"
117 ];
118 };
119 };
120 };
121}