From c84d5bd5408948e7229c37b11a6d62b70267b3d1 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 18 Mar 2026 22:02:32 +0800 Subject: [PATCH] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=9B=B4=E6=96=B0OpenRe?= =?UTF-8?q?sty=E9=85=8D=E7=BD=AE=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8D=87=E7=BA=A7=E6=98=A0=E5=B0=84=E5=92=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=9D=97=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96HTTPS=E5=92=8CHTTP=E9=87=8D=E5=AE=9A=E5=90=91=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openflare_server/common/constants.go | 2 +- openflare_server/service/config_version.go | 20 ++++++--- openflare_server/service/https_phase1_test.go | 41 ++++++++++++++----- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/openflare_server/common/constants.go b/openflare_server/common/constants.go index 4a74bf75..2228637c 100644 --- a/openflare_server/common/constants.go +++ b/openflare_server/common/constants.go @@ -103,7 +103,7 @@ events { http { include mime.types; default_type application/octet-stream; - log_format openflare_json escape=json '{"ts":"$time_iso8601","host":"$host","path":"$request_uri","remote_addr":"$remote_addr","status":$status,"request_time":$request_time,"bytes_sent":$body_bytes_sent,"request_length":$request_length}'; +{{OpenRestyConnectionUpgradeMap}}{{OpenRestyDefaultServerBlock}} log_format openflare_json escape=json '{"ts":"$time_iso8601","host":"$host","path":"$request_uri","remote_addr":"$remote_addr","status":$status,"request_time":$request_time,"bytes_sent":$body_bytes_sent,"request_length":$request_length}'; access_log {{OpenRestyAccessLogPath}} openflare_json; sendfile on; tcp_nopush on; diff --git a/openflare_server/service/config_version.go b/openflare_server/service/config_version.go index f9b945fb..61066d2b 100644 --- a/openflare_server/service/config_version.go +++ b/openflare_server/service/config_version.go @@ -136,6 +136,8 @@ var requiredMainConfigTemplatePlaceholders = []string{ "{{OpenRestyWorkerProcesses}}", "{{OpenRestyWorkerConnections}}", "{{OpenRestyWorkerRlimitNofile}}", + "{{OpenRestyConnectionUpgradeMap}}", + "{{OpenRestyDefaultServerBlock}}", "{{OpenRestyAccessLogPath}}", "{{OpenRestyEventsUseDirective}}", "{{OpenRestyEventsMultiAcceptDirective}}", @@ -664,6 +666,8 @@ func renderMainConfigTemplate(templateText string, cfg openRestyConfigSnapshot) "{{OpenRestyWorkerProcesses}}", cfg.WorkerProcesses, "{{OpenRestyWorkerConnections}}", fmt.Sprintf("%d", cfg.WorkerConnections), "{{OpenRestyWorkerRlimitNofile}}", fmt.Sprintf("%d", cfg.WorkerRlimitNofile), + "{{OpenRestyConnectionUpgradeMap}}", renderConnectionUpgradeMap(), + "{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(), "{{OpenRestyAccessLogPath}}", nginxAccessLogPlaceholder, "{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)), "{{OpenRestyEventsMultiAcceptDirective}}", renderTemplateDirective(cfg.EventsMultiAcceptEnabled, "multi_accept on;"), @@ -765,21 +769,25 @@ func nextVersionNumber(now time.Time) (string, error) { } func renderHTTPProxyServer(domain string, originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput, cfg openRestyConfigSnapshot) string { - return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg)) + return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n\n location / {\n%s%s }\n}\n\n", domain, renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg)) } func renderHTTPRedirectServer(domain string) string { - return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s\n return 301 https://$host$request_uri;\n}\n\n", domain, renderExactHostGuard(domain)) + return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n\n return 301 https://$host$request_uri;\n}\n\n", domain) } func renderHTTPSServer(domain string, originURL string, originHost string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput, cfg openRestyConfigSnapshot) string { certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID)) keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID)) - return fmt.Sprintf("server {\n listen 443 ssl http2;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s\n location / {\n%s%s }\n}\n\n", domain, certPath, keyPath, renderExactHostGuard(domain), renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg)) + return fmt.Sprintf("server {\n listen 443 ssl http2;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n\n location / {\n%s%s }\n}\n\n", domain, certPath, keyPath, renderProxyHeaderBlock(originURL, originHost, customHeaders), renderProxyPassBlock(originURL, cfg)) } -func renderExactHostGuard(domain string) string { - return fmt.Sprintf(" if ($host != %q) {\n return 404;\n }\n", domain) +func renderConnectionUpgradeMap() string { + return " map $http_upgrade $connection_upgrade {\n default upgrade;\n '' close;\n }\n\n" +} + +func renderDefaultServerBlock() string { + return " server {\n listen 80 default_server;\n server_name _;\n\n return 404;\n }\n\n" } func renderProxyHeaderBlock(originURL string, originHost string, customHeaders []ProxyRouteCustomHeaderInput) string { @@ -799,7 +807,7 @@ func renderProxyHeaderBlock(originURL string, originHost string, customHeaders [ if common.OpenRestyWebsocketEnabled { builder.WriteString(" proxy_http_version 1.1;\n") builder.WriteString(" proxy_set_header Upgrade $http_upgrade;\n") - builder.WriteString(" proxy_set_header Connection $http_connection;\n") + builder.WriteString(" proxy_set_header Connection $connection_upgrade;\n") } for _, header := range customHeaders { builder.WriteString(fmt.Sprintf(" proxy_set_header %s %s;\n", header.Key, quoteNginxHeaderValue(header.Value))) diff --git a/openflare_server/service/https_phase1_test.go b/openflare_server/service/https_phase1_test.go index aeac2ff4..8c49c0f1 100644 --- a/openflare_server/service/https_phase1_test.go +++ b/openflare_server/service/https_phase1_test.go @@ -72,8 +72,8 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) { if !strings.Contains(result.Version.RenderedConfig, "listen 443 ssl http2;") { t.Fatal("expected rendered config to include https server block with http2 enabled") } - if !strings.Contains(result.Version.RenderedConfig, `if ($host != "app.example.com") {`) { - t.Fatal("expected rendered config to reject unmatched host headers with 404") + if strings.Contains(result.Version.RenderedConfig, `if ($host != "app.example.com") {`) { + t.Fatal("expected rendered config to avoid per-route host guard") } if !strings.Contains(result.Version.RenderedConfig, "return 301 https://$host$request_uri;") { t.Fatal("expected rendered config to include http redirect") @@ -138,8 +138,8 @@ func TestPublishConfigVersionRendersCustomHeaders(t *testing.T) { if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Upgrade $http_upgrade;") { t.Fatal("expected rendered config to forward websocket upgrade header") } - if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Connection $http_connection;") { - t.Fatal("expected rendered config to forward websocket connection header") + if !strings.Contains(result.Version.RenderedConfig, "proxy_set_header Connection $connection_upgrade;") { + t.Fatal("expected rendered config to use normalized websocket connection header") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://origin.internal;") { t.Fatal("expected rendered config to keep direct proxy_pass when no resolvers are configured") @@ -262,7 +262,7 @@ func TestPreviewConfigVersionCanDisableWebsocketHeaders(t *testing.T) { if strings.Contains(preview.RenderedConfig, "proxy_set_header Upgrade $http_upgrade;") { t.Fatal("expected preview config to omit websocket upgrade header when disabled") } - if strings.Contains(preview.RenderedConfig, "proxy_set_header Connection $http_connection;") { + if strings.Contains(preview.RenderedConfig, "proxy_set_header Connection $connection_upgrade;") { t.Fatal("expected preview config to omit websocket connection header when disabled") } } @@ -411,7 +411,7 @@ func TestPreviewAndDiffConfigVersion(t *testing.T) { } } -func TestRenderConfigRejectsUnknownSubdomainHosts(t *testing.T) { +func TestRenderConfigUsesDefaultServerFallback(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ @@ -431,11 +431,17 @@ func TestRenderConfigRejectsUnknownSubdomainHosts(t *testing.T) { if !strings.Contains(preview.RenderedConfig, `server_name git.arctel.net;`) { t.Fatal("expected rendered config to include exact server_name") } - if !strings.Contains(preview.RenderedConfig, `if ($host != "git.arctel.net") {`) { - t.Fatal("expected rendered config to guard against unknown subdomain host matches") + if strings.Contains(preview.RenderedConfig, `if ($host != "git.arctel.net") {`) { + t.Fatal("expected rendered config to avoid per-route host guard") } - if !strings.Contains(preview.RenderedConfig, "return 404;") { - t.Fatal("expected rendered config to return 404 when host does not exactly match route domain") + if !strings.Contains(preview.MainConfig, "listen 80 default_server;") { + t.Fatal("expected preview main config to include default http server") + } + if !strings.Contains(preview.MainConfig, "server_name _;") { + t.Fatal("expected preview main config to include default server_name") + } + if !strings.Contains(preview.MainConfig, "return 404;") { + t.Fatal("expected preview main config to return 404 for unmatched hosts") } } @@ -483,6 +489,12 @@ func TestOpenRestyMainConfigTemplateRenderAndValidate(t *testing.T) { if !strings.Contains(preview.MainConfig, "access_log __OPENFLARE_ACCESS_LOG__ openflare_json;") { t.Fatal("expected preview main config to preserve managed access log placeholder") } + if !strings.Contains(preview.MainConfig, "map $http_upgrade $connection_upgrade {") { + t.Fatal("expected preview main config to preserve managed websocket upgrade map") + } + if !strings.Contains(preview.MainConfig, "listen 80 default_server;") { + t.Fatal("expected preview main config to preserve managed default server block") + } invalidTemplate := strings.ReplaceAll( common.OpenRestyMainConfigTemplate, @@ -501,6 +513,15 @@ func TestOpenRestyMainConfigTemplateRenderAndValidate(t *testing.T) { if err := ValidateOpenRestyMainConfigTemplate(invalidTemplate); err == nil { t.Fatal("expected template without managed access log placeholder to fail validation") } + + invalidTemplate = strings.ReplaceAll( + common.OpenRestyMainConfigTemplate, + "{{OpenRestyConnectionUpgradeMap}}", + "", + ) + if err := ValidateOpenRestyMainConfigTemplate(invalidTemplate); err == nil { + t.Fatal("expected template without managed websocket upgrade map placeholder to fail validation") + } } func TestOpenRestyCommonRequestOptionsRender(t *testing.T) {