fix(security): add SSH session token expiry, connection limits, and lifecycle cleanup#182
Merged
johntmyers merged 2 commits intomainfrom Mar 9, 2026
Merged
Conversation
…ifecycle cleanup Closes #22 SSH session tokens previously had no TTL and remained valid indefinitely. This adds configurable token expiry (default 24h), per-token (10) and per-sandbox (20) concurrent connection limits, session cleanup on sandbox deletion, and a background reaper for expired/revoked sessions.
drew
approved these changes
Mar 9, 2026
drew
pushed a commit
that referenced
this pull request
Mar 16, 2026
…ifecycle cleanup (#182) * fix(security): add SSH session token expiry, connection limits, and lifecycle cleanup Closes #22 SSH session tokens previously had no TTL and remained valid indefinitely. This adds configurable token expiry (default 24h), per-token (10) and per-sandbox (20) concurrent connection limits, session cleanup on sandbox deletion, and a background reaper for expired/revoked sessions. * fix(security): lower per-token concurrent connection limit from 10 to 3 --------- Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #22
Security Fix
Summary
SSH session tokens previously had no TTL and remained valid indefinitely until explicitly revoked. This change adds configurable token expiry, enforces concurrent connection limits at two levels, cleans up sessions on sandbox deletion, and runs a background reaper for expired/revoked sessions.
Limits Imposed
ssh_session_ttl_secs, 0 = disable)Why two connection tiers matter: A per-token limit alone can be bypassed by creating many tokens. The per-sandbox limit is a second defense that caps total exposure regardless of how many tokens exist. Together they provide defense in depth.
Why TTL does not break UX: Every CLI command (
connect,exec,forward,sync-up,sync-down,ProxyCommand) creates a fresh session token on every invocation viassh_session_config()atcrates/navigator-cli/src/ssh.rs:31. The token is consumed once to establish the CONNECT, then the tunnel lives independently viacopy_bidirectional. A 24h TTL is orders of magnitude longer than any single-use window. Background port forwards are also unaffected — TTL only gates new connections, not existing tunnels.Severity Assessment
ssh_tunnel.rs(tunnel validation),grpc.rs(session creation/deletion),navigator.proto(SshSession schema),config.rs(TTL config)Changes Made
proto/navigator.proto: Addedexpires_at_msfield (tag 7) toSshSessionand (tag 8) toCreateSshSessionResponse. Backward-compatible additive change — existing sessions decode withexpires_at_ms = 0(no expiry).crates/navigator-core/src/config.rs: Addedssh_session_ttl_secsconfig field (default 86400 = 24h). Set to 0 to disable expiry.crates/navigator-server/src/grpc.rs: Compute and setexpires_at_mson session creation. Include in response. Clean up all SSH sessions when a sandbox is deleted.crates/navigator-server/src/ssh_tunnel.rs: Checkexpires_at_msduring tunnel validation (0 = no expiry for backward compat). Enforce per-token (3) and per-sandbox (20) concurrent connection limits with in-memory counters. Addedspawn_session_reaperbackground task (hourly) to delete expired/revoked sessions.crates/navigator-server/src/lib.rs: Addedssh_connections_by_tokenandssh_connections_by_sandboxcounters toServerState. Launch session reaper at startup.architecture/gateway-security.md: Updated SSH Tunnel Authentication section with session lifecycle, connection limits, and updated residual risks table.Tests Added
crates/navigator-server/src/ssh_tunnel.rs— 9 tests covering:decrement_removes_entry_at_zero,decrement_reduces_count,decrement_missing_key_is_noop(connection counter logic)per_token_connection_limit_enforced,per_sandbox_connection_limit_enforced(limit constants)reaper_deletes_expired_sessions,reaper_deletes_revoked_sessions,reaper_preserves_zero_expiry_sessions(reaper with real SQLite store)expired_session_is_detected,future_session_is_not_expired,zero_expiry_is_not_expired(expiry validation logic)Documentation Updated
architecture/gateway-security.md: Added Session Lifecycle and Connection Limits sections under SSH Tunnel Authentication. Updated Residual Risks table to reflect that SSH tokens now have TTL and connection limits.Verification
cargo test --workspace)custom_imageunrelated to this change)cargo fmt --checkpassescargo clippy --workspace --all-targetspasses (no new warnings)