You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit cee2339
Browse filesBrowse the repository at this point in the historyBrowse files
Authorized keys and Match blocks from the TUI (#11)
* Answer "who can actually log in", and let a setting apply to only some of them
Two gaps the first version left open, and they are the same gap seen from two
sides. `PubkeyAuthentication yes` on the config screen says the server accepts
keys; nothing said whose keys those are. And every keyword the editor wrote
applied to the whole server, so "passwords off, except for ana" was a change
the tool could describe and not make.
A sixth screen lists the local accounts with the public keys on them, each with
the fingerprint and comment `ssh-keygen` would print. `A` adds a key: the paste
is checked before anything is staged — one line, not a private key, no per-key
options smuggled in front of it — and then the staged file is handed to
`ssh-keygen -lf`, so the fingerprint the dialog shows is the one ssh-keygen
reported. `R` on that screen removes the selected key by fingerprint rather
than by position, rewriting the file without that one line and copying
everything else through byte for byte: unlike the drop-in, authorized_keys is
not a file this tool owns, and a rewrite that dropped what it did not
understand would lose somebody's `from=` restriction.
Both plans install through `install` with the mode and the owner set in the
same call. That is not tidiness either: with StrictModes on, sshd ignores a
group-writable ~/.ssh and leaves you staring at a key that is plainly there and
plainly not working.
`m` opens the same editor with two fields in front of it and writes the keyword
inside a `Match User`, `Match Group` or `Match Address` block. The renderer now
keeps every file-scope keyword above every block, whichever order they were
written in, because sshd reads what follows a Match line as part of that block
— a keyword appended after one would silently stop applying to the server. The
criteria is validated too: `Match Address 10.0` looks fine and selects nothing.
The fingerprint is computed in-process rather than shelled out for once per key
per read, and a table test pins it to what ssh-keygen prints for three real
keys. A fuzz target on the authorized_keys parser found a line with a carriage
return inside it being copied back into the file it rewrote; such a line is now
left alone rather than understood, and the input is committed with the rest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCypLQF5AD8JD831Ea9S9q
* Name the sample machine's service accounts in the secret scanner's allowlist
The users screen needs home directories to show a key file path, and the sample
machine's are /home/deploy and /home/backup. The family's developer-home-path
rule reads those as a captured workstation path and fails the run — which is
what the rule is for, and why the answer is the allowlist beside it rather than
a weaker regex or a path spelled around the scanner.
Neither name is a person. `deploy` is a service account and `backup` a job
account, which is what a server's /home actually holds and what this screen has
to be able to show; both are invented for the demo, like `ana` already on that
list. The key file paths themselves are now derived through
AuthorizedKeysPathFor rather than spelled out, so the fixtures cannot drift from
the function the product uses to build the same path.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCypLQF5AD8JD831Ea9S9q
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@@ -259,6 +259,31 @@ believed to put it in the same place, but that has not been verified here and
259
259
nothing in the tool depends on it — the include order is read from the machine
260
260
in front of you on every start, and it is what the warning is computed from.
261
261
262
+
### `Match` blocks, and why they go last
263
+
264
+
`m` opens the same editor with two fields in front of it: what the block selects
265
+
on — `User`, `Group` or `Address` — and the value it selects. The keyword list
266
+
and the validators are the ones the file-scope editor uses, so a value sshd
267
+
would refuse is refused here too, and the criteria is checked as well: a
268
+
`Match Address 10.0` looks fine, selects nothing, and is a mistake you would
269
+
otherwise find later.
270
+
271
+
The generated file keeps **every file-scope keyword above every `Match` block**,
272
+
whichever order you write them in. That is not tidiness. `sshd` reads everything
273
+
following a `Match` line as part of that block until the next `Match` or the end
274
+
of the file, so a keyword appended after one would silently stop applying to the
275
+
server and start applying to whoever the block selects. The renderer regenerates
276
+
the whole file in the one order that means what it says, and the same
277
+
`sshd -t -f` gate, diff and two commands apply.
278
+
279
+
```
280
+
PermitRootLogin no
281
+
PasswordAuthentication no
282
+
283
+
Match User ana
284
+
PasswordAuthentication yes
285
+
```
286
+
262
287
### Changing the port
263
288
264
289
Moving `Port` or `ListenAddress` is the one change that can lock you out of a
@@ -307,6 +332,45 @@ directory rather than deleted, so a regeneration done by mistake is reversible
307
332
with a `mv` you can read, and the dialog says in as many words what it does to
308
333
every client that has ever connected.
309
334
335
+
## Who has a key
336
+
337
+
`6` is the accounts somebody could log into — `root`, and the ordinary accounts
338
+
with a real shell — each row a public key from that account's
339
+
`~/.ssh/authorized_keys`, with its type, its SHA256 fingerprint and its comment.
340
+
An account with no key is a row too, saying so: "nobody logs into `backup` with
341
+
a key" is as much of an answer as a fingerprint is, and an account missing from
342
+
the screen would read as an account missing from the machine.
343
+
344
+
`PubkeyAuthentication yes` on the config screen says the server accepts keys.
345
+
This screen is who has one.
346
+
347
+
`A` adds a key: pick the account, paste the `.pub` line. Before anything is
348
+
staged the paste is checked — one line, not a private key, no per-key options
349
+
smuggled in front of it — and then the staged file is handed to `ssh-keygen -lf`
350
+
and the dialog reports the fingerprint **ssh-keygen** gave back, not one this
351
+
tool worked out for itself. Two commands follow, both previewed:
352
+
353
+
```sh
354
+
install -d -m 700 -o ana -g ana /home/ana/.ssh
355
+
install -m 600 -o ana -g ana <staged file> /home/ana/.ssh/authorized_keys
356
+
```
357
+
358
+
The modes are part of the plan rather than left to the umask, because with
359
+
`StrictModes` on — the default — sshd ignores a group-writable `~/.ssh` or
360
+
`authorized_keys` and leaves you staring at a key that is plainly there and
361
+
plainly not working.
362
+
363
+
`R` on this screen removes the selected key, identified by its **fingerprint**
364
+
rather than by its position: the file is re-read, rewritten without that one
365
+
line, and installed the same way. Everything else in it — the comments, the
366
+
`from=` restrictions, the other keys, the order — is copied through byte for
367
+
byte, because unlike the drop-in this is not a file `tui-ssh` owns. Removing the
368
+
last key leaves the file empty rather than deleting it, and the dialog says
369
+
whose access ends and whether a password would still get in.
370
+
371
+
`R` means re-read the server on every other screen, as it always has; `ctrl+r`
372
+
does that from here too.
373
+
310
374
## Usage
311
375
312
376
```sh
@@ -398,14 +462,21 @@ Every one of these is previewed and confirmed first.
398
462
| Key | What runs |
399
463
| --- | --- |
400
464
|`e`|`sshd -t -f <staged file>` (a read, before the question), then `install -m 600 <staged file> /etc/ssh/sshd_config.d/90-tui-ssh.conf` and `systemctl reload <unit>`|
465
+
|`m`| the same three, with the keyword written inside a `Match` block |
466
+
|`A`|`ssh-keygen -l -f <staged file>` (a read, before the question), then `install -d -m 700 -o <user> -g <group> <home>/.ssh` and `install -m 600 -o <user> -g <group> <staged file> <home>/.ssh/authorized_keys`|
467
+
|`R`| on the users screen: `install -m 600 -o <user> -g <group> <staged file> <home>/.ssh/authorized_keys`, with the selected key rewritten out |
401
468
|`t`|`loginctl terminate-session <id>`|
402
469
|`b`|`ufw deny from <address>`, and only when ufw is the active firewall |
0 commit comments