Repository navigation
Releases: doedja/warren
Releases · doedja/warren
Release list
v0.4.11
Security and robustness release from a full-codebase audit. Update nodes
(auto-update picks it up) and redeploy hubs.
Security
- Node TLS pinning now verifies the handshake signature. The pinned-cert
verifier accepted any CertificateVerify, and the hub's self-signed cert is
public, so an active man-in-the-middle could present the real cert with its
own key, complete the handshake, and read the tunnel (including the
enrollment token in the node's Hello). Signature verification now runs
through the real rustls crypto provider; pinning is unchanged otherwise. - Proxy auth fails closed: a store error (locked or broken database) used to
disable authentication for that connection, briefly turning the pool into an
open relay. Node-name uniqueness checks fail closed the same way. - The enrollment replay-guard map is pruned past the timestamp skew window; it
previously grew without bound on attacker-minted keypairs. - Node identity and hub TLS key files are created with mode 0600 from the
start (no world-readable window between write and chmod). - The unix self-update command and systemd unit now shell-quote the hub
address, token, and fingerprint. - Release workflow actions are pinned to commit SHAs (the job holds a write
token, so a hijacked action tag could have published attacker binaries with
matching checksums). CI runs with a read-only token. install.shruns inside a function called on the last line, so a truncated
curl | shcannot execute a partial script; Termux boot-script arguments
are single-quoted.- Geo/public-IP lookups (plaintext HTTP) cap the response at 64 KB.
- The dashboard asks for confirmation before deleting the last proxy user
(zero users disables proxy authentication entirely).
Fixed
- Two nodes enrolling concurrently with the same name could both be approved
and then permanently reject each other on reconnect; the name check and
approval are now atomic. - Two SOCKS5 UDP clients behind one NAT IP no longer tear down each other's
association on disconnect. - A failed auto-update no longer wedges updates until the process restarts;
the debounce re-arms 10 minutes after launching the installer. node install --join CODE --insecurekeeps--insecurein the service
command (the service used to crash-loop on a missing fingerprint).- Node dials time out after 10s instead of hanging in the OS SYN-retry path
and holding up shutdown drains; the node's real dial-failure reason now
shows in the dashboard instead of a generic "dial timed out". - Absolute-URI HTTP proxy requests handle bracketed IPv6 hosts and reject
invalid ports instead of silently sending the request to port 80. - SOCKS5 replies 0x08 (address type not supported) to unknown address types
instead of closing the connection without a reply. - Hub accept loops back off briefly on accept errors instead of hot-spinning
at 100% CPU when out of file descriptors; the node-link TLS handshake is
time-bounded; the sticky-session cap is enforced hard (oldest evicted). - Creating a proxy user with a reserved routing marker in the name returns
400 instead of 500.
v0.4.10
Fixed
- Windows uninstall now removes the node key created under the SYSTEM profile.
The boot task runs as SYSTEM, so the identity lives at
...\System32\config\systemprofile\.warren\node.key, but uninstall runs as the
elevated admin user and only cleared that user's profile. The stale key
survived, so a reinstall reused the old identity and the hub kept the node on
its previous approval (you had to revoke the key on the hub to re-enroll).
Uninstall now clears the SYSTEM-profile key path explicitly.
v0.4.9
Security
- Region routing can no longer be spoofed by a node. A connected node's
self-reported IP, country, and city are now treated as display-only; the hub
uses the control-connection peer address and its own geo lookup for all routing
decisions, so a malicious node cannot claim another country to capture
user-region-*traffic. - Revoking a node key in the dashboard now tears down that device's live link
immediately. Previously the key was removed (blocking reconnect) but an
already-connected device kept proxying until it disconnected on its own. - Enrollment is replay-protected. Each Hello must carry a timestamp strictly
newer than the last one accepted for that key, so a captured Hello cannot be
replayed inside the skew window to displace the live link. - State-changing admin routes (create/delete tokens, users, keys; approve/deny
devices) now require anX-Warren-Adminrequest header in addition to the
Basic-auth token. The dashboard sends it automatically; a cross-site page
cannot, which blocks CSRF. Scripts driving these routes add
-H "X-Warren-Admin: 1"(read-onlyGETs are unaffected). - DoS hardening on the public listeners: a per-IP cap on concurrent node links, a
bounded proxy-handshake timeout, and a cap on the pending-enrollment table. - The TLS certificate and key files are written with
0600permissions on Unix,
matching node identity-key handling.
Fixed
- HTTP
CONNECTnow parses bracketed IPv6 targets ([::1]:443) and bare hosts
(defaulting to port 443) instead of rejecting them. - UDP node selection now applies the same recent-failure health window as TCP, so
a recovered node is no longer wrongly excluded from UDP associations. - Sticky sessions over SOCKS5 UDP now pin to the session's device, matching TCP.
Previouslyuser-session-KEYsilently spread datagrams across the whole pool. - A device may no longer enroll under a name already in use by a different key,
which previously madeuser+namerouting ambiguous. - SOCKS5 UDP replies for domain names longer than 255 bytes are dropped rather
than truncated into a malformed datagram. - Empty proxy usernames and passwords are rejected server-side (was only checked
in the dashboard).
Changed
- Deleting an enrollment token in the dashboard now asks for confirmation.
- A node connecting in plaintext to a TLS hub now fails with a pointed hint
(use the join code or--tls) instead of an opaque handshake error. - launchd plist generation XML-escapes interpolated paths and arguments.
- A client that connects but sends no bytes is logged at debug level.
- README: minimum Rust is 1.88 (was 1.74); UDP egress targets may be IPv4 or
IPv6 (was documented as IPv4-only).
v0.4.8
Fixed
- Hub log no longer floods with
rustls ... Illegal SNI extensionwarnings.
Internet scanners probing the exposed node port present the host IP as the TLS
SNI; rustls warned on every one. The warning was harmless (the SNI is ignored,
node auth is the app-layer enroll token) but buried real warnings in the
dashboard log card. That one rustls target is now filtered below WARN. - Dashboard no longer shows a healthy node as unhealthy next to a contradictory
"100%" success rate. Two fixes: (1) the node Health counter now expires a run
of failures after 60s with no new failure (a successful dial still resets it
instantly), so a node that stopped failing or went idle recovers instead of
sticking red until its next success, and transient dead-target timeouts no
longer pin an otherwise-fine node. (2) the Success column floors to one decimal
instead of rounding, so "100%" shows only when every dial succeeded. The
expired health count also feeds routing order and the/metricsgauge.
Changed
- Node auto-update is now staggered across the fleet. A hub redeploy announces
the new version to every--auto-updatenode at once; previously they all
re-ran the installer and restarted together, so the whole pool blinked out.
Each node now waits a random delay (up to 5 min) before updating and keeps
serving until it fires, so updates roll one node at a time (version tolerance
keeps the not-yet-updated nodes routable). Debounced so a repeated announce
does not stack timers. Single-node setups are unaffected (nothing to stagger).
Security
- The admin/
/metricsBasic-auth token is now compared in constant time, so it
cannot be recovered by timing how long a wrong token takes to reject.
v0.4.7
Fixed
- Unix
--auto-updatecould take a node down instead of upgrading it. The node
launchedinstall.shas a child process, and the installer stops the
warren-node service to swap the binary, which killed the still-running
installer (its parent died) before the swap finished: service stopped, binary
not replaced, node offline until a manual reinstall. The updater now runs
detached from the service: a systemd transient unit on Linux (its own cgroup,
independent of the warren-node unit's restart), or a new session viasetsid
elsewhere (so launchd's process-group teardown does not reach it). This mirrors
the Windows detached-helper design added in 0.4.6. Node-side only; reinstall a
node once to pick up the fixed updater.
v0.4.6
Fixed
- A node that reconnected over a half-open link (the old TCP connection never
sent a FIN, common on Windows) was silently dropped from the routable set even
though its new connection was alive. The hub keyed a node only by its stable id
and removed every entry with that id, so when the dead-peer timer reaped the
stale duplicate connection (~75s after the node had already reconnected) it
evicted the live reconnected entry too. The node stayed "ghost-connected" (TCP
up, replying to pings) but invisible on the dashboard and carrying no traffic
until a restart. Each connection now carries a unique sequence: a reconnect
displaces the old entry on enroll, and the stale reap removes only its own
connection, never the live one. Hub-only fix, no protocol change.
Added
- Windows nodes support opt-in
--auto-update. The running.exeis locked, so
a detached PowerShell helper waits for the node process to exit, then re-runs
install.ps1to swap the binary and restart the service. Off by default, like
the unix path;install.ps1gained an-AutoUpdateswitch and the dashboard
auto-update toggle now applies to the Windows install command too. - CI gained an MSRV (Rust 1.74) build job and a
cargo auditjob, so the
declared minimum toolchain and dependency advisories are both enforced.
v0.4.5
Fixed
- Windows node install failed with "The task XML contains a value which is
incorrectly formatted or out of range" (the restart IntervalPT15Sis below
the Task Scheduler 1-minute minimum, and hand-written task XML is order-
sensitive). The startup task is now built with PowerShell's ScheduledTask
cmdlets, which generate valid XML: restart-on-failure every 1 minute, no
execution time limit, run-on-batteries, SYSTEM at boot. If PowerShell is
unavailable it falls back to the flatschtasksboot task, so install never
hard-fails.
v0.4.4
Added
- Node groups via enrollment tokens. The token's name is now a group label:
every node that joins with a given token belongs to that group, and a client
can route through the whole group with the proxy usernameuser-group-NAME
(case-insensitive, no fallback if the group is empty), alongside the existing
+device,-region-, and-session-selectors. Pure hub-side routing, no
protocol change, existing nodes unaffected. - Dashboard: each node shows its group as a badge next to its name; each
enrollment token has a "rename" button (renaming relabels the group; connected
nodes pick up the new label on their next reconnect) and a "copy group cmd"
button that yields the readyuser-group-NAMEcurl.
Changed
- Enrollment-token names are now validated on create and rename (the name
doubles as theuser-group-NAMErouting selector). Names are restricted to
[A-Za-z0-9._-]and may not contain the reserved routing markers
(-region-,-session-,-group-), so a name can no longer break the copied
group curl or be silently swallowed byparse_route. The API rejects an
invalid or empty name with 400.
v0.4.3
Fixed
- macOS node install no longer requires (and must not use)
sudo. A launchd
LaunchAgent is per-user: when the installer ran it undersudo, the root
launchctl loadtargeted the wrong domain, so the agent silently never
started and the node never appeared on the hub.install.shnow installs the
service as the user on macOS, andnode installloads into the user's GUI
domain (gui/<uid>). - macOS install run as root anyway (
sudo sh install.sh, or a root shell) now
resolves the real login user (SUDO_USER, else the console session owner),
bootstraps into that user's domain, andchowns the plist back to them so no
root-owned agent is left behind and later upgrades/uninstalls work without
sudo. - macOS (re)install rides out the launchd
bootout/bootstraprace
(KeepAlive teardown settles asynchronously and an immediate bootstrap can fail
with EIO): it retries, then falls back to the legacyload -w.
Changed
- Windows startup task is now defined from a task XML so it carries
restart-on-failure (parity with systemdRestart=always/ launchd
KeepAlive: a crashed node recovers instead of staying down until reboot), no
execution time limit (the flat form inherited the 72h default), and
run-on-batteries.install.ps1checks for an elevated shell up front and
fails with a clear message instead of a crypticschtasksaccess error. - CI now also compiles (build + clippy) on macOS and Windows, so the
platform-specific service-install paths are checked on their real targets.
v0.4.2
Added
- Release assets now ship a
sha256checksum next to each archive, and both
installers (install.sh,install.ps1) verify the download against it before
extracting or running anything. Fails closed;WARREN_SKIP_VERIFY=1bypasses.
Because auto-update re-runs the installer, self-updates are verified too.
Changed
- Dashboard is wider (max 1200px) so the Live nodes table fits all columns
(including the new Version) without crowding on a normal desktop.
Fixed
- Node auto-update is now actually reachable:
--auto-updatepassed to the
installer is baked into the boot service (it was previously dropped), and a
self-update re-runs with the flag so it stays on across an upgrade. Still
opt-in and off by default. Append--auto-updateto the install line to
enable it (Unix only).