Skip to content

Commit 4fc234d

Browse files
authored
Merge pull request #8 from AlchemyLink/develop
feat: SNI routing, PROXY protocol, monitoring stack
2 parents c21aa8d + b6a0e9c commit 4fc234d

53 files changed

Lines changed: 2572 additions & 212 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/xray-config-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Xray config (Ansible + xray -test)
22

33
on:
44
push:
5-
branches: [main, master]
5+
branches: [main, master, develop]
66
pull_request:
7-
branches: [main, master]
7+
branches: [main, master, develop]
88

99
jobs:
1010
test:

README.md

Lines changed: 95 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ Languages: **English** | [Русский](README.ru.md)
55
[![CI](https://github.com/AlchemyLink/Raven-server-install/actions/workflows/xray-config-test.yml/badge.svg)](https://github.com/AlchemyLink/Raven-server-install/actions/workflows/xray-config-test.yml)
66
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](LICENSE)
77

8-
Ansible playbooks for deploying a self-hosted VPN server stack based on [Xray-core](https://github.com/XTLS/Xray-core) and [Raven-subscribe](https://github.com/AlchemyLink/Raven-subscribe).
8+
Ansible playbooks for deploying a production-ready self-hosted VPN server stack based on [Xray-core](https://github.com/XTLS/Xray-core) and [Raven-subscribe](https://github.com/AlchemyLink/Raven-subscribe). Designed for censorship circumvention with traffic indistinguishable from regular HTTPS.
99

1010
**What you get:**
1111

12-
- Xray-core with VLESS + XTLS-Reality and VLESS + XHTTP inbounds
13-
- Optional post-quantum VLESS Encryption (mlkem768x25519plus)
12+
- Xray-core with VLESS + XTLS-Reality (TCP) and VLESS + XHTTP (HTTP/2) inbounds
13+
- nginx SNI routing on port 443 — all VPN traffic goes through standard HTTPS port
14+
- Optional post-quantum VLESS Encryption (mlkem768x25519plus, Xray-core ≥ 26.x)
1415
- Optional Hysteria2 via [sing-box](https://github.com/SagerNet/sing-box)
1516
- [Raven-subscribe](https://github.com/AlchemyLink/Raven-subscribe) — subscription server: auto-discovers users, serves client configs via personal URLs
16-
- nginx TLS frontend on EU VPS (`nginx_frontend` role)
17-
- nginx relay + TCP stream proxy on RU VPS for routing through a second server (`relay` role)
17+
- [xray-stats-exporter](https://github.com/AlchemyLink/xray-stats-exporter) + VictoriaMetrics + Grafana — monitoring with per-user and per-inbound traffic dashboards
18+
- nginx TLS frontend on EU VPS (`nginx_frontend` role) with PROXY protocol for real client IPs
19+
- nginx SNI relay on RU VPS — hides EU server IP from clients (`relay` role)
1820
- Systemd services with config validation before every reload
19-
- Ad and tracker blocking via geosite routing rules
21+
- Ad and tracker blocking via geosite routing rules (`geosite:category-ads-all`)
2022
- BBR congestion control and sysctl tuning (`srv_prepare` role)
2123

2224
---
@@ -44,48 +46,50 @@ This repo supports two deployment topologies:
4446

4547
### Single-server (minimal)
4648

47-
One VPS running Xray + Raven-subscribe + nginx frontend.
49+
One VPS running Xray + Raven-subscribe + nginx frontend. All traffic enters on port 443 — nginx routes by SNI.
4850

4951
```
50-
Client ──VLESS+Reality──► VPS:443 (Xray)
51-
Client ──VLESS+XHTTP────► VPS:443 (nginx) ──► VPS:2053 (Xray)
52-
Client ──subscription───► VPS:443 (nginx) ──► VPS:8080 (Raven)
52+
Client ──VLESS+Reality──► VPS:443 (nginx SNI) ──► VPS:4443 (Xray)
53+
Client ──VLESS+XHTTP────► VPS:443 (nginx SNI) ──► VPS:2053 (Xray)
54+
Client ──subscription───► VPS:443 (nginx SNI) ──► VPS:8443 (nginx HTTPS) ──► Raven:8080
5355
```
5456

5557
### Dual-server with RU relay (recommended for CIS users)
5658

5759
EU VPS runs Xray + nginx_frontend + Raven-subscribe.
58-
RU VPS runs a relay that hides the EU IP from clients.
60+
RU VPS runs an SNI relay that hides the EU IP from clients and passes traffic through.
5961

6062
```
61-
EU VPS (media.example.com) RU VPS (example.com)
62-
┌───────────────────────────┐ ┌─────────────────────────────┐
63-
│ Xray :443 TCP │ │ nginx relay │
64-
│ nginx XHTTP :443 HTTPS │◄─────│ my.example.com → EU:8443 │
65-
│ nginx stream:8445 TCP │◄─────│ :8444 TCP → EU:8445 TCP │
66-
│ Raven :8080 local │ └─────────────────────────────┘
67-
│ nginx front :8443 HTTPS │ ▲
68-
└───────────────────────────┘ │
69-
clients
63+
EU VPS RU VPS (example.com)
64+
┌────────────────────────────────┐ ┌─────────────────────────────────────┐
65+
│ nginx stream :443 (SNI routing)│ │ nginx stream :443 (SNI routing) │
66+
│ SNI dest.com → Xray :4443 │◄──│ SNI dest.com → EU:443 │
67+
│ SNI adobe.com → Xray :2053 │◄──│ SNI adobe.com → EU:443 │
68+
│ SNI my.domain → nginx :8443 │ │ SNI my.domain → local nginx :8443 │
69+
│ │ │ → EU:8443 → Raven :8080 │
70+
│ Raven-subscribe :8080 (local) │ └─────────────────────────────────────┘
71+
└────────────────────────────────┘ ▲
72+
clients
7073
```
7174

7275
**Client connection flow:**
7376
```
74-
VLESS Reality: client → RU:8444 (TCP relay) → EU:8445 (nginx stream) → Xray:443
75-
VLESS XHTTP: client → EU:443 (nginx HTTPS) → Xray:2053
76-
Subscription: client → my.example.com (RU relay) → EU:8443 → Raven:8080
77+
VLESS Reality: client → RU:443 (SNI relay) → EU:443 (nginx SNI) → Xray:4443
78+
VLESS XHTTP: client → RU:443 (SNI relay) → EU:443 (nginx SNI) → Xray:2053
79+
Subscription: client → my.example.com:443 → RU nginx → EU:8443 → Raven:8080
7780
```
7881

7982
### Role map
8083

8184
| Role | VPS | Playbook | What it does |
8285
|------|-----|----------|--------------|
83-
| `srv_prepare` | EU | `role_xray.yml` | BBR, sysctl, system user |
86+
| `srv_prepare` | EU | `role_xray.yml` | BBR, sysctl tuning, system user `xrayuser` |
8487
| `xray` | EU | `role_xray.yml` | Xray binary + split config in `/etc/xray/config.d/` |
8588
| `raven_subscribe` | EU | `role_raven_subscribe.yml` | Subscription server, gRPC sync with Xray |
86-
| `nginx_frontend` | EU | `role_nginx_frontend.yml` | nginx TLS proxy + TCP stream relay (port 8443/8445) |
89+
| `nginx_frontend` | EU | `role_nginx_frontend.yml` | nginx SNI routing on :443, HTTPS proxy on :8443, PROXY protocol |
90+
| `monitoring` | EU | `role_monitoring.yml` | xray-stats-exporter + VictoriaMetrics + Grafana |
8791
| `sing-box-playbook` | EU | `role_sing-box.yml` | sing-box + Hysteria2 (optional) |
88-
| `relay` | RU | `role_relay.yml` | nginx reverse proxy + TCP stream relay (port 8444) |
92+
| `relay` | RU | `role_relay.yml` | nginx SNI relay on :443 — forwards all VPN traffic to EU |
8993

9094
---
9195

@@ -253,24 +257,37 @@ Listens on `127.0.0.1:8080`, proxied by nginx_frontend.
253257

254258
### `nginx_frontend` role
255259

256-
Deploys nginx on the EU VPS as a TLS reverse proxy. Responsibilities:
260+
Deploys nginx on the EU VPS as a TLS frontend and SNI router. Port 443 handles all traffic.
257261

262+
- **Stream SNI routing on :443** — reads SNI from TLS ClientHello, routes by hostname:
263+
- SNI `xhttp-dest.com` → Xray XHTTP `:2053`
264+
- SNI `your-domain.com` → nginx HTTPS `:8443` (Raven-subscribe)
265+
- Default (any other SNI) → Xray VLESS Reality `:4443`
266+
- **PROXY protocol** — passes real client IP to all upstreams (Xray uses `xver: 2`)
267+
- **HTTPS on :8443** — proxies `/sub/`, `/c/`, `/api/` → Raven-subscribe `:8080`
258268
- Obtains Let's Encrypt certificate for `nginx_frontend_domain`
259-
- Listens on port **8443** (port 443 is taken by Xray VLESS Reality)
260-
- Proxies XHTTP path → Xray `:2053`
261-
- Proxies subscription/API paths → Raven-subscribe `:8080`
262-
- **TCP stream relay**: port 8445 → `127.0.0.1:443` (passes VLESS Reality through nginx)
269+
270+
**Important:** When deploying nginx_frontend and Xray inbounds together, always deploy **Xray first** (`--tags xray_inbounds`), then nginx. nginx sends PROXY protocol headers immediately — Xray must be ready to accept them.
263271

264272
---
265273

266274
### `relay` role
267275

268-
Deploys nginx on the RU VPS as a relay. Responsibilities:
276+
Deploys nginx on the RU VPS as an SNI relay. Responsibilities:
269277

270-
- Obtains Let's Encrypt certificates for `relay_domain` and `relay_sub_my`
271-
- Serves a static stub site on `relay_domain` (camouflage)
278+
- **Stream SNI routing on :443** — forwards all VPN traffic to EU VPS:443 by default
279+
- Serves a static stub site on `relay_domain` (camouflage, Let's Encrypt cert)
272280
- Proxies `my.relay_domain` → EU VPS nginx_frontend `:8443` (Raven-subscribe)
273-
- **TCP stream relay**: port 8444 → EU VPS `:8445` (VLESS Reality passthrough)
281+
282+
---
283+
284+
### `monitoring` role
285+
286+
Deploys the full monitoring stack on the EU VPS:
287+
288+
- **[xray-stats-exporter](https://github.com/AlchemyLink/xray-stats-exporter)** — Prometheus exporter for per-user and per-inbound traffic metrics
289+
- **VictoriaMetrics** — Prometheus-compatible time series database
290+
- **Grafana** — dashboards for traffic, server health, Raven-subscribe status, and alerting rules
274291

275292
---
276293

@@ -384,20 +401,21 @@ singbox:
384401

385402
| Variable | Default | Description |
386403
|----------|---------|-------------|
387-
| `nginx_frontend_domain` | `media.example.com` | EU VPS domain — set to your domain |
388-
| `nginx_frontend_listen_port` | `8443` | nginx HTTPS listen port (not 443 — taken by Xray) |
389-
| `nginx_frontend_xhttp_port` | `2053` | Xray XHTTP upstream port |
390-
| `nginx_frontend_xhttp_path` | `/api/v3/data-sync` | XHTTP path (must match xray config) |
391-
| `nginx_frontend_reality_port` | `8445` | TCP stream relay port for Reality |
404+
| `nginx_frontend_domain` | `media.example.com` | EU VPS domain — used for TLS cert and SNI routing |
405+
| `nginx_frontend_listen_port` | `8443` | nginx HTTPS internal port (proxied from :443 stream) |
406+
| `nginx_frontend_raven_port` | `8080` | Raven-subscribe upstream port |
407+
| `nginx_frontend_stream_xhttp_sni` | `www.adobe.com` | SNI that routes to Xray XHTTP inbound |
408+
| `nginx_frontend_stream_xhttp_port` | `2053` | Xray XHTTP inbound port |
409+
| `nginx_frontend_stream_reality_port` | `4443` | Xray VLESS Reality inbound port (default SNI target) |
392410

393411
### relay (`roles/relay/defaults/main.yml`)
394412

395413
| Variable | Default | Description |
396414
|----------|---------|-------------|
397-
| `relay_domain` | `example.com` | RU VPS domain — set to your domain |
398-
| `relay_upstream_raven_port` | `8443` | EU nginx_frontend port (must match `nginx_frontend_listen_port`) |
399-
| `relay_stream_port` | `8444` | RU relay TCP port for Reality (exposed to clients) |
400-
| `relay_upstream_xray_port` | `8445` | EU nginx stream port (must match `nginx_frontend_reality_port`) |
415+
| `relay_domain` | `example.com` | RU VPS domain — stub site + SNI routing |
416+
| `relay_sub_my` | `my.example.com` | Subdomain proxied to EU Raven-subscribe |
417+
| `relay_upstream_host` | `EU_VPS_IP` | EU server IP (set in secrets.yml) |
418+
| `relay_upstream_raven_port` | `8443` | EU nginx HTTPS port for Raven-subscribe |
401419
| `relay_stub_title` | `Welcome` | Stub site page title |
402420
| `relay_stub_description` | `Personal website` | Stub site meta description |
403421

@@ -409,11 +427,39 @@ Point the following DNS A records to the correct servers:
409427

410428
| Domain | → | Server | Purpose |
411429
|--------|---|--------|---------|
412-
| `media.example.com` | → | EU VPS IP | nginx_frontend (XHTTP, Raven) |
413-
| `example.com` | → | RU VPS IP | Relay stub site |
414-
| `my.example.com` | → | RU VPS IP | Relay → Raven-subscribe |
430+
| `media.example.com` | → | EU VPS IP | nginx_frontend (SNI routing, TLS cert) |
431+
| `example.com` | → | RU VPS IP | Relay stub site (camouflage) |
432+
| `my.example.com` | → | RU VPS IP | Relay → Raven-subscribe (subscription links) |
433+
434+
Clients connect to the RU VPS on port 443 for all protocols — no additional DNS records needed for VPN traffic.
435+
436+
---
437+
438+
## Monitoring (optional)
415439

416-
The RU VPS TCP relay for Reality (port 8444) works by IP — no DNS record needed.
440+
The `monitoring` role deploys a full observability stack on the EU VPS:
441+
442+
```bash
443+
ansible-playbook roles/role_monitoring.yml -i roles/hosts.yml --vault-password-file vault_password.txt
444+
```
445+
446+
**Grafana dashboards included:**
447+
- **Xray — per-user traffic** — upload/download timeseries, top users, per-inbound breakdown (Reality vs XHTTP)
448+
- **Servers EU/RU — status** — CPU, RAM, network, disk, Xray health, Raven-subscribe latency
449+
450+
**Alerting rules** (Grafana alerts via VictoriaMetrics):
451+
- Xray down
452+
- Raven-subscribe down
453+
- EU/RU server unreachable
454+
- Disk usage > 85%
455+
456+
To deploy only the binary for [xray-stats-exporter](https://github.com/AlchemyLink/xray-stats-exporter):
457+
458+
```bash
459+
ansible-playbook roles/role_monitoring.yml -i roles/hosts.yml --vault-password-file vault_password.txt \
460+
--tags xray_stats_exporter \
461+
-e "xray_stats_exporter_local_binary=/path/to/xray-stats-exporter"
462+
```
417463

418464
---
419465

@@ -497,6 +543,7 @@ ansible-playbook tests/playbooks/render_conf.yml
497543
## Related Projects
498544

499545
- [Raven-subscribe](https://github.com/AlchemyLink/Raven-subscribe) — subscription server (Go): auto-discovers users from Xray config, syncs via gRPC API, serves personal subscription URLs in Xray JSON / sing-box JSON / share link formats
546+
- [xray-stats-exporter](https://github.com/AlchemyLink/xray-stats-exporter) — Prometheus exporter for per-user and per-inbound Xray traffic metrics
500547
- [Xray-core](https://github.com/XTLS/Xray-core) — the VPN core
501548
- [sing-box](https://github.com/SagerNet/sing-box) — alternative VPN core (Hysteria2)
502549

0 commit comments

Comments
 (0)