Skip to content

v0.8.22 — xray loopback guard, one holder per credential, MTProto instance scoping, silent-core recovery, L2TP upgrade billing, x/net v0.62.0

Latest

Choose a tag to compare

@Free-Guy-IR Free-Guy-IR released this 04 Nov 14:38

Release commit e4b18be7589a76a6f1abb01f2d9f44284aba6a2e on main. Previous release: v0.8.21 (347da494). This
release carries the 41 commits in v0.8.21..v0.8.22 (86 files, +5673 / -308); every one is listed by
git log v0.8.21..v0.8.22. NodeVersion 0.6.22 (e05d805b). One patch step after v0.8.21.

Before the fleet rollout

  • One node must pass the L2TP upgrade check below (OBS-a5) before any node is moved from 0.6.20, because that is the
    path v0.8.21 billed wrongly.
  • Any panel config that gives two users the same VLESS id, VMess id or Trojan password now gives the credential to one
    of them only (C2). Check the panel for shared credentials before the rollout.

Xray: no user reaches a loopback destination (C1)

  • Own control ports (a6af5dd7 RED, 90759cd5 fix, 7fd97ebe pins): ApplyAPI writes two rules for this instance's
    API and metrics ports, ahead of every panel rule, to a blackhole outbound the node adds itself. They cover loopback,
    unspecified, link-local and the host's interface addresses, plus localhost, *.localhost and numeric host forms
    (2130706433, 127.1, 0x7f000001, localhost.). They live in the config every clone, Restart and
    restorePreviousConfig replays, and ApplyAPI stays idempotent.
  • Every loopback destination on the user inbounds (decd7f1e test infra, e8594146 RED, 2e5f9649 fix): two more
    rules send 127.0.0.0/8, 0.0.0.0/8, 169.254.0.0/16, ::1/128, ::/128, fe80::/10 and the IPv4-mapped forms, plus
    localhost and *.localhost, on every port, to the blackhole, only for inbounds the node manages users on (vless,
    vmess, trojan, shadowsocks, hysteria). Panel dokodemo-door and other non-user inbounds keep the panel's routing.
  • Known residual, accepted: under routing.domainStrategy AsIs, a public domain name whose DNS record points at
    loopback is matched by neither rule. Under IPIfNonMatch or IPOnDemand the address rule catches it. Operators of
    cores set to AsIs should add their own private-address block (for example geoip:private to a blackhole) or move
    the core to IPIfNonMatch.

Xray: one holder per credential on every inbound (C2)

  • Core patch (fbaddb22 RED, 6283e614 fix): patches/xray-validator-credential-owner.patch, the 20th fork patch at
    the xray pin d2758a0. VLESS and Trojan validators refuse a key another email holds and remove only what still
    belongs to the removed user; VMess refuses a command key whose decoder belongs to another email and removes a decoder
    only while the removed user holds it. The Dockerfile guards every changed file.
  • Node holder logic (bb2f4b63 RED, 86959bde fix, new backend/xray/credential.go): a user whose credential another
    user holds is refused and waits; it is installed when the holder leaves, including through UpdateUsersAndRestart. A
    removed user never takes another user's credential. A user that moves onto a held credential is refused and its old
    account leaves the core.
  • A batch frees the keys its users move off before it admits anyone, and revokes every replaced account before adding
    (db45fd9c RED, 3adeb7c8 fix).
  • Tests: 8083f007, 40dd004b, 96c12a19, 86da26e5.

Xray: a silent core is restarted after its full sync was abandoned

  • A health restart aborts a silent full sync even after its caller gave up (2d195875 RED, 92504e43 fix).
  • Readiness bounds are per-Xray fields with the production defaults (32147e60 RED, 388b3581 fix); an abort racing
    a core that turns ready ends in one clean outcome (f498a706, interleaving test).

MTProto: instance scoping and per-instance live cut (C3/C4)

  • Each MTProto instance accepts only users that have its inbound (308ffcfc RED, cae14431 fix). Before, a user taken
    off one MTProto inbound but kept on another still authenticated on the one it lost.
  • A live session is cut the moment its user loses the inbound it authenticated on (fafc7cbd RED, e8cd97f7 fix,
    merged in 9498dde6). Sessions on the instances the user keeps, and other users' sessions, survive.

L2TP: no rebilling after an upgrade from 0.6.20 (OBS-a5)

  • baf27844: each inbound keeps a persistent marker under /var/lib/pg-node/l2tp-counted, written on the first
    successful save of its counted state. A missing counted file without the marker takes the present sessions as already
    counted; with the marker it still means a fresh /run after a reboot and counts from zero.
  • Upgrade check: /var/lib/pg-node must be on persistent storage in the node's container setup, so the marker survives
    a container recreate.
  • Forward accept rules stay out of mangle chains (0bc4b8ca RED, 0544e60b fix); the linux-only IPsec guard tests
    build only on linux (caf0987e).

Upstream PasarGuard node parity

  • PasarGuard#55: recent node logs are kept and log streams fan out per subscriber (38744465, pkg/logstream).
  • PasarGuard#101: WireGuard forward accept rules stay out of mangle chains (3998a572).
  • Upstream 81af68e (released in upstream node v0.5.7): golang.org/x/net v0.60.0 -> v0.62.0 and the indirect module
    refresh at upstream's exact versions (3bf215e8); the go directive moves to 1.26.9, the toolchain stays 1.26.10.
  • Waiver ledger (69e17a0f, e4b18be7): 81af68e, 24e2b12 (PasarGuard#55) and a3d2d2f (PasarGuard#101) are ported; the version bumps
    739eab9 (v0.5.7) and 675bdaa (v0.5.6) are not applicable to the fork's version line.

Pins

  • mtg: Free-Guy-IR/mtg main 1ca247ba6e85e785d09c890301035096e5469bcb (was 1563871b36dd, 181af755): an accept
    loop stalled by a slow PROXY protocol header and a panic in conns.go are fixed, with tests.
  • sing-box: SINGBOX_COMMIT dbd00be874a06d9870b2602dd92885367384c779 (was 5a7c1f1a31cd, 46c8bc1e), still
    v1.13.21, adds the port of upstream sing-quic 7e0a586 (hysteria2: ignore EMSGSIZE in the port hopping receive
    loop).
  • xray: XRAY_COMMIT d2758a023cd7 unchanged; one new fork patch (C2).
  • Go toolchain 1.26.10 unchanged.

Build, CI and supply chain

  • Every published image carries org.opencontainers.image.revision, the commit it was built from (5a470e57 RED,
    3a911231 fix); a workflow gate refuses any image build step without it.
  • govulncheck: GO-2026-6866, GO-2026-6860 (x/net) and GO-2026-5841 (klauspost/compress) are gone. The node never called
    the fixed x/net symbols, so this is not a security release for the node.
  • The sing-box ledger test fails on the test goroutine instead of hanging (5b9281ab, D-a1).

Upgrade and compatibility

  • Panel side: no .proto or controller API change; only NodeVersion moves to 0.6.22.
  • Shared credentials across users on one xray inbound are no longer served to both (C2): the second user waits.
  • A user moved off one MTProto inbound is refused there and its live sessions there are cut (C3/C4).
  • An xray user on a user inbound can no longer reach loopback, unspecified or link-local destinations (C1), except the
    AsIs residual above.
  • L2TP needs /var/lib/pg-node to persist (OBS-a5).

Rollback

Redeploy the image the node ran before (ghcr.io/free-guy-ir/node:v0.8.20-degraded-start for nodes upgraded from
0.6.20). Rollback loses the C1, C2, C3/C4, OBS-a5 and silent-core fixes. Older images do not read the markers under
/var/lib/pg-node/l2tp-counted, so they can stay in place.

Known, tracked

  • C2H1-S1, C2H1-S2 (LOW): a batch claimant admitted while a stock core's revoke fails, and extra drops if the core
    crashes during the revoke phase until the panel resends; both need a fault-injecting fake core to test.
  • GO-2026-6651: the govulncheck waiver expires 2026-11-30.
  • Upstream waivers: 8d0a7cf8 expires 2026-11-10 and 36 more pending waivers expire 2026-11-20; the Upstream Drift
    gate goes red after an expiry unless the item is ported or the waiver renewed.
  • GO-2026-5932 (x/crypto openpgp) is reported with no call path.