Automatically unlocking the gnome-keyring using LUKS key with greetd and hyprland

Hello there

I used to use GDM as my display manager, but I have had some issues with it, which is why I decided to try to switch go greetd, however I cannot for the life of me, figure out how I can make it auto unlock the gnome-keyring using the LUKS password used for disk decryption when logging in automatically. I had it working using GDM by creating a gdm-autologin file I will include the working configuration for GDM at the end. What I have in my new config so far is the following

{ pkgs, lib, config, ... }:
let
  tuigreet = "${pkgs.greetd.tuigreet}/bin/tuigreet";
  session = "${pkgs.hyprland}/bin/Hyprland";
  username = "wooptidoo";
in
{
  options = {
    desktop.enable = lib.mkEnableOption "enables libraries needed for desktop";
  };
  config = lib.mkIf config.desktop.enable {

    environment.systemPackages = with pkgs; [
      greetd.tuigreet
      libsecret
    ];
    services.xserver.enable = true;

    # Screensharing and stuff + portal for gnome-keyring
    xdg.portal = {
      enable = true;
      wlr.enable = true;
      extraPortals = with pkgs; [ 
        xdg-desktop-portal-gtk 
        xdg-desktop-portal-hyprland
      ];
    };

    # Enable WM and DM
    programs.hyprland.enable = true;
    services.xserver.desktopManager.gnome.enable = false;
    services.xserver.excludePackages = [ pkgs.xterm ]; 
    services.gnome.core-utilities.enable = false;
    services.gnome.rygel.enable = false;

    services.gnome.gnome-keyring.enable = true;
    services.dbus.packages = [ pkgs.gnome.seahorse ];

    boot.initrd.systemd.enable = true;
    security.pam.services.greetd = {
      enableGnomeKeyring = true;
    };

    services.greetd = {
      enable = true;
      settings = {
        initial_session = {
          command = "${session}";
          user = "${username}";
        };
        default_session = {
          command = "${tuigreet} --greeting 'Authorization required...' --asterisks --remember --remember-user-session --time --cmd ${session}";
          user = "greeter";
        };
      };
    };
  };
}

Everything except the automatic unlocking of the gnome-keyring works perfectly. If I don’t set the initial_session, and log in manually after disk decryption, the keyring is unlocked when entering Hyprland as I would expect.

As stated earlier I had this working with GDM, and I want to only enter my password once on boot, and preferably when decrypting my disk. Here is the config I used for GDM for anyone interested:

{ pkgs, lib, config, ... }:
{
  options = {
    desktop.enable = lib.mkEnableOption "enables libraries needed for desktop";
  };
  config = lib.mkIf config.desktop.enable {

    environment.systemPackages = with pkgs; [
      libsecret
    ];

    services.xserver.enable = true;
    xdg.portal = {
      enable = true;
      wlr.enable = true;
      extraPortals = with pkgs; [ 
        xdg-desktop-portal-gtk 
        xdg-desktop-portal-hyprland
      ];
    };

    # Enable WM and DM
    programs.hyprland.enable = true;
    services.xserver.desktopManager.gnome.enable = false;
    services.xserver.excludePackages = [ pkgs.xterm ]; 
    services.gnome.core-utilities.enable = false;
    services.gnome.rygel.enable = false;

    services.xserver.displayManager.gdm.enable = true;
    services.displayManager.autoLogin.enable = true;
    services.displayManager.autoLogin.user = "wooptidoo";
    services.gnome.gnome-keyring.enable = true;
    services.dbus.packages = [ pkgs.gnome.seahorse ];

    # Use the decryption passphrase to also unlock the gnome-keyring
    boot.initrd.systemd.enable = true;
    security.pam.services = {
      gdm-autologin.text = ''
        auth      requisite     pam_nologin.so

        auth      required      pam_succeed_if.so uid >= 1000 quiet
        auth      optional      ${pkgs.gnome.gdm}/lib/security/pam_gdm.so
        auth      optional      ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so
        auth      required      pam_permit.so

        account   sufficient    pam_unix.so

        password  requisite     pam_unix.so nullok yescrypt

        session   optional      pam_keyinit.so revoke
        session   include       login
      '';
    };
  };
}

I hope someone has some experience that can help me forward.

Best regards
Andreas Voss

So these lines are the reason it works with GDM. When GDM does autologin, it uses the gdm-autologin PAM service. The pam_gdm module retrieves the LUKS passphrase from the kernel keyring and sets the PAM auth token to it (though, this is actually the same as what the pam_systemd_loadkey module would do; we just don’t use that anywhere right now I think), then the pam_gnome_keyring.so module launches the gnome-keyring-daemon and passes it the auth token to unlock the keyring.

So you’ll need greetd to be using a similar PAM service when doing autologin. I believe those same GDM pam modules will actually work in another PAM service, believe it or not. If greetd doesn’t also use a PAM service for autologin, then I think you’re out of luck and will have to do something custom.

1 Like

We have a PR to switch to pam_systemd_loadkey: nixos/gdm: enable pam_systemd_loadkey if available by jonathan-conder · Pull Request #286587 · NixOS/nixpkgs · GitHub

I tried to put something together based on that PR and some other information I found online, and ended up with this, however it still does not work, even though I think it should based on my (very limited) understanding. This article seems to match with what I am doing, and what is in the PR (which as far as I can tell is for GDM, which I am trying to adapt to greetd). Can you spot the issue with my approach?

systemd.services.greetd = {
  serviceConfig = {
    KeyringMode = lib.mkForce "inherit";
  };
};

services.gnome.gnome-keyring.enable = true;
services.dbus.packages = [ pkgs.gnome.seahorse ];
# Use the decryption passphrase to also unlock the gnome-keyring (which is currently broken with greetd)
boot.initrd.systemd.enable = true;

security.pam.services = let
  formatModuleArgument =
    token: if lib.hasInfix " " token then "[${lib.replaceStrings [ "]" ] [ "\\]" ] token}]" else token;

  keyname = formatModuleArgument "keyname=${config.desktop.autologin.keyname}";
  loadkeyArgs = lib.optionalString (config.desktop.autologin.keyname != null) " ${keyname}";
in
{
  greetd.text = ''
    # Account management.
    account required pam_unix.so # unix (order 10900)

    # Authentication management.
    auth optional pam_unix.so likeauth nullok # unix-early (order 11500)
    auth required ${config.systemd.package}/lib/security/pam_systemd_loadkey.so${loadkeyArgs}
    auth required ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so # gnome_keyring (order 12100)
    auth sufficient pam_unix.so likeauth nullok try_first_pass # unix (order 12800)
    auth required pam_deny.so # deny (order 13600)

    # Password management.
    password sufficient pam_unix.so nullok yescrypt # unix (order 10200)
    password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok # gnome_keyring (order 11200)

    # Session management.
    session required pam_env.so conffile=/etc/pam/environment readenv=0 # env (order 10100)
    session required pam_unix.so # unix (order 10200)
    session required pam_loginuid.so # loginuid (order 10300)
    session optional ${config.systemd.package}/lib/security/pam_systemd.so # systemd (order 12000)
    session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start # gnome_keyring (order 12600)
  '';
};