Release candidate — soaking for community testing before 1.12 becomes latest. Please try it and report anything odd in #610.
Try it:
- One-line install:
curl "https://i.jpillora.com/jpillora/chisel@v1.12.0-rc1!" | bash - Docker:
docker pull jpillora/chisel:1.12.0-rc1(orghcr.io/jpillora/chisel:1.12.0-rc1) - Binaries/packages: assets below
Upgrading from 1.11.x? Read the Upgrading to 1.12 README section. What follows is from 1.12-changes.md, the verified UX & compatibility review of this line.
1. Breaking changes for existing users
a. SOCKS + --authfile — already on master, branch adds the missing docs
Enforcement (UserAddr() → "socks", channel-level ACL gate) shipped in v1.11.7 (927abde), so branch-vs-master this is not a new break — but today's released chisel has an undocumented breaking change: its README/--help say nothing about the socks token. The branch fixes that in five places (server help, client help, README auth section, SOCKS guide, 1.12 changelog) including the migration line "existing authfiles which should allow SOCKS5 must add an entry matching socks". This documentation is arguably the most valuable UX content on the branch.
The original review flagged a diagnosability gap here: a denied user's server-side trace was Debugf("Denied connection to socks (ACL)"), invisible without -v, making "socks stopped working after upgrade" the #1 anticipated support ticket. This is now fixed — commit 3c66f9b raised it to Infof("Denied connection to %s (ACL)", hostPort) (share/tunnel/tunnel_out_ssh.go:57), so operators see socks ACL denials at the default log level.
b. Truncated legacy MD5 fingerprints rejected — branch-new, loud
Master's verifyLegacyFingerprint used strings.HasPrefix, so --fingerprint a5:b3 matched any key with that MD5 prefix (~1-in-65k spoof risk). The branch requires the full 16-octet colon form (client/client.go:248). Affected users fail loudly at connect with Invalid fingerprint (...), and the preceding info line helpfully prints the correct SHA256 fingerprint to migrate to. SHA256 fingerprints were always exact-match — only MD5 stragglers with shorthand configs are affected. Documented in README Security, and the 1.12 changelog now lists it as breaking (added in c4038c3).
c. --auth without a colon is now a fatal startup error — branch-new, loud
On master, --auth nocolon silently degraded (server: user never registered → effectively no auth; client: empty credentials). That's a security footgun, and the branch turns it into invalid auth string, expected <user>:<pass> at startup on both sides. Anyone hit by this was already running something other than what they believed. Documented in README, and the 1.12 changelog now lists it as breaking (added in c4038c3).
d. Exit code on --max-retry-count exhaustion: 0 → non-zero — branch-new, silent for automation
client_connect.go now returns connection attempts exhausted on give-up (ctx-cancel/Ctrl-C still exits 0). Correct behavior, but it's the one change scripts and Restart=on-failure units experience with no error message to notice — the semantics of $? just flip. It is in the 1.12 changelog, which is the right mitigation.
2. Quiet behavior changes (non-breaking, but observable)
- Reconnect pacing: backoff floor moves 100ms → 1s (new
--min-retry-interval). Softer thundering-herd on server restarts; individual reconnects marginally slower. - Dead connections actually die: pings now time out (
CHISEL_PING_TIMEOUT, default = keepalive interval, so ~50s at defaults vs 15–60 min of kernel retransmit limbo on master). Sleep/wake and NAT-timeout hangs become visible reconnects in logs. Old peers reply to pings, so mixed versions are fine. - Honest connect failures: exit-side dial now happens before channel accept (
CHISEL_DIAL_TIMEOUT30s). Apps see "connection refused/timeout" instead of master's instant-success-then-EOF. Strictly better UX, but tools that measured "connect success" will notice. - Half-close propagation:
shutdown(SHUT_WR)traverses the tunnel (share/cio/pipe.go), fixing netcat-style pipelines and rsync. Falls back to full-close against old peers — no hangs, just old behavior. - SIGTERM is graceful: first signal drains (HTTP drain
CHISEL_SHUTDOWN_GRACE5s, under docker's 10s default), second forces exit. Master died instantly on SIGTERM. - New info-level logs: session
Open (user=… addr=… remotes=…)/Close (… duration=…)andLogin failed for user X (ip)— failed logins are finally fail2ban-able. Two side effects: log parsers keyed on the old debugClosed connectionneed updating, and tunnel endpoints now appear in default-level logs (mild privacy consideration for shipped logs). - Authfile reloads actually work: the watcher survives vim renames, truncation, and k8s ConfigMap symlink swaps, with 100ms debounce; ACLs re-resolve per new channel; the
--authuser is pinned across reloads and wins name clashes; removed users lose new tunnels but established ones aren't cut (documented). Operators who habitually restart after edits will find edits now apply live. - Unanchored ACL patterns warn at every load, per pattern, unsuppressible. Common
.*-style files keep working but get noisy — the warning is doing its job, since unanchored patterns really do over-match. - WS read cap 64KB pre-auth (
CHISEL_WS_READ_LIMIT, 0 disables) on both sides. Max legit SSH packet is ~35KB, so ~2× headroom; the only tail risk is a pathological config payload (thousands of remotes on one client), and the env knob is the escape hatch. - UDP at the flow cap: master permanently blackholed flows past 100; branch sweeps idle over-cap flows (
CHISEL_UDP_DEADLINE15s), so DNS-heavy exit nodes recover instead of wedging. - Nicer failure edges: partial
BindRemotesfailure now unbinds earlier listeners (no zombie ports); bad--keyfileerrors no longer echo raw key material into logs;go installbuilds report real versions;3000/UDPuppercase now parses.
3. Library consumers (Go API)
No compile-breaking signature changes in client, server, or share/.... Additive: client.Config.MinRetryInterval, Client.Ready(ctx), Tunnel.Ready, UserIndex.PinUser. Behavioral: NewServer returns errors where master called log.Fatal inside (a win for embedders), Remote.UserAddr() returns "socks" for forward-socks, L4Proto lowercases — only code depending on those exact outputs would notice.
4. Mixed-version deployments
Protocol string is unchanged (chisel-v3), and no handshake changes were found.
- v1.11.x client ↔ 1.12 server: works. Socks ACL is server-side and already live since v1.11.7; WS cap and ping timeout are old-peer-safe.
- 1.12 client ↔ v1.11.x server: works, degrading gracefully — no half-close benefit, no dial propagation, no server-side changes;
socks5://proxy scheme is client-local.