mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-13 01:14:59 +03:00
ab1c5093291fd3cd0a0df314f7c673fe21a2431d
333 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
82c7711189 |
chore(deps): update Go and dependencies
Raise the Go version to 1.27.2 and refresh Xray, LDAP, and related dependencies. |
||
|
|
442b7fb163 |
chore(deps): bump github.com/valyala/fasthttp from 1.74.0 to 1.75.0 (#6767)
Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.74.0 to 1.75.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.74.0...v1.75.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-version: 1.75.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
0054e671f8 |
feat(tuic): implement native in-process Go TUIC v5 server (#6577)
* feat(tuic): implement native in-process Go TUIC v5 server - Implement native TUIC v5 protocol server on pure Go using quic-go - Bridge decrypted TCP/UDP traffic into Xray-core via loopback SOCKS5 inbound - Support full Xray routing rules (geosite/geoip) and cascading outbounds - Implement atomic per-client traffic accounting with TotalGB and ExpiryTime - Add automatic legacy cleanup for older Rust tuic-server binaries, configs, and orphaned processes - Eliminate external Rust tuic-server downloads from install/CI scripts * fix(tuic): address traffic accounting, client reload, and socket lifecycle issues * fix(service): update checkTuicSocksReverseConflict to use bindAddr for listenOverlaps * fix(tuic): resolve traffic double-accounting, UDP fragmentation, and socket lifecycle issues * feat(tuic): complete native Go integration and address audit findings - Integrate an isolated QUIC fork pinned to a specific commit - Preserve original QUIC dependencies for Xray, Hysteria and Gin - Apply BBR, CUBIC and Reno to server connections and exported client profiles - Bridge Xray BBR with correct monotonic time and congestion type conversions - Handle congestion sender recreation after PMTU changes - Update congestion control for new connections without restarting the listener - Preserve existing connections and their selected congestion controller - Apply per-inbound log levels through the shared panel logger - Add lifecycle, authentication and TCP/UDP relay events without exposing secrets - Rate-limit repeated authentication and relay warnings - Support native and QUIC UDP relay modes on the same listener - Recover UDP associations after relay worker failures - Fix TCP relay cancellation, idle shutdown and half-close handling - Close active sessions when client credentials are revoked or disabled - Track traffic by immutable client statistics IDs across email and UUID changes - Prevent ambiguous accounting and duplicate UUIDs within TUIC inbounds - Persist pending traffic in a durable shutdown journal - Replay journal batches transactionally without duplicate accounting - Report server shutdown failures through the shared logger - Preserve legacy flat and nested TUIC settings compatibility - Normalize congestion controller values consistently across backend and frontend - Preserve controller, UDP mode and SNI in client links and subscriptions - Separate client profile options from server settings in the TUIC form - Keep certificate path autofill explicit when changing client SNI - Align UDP packet size validation with protocol limits - Simplify and localize TUIC field hints and certificate autofill messages - Add controller, TCP/UDP, logging and live settings update tests - Add accounting identity, journal replay and shutdown regression tests - Add relay recovery, session revocation and legacy frontend form tests * fix(service): alias the TUIC duplicate-UUID subquery for PostgreSQL < 16 syncInboundClients runs a COUNT(*) FROM (subquery) for every client sync, whatever the protocol. PostgreSQL before 16 rejects a FROM subquery with no alias, so on the distro PostgreSQL install.sh provisions (14 on Ubuntu 22.04, 15 on Debian 12) every client add or edit failed with SQLSTATE 42601. Reproduced against postgres:15 with the new env-gated test. * fix(database): create tuic_traffic_receipts through the model migration AddTuicTrafficBatch issued CREATE TABLE IF NOT EXISTS at runtime, a schema change outside db.go. The table was invisible to allModels and migrationModels, so x-ui migrate-db dropped the receipts and a retained journal could be counted twice after a SQLite to PostgreSQL move. It is now a GORM model in both lists, and the insert uses OnConflict DoNothing. * chore(tuic): skip the ICMP-dependent relay test on Windows, drop dead collectors Go disables SIO_UDP_CONNRESET on Windows, so a dead UDP bridge never fails a read there and TestAudit3UDPAssociationMustRecoverAfterBridgeReadFailure was red on every Windows run. Server.CollectTotalTraffic and Manager.CollectTraffic had no caller. * refactor(tuic): serve TUIC on apernet/quic-go instead of a personal fork The native server depended on github.com/poise52/quic-go, a personal fork of apernet/quic-go patched only to pick the congestion controller before the handshake. That put a second QUIC/TLS stack in the binary that no upstream security fix reaches. apernet/quic-go is already in the graph through xray-core and exposes SetCongestionControl, so BBR is now installed on each accepted connection with Xray's own congestion.UseBBR; the cross-module BBR adapter is gone. apernet ships New Reno as its only built-in sender, so a cubic setting is served as new_reno server-side (clients still get cubic in their profile). The test inspectors now read the sender under congestionMutex, which the post-handshake install writes under. Linux loopback, single stream through Xray: 2428 -> 3383 Mbit/s (bbr). --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
99bc68fa14 |
chore(deps): update project dependencies
Refresh Go, frontend, and documentation dependencies, including MSW 3 and pnpm 12.8.1. Update the MSW test setup to use the renamed `onUnhandledFrame` option. |
||
|
|
62423cacd1 |
feat(xray): update xray-core to v26.9.30 and adapt panel
Bump xtls/xray-core to b26a91de4f (v26.9.30) and the three binary pins (DockerInit.sh, release.yml Linux + Windows) in lockstep. No deleted symbols; the sing and sing-shadowsocks indirect deps drop out with the SS2022 rewrite. XDNS finalmask (#6718) replaced its string lists with objects: domains are {name, types, edns0, lenLimit, labelLimit} and client resolvers {type, settings.addr}. The loader no longer parses the old lists, so a single stored xdns mask keeps the whole core from starting. The new leaf package internal/util/maskcompat converts them: "name[:type]" becomes a domain and "name[:type]+udp://addr" a domain plus a udp resolver. A bare name maps to TXT, the type legacy clients queried by default, and each converted domain keeps the 1232-byte EDNS0 the old code always used (without it the server caps answers at 512). It runs from: - the XdnsFinalmaskObjectsFix seeder, over inbound streams, hosts, the xray template, the global sub-JSON mask and cached subscription outbounds; - inbound save (normalizeStreamSettings) and GetXrayConfig, for rows that never went through the seeder; - both link importers, since fm= from an older panel carries the lists. The finalmask form edits the object shape (every key needs a registered field, or the finalmask watch drops it on save) and lifts legacy masks on open. The udp-mask golden fixture moves to the object shape, which TestGoldenStreamFixturesBuildInXray now builds through the core. The wire format changed as well, so pre-upgrade clients need the new core. WireGuard outbound (#6771) dropped settings.domainStrategy and the remoteDNS "local" mode. The endpoint lookup now follows sockopt.domainStrategy and in-tunnel targets the outbound's targetStrategy. The old key is silently ignored, which undid the IPv4-first endpoint lookup the WARP outbound depends on (#5205), and "local" now panics the core at startup because remoteDNS goes through netip.MustParseAddr. The WireguardDomainStrategyFix seeder moves a stored family preference to both keys (a value already set wins) and turns "local" into targetStrategy; the outbound form lifts legacy rows the same way and drops its select, the WARP modal writes the new placement, and the inbound form loses a field the server never read. ValidateOutboundConfig now refuses a non-IP remoteDNS entry, which conf.Build() lets through, on template save and for outbound subscriptions. Noise finalmask items accept type "exp" (#6862), a tag expression. The form offers it for noise items only: header-custom items go through the core's PraseByteSlice, which refuses it. TUN gained autoSystemDnsToGateway (Linux) and autoSystemWfpBlockLeak (Windows). Both pass through the settings schema so a value set in JSON survives the next form save. MASQUE (inbound, outbound, transport) and the XDRIVE transport are new protocols the panel does not offer yet; their new loader refusals only cover configs the panel never generates. The FakeDNS IPv6 pool default, the SS2022 rewrite (same gRPC account; emails are now deduped case-insensitively, as the panel already does), the restored udphop interval default and the rest change no panel-facing config. |
||
|
|
3fe92df7ad |
docs: adopt correct-fix-over-small-fix and TDD policy
Replace the "smallest fix" rule with a "correct fix over small fix" policy: fix root causes properly, regardless of size, while still disallowing speculative additions. Add a dedicated TDD section (red-green-refactor, fake-test prohibitions) to CLAUDE.md and CONTRIBUTING.md, consolidating prior scattered testing guidance. Also promote jackc/pgx/v5 from an indirect to a direct go.mod dependency. |
||
|
|
86302d2f2d |
chore(deps): update toolchains and dependencies
Raise the frontend baseline to Node 26/npm 11 and refresh contributor documentation. Update frontend, documentation-site, and Go dependencies with regenerated lockfiles and module checksums. |
||
|
|
840a40edcd |
chore(deps): update frontend and Go deps
Update Ant Design, React i18n, Zod, testing utilities, Oxc tooling, GORM Postgres, Pion transport, and sing dependencies to their latest specified versions. |
||
|
|
dd46a06761 |
Update deps and fix AntD Space API
This change bumps the frontend and Go dependency set to newer patch/minor releases, including Vite, react-hook-form, zod, and the x/* Go modules. It also fixes a compatibility issue in the settings UI by switching Ant Design's Space usage from the deprecated `direction` prop to the current `orientation` prop. |
||
|
|
d0edbcec81 |
feat(xray): update xray-core to v26.9.9 and follow the udpHop move
Bump xtls/xray-core to 52a412d9e2f5 (v26.9.9) and the three binary pins in DockerInit.sh and release.yml in lockstep. Upstream moved UDP port hopping out of finalmask.quicParams.udpHop and into a standalone "udphop" UDP mask with a different shape (mode / interval / remotePorts / remoteIPs). The old key is gone from QuicParams, and since the config loader ignores unknown fields it is now silently dropped rather than rejected — port hopping just stops. The panel adapts where that key was live: - Both link importers rebuilt quicParams.udpHop from the standard mport param, so an imported hysteria2 link produced an outbound that no longer hops. They now emit a udphop mask in intervalremote mode, which is what the old key did. The mode is required: UDPHop.Build() rejects an empty or unknown one. - validFinalMaskUDPTypes and UdpMaskTypeSchema learn "udphop", otherwise the Go link generator strips the mask from every link and sub, and Zod strips it on the next form round trip. - mport generation (Go and frontend) reads the mask first and keeps reading the legacy key, so inbounds stored before the upgrade still advertise their range. On an inbound the old key was always inert — only hysteria's dialer consumed it — so nothing regresses server-side and no migration is needed. udphop stays out of the mask dropdown on purpose: it is client-only in core, which refuses to wrap a server socket, and that form is shared with the inbound editor. |
||
|
|
2d151d7648 |
Update deps and simplify parsing
Bump frontend and Go dependencies, then modernize a few hot paths with newer Go string/range helpers. Also widen the client form quota/limit fields to improve the layout. |
||
|
|
2ec6c73613 |
feat(xray): update xray-core to v26.9.8 and adapt panel
Bump xtls/xray-core to 37ceb8b4b6 (v26.9.8) and the three binary pins (DockerInit.sh, release.yml Linux + Windows) in lockstep. No deleted symbols; the impact is entirely on the JSON config surface. Outbound "proxySettings" is now refused by the config loader (moved to streamSettings.sockopt.dialerProxy) and a freedom outbound rejects sockopt.addressPortStrategy. Either key in a stored template would keep the core from starting after the upgrade, so a new OutboundRemovedKeysFix seeder rewrites xrayTemplateConfig once: proxySettings.tag becomes sockopt.dialerProxy (an existing dialerProxy wins) and addressPortStrategy is dropped from freedom outbounds. Template saves and outbound subscriptions already run through the vendored loader, so the new refusals surface there with the core's own message. REALITY no longer applies a built-in minClientVer (26.3.27) when the field is empty. The form placeholder and the min/max hints in all 13 locales now say that empty means no minimum. New upstream keys the Zod schemas would otherwise strip, with form support where a sibling field already had it: - blackhole response type "custom" with base64 customResponseData - realm finalmask ipMode (dual/v4/v6) and portMapping (UPnP / NAT-PMP) - quicParams brutalDisableLossCompensation, disableChromeParrot, disableGSO, disableStatelessReset - hysteria masquerade proxy xForwarded - wireguard outbound remoteDNS - routing rule localOS freedom.domainStrategy is only deprecated upstream (auto-migrated to sockopt.domainStrategy with a warning) and is left untouched. |
||
|
|
8411b1dd9e |
chore: upgrade Vitest to v5
Update frontend dev tooling to Vitest 5 by bumping `vitest`, `@vitest/browser-playwright`, and `@vitest/coverage-v8`, plus `@types/react-dom`. Add an override for `@storybook/addon-vitest` to pin Vitest-related packages to compatible versions and avoid dependency mismatch issues. Also bump the Go toolchain patch version from `1.27.0` to `1.27.1` in `go.mod`. |
||
|
|
38dd9bcc70 |
Bump Go dependency versions
Refresh the Go module set in go.mod and go.sum to newer patch/minor releases, including xray-related dependencies, gRPC, WireGuard, and supporting indirect libraries. This keeps the project aligned with upstream fixes and compatibility updates without changing application code. |
||
|
|
fcf60eb2e2 |
chore: bump dependencies and clear deprecated frontend APIs
Routine dependency refresh: telego 1.11.2, go-sqlite3 1.14.50, grpc 1.83.1,
miekg/dns 1.1.73, sing 0.8.14 and the usual indirect churn on the Go side;
react-query 5.102.2, i18next 26.4.0, react-hook-form 7.86.0, Storybook
10.5.10 and vite 8.2.2 on the frontend, which also lifts the private frontend
package to 1.0.0.
That left npm run lint:deprecated with five call sites. Zod 4 deprecates the
ZodTypeAny alias in favour of the bare z.ZodType constraint, and react-query
renamed queryClient.fetchQuery to queryClient.query ahead of removing the old
name in the next major — the two share an implementation, so the swap in the
settings test is behaviour-identical.
Also untracks internal/web/dist/.gitkeep.
|
||
|
|
effcccceac |
feat(amneziawg): add native AmneziaWG protocol support (#6105)
* feat(amneziawg): add native AmneziaWG protocol backend AmneziaWG (WireGuard plus DPI-resistant obfuscation) needs no Docker here — it runs as a genuine kernel interface via awg-quick/awg, managed the same way internal/mtproto manages mtg: one Inbound row is one desired Instance, and a Manager reconciles running interfaces toward the database every 10s (internal/web/job/amneziawg_job.go) plus immediately after a client edit (applyLocalAmneziaWG). Clients reuse model.Client verbatim (the same PrivateKey/PublicKey/ PreSharedKey/AllowedIPs fields WireGuard already uses), so bulk operations, the QR/share-link modal and subscriptions come from the shared inbound infrastructure instead of a parallel implementation. internal/amneziawg owns the obfuscation param generator/validator (ported from coinman-dev/3ax-ui, upgraded to AmneziaWG 2.0's S3/S4 padding and I1 signature packet) and the exec wrapper around awg-quick/awg, with fingerprint-based reconcile (noop / reload-via- syncconf / full restart) mirroring mtproto.Manager so a same-protocol edit doesn't force an unnecessary interface bounce that would drop every peer's connection. Frontend and install.sh's DKMS/awg-tools setup are tracked separately; this is backend-only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): add frontend support and fix a Go->Zod generator gap Wires the amneziawg protocol through the panel UI the same way every other protocol is registered: a Zod settings schema (nested {server, clients}, matching the Go JSON exactly), the protocol enum, the inbound-form's per-protocol fields component and its tab-visibility allowlist, the default-settings factory, the client schema dispatcher, and the sniffing-capability exclusion (no Xray inbound exists for amneziawg, same as mtproto). Client key/allowedIPs fields are reused rather than duplicated: since AmneziaWG clients are wire-identical to WireGuard clients (same model.Client fields), ClientFormModal renders one shared field block for both, switching only the visible label by which protocol is active. The private-key input also gets a live public-key sync via a new useEffect, because unlike WireGuard's Xray-native inbound (which re-derives its public key at runtime and never stores one), AmneziaWG's server.publicKey is a real persisted field the Go backend reads directly — free-typing a new private key without this would silently save a mismatched keypair. Adds a downloadable per-client .conf (amneziawgConfig.ts, mirroring wireguardConfig.ts) with the obfuscation lines, and an InboundOption.AwgServer field on the Go side so the config builder gets the full server block in one round trip. Along the way, running tools/openapigen surfaced a real bug: it doesn't flatten anonymously-embedded Go structs the way encoding/json does, so ServerSettings embedding Obfuscation20 produced a Zod schema with a nested `obfuscation20` key that never matches the real wire JSON. Fixed by un-embedding (flat fields + an accessor method) and registering internal/amneziawg in the generator's own package list, which had been silently emitting a dangling schema reference. English and Russian translations are complete; the other 10 locale files still fall back to English for the new keys. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): complete frontend parity for the Inbounds list page The Clients page (form, CRUD, QR/config) already worked from the prior commit; this closes the remaining gap on the Inbounds side and in a couple of protocol allowlists that a plain search for existing wireguard/mtproto handling turned up. lib/xray/inbound-link.ts gets amneziawg-specific link/config builders (genAmneziaWGLink/genAmneziaWGConfig, plus the *s fan-out variants) mirroring the wireguard ones — AmneziaWG has no legacy peers-array to fall back to, so these read settings.clients directly and add the obfuscation lines every client must share with the server. Wired into genInboundLinks generically, and into three consumers that call the wireguard builders directly rather than through that dispatcher: QrCodeModal, InboundInfoModal, and InboundsPage's bulk export. ClientInfoModal, ClientBulkAddModal, and the bulk attach/detach modals each had their own protocol allowlist that needed amneziawg added alongside wireguard/mtproto. Two real gaps surfaced by grepping every remaining 'wireguard' / Protocols.WIREGUARD hit in frontend/src rather than trusting the checklist was exhaustive: - useInbounds.ts's TRACKED_PROTOCOLS gates the deactive/depleted/ expiring/online client counts shown per inbound on the list page; without amneziawg those counts would silently read zero. - inbound-tag.ts is an explicit client-side mirror of the Go backend's port_conflict.go (the file says so itself: "Keep in sync"). It still only special-cased wireguard for UDP, so an amneziawg inbound would have fallen through to the TCP default and disagreed with the backend's own port-conflict math. Also finishes translating the AmneziaWG UI strings into the 11 locale files that were still falling back to English (ar-EG, es-ES, fa-IR, id-ID, ja-JP, pt-BR, tr-TR, uk-UA, vi-VN, zh-CN, zh-TW), matching en-US/ru-RU key-for-key (26 new keys, verified by count in every file). Not run anywhere: npm run typecheck / build. This machine has neither Node nor npm, so nothing here has compiled — reviewed by hand plus brace/paren balance checks and cross-referencing the generated Zod/TS types. Treat this as needing a real typecheck before shipping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(install): note that AmneziaWG kernel module install is still manual Tracked separately (not yet ported into this script) — see coinman-dev/3ax-ui's install_amneziawg for the reference approach (ppa:amnezia/ppa). Also serves as a real, path-filter-matching change to get the previous empty commit's CI trigger to actually fire — release.yml's push trigger is paths-scoped and an empty commit changes no files, so it never matched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): add a button to randomize obfuscation parameters Mirrors the existing key-regenerate button next to the private key field. Client-side randomization matches the ranges/constraints of GenerateObfuscation20's "default" preset (internal/amneziawg/params.go) closely enough for a form suggestion — the user can still hand-edit any field afterward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(install): auto-install the AmneziaWG DKMS module + amneziawg-tools Ports install_amneziawg from coinman-dev/3ax-ui's install.sh, adapted to this script's broader distro coverage and NONINTERACTIVE convention: - Ubuntu/Debian/Armbian: ppa:amnezia/ppa (primary, tested path), with a reachability pre-check for the Launchpad PPA host — often blocked by hosting providers, especially Russian VPS — so a flaky network skips the feature instead of hanging apt through several retries. - Fedora/RHEL-family, Arch/Manjaro/Parch: best-effort fallback to plain wireguard-tools (+ AUR amneziawg-dkms via yay/paru when available), with a manual-install pointer. - Everything else: manual-install pointer only. Also installs ndppd and persists IPv4/IPv6 forwarding (for the future IPv6/NDP phase, not yet wired into the panel) and adds a Secure Boot warning at the end of the run, since a DKMS-built module is unsigned and won't load while it's enabled — a common trap on cloud VPS images. Never fatal: the panel installs and runs fine either way, an AmneziaWG inbound just won't bring up its tunnel until the module is present. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): resolve all 3 real CI failures (typecheck/lint/codegen) Found by checking the fork's Actions tab after the last two pushes — the release build passed (it doesn't run these checks) but the separate CI workflow caught three real issues: - golangci-lint (noctx): every internal/amneziawg/manager.go exec.Command call is now exec.CommandContext with a 30s timeout, so a hung awg-quick/awg invocation can't block the reconcile job indefinitely (mirrors internal/mtproto/process.go's own CommandContext usage). - tsc --noEmit: frontend/src/schemas/client.ts's hand-maintained InboundOptionSchema (used by the useClients hook, separate from the auto-generated one in generated/) never got an awgServer field added when the AmneziaWG frontend work was done — every read of inbound.awgServer.* in amneziawgConfig.ts was typing as {}. Added AwgServerOptionSchema, nested (not flattened like wg*) to match what amneziawgConfig.ts already expects. Also guarded server.publicKey in inbound-link.ts's genAmneziaWGLink against the schema's optional type. - codegen staleness: frontend/public/openapi.json is produced by a Node script (gen:api) this machine can't run; hand-applied the exact diff the CI failure log already showed (amneziawg protocol enum entry, ServerSettings schema, InboundOption.awgServer, one example payload), verified as valid JSON. Also confirmed independently by this run: install_amneziawg (previous commit) installed and loaded the DKMS module successfully on both amd64 and arm64 CI runners. The two "Deploy Smoke Tests" failures are unrelated to this change — this fork has only ever published the dev-latest pre-release, and GitHub's /releases/latest API deliberately excludes pre-releases, so the smoke test's no-argument install path (which resolves "latest") has nothing to find. Not a regression; needs an actual tagged release whenever that's wanted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): Phase 2a — IPv6 support + NDP proxy Adds native dual-stack IPv6 to AmneziaWG inbounds, ported from coinman-dev/3ax-ui's approach: - ServerSettings gets ipv6Enabled/ipv6Subnet/ipv6ExternalInterface; Instance carries the server's own IPv6 address (first host of the subnet) alongside its IPv4 one. - defaultAmneziaWGClients allocates an IPv6 host address per client (second AllowedIPs entry) when the server has IPv6 enabled, reusing allocateWireguardAddress — which needed a real fix along the way: it always suffixed "/32" regardless of address family, which is wrong for an IPv6 host address (needs /128). Now family-aware. - generateServerConfig's PostUp/PostDown gains IPv6 forward-accept rules, proxy_ndp sysctl, and one `ip -6 neigh add/del proxy` entry per enabled peer with an IPv6 address — the lightweight per-client method, not the ndppd-daemon whole-subnet method (not worth the config-file-management complexity at this scale; ndppd itself is still installed by install.sh in case that changes later). - ValidateIPv6Subnet rejects a malformed subnet before save. - Frontend: ipv6Enabled/ipv6Subnet/ipv6ExternalInterface fields on the AmneziaWG inbound form, EN+RU translations, openapi.json/generated/* regenerated (the latter via `go run ./tools/openapigen`, pure Go). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): fill in IPv6 fields missed by the Phase 2a commit Two real gaps the CI caught (both new fields, both my miss): - inbound-defaults.ts's createDefaultAmneziawgInboundSettings() built a server object literal predating ipv6Enabled/ipv6Subnet/ ipv6ExternalInterface — AmneziawgServer's inferred type now requires them (zod .default() fields are non-optional post-parse), so this didn't typecheck at all. - openapi.json's ipv6Enabled property was missing the description the real generator attaches (the Go doc comment covering all three IPv6 fields is attached to the first one) — a one-line diff, but git diff --exit-code doesn't care how small. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): Phase 2b — per-client port-forwarding Admins can now set a per-client ForwardedPorts string (e.g. "80, 443, 8000-8100") that gets DNAT'd + FORWARD'd to that peer's tunnel address via iptables rules in PostUp/PostDown, ported and simplified from coinman-dev/3ax-ui's shared/portfwd. Two decisions worth flagging for future readers: - The iptables --comment tag on each rule is awg-fwd-<fnv32a(email)>, not the raw client email. Email is admin/API-supplied free text that ends up embedded in a shell-executed PostUp/PostDown line; a hash can never carry a shell metacharacter through where raw interpolation could. - The reconcile manager gained a third fingerprint (portFwdFP, next to the existing structural/peers ones). `awg syncconf` only touches the WireGuard peer table — it never re-applies PostUp/PostDown iptables rules — so a port-forward-only change has to force a full awg-quick down+up bounce, same as a structural change, rather than the lighter sync a plain peer add/remove can use. Also fixes a real pre-existing bug found while wiring up IPv6 client allocation in the previous commit's spirit: allocateWireguardAddress always suffixed "/32" regardless of address family, which produced invalid host bits for IPv6 (needs "/128"). ForwardedPorts flows through model.Client -> model.ClientRecord (gorm column wg_forwarded_ports, auto-migrated) -> ToRecord/ToClient/ MergeClientRecord, mirroring the awgServer field's earlier lesson that new fields need checking against a second, hand-maintained persistence-layer struct. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): route a client's traffic through Xray via the Routing page Every enabled AmneziaWG inbound gets its own Xray TPROXY bridge automatically, with no toggle to enable first: a loopback dokodemo-door inbound (sockopt.tproxy) tagged with the AmneziaWG inbound's own real tag, so it's already selectable in the existing Routing page's inbound-tag picker — the same trick the mtproto sidecar's own bridge already relies on (InboundService.GetInboundTags is a plain, protocol-blind SELECT over every inbound row's tag, no dedicated UI plumbing needed). internal/amneziawg's defaultPostUpDown TPROXYs every peer's traffic into that bridge unconditionally; the bridge's port is derived deterministically from the inbound's id (EgressPortForInbound) so the kernel-side reconcile loop and the Xray-config generator never need to negotiate a runtime value between them. injectAmneziawgEgress never generates a routing rule itself — whether a client's traffic goes anywhere beyond Xray's default routing is entirely up to whatever rules the admin adds through the existing Routing UI (pick the AmneziaWG inbound's tag as source, optionally a specific peer's IP via that page's own Source-IP field, and an outbound), exactly the same workflow as routing any other protocol. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): recover orphaned interfaces after an ungraceful exit Two gaps left an AmneziaWG interface stuck outside the manager's control after a crash (kill -9/OOM/panic skips StopAll): - ensureRestart's teardown was gated on the in-memory `exists` map, which is always empty on a fresh process, so a survived interface never got interfaceDown before interfaceUp tried `ip link add` against a name the kernel already had — failing forever and never populating m.ifaces, so traffic accounting silently stopped and the inbound could never be removed. Gate on isInterfaceUp instead, which checks real kernel state rather than this process's own bookkeeping. - An inbound deleted from the database entirely while the panel was down has no entry in `desired` ever again, so it never reaches the per-id cleanup loop in Reconcile (which only walks m.ifaces). Add a one-time sweepOrphansLocked scan of configDir, mirroring mtproto.Manager.sweepOrphansLocked, that tears down and removes any leftover interface/config not in the current desired set. Found by the automated review on MHSanaei/3x-ui#6105 (Finding 1). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * i18n(amneziawg): backfill IPv6/obfuscation/port-forwarding keys in 11 locales Only en-US/ru-RU ever got these 9 keys as each AmneziaWG feature landed (the regenerate-obfuscation button, then Phase 2a's IPv6 fields, then Phase 2b's per-client ForwardedPorts) — the other 11 locale files were never backfilled, so i18next has been silently falling back to English for all of them since Phase 1. Cosmetic-only (never broke anything), but now closed for every shipped locale. * fix(amneziawg): resolve 7 Medium findings from the automated PR review Each is independently reproducible; fixed together since one review pass found all of them. - manager.go: the shared "ip rule add fwmark" policy route had no existence check, so it duplicated in "ip rule show" on every interface bounce (which hostRulesFingerprint forces on any client add/remove/ re-IP). Now checked via "ip rule list | grep -q ..." first. (Finding 2) - params.go: ExternalInterface, IPv6ExternalInterface, and subnetIp/ subnetCidr are interpolated unescaped into a shell-executed PostUp/ PostDown line, but only obfuscation and the IPv6 subnet were validated before save. Added ValidateInterfaceName (a strict charset+length pattern) and ValidateSubnetIPv4 (netip.ParsePrefix), wired into normalizeAmneziaWGSettings. (Finding 3) - amneziawg_job.go: IsAwgInstalled() existed but nothing ever called it, so a host without awg/awg-quick (the Docker image, RHEL, Arch, a failed install.sh PPA step) logged a reconcile failure every 10s forever. Now checked once an inbound actually needs it, warning once instead of spamming. (Finding 4) - client_inbound_apply.go: the WireGuard/AmneziaWG credential carry-forward (added so a metadata-only client edit doesn't rotate keys) never covered ForwardedPorts, so a partial edit -- an API call or Telegram-bot toggle that omits the field -- silently wiped a client's port-forwarding spec. Carried forward and written back the same way the key fields already are. (Finding 5) - manager.go: hostRulesFingerprint keyed each peer on its IPv4 address only, and structuralFingerprint omitted IPv6Enabled/IPv6ExternalInterface entirely, so an IPv6-only change could pick the syncconf reload path (which never re-runs PostUp, leaving a stale NDP-proxy entry) or be a complete no-op. Both fingerprints now cover the IPv6 fields. (Finding 6) - port_conflict.go: the AmneziaWG egress bridge (injectAmneziawgEgress) binds 127.0.0.1:63100+id with no collision check anywhere, since it isn't a database row the ordinary port-conflict query can see -- same blind spot the reserved Xray API port already has its own check for. Added the equivalent check for the AmneziaWG bridge port. (Finding 7) - install.sh: install_amneziawg ran unconditionally for every install/ update, building a DKMS kernel module and enabling host-wide IPv4/IPv6 forwarding whether or not the feature is ever used. Gated behind a new should_install_amneziawg (XUI_INSTALL_AMNEZIAWG=true/false, or an interactive y/N prompt defaulting to no). Also replaced the deprecated apt-key adv with a dedicated keyring + signed-by= on the Debian branch, and guarded its sources.list appends against duplication on a retried install. (Finding 8) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(amneziawg): make the Xray TPROXY bridge a per-inbound opt-in Addresses Finding 10 from the automated PR review: an always-on TPROXY bridge makes every AmneziaWG tunnel hard-depend on Xray being up (all traffic, including DNS, drops whenever Xray restarts), and forces a full awg-quick down+up bounce on any client add/remove/re-IP, permanently losing the syncconf fast path. Adds ServerSettings.RouteThroughXray (off by default): - defaultPostUpDown only emits the TPROXY/policy-route rules when it's on; a plain AmneziaWG tunnel now has zero Xray dependency out of the box. - structuralFingerprint covers it (toggling it changes whether PostUp/ PostDown contain any TPROXY rules at all -- structural, not a per-peer host-rule). hostRulesFingerprint's IPv4 tracking is now itself conditional on RouteThroughXray (and IPv6 tracking on IPv6Enabled), so an instance that never uses either keeps the syncconf fast path for a plain peer re-IP. - injectAmneziawgEgress only creates a bridge for inbounds that opted in; checkAmneziawgEgressConflict (the Finding-7 fix) now parses each candidate through InstanceFromInbound so a non-routed inbound's port is correctly never treated as reserved. - New inbound-level Switch in the AmneziaWG form; the actual outbound decision is still made entirely through the panel's stock Routing page, same as before -- only whether the bridge exists at all is now a choice. Translation keys added to all 13 locales in the same commit this time, not backfilled later (see Finding 9's lesson). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): resolve 4 Low findings from the automated PR review - manager.go: serverAddress assumed subnetIp always ends in ".0"; a base like "10.8.1.5" was used verbatim as the server's own address, eventually colliding with peer allocation (which starts at .2 upward). Now derives the first host of the actual subnetIp/subnetCidr network via netip, matching serverAddressV6's own approach. A /32 base (no host bits at all) is still used as-is. (Finding 12, partial -- the /16 pool-widening half of this finding only exists on the upstream-pr/amneziawg branch's merged client_wireguard.go, not here; handled separately on that branch.) - manager.go: ensureLocked carried the previous per-peer traffic counters (`last`) forward even through a full restart, but awg-quick down+up resets the kernel's own counters to zero -- the next CollectTraffic computed a large negative delta (clamped to 0), silently discarding real traffic. Extracted the decision into nextTrafficBaseline: only a reload (syncconf) preserves the baseline. (Finding 13) - portfwd.go: exported ForwardedPortsInclude; inbound_amneziawg.go's new checkForwardedPortsConflict uses it to reject, at save time, a client's forwardedPorts that would DNAT the panel's own port or another enabled inbound's port to the tunnel client -- portForwardLines has no destination restriction, so this collision was previously silent. Wired into both the single-client update path and the add-client path (client_inbound_apply.go), plus normalizeAmneziaWGSettings for the whole-inbound save path. (Finding 14) - inbound.go: InboundOption.AwgServer sent the whole ServerSettings struct including PrivateKey to GetInboundOptions callers -- a shared, admin-wide dropdown-filling endpoint the frontend's own AwgServerOptionSchema never reads that field from. Redacted it before assigning. (Finding 11) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): don't widen the peer address pool past AmneziaWG's own subnet Completes Finding 12 from the automated PR review (the serverAddress half of this finding was already fixed on main and cherry-picked here). This half is specific to this branch: allocateWireguardAddress's /16 pool-widening fallback is an independent addition from upstream's own main that this branch inherited during the cherry-pick rebase -- it doesn't exist on the fork's own main at all, so this fix can't be cherry-picked the normal way and is committed directly here. Widening is safe for WireGuard's own Xray-native inbound (AllowedIPs isn't tied to a strict kernel interface subnet), but AmneziaWG's kernel interface Address is exactly the configured subnet -- an address allocated from the containing /16 once the /24 fills up would be silently unroutable. allocateWireguardAddress now takes an explicit allowWidening bool: WireGuard's own caller passes true (unchanged behavior), AmneziaWG's passes false (fails loudly on exhaustion instead). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(docker): note that AmneziaWG doesn't work in this image Investigated: the image is Alpine-based, and AmneziaWG's own packaging (DKMS module + amneziawg-tools) doesn't target Alpine/musl at all -- unlike the Debian/Ubuntu/Fedora/Arch paths install.sh already handles, there's no package to apk add even with full host network/capabilities. The panel already degrades gracefully (IsAwgInstalled() logs one warning instead of retrying forever), so no code change is needed -- just made the reason explicit at the point where a user would reach for cap_add/ network_mode to try to work around it. * fix(sub): include amneziawg inbounds in subscription links getInboundsBySubId's SQL protocol allowlist never had 'amneziawg' added, so every AmneziaWG client was silently excluded from all three subscription formats (plain/individual links, JSON, Clash) and from the Telegram bot's QR/individual-link buttons, which fetch through the same path. genAmneziaWGLink itself was already fully implemented and already wired into GetLink's dispatch switch -- it just never got a chance to run. Same bug shape as the earlier TRACKED_PROTOCOLS frontend gap: a hardcoded protocol list one entry short. Found while investigating whether the Telegram bot needed AmneziaWG- specific client-management code -- it doesn't (the bot itself is fully protocol-agnostic), but this is the actual root cause of "can't share an AmneziaWG client's config via the bot." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(inbound): enforce node-eligibility server-side, not just in the UI Investigated multi-node interaction with AmneziaWG: the master's own reconcile (DesiredAmneziaWGInstances) and Xray config generation (injectAmneziawgEgress, the GenXrayInboundConfig protocol skip) all correctly filter on NodeID IS NULL, so a node-assigned AmneziaWG (or MTProto) inbound would never be managed by the master. But nothing stopped one from being created that way: NODE_ELIGIBLE_PROTOCOLS (frontend/src/pages/inbounds/form/InboundFormModal.tsx) only hides the node picker client-side -- a direct API call could set nodeId on an AmneziaWG inbound, which every node then reconciles as an ordinary local inbound (nodes run the identical binary, full cron suite included), leaving it running unmanaged and untracked by the master's own AmneziaWG bookkeeping. Added isNodeEligibleProtocol (inbound_protocol.go), mirroring the frontend's allowlist, and enforced it in both AddInbound (the actually exploitable path -- nodeId comes straight from the request) and UpdateInbound (defense in depth; NodeID is already restored from the stored row there before this check, so it mainly guards against a protocol change on an existing node-hosted inbound). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(amneziawg): allow TPROXY-marked traffic through a default-deny INPUT chain TPROXY never rewrites a packet's own destination address, only the routing decision. A default-deny firewall whose INPUT chain sanity-checks "is this destination actually local" (UFW's ufw-not-local, via addrtype --dst-type LOCAL, is a concrete example) silently drops the redirected packet before Xray's socket ever sees it -- RouteThroughXray looked fully configured (TPROXY rule present and counting, Xray listening with IP_TRANSPARENT set) yet every peer's traffic vanished with no trace on either side. Adds an idempotent, never-torn-down "iptables -I INPUT 1 -m mark --mark <fwmark> -j ACCEPT" alongside the existing shared policy route, so this works regardless of which firewall manager owns the rest of the INPUT chain. * fix(frontend): give AmneziaWG the same UDP tag and its own tag color The Inbounds list only special-cased isWireguard/isHysteria for the "UDP" network badge, so an AmneziaWG row showed just the bare protocol tag with no transport badge next to it. Added the missing isAmneziawg flag (mirrors isWireguard exactly) and wired it into the same branch. Client-row protocol-color maps in ClientsPage/HostList had no amneziawg entry, silently falling back to grey -- ClientInfoModal already had amneziawg: 'yellow' from earlier work, these two just never got it. * feat(logs): show which AmneziaWG client an access-log line belongs to The dokodemo-door TPROXY bridge every AmneziaWG peer's traffic is routed through has no per-user identity, so Xray's own access log never carries an "email:" token for these lines -- the Access Logs modal showed a blank Email column for every in-*-udp row, even though every other protocol's rows show the client normally. The peer's decapsulated tunnel IP does survive as the log's "from" address, and that IP deterministically maps to exactly one configured peer. Builds a "<inbound tag>|<ip>" -> email index from the same AmneziaWG inbounds already parsed elsewhere (amneziawg.InstanceFromInbound), and fills in Email from it whenever the raw log line didn't have one. * fix(amneziawg): enable sniffing on the TPROXY bridge Domain-based Routing rules could never match RouteThroughXray traffic: an AmneziaWG peer resolves DNS itself, through the tunnel, before ever sending a packet, so the decapsulated traffic TPROXY hands to the bridge is already a bare destination IP with no domain name attached at the network layer. Every other inbound recovers this via sniffing (confirmed working for the stock wireguard inbound, which does have it configured); the bridge never got a sniffing block at all, so only tag/IP/network-based rules could ever match it -- any domain rule above it in the list was silently unreachable. * docs: add an AmneziaWG config page and list it as a supported protocol Closes the PR checklist gap: the feature shipped with zero mention on the docs site. Mirrors reality.mdx's structure (key settings, setup steps, config excerpt) and notes the Docker/multi-node/Telegram-bot caveats the PR itself is honest about not having confirmed. * fix: address the fresh review round on PR #6105 (8 findings) 1. hostRulesFingerprint didn't account for ForwardedPorts when RouteThroughXray was off, so re-IPing a peer with port-forwarding configured left stale DNAT rules pointing at an address the next peer could be handed. 2. Server/client config values (keys, email, I1) were never validated for control characters before being written into the generated .conf; a newline could smuggle a PostUp hook into awg-quick's parser. Added ValidateConfigValue at save time and a sanitizeConfigValue backstop at render time. 3. checkForwardedPortsConflict didn't scope to node_id IS NULL, so a port used only on a different node produced a false collision; also hoisted the panel-port/inbounds lookup out of the per-client loop (portConflictContext) so N clients cost one query, not N. 4. PostDown commands were ";"-joined and abort on the first failure; appendOrTrue makes teardown best-effort so an external firewall flush can't leave DNAT rules to accumulate across bounces. 5. The "ip rule list | grep -q" existence check could SIGPIPE under pipefail and re-add a duplicate rule; switched to grep -c >/dev/null. 6. Ported the vpn:// share-link format (base64url of the plain .conf text, matching the real AmneziaVPN app) onto this branch -- it had only ever landed on our own fork's main, so this PR branch was still on the old amneziawg://+query-params scheme our own docs no longer described. Also corrected the docs' install.sh claim (opt-in/ interactive, not automatic) and stale pre-opt-in comments in route_egress.go. 7. install.sh: Arch's ndppd install used pacman -Syu (full system upgrade) instead of -Sy like every other call in the script; and should_install_amneziawg re-prompted on every `x-ui update` even when awg was already installed. 8. CollectTraffic could clobber a concurrent restart's freshly-reset (empty) traffic baseline with stale pre-restart counters, since getPeerStats runs lock-free; now checks pointer identity before writing back. sweepOrphansLocked permanently disabled itself on a transient os.ReadDir failure instead of allowing a retry. go build/vet/test and frontend typecheck/lint/build/vitest all pass. * fix(install.sh): check the live sysctl value, not sysctl.conf text Reviewer feedback (cherts, PR #6105): grepping /etc/sysctl.conf for the setting name is unreliable -- many distros split sysctl config across /etc/sysctl.d/*.conf, and /etc/sysctl.conf can be a symlink into that directory, so the check can miss an already-active setting (harmless duplicate append) or match a disabled/commented line (forwarding silently stays off). Query the live value via `sysctl -n` instead, which is accurate regardless of which file set it. Applied the same fix to both the IPv6 and IPv4 checks for consistency. * fix: update inbound_amneziawg.go to the split buildInboundForLocalRuntime Same fork-only-file blind spot as the one caught on our own main after the 3.6.0 sync: upstream split buildRuntimeInboundForAPI into buildInboundForNodePush / buildInboundForLocalRuntime (part of the node-sync client-deletion fix, |
||
|
|
81fcacab11 |
chore(build): bump Go toolchain to 1.27.0
Go 1.27.0 shipped on 2026-08-19. Raise the go directive and the builder image so Docker and release builds pick it up; every CI job already reads the version from go.mod, and golangci-lint v2.13.1 release binaries are themselves built with go1.27.0, so the lint job needs no pin change. |
||
|
|
d291e1c5ee |
Bump Go toolchain and x dependencies
Refresh the Go toolchain from 1.26.5 to 1.26.6 and update the related x/* and protobuf dependency set in go.mod/go.sum. This keeps the project aligned with the current patch releases and ensures the module graph matches the expected transitive versions. |
||
|
|
e2f75acad2 |
chore(deps): bump github.com/klauspost/compress from 1.19.1 to 1.19.2 (#6212)
Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.19.1 to 1.19.2. - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](https://github.com/klauspost/compress/compare/v1.19.1...v1.19.2) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.19.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
3883882726 |
chore: bump frontend and Go dependencies
Updates multiple frontend packages (antd, react-hook-form, storybook, vite, swagger-ui-react, playwright, typescript-eslint, etc.) and Go dependencies (gopsutil, gorm postgres driver, pion/transport, ugorji/codec, genproto, and others). Also replaces `__dirname` with `import.meta.dirname` in vite.config.js for ESM compatibility. |
||
|
|
138e1bd840 |
chore(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.0 (#6162)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.1 to 1.83.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.82.1...v1.83.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.83.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
c56f6447a8 |
chore: refresh dependencies and modernize Go test idioms
Frontend deps: @hookform/resolvers 5.4.3 -> 5.5.7, Storybook 10.5.4 -> 10.5.5 across the four packages we declare, globals 17.7.0 -> 17.8.0, and jsdom 29.1.1 -> 30.0.1. The jsdom major replaces its CSS and selector stack -- @asamuzakjp/css-color 5 -> 6, @asamuzakjp/dom-selector 7 -> 8, undici 7 -> 8, nwsapi and generational-cache folded into their parents, whatwg-url 17 nested underneath. Nothing in the Vitest suites reaches those directly and the whole frontend gate (typecheck, lint, tests, build, Storybook compile) is green. Panel frontend version to 0.6.0. Backend deps: mattn/go-sqlite3 1.14.48 -> 1.14.49 and valyala/fasthttp 1.72.0 -> 1.73.0, plus the golang.org/x/exp and genproto/googleapis/rpc indirect bumps that came with them. Go tests: modernize -fix output, covering range-over-int, sync.WaitGroup.Go in place of manual Add/Done pairs, maps.Copy, and Go 1.26 new(expr) for pointer-to-value in the forwarded-trust table. The storedAs helper is deleted instead of being left behind a //go:fix inline directive -- keeping it that way fails govet on the one call site the rewrite did not reach, and every caller now takes new(...) directly. Behaviour is unchanged. DnsTab: the hosts-sync effect tested dns while declaring dnsEnabled in its dependency array. Both carry the same truth value, so this is exhaustive-deps hygiene rather than a behaviour change. |
||
|
|
af5a8e5d40 |
fix(database): create SQLite backup snapshots online (#6137)
* fix(database): snapshot SQLite backups online Use SQLite's online backup API for downloadable backups and SQLite migration exports instead of checkpointing then reading the live database file. The regression test validates a backup made while writes continue. * style(database): group SQLite driver imports * fix(database): bound online backup retries Use a single backup step and a bounded connection-acquisition/retry context. Tighten temporary-file cleanup and regression assertions while removing the unused checkpoint helper. * test(database): cover existing backup destinations * fix(database): harden SQLite snapshot lifecycle Sweep interrupted snapshot directories at SQLite startup, keep rollback-journal backups incremental, and make caller-owned cleanup explicit. Reuse one scheduled Telegram snapshot across administrators and make the direct SQLite driver dependency explicit. --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local> |
||
|
|
7f7b7e16a4 |
feat(xray): update xray-core to v26.7.28 and adapt panel
Bump xtls/xray-core to 5ca6f4b7d4dc (v26.7.28) and move the three binary pins (DockerInit.sh, the Linux and Windows URLs in release.yml) in lockstep so the in-process conf.Build() validation and the child binary agree. XMC finalmask (#6487) is the breaking change. The mask's `usernames` string list is gone, replaced by a required `profiles` array whose entries each need a 3-16 character [A-Za-z0-9_] username, a parseable UUID and both Mojang texture fields; the "default to Dream when empty" fallback was removed, so an xmc mask saved by an older panel now fails to build and takes the whole config down with it rather than degrading one inbound. The textures are a signed blob only Mojang's session server can issue, so a legacy username cannot be upgraded automatically. The panel now: - rejects an incomplete xmc mask at save time (AddInbound/UpdateInbound), pointing at the specific field that is missing; - drops only the offending mask when generating the core config, for rows that never went through the form (upgrade, node sync, restored backup, direct DB edit), warning which inbound lost its obfuscation instead of leaving every inbound offline; - carries legacy usernames into profile stubs in the finalmask form so the operator keeps their player names and sees exactly what still needs filling in, and edits profiles through a list editor. No destructive DB migration: unlike the removed shadowsocks ciphers there is no valid replacement to rewrite to, and dropping the mask from stored rows would discard the operator's hostname and password for config they can still repair. The generation-time strip already prevents the startup failure. Also track the core's xmux maxConnections fallback, lowered from 6 to 3 for anti-TSPU, in the fresh-XMUX seed so a new panel config matches what the core would pick on its own. TUN gained a `desc` key and random utunN naming, but the Go validator no longer accepts TUN inbounds and the panel only renders legacy saved rows, so nothing there needs adapting. The remaining commits are REALITY log-warning wording, gRPC/XHTTP localAddr accuracy and a routing tweak, none of which change the JSON config surface. Tests cross-check the panel's profile predicate against conf.XMCProfile.Build() so a future core release that tightens or relaxes the rules fails loudly rather than silently emitting configs the core refuses to start on. |
||
|
|
f4e79e70ea |
chore: refresh dependencies, fix Linux tool tasks, modernize Go idioms
Frontend deps: @hookform/resolvers 5.4.0 -> 5.4.3 and react-hook-form 7.82.0 -> 7.83.0. The @typeschema/valibot override is what makes this installable at all. Resolvers 5.4.3 re-declares 25 optional peers for its validator matrix, and npm resolves them into the ideal tree even though none are used here; two of them contradict, since resolvers wants valibot ^1 while @typeschema/main -> @typeschema/valibot pins valibot ^0.39. Both target the same node_modules/valibot, so a plain npm update dies with ERESOLVE. The override settles that one edge and nothing extra lands in node_modules. Backend deps: telego 1.10.0 -> 1.11.1 (Telegram Bot API v10.2, additive only), klauspost/compress 1.19.1, plus the indirect bumps that came with them. VS Code tasks: the golangci-lint and modernize tasks assumed Windows PATH semantics, where PATH is a persistent user variable that every process inherits, so ~/go/bin was always visible. On Linux that directory is exported from ~/.bashrc, which the non-interactive `bash -c` behind a task never sources, and both tasks failed with exit 127. Adds linux/osx option blocks that prepend the Go bin directories and leaves the Windows path untouched, plus tasks to install the two tools; those are split because go install rejects packages from different modules in one invocation. Go sources: modernize -fix output, covering range-over-int, slices.Backward, maps.Copy, strings.CutPrefix and strings.SplitSeq. Behaviour is unchanged. |
||
|
|
28b360f2af |
chore(deps): bump google.golang.org/grpc from 1.82.0 to 1.82.1 (#5994)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.0 to 1.82.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.82.0...v1.82.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.82.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
8dfb639dcf |
chore(deps): bump github.com/go-ldap/ldap/v3 from 3.4.13 to 3.4.14 (#5993)
Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.4.13 to 3.4.14. - [Release notes](https://github.com/go-ldap/ldap/releases) - [Commits](https://github.com/go-ldap/ldap/compare/v3.4.13...v3.4.14) --- updated-dependencies: - dependency-name: github.com/go-ldap/ldap/v3 dependency-version: 3.4.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
814cda3fb4 |
feat(xray): update xray-core to v26.7.11 and adapt panel
Bump xtls/xray-core to 50231eaf (v26.7.11) and the three binary pins (DockerInit.sh, release.yml x2) in lockstep. Adapt the panel to the upstream changes: - Shadowsocks "none"/"plain" and VMess "none"/"zero" were removed from the core. A migration rewrites stored none/plain SS methods to a supported cipher and none/zero VMess security to "auto" (on both the clients column and inbound settings JSON); the SS build-time heal does the same so a row injected after boot cannot brick startup. The removed values are dropped from every frontend option list, schema and adapter, and coerced to "auto" at the Go link/sub/Clash emit sites and both link importers. Fix the CipherType_NONE sentinel that no longer compiles. - Unencrypted vless/trojan outbounds to a public address are now refused by the core. Validate outbounds through the vendored config loader when saving the xray template and when storing/merging outbound subscriptions, so one such outbound cannot keep the core from starting. - New TCP finalmask type "xmc" (Minecraft mimicry): add it to the sub link allowlist, the frontend enum and the FinalMask form (hostname, usernames, required password), and document it. - streamSettings gained a "method" alias for "network"; canonicalize it to "network" at inbound save time and in the form adapters/schema so a method-keyed config keeps its transport. - New root "env" config key is passed through xray.Config, compared in Equals, and forces a restart in the hot diff. - REALITY now defaults minClientVer to 26.3.27; update the form placeholder. |
||
|
|
3d513b5084 |
chore(deps): bump golang.org/x/text from 0.38.0 to 0.40.0 (#5872)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.38.0 to 0.40.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.38.0...v0.40.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-version: 0.40.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
7c183dbd97 |
fix(clients): surface bulk-reset auto-enable failures (#5763)
* fix(clients): surface bulk-reset auto-enable failures BulkResetTraffic re-enables a disabled client before resetting its traffic, but discarded the s.Update result with `_, _ =`, so a failed re-enable was silent: the client stayed disabled with nothing logged, unlike the single-client ResetTraffic path which already warns on the same call. Check the error and log a warning to match, and add a regression test covering BulkResetTraffic's previously-untested re-enable path. * ci: update Go toolchain for govulncheck --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
ccd56a56a8 |
chore(deps): bump github.com/klauspost/compress from 1.18.6 to 1.19.0 (#5731)
Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.18.6 to 1.19.0. - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](https://github.com/klauspost/compress/compare/v1.18.6...v1.19.0) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.19.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
7a844682b3 |
chore(deps): bump github.com/shirou/gopsutil/v4 from 4.26.5 to 4.26.6 (#5730)
Bumps [github.com/shirou/gopsutil/v4](https://github.com/shirou/gopsutil) from 4.26.5 to 4.26.6. - [Release notes](https://github.com/shirou/gopsutil/releases) - [Commits](https://github.com/shirou/gopsutil/compare/v4.26.5...v4.26.6) --- updated-dependencies: - dependency-name: github.com/shirou/gopsutil/v4 dependency-version: 4.26.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
6626bf4a07 |
chore(deps): bump google.golang.org/grpc from 1.81.1 to 1.82.0 (#5729)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.81.1 to 1.82.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.81.1...v1.82.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.82.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
8332ba67ae |
chore(deps): bump antd to 6.5 and migrate deprecated component props
Upgrade frontend deps (antd 6.4.5 -> 6.5.0, Ant Design icons, TanStack Query, i18next, eslint) and fasthttp 1.71 -> 1.72. AntD 6.5 deprecated several Input/Card/Space props, so adapt the panel UI: - Input/InputNumber addonBefore/addonAfter -> prefix/suffix - Card bordered -> variant="outlined" - Space direction -> orientation - swap the hand-rolled Telegram SVG for the new TelegramFilled icon - guard SettingListItem against cloning aria-labelledby onto a Fragment, which only accepts key/children |
||
|
|
bbfbd7eba6 |
Bump minimum eligible Xray version
Update Xray release filtering to only include versions at or above v26.6.27 (previously v26.4.25). Also mark `google.golang.org/protobuf` as a direct dependency in `go.mod` by removing the `// indirect` annotation. |
||
|
|
e44075a6e0 |
chore(deps): bump xray-core to v26.6.27
Update the xray-core Go module (infra/conf builders + gRPC command clients) and the bundled binary pin in DockerInit.sh and the release workflow from v26.6.22 to v26.6.27. No gRPC command-API breaking changes. The release's other inbound work rides along with the bump: TUN autoSystemRoutingTable/autoOutboundsInterface are already modeled in the frontend tun schema, while Hysteria vlessRoute (UUID-derived) and the TUN traffic counters are internal to xray-core and need no panel changes. |
||
|
|
dc6d13b58f |
chore: bump deps and modernize test loops
- release.yml: download-artifact v7 -> v8 - frontend: i18next 26.3.1 -> 26.3.2, qs 6.15.2 -> 6.15.3 - go.mod: consolidate indirect requires (go mod tidy) - tests: adopt Go 1.22 range-over-int loops |
||
|
|
a2961fd046 |
Update Xray to v26.6.22
Point CI workflow and DockerInit.sh to Xray v26.6.22 (update download URLs for Linux and Windows). Update go.mod to the matching github.com/xtls/xray-core pseudo-version and bump github.com/pion/stun to v3.1.6; refresh corresponding go.sum entries. |
||
|
|
0483273839 |
fix(tls): pin remote cert via native uTLS handshake instead of xray subprocess
GetRemoteCertHash shelled out to 'xray tls ping' and scraped its stdout, which swallowed the real failure (a refused dial surfaced only as 'no certificate hash found'). Replace it with a native uTLS Chrome handshake: dial/handshake errors now surface verbatim, host:port is honoured, and the leaf is taken from PeerCertificates[0] so IP-only self-signed certs (no DNS SANs) hash correctly. Mirrors alireza0/x-ui@1372ad0 without its nil-leaf panic. |
||
|
|
3aa76ea05b | fix(deps): bump xray-core past finalmask UDP buffer fix (#5462) | ||
|
|
37c5e0bfd2 |
feat(node): node hardening — mTLS, hashed+zstd reconcile transport, per-node net metrics (#5382)
* fix(api-docs): document clientIpsByGuid route
Restores a green `go test ./...` baseline: TestAPIRoutesDocumented
flagged POST /panel/api/clients/clientIpsByGuid (added in
|
||
|
|
dc781b28c4 | chore(deps): bump telego to v1.10.0 | ||
|
|
7605902324 |
Test-quality audit: fix 2 prod bugs, strengthen weak tests, add mutation/fuzz/CI tooling (#5345)
* test(audit): add gremlins/rapid/coverage tooling + AUDIT.md scaffold * test(audit): hygiene sweep (race-clean except logger global; Finding #2) + smell inventory * test(audit): cover untested error/edge branches (TLS proxy+pin, migration tag cleanup=Finding #1) * test(audit): strengthen internal/sub link tests (dedup key, TLS/Reality mapping, clash well-formedness) * test(audit): property (rapid) + fuzz tests for joinHostPort/userinfo/pin/ParseLink * test(audit): tighten frontend subSortIndex rejection assertions + wire coverage * ci(audit): add shuffle gate + non-blocking race job (Finding #2) + fuzz-smoke; document mutation policy * chore(audit): gitignore frontend coverage output * test(audit): exhaustive whole-repo pass — strengthen 5 weak/fake tests (netproxy, CSP, modal per-protocol loops, schema coercions) * docs(contributing): add Testing section (conventions, race/shuffle, fuzz, mutation policy); drop AUDIT.md ledger * fix(logger,migration): guard logBuffer with mutex; execute legacy tag cleanup (tx.Exec); make CI race gate blocking * ci(mutation): add nightly scoped gremlins workflow (informational artifacts) * test(audit): strengthen runtime tests — baseURL scheme/port bounds, isNonEmptySlice, trafficReset * test(audit): strengthen clash tests — reality field mapping + tcp-header validation * test(audit): runtime — egress-proxy + content-type tests; drop redundant bp=='' branch * test(audit): strengthen link parser/helper tests (defaultPort, splitComma, base64, canonicalQuery, tls/reality/transport mapping) * test(audit): strengthen sub/xray/common/netsafe/mtproto/config/middleware tests (kill surviving mutants) * test(audit): raise timeout on protocol-iteration modal tests (heavy re-renders, slow on CI) * fix(logger): GetLogs returns at most c entries (off-by-one fix; addresses PR review) * perf(logger): snapshot logBuffer under lock so GetLogs doesn't block logging; clarify fuzz-seed docs (addresses PR review) |
||
|
|
cd46730bb9 |
Bump Go indirect deps; update frontend lock
Bump several Go indirect dependencies (golang.org/x/exp, golang.org/x/tools, google.golang.org/genproto) and update go.sum accordingly. Regenerate frontend/package-lock.json to record updated npm package versions (including Ant Design and related rc-component packages and other transitive updates). |
||
|
|
eee652c4a5 |
chore(deps): bump golang.org/x/net from 0.55.0 to 0.56.0 (#5199)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.55.0 to 0.56.0. - [Commits](https://github.com/golang/net/compare/v0.55.0...v0.56.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.56.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
9acde8da9d |
Bump frontend version and deps
Update frontend package.json and refresh dependencies for a new release (frontend version -> 0.3.0). Regenerated lockfile and upgraded multiple JS packages (notably @swagger-api/apidom family, @rc-component packages, codemirror, etc.) and added libc metadata where applicable. Also update Go module dependencies (go.mod and go.sum) as part of routine dependency maintenance. |
||
|
|
72944daab7 | chore(deps): bump xray-core to v1.260327.1 and add pion/wireguard deps | ||
|
|
039d05a743 |
fix(ci): bump Go to 1.26.4 and exempt /panel/groups SPA route from api-docs test
- Bump go directive to 1.26.4 to pick up stdlib security fixes in crypto/x509, mime and net/textproto flagged by govulncheck - Add /panel/groups to the api_docs_test SPA-page allowlist so the UI page route is not treated as an undocumented API endpoint - go.sum carries pgx/v5 v5.10.0 bump |
||
|
|
eee5e8f6b6 |
Update Go module dependency versions
Bump several Go module versions in go.mod and regenerate go.sum. Updated dependencies include github.com/go-playground/validator/v10 (v10.30.2 -> v10.30.3), github.com/shirou/gopsutil/v4 (v4.26.4 -> v4.26.5), github.com/ebitengine/purego (v0.10.0 -> v0.10.1), github.com/rogpeppe/go-internal (v1.14.1 -> v1.15.0), golang.org/x/exp (updated pseudo-version), and google.golang.org/genproto/googleapis/rpc (updated pseudo-version). These are routine patch/minor updates to pick up fixes and checksum changes. |
||
|
|
9d9737f470 |
feat(settings): panel network proxy for the panel's own outbound requests
Add a panelProxy setting that routes the panel's self-initiated HTTP requests (geo updates, Xray version/core download, panel update check) through an admin-configured socks5/http(s) proxy, to bypass server-side filtering of GitHub/Telegram. The Telegram bot falls back to it when tgBotProxy is empty (socks5 only). New util/netproxy.NewHTTPClient builds the proxied client. Also fix the Mixed-inbound SOCKS/HTTP share URLs that had host:port and user:pass in the wrong order, and consolidate the Telegram settings tab (move API server into the general tab, drop the empty Proxy & Server tab). |