Skip to content

feat(wireguard): support WireGuard as an Xray inbound - #672

Open
bitwiresys wants to merge 6 commits into
PasarGuard:devfrom
bitwiresys:pr-wg-panel
Open

feat(wireguard): support WireGuard as an Xray inbound#672
bitwiresys wants to merge 6 commits into
PasarGuard:devfrom
bitwiresys:pr-wg-panel

Conversation

@bitwiresys

@bitwiresys bitwiresys commented Jul 3, 2026

Copy link
Copy Markdown

What

Lets the panel treat a wireguard inbound inside an Xray core as a first-class inbound: its tag is exposed (so users can be assigned and subscriptions generated for it), and its per-user traffic / online state are recorded. Purely additive — the standalone WireGuard backend (CoreType.wg) is untouched.

Why

Xray now ships a first-class WireGuard inbound with a UserManager (XTLS/Xray-core#6360, in v26.6.27). That makes it possible to serve WireGuard through the same Xray core as VLESS/VMess/Trojan/Shadowsocks — gaining Xray routing, sniffing, unified stats, and transport obfuscation (finalmask) for WG clients — as an alternative to the kernel WG backend. For the panel to use it, the Xray config reader must recognize a wireguard inbound, and usage recording must map WG stats back to panel users.

How

  • app/core/xray.py — add _read_wireguard_inbound, dispatched from _read_inbound when protocol == "wireguard". Surfaces the inbound tag (appears in /api/inbounds) and captures public params (listen port, address, mtu, derived public key).
  • app/jobs/record_usages.py — Xray's WG inbound reports per-user stats keyed by the peer's hex public key (user>>>{hex_pubkey}>>>traffic) rather than the numeric {id}. Add a cached {hex_public_key -> user_id} map and translate those stat names before recording, so WG traffic and the online indicator attribute to the right user. Non-WG stats unaffected (the map is only consulted for non-numeric stat names).

Compatibility / notes

Testing

  • ruff check passes on the changed files; py_compile clean.
  • WG-in-Xray verified end-to-end against a live node: user connects, used_traffic climbs and online_at updates each cycle; 15-minute soak, no disconnects, no client/server errors.

Summary by CodeRabbit

  • New Features

    • Added WireGuard support in Xray inbounds, including automatic handling of public keys, addresses, MTU (defaulting to 1420), and UDP networking.
    • WireGuard inbounds are now properly included and tagged in configuration editing.
  • Bug Fixes

    • Improved WireGuard inbound preparation to default missing pre-shared keys to an empty value.
    • Enhanced usage statistics processing to better handle malformed responses.
  • UI/Validation

    • Updated inbound form validation and rendering rules for WireGuard inbounds during edit/commit.
  • Sanitization

    • Adjusted WireGuard inbound sanitization to remove transport/security fields when applicable.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 21a6af66-a42d-46b7-9502-637f9d1f874c

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd1367 and 2bf15bb.

📒 Files selected for processing (5)
  • app/core/hosts.py
  • app/core/xray.py
  • app/jobs/record_usages.py
  • dashboard/src/features/core-editor/components/xray/xray-inbounds-section.tsx
  • dashboard/src/features/core-editor/kit/sanitize-inbound.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/core/xray.py

Walkthrough

Xray now parses and indexes WireGuard inbounds, while the dashboard sanitizes, validates, renders, and auto-generates WireGuard-specific settings. Subscription preparation defaults missing pre-shared keys to empty strings, and usage-statistics handling receives comment and syntax updates.

Changes

WireGuard inbound support

Layer / File(s) Summary
Sanitize and edit WireGuard inbounds
dashboard/src/features/core-editor/...
WireGuard tags, validation, rendering, and sanitized fields now follow WireGuard-specific rules.
Parse and prepare WireGuard settings
app/core/xray.py, app/core/hosts.py
WireGuard settings are parsed, public keys derived, inbounds indexed, and missing pre-shared keys defaulted to empty strings.

Usage statistics processing cleanup

Layer / File(s) Summary
Update usage-statistics processing
app/jobs/record_usages.py
Processing comments and multi-exception syntax are clarified without changing error handling behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Dashboard
  participant XrayConfig
  participant WireGuardKeyHelper
  participant InboundIndexes
  Dashboard->>XrayConfig: submit sanitized WireGuard inbound
  XrayConfig->>WireGuardKeyHelper: derive public key from secret key
  XrayConfig->>InboundIndexes: register WireGuard tag and settings
Loading

Possibly related PRs

Suggested labels: enhancement, Backend

Suggested reviewers: x0sina, immohammad20000

Poem

A rabbit hops through tunnels bright,
WireGuard keys take graceful flight.
Stats gather neatly, errors pair,
Clean lines of code float through the air.
Thump, thump—everything’s just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding WireGuard support as an Xray inbound.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ImMohammad20000

Copy link
Copy Markdown
Contributor

is ther any changes require in https://github.com/PasarGuard/node_bridge_py ?

@bitwiresys

Copy link
Copy Markdown
Author

is ther any changes require in https://github.com/PasarGuard/node_bridge_py ?

no change required

@bitwiresys

bitwiresys commented Jul 12, 2026

Copy link
Copy Markdown
Author

Synced with upstream main and dropped the WireGuard stats workaround, see PasarGuard/node#64 for the root cause.

record_usages.py kept a hex-pubkey-to-user-id cache to translate stats keys for the WireGuard inbound, on the assumption xray reports WG peers by their public key. But the node already sends str(user.id) as Email for WireGuard users, same as vmess/vless/trojan/shadowsocks/hysteria. Once both the xray-core side and node side stop overriding Email with the pubkey, WireGuard stats arrive keyed by the plain id like every other protocol. The mapping had nothing to attach to, it just silently dropped WireGuard stats.

Also fixed a Python 2-style except ValueError, TypeError: in _process_users_stats_response that would have crashed the job at runtime, and removed the unused base64 import.

Verified end-to-end in Docker: panel + node + xray v26.7.11, real WireGuard client, traffic through the tunnel, used_traffic updates correctly on the user, no pubkey mapping involved.

@ImMohammad20000

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/core/xray.py (1)

419-425: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Narrow the exception clause and validate the port.

Catching ValueError is safer than a broad Exception, which could mask unexpected errors like TypeError. Additionally, explicitly validating that the port key exists ensures consistency with how other inbounds handle missing ports via _handle_port_settings.

♻️ Proposed refactor
-        try:
-            public_key = get_wireguard_public_key(secret_key) if secret_key else ""
-        except Exception:
-            public_key = ""
-        settings = self._create_base_settings(inbound)
-        settings["listen_port"] = inbound.get("port")
+        try:
+            public_key = get_wireguard_public_key(secret_key) if secret_key else ""
+        except ValueError:
+            public_key = ""
+        settings = self._create_base_settings(inbound)
+        
+        if "port" not in inbound:
+            raise ValueError(f"{inbound['tag']} inbound doesn't have port")
+        settings["listen_port"] = inbound["port"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/core/xray.py` around lines 419 - 425, In the public key setup around
get_wireguard_public_key, catch only ValueError instead of broad Exception.
Replace the direct inbound port assignment in the settings construction with the
existing _handle_port_settings validation so missing or invalid port keys follow
the established inbound behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/core/xray.py`:
- Around line 419-425: In the public key setup around get_wireguard_public_key,
catch only ValueError instead of broad Exception. Replace the direct inbound
port assignment in the settings construction with the existing
_handle_port_settings validation so missing or invalid port keys follow the
established inbound behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4a7b9aea-3791-4b86-a901-f2790949bb36

📥 Commits

Reviewing files that changed from the base of the PR and between f3b64bb and 6cd1367.

📒 Files selected for processing (2)
  • app/core/xray.py
  • app/jobs/record_usages.py

bitwiresys added 3 commits July 22, 2026 21:53
Recognize a `wireguard` protocol inbound inside an Xray core so its tag is
exposed (via /api/inbounds) and users can be assigned to it, and attribute
its per-user traffic/online state.

Xray now ships a first-class WireGuard inbound with a UserManager
(XTLS/Xray-core#6360), so WireGuard can be served through the same Xray
core as the other protocols — in addition to the existing standalone WG
backend (CoreType.wg). This is purely additive.

- app/core/xray.py: `_read_wireguard_inbound` registers a WG-in-Xray
  inbound (interface/listen_port/address/mtu/public key) alongside the
  vmess/vless/trojan/shadowsocks/hysteria readers.
- app/jobs/record_usages.py: the Xray WG inbound reports per-user stats
  keyed by the peer's hex public key; map that back to the panel user id
  so WG traffic and online state are recorded like any other protocol.
The merged xray-core PR (XTLS/Xray-core#6360) matches WireGuard peers
on Email like every other inbound, and the node already sends
str(user.id) as the Email for WireGuard users (app/node/user.py,
serialize_user) same as vmess/vless/trojan/shadowsocks/hysteria. So
UsersStat has always arrived keyed by the plain user id for WireGuard
too — the pubkey->uid cache in _get_wg_pubkey_to_uid() was solving a
problem that didn't exist, added under the assumption that WireGuard's
xray-side Email held the peer's public key.

Also fixes an unrelated Python 2-style `except ValueError, TypeError:`
in _process_users_stats_response that would have crashed the job at
runtime, and drops the now-unused base64 import.

Verified end-to-end: real WireGuard client -> xray wireguard inbound
-> record_user_usages correctly attributes traffic to the user's id
with no mapping step.
@bitwiresys

Copy link
Copy Markdown
Author

Rebased onto the latest dev (v5.1.0) and added fixes for the WireGuard inbound in the core editor:

  • Edit dialog rendered the transport/security form instead of the WireGuard fields, because the editor hydrates transport/security onto the inbound and the branch was picked by key presence. Secret key, MTU, address and peers were unreachable after saving an inbound.
  • commitEditInbound matched the same transport/security branch first, so wgSecretKey was never validated on edit.
  • Saving a WireGuard inbound persisted the hydrated transport/security keys into the config; they are now stripped in sanitizeProfileInbounds.
  • Auto-generated tag used the transport value and produced WIREGUARD TCP …; WireGuard is UDP-only, and the security segment is skipped for it.

Local CI is green: ruff check, tsc --noEmit and the dashboard build. Backend pytest shows the same pre-existing failures as a clean dev checkout.

@bitwiresys

Copy link
Copy Markdown
Author

One more fix: creating a host for a WireGuard inbound returned 500.

_prepare_subscription_inbound_data passed inbound_config.get("pre_shared_key", None) into SubscriptionInboundData.wireguard_pre_shared_key, which is typed str. A WireGuard inbound has no top-level pre_shared_key (PSK is per peer), so the value was None and pydantic raised a ValidationError on every WireGuard host creation. Since subscriptions are built from hosts, no WireGuard host could exist and the subscription came back empty.

@ImMohammad20000

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added Backend enhancement New feature or request labels Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants