Skip to content

Sir-MmD/vpn-ui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2,312 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English | فارسی | العربية | 中文 | Español | Русский | Türkçe

VPN-UI Logo

This project is an enhanced version of the 3X-UI panel (version 2.9.3). The goal of this project is to add various protocols and set it up as an all-in-one panel with support for Xray-core features.

New Protocols

  • PPTP
  • L2TP (RAW)
  • L2TP/IPsec
  • OpenVPN
  • OpenConnect (cisco)
  • SSTP
  • IKEv2
  • WireGuard (C)
  • AmneziaWG (obfuscated WireGuard)
  • MTProto Proxy (Telegram)
  • SSH

New Features

  • Client to Client support, even as Cross Inbound (an internal connection between an L2TP user and an OpenVPN user)
  • Added AES-256-GCM and AES-128-GCM Encryption to the Shadowsocks protocol
  • Support for XHTTP Object in Outbound
  • Automatic installation script for WARP-CLI (Cloudflare's official version)
  • A patched Xray-core that fixes the "Unsupported Cipher" error in the Shadowsocks protocol
  • Bundling all files (Geofile, Xray-core, and Backend cores) into a single binary
  • Exporting account links as TXT and PDF
  • Ability to Freeze accounts
  • Added checkboxes to clients and Inbounds
  • Bulk Operation support:
    • Bulk change of accounts' traffic
    • Bulk change of accounts' days
    • Bulk enable/disable of accounts
    • Bulk delete of accounts
    • Bulk delete of Inbounds
    • Bulk Freeze/Un-Freeze of accounts

Tested Operating Systems

Distribution Version Version
Ubuntu Ubuntu 24.04 26.04
Debian Debian 12 13
Fedora Fedora 43 44
AlmaLinux AlmaLinux 9 10
Rocky Linux Rocky Linux 9 10
CentOS Stream CentOS Stream 9 10
Arch Linux Arch Linux Rolling

Important

It is strongly recommended that you install the panel on one of the tested operating systems, because there is a high chance that the new cores will not work correctly on other operating systems!

Note

AmneziaWG runs on Debian 12/13 and Ubuntu 24.04/26.04 only. Unlike every other protocol, AmneziaWG is not in any distribution's kernel: the panel compiles its kernel module on your server during setup. That module currently fails to build in two cases. On kernel 7.1 or newer (Fedora 43/44, Arch) the kernel removed the ipv6_stub symbol the module still uses. On AlmaLinux, Rocky Linux and CentOS Stream the backported RHEL kernels collide with the module's compatibility layer, and EL10 is not recognised by it at all. Both are limitations of the upstream AmneziaWG module, with fixes still open upstream, so they are not something the panel can configure around. Setup detects this and tells you, rather than failing silently. Every other protocol works normally on all tested operating systems.

Installing the Panel

curl -Ls https://raw.githubusercontent.com/Sir-MmD/vpn-ui/refs/heads/main/deploy.sh | sudo bash

Uninstalling the Panel

sudo /opt/vpn-ui/vpn-ui-amd64 --uninstall

Note

The database path, the systemd service, and all default ports have been changed, so you can install this panel alongside your other panels without any issues.

Screenshots

Overview Core Settings

How the New Protocols Interact with Xray-core

flowchart TB
  Client["VPN Client<br/>(L2TP/IPsec · PPTP · OpenVPN · OpenConnect · SSTP · IKEv2 · WireGuard (C) · AmneziaWG)"]
  TGC["Telegram Client<br/>(MTProto Proxy)"]
  SSHC["SSH Client<br/>(ssh -D dynamic SOCKS · badvpn-udpgw for UDP)"]

  subgraph PANEL["vpn-ui panel — root process"]
    PROC["procmgr<br/>supervises the daemons"]
    RAD["in-binary RADIUS<br/>127.0.0.1:1812 auth · :1813 acct"]
    HOOK["OpenVPN hooks<br/>auth / connect / disconnect / evict"]
    CONF["writes Xray config:<br/>dokodemo-door inbound +<br/>per-account source-IP routing"]
    STAT["reads Xray stats (gRPC)<br/>enforces traffic / device limits"]
    SSHSRV["in-binary SSH gateway (x/crypto/ssh)<br/>no daemon, no bundle: direct-tcpip + udpgw"]
  end

  subgraph DAEMON["Bundled VPN daemons (panel children)"]
    D["xl2tpd + strongSwan/charon · pptpd · openvpn · ocserv · accel-ppp<br/>(pppd for L2TP/PPTP · accel-ppp for SSTP · charon for IKEv2)"]
    MT["telemt (MTProto Proxy)<br/>userspace relay: no tunnel, no pool IP"]
  end

  subgraph KERNEL["Linux kernel data plane"]
    IFACE["ppp0 / tun0 / wgc0 / awg0<br/>client is assigned a pool IP"]
    NFT["nftables mark:<br/>UDP → TPROXY · TCP → REDIRECT"]
    RULE["ip rule fwmark 1 → table 100"]
  end

  subgraph XRAY["Xray-core (bundled, panel-managed)"]
    DOKO["dokodemo-door inbound<br/>sockopt tproxy, mark 255"]
    SOCKS["socks inbound (loopback)<br/>tag = MTProto / SSH inbound<br/>username = account"]
    ROUTE{"routing:<br/>match source IP → account<br/>or socks username → account"}
    OUT["outbound<br/>freedom / proxy / WARP"]
  end

  NET["Internet"]

  %% control plane
  Client -->|"tunnel + credentials"| D
  Client -.->|"WireGuard (C): in-kernel wgc, no daemon"| IFACE
  Client -.->|"AmneziaWG: in-kernel awg (DKMS module), no daemon<br/>obfuscated handshake: Jc/Jmin/Jmax · S1/S2 · H1-H4"| IFACE
  TGC -->|"obfuscated2 / dd / FakeTLS secret"| MT
  SSHC -->|"username + password (checked in-process, no RADIUS)"| SSHSRV
  D -.->|"MS-CHAPv2 Access-Request"| RAD
  RAD -.->|"Accept + pool IP"| D
  D -.->|"user-pass / client-connect"| HOOK
  HOOK -.->|"lease per-account IP"| D
  PROC --- D
  CONF --> DOKO
  CONF --> ROUTE

  %% data plane
  D -->|"decapsulated packets"| IFACE
  IFACE --> NFT --> RULE --> DOKO
  DOKO --> ROUTE --> OUT --> NET
  MT -->|"relayed TCP, socks user = account"| SOCKS
  SSHSRV -->|"direct-tcpip → socks CONNECT · udpgw → socks UDP ASSOCIATE<br/>socks user = account"| SOCKS
  SOCKS --> ROUTE

  %% accounting + return
  OUT -.->|"per-account counters"| STAT
  MT -.->|"per-account octets (Prometheus scrape)"| STAT
  SSHSRV -.->|"per-account octets (in-process counters)"| STAT
  STAT -.->|"disconnect over-limit"| RAD
  NET -.->|"replies (symmetric path back)"| OUT
Loading

How RBridge Handles Non-RADIUS Protocols

WireGuard (C), AmneziaWG and the IKEv2 PSK / EAP-TLS modes authenticate with a public key or a certificate, so they never make a RADIUS round-trip. On their own they would get no session record, no traffic accounting, and no User Limit enforcement. RBridge (Radius Bridge) closes that gap: once per traffic tick its Sweeper polls each protocol's live tunnels, enforces quota, disable, and the per-account User Limit K (evicting the losers), then reconciles the survivors into the very same in-binary RADIUS session registry and nftables accounting the RADIUS protocols already use. A key-based protocol therefore behaves identically for usage, quota, and device limits, and egresses through the same Xray dokodemo-door data plane.

For the two key-based tunnel protocols, WireGuard (C) and AmneziaWG, a User Limit of K provisions K device slots per account: K keypairs, K configs and K distinct tunnel IPs, one config per device. That is the same model the commercial providers use, and it is what makes a single account usable on a phone, a laptop and a router at once without the devices fighting over one key.

flowchart TB
  subgraph SRC["Non-RADIUS protocols (public-key / certificate auth, no RADIUS round-trip)"]
    WG["WireGuard (C)<br/>in-kernel, wgctrl-managed"]
    AWG["AmneziaWG<br/>in-kernel amneziawg (DKMS), obfuscated"]
    IKE["IKEv2 PSK / EAP-TLS<br/>strongSwan charon"]
  end

  subgraph BRIDGE["RBridge, the Radius Bridge (one pass per traffic tick)"]
    SWEEP["Sweeper.Tick()"]
    P1["1 · Poll live tunnels via each Adapter"]
    P2["2 · Enforce quota + disable<br/>+ User-Limit K + strategy"]
    P3["3 · Reconcile survivors into the Sink"]
  end

  subgraph SINK["Sink, the existing RADIUS session model"]
    REG["in-binary RADIUS<br/>session registry"]
    ACCT["nftables per-account counters<br/>→ client_traffics (usage / quota)"]
  end

  XRAY["Xray-core<br/>source-IP routing → outbound → Internet"]

  %% control plane
  WG -.->|"peers + last-handshake"| P1
  AWG -.->|"peers + last-handshake"| P1
  IKE -.->|"active SAs + Framed-IP"| P1
  SWEEP --> P1 --> P2 --> P3
  P2 -.->|"evict: remove peer / terminate SA"| WG
  P2 -.->|"evict: remove peer"| AWG
  P2 -.->|"evict: terminate SA"| IKE
  P3 -->|"tunnel IP → account"| REG
  P3 -->|"add / remove counters"| ACCT
  ACCT -.->|"disabled / over-quota"| P2

  %% data plane
  WG ==> XRAY
  AWG ==> XRAY
  IKE ==> XRAY
  ACCT -.- XRAY
Loading

Building from Source

git clone https://github.com/Sir-MmD/vpn-ui.git && cd vpn-ui
./build.sh

E2E Testing

E2E Test

A complete E2E test written in Python has been designed for this project inside the test_unit folder, which you are welcome to use. The steps are as follows:

  1. Go into the test_unit folder and enter your desired settings in config.toml.
  2. Run the setup.sh script.
  3. Place the compiled binary inside the test_subject folder.
  4. Run run.sh with sudo privileges.

Important

The full E2E test is extremely time-consuming; if you have only made a small change to the project, it is better to test only that specific part using the --tests switch:

Test ID Description
core-init provision kernel modules + packages + xray core
server-setup create inbounds + accounts + source-IP routing rules
openvpn connect variants + checks + peer reachability (OpenVPN)
l2tp connect variants + checks + peer reachability (L2TP/IPsec)
pptp connect variants + checks + peer reachability (PPTP)
openconnect connect variants + checks + peer reachability + same-NAT user-limit (OpenConnect/ocserv)
sstp connect variants + checks + peer reachability (SSTP/accel-ppp, PPP-over-TLS)
ikev2 connect + checks + peer reachability (IKEv2/IPsec, strongSwan charon; eap-mschapv2 + psk + eap-tls)
wg-c connect + checks + peer reachability + per-account usage/termination (WireGuard C, in-kernel wgctrl, gateway /29, + preshared-key mode)
awg connect + checks + peer reachability + per-account usage/termination (AmneziaWG, in-kernel amneziawg DKMS module, obfuscation params, + preshared-key mode)
mtproto alias: runs every MTProto phase below (MTProto Proxy, telemt)
mtproto-classic handshake + relay to a real Telegram DC + wrong-secret control + usage (obfuscated2)
mtproto-secure same, "dd" random-padding secret
mtproto-tls same + FakeTLS ServerHello HMAC verified, "ee" secret
mtproto-toggle editing an account's modes takes effect on the RUNNING daemon (no restart)
mtproto-termination quota auto-disables the account AND the proxy stops relaying for it
mtproto-adtag an ad tag forces middle-proxy egress and drops the inbound's Xray routing, and clearing it restores both
ssh connect + checks + routing + user-limit + both strategies + per-account usage/termination (SSH relay, in-binary Go gateway)
ssh-udp UDP through the relay: udpgw terminated in-process and bridged to Xray via SOCKS5 UDP ASSOCIATE, plus accounting
bulk-ops bulk client add/sub/enable/disable + TXT/PDF export via API
backup-restore DB export + import round-trip
warp-socks Cloudflare warp-cli SOCKS install + egress
random-cfg --random switch: randomize port + creds + webpath, then restore
systemd --systemd switch: install + run the panel as a systemd unit
uninstall --uninstall switch: install everything, tear down, assert clean host
export-js host-side Node TXT/PDF export test (no VM)

To test on only one specific operating system, you can use the --only switch:

sudo ./run.sh --only ubuntu-24

Donate

🔹USDC-Polygon: 0xdC2Ab962954e8fA1502C44656c5A32CF2979568C

🔹USDT-BEP20: 0xdC2Ab962954e8fA1502C44656c5A32CF2979568C

🔹USDT-TRC20: TXEhckDXtdLGAjP5PZXfNnQjPHzEVTcBmR

🔹TRX: TXEhckDXtdLGAjP5PZXfNnQjPHzEVTcBmR

🔹LTC: ltc1qmapmnuf6cq9x679nmu0k4uyq779mxxcwnkgdll

🔹BTC: bc1q62w7lyndzndsp74vj4dsayvun8xnapzq6hx5ea

🔹ETH: 0xdC2Ab962954e8fA1502C44656c5A32CF2979568C