- Rust 70.2%
- Shell 22%
- Scheme 5.1%
- Makefile 2.6%
- Common Lisp 0.1%
|
|
||
|---|---|---|
| .forgejo | ||
| .jerboa | ||
| bin | ||
| crates/jsshd-core | ||
| docs | ||
| examples | ||
| fuzz | ||
| packaging | ||
| scripts | ||
| support | ||
| .gitignore | ||
| .gitsafeignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| jpkg.sexp | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| SECURITY.md | ||
| VERSION | ||
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_keysparsing for configured SSH users - public-key authentication only
- exact-match restricted
execcommands - 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_CONFIGJSSHD_BINDJSSHD_HOST_KEYJSSHD_DEV_EPHEMERAL_HOST_KEYJSSHD_ALLOW_SANDBOX_FALLBACKJSSHD_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.