← Back to README · Русский · 简体中文
Sections: Service account and permissions · Subscription security · SSH access to the VPS · Desktop GUI credentials
Xray runs as the system user xray. The drop-in
/etc/systemd/system/xray.service.d/security.conf sets User=xray and narrows the capabilities to
CAP_NET_BIND_SERVICE, which is enough for low ports. The manager state is deliberately outside the
service account's write boundary.
The ownership model is:
| Path or class | Owner and access |
|---|---|
/usr/local/etc/xray/ and its state directories |
Mostly root-owned and not service-writable; generated metadata such as .server_country may be xray-owned |
config.json, profiles and migration markers |
Normally root:root and readable by Xray as required; a rollback helper may set the live config to root:xray with mode 0640 |
| Reality and VLESS private key files | root:root, mode 0600 |
| public key files and generated metadata | Readable where Xray or the subscription handler needs them; ownership can differ for generated metadata |
/usr/local/etc/xray/scripts/ and /usr/local/bin/xrayebator* |
root:root, executable manager scripts protected from service-account replacement |
/var/log/xray/ |
writable by the Xray runtime account for service logs |
The service account can read the configuration it needs, but cannot replace executable manager scripts,
markers or private keys. Most state/config files are root-protected, not universally immutable or
necessarily root:root; do not treat .server_country as a private key or as a permission boundary.
Backups are root-owned and kept separately from the live files. Do not put unrelated secrets in the
Xray state directory.
Runtime configuration changes made by xrayebator use a backup, validated atomic write and the
safe_restart_xray transaction. This scope is limited to runtime mutations owned by xrayebator.
Install and update have separate validation, restart and rollback paths and are not guaranteed to be
identical. After either lifecycle update, the operator should verify Xray, DNS and the subscription
endpoint/service rather than assuming that a successful script exit proves every path is healthy.
The subscription URL is a bearer credential. It is not public, but anyone holding the full URL can download the route list and the token-protected subscription resources.
Already handled server-side:
- a 32-character hexadecimal token generated by
openssl rand -hex 16; /sub/without a valid token returns an identical404;- a profile with no live routes returns
410and serves no routes; a partially stale multi-route profile can still return its remaining live routes with200; - nginx adds
Cache-Control: no-storeand rate-limits the subscription location; - the root path and paths outside
/sub/return404; Revokerotatessub_token, so the old URL stops working.
Left to the operator:
- do not post the
subscription_urlin public chats or issue trackers; - press
Revokeimmediately after a leak; - treat every saved
vless://link as a credential too; - never hand a local-only URL to an external client;
- do not host third-party panels or proxies on the same domain without understanding the nginx configuration.
Xrayebator can be installed straight from root, but a dedicated user with narrowly scoped sudo is
the better practice.
On the server:
adduser <username>
usermod -aG sudo <username>
su - <username>On your own machine:
ssh-keygen -t ed25519 -C <your_email@example.com>
ssh-copy-id <username>@<server_ip>Then log in as <username>@<server_ip>. Once key-based login works, disable passwords and optionally
forbid root login:
sudo nano /etc/ssh/sshd_configPermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Losing the SSH keys means losing access to the server. Confirm that key-based login works before disabling passwords.
A keep-alive helps the SSH session survive VPN reconfiguration:
ClientAliveInterval 60
ClientAliveCountMax 120
TCPKeepAlive yes
The active Electron GUI supports SSH passwords and private keys, with direct-root or sudo execution. Passwords, sudo passwords, key passphrases and private-key bytes are kept only in the active form or operation; they are not persisted. A private key is read only after it was selected through the native Electron file dialog.
The GUI does persist the server metadata needed to return to a server, including the host, SSH port,
username, authentication method, privilege mode and selected key path. It also persists preferences,
the subscription_url, fetched vless:// links and the SHA-256 SSH host-key pin. The subscription
URL and VLESS links are bearer credentials, so protect the local Electron application data and revoke
the subscription if they leak.
SSH host keys use trust on first successful authentication. The fingerprint is then pinned; any later mismatch fails closed before commands are executed. After an intentional VPS reinstall, explicitly reset the pin in Server Settings and confirm the new key on the next successful connection.
keytar is listed in package.json, but the active Electron GUI does not use it to store SSH
passwords or passphrases in an operating-system keychain. The secrets remain session-only.
See Electron Desktop GUI for the complete Electron boundary, command adapters and packaging details.