main #1
Reference in New Issue
Block a user
No description provided.
Delete Branch "200mill/rsh:main"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
PR #1 Review:
feat: rshc: add sshserve(+fix: security)Neat idea — SSH-to-rsh bridging is a natural UX improvement. But there are some serious issues that need addressing before merge.
🔴 Critical
1. Hardcoded SSH private key in source code
The
fix: securitycommit added a hardcoded private key:This is a showstopper:
serve-sshinstanceetx@etx-desktop— this is someone's personal key, not a generated ephemeral oneserve-sshinstance worldwide will share the same host key — host key verification is completely uselessconfig.keys.push(key)+config.keys.push(host_key)), so clients will see two host keys and one is publicly knownFix: Generate an ephemeral host key at startup (
russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519)) and optionally accept a--host-keyflag for a persistent one. Never commit private keys.2. First commit was a completely open SSH server
The initial
featcommit had all three auth handlers returningOk(Auth::Accept):While the
fix: securitycommit patched this, the PR history shows an unauthenticated SSH reverse-shell was pushed to a public repo. Security-sensitive code should be correct from the first commit, not "fixed later."🟠 Important
3. Operator key used as host key — dual-purpose key, identity leak
The operator's SSH private key serves as both the rsh-backend auth key and the SSH server's host key. Anyone connecting to
serve-sshlearns the operator's key fingerprint. Compromising one key compromises both authentication surfaces. Ifserve-sshis bound to a public interface, the operator's key identity is exposed to scanners.Consider generating a separate host key at startup or accepting a
--host-keypath.4. New
AuthedClientper SSH channel — resource exhaustion riskspawn_shell()callsAuthedClient::connect(&self.cfg)for every channel. Each SSH shell/exec request opens a brand-new WebSocket to the rsh-backend. A single SSH session with multiple channels (e.g., concurrentexeccommands) will open many backend connections. This could exhaust backend resources or hit connection limits. Consider sharing oneAuthedClientper SSH session.5.
channel_closesendsKill— always force-kills the remote shellchannel_eofalready sendsEof(graceful close), butchannel_closeimmediately sendsKill. If the SSH client closes the channel gracefully (EOF → close), the remote shell gets bothEofand thenKill. Consider only sendingKillif EOF hasn't been sent, or just rely onEof+drop_stream.6. No graceful shutdown
The accept loop runs forever with no way to shut down cleanly. A
tokio::select!onlistener.accept()+ a cancellation token (or ctrlc signal) would allow clean teardown.🟡 Minor / Style
7. Accept errors silently swallowed —
Err(_) => continueon listener accept. Some errors (EMFILE, ENFILE) indicate resource exhaustion and should at least be logged.8.
auth_rejection_time_initial: Some(Duration::from_secs(0))— This skips the initial delay on auth failure, removing a timing-sidechannel mitigation. Since only pubkey auth is advertised, the risk is low, but worth a comment explaining why.9.
String::from_utf8_lossyinexec_request— Silently replaces invalid bytes with\u{fffd}. For commands this is usually fine, but worth a brief comment.10. No SSH
signalhandler — SSH signal requests (SIGINT, SIGTERM, etc.) aren't forwarded to the remote shell. Not a blocker but a functional gap.11. Double blank line in main.rs — Extra blank line between dispatch arms after the
ServeSshmatch arm.12. CLAUDE.md bundled with feature PR — The 140-line CLAUDE.md is tangential to the serve-ssh feature. Consider splitting into a separate commit/PR.
Summary
The hardcoded private key is an automatic blocker, and the initial open-auth commit pattern is a serious concern for a reverse-shell project. Requesting changes — at minimum, the hardcoded key must be removed and proper host key generation implemented.