unl0kr: split the gocryptfs unlocking into its own separate service
/mnt/persist/private can be depended on by both s6 user services and systemd system services (which will become useful for servo) /mnt/persist/private can be unlocked by dropping the key in remotely, however that won't kill unl0kr TODO: fix unl0kr to not also output text to the tty TODO: ensure gocryptfs mount can handle being fed a wrong password
This commit is contained in:
@@ -265,6 +265,7 @@ in
|
||||
# N.B.: gtk apps support absolute paths for this; webkit apps (e.g. geary) support only relative paths (relative to $XDG_RUNTIME_DIR)
|
||||
env.WAYLAND_DISPLAY = "wl/wayland-1";
|
||||
|
||||
services.private-storage.dependencyOf = [ "sway" ]; #< HACK: prevent unl0kr and sway from fighting over the tty
|
||||
services.sway = {
|
||||
description = "sway: tiling wayland desktop environment";
|
||||
partOf = [
|
||||
|
@@ -65,18 +65,18 @@ in
|
||||
services.unl0kr = {
|
||||
description = "unl0kr framebuffer password entry/filesystem unlocker";
|
||||
partOf = [ "private-storage" ];
|
||||
# TODO: lift the gocryptfs stuff into a separate service
|
||||
command = pkgs.writeShellScript "unl0kr-start" ''
|
||||
# if ! test -d $XDG_RUNTIME_DIR/unl0kr; then
|
||||
# mkdir $XDG_RUNTIME_DIR/unl0kr
|
||||
# fi
|
||||
# unl0kr > $XDG_RUNTIME_DIR/unl0kr/passwd
|
||||
if ! test -f /mnt/persist/private/init; then
|
||||
unl0kr | sudo gocryptfs -fg /nix/persist/private /mnt/persist/private -o allow_other
|
||||
fi
|
||||
#^ otherwise, if already unlocked, exit true
|
||||
while ! test -f /mnt/persist/private/init; do
|
||||
unl0kr > /run/gocryptfs/private.key.incoming
|
||||
cp /run/gocryptfs/private.key.incoming /run/gocryptfs/private.key
|
||||
#v give time for the fs to be mounted, before assuming user entered the wrong key & we should restart
|
||||
# TODO: use inotify to wait for `init`
|
||||
sleep 3
|
||||
done
|
||||
while true; do
|
||||
sleep infinity
|
||||
done
|
||||
'';
|
||||
readiness.waitExists = [ "/mnt/persist/private/init" ];
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -38,17 +38,56 @@ lib.mkIf config.sane.persist.enable
|
||||
"allow_other" # root ends up being the user that mounts this, so need to make it visible to other users.
|
||||
# "quiet"
|
||||
# "defaults" # "unknown flag: --defaults. Try 'gocryptfs -help'"
|
||||
"passfile=/run/gocryptfs/private.key"
|
||||
];
|
||||
noCheck = true;
|
||||
};
|
||||
|
||||
# let sane.fs know about the mount
|
||||
sane.fs."${origin}".mount = {};
|
||||
# let sane.fs know about the mount, and let systemd know to wait for the pass key before attempting a mount
|
||||
sane.fs."${origin}".mount = {
|
||||
depends = let
|
||||
keyfile = config.sane.fs."/run/gocryptfs/private.key";
|
||||
in [ keyfile.unit ];
|
||||
};
|
||||
# it also needs to know that the underlying device is an ordinary folder
|
||||
sane.fs."${backing}" = sane-lib.fs.wanted {
|
||||
dir.acl.user = config.sane.defaultUser;
|
||||
# dir.acl.mode = "755";
|
||||
};
|
||||
|
||||
sane.fs."/run/gocryptfs/private.key".generated = let
|
||||
script = pkgs.writeShellScript "wait-for-gocryptfs-private" ''
|
||||
file="$1"
|
||||
dir=$(dirname "$file")
|
||||
|
||||
succeedOrWait() {
|
||||
if [ -f "$file" ]; then
|
||||
exit 0
|
||||
else
|
||||
# wait for some file to be created inside the directory.
|
||||
# inotifywait returns 0 if the file was created. 1 or 2 if timeout was hit or it was interrupted by a different event.
|
||||
${lib.getExe' pkgs.inotify-tools "inotifywait"} --timeout "$1" --event create "$dir" || true
|
||||
fi
|
||||
}
|
||||
# there's a race condition between testing the path and starting `inotifywait`.
|
||||
# therefore, use a retry loop. exponential backoff to decrease the impact of the race condition,
|
||||
# especially near the start of boot to allow for quick reboots even if/when i hit the race.
|
||||
for i in 4 4 8 8 16 16 16 16 16 16 16 16; do
|
||||
succeedOrWait "$i"
|
||||
done
|
||||
while true; do
|
||||
succeedOrWait 30
|
||||
done
|
||||
'';
|
||||
in {
|
||||
command = [ "${script}" "/run/gocryptfs/private.key" ];
|
||||
# no need for anyone else to be able to read the key
|
||||
acl.mode = "0400";
|
||||
};
|
||||
sane.fs."/run/gocryptfs" = sane-lib.fs.wanted {
|
||||
dir.acl.user = config.sane.defaultUser;
|
||||
dir.acl.mode = "0700";
|
||||
};
|
||||
|
||||
# in order for non-systemd `mount` to work, the mount point has to already be created, so make that a default target
|
||||
systemd.units = let
|
||||
originUnit = config.sane.fs."${origin}".generated.unit;
|
||||
@@ -58,5 +97,11 @@ lib.mkIf config.sane.persist.enable
|
||||
|
||||
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
||||
system.fsPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||
|
||||
sane.user.services.gocryptfs-private = {
|
||||
description = "wait for /mnt/persist/private to be mounted";
|
||||
startCommand = "${lib.getExe' pkgs.systemd "systemctl"} start mnt-persist-private.mount";
|
||||
partOf = [ "private-storage" ];
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -178,8 +178,7 @@ let
|
||||
config = lib.mkMerge [
|
||||
# if we're the default user, inherit whatever settings were routed to the default user
|
||||
(lib.mkIf config.default {
|
||||
inherit (sane-user-cfg) fs persist environment;
|
||||
services = lib.mapAttrs (_: lib.mkMerge) sane-user-cfg.services;
|
||||
inherit (sane-user-cfg) fs environment persist services;
|
||||
})
|
||||
{
|
||||
fs."/".dir.acl = {
|
||||
@@ -295,6 +294,10 @@ let
|
||||
# grouping it like this is mostly a power-saving thing to make certain services not auto-launched
|
||||
description = "service (bundle) which provides high-precision location info (e.g. from GPS)";
|
||||
};
|
||||
services."private-storage" = {
|
||||
description = "service (bundle) which is active once the persist.private datastore has been opened";
|
||||
dependencyOf = [ "graphical-session" ]; #< prevent any graphical environment from competing with the login/unlocker service over the framebuffer
|
||||
};
|
||||
services."sound" = {
|
||||
description = "service (bundle) which represents functional sound input/output when active";
|
||||
# partOf = [ "default" ];
|
||||
|
Reference in New Issue
Block a user