clawmates-node: fix rustls CryptoProvider panic + non-intrusive Tailscale
ci / gates (push) Failing after 6s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped

Two bugs surfaced running the daemon on a real node:
- Linking cm-sandbox (bollard) brought a second rustls provider into the graph,
  so rustls couldn't auto-pick one and panicked at the WSS TLS handshake. Install
  the ring provider explicitly at startup (rustls dep + install_default()).
- The daemon auto-ran `tailscale set --ssh`, which tries to reroute the user's
  live SSH session and aborts ("will result in your session disconnecting"). Now
  Tailscale is only touched when an auth key is explicitly passed (opt-in), with
  --accept-risk=lose-ssh to avoid the interactive abort.

Rebuilt + re-hosted both binaries (linux-amd64, darwin-arm64).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 16:10:42 -07:00
co-authored by Claude Opus 4.8
parent 417d858bdd
commit cfa16751c5
3 changed files with 23 additions and 11 deletions
Generated
+1
View File
@@ -743,6 +743,7 @@ dependencies = [
"cm-sandbox", "cm-sandbox",
"futures", "futures",
"portable-pty", "portable-pty",
"rustls",
"serde", "serde",
"serde_json", "serde_json",
"sysinfo", "sysinfo",
+3
View File
@@ -20,6 +20,9 @@ sysinfo = "0.33"
portable-pty = "0.8" portable-pty = "0.8"
base64 = "0.22" base64 = "0.22"
cm-sandbox = { path = "../../cm-sandbox" } cm-sandbox = { path = "../../cm-sandbox" }
# Linking cm-sandbox (bollard) brings a second rustls provider into the graph, so
# rustls can't auto-pick one — we install `ring` explicitly at startup.
rustls = { version = "0.23", default-features = false, features = ["ring"] }
[lints] [lints]
workspace = true workspace = true
+19 -11
View File
@@ -33,6 +33,9 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// The dep graph enables both rustls crypto providers (tungstenite + bollard),
// so rustls can't auto-pick — install one explicitly before any TLS.
let _ = rustls::crypto::ring::default_provider().install_default();
let (server, token, ts_authkey) = parse_args(); let (server, token, ts_authkey) = parse_args();
if server.is_empty() || token.is_empty() { if server.is_empty() || token.is_empty() {
eprintln!("usage: clawmates-node --server <https://gateway> --token <token> [--tailscale-authkey <key>]"); eprintln!("usage: clawmates-node --server <https://gateway> --token <token> [--tailscale-authkey <key>]");
@@ -407,17 +410,22 @@ async fn sb_op(op: &str, v: &Value) -> (bool, String) {
} }
/// Best-effort: join the user's tailnet (BYO Tailscale) and enable Tailscale SSH /// Best-effort: join the user's tailnet (BYO Tailscale) and enable Tailscale SSH
/// so they can reach this node keylessly. Failures are non-fatal — the WSS /// so they can reach this node keylessly. ONLY when an auth key is explicitly
/// control channel works regardless. /// passed — enabling SSH unprompted can drop the user's current SSH session.
/// `--accept-risk` avoids the interactive abort when they're on Tailscale.
/// Non-fatal: the WSS control channel works regardless of Tailscale.
fn tailscale_up(authkey: &str) { fn tailscale_up(authkey: &str) {
if !authkey.is_empty() { if authkey.is_empty() {
let _ = std::process::Command::new("tailscale") return;
.args(["up", "--authkey", authkey, "--ssh", "--accept-routes"])
.status();
} else {
// Already authed by the user? Just make sure SSH is on.
let _ = std::process::Command::new("tailscale")
.args(["set", "--ssh=true"])
.status();
} }
let _ = std::process::Command::new("tailscale")
.args([
"up",
"--authkey",
authkey,
"--ssh",
"--accept-routes",
"--accept-risk=lose-ssh",
])
.status();
} }