Bladeren bron

fix(panel): default TLS ALPN to http/1.1 for WebSocket transport (#6787)

* fix(panel): default TLS ALPN to http/1.1 for WebSocket transport

WebSocket bootstraps over HTTP/1.1: leaving ALPN at the schema default
['h2','http/1.1'] makes the server negotiate h2 and the WS handshake
fails. See issue #6782.

* style(frontend): keep comments within the two-line limit

* style(frontend): keep comments within the two-line limit
Leslie Alexander 14 uur geleden
bovenliggende
commit
66234315e4

+ 6 - 1
frontend/src/lib/xray/inbound-tls-defaults.ts

@@ -14,7 +14,7 @@ function defaultCertificate(): Record<string, unknown> {
   };
 }
 
-export function createTlsSettingsWithDefaultCert(): Record<string, unknown> {
+export function createTlsSettingsWithDefaultCert(network?: string): Record<string, unknown> {
   const tls = TlsStreamSettingsSchema.parse({}) as Record<string, unknown>;
   tls.certificates = [defaultCertificate()];
   const settings =
@@ -23,6 +23,11 @@ export function createTlsSettingsWithDefaultCert(): Record<string, unknown> {
       : {};
   settings.fingerprint = 'chrome';
   tls.settings = settings;
+  /* WebSocket bootstraps over HTTP/1.1: the schema default ALPN
+   * ['h2','http/1.1'] makes the server negotiate h2 (see issue #6782). */
+  if (network === 'ws') {
+    tls.alpn = ['http/1.1'];
+  }
   return tls;
 }
 

+ 6 - 0
frontend/src/pages/inbounds/form/InboundFormModal.tsx

@@ -894,6 +894,12 @@ export default function InboundFormModal({
         cleaned.finalmask = { ...fm, udp };
       }
     }
+    /* WebSocket needs HTTP/1.1 for its handshake; the TLS default
+     * ALPN ['h2','http/1.1'] makes the server negotiate h2 (see #6782). */
+    if (next === 'ws' && cleaned.security === 'tls') {
+      const tls = (cleaned.tlsSettings as Record<string, unknown> | undefined) ?? {};
+      cleaned.tlsSettings = { ...tls, alpn: ['http/1.1'] };
+    }
     setV('streamSettings', cleaned);
   };
 

+ 2 - 1
frontend/src/pages/inbounds/form/useSecurityActions.ts

@@ -344,7 +344,8 @@ export function useSecurityActions({
     delete cleaned.tlsSettings;
     delete cleaned.realitySettings;
     if (next === 'tls') {
-      cleaned.tlsSettings = createTlsSettingsWithDefaultCert();
+      const network = (current.network as string | undefined) ?? '';
+      cleaned.tlsSettings = createTlsSettingsWithDefaultCert(network);
     }
     if (next === 'reality') {
       const reality = RealityStreamSettingsSchema.parse({}) as Record<string, unknown>;

+ 21 - 1
frontend/src/test/inbound-defaults.test.ts

@@ -18,12 +18,16 @@ import {
   createDefaultVmessInboundSettings,
   createDefaultWireguardInboundSettings,
 } from '@/lib/xray/inbound-defaults';
-import { createHysteriaTlsSettingsWithDefaultCert } from '@/lib/xray/inbound-tls-defaults';
+import {
+  createHysteriaTlsSettingsWithDefaultCert,
+  createTlsSettingsWithDefaultCert,
+} from '@/lib/xray/inbound-tls-defaults';
 import { HttpInboundSettingsSchema } from '@/schemas/protocols/inbound/http';
 import {
   HysteriaClientSchema,
   HysteriaInboundSettingsSchema,
 } from '@/schemas/protocols/inbound/hysteria';
+import { TlsStreamSettingsSchema } from '@/schemas/protocols/security/tls';
 import { MixedInboundSettingsSchema } from '@/schemas/protocols/inbound/mixed';
 import {
   ShadowsocksClientSchema,
@@ -234,3 +238,19 @@ describe('createHysteriaTlsSettingsWithDefaultCert', () => {
     ]);
   });
 });
+
+describe('createTlsSettingsWithDefaultCert', () => {
+  it('keeps the schema ALPN default for non-WebSocket transports', () => {
+    for (const network of [undefined, '', 'tcp', 'kcp', 'grpc', 'httpupgrade', 'xhttp']) {
+      const tls = createTlsSettingsWithDefaultCert(network);
+      expect(tls.alpn).toEqual(['h2', 'http/1.1']);
+    }
+  });
+
+  it("defaults ALPN to http/1.1 for WebSocket (issue #6782)", () => {
+    const tls = createTlsSettingsWithDefaultCert('ws');
+    expect(tls.alpn).toEqual(['http/1.1']);
+    // The overridden value must still satisfy the TLS settings schema.
+    expect(TlsStreamSettingsSchema.parse(tls).alpn).toEqual(['http/1.1']);
+  });
+});