Skip to content

Add per-host SOCKS5 proxy routes - #336

Merged
bifrost0x merged 13 commits into
mainfrom
feature/socks5-proxy-routes
Nov 14, 2026
Merged

bifrost0x merged 13 commits into
mainfrom
feature/socks5-proxy-routes

Conversation

@bifrost0x

@bifrost0x bifrost0x commented Nov 14, 2026 •

Copy link
Copy Markdown
Owner

Problem and outcome

Closes #334.

Saved SSH hosts can now select a reusable, user-owned SOCKS5 proxy. Terminal connections and their session SFTP channels use that route, including password authentication at the proxy, reconnects, and persistent sessions. Direct and Jump Host connections retain their existing behavior.

The implementation uses a small standard-library SOCKS5 CONNECT client with a bounded handshake, then passes the resulting socket to the existing Paramiko transport. Proxy metadata has its own per-user store; passwords use the existing encrypted credential service. No dependencies were added.

Scope and security boundaries

  • Proxy endpoints use the existing pinned network-policy connection path, with no special loopback exception. Target IPs and locally resolved names are validated before CONNECT.
  • Remote DNS uses a separate, exact-name SOCKS_REMOTE_DNS_ALLOWLIST. With internal blocking enabled, names outside it use a validated local IP. Delegated DNS cannot constrain the proxy's final target IP; this trust boundary is documented.
  • Routes are resolved live from the current user's store. Missing, changed, foreign, malformed, or failed proxies cannot fall back to a direct connection. The same rule covers history, reconnect, probes, and secondary launch paths.
  • Target SSH host-key trust and revocation remain unchanged. SOCKS username/password authentication itself is unencrypted between WebSSH and the proxy; the editor and documentation explain this.
  • Direct/Jump credential fingerprints remain compatible. SOCKS credential bindings include the route and DNS policy. Storage and session changes are additive, with coordinated quotas, recovery, reference-protected deletion, and backup coverage for metadata only.
  • The proxy manager, route selector, credential prompts, overview, recovery guidance, and errors support all six locales, keyboard navigation, and mobile layouts.

New optional settings: SOCKS_REMOTE_DNS_ALLOWLIST and SOCKS_PROXY_MAX_RECORDS. Existing defaults remain unchanged.

Standalone Quick SFTP, Automation, SOCKS-to-Jump chaining, Tailscale SSH through SOCKS, and route-preserving ssh_config export remain unsupported and fail explicitly. They are separate follow-ups.

Compatibility decisions

The current source uses CONNECTION_CONFIG_MAX_BYTES for the combined store quota and CONNECTION_STORE_MAX_BYTES for each file; both contracts are preserved. Existing Jump DNS allowlist parsing is also preserved while the new SOCKS setting receives strict startup validation.

The current export format is JSON, rather than ssh_config. Exported SOCKS hosts carry an explicit omitted-route warning, and reimport is blocked so route loss cannot create a direct connection. ProxyCommand remains rejected.

Validation

Current revision: dfb741ec1b92beb0ad2f64fddddb71ae97475d30, including the updated main branch. All 31 executed GitHub checks passed; the three release-only jobs were skipped. The complete test workflow, container security workflow, and CodeQL workflow are green.

The CI follow-up fixes a race in the SOCKS test peer: an immediately rejected client could finish before the server accepted its queued connection, and cleanup closed the listener too early. Cleanup now waits for the peer before closing the listener. A deterministic regression reproduced the original failure and passes with the fix; existing timeouts and error assertions remain intact. All 91 SOCKS protocol tests passed locally on Python 3.11 and 3.14 on Windows and Python 3.12 with two workers on Linux. The previously failing Python 3.14 CI shard passed with 1,774 tests and one skip. This follow-up changes test code only.

Initial feature revision: cf2e0a1eba59f50695cb448b494c347a5241ed9c. Runtime, backend tests and dependencies are byte-for-byte unchanged from 97f855cb3d926350f89a8f11aab7b1b62ad72699, on which the full Python, OpenSSH and production-image gates passed. The final commit adds one readiness wait to a browser-test helper; JavaScript and the complete browser gate are rerun on the final commit.

  • JavaScript: 1,134 passed; one existing Linux-only test skipped on Windows. Lint and all 16 vendored assets passed.
  • All three dependency lock checks passed.
  • Terminal load: both cases passed, retaining 20 active TUIs without reconnects.
  • Disposable OpenSSH/SOCKS integration: 55 passed, including all eight SOCKS integration cases.
  • Production AMD64 image at 97f855c and hardened smoke passed: single-worker gthread, readiness, runtime restrictions, online/offline backups, restore/SQLite, restart, and clean shutdown.
  • Python 3.11.15 and 3.14.3: 5,321 passed, 36 skipped and five subtests passed on each version. Integration fixtures ran separately through the OpenSSH runner. Skips cover unavailable POSIX capabilities on Windows and an unconfigured external Redis URL; Python 3.14 also reports 11 existing dependency deprecation warnings.
  • Complete browser suite on cf2e0a1: 908 passed (906 shared cases across eight isolated shards plus two exclusive terminal-load cases), with no failures, skips or retries.

Regression coverage includes hostile/fragmented SOCKS replies, total deadlines and cancellation, authentication downgrade rejection, network-policy and DNS matrices, user isolation, credentials and unchanged legacy hashes, corrupt/oversized storage, route changes and deletion, no-direct-fallback launch paths, and real SSH/SFTP through the proxy.

Direct credential-payload compatibility, SOCKS capture fixtures, and missing-proxy alert dismissal have regression coverage. The editor preserves the exact saved SSH key before its inventory loads, verified with a failing-before/passing-after regression. Browser fixtures wait for actual inventory/readiness and use user-visible command filters. Capture, credential, focus and viewport assertions remain intact; all 16 isolated repeated scenarios passed without retries.

Python checks use isolated Windows virtual environments and temporary directories; the complete browser suite runs in isolated Linux containers. Graphify could not run because this checkout has neither its graph nor its interpreter marker.

Delivery

This PR implements the feature; it does not publish a release or change a running installation. No merge, release, or deployment was performed.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-14T10:47:01.374973Z cf2e0a1 PR opened
🔒 Security Review ✅ Completed 2026-11-14T10:49:34.065645Z cf2e0a1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@bifrost0x
bifrost0x merged commit 0534360 into main Nov 14, 2026
34 checks passed
@bifrost0x
bifrost0x deleted the feature/socks5-proxy-routes branch November 14, 2026 12:38
@github-project-automation github-project-automation Bot moved this from Backlog to Done in WebSSH Roadmap Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Feature Request: Support SOCKS5 Proxy for Individual SSH Connections

1 participant