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

1 Like

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)
  '';
};
1 Like