Before you submit
PasarGuard version
Node dev at d44ca8e (0.5.4 plus 8 commits). Xray v26.3.27 (d2758a0), the official release that make install_xray installs.
Installation type
Source / uv
What happened?
When the panel limits, disables, expires or deletes a user, the node removes that user from each Xray inbound with RemoveUserOperation. After that, the user cannot open new connections. But the connections the user already had stay open and keep carrying traffic until the client closes them. A download that was running when the user was limited keeps running at full speed.
A limited user can therefore keep using traffic for as long as their client keeps its tunnel open.
Steps to reproduce
- Start the node's Xray backend with one VLESS TCP inbound and one user,
1.alice.
- As
1.alice, open a connection that downloads continuously.
- Remove the user: call
SyncUser with the same user and no inbounds. This is what the panel sends when a user is limited or expires.
- Open a new connection as
1.alice. It is refused, as expected.
- Keep reading from the connection opened in step 2. It keeps receiving data.
The pull request that follows this issue adds a test that runs these steps against the real core and logs the result of step 5.
Expected behavior
- Removing a user from an inbound closes the connections that user already has on that inbound.
- Sending an unchanged user again does not touch their connections. The panel does this, for example, after an admin edits a user's data limit or expiry date.
Logs and error messages
# node dev d44ca8e, official Xray v26.3.27, Linux container limited to 1.5 CPUs, loopback
=== RUN TestUserSyncKeepsAnUnchangedUsersOpenConnection
user_session_test.go:175: the connection the removed user had already opened received 532602880 bytes in the next second (err <nil>)
--- PASS: TestUserSyncKeepsAnUnchangedUsersOpenConnection (3.59s)
Neither the node nor Xray logs an error. The removal itself succeeds.
Environment
OS: Linux (Alpine container, linux/amd64); also reproduced on macOS
Database: not involved
Python version: not involved
Node.js version: not involved
Browser: not involved
Reverse proxy / CDN: none, loopback only
Go: 1.26.3
Xray: v26.3.27, official release
Relevant configuration
{
"inbounds": [{
"tag": "VLESS TCP SESSION",
"listen": "127.0.0.1",
"port": 10000,
"protocol": "vless",
"settings": {"clients": [], "decryption": "none"}
}],
"outbounds": [{"tag": "direct", "protocol": "freedom"}]
}
Screenshots or videos
No response
Additional context
Root cause, in Xray v26.3.27 (d2758a0)
app/proxyman/command/command.go:55-65: RemoveUserOperation.ApplyInbound only calls UserManager.RemoveUser.
RemoveUser only deletes the user from the validator:
proxy/vless/inbound/inbound.go:245-248
proxy/vmess/inbound/inbound.go:176
proxy/trojan/server.go:124-126
- A connection is authenticated once, when it is accepted (
proxy/vless/inbound/inbound.go:537). Mux sub-streams reuse that user. Nothing closes the connection when the user is removed later.
The node cannot close these connections by itself, because Xray gives it no handle to a user's connections. The fix has to be in the core. XTLS/Xray-core#5844 makes RemoveUser close the user's connections on that inbound.
A node-side change is needed as well. SyncUser and UpdateUsers remove and re-add a user on every inbound the user stays on, even when nothing about the account changed. With a core that closes connections on RemoveUser, every such sync would disconnect the user. I built the head of XTLS/Xray-core#5844 (f356a34) and ran the same test: a SyncUser that changed nothing cut 1.alice's open connection at once:
user_session_test.go:159: open connection stopped after SyncUser sent the same user again: received 0 bytes, err EOF
--- FAIL: TestUserSyncKeepsAnUnchangedUsersOpenConnection (1.54s)
The pull request that follows makes the node remove and re-add a user only when their account on that inbound changed.
Impact. This comes from a production deployment of a fork of this node, which runs the same Xray backend code with Xray v26.3.27. After the panel limited a user, the user's open vless-xhttp and trojan-xhttp downloads were never cut. One ran at full speed to the end of a 480 MiB file, while new connections were refused. At that point, 24 users were 0.5 GB or more over their limit, about 50 GB in total.
Before you submit
PasarGuard version
Node
devatd44ca8e(0.5.4 plus 8 commits). Xray v26.3.27 (d2758a0), the official release thatmake install_xrayinstalls.Installation type
Source / uv
What happened?
When the panel limits, disables, expires or deletes a user, the node removes that user from each Xray inbound with
RemoveUserOperation. After that, the user cannot open new connections. But the connections the user already had stay open and keep carrying traffic until the client closes them. A download that was running when the user was limited keeps running at full speed.A limited user can therefore keep using traffic for as long as their client keeps its tunnel open.
Steps to reproduce
1.alice.1.alice, open a connection that downloads continuously.SyncUserwith the same user and no inbounds. This is what the panel sends when a user is limited or expires.1.alice. It is refused, as expected.The pull request that follows this issue adds a test that runs these steps against the real core and logs the result of step 5.
Expected behavior
Logs and error messages
Neither the node nor Xray logs an error. The removal itself succeeds.
Environment
OS: Linux (Alpine container, linux/amd64); also reproduced on macOS
Database: not involved
Python version: not involved
Node.js version: not involved
Browser: not involved
Reverse proxy / CDN: none, loopback only
Go: 1.26.3
Xray: v26.3.27, official release
Relevant configuration
Screenshots or videos
No response
Additional context
Root cause, in Xray v26.3.27 (
d2758a0)app/proxyman/command/command.go:55-65:RemoveUserOperation.ApplyInboundonly callsUserManager.RemoveUser.RemoveUseronly deletes the user from the validator:proxy/vless/inbound/inbound.go:245-248proxy/vmess/inbound/inbound.go:176proxy/trojan/server.go:124-126proxy/vless/inbound/inbound.go:537). Mux sub-streams reuse that user. Nothing closes the connection when the user is removed later.The node cannot close these connections by itself, because Xray gives it no handle to a user's connections. The fix has to be in the core. XTLS/Xray-core#5844 makes
RemoveUserclose the user's connections on that inbound.A node-side change is needed as well.
SyncUserandUpdateUsersremove and re-add a user on every inbound the user stays on, even when nothing about the account changed. With a core that closes connections onRemoveUser, every such sync would disconnect the user. I built the head of XTLS/Xray-core#5844 (f356a34) and ran the same test: aSyncUserthat changed nothing cut1.alice's open connection at once:The pull request that follows makes the node remove and re-add a user only when their account on that inbound changed.
Impact. This comes from a production deployment of a fork of this node, which runs the same Xray backend code with Xray v26.3.27. After the panel limited a user, the user's open vless-xhttp and trojan-xhttp downloads were never cut. One ran at full speed to the end of a 480 MiB file, while new connections were refused. At that point, 24 users were 0.5 GB or more over their limit, about 50 GB in total.