Skip to content

fix: handle long inline keys in server.user_authorized_keys - #1908

Merged
Fizzadar merged 4 commits into
pyinfra-dev:3.xfrom
KalvadTech:fix/user-authorized-keys-enametoolong
Sep 14, 2026
Merged

Fizzadar merged 4 commits into
pyinfra-dev:3.xfrom
KalvadTech:fix/user-authorized-keys-enametoolong

Conversation

@wowi42

@wowi42 wowi42 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1907.

Problem

Since v3.10, passing a long inline public key (e.g. a full RSA key) to server.user_authorized_keys / server.user(public_keys=...) crashes with OSError: [Errno 36] File name too long on Python 3.11–3.13.

4a7440e (os.path → pathlib refactor) changed os.path.exists(try_path) to Path(try_path).exists() in read_any_pub_key_file. Inline keys are tried as file paths first, and a long key exceeds NAME_MAX. The two APIs handle ENAMETOOLONG differently:

  • os.path.exists() catches all OSError → returns False
  • pathlib.Path.exists() (Python ≤ 3.13) only swallows ENOENT/ENOTDIR/EBADF/ELOOP and re-raises ENAMETOOLONG

Python 3.14 is unaffected because pathlib there catches all OSError in exists(), which matches the reporter's observations.

Fix

Revert that check to os.path.exists() — semantically the right API for "does this string happen to be a file?" — with a comment explaining why, and drop the now-unused pathlib import.

Test

New fixture tests/operations/server.user_authorized_keys/long_inline_key_not_a_file.yaml covering a >255-char inline key. Verified manually against the real filesystem on Python 3.13.14 (old code raises ENAMETOOLONG, fixed code returns False) and 3.14.6; the full suite passes on both.

Commit 4a7440e replaced os.path.exists with Path.exists in
read_any_pub_key_file. On Python < 3.14, pathlib.Path.exists re-raises
OSError (ENAMETOOLONG) for paths exceeding NAME_MAX, so passing a long
inline public key (eg a full RSA key) crashed the operation instead of
being used as a key. os.path.exists swallows OSError and returns False,
which is the correct semantic here ("is this string a file?").

Fixes pyinfra-dev#1907
@wowi42 wowi42 added bug Label for all kind of bugs. operations Issues with operations. labels Aug 16, 2026

@Fizzadar Fizzadar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix looks good but the regression test doesn't actually do anything (passes without the fix) due to the mocking. We should teach the conftest mock to raise OSError(ENAMETOOLONG) for over-long path components.

@wowi42

wowi42 commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

Good catch, done in 264f8bc. The conftest mock now mimics the real filesystem: any path component over 255 bytes raises OSError(ENAMETOOLONG). The mocked os.path.exists swallows it and returns False, and the mocked Path.exists re-raises it, matching pathlib on Python < 3.14.

Verified the fixture is no longer vacuous: with the fix reverted to Path(try_path).exists() it fails with [Errno 36] File name too long; with the fix it passes. Full suite green.

@Fizzadar Fizzadar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@Fizzadar
Fizzadar merged commit 2a3f5df into pyinfra-dev:3.x Sep 14, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Label for all kind of bugs. operations Issues with operations.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pwhash issue

2 participants