Skip to content

chore: enforce private-key file permission check for Ed25519 key - #16

Merged
rubenhensen merged 2 commits into
masterfrom
chore/harden-ed25519-key-perms
Jul 20, 2026
Merged

chore: enforce private-key file permission check for Ed25519 key#16
rubenhensen merged 2 commits into
masterfrom
chore/harden-ed25519-key-perms

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jul 20, 2026

Copy link
Copy Markdown

The XMSSMT key loader refuses to load a private key file with lax permissions; the Ed25519 key loader did not apply the same guard. Both are signing keys of the timestamping service, so an Ed25519 key left with lax file permissions was loaded silently while an equally-exposed XMSSMT key would be rejected.

This change:

  • extracts the shared permission predicate into keyFilePermsInsecure,
  • applies it in both loadXMSSMTKey and loadEd25519Key,
  • adds regression tests for the predicate.

The predicate is deliberately not the blanket perm&077 != 0 the XMSSMT loader used before: that check cannot pass on Kubernetes, where secret volumes are written root-owned and fsGroup — the standard mechanism for exposing them to a non-root container — yields mode 0440. Instead, following PostgreSQL's ssl_key_file precedent:

  • any world bits, group-write or group-execute → refused,
  • group-read → accepted only when the file's group is the process' effective or a supplementary group (grants nobody access the process does not already have).

Verified against a running cluster deployment (secret mounted with defaultMode: 0400 + fsGroup, resulting in root:1000 0440): the old check would have crash-looped the pod on the next image pull; the amended check accepts it while still refusing genuinely exposed keys.

Defense-in-depth hardening; no protocol or config changes. go test -race ./... passes.

Found during a routine in-depth security review of this repository.

loadXMSSMTKey refuses to load a private key whose file is group- or
world-accessible, but loadEd25519Key did not apply the same guard. Both
are signing keys of the timestamping service, so an Ed25519 key left with
lax permissions was loaded silently while an equally-exposed XMSSMT key
was rejected.

Extract the shared permission predicate into keyFilePermsInsecure, apply
it in both loaders, and add a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dobby-coder
dobby-coder Bot requested a review from rubenhensen July 20, 2026 00:06
rubenhensen
rubenhensen previously approved these changes Jul 20, 2026
@rubenhensen
rubenhensen marked this pull request as ready for review July 20, 2026 10:02
A blanket perm&077 != 0 check cannot pass on Kubernetes: secret volumes
are written root-owned, and fsGroup — the standard mechanism for exposing
them to a non-root container — chowns them to the pod's group and yields
mode 0440. The group-read bit is the only thing that lets the process
read its own key, so refusing it makes the check incompatible with the
platform rather than stricter.

Follow PostgreSQL's ssl_key_file precedent: still refuse any world bits,
group-write and group-execute, but accept group-read when the file's
group is the process' effective or a supplementary group — that grants
nobody access the process does not already have.
@rubenhensen
rubenhensen merged commit 99257fa into master Jul 20, 2026
3 checks passed
@dobby-coder
dobby-coder Bot deleted the chore/harden-ed25519-key-perms branch July 20, 2026 20:04
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.

2 participants