Releases: lablup/bssh
v2.2.1
New Features
None.
Improvements
- Bump workspace dependencies and sync both internal russh forks to upstream stable (#203). Main bssh:
lru0.17 to 0.18 (lifetime fix inget_or_insert_mut_ref),signal-hook0.3 to 0.4 (only the unusedlow_level::pipeAPI changed),opentelemetry/opentelemetry_sdk/opentelemetry-otlp0.31 to 0.32, plusnix0.31.2 to 0.31.3 and transitivepin-project/tower-http/zerofrompatches viacargo update. - Sync
bssh-russh0.60.1 to 0.60.3 (upstream stable). Picks upaws-lc-rs1.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-sftp2.1.1 to 2.1.2 (upstream stable). The fork's originalserde_bytesperf fix was absorbed by upstream 2.1.2 and is moved topatches/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 newFeaturesAPI with chunk sizing derived fromfeatures.limits.{write,read}_lenorfeatures.max_packet_len.saturating_sub({WRITE,READ}_OVERHEAD_LENGTH)instead of the removedMAX_*_LENGTHconstants.crates/bssh-russh-sftp/Cargo.tomlswapsflurryfordashmap6.1.0 and addsserde_bytesas a direct dep to match upstream's set.
Bug Fixes
- Add the missing
[dev-dependencies]block to thebssh-russhfork so its inline test target actually compiles for the first time since the fork's inception (#204). The fork'ssrc/client/test.rs,src/keys/mod.rs, andsrc/tests.rswere imported verbatim from upstream russh during the initial sync (commit508aa3f0), but the matching[dev-dependencies]were never copied across, socargo test -p bssh-russhhad failed with E0433 onenv_logger/tempfileand cascading E0282 type-inference errors. Adds a minimal block withenv_logger,tempfile, andtokiowithprocess/macrosfeatures (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 realssh-agentover a Unix-domain socket. Workspace test aggregate climbs from 1796 to 1871 passed. - Drop a redundant
.into_iter()on aIterator::chainargument in the synced SFTP session loop (crates/bssh-russh-sftp/src/client/session.rs:194) to satisfy rustc 1.95's stricterclippy::useless_conversionlint (#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
lru0.17.0 to 0.18.0 (lifetime fix inget_or_insert_mut_ref; no API surface change for ourLruCache<PathBuf, CacheEntry>usage insrc/ssh/config_cache/manager.rs).signal-hook0.3 to 0.4 (onlylow_level::pipeAPI changed:IntoRawFdtoOwnedFd. Ourconsts::SIGWINCHanditerator::Signalsusages insrc/pty/are stable across the bump).opentelemetry/opentelemetry_sdk/opentelemetry-otlp0.31 to 0.32 (the removedExportConfig/HasExportConfig/with_export_config()trio isn't called from oursrc/server/audit/otel.rs;WithExportConfig::with_endpoint,LogExporter::builder().with_tonic(),SdkLoggerProvider::builder(),Resource::builder(), and theLogRecordAPI all remain source-compatible). Note: 0.32 now errors when anhttps://endpoint is configured without a TLS feature; our current features stay at[grpc-tonic, logs]because all uses today arehttp://, but a follow-up will be needed if anyone runs the audit exporter against TLS in production.nix0.31.2 to 0.31.3 plus transitive patches inpin-project1.1.12 to 1.1.13,tower-http0.6.10 to 0.6.11,zerofrom0.1.7 to 0.1.8 viacargo update.aws-lc-rs1.16.3 to 1.17.0 (also pullsaws-lc-sys0.40.0 to 0.41.0).russh-cryptovec0.59.0 to 0.60.3 inside thebssh-russhfork. Brings in the cryptovec hardening half of CVE-2026-46673.bssh-russh-sftpfork: replaceflurry0.5 withdashmap6.1.0 to match upstream russh-sftp 2.1.2; addserde_bytes0.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(aes0.9,hmac0.13,sha1/sha20.11,digest0.11,cbc/ctr/desnew majors,pkcs5/pkcs8stable, and the RC familyecdsarc.18 /rsarc.18 /elliptic-curverc.32 /ed25519-dalekpre.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.4andsignal-hook@0.3.18are pulled in byratatui-coreandcrosstermrespectively and will resolve themselves whenever those upstream crates bump. opentelemetry-otlp0.32 errors when anhttps://endpoint is configured without an explicittls-ringortls-aws-lcfeature. We did not enable a TLS feature in this release because all current uses arehttp://; users running the audit exporter against a TLS-protected OTLP collector will need to either disable the warning by switching tohttp://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
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_PASSWORDenvironment 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
--passwordonce up-front in the dispatcher and share the secret across all parallel SSH tasks viaArc<Password>(backed bysecrecy::SecretString, auto-zeroized on drop) (#201, closes #200). The prompt now runs before any executor or indicatifMultiProgressis initialized, so the terminal is in a clean state when reading the password. The pre-collected secret is threaded throughParallelExecutor,ExecutionConfig,ConnectionConfig,FileTransferParams, the jump-hostdetermine_auth_methodpath, the download glob-resolution path (SshClient::connect_and_execute_with_host_check), the SFTP*_with_jump_hostshelpers, and the legacyexecute_command_with_forwardingpath. The in-taskrpassword::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-passwordis passed to subcommands where it has no effect (ping,upload,download,list,cache-stats, and interactive shells) (#201, follow-up #200). The warning routes througheprintln!sobssh ... ping | grep ...pipelines stay clean.execand the SSH-mode interactive path continue to honor-Sas 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
--passwordrace 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
-Sbeing silently dropped byping,upload, anddownload(#201, closes #200). The dispatcher only readcli.sudo_passwordin theexecand interactive branches, so users could pass-Sto other subcommands without any error or feedback that the flag had no effect. - Fix the
-Signored-warning landing on stdout (#201, follow-up). The previoustracing::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.rsmodule mirroring the existingsrc/security/sudo.rsdesign: aPasswordwrapper backed bysecrecy::SecretStringwith auto-zeroize on drop,prompt_password()with a non-host-specific prompt,get_password_from_env()readingBSSH_PASSWORD, andget_password(warn_env)combining both. Wrapped inArc<Password>for sharing across per-node tasks without duplicating secret material. AuthContext(src/ssh/auth.rs) gains apassword: Option<Arc<Password>>field and awith_pre_collected_password()builder.password_auth()consumes the pre-collected value when available.- Migrated four call sites from
lazy_static!/once_cell::sync::Lazy/OnceCelltostd::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)torand::random_range(range);rand0.10 was already a direct dependency. - Migrated five callers of
std::io::stdin().is_terminal()/stdout().is_terminal()from the unmaintainedattycrate. - 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 inssh/auth.rsconfirming the builder semantics and thatpassword_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 withtokio::signal::ctrl_c),directories(replaced withdirs, already used in 16 other sites),signal-hook0.4.4 (downgraded to 0.3 to share the cratecrosstermalready pulls in viasignal-hook-mio), plus the macOSobjc2/block2/dispatch2chain pulled in byctrlc. Three more crates (lazy_static,once_cell,fastrand) remain transitively but are no longer directly referenced.Cargo.lockloses 76 lines including the entiresignal-hook0.4.4 subtree. - Replace
attywithstd::io::IsTerminal(#198): drops RUSTSEC-2024-0375 (unmaintained) and RUSTSEC-2021-0145 (unsound unaligned read). - 33 transitive patch bumps from
cargo updatewithin 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.tomlignore with an explanatory comment (#198). Bothrsa0.9.10 (viassh-key0.6.x) andrsa0.10.0-rc.17 (via the vendoredbssh-russhfork) are affected, and no fixed upstreamrsarelease exists. Bumping to 0.10.0-rc.18 conflicts with thebssh-russhpkcs5 = "=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
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_recursivepreviously loaded the entire local file into aVec<u8>viatokio::fs::readbefore callingwrite_all, anddownload_file/download_dir_recursivecalledread_to_endinto a pooled buffer plus aclone()to a separateVecbefore 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 astream_copy()helper looping on 255 KiB reads/writes through the existingAsyncRead/AsyncWriteimpls ontokio::fs::Fileandrussh_sftp::client::fs::File. Buffer size matchesMAX_WRITE_LENGTHso each chunk maps to one SFTP packet without further fragmentation.Verified locally on macOS arm64 against
bssh-serverv2.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
fstatsize 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_SIZEfrom 64 KiB to the 255 KiB SFTP standard (#197). The server previously hard-capped every SFTPREADreply at 64 KiB regardless of what the client requested, whilebssh-russh-sftpand OpenSSH'ssftp-serverboth useMAX_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 atMAX_HANDLES = 1000per 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-sftppacket 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
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.rootconfiguration field (#186): SCP transfers now honor a chroot setting separate from SFTP. When unset, SCP falls back tosftp.root, so a single top-level chroot setting governs both subsystems unless an admin explicitly wants them split.- Internal fork of
russh-sftpasbssh-russh-sftp(#188) with aserde_bytesperformance fix forSSH_FXP_WRITE/SSH_FXP_DATApackets. The upstream serde derive routedVec<u8>throughdeserialize_seq(byte-by-byte), accounting for ~42% of server CPU during 1 GiB SFTP uploads inperfprofiling. Annotating thedatafields with#[serde(with = "serde_bytes")]and implementing wire-compatibleserialize_byteson the SFTPSerializerroutes through the existing bulkdeserialize_byte_buf/try_get_bytespath. 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 OpenSSHsftp-serverfrom ~26% to ~5%.
Improvements
- Default file-transfer behavior is no longer chrooted to the user's home directory (#186). With
sftp.root/scp.rootunset (the default), absolute client paths are honored verbatim and relative paths resolve from the user's home directory, matching OpenSSHsftp-server/scpdefaults. Deployments that intentionally want chroot-at-home-dir must now setsftp.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 whenpending_datais non-empty (upstream russh #693). Refactoredsync-upstream.shto iteratepatches/and reverse-apply--dry-runfirst so already-merged patches are auto-skipped. - Switched the top-level
russh-sftpdependency from crates.iorussh-sftp = "2.1.1"to the vendoredbssh-russh-sftppackage; existinguse russh_sftp::...imports continue to work unchanged.
Bug Fixes
- bssh-server SCP/SFTP path doubling on absolute client paths (#186):
ScpHandler::resolve_pathandSftpHandler::resolve_path_staticpreviously re-rooted every absolute client path under the user's home directory, soscp local user@host:/home/work/file.binwrote to/home/work/home/work/file.binandbssh upload local /abs/remote.binfailed withNo such file. The resolver now treats absolute client paths verbatim when no chroot is configured and rejects out-of-chroot absolute paths withpermission_deniedwhen 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_filenow consultstarget_is_directory(parsed from-d/-r) and the filesystem state of the resolved target.scp local.bin user@host:/tmp/dest.binnow writes to/tmp/dest.bininstead of/tmp/dest.bin/local.bin. Directory destinations (/tmp/dir/, existing directory, or-d/-rflag) keep the previous filename-appending behavior. - Configured
sftp.rootis no longer dead code (#186): the handler-construction sites inSshHandlerpreviously hard-codeduser_info.home_diras the chroot root and ignoredconfig.sftp.rootentirely. Settingsftp.rootin the YAML configuration now actually changes the SFTP chroot. The same plumbing now exists forscp.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 targetchroot/escape/newfileand haveopen(...)/create_dir(...)follow the symlink, writing outside the chroot. BothScpHandler::resolve_pathandSftpHandler::resolve_path_staticnow 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/checkoutv4 → v6actions/cachev4 → v5actions/upload-artifactv4 → v7apple-actions/import-codesign-certsv3 → 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_withcheck in the relative-path chroot resolver afterParentDirclamping (already guarded byif resolved != root). - Added focused unit tests for
scp.root/sftp.rootprecedence rules and the no-chroot default. - Added
tests/scp_sftp_path_resolution_test.rscovering 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 throughrealpath) 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, andbssh-server.8into 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.rootandscp.rootinbssh-server.8configuration sections, and add intermediate-directory-symlink chroot protection to SECURITY CONSIDERATIONS.
Dependencies
tokio1.52.1,clap4.6.1,tracing0.1.44,lru0.17,uuid1.23.1,tokio-util0.7.18bssh-russh:aws-lc-rs1.16.3,ecdsarc.17,elliptic-curverc.31,p256/p384/p521rc.9,tokio1.52.1- Pinned
pkcs5="=0.8.0-rc.13"becausepkcs80.11.0-rc.11 still calls the rc.13-eraParameters::recommendedAPI; stable 0.8.0 renamed it togenerate_recommendedand 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.rootset, absolute client paths are honored verbatim (matching OpenSSH defaults). Deployments that relied on the implicit confinement must explicitly setsftp.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
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()ininteractive_signal.rsnow delegates toforce_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 viaTerminalGuard) 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_cleanupsafe to call from the panic hook: switched the globalTERMINAL_MUTEXacquisition totry_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 viaunwrap(). The lock only serializes concurrent teardown; the underlying stdout writes anddisable_raw_modeare individually safe.
CI/CD Improvements
- Trigger Homebrew formula update only after the official release:
release.ymlnow callsupdate_homebrew_formula.ymlviaworkflow_callfrom thepublish-releasejob (which converts pre-release to official) instead ofworkflow_runfiring on every Release workflow completion (including pre-release builds).workflow_dispatchis kept for manual runs. - Prevent double-trigger of the release workflow: removed the
publishedevent type from the release workflow trigger list. The workflow was previously firing twice — once onprereleasedfromgh release create --prerelease, and again onpublishedwhenpublish-releaseconverted the pre-release to official.publish-releasealready handles that conversion, andworkflow_dispatchcovers manual runs.
Technical Details
- Added unit tests for
force_terminal_cleanup()insrc/pty/terminal.rscovering idempotency, poisoned-mutex resilience, and held-mutex resilience. Tests use localMutexinstances rather than the globalTERMINAL_MUTEXso 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
Full Changelog: v2.1.1...v2.1.2
v2.1.1
New Features
None
Improvements
- Improved Launchpad PPA packaging for Rust 2024 edition
- Added
debian/sanitize-vendor.pyfor vendored crate checksum sanitization - Added
.pycfiles to.gitignore
Bug Fixes
- bssh-server panic on client connection:
new_client_with_addrcalledblock_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-blockingAuthRateLimiter::try_is_banned()usingRwLock::try_read(). (#185) - bssh-server auth rejection after successful verification: All
SshHandlerconstructors created a localSessionInfothat was never registered withSessionManager. After public key or password verification succeeded,authenticate_session()returnedSessionNotFound, rejecting the client. Fixed by deferring session creation to the authentication flow viaSessionManager::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 usingtokio::sync::RwLock::try_read()for use in sync trait contexts - Changed
SshHandlerconstructors to initializesession_infoasNone; sessions are now registered withSessionManagerduring 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
bssh v2.1.0
This release migrates bssh to the Rust 2024 edition and hardens test-environment handling.
New Features
EnvGuardRAII wrapper for safe environment variable handling in tests (#179, #181). Wrapsstd::env::set_var/remove_varbehind a Drop-based restore so tests cannot leak environment mutations across runs. Single source of truth insrc/test_helpers/env_guard.rs, shared with integration tests viatests/common/mod.rs.
Improvements
- Rust 2024 edition migration: both the
bsshmain package and the bundledbssh-russhcrate now target the 2024 edition. - 2024 edition clippy improvements: collapsed 38 nested
if letstatements into guard-clause form (if let Some(x) = y && condition) acrossbssh-russhclient/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 withEnvGuard::set/EnvGuard::removeplus#[serial]from theserial_testcrate. - 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 mutbindings inif letpatterns insrc/server/handler.rsandsrc/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.mddocumenting theEnvGuardsoundness contract, when to use#[serial]vs#[serial(key)], and how integration tests accessEnvGuardviatests/common/mod.rs. EnvGuarddocuments 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
EnvGuardDrop-semantics tests. - Validation:
cargo check --lib --testswent from 120 E0133 errors to 0;cargo clippy --all-targets --all-features -- -D warningsis clean;cargo test --libpasses 1194 tests.
Dependencies
- Pinned
bytesto v1.11.1.
Breaking Changes
None
Known Issues
None
Full Changelog: v2.0.1...v2.1.0
v2.0.1
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
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
Hostalias reference injump_hostconfiguration - 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-cexamples 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 toNonewhen keepalive is enabled, so the keepalive mechanism alone decides peer liveness- TCP-level
SO_KEEPALIVE(viasocket2::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
SshConnectionConfigat theConnectionConfigboundary; it now flows throughconnect_direct/connect_via_jump_hosts/ the jump chain, soserver_alive_intervalactually takes effect in non-interactive runs
- bssh-server: Use consistent source package name in the Debian
controlfile so dual-package builds resolve correctly - bssh-server: Use type inference for
ioctlto 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 setsinactivity_timeoutexplicitly based on keepalive state;to_tcp_keepalive()derives kernel TCP keepalive params from the same settingsClient::connect_with_configwas rewritten aroundrussh::client::connect_stream, building theTcpStreammanually soSO_KEEPALIVEcan be applied before the SSH handshake- Key-generation call sites now pass
&mut rand::rng()(rand 0.10's thread RNG implements the newrand_core 0.10CryptoRngtrait directly), dropping the previousssh_key::rand_core::OsRngworkaround
Dependencies
- Synced
bssh-russhfork with upstreamwarp-tech/russhv0.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.6der 0.8,sec1 0.8,pkcs8 0.11.0-rc.11,pkcs5 0.8.0-rc.13,spki 0.8.0-rc.4ml-kem 0.3.0-rc.1(addsmodule-lattice 0.2)ssh-key 0.6.18(viainternal-russh-forked-ssh-key)
- rand 0.8 → 0.10 stable (via
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-serveris no longer bundled withbssh— 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
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 frombrew install bssh) - For Debian:
apt install bssh-server(separate fromapt install bssh) - Existing bssh installations continue to work unchanged
- For Homebrew:
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