v0.3.0
Alighieri 0.3.0
A security-focused release: substantial hardening of the UDP relay, DNS path, filesystem handling, and the Windows service, plus graceful connection draining on shutdown and stricter config parsing. Two breaking changes — please read before upgrading.
⚠️ Breaking changes
1. Public metrics now require an explicit opt-in.
A non-loopback metrics.listen (including 0.0.0.0) is refused at startup unless you also set metrics.allowpublic: true. The metrics endpoint is unauthenticated and exposes operational counters and rule labels, so exposing it off-loopback is now a deliberate choice.
→ Action: if you bind metrics to a routable address, add metrics.allowpublic: true (and keep it behind your own access controls). Loopback binds are unaffected.
2. UDP reply filtering is strict by default.
udp.strictreply now defaults to true: a UDP ASSOCIATE reply must come from the exact host:port the client contacted, not just the host — blocking a co-located attacker on the same host but a different port.
→ Action: if a server legitimately answers from a different port (e.g. TFTP), set udp.strictreply: false to restore host-only matching.
🔒 Security hardening
- UDP relay: replies are accepted only from remotes the client actually sent to; the idle timer is refreshed only by validated, authorized traffic, so spoofed or unrelated datagrams can no longer pin an association (and its port) open; and UDP is authorized against the
socksrules before any relay socket is bound. IPv6 UDP destinations now work, and empty UDP domain names are rejected. - Open-proxy guardrail: warns at startup and on reload when the no-auth (
none) SOCKS method is offered on a non-loopback listener with permissive rules. - DNS path: concurrent system lookups are capped per resolver, timed-out names get a brief fail-fast backoff, and every resolution path is bounded by
dns.timeout(default 5s) — so a slow or wedged resolver can no longer pin connection permits or stall UDP forwarding.dns.deny: reservednow also covers the 6to4-relay and benchmarking ranges. - Filesystem / TOCTOU: userlist, config-wizard, and ACME backups and lock files refuse symlinked paths (
O_NOFOLLOW+ atomic rename), and the ACME cache directory is created owner-only (0700). - Abuse controls: the per-client map (and its prune scan) can no longer grow without bound under a connection spray from many distinct source IPs.
- Hardened inputs: the metrics endpoint answers only
GET/HEAD(others get405); RFC 1929 rejects zero-length usernames before they reach an auth backend; and theproxyprotocoltrust gate canonicalizes IPv4-mapped peers so a dual-stack listener recognizes trusted IPv4 upstreams.
🔁 Reliability & operations
- Graceful shutdown drain: on stop, the accept loop stops taking new connections and waits up to the new
shutdown.draintimeout(default 10s;0cuts immediately) for in-flight connections to finish, then aborts the rest — a busy server is no longer severed mid-transfer, and an idle one still exits at once. - Shutdown stays responsive during a config reload — a slow or wedged config/userlist read no longer delays a stop signal.
- Reload correctness: restart-only settings no longer drift (
metrics.allowpublic), footgun warnings now reflect the effective config, anddns.cachettl/ratelimit.byteratechanges take effect immediately. - Windows service: install is now transactional around its config marker, and
install/start/reloadrun the same startup validation as the server — so misconfigurations surface up front instead of at bind time. - Userlist is read and hash-parsed off the runtime worker threads during startup and reload, so a large userlist on slow storage can't stall a worker.
🧱 Stricter config parsing (fail loud, not silent)
Several settings that previously silently broadened a policy on a typo now fail to parse instead:
- stray tokens after an address or rule selector (e.g.
ports =no longer parses as "all ports"), emptyprotocol:/command:selectors, and tokens after a rule's closing}; - trailing tokens on numeric / boolean / keyword settings, and
0forconnecttimeout/handshaketimeout; maxconnectionsandlogrotate.keepare now bounded both ways (no pointer-width overflow, no absurd values).
config metadata --json again lists every configuration setting, guarded by a regression test.
📦 Supply chain
- The
Dockerfilepins its base images by digest, with a new Dependabotdockerecosystem to keep them current.
Install
Prebuilt binaries for Linux and Windows (x86-64 and ARM64) are attached below.
Container image (multi-arch amd64 + arm64):
docker pull ghcr.io/wiresock/alighieri:0.3.0
Full details: CHANGELOG.md · Compare: v0.2.0...v0.3.0