瀏覽代碼

feat(inbounds): add Real client IP presets to capture visitor IP behind CDN/relay

Surface the existing sockopt knobs (acceptProxyProtocol, trustedXForwardedFor) as a guided 'Real client IP' preset selector in the inbound form, so the real visitor IP is recovered behind Cloudflare CDN or an L4 tunnel/relay instead of recording the intermediary address. Presets are mutually exclusive, warn on incompatible transports, and add tooltips, docs, and translations for all locales.
MHSanaei 9 小時之前
父節點
當前提交
d882d6aa74

+ 102 - 0
docs/real-client-ip.md

@@ -0,0 +1,102 @@
+# Capturing the Real Client IP
+
+When an Xray inbound sits behind an intermediary — a CDN like Cloudflare, an L4 tunnel/relay,
+or another panel — the IP that Xray sees is the **intermediary's** address, not the visitor's.
+That intermediary IP is what shows up in the panel's online/IP view and what the per-client
+**IP limit** counts against, which makes both useless behind a proxy.
+
+Xray-core can recover the real visitor IP. 3x-ui exposes the two mechanisms in the inbound form
+and feeds the recovered IP into the same pipeline that drives IP-limit enforcement, the online
+list, and multi-node sync — so once it is set, everything downstream just works.
+
+## Where to set it
+
+Open an inbound → **Transport / Stream Settings** → enable **Sockopt** → use the
+**Real client IP** preset selector:
+
+| Preset | What it does | Use for |
+|---|---|---|
+| **Off / direct** | Clears both fields. | Inbound reachable directly by clients. |
+| **Cloudflare CDN** | Sets `sockopt.trustedXForwardedFor = ["CF-Connecting-IP"]`. | WebSocket / HTTPUpgrade / XHTTP behind Cloudflare's CDN (orange cloud). |
+| **L4 relay / Spectrum (PROXY)** | Sets `acceptProxyProtocol = true`. | An L4 tunnel/relay in front, or Cloudflare **Spectrum**. |
+
+The raw `Proxy Protocol` switch and `Trusted X-Forwarded-For` list stay visible below the preset
+selector for manual / advanced tuning — the presets just fill them in for you.
+
+## Scenario 1 — Cloudflare CDN
+
+Cloudflare's CDN (the orange cloud) forwards the visitor's IP in the `CF-Connecting-IP` request
+header. Xray reads it when the transport is **WebSocket**, **HTTPUpgrade**, or **XHTTP** and
+the header name is listed in `sockopt.trustedXForwardedFor`.
+
+```json
+"streamSettings": {
+  "network": "ws",
+  "sockopt": { "trustedXForwardedFor": ["CF-Connecting-IP"] }
+}
+```
+
+Pick the **Cloudflare CDN** preset. You can add `X-Real-IP`, `True-Client-IP`, or `X-Client-IP`
+to the list if a different upstream uses those.
+
+> This is **not** the same as Cloudflare Spectrum. The free/CDN tier forwards HTTP headers — use
+> this scenario. Spectrum (a TCP/L4 product) can send the PROXY protocol — use Scenario 2.
+
+## Scenario 2 — L4 tunnel / relay or Cloudflare Spectrum (PROXY protocol)
+
+For a TCP-level front (HAProxy, gost, nginx `stream`, an Xray dokodemo-door relay, or Cloudflare
+Spectrum), the real IP is carried in the **PROXY protocol** header. Enable
+`acceptProxyProtocol` and make sure the **upstream emits PROXY protocol** — otherwise the
+connection will fail.
+
+```json
+"streamSettings": {
+  "network": "tcp",
+  "sockopt": { "acceptProxyProtocol": true }
+}
+```
+
+Pick the **L4 relay / Spectrum (PROXY)** preset. Works on TCP/RAW, WebSocket, HTTPUpgrade, gRPC
+and XHTTP; **not** on mKCP. The front must be configured to send the header, e.g.:
+
+- **HAProxy**: `server backend 127.0.0.1:443 send-proxy` (or `send-proxy-v2`).
+- **nginx** (`stream {}` block): `proxy_protocol on;` on the `server`, and on the upstream side
+  `proxy_protocol on;` in the `server` that connects to Xray.
+
+## Transport support matrix
+
+| Mechanism | TCP/RAW | mKCP | WebSocket | gRPC | HTTPUpgrade | XHTTP |
+|---|:--:|:--:|:--:|:--:|:--:|:--:|
+| `trustedXForwardedFor` (header) | – | – | ✅ | – | ✅ | ✅ |
+| `acceptProxyProtocol` (PROXY)   | ✅ | – | ✅ | ✅ | ✅ | ✅ |
+
+The form shows a warning when you select a preset that the current transport cannot honor.
+
+> **Use one, not both.** `acceptProxyProtocol` and `trustedXForwardedFor` are independent — the
+> first reads the real IP from the L4 PROXY header, the second from an HTTP request header. On
+> WebSocket / HTTPUpgrade / XHTTP, xray applies the HTTP header *last*, so a stale
+> `trustedXForwardedFor` would override (and defeat) a PROXY-protocol setup. The presets are
+> mutually exclusive and clear the other field for you; only mix them by hand if you know your
+> upstream chain needs it.
+
+## Multi-node
+
+No extra configuration is needed. The inbound's `streamSettings` (including these sockopt
+fields) is pushed to child nodes verbatim, so the node's Xray records the real IP, and the
+parent panel pulls each node's per-client IPs roughly every 10 seconds. The real visitor IP
+shows up on the parent automatically.
+
+## Security note
+
+Both `acceptProxyProtocol` and `trustedXForwardedFor` are **server-side only** — they are
+stripped from subscription output, so they never reach clients. Only enable
+`trustedXForwardedFor` when the inbound is genuinely behind a trusted proxy that sets the
+header; otherwise a client could spoof the header and forge its own source IP.
+
+## Verifying
+
+1. Set the preset and save the inbound.
+2. Inspect the generated Xray config and confirm `streamSettings.sockopt` carries the expected
+   field (`trustedXForwardedFor` or `acceptProxyProtocol`).
+3. Connect through the intermediary, then open the client's IPs / online view in the panel — it
+   should show the real visitor IP rather than the CDN/relay address.

+ 1 - 1
frontend/src/pages/inbounds/form/InboundFormModal.tsx

@@ -818,7 +818,7 @@ export default function InboundFormModal({
         <ExternalProxyForm toggleExternalProxy={toggleExternalProxy} />
       )}
 
-      <SockoptForm toggleSockopt={toggleSockopt} />
+      <SockoptForm toggleSockopt={toggleSockopt} network={network as string} />
 
       {/* Transport masks don't apply to tunnel (a transparent forwarder), so
           its stream tab is just sockopt + TProxy. */}

+ 142 - 1
frontend/src/pages/inbounds/form/transport/sockopt.tsx

@@ -1,5 +1,5 @@
 import { useTranslation } from 'react-i18next';
-import { Button, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
+import { Alert, Button, Form, Input, InputNumber, Segmented, Select, Space, Switch } from 'antd';
 
 import {
   Address_Port_Strategy,
@@ -8,12 +8,68 @@ import {
 } from '@/schemas/primitives';
 import { HappyEyeballsSchema } from '@/schemas/protocols/stream/sockopt';
 
+// Transport key that carries its own acceptProxyProtocol field (mirrored
+// alongside the sockopt-level one so the PROXY preset never silently no-ops).
+const TRANSPORT_PROXY_FIELD: Record<string, string> = {
+  tcp: 'tcpSettings',
+  ws: 'wsSettings',
+  httpupgrade: 'httpupgradeSettings',
+};
+// Transports on which xray-core honors sockopt.trustedXForwardedFor.
+const TRUSTED_HEADER_NETWORKS = ['ws', 'httpupgrade', 'xhttp'];
+
+type RealClientIpPreset = 'off' | 'cloudflare' | 'proxy';
+
 export default function SockoptForm({
   toggleSockopt,
+  network,
 }: {
   toggleSockopt: (on: boolean) => void;
+  network: string;
 }) {
   const { t } = useTranslation();
+
+  // Presets write the same sockopt fields the user could set by hand below,
+  // picking the mechanism xray-core actually honors for the chosen transport:
+  // CF-Connecting-IP via trustedXForwardedFor (ws/httpupgrade/xhttp) or the
+  // PROXY-protocol header via acceptProxyProtocol (every transport but mKCP).
+  const applyRealClientIpPreset = (
+    preset: RealClientIpPreset,
+    getFieldValue: (name: (string | number)[]) => unknown,
+    setFieldValue: (name: (string | number)[], value: unknown) => void,
+  ) => {
+    const sockopt = getFieldValue(['streamSettings', 'sockopt']);
+    const sockoptOn =
+      !!sockopt && typeof sockopt === 'object' && Object.keys(sockopt as object).length > 0;
+    if (preset !== 'off' && !sockoptOn) {
+      toggleSockopt(true);
+    }
+    const transportField = TRANSPORT_PROXY_FIELD[network];
+
+    if (preset === 'off') {
+      setFieldValue(['streamSettings', 'sockopt', 'trustedXForwardedFor'], []);
+      setFieldValue(['streamSettings', 'sockopt', 'acceptProxyProtocol'], false);
+      if (transportField) setFieldValue(['streamSettings', transportField, 'acceptProxyProtocol'], false);
+      return;
+    }
+
+    if (preset === 'cloudflare') {
+      const current = getFieldValue(['streamSettings', 'sockopt', 'trustedXForwardedFor']);
+      const list = Array.isArray(current) ? [...(current as string[])] : [];
+      if (!list.includes('CF-Connecting-IP')) list.push('CF-Connecting-IP');
+      setFieldValue(['streamSettings', 'sockopt', 'trustedXForwardedFor'], list);
+      setFieldValue(['streamSettings', 'sockopt', 'acceptProxyProtocol'], false);
+      if (transportField) setFieldValue(['streamSettings', transportField, 'acceptProxyProtocol'], false);
+      return;
+    }
+
+    // proxy — clear trustedXForwardedFor so a lingering header can't override the
+    // PROXY-recovered IP (xray reads the header last on ws/httpupgrade/xhttp).
+    setFieldValue(['streamSettings', 'sockopt', 'trustedXForwardedFor'], []);
+    setFieldValue(['streamSettings', 'sockopt', 'acceptProxyProtocol'], true);
+    if (transportField) setFieldValue(['streamSettings', transportField, 'acceptProxyProtocol'], true);
+  };
+
   return (
     <Form.Item
       noStyle
@@ -33,6 +89,89 @@ export default function SockoptForm({
             </Form.Item>
             {on && (
               <>
+                <Form.Item
+                  noStyle
+                  shouldUpdate={(prev, curr) => {
+                    type ProxyWatch = {
+                      streamSettings?: {
+                        sockopt?: { trustedXForwardedFor?: unknown; acceptProxyProtocol?: unknown };
+                        tcpSettings?: { acceptProxyProtocol?: unknown };
+                        wsSettings?: { acceptProxyProtocol?: unknown };
+                        httpupgradeSettings?: { acceptProxyProtocol?: unknown };
+                      };
+                    };
+                    const pick = (v: ProxyWatch) => {
+                      const s = v.streamSettings;
+                      return JSON.stringify([
+                        s?.sockopt?.trustedXForwardedFor,
+                        s?.sockopt?.acceptProxyProtocol,
+                        s?.tcpSettings?.acceptProxyProtocol,
+                        s?.wsSettings?.acceptProxyProtocol,
+                        s?.httpupgradeSettings?.acceptProxyProtocol,
+                      ]);
+                    };
+                    return pick(prev as ProxyWatch) !== pick(curr as ProxyWatch);
+                  }}
+                >
+                  {({ getFieldValue, setFieldValue }) => {
+                    const sockopt = (getFieldValue(['streamSettings', 'sockopt']) ?? {}) as Record<
+                      string,
+                      unknown
+                    >;
+                    const transportField = TRANSPORT_PROXY_FIELD[network];
+                    const transportPP = transportField
+                      ? getFieldValue(['streamSettings', transportField, 'acceptProxyProtocol']) === true
+                      : false;
+                    const proxyOn = sockopt.acceptProxyProtocol === true || transportPP;
+                    const trusted = Array.isArray(sockopt.trustedXForwardedFor)
+                      ? (sockopt.trustedXForwardedFor as string[])
+                      : [];
+                    const value: RealClientIpPreset = proxyOn
+                      ? 'proxy'
+                      : trusted.length > 0
+                        ? 'cloudflare'
+                        : 'off';
+                    const trustedMismatch =
+                      trusted.length > 0 && !TRUSTED_HEADER_NETWORKS.includes(network);
+                    const proxyMismatch = proxyOn && network === 'kcp';
+                    return (
+                      <>
+                        <Form.Item
+                          label={t('pages.inbounds.form.realClientIp')}
+                          tooltip={t('pages.inbounds.form.realClientIpHint')}
+                        >
+                          <Segmented
+                            value={value}
+                            onChange={(v) =>
+                              applyRealClientIpPreset(v as RealClientIpPreset, getFieldValue, setFieldValue)
+                            }
+                            options={[
+                              { value: 'off', label: t('pages.inbounds.form.realClientIpPresetOff') },
+                              { value: 'cloudflare', label: t('pages.inbounds.form.realClientIpPresetCloudflare') },
+                              { value: 'proxy', label: t('pages.inbounds.form.realClientIpPresetProxyProtocol') },
+                            ]}
+                          />
+                        </Form.Item>
+                        {trustedMismatch && (
+                          <Alert
+                            type="warning"
+                            showIcon
+                            style={{ marginBottom: 16 }}
+                            message={t('pages.inbounds.form.realClientIpTrustedHeaderTransportWarn')}
+                          />
+                        )}
+                        {proxyMismatch && (
+                          <Alert
+                            type="warning"
+                            showIcon
+                            style={{ marginBottom: 16 }}
+                            message={t('pages.inbounds.form.realClientIpProxyProtocolTransportWarn')}
+                          />
+                        )}
+                      </>
+                    );
+                  }}
+                </Form.Item>
                 <Form.Item name={['streamSettings', 'sockopt', 'mark']} label={t('pages.inbounds.form.routeMark')}>
                   <InputNumber min={0} />
                 </Form.Item>
@@ -67,6 +206,7 @@ export default function SockoptForm({
                 <Form.Item
                   name={['streamSettings', 'sockopt', 'acceptProxyProtocol']}
                   label={t('pages.inbounds.form.proxyProtocol')}
+                  tooltip={t('pages.inbounds.form.proxyProtocolHint')}
                   valuePropName="checked"
                 >
                   <Switch />
@@ -139,6 +279,7 @@ export default function SockoptForm({
                 <Form.Item
                   name={['streamSettings', 'sockopt', 'trustedXForwardedFor']}
                   label={t('pages.inbounds.form.trustedXForwardedFor')}
+                  tooltip={t('pages.inbounds.form.trustedXForwardedForHint')}
                 >
                   <Select
                     mode="tags"

+ 1 - 0
frontend/src/test/__snapshots__/inbound-form-blocks.test.tsx.snap

@@ -80,6 +80,7 @@ exports[`inbound transport forms > RawForm field structure is stable 1`] = `
 exports[`inbound transport forms > SockoptForm field structure is stable (enabled + happy eyeballs) 1`] = `
 [
   "Sockopt",
+  "Real client IP",
   "Route Mark",
   "TCP Keep Alive Interval",
   "TCP Keep Alive Idle",

+ 1 - 1
frontend/src/test/inbound-form-blocks.test.tsx

@@ -89,7 +89,7 @@ describe('inbound transport forms', () => {
 
   it('SockoptForm field structure is stable (enabled + happy eyeballs)', () => {
     renderInForm(
-      () => <SockoptForm toggleSockopt={noop} />,
+      () => <SockoptForm toggleSockopt={noop} network="tcp" />,
       { streamSettings: { sockopt: { happyEyeballs: {} } } },
     );
     expect(fieldLabels()).toMatchSnapshot();

+ 9 - 0
internal/web/translation/ar-EG.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For موثوق",
+        "trustedXForwardedForHint": "ثِق بترويسة الطلب هذه للحصول على IP الحقيقي للعميل (مثل CF-Connecting-IP خلف CDN الخاص بـ Cloudflare). تعمل فقط على وسائل النقل WebSocket و HTTPUpgrade و XHTTP. اتركها فارغة لتجاهل ترويسات التمرير.",
+        "proxyProtocolHint": "اقبل ترويسة PROXY protocol لمعرفة IP الحقيقي للعميل من نفق/مُرحِّل L4 أعلى (HAProxy و gost و nginx-stream و Xray dokodemo-door) أو Cloudflare Spectrum. يجب على الجهة الأعلى إرسال PROXY protocol. تعمل على TCP و WebSocket و HTTPUpgrade و gRPC؛ ولا تعمل على mKCP.",
+        "realClientIp": "IP الحقيقي للعميل",
+        "realClientIpHint": "احصل على IP الحقيقي للزائر عندما يصل المرور إلى هذا الـ inbound عبر CDN أو مُرحِّل، بدلاً من تسجيل عنوان الوسيط. اختر إعدادًا مسبقًا لملء حقول sockopt المقابلة أدناه. لا تُرسَل هذه الحقول أبدًا إلى العملاء في الاشتراكات.",
+        "realClientIpPresetOff": "إيقاف / مباشر",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "مُرحِّل L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For تعمل فقط على WebSocket و HTTPUpgrade و XHTTP. على وسيلة النقل الحالية يتم تجاهل هذه الترويسة.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol غير مدعوم على وسيلة النقل هذه (mKCP). استخدم TCP/RAW أو WebSocket أو HTTPUpgrade أو gRPC أو XHTTP.",
         "addressPortStrategy": "استراتيجية العنوان+المنفذ",
         "tryDelayMs": "تأخير المحاولة (ms)",
         "prioritizeIPv6": "أولوية IPv6",

+ 9 - 0
internal/web/translation/en-US.json

@@ -558,6 +558,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "Trusted X-Forwarded-For",
+        "trustedXForwardedForHint": "Trust this request header for the real client IP (e.g. CF-Connecting-IP behind Cloudflare's CDN). Only honored on WebSocket, HTTPUpgrade and XHTTP transports. Leave empty to ignore forwarded headers.",
+        "proxyProtocolHint": "Accept the PROXY-protocol header to learn the real client IP from an upstream L4 tunnel or relay (HAProxy, gost, nginx-stream, Xray dokodemo-door) or Cloudflare Spectrum. The upstream MUST emit PROXY protocol. Works on TCP, WebSocket, HTTPUpgrade and gRPC; not on mKCP.",
+        "realClientIp": "Real client IP",
+        "realClientIpHint": "Capture the visitor's real IP when traffic reaches this inbound through a CDN or relay, instead of recording the intermediary's address. Pick a preset to fill the matching sockopt fields below. These fields are never sent to clients in subscriptions.",
+        "realClientIpPresetOff": "Off / direct",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4 relay / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For is only honored on WebSocket, HTTPUpgrade and XHTTP. On the current transport this header is ignored.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol is not supported on this transport (mKCP). Use TCP/RAW, WebSocket, HTTPUpgrade, gRPC or XHTTP.",
         "addressPortStrategy": "Address+port strategy",
         "tryDelayMs": "Try delay (ms)",
         "prioritizeIPv6": "Prioritize IPv6",

+ 9 - 0
internal/web/translation/es-ES.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For de confianza",
+        "trustedXForwardedForHint": "Confía en esta cabecera de solicitud para obtener la IP real del cliente (p. ej. CF-Connecting-IP detrás del CDN de Cloudflare). Solo válido en los transportes WebSocket, HTTPUpgrade y XHTTP. Déjalo vacío para ignorar las cabeceras reenviadas.",
+        "proxyProtocolHint": "Acepta la cabecera PROXY protocol para obtener la IP real del cliente desde un túnel/relé L4 superior (HAProxy, gost, nginx-stream, Xray dokodemo-door) o Cloudflare Spectrum. El nodo superior DEBE enviar PROXY protocol. Funciona en TCP, WebSocket, HTTPUpgrade y gRPC; no en mKCP.",
+        "realClientIp": "IP real del cliente",
+        "realClientIpHint": "Captura la IP real del visitante cuando el tráfico llega a este inbound a través de un CDN o relé, en lugar de registrar la dirección del intermediario. Elige un preajuste para rellenar los campos sockopt correspondientes más abajo. Estos campos nunca se envían a los clientes en las suscripciones.",
+        "realClientIpPresetOff": "Desactivado / directo",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "Relé L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For solo es válido en WebSocket, HTTPUpgrade y XHTTP. En el transporte actual esta cabecera se ignora.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol no es compatible con este transporte (mKCP). Usa TCP/RAW, WebSocket, HTTPUpgrade, gRPC o XHTTP.",
         "addressPortStrategy": "Estrategia dirección+puerto",
         "tryDelayMs": "Retraso de intento (ms)",
         "prioritizeIPv6": "Priorizar IPv6",

+ 9 - 0
internal/web/translation/fa-IR.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "تراکم TCP",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For مورد اعتماد",
+        "trustedXForwardedForHint": "این هدر درخواست برای گرفتن IP واقعی کاربر مورد اعتماد قرار می‌گیرد (مثلاً CF-Connecting-IP پشت CDN کلودفلر). فقط روی ترنسپورت‌های WebSocket، HTTPUpgrade و XHTTP اعمال می‌شود. برای نادیده‌گرفتن هدرها خالی بگذارید.",
+        "proxyProtocolHint": "پذیرش هدر PROXY protocol برای گرفتن IP واقعی کاربر از یک تونل/رله L4 بالادست (HAProxy، gost، nginx-stream، Xray dokodemo-door) یا Cloudflare Spectrum. بالادست باید PROXY protocol را ارسال کند. روی TCP، WebSocket، HTTPUpgrade و gRPC کار می‌کند؛ روی mKCP خیر.",
+        "realClientIp": "IP واقعی کاربر",
+        "realClientIpHint": "وقتی ترافیک از طریق CDN یا رله به این ورودی می‌رسد، به‌جای ثبت آدرس واسط، IP واقعی کاربر گرفته می‌شود. یک پریست انتخاب کنید تا فیلدهای sockopt مربوطه پایین تکمیل شوند. این فیلدها هرگز در اشتراک‌ها به کلاینت‌ها ارسال نمی‌شوند.",
+        "realClientIpPresetOff": "خاموش / مستقیم",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "رله L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For فقط روی WebSocket، HTTPUpgrade و XHTTP اعمال می‌شود. روی ترنسپورت فعلی این هدر نادیده گرفته می‌شود.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol روی این ترنسپورت (mKCP) پشتیبانی نمی‌شود. از TCP/RAW، WebSocket، HTTPUpgrade، gRPC یا XHTTP استفاده کنید.",
         "addressPortStrategy": "استراتژی آدرس+پورت",
         "tryDelayMs": "تأخیر تلاش (ms)",
         "prioritizeIPv6": "اولویت IPv6",

+ 9 - 0
internal/web/translation/id-ID.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For tepercaya",
+        "trustedXForwardedForHint": "Percayai header permintaan ini untuk IP klien asli (mis. CF-Connecting-IP di belakang CDN Cloudflare). Hanya berlaku pada transport WebSocket, HTTPUpgrade, dan XHTTP. Kosongkan untuk mengabaikan header yang diteruskan.",
+        "proxyProtocolHint": "Terima header PROXY protocol untuk mengetahui IP klien asli dari tunnel/relay L4 di hulu (HAProxy, gost, nginx-stream, Xray dokodemo-door) atau Cloudflare Spectrum. Hulu HARUS mengirim PROXY protocol. Berfungsi pada TCP, WebSocket, HTTPUpgrade, dan gRPC; tidak pada mKCP.",
+        "realClientIp": "IP klien asli",
+        "realClientIpHint": "Tangkap IP asli pengunjung saat lalu lintas mencapai inbound ini melalui CDN atau relay, alih-alih mencatat alamat perantara. Pilih preset untuk mengisi kolom sockopt terkait di bawah. Kolom ini tidak pernah dikirim ke klien dalam langganan.",
+        "realClientIpPresetOff": "Mati / langsung",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "Relay L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For hanya berlaku pada WebSocket, HTTPUpgrade, dan XHTTP. Pada transport saat ini header ini diabaikan.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol tidak didukung pada transport ini (mKCP). Gunakan TCP/RAW, WebSocket, HTTPUpgrade, gRPC, atau XHTTP.",
         "addressPortStrategy": "Strategi alamat+port",
         "tryDelayMs": "Penundaan percobaan (ms)",
         "prioritizeIPv6": "Prioritaskan IPv6",

+ 9 - 0
internal/web/translation/ja-JP.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "信頼できる X-Forwarded-For",
+        "trustedXForwardedForHint": "実際のクライアント IP を取得するためにこのリクエストヘッダーを信頼します(例: Cloudflare CDN の背後の CF-Connecting-IP)。WebSocket、HTTPUpgrade、XHTTP トランスポートでのみ有効です。空欄にすると転送ヘッダーを無視します。",
+        "proxyProtocolHint": "PROXY protocol ヘッダーを受け入れ、上流の L4 トンネル/リレー(HAProxy、gost、nginx-stream、Xray dokodemo-door)または Cloudflare Spectrum から実際のクライアント IP を取得します。上流は必ず PROXY protocol を送信する必要があります。TCP、WebSocket、HTTPUpgrade、gRPC で動作します。mKCP では動作しません。",
+        "realClientIp": "実際のクライアント IP",
+        "realClientIpHint": "トラフィックが CDN やリレーを経由してこのインバウンドに到達したときに、中継ノードのアドレスではなく訪問者の実際の IP を取得します。プリセットを選ぶと、下の対応する sockopt フィールドが自動入力されます。これらのフィールドはサブスクリプションでクライアントに送信されることはありません。",
+        "realClientIpPresetOff": "オフ / 直接",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4 リレー / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For は WebSocket、HTTPUpgrade、XHTTP でのみ有効です。現在のトランスポートではこのヘッダーは無視されます。",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol はこのトランスポート(mKCP)ではサポートされていません。TCP/RAW、WebSocket、HTTPUpgrade、gRPC、または XHTTP を使用してください。",
         "addressPortStrategy": "アドレス+ポート戦略",
         "tryDelayMs": "試行遅延 (ms)",
         "prioritizeIPv6": "IPv6 優先",

+ 9 - 0
internal/web/translation/pt-BR.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For confiável",
+        "trustedXForwardedForHint": "Confie neste cabeçalho de requisição para obter o IP real do cliente (ex.: CF-Connecting-IP atrás do CDN da Cloudflare). Válido apenas nos transportes WebSocket, HTTPUpgrade e XHTTP. Deixe vazio para ignorar cabeçalhos encaminhados.",
+        "proxyProtocolHint": "Aceite o cabeçalho PROXY protocol para obter o IP real do cliente a partir de um túnel/relay L4 upstream (HAProxy, gost, nginx-stream, Xray dokodemo-door) ou Cloudflare Spectrum. O upstream DEVE enviar PROXY protocol. Funciona em TCP, WebSocket, HTTPUpgrade e gRPC; não em mKCP.",
+        "realClientIp": "IP real do cliente",
+        "realClientIpHint": "Capture o IP real do visitante quando o tráfego chega a este inbound através de um CDN ou relay, em vez de registrar o endereço do intermediário. Escolha uma predefinição para preencher os campos sockopt correspondentes abaixo. Esses campos nunca são enviados aos clientes nas assinaturas.",
+        "realClientIpPresetOff": "Desligado / direto",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "Relay L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For é válido apenas em WebSocket, HTTPUpgrade e XHTTP. No transporte atual este cabeçalho é ignorado.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol não é suportado neste transporte (mKCP). Use TCP/RAW, WebSocket, HTTPUpgrade, gRPC ou XHTTP.",
         "addressPortStrategy": "Estratégia endereço+porta",
         "tryDelayMs": "Atraso de tentativa (ms)",
         "prioritizeIPv6": "Priorizar IPv6",

+ 9 - 0
internal/web/translation/ru-RU.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "Доверенный X-Forwarded-For",
+        "trustedXForwardedForHint": "Доверять этому заголовку запроса для определения реального IP клиента (например, CF-Connecting-IP за CDN Cloudflare). Работает только на транспортах WebSocket, HTTPUpgrade и XHTTP. Оставьте пустым, чтобы игнорировать заголовки пересылки.",
+        "proxyProtocolHint": "Принимать заголовок PROXY protocol, чтобы получить реальный IP клиента от вышестоящего L4-туннеля или релея (HAProxy, gost, nginx-stream, Xray dokodemo-door) либо Cloudflare Spectrum. Вышестоящий узел ДОЛЖЕН отправлять PROXY protocol. Работает на TCP, WebSocket, HTTPUpgrade и gRPC; не работает на mKCP.",
+        "realClientIp": "Реальный IP клиента",
+        "realClientIpHint": "Получать реальный IP посетителя, когда трафик приходит на этот входящий через CDN или релей, вместо адреса промежуточного узла. Выберите пресет, чтобы заполнить соответствующие поля sockopt ниже. Эти поля никогда не отправляются клиентам в подписках.",
+        "realClientIpPresetOff": "Выкл. / напрямую",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4-релей / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For работает только на WebSocket, HTTPUpgrade и XHTTP. На текущем транспорте этот заголовок игнорируется.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol не поддерживается на этом транспорте (mKCP). Используйте TCP/RAW, WebSocket, HTTPUpgrade, gRPC или XHTTP.",
         "addressPortStrategy": "Стратегия адрес+порт",
         "tryDelayMs": "Задержка попытки (мс)",
         "prioritizeIPv6": "Приоритет IPv6",

+ 9 - 0
internal/web/translation/tr-TR.json

@@ -558,6 +558,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "Güvenilir X-Forwarded-For",
+        "trustedXForwardedForHint": "Gerçek istemci IP'sini almak için bu istek başlığına güven (örn. Cloudflare CDN arkasındaki CF-Connecting-IP). Yalnızca WebSocket, HTTPUpgrade ve XHTTP taşımalarında geçerlidir. İletilen başlıkları yok saymak için boş bırakın.",
+        "proxyProtocolHint": "Gerçek istemci IP'sini bir üst L4 tüneli veya rölesi (HAProxy, gost, nginx-stream, Xray dokodemo-door) ya da Cloudflare Spectrum üzerinden öğrenmek için PROXY protocol başlığını kabul et. Üst sunucu PROXY protocol göndermek ZORUNDADIR. TCP, WebSocket, HTTPUpgrade ve gRPC üzerinde çalışır; mKCP üzerinde çalışmaz.",
+        "realClientIp": "Gerçek istemci IP'si",
+        "realClientIpHint": "Trafik bu gelen bağlantıya bir CDN veya röle üzerinden ulaştığında, aracı adresini kaydetmek yerine ziyaretçinin gerçek IP'sini al. Aşağıdaki ilgili sockopt alanlarını doldurmak için bir hazır ayar seç. Bu alanlar aboneliklerde istemcilere asla gönderilmez.",
+        "realClientIpPresetOff": "Kapalı / doğrudan",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4 röle / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For yalnızca WebSocket, HTTPUpgrade ve XHTTP üzerinde geçerlidir. Mevcut taşımada bu başlık yok sayılır.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol bu taşımada (mKCP) desteklenmez. TCP/RAW, WebSocket, HTTPUpgrade, gRPC veya XHTTP kullanın.",
         "addressPortStrategy": "Adres+Port Stratejisi",
         "tryDelayMs": "Deneme Gecikmesi (ms)",
         "prioritizeIPv6": "IPv6 Önceliği",

+ 9 - 0
internal/web/translation/uk-UA.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "Довірений X-Forwarded-For",
+        "trustedXForwardedForHint": "Довіряти цьому заголовку запиту для визначення справжнього IP клієнта (наприклад, CF-Connecting-IP за CDN Cloudflare). Працює лише на транспортах WebSocket, HTTPUpgrade та XHTTP. Залиште порожнім, щоб ігнорувати заголовки пересилання.",
+        "proxyProtocolHint": "Приймати заголовок PROXY protocol, щоб отримати справжній IP клієнта від висхідного L4-тунелю чи релея (HAProxy, gost, nginx-stream, Xray dokodemo-door) або Cloudflare Spectrum. Висхідний вузол МУСИТЬ надсилати PROXY protocol. Працює на TCP, WebSocket, HTTPUpgrade та gRPC; не працює на mKCP.",
+        "realClientIp": "Справжній IP клієнта",
+        "realClientIpHint": "Отримувати справжній IP відвідувача, коли трафік надходить на цей вхідний через CDN або релей, замість адреси проміжного вузла. Виберіть пресет, щоб заповнити відповідні поля sockopt нижче. Ці поля ніколи не надсилаються клієнтам у підписках.",
+        "realClientIpPresetOff": "Вимк. / напряму",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4-релей / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For працює лише на WebSocket, HTTPUpgrade та XHTTP. На поточному транспорті цей заголовок ігнорується.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol не підтримується на цьому транспорті (mKCP). Використовуйте TCP/RAW, WebSocket, HTTPUpgrade, gRPC або XHTTP.",
         "addressPortStrategy": "Стратегія адрес+порт",
         "tryDelayMs": "Затримка спроби (мс)",
         "prioritizeIPv6": "Пріоритет IPv6",

+ 9 - 0
internal/web/translation/vi-VN.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "X-Forwarded-For tin cậy",
+        "trustedXForwardedForHint": "Tin cậy header yêu cầu này để lấy IP thật của client (ví dụ CF-Connecting-IP phía sau CDN của Cloudflare). Chỉ có hiệu lực trên các transport WebSocket, HTTPUpgrade và XHTTP. Để trống để bỏ qua các header chuyển tiếp.",
+        "proxyProtocolHint": "Chấp nhận header PROXY protocol để lấy IP thật của client từ tunnel/relay L4 phía trên (HAProxy, gost, nginx-stream, Xray dokodemo-door) hoặc Cloudflare Spectrum. Phía trên PHẢI gửi PROXY protocol. Hoạt động trên TCP, WebSocket, HTTPUpgrade và gRPC; không hoạt động trên mKCP.",
+        "realClientIp": "IP thật của client",
+        "realClientIpHint": "Lấy IP thật của khách khi lưu lượng đến inbound này qua CDN hoặc relay, thay vì ghi lại địa chỉ của trung gian. Chọn một preset để tự điền các trường sockopt tương ứng bên dưới. Các trường này không bao giờ được gửi đến client trong subscription.",
+        "realClientIpPresetOff": "Tắt / trực tiếp",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "Relay L4 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For chỉ có hiệu lực trên WebSocket, HTTPUpgrade và XHTTP. Trên transport hiện tại header này bị bỏ qua.",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol không được hỗ trợ trên transport này (mKCP). Hãy dùng TCP/RAW, WebSocket, HTTPUpgrade, gRPC hoặc XHTTP.",
         "addressPortStrategy": "Chiến lược địa chỉ+cổng",
         "tryDelayMs": "Độ trễ thử (ms)",
         "prioritizeIPv6": "Ưu tiên IPv6",

+ 9 - 0
internal/web/translation/zh-CN.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "可信 X-Forwarded-For",
+        "trustedXForwardedForHint": "信任此请求头来获取真实客户端 IP(例如 Cloudflare CDN 后的 CF-Connecting-IP)。仅在 WebSocket、HTTPUpgrade 和 XHTTP 传输上生效。留空则忽略转发头。",
+        "proxyProtocolHint": "接受 PROXY protocol 头,从上游 L4 隧道或中继(HAProxy、gost、nginx-stream、Xray dokodemo-door)或 Cloudflare Spectrum 获取真实客户端 IP。上游必须发送 PROXY protocol。适用于 TCP、WebSocket、HTTPUpgrade 和 gRPC;不适用于 mKCP。",
+        "realClientIp": "真实客户端 IP",
+        "realClientIpHint": "当流量通过 CDN 或中继到达此入站时,获取访客的真实 IP,而不是记录中间节点的地址。选择一个预设以自动填写下方对应的 sockopt 字段。这些字段绝不会在订阅中发送给客户端。",
+        "realClientIpPresetOff": "关闭 / 直连",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4 中继 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For 仅在 WebSocket、HTTPUpgrade 和 XHTTP 上生效。在当前传输上此请求头将被忽略。",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol 不支持此传输(mKCP)。请使用 TCP/RAW、WebSocket、HTTPUpgrade、gRPC 或 XHTTP。",
         "addressPortStrategy": "地址+端口策略",
         "tryDelayMs": "尝试延迟 (ms)",
         "prioritizeIPv6": "IPv6 优先",

+ 9 - 0
internal/web/translation/zh-TW.json

@@ -557,6 +557,15 @@
         "tcpCongestion": "TCP Congestion",
         "dialerProxy": "Dialer Proxy",
         "trustedXForwardedFor": "信任的 X-Forwarded-For",
+        "trustedXForwardedForHint": "信任此請求標頭以取得真實用戶端 IP(例如 Cloudflare CDN 後的 CF-Connecting-IP)。僅在 WebSocket、HTTPUpgrade 和 XHTTP 傳輸上生效。留空則忽略轉發標頭。",
+        "proxyProtocolHint": "接受 PROXY protocol 標頭,從上游 L4 隧道或中繼(HAProxy、gost、nginx-stream、Xray dokodemo-door)或 Cloudflare Spectrum 取得真實用戶端 IP。上游必須傳送 PROXY protocol。適用於 TCP、WebSocket、HTTPUpgrade 和 gRPC;不適用於 mKCP。",
+        "realClientIp": "真實用戶端 IP",
+        "realClientIpHint": "當流量透過 CDN 或中繼到達此入站時,取得訪客的真實 IP,而非記錄中間節點的位址。選擇一個預設以自動填入下方對應的 sockopt 欄位。這些欄位絕不會在訂閱中傳送給用戶端。",
+        "realClientIpPresetOff": "關閉 / 直連",
+        "realClientIpPresetCloudflare": "Cloudflare CDN",
+        "realClientIpPresetProxyProtocol": "L4 中繼 / Spectrum (PROXY)",
+        "realClientIpTrustedHeaderTransportWarn": "Trusted X-Forwarded-For 僅在 WebSocket、HTTPUpgrade 和 XHTTP 上生效。在目前的傳輸上此標頭將被忽略。",
+        "realClientIpProxyProtocolTransportWarn": "PROXY protocol 不支援此傳輸(mKCP)。請使用 TCP/RAW、WebSocket、HTTPUpgrade、gRPC 或 XHTTP。",
         "addressPortStrategy": "地址+連接埠策略",
         "tryDelayMs": "嘗試延遲 (ms)",
         "prioritizeIPv6": "IPv6 優先",