fix s6 service ordering: unl0kr -> (wait for mount) -> sway

note that the systemd-aware mount never completes -- it's stuck in 'activating' forever. that's the next challenge
This commit is contained in:
2024-07-26 12:18:14 +00:00
parent b93e9e75e6
commit fcbbfc4a65
2 changed files with 67 additions and 41 deletions

View File

@@ -32,8 +32,6 @@ in
wheelNeedsPassword = false;
};
security.pam.mount.enable = true;
system.activationScripts.makeEtcShadowSandboxable = {
deps = [ "users" ];
text = ''

View File

@@ -5,6 +5,61 @@ let
persist-base = "/nix/persist";
origin = config.sane.persist.stores."private".origin;
backing = sane-lib.path.concat [ persist-base "private" ];
gocryptfs-private = pkgs.writeShellApplication {
name = "mount.fuse.gocryptfs-private";
runtimeInputs = with pkgs; [
coreutils-full
gocryptfs
inotify-tools
];
text = ''
# backing=$1
# facing=$2
mountArgs=("$@")
passdir=/run/gocryptfs
passfile="$passdir/private.key"
waitForPassfileOnce() {
local timeout=$1
if [ -f "$passfile" ]; then
return 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.
inotifywait --timeout "$timeout" --event create "$passdir"
return 1 #< maybe it was created; we'll pick that up immediately, on next check
fi
}
waitForPassfile() {
# 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 timeout in 4 4 8 8 8 8 16 16 16 16 16 16 16 16; do
if waitForPassfileOnce "$timeout"; then
return 0
fi
done
while true; do
if waitForPassfileOnce 30; then
return 0
fi
done
}
tryOpenStore() {
# try to open the store (blocking), if it fails, then delete the passfile because the user probably entered the wrong password
echo "mounting with ''${mountArgs[*]}"
gocryptfs -fg "''${mountArgs[@]}"
rc=$?
# should only return if the mount errored -- probably because of a password failure!
rm -f "$passfile"
return "$rc"
}
waitForPassfile
tryOpenStore
'';
};
in
lib.mkIf config.sane.persist.enable
{
@@ -28,7 +83,7 @@ lib.mkIf config.sane.persist.enable
fileSystems."${origin}" = {
device = backing;
fsType = "fuse.gocryptfs";
fsType = "fuse.gocryptfs-private";
options = [
"noauto" # don't try to mount, until the user logs in!
"nofail"
@@ -39,50 +94,22 @@ lib.mkIf config.sane.persist.enable
# "quiet"
# "defaults" # "unknown flag: --defaults. Try 'gocryptfs -help'"
"passfile=/run/gocryptfs/private.key"
# options so that we can block for the password file *without* systemd killing us.
# see: <https://bbs.archlinux.org/viewtopic.php?pid=1906174#p1906174>
"x-systemd.mount-timeout=infinity"
# "retry=10000"
"fg"
];
noCheck = true;
};
# 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 ];
};
# let sane.fs know about the mount
sane.fs."${origin}".mount = {};
# 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;
};
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";
@@ -95,12 +122,13 @@ lib.mkIf config.sane.persist.enable
"${originUnit}".wantedBy = [ "local-fs.target" ];
};
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
system.fsPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
system.fsPackages = [ gocryptfs-private ];
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";
# startCommand = "${lib.getExe' pkgs.systemd "systemctl"} start mnt-persist-private.mount";
command = "sleep infinity";
readiness.waitExists = [ "/mnt/persist/private/init" ];
partOf = [ "private-storage" ];
};
}