Unlock GnuPG keys on login
A PAM module that hands over your login password to gpg-agent
, which can be
useful if you are using a GnuPG-based password manager like
pass.
Requires GnuPG 2.1, and probably only works on Linux.
The code was written mainly by looking at and occasionally copying from Gnome Keyring's PAM module and pam_mount and is based on a somewhat mediocre understanding of the details of both PAM and C. You should be aware that there may be potentially dangerous bugs lurking.
- Arch: pam-gnupg from the AUR
- Void: pam-gnupg
- NixOS: security.pam.services.<name>.gnupg (currently only in the unstable channel)
The usual
./autogen.sh
./configure
make
make install
should work. configure
takes an option --with-moduledir
to set the
installation path of the PAM module. It defaults to /lib/security
, but your
distribution might use a different path.
-
For services that open a new session (gdm, sddm, login, ...), add the lines
auth optional pam_gnupg.so store-only session optional pam_gnupg.so
at the end (¹) of the corresponding file in
/etc/pam.d
, or in one of the files included from there, e.g.system-local-login
on Arch.When opening the session, gpg-agent will be autostarted if necessary. If you want to start it by other means, e.g. as a systemd user service, make sure it is up before
pam_gnupg.so
is run, e.g. by putting the lines somewhere belowpam_systemd.so
, and (optionally) addno-autostart
to thesession
line.(¹): The end is usually a good place, but details depend on your PAM setup. In particular, modules declared
sufficient
can terminate the PAM stack early. At least,pam_gnupg.so
should come afterpam_unix.so
,pam_systemd_home.so
,pam_systemd.so
andpam_env.so
in case you use those modules. -
For services that only authenticate (i3lock, physlock, ...), use
auth optional pam_gnupg.so
For screen lockers, this only really makes sense if you arrange for the password cache to be cleared prior to locking the screen by calling
gpg-connect-agent --no-autostart reloadagent /bye
During authentication, the agent will never be autostarted.
-
Add
allow-preset-passphrase
to
~/.gnupg/gpg-agent.conf
. Optionally, customize the cache timeout viamax-cache-ttl
, e.g. setmax-cache-ttl 86400
to have it expire after a day.
-
Run
gpg -K --with-keygrip
The output should look something like this:
sec rsa2048 2018-11-16 [SC] 9AB5DD43C5E5FD40475FA6DA0D776275F7F5B2E7 Keygrip = 6F4ABB77A88E922406BCE6627AFEEE2363914B76 uid [ultimate] Chris Ruegge <mail@cxcs.de> ssb rsa2048 2018-11-16 [E] Keygrip = FBDEAD7B0C484CDC85F1CF70352833EB0C921D58
Write the keygrip for the encryption subkey marked
[E]
– shown in boldface in the output above – into~/.pam-gnupg
. If you want to unlock multiple keys or subkeys, add all keygrips on separate lines.Keygrips are exactly 40 characters in length. Leading whitespace, lines starting with
#
and everything after the keygrip is ignored.If
~/.pam-gnupg
does not exists,$XDG_CONFIG_HOME/pam-gnupg
will be tried, withXDG_CONFIG_HOME
defaulting to~/.config
as usual. If you want to customize this variable, read the section on environment variables below. -
Set the same password for your gpg key and your user account. All pam-gnupg does is to send the password as entered to gpg-agent. It is therefore not compatible with auto-login of any kind; you actually have to type your password for things to work.
If you change your gnupg directory from the default ~/.gnupg
by setting
GNUPGHOME
, this variable needs to be made available to pam-gnupg when
presetting. Since PAM usually runs before your init scripts, it needs to obtain
the variable in a different way.
To set it, add the path to the config file on a separate line before any
keygrips, either as absolute path or starting with ~/
for paths relative to
the home directory. The connection to the agent will be opened when the first
keygrip is read, so setting GNUPGHOME
after that will have no effect.
Note that the variable is only used for connecting to and optionally autostarting the agent. It is not passed down to your login shell or desktop session, so you also need to set it in your init scripts. Additionally, if you start the agent via systemd, you need to adjust the various service and socket units separately.
If you use systemd-homed
, you can modify env vars via homectl --setenv
, and
they will be made available to PAM by pam_systemd_home.so
.
Another way is to run pam_env.so
with user_readenv=1
before pam_gnupg.so
,
so you can set env vars from ~/.pam_environment
, e.g.
GNUPGHOME DEFAULT=@{HOME}/path/to/your/gnupg
You can also modify XDG_CONFIG_HOME
this way. Unfortunately, user_readenv
is
deprecated and will go away in some future version of pam_env
.
SSH key support is indirect via gpg-agent's built-in SSH support (there's no
SSH specific code in pam-gnupg). The full details are in gpg-agent(1)
, but
here's a basic step-by-step guide:
-
Add
enable-ssh-support
to
~/.gnupg/gpg-agent.conf
. (This is not actually strictly necessary in all setups, but doesn't hurt either.) -
Set the
SSH_AUTH_SOCK
variable to gpg-agent's SSH socket by puttingexport SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
into your relevant init script.
-
Add your SSH keys to the agent using
ssh-add
as usual. This only needs to be done once. The agent will re-encrypt the keys and store them in~/.gnupg
, independent of the ones in~/.ssh
. -
Get the SSH keygrips using
gpg-connect-agent 'keyinfo --ssh-list' /bye
The output should look like
S KEYINFO DBB0B60CFE5F23716ABEE8787C6184C27E2486E1 D - - - P - - S OK
with one keygrip per line. Alternatively, get them from
~/.gnupg/sshcontrol
. -
Add the keygrips to
~/.pam-gnupg
the same way as for the gpg keys.
Both the auth
and the session
module take a debug
option to enable some
basic debug logging to syslog / journal.
-
Using
pass
during startup of systemd user services has a racing condition even if the service declaresAfter=gpg-agent.socket
, because systemd does not know about pam-gnupg and will start the service right after the socket is up, but maybe before the key has been unlocked. Until I figure out a cleaner solution, you can circumvent this by adding a small startup delay to the service, e.g.ExecStartPre=/usr/bin/sleep 5
-
Screen lockers need to call
pam_setcred
after authentication to actually send the passphrase. Those who don't will not work with pam-gnupg. -
Specifically for suckless' slock with the pam-auth patch, you have to set
user
andgroup
to your user name and your primary group (as displayed byid -gn
) in slock'sconfig.h
, which will therefore not work for multiple users. Alternatively, you can try the (untested) steps outlined in this issue comment.
- Email: mail@cxcs.de, gpg key. The
gpg -K
output above is real, so the second line is the actual fingerprint.