87 lines
3.5 KiB
Nix
87 lines
3.5 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
let
|
|
cfg = config.sane.programs.unl0kr;
|
|
in
|
|
{
|
|
sane.programs.unl0kr = {
|
|
configOption = with lib; mkOption {
|
|
default = {};
|
|
type = types.submodule {
|
|
options.autostart = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
whether to launch unl0kr at boot.
|
|
unl0kr takes the role of a greeter, presenting a virtual keyboard to the framebuffer
|
|
and allowing password auth via either keyboard, mouse, or touch.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
# pkgs.unl0kr works fine, but the newer version packaged as part of buffybox is way more performant
|
|
packageUnwrapped = pkgs.buffybox;
|
|
|
|
# N.B.: this sandboxing applies to `unl0kr` itself -- the on-screen-keyboard;
|
|
# NOT to the wrapper which invokes `login`.
|
|
sandbox.whitelistDri = true;
|
|
sandbox.extraPaths = [
|
|
"/dev/fb0"
|
|
"/dev/input"
|
|
"/run/udev"
|
|
"/sys/class/input"
|
|
"/sys/devices"
|
|
#v without /dev/tty0, it fails to fully take over the tty (even though it's ostensibly running on ${cfg.config.vt})
|
|
# and your password is dumped to the framebuffer.
|
|
# it still *works*, but wow, kinda weird and concerning
|
|
"/dev/tty0"
|
|
];
|
|
|
|
services.unl0kr = {
|
|
description = "unl0kr framebuffer password entry/filesystem unlocker";
|
|
partOf = lib.mkIf cfg.config.autostart [ "private-storage" ];
|
|
command = pkgs.writeShellScript "unl0kr-start" ''
|
|
while ! test -f /mnt/persist/private/init; do
|
|
if test -f /run/gocryptfs/private.key; then
|
|
# wait for the mount to appear or for the key to disappear (because it was incorrect).
|
|
# i'm not sure this is actually correct, or just behaves as `sleep 4` in practice
|
|
${lib.getExe' pkgs.inotify-tools "inotifywait"} --timeout 4 --event create --event delete /mnt/persist/private /run/gocryptfs
|
|
else
|
|
echo "starting unl0kr"
|
|
if [ -n "$XDG_VTNR" ]; then
|
|
# switch back to the tty our session is running on (in case the user tabbed away after logging in),
|
|
# as only that TTY is sure to have echo disabled.
|
|
# this is racy, but when we race it's obvious from the UI that your password is being echo'd
|
|
${lib.getExe' pkgs.kbd "chvt"} "$XDG_VTNR"
|
|
fi
|
|
# don't start unl0kr if there's no framebuffer yet, else it'll just not render anything, indefinitely.
|
|
test -e /dev/fb0 && \
|
|
unl0kr > /run/gocryptfs/private.key.incoming &&
|
|
cp /run/gocryptfs/private.key.incoming /run/gocryptfs/private.key
|
|
echo "unl0kr exited"
|
|
fi
|
|
sleep 1
|
|
done
|
|
while true; do
|
|
sleep infinity
|
|
done
|
|
'';
|
|
};
|
|
};
|
|
|
|
security.loginDefs.settings = lib.mkIf cfg.enabled {
|
|
# see: `man login.defs`
|
|
# disable timeout for `login` program.
|
|
# LOGIN_TIMEOUT=0 lets me pipe input into `login` and not worry about the pipe randomly dying.
|
|
LOGIN_TIMEOUT = 0;
|
|
# LOGIN_RETRIES=1 ensures that if the password is wrong, then login exits and the whole service restarts so unl0kr re-appears.
|
|
# docs mention `UNIX_MAX_RETRIES` setting within pam_unix (hardcoded to 3): seems that's an upper-limit to this value, but no lower limit.
|
|
LOGIN_RETRIES = 1;
|
|
FAIL_DELAY = 1; #< delay this long after failed loging before allowing retry
|
|
};
|
|
|
|
environment.etc."unl0kr.conf" = lib.mkIf cfg.enabled {
|
|
source = ./unl0kr.conf;
|
|
};
|
|
}
|