Skip to content

fix: confine keys to rootdir; verify host keys by default - #8

Merged
thorwhalen merged 3 commits into
masterfrom
confine-keys-verify-host-keys
Sep 22, 2026
Merged

thorwhalen merged 3 commits into
masterfrom
confine-keys-verify-host-keys

Conversation

@thorwhalen

@thorwhalen thorwhalen commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Closes #7

What changes

Keys confined to rootdir. A key that is absolute, or whose .. segments leave rootdir after normalisation, raises KeyError in __getitem__, __setitem__, __delitem__ and mkdir, and in returns False. Keys that stay inside (a/../b, ./a) behave as before. Keyword-only allow_escape=True restores the old behaviour. Symbolic links on the server are not resolved by this check (documented).

Host keys verified by default. The client used AutoAddPolicy, accepting any host key. It now reads known_hosts the way OpenSSH picks the files (GlobalKnownHostsFile / UserKnownHostsFile from ~/.ssh/config replace the defaults; /dev/null means none), parsing leniently (marker, unsupported or malformed lines are skipped, so @revoked is not honoured), matching HostKeyAlias and lower-cased names. For a host not found there it:

  • follows the host's ssh config: StrictHostKeyChecking no / off / accept-new accept the key for the session;
  • otherwise refuses, with a message explaining how to proceed (connect once with ssh, set the ssh config option, or pass the policy);
  • keyword-only missing_host_key_policy= (a paramiko policy class or instance) overrides both.
    A changed key for a known host is always refused (paramiko behaviour). Subdirectory instances (which open new connections) are pinned to the key their parent connected with. Keys containing NUL are refused.

Release note (behaviour change)

  • Connecting to a host that is not in known_hosts and has no StrictHostKeyChecking setting now fails instead of silently trusting the key. Fix: ssh <host> once, or pass missing_host_key_policy=paramiko.AutoAddPolicy.
  • Keys pointing outside rootdir now raise KeyError; pass allow_escape=True if you relied on that.

No fleet dependents.

Review

Independent refute-review by a sub-agent: first round found one blocker (a known_hosts file with @cert-authority/malformed lines could crash the constructor) and should-fixes (UserKnownHostsFile should replace defaults, silent test skips, subdirectory re-trust, HostKeyAlias/case); all fixed. Re-review: no blockers; its performance note (quadratic key loading) and the remaining silent-skip path were fixed too.

Tests

sshdol/tests/test_confinement.py (no server needed) plus doctests. The existing integration tests run in hosted CI against a localhost sshd whose ssh config sets StrictHostKeyChecking no, which exercises the config-following path. wads ci-local passes (integration tests skipped locally: no test server). When SSH_TEST_HOST is set explicitly (as in CI), a failing connection now fails the tests instead of skipping them.

🤖 Generated with Claude Code

thorwhalen and others added 3 commits September 22, 2026 16:19
- Keys that are absolute or whose `..` segments leave rootdir now raise
  KeyError in getitem/setitem/delitem/mkdir (and `in` returns False).
  Keyword-only `allow_escape=True` restores the old behaviour.
- Host keys are checked against known_hosts (system, user, and the ssh
  config's UserKnownHostsFile). Unknown keys are refused unless the host's
  ssh config sets StrictHostKeyChecking no/accept-new, or the caller passes
  keyword-only `missing_host_key_policy` (e.g. paramiko.AutoAddPolicy).

Closes #7

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- known_hosts is parsed line by line; marker (@cert-authority/@Revoked),
  unsupported, malformed or undecodable lines are skipped instead of
  aborting (or crashing) the connection.
- GlobalKnownHostsFile/UserKnownHostsFile replace the defaults, as in
  OpenSSH (/dev/null or none = no file).
- Keys recorded under HostKeyAlias or the lower-cased host name are matched.
- Subdirectory instances (new connections) are pinned to the host key their
  parent connected with.
- Keys containing NUL are refused; clearer refusal message.
- Integration tests fail instead of skipping when SSH_TEST_HOST is set
  explicitly (as in CI) and the connection fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hable configured test server

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@thorwhalen
thorwhalen merged commit 41be0ca into master Sep 22, 2026
8 checks passed
@thorwhalen
thorwhalen deleted the confine-keys-verify-host-keys branch September 22, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Confine keys to rootdir; verify host keys by default

1 participant