Skip to content

Commit b06f76a

Browse files
committed
post: publish proton pass secret service post
1 parent b0890ce commit b06f76a

File tree

2 files changed

+80
-44
lines changed

2 files changed

+80
-44
lines changed

content/posts/2026-03-24-proton-pass-secret-service.md

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,95 @@
11
+++
22
title = 'Proton Pass CLI: switching to the Secret Service keyring'
33
date = 2026-03-24T09:00:00Z
4-
draft = true
4+
draft = false
55
tags = ['nixos', 'linux', 'security', 'ssh']
66
+++
77

88
In my [previous post](/posts/2026-03-07-proton-pass-migration/) I documented the migration from 1Password to Proton Pass and landed on a workaround for the main annoyance: `pass-cli` stores its local encryption key in the Linux kernel keyring, which is wiped on every reboot, forcing a manual `pass-cli login` before SSH keys could be loaded each morning.
99

10-
Proton has now merged a change that makes this unnecessary.
10+
Fortunately, Proton has now merged a change that makes this unnecessary and allows for a setup much closer to that of 1Password.
1111

1212
## The change
1313

1414
`pass-cli` now supports a `PROTON_PASS_LINUX_KEYRING` environment variable that selects which keyring backend it uses to store the encryption key ([docs](https://protonpass.github.io/pass-cli/get-started/configuration/#1-keyring-storage-default)):
1515

16-
| Value | Backend | Persists across reboots |
17-
|---|---|---|
18-
| `kernel` (default) | Linux kernel user keyring | No |
19-
| `dbus` | D-Bus Secret Service | Yes |
16+
| Value | Backend | Persists across reboots |
17+
|--------------------|---------------------------|-------------------------|
18+
| `kernel` (default) | Linux kernel user keyring | No |
19+
| `dbus` | D-Bus Secret Service | Yes |
2020

2121
Setting it to `dbus` makes `pass-cli` use the D-Bus Secret Service instead of the kernel keyring. On a desktop machine with GNOME Keyring running and unlocked at login, the encryption key now survives reboots.
2222

23-
## The NixOS config change
24-
25-
My setup already had GNOME Keyring running and unlocked at login via PAM, so no additional plumbing was needed. The full change in my NixOS config was:
26-
27-
```nix
28-
# profiles/base/proton-pass/home.nix
29-
30-
home.sessionVariables = {
31-
PROTON_PASS_LINUX_KEYRING = "dbus";
32-
};
33-
34-
programs.zsh.shellAliases = {
35-
# login step is no longer needed after reboot
36-
pass-ssh-load = "pass-cli ssh-agent load";
37-
};
38-
```
23+
This opens up the option for automatically loading our SSH keys on startup once the keyring is unlocked.
3924

40-
`home.sessionVariables` is picked up by login shells and PAM sessions, so the variable is available to `pass-cli` wherever it runs.
41-
42-
The `pass-ssh-load` alias still exists, as loading SSH keys into the agent is still a manual step since `pass-cli ssh-agent load` is a one-shot command rather than a persistent process, but the `pass-cli login` prefix is gone.
25+
## The NixOS config change
4326

44-
## Auto-loading SSH keys on login
27+
My setup already had GNOME Keyring running and unlocked at login via PAM, so no additional plumbing was needed. If you're setting up from scratch check the [NixOS wiki](https://wiki.nixos.org/wiki/Secret_Service) for more information on setting this up.
4528

46-
With the authentication problem solved, I can now auto-load SSH keys into the agent at session start. `pass-cli ssh-agent load` is a one-shot command, so a systemd user service is the right fit. It runs after login, has access to the D-Bus session bus, and can be ordered after both the keyring and the SSH agent are ready.
29+
Here is the full `profiles/base/proton-pass/home.nix` after all the changes:
4730

4831
```nix
4932
# profiles/base/proton-pass/home.nix
50-
51-
systemd.user.services.proton-pass-ssh-load = {
52-
Unit = {
53-
Description = "Load Proton Pass SSH keys into agent";
54-
After = [
55-
"graphical-session.target"
56-
"gnome-keyring-daemon.service"
57-
"ssh-agent.service"
58-
];
59-
};
60-
Service = {
61-
Type = "oneshot";
62-
ExecStart = "${pkgs-unstable.proton-pass-cli}/bin/pass-cli ssh-agent load";
33+
{
34+
config,
35+
pkgs,
36+
pkgs-unstable,
37+
...
38+
}:
39+
40+
let
41+
# Version 1.8.0 introduces PROTON_PASS_LINUX_KEYRING support.
42+
# Remove this override once 1.8.0 reaches nixpkgs-unstable.
43+
proton-pass-cli = pkgs-unstable.proton-pass-cli.overrideAttrs (old: {
44+
version = "1.8.0";
45+
src = pkgs-unstable.fetchurl {
46+
url = "https://proton.me/download/pass-cli/1.8.0/pass-cli-linux-x86_64";
47+
hash = "sha256-M7zWxVYHHjM86/l3K+0AR8QceiydP0n0sXj9rSctaeI=";
48+
};
49+
});
50+
in
51+
52+
{
53+
home.packages = [ proton-pass-cli ];
54+
55+
# Use the D-Bus Secret Service (GNOME Keyring) as the keyring backend so
56+
# that the pass-cli encryption key persists across reboots.
57+
home.sessionVariables = {
58+
PROTON_PASS_LINUX_KEYRING = "dbus";
6359
};
64-
Install = {
65-
WantedBy = [ "graphical-session.target" ];
60+
61+
# Auto-load SSH keys into the agent at login.
62+
systemd.user.services.proton-pass-ssh-load = {
63+
Unit = {
64+
Description = "Load Proton Pass SSH keys into agent";
65+
After = [
66+
"graphical-session.target"
67+
"gnome-keyring-daemon.service"
68+
"ssh-agent.service"
69+
];
70+
};
71+
Service = {
72+
Type = "oneshot";
73+
Environment = [
74+
"PROTON_PASS_LINUX_KEYRING=dbus"
75+
"SSH_AUTH_SOCK=%t/ssh-agent"
76+
];
77+
ExecStart = "${proton-pass-cli}/bin/pass-cli ssh-agent load";
78+
};
79+
Install = {
80+
WantedBy = [ "graphical-session.target" ];
81+
};
6682
};
67-
};
83+
}
6884
```
6985

86+
The `overrideAttrs` block pins `pass-cli` to 1.8.0, the first release with `PROTON_PASS_LINUX_KEYRING` support. It can be removed once 1.8.0 reaches `nixpkgs-unstable` and `pkgs-unstable.proton-pass-cli` can be used directly.
87+
88+
The systemd service sets `PROTON_PASS_LINUX_KEYRING` and `SSH_AUTH_SOCK` directly in the `Service` block because user units don't inherit the session environment. `%t` is the systemd specifier for the user runtime directory (`/run/user/<uid>`), so `%t/ssh-agent` resolves to the same socket that `programs.ssh.startAgent` creates without hardcoding a UID.
89+
7090
The `After` ordering ensures the service waits for GNOME Keyring to be unlocked (so `pass-cli` can retrieve its encryption key via D-Bus) and for the SSH agent to be running (so there is a socket to load keys into). The store path is used directly in `ExecStart` rather than relying on `$PATH`, since systemd services don't inherit the user's shell environment.
7191

72-
Keys injected by `pass-cli ssh-agent load` live only in the agent's memory for the session. The keyring has no record of them, so the service needs to run on every login, which is exactly what `WantedBy = graphical-session.target` gives you.
92+
Keys injected by `pass-cli ssh-agent load` live only in the agent's memory for the session. The keyring has no record of them, so the service needs to run on every login, which is exactly what `WantedBy = graphical-session.target` provides.
7393

7494
## One caveat
7595

themes/tales/static/css/style.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,22 @@ hr {
147147
border-top: 1px solid var(--hrcolor);
148148
}
149149

150+
/* Tables */
151+
table {
152+
border-collapse: collapse;
153+
width: 100%;
154+
margin: 1em 0;
155+
}
156+
157+
th, td {
158+
padding: 6px 12px;
159+
text-align: left;
160+
}
161+
162+
th {
163+
background: var(--prebgcolor);
164+
}
165+
150166
/* Images */
151167
.center {
152168
display: block;

0 commit comments

Comments
 (0)