impermanence: transform gocryptfs key generation from activation script to systemd unit

This commit is contained in:
colin 2022-12-31 10:15:08 +00:00
parent d745e3c1ee
commit f7e3e7294a
2 changed files with 78 additions and 68 deletions

View File

@ -0,0 +1,71 @@
{ config, lib, pkgs, utils, ... }:
let
store = {
device = "/mnt/impermanence/crypt/clearedonboot";
underlying = {
path = "/nix/persist/crypt/clearedonboot";
# TODO: consider moving this to /tmp, but that requires tmp be mounted first?
key = "/mnt/impermanence/crypt/clearedonboot.key";
};
};
prepareEncryptedClearedOnBoot = pkgs.writeShellApplication {
name = "prepareEncryptedClearedOnBoot";
runtimeInputs = with pkgs; [ gocryptfs ];
text = ''
backing="$1"
passfile="$2"
if ! test -e "$passfile"
then
tmpdir=$(dirname "$passfile")
mkdir -p "$backing" "$tmpdir"
# if the key doesn't exist, it's probably not mounted => delete the backing dir
rm -rf "''${backing:?}"/*
# generate key. we can "safely" keep it around for the lifetime of this boot
dd if=/dev/random bs=128 count=1 | base64 --wrap=0 > "$passfile"
# initialize the crypt store
gocryptfs -quiet -passfile "$passfile" -init "$backing"
fi
'';
};
in lib.mkIf config.sane.impermanence.enable
{
# the crypt store requires keys before being mounted
sane.fs."${store.device}".depends = [
"prepareEncryptedClearedOnBoot.service"
];
systemd.services."prepareEncryptedClearedOnBoot" =
let
mount-unit = "${utils.escapeSystemdPath store.device}.mount";
in {
description = "prepare keys for ${store.device}";
serviceConfig.ExecStart = ''
${prepareEncryptedClearedOnBoot}/bin/prepareEncryptedClearedOnBoot ${store.underlying.path} ${store.underlying.key}
'';
serviceConfig.Type = "oneshot";
# remove implicit dep on sysinit.target
unitConfig.DefaultDependencies = "no";
# make sure the encrypted file system is mounted *after* its keys have been generated.
wantedBy = [ mount-unit ];
before = [ mount-unit ];
};
fileSystems."${store.device}" = {
device = store.underlying.path;
fsType = "fuse.gocryptfs";
options = [
"nodev"
"nosuid"
"allow_other"
"passfile=${store.underlying.key}"
"defaults"
];
noCheck = true;
};
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
}

View File

@ -8,27 +8,12 @@ with lib;
let
cfg = config.sane.impermanence;
getStore = { encryptedClearOnBoot, ... }: (
if encryptedClearOnBoot then {
device = "/mnt/impermanence/crypt/clearedonboot";
underlying = {
path = "/nix/persist/crypt/clearedonboot";
# TODO: consider moving this to /tmp, but that requires tmp be mounted first?
type = "gocryptfs";
key = "/mnt/impermanence/crypt/clearedonboot.key";
};
} else {
device = "/nix/persist";
# device = "/mnt/impermenanence/persist/plain";
# underlying = {
# path = "/nix/persist";
# type = "bind";
# };
}
if encryptedClearOnBoot then
"/mnt/impermanence/crypt/clearedonboot"
else
"/nix/persist"
);
# turn a path into a name suitable for systemd
cleanName = utils.escapeSystemdPath;
# split the string path into a list of string components.
# root directory "/" becomes the empty list [].
# implicitly performs normalization so that:
@ -104,6 +89,7 @@ in
};
imports = [
./crypt.nix
./root-on-tmpfs.nix
];
@ -131,52 +117,6 @@ in
group = config.users.users.colin.group;
mode = config.users.users.colin.homeMode;
};
# TODO: convert this to a systemd unit file?
system.activationScripts.prepareEncryptedClearedOnBoot =
let
script = pkgs.writeShellApplication {
name = "prepareEncryptedClearedOnBoot";
runtimeInputs = with pkgs; [ gocryptfs ];
text = ''
backing="$1"
passfile="$2"
if ! test -e "$passfile"
then
tmpdir=$(dirname "$passfile")
mkdir -p "$backing" "$tmpdir"
# if the key doesn't exist, it's probably not mounted => delete the backing dir
rm -rf "''${backing:?}"/*
# generate key. we can "safely" keep it around for the lifetime of this boot
dd if=/dev/random bs=128 count=1 | base64 --wrap=0 > "$passfile"
# initialize the crypt store
gocryptfs -quiet -passfile "$passfile" -init "$backing"
fi
'';
};
store = getStore { encryptedClearOnBoot = true; };
in {
text = ''${script}/bin/prepareEncryptedClearedOnBoot ${store.underlying.path} ${store.underlying.key}'';
};
fileSystems = let
store = getStore { encryptedClearOnBoot = true; };
in {
"${store.device}" = {
device = store.underlying.path;
fsType = "fuse.gocryptfs";
options = [
"nodev"
"nosuid"
"allow_other"
"passfile=${store.underlying.key}"
"defaults"
];
noCheck = true;
};
};
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
}
(
@ -184,9 +124,8 @@ in
let
# systemd creates <path>.mount services for every fileSystems entry.
# <path> gets escaped as part of that: this code tries to guess that escaped name here.
# backing-mount = cleanName opt.store.device;
mount-service = cleanName opt.directory;
backing-path = concatPaths [ opt.store.device opt.directory ];
mount-service = utils.escapeSystemdPath opt.directory;
backing-path = concatPaths [ opt.store opt.directory ];
dir-service = config.sane.fs."${opt.directory}".service;
backing-service = config.sane.fs."${backing-path}".service;