Document the new level of support for SSH agents (also in `--version` output)
Marco Ricci

Marco Ricci commited on 2025-08-02 14:27:29
Zeige 3 geänderte Dateien mit 91 Einfügungen und 11 Löschungen.


Document the SSH agent socket providers in the API reference
documentation.  Additionally, update the user-facing, checklist-style
reference documentation on the state of SSH agent support for The
Annoying Operating System: still unsupported, but basic infrastructure
is being built.  Also go into more detail on GnuPG's `gpg-agent` -- how
it still is unsupported on The Annoying Operating System, how to connect
to it on other systems, and how to prepare OpenPGP keys of the right
type for use with SSH.  Finally -- though the information will likely
still relevant for quite some time -- date the SSH key type
recommendations explicitly, and update the explanation of the
`--version` output of `derivepassphrase vault` to mention that SSH agent
construction might still fail at runtime, even if support is indicated.
... ...
@@ -1,3 +1,7 @@
1 1
 ::: derivepassphrase.ssh_agent
2 2
     options:
3 3
       heading_level: 1
4
+
5
+::: derivepassphrase.ssh_agent.socketprovider
6
+    options:
7
+      heading_level: 2
... ...
@@ -55,11 +55,45 @@ canonical SSH agent implementation.
55 55
           default, contrary to other SSH agents, so it must be manually
56 56
           advertised:
57 57
 
58
+            === "UNIX"
59
+
58 60
                 ~~~~ console
59 61
                 $ SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
60 62
                 $ export SSH_AUTH_SOCK
61 63
                 ~~~~
62 64
 
65
+            === "Windows (`libassuan` socket)"
66
+
67
+                `gpg-agent` on Windows contains a native emulation of
68
+                UNIX domain sockets by the `assuan` library, which GnuPG
69
+                internally uses for network connectivity and
70
+                inter-process communication.  No specific configuration
71
+                is necessary, and the agent address can be directly
72
+                obtained with `gpgconf`:
73
+
74
+                ~~~~ console
75
+                $ gpgconf --list-dirs agent-ssh-socket
76
+                ~~~~
77
+
78
+                This mode, sadly, is not currently supported.  The
79
+                system is specific to GnuPG/`libassuan`, and unlikely to
80
+                be deployed widely enough to make implementing this
81
+                a priority for us… at least, relative to the other, more
82
+                common interprocess communication channels for SSH
83
+                agents on Windows.
84
+
85
+            === "Windows (OpenSSH emulation)"
86
+
87
+                From v2.4 onwards, `gpg-agent` supports masquerading as
88
+                OpenSSH's `ssh-agent` on Windows, by starting the agent
89
+                with the `--enable-win32-openssh-support` command-line
90
+                argument.  (Usually, this would be added to the
91
+                `gpg-agent` configuration file instead of being manually
92
+                supplied on the command-line.)
93
+
94
+                [This mode, sadly, is not currently
95
+                supported.](#python-support)
96
+
63 97
 </section>
64 98
 
65 99
 ### A Python installation that can talk to the SSH agent { #python-support }
... ...
@@ -74,10 +108,12 @@ canonical SSH agent implementation.
74 108
     does not inherently support named pipes.  Since no comprehensive
75 109
     third-party Python modules to interface with named pipes appear to
76 110
     exist, teaching `derivepassphrase` to use Windows named pipes
77
-    will require us developers to write a custom low-level C module
78
-    specific to this application---an unrealistic task if we lack both
79
-    technical know-how for the named pipe API as well as Windows
80
-    hardware to test any potential implementation on.
111
+    requires us developers to write the code to interface the Windows
112
+    system libraries ourselves.  Development on this started after the
113
+    release of version 0.5, but since this is not our area of expertise,
114
+    and because the pre-0.5 design hard-codes the UNIX style of looking
115
+    up and connecting to an SSH agent, development progress on this
116
+    front has been much slower than usual.
81 117
 
82 118
 On non-Windows operating systems, the SSH agent is expected to advertise
83 119
 its communication socket via the `SSH_AUTH_SOCK` environment variable,
... ...
@@ -85,7 +121,13 @@ which is common procedure.  Therefore, [your Python installation must
85 121
 support UNIX domain sockets][socket.AF_UNIX].
86 122
 
87 123
 `derivepassphrase vault --version` will report on supported and on
88
-unavailable features, including "master SSH key":
124
+unavailable features, including "master SSH key":[^1]
125
+
126
+[^1]: This indicates support in principle, on this
127
+    hardware/software/system combination, for interfacing with an SSH
128
+    agent.  At runtime, this could still fail, e.g. because the SSH
129
+    agent isn't actually running, because `derivepassphrase` is trying
130
+    to connect to the wrong address, etc.
89 131
 
90 132
 === "supported"
91 133
 
... ...
@@ -114,8 +156,8 @@ unavailable features, including "master SSH key":
114 156
 For an SSH key to be usable by `derivepassphrase`, the SSH agent must
115 157
 always generate the same signature for the same input, i.e. the
116 158
 signature must be deterministic for this key type.  Commonly used SSH
117
-key types include [RSA][], [DSA][], [ECDSA][], [Ed25519][] and
118
-[Ed448][].
159
+key types (as of August 2025) include [ECDSA][], [Ed25519][], [RSA][],
160
+and, somewhat less commonly, [Ed448][] and [DSA][].
119 161
 
120 162
   [RSA]: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
121 163
   [DSA]: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
... ...
@@ -168,7 +210,7 @@ key types include [RSA][], [DSA][], [ECDSA][], [Ed25519][] and
168 210
 If you do not yet have a (supported) SSH key, we recommend Ed25519 for
169 211
 maximum speed and reasonable availability, otherwise RSA for maximum
170 212
 availability.  We do not in general recommend Ed448 because it is not
171
-widely implemented.
213
+widely implemented (as of August 2025).
172 214
 
173 215
 ??? example "Generating new SSH keys for `derivepassphrase`"
174 216
 
... ...
@@ -220,11 +262,42 @@ widely implemented.
220 262
 
221 263
     === "GnuPG"
222 264
 
223
-        Not supported natively.  An alternative SSH client distribution
224
-        such as OpenSSH or PuTTY is necessary.
265
+        Not supported natively.  A different SSH client distribution
266
+        such as OpenSSH or PuTTY is necessary to create SSH keys
267
+        specifically.
225 268
 
226 269
         Alternatively, GnuPG supports reusing keys in its native OpenPGP
227 270
         format for SSH as long as the underlying key type is compatible.
271
+        First, obtain GnuPG's internal identifier (the "keygrip") for
272
+        the correct key you may want to use.  (Warning: OpenPGP subkeys
273
+        have a different keygrip, so be sure to use the correct one.)
274
+
275
+        ~~~~ console
276
+        $ gpg --list-keys --with-keygrip sample-key@example.com
277
+        pub   rsa4096 2025-07-27 [SC] [expires: 2025-08-01]
278
+              675F056879A81925E3E0DE60370C2A7D2E40FF4C
279
+              Keygrip = C71CB33DC50C9972EF9C135B0FB70D87B1491923
280
+        uid           [ultimate] Sample Key <sample-key@example.com>
281
+        sub   rsa4096 2025-07-27 [E] [expires: 2025-08-01]
282
+              Keygrip = 84129D49C9A0654BDFAE2DACBC7A9D8C563FF884
283
+        ~~~~
284
+
285
+        === "before v2.3.7"
286
+
287
+            Add the keygrip (on a line of its own) to the `sshcontrol`
288
+            file in the GnuPG configuration directory.
289
+
290
+            ~~~~ console
291
+            $ echo C71CB33DC50C9972EF9C135B0FB70D87B1491923 >> ~/.gnupg/sshcontrol
292
+            ~~~~
293
+
294
+        === "v2.3.7 and later"
295
+
296
+            Set a key attribute to permit this key's use in SSH:
297
+
298
+            ~~~~ console
299
+            $ gpg-connect-agent 'keyattr C71CB33DC50C9972EF9C135B0FB70D87B1491923 Use-for-ssh: true' /bye
300
+            ~~~~
228 301
 
229 302
 ---
230 303
 
... ...
@@ -80,7 +80,10 @@ class SSHAgentClient:
80 80
     """
81 81
 
82 82
     _connection: _types.SSHAgentSocket
83
-    SOCKET_PROVIDERS: ClassVar = ['native']
83
+    SOCKET_PROVIDERS: ClassVar = ('native',)
84
+    """
85
+    The default list of SSH agent socket providers.
86
+    """
84 87
 
85 88
     def __init__(  # noqa: C901, PLR0912
86 89
         self,
87 90