No description
  • Rust 67.9%
  • Shell 23.4%
  • Scheme 5.8%
  • Makefile 2.8%
  • Common Lisp 0.1%
Find a file
2026-07-30 15:47:31 -06:00
.builds ci: set umask 022 in package/verify tasks too (jerbuild creates group-writable cache dirs during pkg operations) 2026-07-22 13:10:59 -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: use random per-process telemetry nonce prefix to avoid cross-restart GCM nonce reuse 2026-07-22 11:30:19 -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 fix: close security audit findings 2026-07-11 17:25:35 -06:00
packaging Initial jerboa sshd implementation 2026-06-11 09:50:16 -06:00
scripts fix: close security audit findings 2026-07-11 17:25:35 -06:00
support Security hardening and release readiness 2026-06-23 10:53:04 -06:00
.gitignore Security hardening and release readiness 2026-06-23 10:53:04 -06:00
AGENTS.md docs: remove jerboa-emacs restriction 2026-07-30 15:47:31 -06:00
Cargo.lock fix: close security audit findings 2026-07-11 17:25:35 -06:00
Cargo.toml build: enable integer overflow checks in release profile 2026-07-21 16:40:31 -06:00
jpkg.sexp Add jpkg packaging 2026-07-21 15:09:27 -06:00
LICENSE Switch to MIT license 2026-07-21 13:42:20 -06:00
Makefile Security hardening and release readiness 2026-06-23 10:53:04 -06:00
README.md fix: close security audit findings 2026-07-11 17:25:35 -06:00
SECURITY.md fix: close security audit findings 2026-07-11 17:25:35 -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 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. The fixed queue drops excess events rather than blocking SSH protocol work.

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