Add how-to for setting up an SSH key for `derivepassphrase vault`
Marco Ricci

Marco Ricci commited on 2024-11-23 18:50:10
Zeige 4 geänderte Dateien mit 186 Einfügungen und 7 Löschungen.


We explain the necessary software/operating system prerequisites and
configurations.  We further prominently note that Windows is currently
not supported, and that `gpg-agent` behaves differently than other
agents in regard to SSH key management.

We further clean up the list of future work, and ensure styling
consistency with the basic setup (passphrase) tutorial.
... ...
@@ -47,12 +47,11 @@
47 47
 
48 48
 ### Tutorials
49 49
 
50
-* [Setting up `derivepassphrase` from scratch for three existing accounts, with a master passphrase](tutorials/basic-setup-passphrase.md)
51
-* Setting up `derivepassphrase` from scratch for three existing accounts, with a new SSH key
50
+* [Setting up `derivepassphrase vault` from scratch for three existing accounts, with a master passphrase](tutorials/basic-setup-passphrase.md)
52 51
 
53 52
 ### How-tos
54 53
 
55
-* How to set up `derivepassphrase` with an SSH key
54
+* [How to set up `derivepassphrase vault` with an SSH key](how-tos/ssh-key.md)
56 55
 * How to choose a good service name
57 56
 * How to edit a saved `derivepassphrase vault` configuration correctly
58 57
 * How to deal with "supported" and "unsupported" special characters
... ...
@@ -67,4 +66,3 @@
67 66
 * Security aspects and other tradeoffs when using deterministic password generators
68 67
 * Tradeoffs between a master passphrase and a master SSH key
69 68
 * Why is `vault`'s `--repeat` option named this way if it counts occurrences, not repetitions?
70
-* Why are master SSH keys not supported under Windows?
... ...
@@ -0,0 +1,181 @@
1
+# How to set up `derivepassphrase vault` with an SSH key
2
+
3
+## Prerequisites
4
+
5
+!!! abstract "See also"
6
+
7
+    → Tradeoffs between a master passphrase and a master SSH key (TODO)
8
+
9
+ 1. [A running SSH agent; typically provided by OpenSSH or
10
+    PuTTY.](#prereq-ssh-agent)
11
+ 2. [A Python installation that can talk to the SSH
12
+    agent.](#prereq-python-support)
13
+ 3. [A supported SSH key; typically an RSA, Ed25519 or Ed448
14
+    key.](#prereq-ssh-key)
15
+
16
+### A running SSH agent { #prereq-ssh-agent }
17
+
18
+Install an SSH agent, which is usually part of an SSH client
19
+distribution.  `ssh-agent` from [OpenSSH][], Pageant from [PuTTY][] and
20
+`gpg-agent` (v2) from [GnuPG][] are known to work.  If in doubt, choose
21
+OpenSSH.
22
+
23
+??? note "Agent-specific features"
24
+
25
+    * OpenSSH's `ssh-agent` supports limiting the time the agent holds
26
+      the key in memory ("key lifetime").  Such usage is *recommended*.
27
+    * `ssh-agent` and GnuPG's `gpg-agent` support requiring confirmation
28
+      upon each use for a specific key.  Such usage is *also
29
+      recommended*.
30
+
31
+??? note "Other agent-specific notes"
32
+
33
+    === "GnuPG/`gpg-agent`"
34
+
35
+        * `gpg-agent` v2.0 and later uses a *persistent* database of
36
+          known keys, SSH or otherwise.  "Adding" a key to the agent
37
+          actually means *importing* it, and requires choosing an
38
+          "import passphrase" to protect the key on disk, in the
39
+          persistent database.  `gpg-agent` will cache the import
40
+          passphrase in memory, and if that cache entry expires, then
41
+          the *import passphrase* must be provided to unlock the key.
42
+        * As a design consequence, `gpg-agent` always lists all known
43
+          SSH keys as available in the agent.  It is impossible to
44
+          remove an SSH key from `gpg-agent` using standard SSH agent
45
+          operations.
46
+
47
+### A Python installation that can talk to the SSH agent { #prereq-python-support }
48
+
49
+On non-Windows operating systems, your Python installation must support
50
+UNIX domain sockets (the `socket.AF_UNIX` symbol).  The SSH agent must
51
+expose its communication socket via the `SSH_AUTH_SOCK` environment
52
+variable.
53
+
54
+??? bug "Windows is currently *not* supported"
55
+
56
+    [→ Issue `the-13th-letter/derivepassphrase#13`: Support
57
+    PuTTY/Pageant on Windows][ISSUE_WINDOWS_SUPPORT]
58
+
59
+    The two major SSH agents on Windows (PuTTY/Pageant and OpenSSH) use
60
+    <i>Windows named pipes</i> for communication, and Python on Windows
61
+    does not inherently support named pipes.  No comprehensive
62
+    third-party modules to interface with named pipes appears to exist,
63
+    so teaching `derivepassphrase` to use Windows named pipes will
64
+    require us developers to write a custom (C?) module specific to this
65
+    application---an unrealistic task if we lack both technical know-how
66
+    for the named pipe API as well as Windows hardware to test any
67
+    potential implementation on.
68
+
69
+### A supported SSH key { #prereq-ssh-key }
70
+
71
+For an SSH key to be usable by `derivepassphrase`, the SSH agent must
72
+always generate the same signature for the same input, i.e. the
73
+signature must be deterministic for this key type.  Commonly used SSH
74
+types include RSA, DSA, ECDSA, Ed25519 and Ed448.
75
+
76
+* RSA, Ed25519 and Ed448 signatures are deterministic by definition.
77
+  Thus RSA, Ed25519 and Ed448 keys are suitable under any SSH agent.
78
+
79
+* DSA and ECDSA signatures require choosing a value specific to each
80
+  signature (a "cryptographic nonce"), which must be unpredictable.
81
+  Typical DSA/ECDSA implementations therefore generate a suitably large
82
+  random number as the nonce.  This makes signatures non-deterministic,
83
+  and thus unsuitable for `derivepassphrase`.
84
+
85
+    ??? info "Exception: PuTTY/Pageant and RFC 6979"
86
+
87
+        [RFC 6979][] specifies a method to *calculate* the nonce from
88
+        the DSA/ECDSA key and the message to be signed.  DSA/ECDSA
89
+        signatures from SSH agents implementing RFC 6979 are therefore
90
+        deterministic, and thus *also* suitable for `derivepassphrase`.
91
+        Pageant 0.81 implements RFC 6979.
92
+
93
+        !!! warning "Warning: Pageant < 0.81"
94
+
95
+            Pageant 0.80 and earlier uses a different, homegrown method
96
+            to calculate the nonce deterministically.  Those versions
97
+            are *also* prinicipally suitable for use with
98
+            `derivepassphrase`, but **they generate different signatures
99
+            -- and different derived passphrases -- than Pageant 0.81
100
+            and later**.
101
+
102
+## Configuring `derivepassphrase vault` to use an SSH key
103
+
104
+Assuming the [prerequisites above](#prerequisites) are satisfied, ensure
105
+that the SSH agent is running, the SSH key is loaded into the agent, and
106
+the `SSH_AUTH_SOCK` environment variable is correctly set up.  The exact
107
+commands depend on the SSH agent in use.
108
+
109
+=== "OpenSSH"
110
+
111
+    ~~~~ console title="Typical setup commands"
112
+    $ # Start the agent.  Also sets up the environment.
113
+    $ eval `ssh-agent -s`
114
+    Agent pid 12345
115
+    $ # Add your key, with a 900s timeout and requiring confirmation.
116
+    $ ssh-add -t 900 -c ~/.ssh/my-vault-ed25519-key
117
+    Enter passphrase for /home/user/.ssh/my-vault-ed25519-key (will confirm each use): 
118
+    Identity added: /home/user/.ssh/my-vault-ed25519-key (vault key)
119
+    Lifetime set to 900 seconds
120
+    The user must confirm each use of the key
121
+    $ # The agent is ready to use.  Don't forget to terminate the agent
122
+    $ # when you're done: `kill 12345`, or whatever the agent pid is.
123
+    ~~~~
124
+
125
+=== "PuTTY"
126
+
127
+    ~~~~ console title="Typical setup commands"
128
+    $ # Start the agent.  Also sets up the environment and adds your key.
129
+    $ eval `pageant -T ~/.ssh/my-vault-ed25519-key.ppk`
130
+    Enter passphrase to load key 'vault key': 
131
+    $ # The agent is ready to use, and will persist until this console
132
+    $ # is closed.
133
+    ~~~~
134
+
135
+=== "GnuPG"
136
+
137
+    `gpg-agent` is mainly intended to reuse OpenPGP keys in SSH
138
+    contexts.  Actually loading native SSH keys into `gpg-agent`
139
+    requires a separate SSH agent client (such as OpenSSH).
140
+
141
+    ~~~~ console title="Typical setup commands"
142
+    $ # Enable SSH agent support in GnuPG; equivalent to passing
143
+    $ # --enable-ssh-support upon agent startup.
144
+    $ echo enable-ssh-support:0:1 | gpgconf --change-options gpg-agent
145
+    $ # Add your key, requiring confirmation.  Here we use the OpenSSH
146
+    $ # tools.
147
+    $ ssh-add -c ~/.ssh/my-vault-ed25519-key
148
+    Enter passphrase for /home/user/.ssh/my-vault-ed25519-key (will confirm each use): 
149
+    Identity added: /home/user/.ssh/my-vault-ed25519-key (vault key)
150
+    The user must confirm each use of the key
151
+    $ # The agent is ready to use.
152
+    ~~~~
153
+
154
+Next, configure `derivepassphrase vault` to use the loaded SSH key.  (You
155
+will be prompted to select the correct SSH key among the keys the agent
156
+holds, unless there is only one suitable key.)
157
+
158
+=== "global key"
159
+
160
+    ~~~~ console
161
+    $ derivepassphrase vault -k
162
+    ~~~~
163
+
164
+    Now `derivepassphrase vault` will automatically use the configured
165
+    key globally, even without the `-k`/`--key` option.
166
+
167
+=== "key specifically for <var>SERVICE</var>"
168
+
169
+    ~~~~ console
170
+    $ derivepassphrase vault -k SERVICE
171
+    ~~~~
172
+
173
+    Now `derivepassphrase vault` will automatically use the configured
174
+    key for <var>SERVICE</var>, even without the `-k`/`--key` option.
175
+
176
+[GnuPG]: https://gnupg.org/
177
+[ISSUE_WINDOWS_SUPPORT]: https://github.com/the-13th-letter/derivepassphrase/issues/13
178
+[OpenSSH]: https://www.openssh.com/
179
+[PuTTY]: https://www.chiark.greenend.org.uk/~sgtatham/putty/
180
+[PYTHON_AF_UNIX]: https://docs.python.org/3/library/socket.html#socket.AF_UNIX
181
+[RFC 6979]: https://www.rfc-editor.org/rfc/rfc6979
... ...
@@ -94,7 +94,7 @@ We need to translate the passphrase policy into options for `derivepassphrase`:
94 94
 * A policy "between <var>n</var> and <var>m</var> characters long" translates to <code>-<span/>-length <var>k</var></code>, for any choice of <var>k</var> which satisfies <var>n</var> ≤ <var>k</var> ≤ <var>m</var>.
95 95
   (`derivepassphrase` does not explicitly choose <var>k</var> for you.)
96 96
 
97
-??? note "Mnemonic: the `--repeat` option"
97
+??? info "Mnemonic: the `--repeat` option"
98 98
 
99 99
     The `--repeat` option denotes the *total* number of consecutive occurrences of the same character.
100 100
     Or alternatively: if you request <code>-<span/>-repeat <var>n</var></code>, then `derivepassphrase` will *avoid* deriving any passphrase that repeats a character *another <var>n</var> times*.
... ...
@@ -88,10 +88,10 @@ plugins:
88 88
 
89 89
 nav:
90 90
   - Overview: index.md
91
-  #- Tutorials & Examples: tutorials.md
92 91
   - Tutorials & Examples:
93 92
     - tutorials/basic-setup-passphrase.md
94
-  #- How-Tos: how-tos.md
93
+  - How-Tos:
94
+    - how-tos/ssh-key.md
95 95
   - Reference:
96 96
     - reference/index.md
97 97
     - 'Man page: derivepassphrase': reference/derivepassphrase.1.md
98 98