Skip to content

Releases: lablup/bssh

v2.2.1

18 May 16:54

Choose a tag to compare

New Features

None.

Improvements

  • Bump workspace dependencies and sync both internal russh forks to upstream stable (#203). Main bssh: lru 0.17 to 0.18 (lifetime fix in get_or_insert_mut_ref), signal-hook 0.3 to 0.4 (only the unused low_level::pipe API changed), opentelemetry / opentelemetry_sdk / opentelemetry-otlp 0.31 to 0.32, plus nix 0.31.2 to 0.31.3 and transitive pin-project / tower-http / zerofrom patches via cargo update.
  • Sync bssh-russh 0.60.1 to 0.60.3 (upstream stable). Picks up aws-lc-rs 1.16.3 to 1.17.0 and the upstream v0.60.2 unreleased fixes our previous PR #193 forward-port had already brought in (#690 SHA-1 MAC exclusion, #693 channel write ordering).
  • Sync bssh-russh-sftp 2.1.1 to 2.1.2 (upstream stable). The fork's original serde_bytes perf fix was absorbed by upstream 2.1.2 and is moved to patches/historical/ for provenance; the remaining custom value-add is the two pipelined File I/O helpers (write_all_pipelined / read_to_writer_pipelined), re-ported on top of upstream 2.1.2's new Features API with chunk sizing derived from features.limits.{write,read}_len or features.max_packet_len.saturating_sub({WRITE,READ}_OVERHEAD_LENGTH) instead of the removed MAX_*_LENGTH constants. crates/bssh-russh-sftp/Cargo.toml swaps flurry for dashmap 6.1.0 and adds serde_bytes as a direct dep to match upstream's set.

Bug Fixes

  • Add the missing [dev-dependencies] block to the bssh-russh fork so its inline test target actually compiles for the first time since the fork's inception (#204). The fork's src/client/test.rs, src/keys/mod.rs, and src/tests.rs were imported verbatim from upstream russh during the initial sync (commit 508aa3f0), but the matching [dev-dependencies] were never copied across, so cargo test -p bssh-russh had failed with E0433 on env_logger / tempfile and cascading E0282 type-inference errors. Adds a minimal block with env_logger, tempfile, and tokio with process / macros features (additive merge). 75 tests now run, covering agent client/server round-trip, PKCS#8 / OpenSSH key decoding, channel lifecycle, GEX, compression, future certificate auth, and server kex junk handling. The agent tests directly exercise the new frame-length cap from PR #203 (the CVE-2026-46673 mitigation) by spawning a real ssh-agent over a Unix-domain socket. Workspace test aggregate climbs from 1796 to 1871 passed.
  • Drop a redundant .into_iter() on a Iterator::chain argument in the synced SFTP session loop (crates/bssh-russh-sftp/src/client/session.rs:194) to satisfy rustc 1.95's stricter clippy::useless_conversion lint (#205). The line was imported verbatim from upstream russh-sftp 2.1.2 in PR #203 and broke CI after the toolchain bump. PR #203 was developed on rustc 1.93.1 where the case did not lint-fire.

CI/CD Improvements

None.

Technical Details

The agent frame-length cap forward-port (CVE-2026-46673 SSH-agent half) lives in crates/bssh-russh/src/keys/agent/{client,server}.rs. Both files now declare const MAX_AGENT_FRAME_LEN: usize = 256 * 1024; and route the receive path through a new read_frame() helper that reads the 4-byte big-endian length prefix, rejects values above the cap with Error::AgentProtocolError before resizing the receive buffer, then reads exactly len bytes. Mirror of upstream russh commit a2d48a7 by Mika Cohen. Recorded as crates/bssh-russh/patches/agent-frame-length-cap.patch so sync-upstream.sh's reverse-apply dry-run will auto-skip on the next sync.

The pipelined SFTP File I/O port lives in crates/bssh-russh-sftp/src/client/fs/file.rs. The original constants (MAX_READ_LENGTH = 261120, MAX_WRITE_LENGTH = 261120) and the bounded_chunk_size(limit, default) helper are gone; chunk size is now derived per-call from self.features.limits.{write,read}_len (when negotiated) or self.features.max_packet_len.saturating_sub(WRITE_OVERHEAD_LENGTH + handle.len() as u32) as u64 (write side) / self.features.max_packet_len.saturating_sub(READ_OVERHEAD_LENGTH) as u64 (read side). The high-level pipelining loops (FuturesUnordered for in-flight requests, BTreeMap reorder buffer for reads, file_end size-cap for EOF detection) are unchanged. The fork's [dev-dependencies] adds futures directly because upstream 2.1.2 no longer depends on it transitively for this code path.

Dependencies

  • lru 0.17.0 to 0.18.0 (lifetime fix in get_or_insert_mut_ref; no API surface change for our LruCache<PathBuf, CacheEntry> usage in src/ssh/config_cache/manager.rs).
  • signal-hook 0.3 to 0.4 (only low_level::pipe API changed: IntoRawFd to OwnedFd. Our consts::SIGWINCH and iterator::Signals usages in src/pty/ are stable across the bump).
  • opentelemetry / opentelemetry_sdk / opentelemetry-otlp 0.31 to 0.32 (the removed ExportConfig / HasExportConfig / with_export_config() trio isn't called from our src/server/audit/otel.rs; WithExportConfig::with_endpoint, LogExporter::builder().with_tonic(), SdkLoggerProvider::builder(), Resource::builder(), and the LogRecord API all remain source-compatible). Note: 0.32 now errors when an https:// endpoint is configured without a TLS feature; our current features stay at [grpc-tonic, logs] because all uses today are http://, but a follow-up will be needed if anyone runs the audit exporter against TLS in production.
  • nix 0.31.2 to 0.31.3 plus transitive patches in pin-project 1.1.12 to 1.1.13, tower-http 0.6.10 to 0.6.11, zerofrom 0.1.7 to 0.1.8 via cargo update.
  • aws-lc-rs 1.16.3 to 1.17.0 (also pulls aws-lc-sys 0.40.0 to 0.41.0).
  • russh-cryptovec 0.59.0 to 0.60.3 inside the bssh-russh fork. Brings in the cryptovec hardening half of CVE-2026-46673.
  • bssh-russh-sftp fork: replace flurry 0.5 with dashmap 6.1.0 to match upstream russh-sftp 2.1.2; add serde_bytes 0.11 as a direct dep (no longer transitive via removed code paths).

Breaking Changes

None for users of the bssh CLI or bssh-server daemon. The package alias for the SFTP crate stays russh-sftp so all use russh_sftp::* import paths continue to work unchanged. Within the vendored bssh-russh-sftp crate, the upstream 2.1.2 Extensions to Features rename and the flurry to dashmap migration would matter to anyone depending on the fork directly, but no external consumer does today (the fork is workspace-internal).

Known Issues

  • The remaining "outdated" direct deps in bssh-russh (aes 0.9, hmac 0.13, sha1 / sha2 0.11, digest 0.11, cbc / ctr / des new majors, pkcs5 / pkcs8 stable, and the RC family ecdsa rc.18 / rsa rc.18 / elliptic-curve rc.32 / ed25519-dalek pre.7) are blocked by RustCrypto's coordinated release model: shared trait crates, and the pre-release crypto crates we depend on still require the older trait versions. Upstream russh stable v0.60.3 carries the same constraints, so we deliberately stay aligned with it.
  • Transitive lru@0.16.4 and signal-hook@0.3.18 are pulled in by ratatui-core and crossterm respectively and will resolve themselves whenever those upstream crates bump.
  • opentelemetry-otlp 0.32 errors when an https:// endpoint is configured without an explicit tls-ring or tls-aws-lc feature. We did not enable a TLS feature in this release because all current uses are http://; users running the audit exporter against a TLS-protected OTLP collector will need to either disable the warning by switching to http:// or to enable a TLS feature in a follow-up build.

What's Changed

  • update: bump workspace deps and sync russh forks to upstream stable by @inureyes in #203
  • fix: add missing dev-dependencies to bssh-russh fork by @inureyes in #204
  • fix: drop useless .into_iter() on chain arg in synced sftp session by @inureyes in #205

Full Changelog: v2.2.0...v2.2.1

v2.2.0

18 May 14:14

Choose a tag to compare

v2.2.0 — Single-prompt password collection, dependency cleanup

This release reworks --password collection to mirror the existing Arc<SudoPassword> pattern (prompt once in the dispatcher, share an Arc<Password> across all parallel tasks), making -S consistent across subcommands, and continues the dependency-hygiene work started in v2.1.4 by dropping five stale or redundant direct dependencies and clearing all cargo-audit findings.

New Features

  • BSSH_PASSWORD environment variable for non-interactive password authentication (#201). Documented in the man page and the README environment-variables section. Discouraged for production deployments; recommended for automated test pipelines and CI scenarios where SSH agent or key-based auth is not feasible.

Improvements

  • Collect --password once up-front in the dispatcher and share the secret across all parallel SSH tasks via Arc<Password> (backed by secrecy::SecretString, auto-zeroized on drop) (#201, closes #200). The prompt now runs before any executor or indicatif MultiProgress is initialized, so the terminal is in a clean state when reading the password. The pre-collected secret is threaded through ParallelExecutor, ExecutionConfig, ConnectionConfig, FileTransferParams, the jump-host determine_auth_method path, the download glob-resolution path (SshClient::connect_and_execute_with_host_check), the SFTP *_with_jump_hosts helpers, and the legacy execute_command_with_forwarding path. The in-task rpassword::prompt_password() call remains only as the fallback for the OpenSSH-style "all key methods failed" opportunistic prompt path, which has no dispatcher-side collection.
  • Warn on stderr when -S / --sudo-password is passed to subcommands where it has no effect (ping, upload, download, list, cache-stats, and interactive shells) (#201, follow-up #200). The warning routes through eprintln! so bssh ... ping | grep ... pipelines stay clean. exec and the SSH-mode interactive path continue to honor -S as before.
  • Tighten sudo-password collection so it only applies to exec paths that can inject sudo responses, and avoid unused SSH password collection for local-only dispatcher paths.

Bug Fixes

  • Fix --password race across parallel SSH connections (#200, #201). Multiple per-node tasks previously raced for stdin: the prompt could be missed, repeated per node, or interleaved with the indicatif progress UI rendering.
  • Fix -S being silently dropped by ping, upload, and download (#201, closes #200). The dispatcher only read cli.sudo_password in the exec and interactive branches, so users could pass -S to other subcommands without any error or feedback that the flag had no effect.
  • Fix the -S ignored-warning landing on stdout (#201, follow-up). The previous tracing::warn! was being mixed into stdout under the default tracing subscriber, breaking shell pipelines that consumed bssh output.

CI/CD Improvements

None.

Technical Details

  • New src/security/password.rs module mirroring the existing src/security/sudo.rs design: a Password wrapper backed by secrecy::SecretString with auto-zeroize on drop, prompt_password() with a non-host-specific prompt, get_password_from_env() reading BSSH_PASSWORD, and get_password(warn_env) combining both. Wrapped in Arc<Password> for sharing across per-node tasks without duplicating secret material.
  • AuthContext (src/ssh/auth.rs) gains a password: Option<Arc<Password>> field and a with_pre_collected_password() builder. password_auth() consumes the pre-collected value when available.
  • Migrated four call sites from lazy_static!/once_cell::sync::Lazy/OnceCell to std::sync::LazyLock/OnceLock: ui/tui/progress.rs, executor/output_sync.rs, pty/terminal.rs, utils/logging.rs, ssh/ssh_config/env_cache/global.rs, ssh/config_cache/global.rs (edition 2024 mandates Rust 1.85+).
  • Migrated three forwarding reconnect-jitter sites from fastrand::u64(range) to rand::random_range(range); rand 0.10 was already a direct dependency.
  • Migrated five callers of std::io::stdin().is_terminal()/stdout().is_terminal() from the unmaintained atty crate.
  • 9 new unit tests for Password (creation, empty rejection, debug redaction, clone independence, Arc sharing, env var handling, dispatcher-collection-pattern verification) and 2 new tests in ssh/auth.rs confirming the builder semantics and that password_auth() never blocks on stdin when a pre-collected value is present.

Dependencies

  • Drop five stale or redundant direct dependencies (#199): arrayvec (unused), ctrlc (replaced with tokio::signal::ctrl_c), directories (replaced with dirs, already used in 16 other sites), signal-hook 0.4.4 (downgraded to 0.3 to share the crate crossterm already pulls in via signal-hook-mio), plus the macOS objc2/block2/dispatch2 chain pulled in by ctrlc. Three more crates (lazy_static, once_cell, fastrand) remain transitively but are no longer directly referenced. Cargo.lock loses 76 lines including the entire signal-hook 0.4.4 subtree.
  • Replace atty with std::io::IsTerminal (#198): drops RUSTSEC-2024-0375 (unmaintained) and RUSTSEC-2021-0145 (unsound unaligned read).
  • 33 transitive patch bumps from cargo update within current semver constraints (#198): tokio 1.52.1 to 1.52.3, rustls 0.23.39 to 0.23.40, h2 0.4.13 to 0.4.14, digest 0.11.2 to 0.11.3, rpassword 7.4.0 to 7.5.2, and others.

Breaking Changes

None. BSSH_PASSWORD and the -S warning are both additive: existing scripts that happen to pass -S to non-applicable subcommands keep working but now receive visible stderr feedback.

Known Issues

  • RUSTSEC-2023-0071 (RSA Marvin Attack) acknowledged via .cargo/audit.toml ignore with an explanatory comment (#198). Both rsa 0.9.10 (via ssh-key 0.6.x) and rsa 0.10.0-rc.17 (via the vendored bssh-russh fork) are affected, and no fixed upstream rsa release exists. Bumping to 0.10.0-rc.18 conflicts with the bssh-russh pkcs5 = "=0.8.0-rc.13" pin. Users handling untrusted hosts should prefer Ed25519 or ECDSA keys instead of RSA.

What's Changed

  • update: clean cargo-audit by removing atty and pinning rsa advisory by @inureyes in #198
  • refactor: drop stale and redundant dependencies by @inureyes in #199
  • fix: collect --password once up-front and honor -S consistently across subcommands by @inureyes in #201
  • fix: warn when sudo password is ignored outside exec by @inureyes in #202

Full Changelog: v2.1.4...v2.2.0

v2.1.4

10 May 12:09

Choose a tag to compare

SFTP transfer performance release. Streams uploads/downloads in 255 KiB chunks instead of buffering whole files, pipelines up to 64 concurrent SFTP requests, and raises the server-side MAX_READ_SIZE to the 255 KiB SFTP standard. On a 1 GiB transfer over loopback this drops upload peak RSS from ~3.23 GB to ~20 MB and wall time from 38.6 s to 3.5 s; multi-GB transfers no longer OOM the client.

New Features

None.

Improvements

  • Stream SFTP uploads/downloads instead of buffering whole files in memory (#195). upload_file/upload_dir_recursive previously loaded the entire local file into a Vec<u8> via tokio::fs::read before calling write_all, and download_file/download_dir_recursive called read_to_end into a pooled buffer plus a clone() to a separate Vec before writing locally — so multi-GB transfers had peak RSS that scaled with file size and large files OOM'd the client. Each path now uses a stream_copy() helper looping on 255 KiB reads/writes through the existing AsyncRead/AsyncWrite impls on tokio::fs::File and russh_sftp::client::fs::File. Buffer size matches MAX_WRITE_LENGTH so each chunk maps to one SFTP packet without further fragmentation.

    Verified locally on macOS arm64 against bssh-server v2.1.3 over loopback with a 1 GiB file:

    Op Build real RSS
    upload unpatched 38.65s 3.23 GB
    upload streaming 3.47s 20 MB
    download unpatched 3.93s 2.17 GB
    download streaming 3.41s 16 MB
  • Pipeline up to 64 concurrent SFTP requests for upload and download (#196). Bounded pipelined SFTP upload/download helpers replace the previous strictly-sequential request/response loop. Review follow-ups also: cap server-advertised read/write lengths against local maxima to avoid oversized allocations from untrusted SFTP metadata, bound the download reorder queue across both in-flight and pending out-of-order responses, use fstat size info where available to avoid reads past EOF and validate unexpected short reads, and preserve remote download handle shutdown after syncing with main.

  • Raise bssh-server SFTP MAX_READ_SIZE from 64 KiB to the 255 KiB SFTP standard (#197). The server previously hard-capped every SFTP READ reply at 64 KiB regardless of what the client requested, while bssh-russh-sftp and OpenSSH's sftp-server both use MAX_READ_LENGTH = 261120 (255 KiB). A client asking for a 256 KiB chunk only ever got 64 KiB back, forcing four extra requests per byte stream. Bumped to 261120 so server replies match the chunk size used by the rest of the stack — combined with client-side pipelining (#196), this cuts the per-MiB request count on downloads from 16 to 4. Memory exposure stays bounded: handles are still capped at MAX_HANDLES = 1000 per session and each in-flight read still uses a single per-request buffer of this size.

Bug Fixes

None.

CI/CD Improvements

None.

Technical Details

  • Streaming buffer size (255 KiB) is aligned with the russh-sftp packet limit so each chunk stays within one SFTP read/write request without further fragmentation.
  • Downloaded remote handles are now explicitly closed after successful copies; chunk-size capping and in-memory pipelined upload/download behavior are covered by new SFTP crate tests.

Dependencies

None — Cargo.lock only changed because the workspace package bssh itself bumped from 2.1.3 to 2.1.4.

Breaking Changes

None.

Known Issues

None at release time. The 64-way SFTP pipelining is a fixed concurrency cap (not user-configurable in this release); workloads that need a different parallelism point should track follow-up issues on the repository.

What's Changed

  • perf: stream SFTP uploads/downloads instead of buffering whole file by @Yaminyam in #195
  • perf: pipeline SFTP requests for upload/download (~2-3x speedup) by @Yaminyam in #196
  • perf: raise server MAX_READ_SIZE to SFTP standard 255 KiB by @Yaminyam in #197

Full Changelog: v2.1.3...v2.1.4

v2.1.3

30 Apr 18:02

Choose a tag to compare

bssh v2.1.3

This release fixes several SCP/SFTP path-resolution bugs in bssh-server (issue #186), vendors russh-sftp with a serde_bytes performance optimization (+29% upload throughput), forward-ports unreleased upstream russh fixes, and standardizes man page trailers.

New Features

  • scp.root configuration field (#186): SCP transfers now honor a chroot setting separate from SFTP. When unset, SCP falls back to sftp.root, so a single top-level chroot setting governs both subsystems unless an admin explicitly wants them split.
  • Internal fork of russh-sftp as bssh-russh-sftp (#188) with a serde_bytes performance fix for SSH_FXP_WRITE/SSH_FXP_DATA packets. The upstream serde derive routed Vec<u8> through deserialize_seq (byte-by-byte), accounting for ~42% of server CPU during 1 GiB SFTP uploads in perf profiling. Annotating the data fields with #[serde(with = "serde_bytes")] and implementing wire-compatible serialize_bytes on the SFTP Serializer routes through the existing bulk deserialize_byte_buf/try_get_bytes path. Measured impact on a CPU-bound host (Xeon Silver 4214): 1 GiB SFTP upload throughput improves from 74.8 MiB/s to 96.4 MiB/s (+29%), closing the gap to OpenSSH sftp-server from ~26% to ~5%.

Improvements

  • Default file-transfer behavior is no longer chrooted to the user's home directory (#186). With sftp.root/scp.root unset (the default), absolute client paths are honored verbatim and relative paths resolve from the user's home directory, matching OpenSSH sftp-server/scp defaults. Deployments that intentionally want chroot-at-home-dir must now set sftp.root: <home dir> (or equivalent) explicitly.
  • Forward-port unreleased upstream russh fixes (#193): exclude SHA-1 MACs from Preferred::DEFAULT/COMPRESSED (upstream russh #690) and fix channel write ordering when pending_data is non-empty (upstream russh #693). Refactored sync-upstream.sh to iterate patches/ and reverse-apply --dry-run first so already-merged patches are auto-skipped.
  • Switched the top-level russh-sftp dependency from crates.io russh-sftp = "2.1.1" to the vendored bssh-russh-sftp package; existing use russh_sftp::... imports continue to work unchanged.

Bug Fixes

  • bssh-server SCP/SFTP path doubling on absolute client paths (#186): ScpHandler::resolve_path and SftpHandler::resolve_path_static previously re-rooted every absolute client path under the user's home directory, so scp local user@host:/home/work/file.bin wrote to /home/work/home/work/file.bin and bssh upload local /abs/remote.bin failed with No such file. The resolver now treats absolute client paths verbatim when no chroot is configured and rejects out-of-chroot absolute paths with permission_denied when one is. Path-traversal and symlink-escape protections continue to apply.
  • SCP single-file destinations no longer have the source filename appended (#186): ScpHandler::receive_file now consults target_is_directory (parsed from -d/-r) and the filesystem state of the resolved target. scp local.bin user@host:/tmp/dest.bin now writes to /tmp/dest.bin instead of /tmp/dest.bin/local.bin. Directory destinations (/tmp/dir/, existing directory, or -d/-r flag) keep the previous filename-appending behavior.
  • Configured sftp.root is no longer dead code (#186): the handler-construction sites in SshHandler previously hard-coded user_info.home_dir as the chroot root and ignored config.sftp.root entirely. Setting sftp.root in the YAML configuration now actually changes the SFTP chroot. The same plumbing now exists for scp.root.
  • Chroot bypass via intermediate-directory symlink: the chroot resolver previously checked only lexical containment for paths whose final component did not exist (typical for new-file creates and mkdir). A symlink inside the chroot pointing to a directory outside the chroot would let a client target chroot/escape/newfile and have open(...)/create_dir(...) follow the symlink, writing outside the chroot. Both ScpHandler::resolve_path and SftpHandler::resolve_path_static now canonicalize the closest existing ancestor of the target path and verify it stays inside the canonicalized chroot, blocking the parent-symlink escape. Found during PR #194 review.

CI/CD Improvements

  • Bump GitHub Actions to Node.js 24-compatible versions to address Node.js 20 deprecation warnings that become errors on 2026-06-02 (#191):
    • actions/checkout v4 → v6
    • actions/cache v4 → v5
    • actions/upload-artifact v4 → v7
    • apple-actions/import-codesign-certs v3 → v7

Technical Details

  • Path resolver now walks up to the closest existing ancestor of the target path, canonicalizes both that ancestor and the chroot root, and verifies the canonical ancestor stays inside the canonical root. Operator-misconfigured chroots that don't exist on disk fall back to the lexical check.
  • Removed an unreachable starts_with check in the relative-path chroot resolver after ParentDir clamping (already guarded by if resolved != root).
  • Added focused unit tests for scp.root/sftp.root precedence rules and the no-chroot default.
  • Added tests/scp_sftp_path_resolution_test.rs covering Backend.AI reproduction scenarios (absolute SCP/SFTP paths, no doubling, no chroot honors absolute paths, chroot rejects out-of-root paths, .. clamped, chroot / round-trips through realpath) plus symlink-escape blocking under chroot. Six regression tests added for the parent-symlink chroot bypass.
  • Hardened packet length handling in vendored SFTP with checked u32 conversions; removed an extra byte-buffer copy; added wire-format tests; narrowed lockfile delta; fixed clippy warnings; made Keychain-backed tests skip cleanly when local authorization is unavailable.

Documentation

  • Standardize man page trailers across bssh.1, bssh-keygen.1, and bssh-server.8 into a consistent BUGS / AUTHORS / COPYRIGHT / SEE ALSO order (#192). Author attribution, contact email, Apache-2.0 license notice, and project homepage link are now uniform across all three pages.
  • Document sftp.root and scp.root in bssh-server.8 configuration sections, and add intermediate-directory-symlink chroot protection to SECURITY CONSIDERATIONS.

Dependencies

  • tokio 1.52.1, clap 4.6.1, tracing 0.1.44, lru 0.17, uuid 1.23.1, tokio-util 0.7.18
  • bssh-russh: aws-lc-rs 1.16.3, ecdsa rc.17, elliptic-curve rc.31, p256/p384/p521 rc.9, tokio 1.52.1
  • Pinned pkcs5="=0.8.0-rc.13" because pkcs8 0.11.0-rc.11 still calls the rc.13-era Parameters::recommended API; stable 0.8.0 renamed it to generate_recommended and breaks the build

Breaking Changes

  • Default chroot behavior change (#186): the implicit chroot at the user's home directory is removed. With no sftp.root/scp.root set, absolute client paths are honored verbatim (matching OpenSSH defaults). Deployments that relied on the implicit confinement must explicitly set sftp.root: <home dir> (or equivalent) in the server YAML.

Known Issues

None.

Full Changelog: v2.1.2...v2.1.3

What's Changed

  • chore: bump GitHub Actions to Node.js 24-compatible versions by @inureyes in #191
  • docs: standardize man page trailers with AUTHORS, COPYRIGHT, and SEE ALSO by @inureyes in #192
  • update: upgrade deps and forward-port unreleased upstream russh fixes by @inureyes in #193
  • feat: vendor russh-sftp with serde_bytes perf fix by @Yaminyam in #188
  • fix: SCP/SFTP path resolution and dead chroot config (#186) by @inureyes in #194

New Contributors

Full Changelog: v2.1.2...v2.1.3

v2.1.2

26 Apr 22:25

Choose a tag to compare

This release fixes terminal mouse-tracking leakage on PTY disconnect, hardens panic-time terminal cleanup, and corrects two release-workflow misfires that surfaced after v2.1.1.

New Features

None

Improvements

  • Centralized terminal teardown logic: TerminalGuard::restore_terminal() in interactive_signal.rs now delegates to force_terminal_cleanup() instead of carrying its own incomplete cleanup, so the panic-hook path and the normal Drop path emit identical sequences.

Bug Fixes

  • Restore terminal mouse tracking state on PTY session disconnect (#190, supersedes #189): After a PTY session disconnects (normal exit, Ctrl+C, network drop, or panic), remote interactive programs (vim, tmux, htop) may have enabled mouse tracking on the local terminal. Without cleanup, the terminal remained in tracking mode and printed raw SGR escape sequences on mouse movement. All cleanup paths (TerminalStateGuard::Drop, force_terminal_cleanup, and the panic hook via TerminalGuard) now emit the full set of mouse-tracking-off sequences (modes 1000, 1002, 1003, 1006, 1015) plus cursor-show and alternate-screen-exit on teardown.
  • Make force_terminal_cleanup safe to call from the panic hook: switched the global TERMINAL_MUTEX acquisition to try_lock() so the panic-hook path cannot deadlock if the panicking thread already holds the mutex, and a poisoned mutex from a previous panic no longer triggers a secondary panic via unwrap(). The lock only serializes concurrent teardown; the underlying stdout writes and disable_raw_mode are individually safe.

CI/CD Improvements

  • Trigger Homebrew formula update only after the official release: release.yml now calls update_homebrew_formula.yml via workflow_call from the publish-release job (which converts pre-release to official) instead of workflow_run firing on every Release workflow completion (including pre-release builds). workflow_dispatch is kept for manual runs.
  • Prevent double-trigger of the release workflow: removed the published event type from the release workflow trigger list. The workflow was previously firing twice — once on prereleased from gh release create --prerelease, and again on published when publish-release converted the pre-release to official. publish-release already handles that conversion, and workflow_dispatch covers manual runs.

Technical Details

  • Added unit tests for force_terminal_cleanup() in src/pty/terminal.rs covering idempotency, poisoned-mutex resilience, and held-mutex resilience. Tests use local Mutex instances rather than the global TERMINAL_MUTEX so the global state is not disturbed for other tests in the same process.
  • Extended the force_terminal_cleanup() docstring to make explicit that "unsynchronized" means "without the lock" (not "skipped"), addressing the LOW-severity documentation finding from the earlier security review of #189.

Dependencies

None

Breaking Changes

None

Known Issues

None

What's Changed

  • fix: restore terminal mouse tracking state on PTY session disconnect by @inureyes in #190

Full Changelog: v2.1.1...v2.1.2

v2.1.1

16 Apr 15:32

Choose a tag to compare

New Features

None

Improvements

  • Improved Launchpad PPA packaging for Rust 2024 edition
  • Added debian/sanitize-vendor.py for vendored crate checksum sanitization
  • Added .pyc files to .gitignore

Bug Fixes

  • bssh-server panic on client connection: new_client_with_addr called block_on() inside the tokio async runtime to check the auth rate limiter ban list, causing an immediate "Cannot start a runtime from within a runtime" panic on every incoming connection. Added non-blocking AuthRateLimiter::try_is_banned() using RwLock::try_read(). (#185)
  • bssh-server auth rejection after successful verification: All SshHandler constructors created a local SessionInfo that was never registered with SessionManager. After public key or password verification succeeded, authenticate_session() returned SessionNotFound, rejecting the client. Fixed by deferring session creation to the authentication flow via SessionManager::create_session(). (#185)

CI/CD Improvements

  • Sanitize hidden vendored paths for PPA builds
  • Sanitize vendored crate checksums for PPA builds
  • Updated PPA packaging for Rust 1.92
  • Fixed pending PPA upload version counting
  • Fixed overlay packaging files after tag checkout
  • Updated Launchpad PPA packaging for Rust 2024

Technical Details

  • Added AuthRateLimiter::try_is_banned() non-blocking method using tokio::sync::RwLock::try_read() for use in sync trait contexts
  • Changed SshHandler constructors to initialize session_info as None; sessions are now registered with SessionManager during the authentication flow (auth_none, auth_publickey, auth_password)
  • 429 unit tests pass (21 binary + 408 library); 33-item integration test suite verified

Dependencies

None

Breaking Changes

None

Known Issues

None

What's Changed

  • Fix Launchpad PPA packaging for Rust 2024 by @inureyes in #182
  • Fix PPA workflow packaging overlay by @inureyes in #183
  • Fix PPA version bump for pending uploads by @inureyes in #184
  • fix: resolve server panic and auth failure on client connection by @inureyes in #185

Full Changelog: v2.1.0...v2.1.1

v2.1.0

14 Apr 05:02

Choose a tag to compare

bssh v2.1.0

This release migrates bssh to the Rust 2024 edition and hardens test-environment handling.

New Features

  • EnvGuard RAII wrapper for safe environment variable handling in tests (#179, #181). Wraps std::env::set_var / remove_var behind a Drop-based restore so tests cannot leak environment mutations across runs. Single source of truth in src/test_helpers/env_guard.rs, shared with integration tests via tests/common/mod.rs.

Improvements

  • Rust 2024 edition migration: both the bssh main package and the bundled bssh-russh crate now target the 2024 edition.
  • 2024 edition clippy improvements: collapsed 38 nested if let statements into guard-clause form (if let Some(x) = y && condition) across bssh-russh client/server/keys/kex modules.
  • Test environment handling overhaul: replaced 177 ad-hoc unsafe { env::set_var / remove_var } call sites across 17 test-bearing files with EnvGuard::set / EnvGuard::remove plus #[serial] from the serial_test crate.
  • Removed the hand-rolled ENV_MUTEX / once_cell::sync::Lazy<Mutex<()>> pattern in integration tests in favor of #[serial].

Bug Fixes

  • Fixed pattern matching for Rust 2024 edition: removed explicit ref / ref mut bindings in if let patterns in src/server/handler.rs and src/server/shell.rs (2024 edition uses implicit borrowing in patterns).

CI/CD Improvements

None

Technical Details

  • Added "Test Environment-Variable Mutation Pattern" section to ARCHITECTURE.md documenting the EnvGuard soundness contract, when to use #[serial] vs #[serial(key)], and how integration tests access EnvGuard via tests/common/mod.rs.
  • EnvGuard documents its soundness contract: all tests observing or mutating the same variable must be #[serial] or share a matching #[serial(key)] group, because the serial attribute only serializes against other annotated tests — not unannotated tests that may still race on environment reads.
  • Lib test count raised from 1189 to 1194 with five focused EnvGuard Drop-semantics tests.
  • Validation: cargo check --lib --tests went from 120 E0133 errors to 0; cargo clippy --all-targets --all-features -- -D warnings is clean; cargo test --lib passes 1194 tests.

Dependencies

  • Pinned bytes to v1.11.1.

Breaking Changes

None

Known Issues

None

Full Changelog: v2.0.1...v2.1.0

v2.0.1

13 Apr 14:48

Choose a tag to compare

New Features

  • Added bssh-keygen to Debian package build pipeline for all architectures (amd64, arm64)
  • bssh-keygen now available as separate .deb package alongside bssh and bssh-server

Improvements

  • Enhanced GitHub Actions workflows with improved Debian package building
  • Better distro support with updated Ubuntu 26.04 (Resolute Raccoon) configuration

Bug Fixes

  • Fixed GitHub Actions debian_build.yml distro configuration that caused workflow validation errors
  • Corrected matrix mismatch between "questing" and "resolute" distro names

CI/CD Improvements

  • Added bssh-keygen to Debian package build matrix (debian_build.yml)
  • Created debian/control.bssh-keygen.binary and debian/rules.bssh-keygen.binary for packaging
  • Fixed workflow_run triggers and environment configuration

Technical Details

  • bssh-keygen now has proper Debian control files for package metadata
  • Added architecture-specific binary asset mappings for bssh-keygen

Dependencies

  • Bumped bssh-russh from 0.60.0 to 0.60.1 with security and dependency updates
  • Updated RC dependencies to latest versions:
    • rsa 0.10.0-rc.17 (fixes RUSTSEC-2023-0071 RSA Marvin Attack vulnerability)
    • elliptic-curve 0.14.0-rc.30
    • p256/p384/p521 0.14.0-rc.8
    • ml-kem 0.3.0-rc.2
  • Upgraded spki from 0.8.0-rc.4 to 0.8.0 (stable release)

Breaking Changes

None

Known Issues

None

v2.0.0

13 Apr 06:34

Choose a tag to compare

bssh v2.0.0

Major release. Highlights since v1.7.0:

New Features

  • bssh-server SSH Server — a lightweight SSH server designed for container environments
    • Full SSH, SFTP, and SCP protocol support
    • PTY / shell session support with terminal handling
    • Password and public-key authentication
    • YAML-based comprehensive configuration system
    • Command execution handler with security controls
  • Audit Logging Infrastructure
    • File-based audit exporter (JSON Lines)
    • OpenTelemetry audit exporter for observability platforms
    • Logstash audit exporter for ELK-stack integration
    • Configurable audit event types and logging levels
  • Security Features
    • IP-based access control (allow/deny lists)
    • Authentication rate limiting (fail2ban-like protection)
    • Session management and connection limits
    • Path-traversal prevention in the SFTP handler
  • File Transfer Filtering
    • Path-based and pattern-based (glob) filter rules
    • Configurable filter actions (allow/deny)
  • bssh-keygen — SSH key pair generation utility (RSA, Ed25519, ECDSA)
  • Server Configuration Enhancements
    • Per-jump-host SSH private key configuration
    • SSH config Host alias reference in jump_host configuration
    • SSH keepalive settings in interactive mode
  • Separate Packaging — bssh and bssh-server are now distributed as independent Debian packages and Homebrew formulas

Improvements

  • Added comprehensive server configuration manual and manpages
  • Shared module structure for client/server code reuse
  • Help examples and man pages now correctly show -C (uppercase) for the cluster flag — running the old -c examples would fail with clap's "unexpected argument" error

Bug Fixes

  • SSH idle disconnects: interactive sessions could disconnect inconsistently after idle time — sometimes within minutes, sometimes at the ~10-minute mark. Three underlying issues were addressed:
    • russh::Config::inactivity_timeout (10-minute client-side ceiling) is now explicitly set to None when keepalive is enabled, so the keepalive mechanism alone decides peer liveness
    • TCP-level SO_KEEPALIVE (via socket2::TcpKeepalive) is now applied to every SSH socket, so the kernel can detect broken paths even when SSH keepalive replies are dropped by middleboxes
    • The exec-mode code path previously dropped the user-configured SshConnectionConfig at the ConnectionConfig boundary; it now flows through connect_direct / connect_via_jump_hosts / the jump chain, so server_alive_interval actually takes effect in non-interactive runs
  • bssh-server: Use consistent source package name in the Debian control file so dual-package builds resolve correctly
  • bssh-server: Use type inference for ioctl to support both glibc and musl builds

CI/CD Improvements

  • Updated release workflow for dual-package distribution
  • Separate Debian packages and Homebrew formulas for client and server
  • Added Teams release notification
  • Updated non-LTS Ubuntu target from 25.04 plucky to 25.10 questing

Technical Details

  • russh-based SSH server handler implementation
  • Modular audit-exporter architecture with trait-based design
  • SshConnectionConfig::to_russh_config() now sets inactivity_timeout explicitly based on keepalive state; to_tcp_keepalive() derives kernel TCP keepalive params from the same settings
  • Client::connect_with_config was rewritten around russh::client::connect_stream, building the TcpStream manually so SO_KEEPALIVE can be applied before the SSH handshake
  • Key-generation call sites now pass &mut rand::rng() (rand 0.10's thread RNG implements the new rand_core 0.10 CryptoRng trait directly), dropping the previous ssh_key::rand_core::OsRng workaround

Dependencies

  • Synced bssh-russh fork with upstream warp-tech/russh v0.60.0, pulling in the RustCrypto migration:
    • rand 0.8 → 0.10 stable (via rand_core 0.10.0)
    • signature 3.0.0-rc.10, ed25519-dalek 3.0.0-pre.6, elliptic-curve 0.14.0-rc.28, p256/p384/p521 0.14.0-rc.7, ecdsa 0.17.0-rc.16, curve25519-dalek 5.0.0-pre.6
    • der 0.8, sec1 0.8, pkcs8 0.11.0-rc.11, pkcs5 0.8.0-rc.13, spki 0.8.0-rc.4
    • ml-kem 0.3.0-rc.1 (adds module-lattice 0.2)
    • ssh-key 0.6.18 (via internal-russh-forked-ssh-key)
  • tokio 1.50.0 → 1.51.1, socket2 0.6.1 → 0.6.3, signal-hook 0.4.3 → 0.4.4, fastrand 2.3.0 → 2.4.1, async-compression 0.4.37 → 0.4.41, bytes 1.11.0 → 1.11.1
  • Added direct dep on socket2 = "0.6" for TCP keepalive configuration

Breaking Changes

  • Package split: bssh-server is no longer bundled with bssh — it must be installed as a separate package. Debian users: apt install bssh-server. Homebrew users: brew install lablup/tap/bssh-server.

Known Issues

None

What's Changed

  • feat: Create shared module structure for client/server code reuse by @inureyes in #145
  • feat: Implement basic SSH server handler with russh by @inureyes in #146
  • feat: Implement public key authentication for server by @inureyes in #147
  • feat: Implement command execution handler for server by @inureyes in #148
  • feat: Add comprehensive YAML-based configuration system for bssh-server (#130) by @inureyes in #149
  • feat: Implement server CLI interface (bssh-server binary) (#131) by @inureyes in #150
  • feat: Implement SFTP server handler with path traversal prevention (#132) by @inureyes in #151
  • feat: Implement password authentication for server (#127) by @inureyes in #152
  • feat: Implement PTY/shell session support for server (#129) by @inureyes in #153
  • feat: Fix PTY shell output and publish bssh-russh crate by @inureyes in #154
  • feat: Implement authentication rate limiting (fail2ban-like) by @inureyes in #155
  • feat: Implement IP-based access control by @inureyes in #157
  • feat: Implement audit event types and logging infrastructure (#134) by @inureyes in #158
  • feat: Implement file-based audit exporter (JSON Lines) #135 by @inureyes in #160
  • feat: Implement OpenTelemetry audit exporter (#136) by @inureyes in #161
  • feat: Implement Logstash audit exporter by @inureyes in #162
  • feat: Implement SCP server protocol by @inureyes in #156
  • feat: Implement session management and limits (#142) by @inureyes in #159
  • feat: Implement bssh-keygen tool by @inureyes in #163
  • feat: Implement file transfer filtering infrastructure by @inureyes in #164
  • docs: Add server configuration manual and manpages by @inureyes in #166
  • feat: Implement path-based and pattern-based filter rules by @inureyes in #165
  • feat: Support per-jump-host SSH private key configuration by @inureyes in #169
  • feat: Support SSH config Host alias reference in jump_host configuration by @inureyes in #171
  • feat: Support SSH keepalive settings in interactive mode by @inureyes in #172
  • update: Upgrade all dependencies to latest versions by @inureyes in #173
  • fix: Correct cluster flag short form in help examples by @inureyes in #174
  • fix: Prevent SSH idle disconnects via proper keepalive wiring by @inureyes in #175
  • update: Upgrade Cargo dependencies to latest compatible versions by @inureyes in #176
  • update: Bump rand 0.8 -> 0.9 by @inureyes in #177
  • update: Sync bssh-russh fork with upstream russh 0.60.0 by @inureyes in #178

Full Changelog: v1.7.0...v2.0.0

v2.0.0-beta.1

29 Jan 05:58

Choose a tag to compare

v2.0.0-beta.1 Pre-release
Pre-release

v2.0.0-beta.1 Release Notes

This is the first beta release of bssh 2.0, introducing the bssh-server SSH server for container environments with comprehensive security and audit features.

New Features

  • bssh-server SSH Server - A lightweight SSH server designed for container environments

    • Full SSH, SFTP, and SCP protocol support
    • PTY/shell session support with terminal handling
    • Password and public key authentication
    • YAML-based comprehensive configuration system
    • Command execution handler with security controls
  • Audit Logging Infrastructure

    • File-based audit exporter (JSON Lines format)
    • OpenTelemetry audit exporter for observability platforms
    • Logstash audit exporter for ELK stack integration
    • Configurable audit event types and logging levels
  • Security Features

    • IP-based access control (allow/deny lists)
    • Authentication rate limiting (fail2ban-like protection)
    • Session management and connection limits
    • Path traversal prevention in SFTP handler
  • File Transfer Filtering

    • Path-based filter rules for upload/download control
    • Pattern-based filtering with glob support
    • Configurable filter actions (allow/deny)
  • bssh-keygen Tool

    • SSH key pair generation utility
    • Support for RSA, Ed25519, and ECDSA key types
    • Configurable key sizes and comments
  • Server Configuration Enhancements

    • Per-jump-host SSH private key configuration
    • SSH config Host alias reference in jump_host configuration
    • SSH keepalive settings in interactive mode
  • Separate Packaging

    • bssh and bssh-server now distributed as separate packages
    • Independent Debian packages for client and server
    • Separate Homebrew formulas for each component

Improvements

  • Added comprehensive server configuration manual and manpages
  • Shared module structure for client/server code reuse
  • Updated release workflow for dual-package distribution

Bug Fixes

None

CI/CD Improvements

  • Updated release workflow to build and package bssh and bssh-server separately
  • Separate Debian packages for client and server components
  • Separate Homebrew formulas for independent installation
  • Added bssh-server.8 manpage to packages

Technical Details

  • russh-based SSH server handler implementation
  • Modular audit exporter architecture with trait-based design
  • Shared module structure enabling code reuse between client and server
  • SCP server protocol implementation

Dependencies

None

Breaking Changes

  • Package Split: bssh-server is now distributed as a separate package
    • For Homebrew: brew install bssh-server (separate from brew install bssh)
    • For Debian: apt install bssh-server (separate from apt install bssh)
    • Existing bssh installations continue to work unchanged

Known Issues

  • This is a beta release; please report any issues on GitHub

What's Changed

  • feat: Create shared module structure for client/server code reuse by @inureyes in #145
  • feat: Implement basic SSH server handler with russh by @inureyes in #146
  • feat: Implement public key authentication for server by @inureyes in #147
  • feat: Implement command execution handler for server by @inureyes in #148
  • feat: Add comprehensive YAML-based configuration system for bssh-server (#130) by @inureyes in #149
  • feat: Implement server CLI interface (bssh-server binary) (#131) by @inureyes in #150
  • feat: Implement SFTP server handler with path traversal prevention (#132) by @inureyes in #151
  • feat: Implement password authentication for server (#127) by @inureyes in #152
  • feat: Implement PTY/shell session support for server (#129) by @inureyes in #153
  • feat: Fix PTY shell output and publish bssh-russh crate by @inureyes in #154
  • feat: Implement authentication rate limiting (fail2ban-like) by @inureyes in #155
  • feat: Implement IP-based access control by @inureyes in #157
  • feat: Implement audit event types and logging infrastructure (#134) by @inureyes in #158
  • feat: Implement file-based audit exporter (JSON Lines) #135 by @inureyes in #160
  • feat: Implement OpenTelemetry audit exporter (#136) by @inureyes in #161
  • feat: Implement Logstash audit exporter by @inureyes in #162
  • feat: Implement SCP server protocol by @inureyes in #156
  • feat: Implement session management and limits (#142) by @inureyes in #159
  • feat: Implement bssh-keygen tool by @inureyes in #163
  • feat: Implement file transfer filtering infrastructure by @inureyes in #164
  • docs: Add server configuration manual and manpages by @inureyes in #166
  • feat: Implement path-based and pattern-based filter rules by @inureyes in #165
  • feat: Support per-jump-host SSH private key configuration by @inureyes in #169
  • feat: Support SSH config Host alias reference in jump_host configuration by @inureyes in #171
  • feat: Support SSH keepalive settings in interactive mode by @inureyes in #172

Full Changelog: v1.7.0...v2.0.0-beta.1