nixos/settings/auth.nix

110 lines
4.9 KiB
Nix

# Authenticate before creating the desktop; keep its lock separate from the bar.
{ config, lib, pkgs, ... }:
let
enabled = lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ];
theme = config.home-manager.users.fred.xdg.configFile."quickshell/Theme.qml".text;
bundle = name: shell: pkgs.linkFarm name [
{ name = "shell.qml"; path = shell; }
{ name = "AuthView.qml"; path = ./quickshell/AuthView.qml; }
{ name = "Theme.qml"; path = pkgs.writeText "Theme.qml" theme; }
{ name = "qmldir"; path = pkgs.writeText "qmldir" "singleton Theme 1.0 Theme.qml\nAuthView 1.0 AuthView.qml\n"; }
];
greeter = bundle "fred-greeter" ./quickshell/Greeter.qml;
# Reuse the desktop's explicit mode instead of keeping a second 180 Hz
# setting here. The MacBook uses automatic/preferred output configuration.
gamingMonitor = builtins.head config.home-manager.users.fred.wayland.windowManager.hyprland.settings.monitor;
greeterClient = pkgs.writeShellScript "fred-greeter-client" ''
${lib.optionalString (config.networking.hostName == "FredOS-Gaming") ''
# Run inside Cage, where its Wayland output-management socket exists.
# A missing connector or unsupported mode must not prevent login.
if ! ${pkgs.coreutils}/bin/timeout -k 1s 5s ${pkgs.wlr-randr}/bin/wlr-randr \
--output ${lib.escapeShellArg gamingMonitor.output} \
--mode ${lib.escapeShellArg gamingMonitor.mode} \
--scale ${lib.escapeShellArg (toString gamingMonitor.scale)}; then
echo "Greeter: desktop display mode unavailable; keeping Cage's current mode" >&2
fi
''}
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
exec ${pkgs.quickshell}/bin/qs -p ${greeter}
'';
locker = bundle "fred-lock" ./quickshell/SessionLock.qml;
session = pkgs.writeShellScript "hyprland-session" ''
exec ${config.programs.hyprland.package}/bin/start-hyprland >/dev/null 2>&1
'';
lockCommand = pkgs.writeShellApplication {
name = "fred-lock";
runtimeInputs = [ pkgs.systemd pkgs.coreutils pkgs.quickshell ];
text = builtins.readFile ../scripts/lock-session.sh;
};
in {
config = lib.mkIf enabled {
system.build.auth = pkgs.linkFarm "fred-auth" [
{ name = "greeter"; path = greeter; }
{ name = "lock"; path = locker; }
{ name = "bin"; path = "${lockCommand}/bin"; }
{ name = "greeter-client"; path = greeterClient; }
];
services.greetd = {
enable = true;
greeterManagesPlymouth = true;
settings.default_session = {
user = "greeter";
command = toString (pkgs.writeShellScript "fred-greeter" ''
export FRED_SESSION_COMMAND=${session}
export FRED_GREETER=1
export QML2_IMPORT_PATH=${pkgs.qt6.qt5compat}/lib/qt-6/qml
export XKB_DEFAULT_LAYOUT=gb,no
export XKB_DEFAULT_OPTIONS=${lib.escapeShellArg ("grp:win_space_toggle" + lib.optionalString (config.networking.hostName == "FredOS-Gaming") ",altwin:swap_lalt_lwin")}
exec ${pkgs.dbus}/bin/dbus-run-session -- ${pkgs.cage}/bin/cage -s -- ${greeterClient}
'');
};
};
security.pam.services.greetd.allowNullPassword = lib.mkForce false;
security.pam.services.fred-lock = { allowNullPassword = false; };
environment.systemPackages = [ lockCommand ];
home-manager.users.fred.systemd.user.services.quickshell-lock = {
Unit = {
Description = "Independent Quickshell session lock";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
StartLimitIntervalSec = 0;
};
Service = {
ExecStart = "${pkgs.quickshell}/bin/qs -p ${locker}";
Environment = [ "QML2_IMPORT_PATH=${pkgs.qt6.qt5compat}/lib/qt-6/qml" ];
Restart = "always";
RestartSec = 1;
TimeoutStartSec = 5;
};
# No WantedBy: greetd already authenticated the new desktop session.
};
systemd.services.lock-before-sleep = {
description = "Require a confirmed session lock before sleep";
before = [ "sleep.target" ];
requiredBy = [ "sleep.target" ];
serviceConfig = {
Type = "oneshot";
User = "fred";
TimeoutStartSec = "20s";
};
script = ''
set -euo pipefail
export HOME=/home/fred
export XDG_RUNTIME_DIR=/run/user/$(${pkgs.coreutils}/bin/id -u)
# A greeter has no desktop to lock. Query logind, not a potentially
# stale user target, and fail closed if querying logind fails.
sessions=$(${pkgs.systemd}/bin/loginctl list-sessions --no-legend --no-pager)
fred_uid=$(${pkgs.coreutils}/bin/id -u)
while read -r session uid _; do
[ "$uid" = "$fred_uid" ] || continue
type=$(${pkgs.systemd}/bin/loginctl show-session "$session" -p Type --value)
if [ "$type" = wayland ] || [ "$type" = x11 ]; then
exec ${lockCommand}/bin/fred-lock
fi
done <<< "$sessions"
'';
};
};
}