No description
  • Rust 70.2%
  • Shell 22%
  • Scheme 5.1%
  • Makefile 2.6%
  • Common Lisp 0.1%
Find a file
ober 8f31769a50
All checks were successful
required-ci / required (push) Successful in 8m37s
Merge pull request #5
2026-09-25 04:49:19 -04:00
.forgejo Use FreeBSD Forgejo CI runner 2026-08-03 14:52:26 -06:00
.jerboa Security hardening and release readiness 2026-06-23 10:53:04 -06:00
bin fix: accept loop resilience, RLIMIT_CPU in child, PSK validation, Landlock rights, shebang, production defaults 2026-07-21 09:34:07 -06:00
crates/jsshd-core Fix FreeBSD CPU rlimit portability 2026-09-25 02:33:07 -06:00
docs fix: close security audit findings 2026-07-11 17:25:35 -06:00
examples fix: accept loop resilience, RLIMIT_CPU in child, PSK validation, Landlock rights, shebang, production defaults 2026-07-21 09:34:07 -06:00
fuzz Sync fuzz lock package version 2026-09-25 02:03:45 -06:00
packaging Initial jerboa sshd implementation 2026-06-11 09:50:16 -06:00
scripts Add optional Conduit secmon producer mode 2026-09-25 02:20:56 -06:00
support Set up Forgejo CI/CD policy 2026-08-03 12:55:31 -06:00
.gitignore Security hardening and release readiness 2026-06-23 10:53:04 -06:00
.gitsafeignore Set up Forgejo CI/CD policy 2026-08-03 12:55:31 -06:00
AGENTS.md docs(agents): add checkout hygiene policy (work dirs under ~/work, cleanup when done) 2026-09-12 19:19:16 -06:00
Cargo.lock Sync jsshd package version and formatting 2026-09-25 02:01:49 -06:00
Cargo.toml build: enable integer overflow checks in release profile 2026-07-21 16:40:31 -06:00
jpkg.sexp Use FreeBSD Forgejo CI runner 2026-08-03 14:52:26 -06:00
LICENSE Switch to MIT license 2026-07-21 13:42:20 -06:00
Makefile Use self-contained Jerboa runtime for wrapper smoke 2026-09-25 02:37:02 -06:00
README.md Add optional Conduit secmon producer mode 2026-09-25 02:20:56 -06:00
SECURITY.md fix: close security audit findings 2026-07-11 17:25:35 -06:00
VERSION Require explicit Conduit-compatible secmon source IDs 2026-09-25 01:54:16 -06:00

jerboa-sshd

Experimental SSH server for Jerboa-managed network services.

The intended split is conservative:

  • Rust owns SSH transport, packet framing, key exchange, crypto, auth state, channel flow control, and later PTY/SFTP plumbing.
  • Jerboa owns policy, configuration, supervision, audit logging, reloads, and OS sandbox orchestration.

See docs/secure-jerboa-sshd-plan.md for the current design plan.

Current Status

jsshd-core is now a config-driven Rust SSH daemon using russh with the aws-lc-rs crypto backend. It supports:

  • OpenSSH host keys, config, and authorized-key files opened once with no-follow, owner, link-count, parent-directory, size, and mode checks on Unix
  • OpenSSH authorized_keys parsing for configured SSH users
  • public-key authentication only
  • exact-match restricted exec commands
  • bounded stdin/stdout/stderr, command timeouts, and process-group cleanup
  • connection, user, and server-wide live-exec permits held for the complete child lifetime, plus a per-connection command-start rate limit
  • global connection limiting
  • configured Unix rlimits inherited by child commands
  • bind-before-sandbox startup, with optional Unix chroot, chdir, setgid, setuid, and Linux Landlock filesystem rules
  • Jerboa wrapper entrypoint for build/check/run/health/supervise/smoke workflows
  • cargo-fuzz targets for config, authorized-key, and exec-request parsers
  • systemd and launchd service examples
  • rejection of password, keyboard-interactive, shell, PTY, agent forwarding, X11, TCP forwarding, streamlocal forwarding, and subsystems
  • optional telemetry through one bounded nonblocking queue and one worker with a numeric collector address and a total send deadline

Build

Install the audit tool once:

cargo install cargo-audit --locked
make check
make test
make lint
make audit
make fuzz-check
make smoke
make smoke-hardening
make jerboa-smoke
make verify

Bounded local coverage-guided fuzz evidence is generated with:

JSSHD_RUN_COVERAGE_FUZZ=1 JSSHD_FUZZ_RUNS=2048 make fuzz-evidence

Release evidence is generated with:

make release-evidence

The release bundle includes SBOM, RustSec, reproducibility, bounded fuzz status, and SSHD connection/load status under dist/release-evidence/. Default local evidence records sustained connection/load soak as blocked/not-run; production release requires target-host connection/load evidence beyond the OpenSSH, hardening, and bounded local connection-load smokes.

Configuration

Start from examples/jsshd.example.toml.

max_exec_sessions_per_connection, max_exec_sessions_per_user, and max_exec_sessions_global bound live child operations; closing an SSH channel does not release a permit until its process group is terminated and the direct child is reaped. max_exec_starts_per_minute bounds accepted start attempts on each connection. Relative trusted-file paths may not contain ..; every parent must be root- or daemon-owned and non-writable by group/other (root-owned sticky temporary directories are permitted for local tests).

Production mode requires a host private key:

ssh-keygen -t ed25519 -N '' -f ssh_host_ed25519_key
chmod 600 ssh_host_ed25519_key
cargo run -p jsshd-core -- --config jsshd.toml

Development mode can use an ephemeral host key, but clients will see a new host identity on every restart:

JSSHD_DEV_EPHEMERAL_HOST_KEY=1 cargo run -p jsshd-core -- --config jsshd.toml

Validate config without starting the listener:

cargo run -p jsshd-core -- --config jsshd.toml --check-config

Or use the Jerboa wrapper:

/Users/user/mine/jerboa/.chez/bin/scheme --libdirs /Users/user/mine/jerboa/lib --script bin/jsshd.ss check --config jsshd.toml
/Users/user/mine/jerboa/.chez/bin/scheme --libdirs /Users/user/mine/jerboa/lib --script bin/jsshd.ss health --config jsshd.toml
/Users/user/mine/jerboa/.chez/bin/scheme --libdirs /Users/user/mine/jerboa/lib --script bin/jsshd.ss run --config jsshd.toml
/Users/user/mine/jerboa/.chez/bin/scheme --libdirs /Users/user/mine/jerboa/lib --script bin/jsshd.ss supervise --config jsshd.toml

Environment overrides are available for deployment glue:

  • JSSHD_CONFIG
  • JSSHD_BIND
  • JSSHD_HOST_KEY
  • JSSHD_DEV_EPHEMERAL_HOST_KEY
  • JSSHD_ALLOW_SANDBOX_FALLBACK
  • JSSHD_ALLOW_LANDLOCK_FALLBACK

Optional Secmon telemetry defaults to the legacy encrypted mux mode and uses SECMON_TELEMETRY_ADDR (or SECMON_TELEMETRY_CONNECT) and requires a numeric IP:port, not a hostname, so hostile events cannot consume an unbounded DNS resolver path. When telemetry is enabled, set SECMON_TELEMETRY_SOURCE to the deployment's canonical source ID; it must be 1–128 ASCII bytes containing only lowercase letters, digits, ., _, or -. There is no generated source default, since changing a deployment's source identity implicitly could misroute or split its event history. Missing or invalid source IDs disable telemetry with a warning. The fixed queue drops excess events rather than blocking SSH protocol work.

To use the optional Conduit producer, set SECMON_TELEMETRY_MODE=conduit and configure all of the following explicitly:

Variable Value
SECMON_CONDUIT_CLIENT Absolute path to the trusted executable conduit client; regular, executable, and not group/world writable.
SECMON_CONDUIT_ENDPOINT Numeric tcp://IP:PORT or absolute unix:///path endpoint.
SECMON_CONDUIT_PEER Complete hex encoded endpoint identity bundle from provisioning.
SECMON_CONDUIT_IDENTITY_RECORD Absolute path to the provisioned client identity record.
SECMON_CONDUIT_SEQUENCE_DB Absolute path to this source's persistent sequence database.
SECMON_TELEMETRY_SOURCE Explicit Conduit v1 source ID.
SECMON_TELEMETRY_HOST Exact host authorized by the endpoint policy.
SECMON_CONDUIT_POLICY_GENERATION Positive policy generation.
SECMON_CONDUIT_REVOCATION_ID Positive revocation ID.

The endpoint must grant this peer, source, host, and daemon (jsshd) for secmon.telemetry.v1. This mode invokes the existing Conduit CLI with one JSON draft per event. The CLI owns persistent sequencing, peer authentication, and the response. jsshd accepts only inserted or identical-replay and stops that telemetry worker after any other result or uncertain child outcome, without submitting another event. The CLI performs its own bounded pending frame retry during one invocation. Inspect the sequence database and destination and recover an unresolved event before reenabling this mode; changing the source or sequence database to evade the pending event would lose replay protection. Each child has a 10 second deadline and bounded output, and SSH handling retains the bounded nonblocking queue. The Conduit CLI's persistent identity custody currently supports macOS; verify the service account can execute the client and access its identity and sequence database before enabling this mode. SECMON_TELEMETRY_MODE=legacy explicitly selects the original mux path; omitting the mode keeps that behavior.

See docs/deployment.md for release, host-key, sandbox, service-manager, health-check, and rollback guidance.