[GH-ISSUE #3260] Xray protocol: changing SNI via UI updates serverNames but leaves dest unchanged, breaking REALITY handshake for strict clients (e.g. sing-box) #1507

Open
opened 2026-10-13 20:44:00 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @vinser on GitHub (Oct 5, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3260

Summary

When changing the SNI field ("Server name (sni)") for the Xray protocol via the Amnezia-client UI, the server-side server.json inside the amnezia-xray container is updated only in realitySettings.serverNames. The realitySettings.dest field is not updated and keeps the original default value (e.g. www.googletagmanager.com:443).

As a result, the server continues to fetch and present the TLS certificate of the old dest host, while advertising a different SNI to clients. This creates a mismatch between the requested SNI and the returned certificate.

Affected setup

  • Protocol: Xray (VLESS + REALITY, xtls-rprx-vision)
  • Server: Amnezia self-hosted VPS, protocol installed via Amnezia-client (Docker container amnezia-xray)
  • Container entrypoint: dumb-init /opt/amnezia/start.sh
  • Config path inside container: /opt/amnezia/xray/server.json
  • Clients used for testing: sing-box 1.14.x (TUN mode, strict certificate verification), plus Amnezia mobile clients (Android)

Steps to reproduce

  1. Install Xray protocol on a clean VPS via the Amnezia client (Docker container amnezia-xray).
  2. In the Amnezia client UI, open the Xray protocol settings and change the "Server name (SNI)" field from the default value (e.g. www.googletagmanager.com) to another domain, for example files.pythonhosted.org.
  3. Apply changes.
  4. On the server, inspect the container config:
    docker exec -it amnezia-xray cat /opt/amnezia/xray/server.json
    

Actual behavior

The config contains a mismatch:

"realitySettings": {
    "dest": "www.googletagmanager.com:443",
    "fingerprint": "chrome",
    "privateKey": "...",
    "serverNames": [
        "files.pythonhosted.org"
    ],
    "shortIds": [
        "..."
    ]
}
  • serverNames — updated to the new SNI entered in the UI.
  • dest — left unchanged at the old default (www.googletagmanager.com:443).

Because REALITY uses dest as the source of the certificate it presents during the handshake, the server returns a certificate valid for *.google-analytics.com / *.googletagmanager.com while the client expects files.pythonhosted.org.

Expected behavior

Changing the SNI in the UI should update both serverNames and dest (or at least provide a separate field / automatic synchronization), so that the presented certificate matches the advertised SNI.

Impact

  • sing-box (and any client that strictly verifies the certificate) fails to connect with:
    x509: certificate is valid for *.google-analytics.com, ..., not files.pythonhosted.org
    
  • Amnezia mobile clients appear to work because they do not strictly verify the returned certificate, which masks the bug during normal use.
  • The mismatched SNI/certificate pair is an anomaly in TLS traffic that can be flagged by DPI systems, potentially putting the server IP at risk of being blocked.

Workaround

Manually editing dest inside the container fixes the issue:

docker cp amnezia-xray:/opt/amnezia/xray/server.json /root/server.json
# edit "dest" to match "serverNames", e.g. "files.pythonhosted.org:443"
python3 -m json.tool /root/server.json > /dev/null && echo "JSON OK"
docker cp /root/server.json amnezia-xray:/opt/amnezia/xray/server.json
docker restart amnezia-xray

However, this is fragile: any subsequent change to the Xray protocol via the Amnezia client UI (adding a client, changing SNI, etc.) overwrites server.json and reverts dest back to the old default, requiring the manual fix again.

Suggested fix

In the Amnezia-client code that generates server.json for the Xray protocol, when the user changes the SNI, update realitySettings.dest accordingly (e.g. set dest to <new_sni>:443), or add an explicit dest/target field in the UI so both values can be configured and kept in sync.

Environment

  • Amnezia-client version: 5.0.3.0
  • Desktop OS: Windows 11
  • Server OS: Debian 13
  • Container image: amnezia-xray
  • Client OS used for reproduction: Ubuntu 24.04 (ARM64), sing-box 1.14.1 in TUN mode
  • Date: 2026-10-05
Originally created by @vinser on GitHub (Oct 5, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3260 ### Summary When changing the SNI field ("Server name (sni)") for the **Xray** protocol via the Amnezia-client UI, the server-side `server.json` inside the `amnezia-xray` container is updated only in `realitySettings.serverNames`. The `realitySettings.dest` field is **not** updated and keeps the original default value (e.g. `www.googletagmanager.com:443`). As a result, the server continues to fetch and present the TLS certificate of the old `dest` host, while advertising a different SNI to clients. This creates a mismatch between the requested SNI and the returned certificate. ### Affected setup - Protocol: **Xray (VLESS + REALITY, xtls-rprx-vision)** - Server: Amnezia self-hosted VPS, protocol installed via Amnezia-client (Docker container `amnezia-xray`) - Container entrypoint: `dumb-init /opt/amnezia/start.sh` - Config path inside container: `/opt/amnezia/xray/server.json` - Clients used for testing: **sing-box 1.14.x** (TUN mode, strict certificate verification), plus Amnezia mobile clients (Android) ### Steps to reproduce 1. Install Xray protocol on a clean VPS via the Amnezia client (Docker container `amnezia-xray`). 2. In the Amnezia client UI, open the Xray protocol settings and change the **"Server name (SNI)"** field from the default value (e.g. `www.googletagmanager.com`) to another domain, for example `files.pythonhosted.org`. 3. Apply changes. 4. On the server, inspect the container config: ```bash docker exec -it amnezia-xray cat /opt/amnezia/xray/server.json ``` ### Actual behavior The config contains a **mismatch**: ```json "realitySettings": { "dest": "www.googletagmanager.com:443", "fingerprint": "chrome", "privateKey": "...", "serverNames": [ "files.pythonhosted.org" ], "shortIds": [ "..." ] } ``` - `serverNames` — updated to the new SNI entered in the UI. - `dest` — **left unchanged** at the old default (`www.googletagmanager.com:443`). Because REALITY uses `dest` as the source of the certificate it presents during the handshake, the server returns a certificate valid for `*.google-analytics.com` / `*.googletagmanager.com` while the client expects `files.pythonhosted.org`. ### Expected behavior Changing the SNI in the UI should update **both** `serverNames` **and** `dest` (or at least provide a separate field / automatic synchronization), so that the presented certificate matches the advertised SNI. ### Impact - **sing-box** (and any client that strictly verifies the certificate) fails to connect with: ``` x509: certificate is valid for *.google-analytics.com, ..., not files.pythonhosted.org ``` - Amnezia mobile clients appear to work because they do not strictly verify the returned certificate, which **masks the bug** during normal use. - The mismatched SNI/certificate pair is an anomaly in TLS traffic that can be flagged by DPI systems, potentially putting the server IP at risk of being blocked. ### Workaround Manually editing `dest` inside the container fixes the issue: ```bash docker cp amnezia-xray:/opt/amnezia/xray/server.json /root/server.json # edit "dest" to match "serverNames", e.g. "files.pythonhosted.org:443" python3 -m json.tool /root/server.json > /dev/null && echo "JSON OK" docker cp /root/server.json amnezia-xray:/opt/amnezia/xray/server.json docker restart amnezia-xray ``` However, this is fragile: any subsequent change to the Xray protocol via the Amnezia client UI (adding a client, changing SNI, etc.) overwrites `server.json` and reverts `dest` back to the old default, requiring the manual fix again. ### Suggested fix In the Amnezia-client code that generates `server.json` for the Xray protocol, when the user changes the SNI, update `realitySettings.dest` accordingly (e.g. set `dest` to `<new_sni>:443`), or add an explicit `dest`/`target` field in the UI so both values can be configured and kept in sync. ### Environment - Amnezia-client version: _5.0.3.0_ - Desktop OS: Windows 11 - Server OS: Debian 13 - Container image: `amnezia-xray` - Client OS used for reproduction: Ubuntu 24.04 (ARM64), sing-box 1.14.1 in TUN mode - Date: 2026-10-05
Author
Owner

@koryukov commented on GitHub (Oct 12, 2026):

Duplicates #3145

<!-- gh-comment-id:6127427602 --> @koryukov commented on GitHub (Oct 12, 2026): Duplicates #3145
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#1507
No description provided.