Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 24 Jun 21:54
· 63 commits to main since this release
5425f4d

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 socks rules 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: reserved now 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 get 405); RFC 1929 rejects zero-length usernames before they reach an auth backend; and the proxyprotocol trust 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; 0 cuts 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, and dns.cachettl / ratelimit.byterate changes take effect immediately.
  • Windows service: install is now transactional around its config marker, and install / start / reload run 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"), empty protocol: / command: selectors, and tokens after a rule's closing };
  • trailing tokens on numeric / boolean / keyword settings, and 0 for connecttimeout / handshaketimeout;
  • maxconnections and logrotate.keep are 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 Dockerfile pins its base images by digest, with a new Dependabot docker ecosystem 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