package service import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/json" "encoding/pem" "math/big" "openflare/common" "openflare/model" "path/filepath" "strings" "testing" "time" ) func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) { setupServiceTestDB(t) certPEM, keyPEM := generateCertificatePair(t, []string{"app.example.com"}) certificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "app-example", CertPEM: certPEM, KeyPEM: keyPEM, Remark: "test cert", }) if err != nil { t.Fatalf("CreateTLSCertificate failed: %v", err) } if certificate.NotAfter.Before(certificate.NotBefore) { t.Fatal("expected certificate validity period to be parsed") } route, err := CreateProxyRoute(ProxyRouteInput{ Domain: "app.example.com", OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, CertID: &certificate.ID, RedirectHTTP: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if !route.EnableHTTPS || route.CertID == nil { t.Fatal("expected https fields to be persisted") } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.MainConfig, "include __OPENFLARE_ROUTE_CONFIG__;") { t.Fatal("expected main config to include managed route config placeholder") } if !strings.Contains(result.Version.MainConfig, "access_log __OPENFLARE_ACCESS_LOG__ openflare_json;") { t.Fatal("expected main config to include managed access log placeholder") } if !strings.Contains(result.Version.MainConfig, "error_log __OPENFLARE_ERROR_LOG__ warn;") { t.Fatal("expected main config to include managed error log placeholder") } if !strings.Contains(result.Version.MainConfig, "log_by_lua_file __OPENFLARE_LUA_DIR__/observability/log.lua;") { t.Fatal("expected main config to include managed openresty lua log hook") } if !strings.Contains(result.Version.MainConfig, "listen __OPENFLARE_OBSERVABILITY_LISTEN__;") { t.Fatal("expected main config to include managed openresty observability listen placeholder") } if strings.Contains(result.Version.MainConfig, "resolver ") { t.Fatal("expected main config to omit resolver directive when no resolvers are configured") } if !strings.Contains(result.Version.MainConfig, "use epoll;") { t.Fatal("expected main config to default to epoll event model") } if !strings.Contains(result.Version.MainConfig, "multi_accept on;") { t.Fatal("expected main config to default multi_accept to on") } if !strings.Contains(result.Version.MainConfig, "keepalive_timeout 20;") { t.Fatal("expected main config to default keepalive_timeout to 20") } if !strings.Contains(result.Version.MainConfig, "proxy_connect_timeout 3;") { t.Fatal("expected main config to default proxy_connect_timeout to 3") } if strings.Contains(result.Version.MainConfig, "allow 127.0.0.1;") { t.Fatal("expected main config to avoid hard-coded allow rules on observability server") } if !strings.Contains(result.Version.RenderedConfig, "listen 443 ssl;") { t.Fatal("expected rendered config to include https ssl listener") } if !strings.Contains(result.Version.RenderedConfig, "http2 on;") { t.Fatal("expected rendered config to enable http2 with dedicated directive") } 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") } if !strings.Contains(result.Version.RenderedConfig, "__OPENFLARE_CERT_DIR__/") { t.Fatal("expected rendered config to keep cert dir placeholder for certificates") } if !strings.Contains(result.Version.SupportFilesJSON, ".crt") || !strings.Contains(result.Version.SupportFilesJSON, ".key") { t.Fatal("expected support files to contain certificate and key") } } func TestNextVersionNumberUsesMaxDailySequence(t *testing.T) { setupServiceTestDB(t) seed := []model.ConfigVersion{ { Version: "20260526-001", SnapshotJSON: "{}", RenderedConfig: "server {}", SupportFilesJSON: "[]", Checksum: "checksum-1", CreatedBy: "root", }, { Version: "20260526-003", SnapshotJSON: "{}", RenderedConfig: "server {}", SupportFilesJSON: "[]", Checksum: "checksum-3", CreatedBy: "root", }, } for _, version := range seed { if err := model.DB.Create(&version).Error; err != nil { t.Fatalf("failed to seed config version %s: %v", version.Version, err) } } next, err := nextVersionNumber(time.Date(2026, 5, 26, 12, 0, 0, 0, time.Local)) if err != nil { t.Fatalf("nextVersionNumber failed: %v", err) } if next != "20260526-004" { t.Fatalf("expected next version to follow max suffix, got %s", next) } } func TestCreateProxyRouteRejectsHTTPSWithoutCertificate(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "secure.example.com", OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, }) if err == nil || !strings.Contains(err.Error(), "must select a certificate") { t.Fatalf("expected certificate validation error, got %v", err) } } func TestCreateProxyRouteSupportsWebsiteDomains(t *testing.T) { setupServiceTestDB(t) route, err := CreateProxyRoute(ProxyRouteInput{ SiteName: "main-site", Domains: []string{"app.example.com", "www.example.com"}, OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if route.SiteName != "main-site" { t.Fatalf("unexpected site name: %s", route.SiteName) } if route.Domain != "app.example.com" { t.Fatalf("expected primary domain mirror, got %s", route.Domain) } if len(route.Domains) != 2 || route.Domains[1] != "www.example.com" { t.Fatalf("expected domains payload to contain alias, got %#v", route.Domains) } } func TestPublishConfigVersionRendersCustomHeaders(t *testing.T) { setupServiceTestDB(t) if err := model.UpdateOption("OpenRestyWebsocketEnabled", "true"); err != nil { t.Fatalf("UpdateOption OpenRestyWebsocketEnabled failed: %v", err) } _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "custom.example.com", OriginURL: "https://origin.internal", Enabled: true, CustomHeaders: []ProxyRouteCustomHeaderInput{ {Key: "X-Trace-Id", Value: "$request_id"}, {Key: "X-Env", Value: "staging edge"}, }, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, `proxy_set_header X-Trace-Id "$request_id";`) { t.Fatal("expected rendered config to include custom header") } if !strings.Contains(result.Version.RenderedConfig, `proxy_set_header X-Env "staging edge";`) { t.Fatal("expected rendered config to include quoted custom header value") } if !strings.Contains(result.Version.SnapshotJSON, "custom_headers") { t.Fatal("expected snapshot to include custom headers") } if !strings.Contains(result.Version.RenderedConfig, "proxy_http_version 1.1;") { t.Fatal("expected rendered config to enable HTTP/1.1 proxying for websocket upgrades") } 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 $connection_upgrade;") { t.Fatal("expected rendered config to use normalized websocket connection header") } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_custom_example_com_1 {") { t.Fatal("expected hostname origin to render named upstream") } if !strings.Contains(result.Version.RenderedConfig, "server origin.internal max_fails=3 fail_timeout=10s;") { t.Fatal("expected hostname origin to render upstream server entry") } if !strings.Contains(result.Version.RenderedConfig, "keepalive 128;") { t.Fatal("expected named upstream to enable keepalive") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://backend_custom_example_com_1;") { t.Fatal("expected hostname origin to proxy through named upstream") } } func TestCreateProxyRouteRejectsCachePolicyWithoutRules(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "cache.example.com", OriginURL: "https://origin.internal", Enabled: true, CacheEnabled: true, CachePolicy: proxyRouteCachePolicySuffix, }) if err == nil || !strings.Contains(err.Error(), "at least one suffix") { t.Fatalf("expected cache rule validation error, got %v", err) } } func TestPublishConfigVersionRendersRouteLevelCachePolicy(t *testing.T) { setupServiceTestDB(t) if err := model.UpdateOption("OpenRestyCacheEnabled", "true"); err != nil { t.Fatalf("UpdateOption OpenRestyCacheEnabled failed: %v", err) } if err := model.UpdateOption("OpenRestyCachePath", "/var/cache/openresty/openflare"); err != nil { t.Fatalf("UpdateOption OpenRestyCachePath failed: %v", err) } _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "static.example.com", OriginURL: "https://origin.internal", Enabled: true, CacheEnabled: true, CachePolicy: proxyRouteCachePolicySuffix, CacheRules: []string{"jpg", ".css", "js"}, }) if err != nil { t.Fatalf("CreateProxyRoute cached failed: %v", err) } _, err = CreateProxyRoute(ProxyRouteInput{ Domain: "nocache.example.com", OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute uncached failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.MainConfig, "proxy_cache_path /var/cache/openresty/openflare") { t.Fatal("expected main config to include cache zone when cache infra is enabled") } if !strings.Contains(result.Version.MainConfig, `proxy_cache_key "$scheme$host$request_uri";`) { t.Fatal("expected main config to default cache key to host dimension") } if !strings.Contains(result.Version.RenderedConfig, "proxy_cache_methods GET;") { t.Fatal("expected rendered config to only cache GET requests") } if !strings.Contains(result.Version.RenderedConfig, "proxy_cache_bypass $openflare_skip_cache;") { t.Fatal("expected rendered config to bypass cache when request is unsafe") } if !strings.Contains(result.Version.RenderedConfig, "proxy_no_cache $openflare_skip_cache;") { t.Fatal("expected rendered config to avoid storing unsafe requests in cache") } if !strings.Contains(result.Version.RenderedConfig, "if ($http_authorization != \"\")") { t.Fatal("expected rendered config to bypass authenticated requests") } if !strings.Contains(result.Version.RenderedConfig, "if ($request_method != GET)") { t.Fatal("expected rendered config to bypass non-GET requests") } if !strings.Contains(result.Version.RenderedConfig, "if ($uri !~* \"\\\\.(?:jpg|css|js)$\")") { t.Fatal("expected rendered config to render suffix cache matching rule") } if strings.Count(result.Version.RenderedConfig, "proxy_cache openflare_cache;") != 1 { t.Fatal("expected only cache-enabled route to include proxy_cache directive") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://backend_static_example_com_1;") { t.Fatal("expected cache-enabled hostname route to proxy through named upstream") } if !strings.Contains(result.Version.SnapshotJSON, `"cache_enabled":true`) { t.Fatal("expected snapshot to include route cache toggle") } if !strings.Contains(result.Version.SnapshotJSON, `"cache_policy":"suffix"`) { t.Fatal("expected snapshot to include route cache policy") } } func TestPublishConfigVersionRendersMultipleUpstreams(t *testing.T) { setupServiceTestDB(t) route, err := CreateProxyRoute(ProxyRouteInput{ Domain: "lb.example.com", OriginURL: "http://10.0.0.11:39010", Upstreams: []string{"http://10.0.0.12:39010", "http://10.0.0.13:39010"}, Enabled: true, OriginHost: "lb.example.com", }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if !strings.Contains(route.Upstreams, "10.0.0.12:39010") { t.Fatalf("expected route upstreams to persist, got %s", route.Upstreams) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_lb_example_com_1 {") { t.Fatal("expected rendered config to define upstream block for load balancing route") } if strings.Count(result.Version.RenderedConfig, "max_fails=3 fail_timeout=10s;") < 3 { t.Fatal("expected rendered config to include every upstream server") } if !strings.Contains(result.Version.RenderedConfig, "server 10.0.0.11:39010 max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include primary upstream server") } if !strings.Contains(result.Version.RenderedConfig, "server 10.0.0.12:39010 max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include secondary upstream server") } if !strings.Contains(result.Version.RenderedConfig, "server 10.0.0.13:39010 max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include tertiary upstream server") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass http://backend_lb_example_com_1;") { t.Fatal("expected rendered config to proxy through load balancing upstream") } if !strings.Contains(result.Version.SnapshotJSON, `"upstreams":["http://10.0.0.11:39010","http://10.0.0.12:39010","http://10.0.0.13:39010"]`) { t.Fatal("expected snapshot to include upstream list") } } func TestPublishConfigVersionRendersMultiDomainWebsite(t *testing.T) { setupServiceTestDB(t) certPEM, keyPEM := generateCertificatePair(t, []string{"app.example.com", "www.example.com"}) certificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "multi-domain", CertPEM: certPEM, KeyPEM: keyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate failed: %v", err) } _, err = CreateProxyRoute(ProxyRouteInput{ SiteName: "marketing-site", Domains: []string{"app.example.com", "www.example.com"}, OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, CertID: &certificate.ID, RedirectHTTP: true, CacheEnabled: true, CachePolicy: proxyRouteCachePolicyPathPrefix, CacheRules: []string{"/assets"}, CustomHeaders: []ProxyRouteCustomHeaderInput{{Key: "X-Site", Value: "marketing"}}, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "server_name app.example.com www.example.com;") { t.Fatal("expected rendered config to include all domains in one server_name") } if strings.Contains(result.Version.RenderedConfig, "server_name app.example.com;") { t.Fatal("expected rendered config to avoid standalone primary-domain server block") } if strings.Contains(result.Version.RenderedConfig, "server_name www.example.com;") { t.Fatal("expected rendered config to avoid standalone alias server block") } if !strings.Contains(result.Version.SnapshotJSON, `"site_name":"marketing-site"`) { t.Fatal("expected snapshot to include site_name") } if !strings.Contains(result.Version.SnapshotJSON, `"domains":["app.example.com","www.example.com"]`) { t.Fatal("expected snapshot to include domain list") } } func TestPublishConfigVersionRendersMultipleCertificatesForMultiDomainWebsite(t *testing.T) { setupServiceTestDB(t) appCertPEM, appKeyPEM := generateCertificatePair(t, []string{"app.example.com"}) appCertificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "app-only", CertPEM: appCertPEM, KeyPEM: appKeyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate app-only failed: %v", err) } wwwCertPEM, wwwKeyPEM := generateCertificatePair(t, []string{"www.example.com"}) wwwCertificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "www-only", CertPEM: wwwCertPEM, KeyPEM: wwwKeyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate www-only failed: %v", err) } route, err := CreateProxyRoute(ProxyRouteInput{ SiteName: "marketing-site", Domains: []string{"app.example.com", "www.example.com"}, OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, DomainCertIDs: []uint{appCertificate.ID, wwwCertificate.ID}, RedirectHTTP: true, CacheEnabled: true, CachePolicy: proxyRouteCachePolicyPathPrefix, CacheRules: []string{"/assets"}, CustomHeaders: []ProxyRouteCustomHeaderInput{{Key: "X-Site", Value: "marketing"}}, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if route.CertID == nil || *route.CertID != appCertificate.ID { t.Fatalf("expected primary cert mirror to point at first certificate, got %#v", route.CertID) } if len(route.CertIDs) != 2 || route.CertIDs[0] != appCertificate.ID || route.CertIDs[1] != wwwCertificate.ID { t.Fatalf("expected cert_ids to persist in order, got %#v", route.CertIDs) } if len(route.DomainCertIDs) != 2 || route.DomainCertIDs[0] != appCertificate.ID || route.DomainCertIDs[1] != wwwCertificate.ID { t.Fatalf("expected domain_cert_ids to persist per domain, got %#v", route.DomainCertIDs) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if strings.Count(result.Version.RenderedConfig, "ssl_certificate __OPENFLARE_CERT_DIR__/") != 2 { t.Fatalf("expected rendered config to include two ssl_certificate directives, got %s", result.Version.RenderedConfig) } if strings.Count(result.Version.RenderedConfig, "ssl_certificate_key __OPENFLARE_CERT_DIR__/") != 2 { t.Fatalf("expected rendered config to include two ssl_certificate_key directives, got %s", result.Version.RenderedConfig) } if !strings.Contains(result.Version.SupportFilesJSON, certificateCertFileName(appCertificate.ID)) { t.Fatal("expected support files to include first certificate") } if !strings.Contains(result.Version.SupportFilesJSON, certificateCertFileName(wwwCertificate.ID)) { t.Fatal("expected support files to include second certificate") } if !strings.Contains(result.Version.SnapshotJSON, `"cert_ids":[`) { t.Fatal("expected snapshot to include cert_ids") } if !strings.Contains(result.Version.SnapshotJSON, `"domain_cert_ids":[`) { t.Fatal("expected snapshot to include domain_cert_ids") } } func TestPublishConfigVersionSkipsHTTPSForDomainsWithoutCertificate(t *testing.T) { setupServiceTestDB(t) if err := model.UpdateOption("OpenRestyHTTP3Enabled", "false"); err != nil { t.Fatalf("UpdateOption failed: %v", err) } appCertPEM, appKeyPEM := generateCertificatePair(t, []string{"app.example.com"}) appCertificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "app-only", CertPEM: appCertPEM, KeyPEM: appKeyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate app-only failed: %v", err) } route, err := CreateProxyRoute(ProxyRouteInput{ SiteName: "partial-https-site", Domains: []string{"app.example.com", "www.example.com"}, OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, DomainCertIDs: []uint{appCertificate.ID, 0}, RedirectHTTP: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if len(route.CertIDs) != 1 || route.CertIDs[0] != appCertificate.ID { t.Fatalf("expected website cert_ids to keep used certificates only, got %#v", route.CertIDs) } if len(route.DomainCertIDs) != 2 || route.DomainCertIDs[0] != appCertificate.ID || route.DomainCertIDs[1] != 0 { t.Fatalf("expected domain_cert_ids to preserve unassigned domains, got %#v", route.DomainCertIDs) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if strings.Contains(result.Version.RenderedConfig, "listen 443 ssl;\n http2 on;\n server_name app.example.com www.example.com;") { t.Fatal("expected https server block to exclude domains without certificate") } if !strings.Contains(result.Version.RenderedConfig, "listen 443 ssl;\n http2 on;\n server_name app.example.com;") { t.Fatal("expected https server block to contain only the certified domain") } if !strings.Contains(result.Version.RenderedConfig, "listen 80;\n server_name app.example.com;\n\n return 301 https://$host$request_uri;") { t.Fatal("expected certified domain to keep http redirect") } if !strings.Contains(result.Version.RenderedConfig, "listen 80;\n server_name www.example.com;") { t.Fatal("expected non-certified domain to stay on plain http") } } func TestDiffConfigVersionTracksAddedDomainWithinWebsite(t *testing.T) { setupServiceTestDB(t) route, err := CreateProxyRoute(ProxyRouteInput{ SiteName: "main-site", Domains: []string{"app.example.com"}, OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if _, err := PublishConfigVersion("root", false); err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if _, err := UpdateProxyRoute(route.ID, ProxyRouteInput{ SiteName: "main-site", Domains: []string{"app.example.com", "www.example.com"}, OriginURL: "https://origin.internal", Enabled: true, }); err != nil { t.Fatalf("UpdateProxyRoute failed: %v", err) } diff, err := DiffConfigVersion() if err != nil { t.Fatalf("DiffConfigVersion failed: %v", err) } if len(diff.AddedDomains) != 1 || diff.AddedDomains[0] != "www.example.com" { t.Fatalf("unexpected added domains: %#v", diff.AddedDomains) } if len(diff.ModifiedDomains) != 1 || diff.ModifiedDomains[0] != "app.example.com" { t.Fatalf("unexpected modified domains: %#v", diff.ModifiedDomains) } if len(diff.ModifiedSites) != 1 || diff.ModifiedSites[0] != "main-site" { t.Fatalf("unexpected modified sites: %#v", diff.ModifiedSites) } } func TestCreateProxyRouteRejectsInvalidRateLimitFields(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "limit.example.com", OriginURL: "https://origin.internal", Enabled: true, LimitConnPerServer: -1, }) if err == nil || !strings.Contains(err.Error(), "limit_conn_per_server") { t.Fatalf("expected limit_conn_per_server validation error, got %v", err) } _, err = CreateProxyRoute(ProxyRouteInput{ Domain: "limit.example.com", OriginURL: "https://origin.internal", Enabled: true, LimitRate: "12x", }) if err == nil || !strings.Contains(err.Error(), "limit_rate") { t.Fatalf("expected limit_rate validation error, got %v", err) } } func TestPublishConfigVersionRendersRouteRateLimits(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ SiteName: "limited-site", Domains: []string{"limit.example.com", "www.limit.example.com"}, OriginURL: "https://origin.internal", Enabled: true, LimitConnPerServer: 120, LimitConnPerIP: 12, LimitRate: "512K", }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.MainConfig, "limit_conn_zone $server_name zone=openflare_conn_per_server:10m;") { t.Fatal("expected main config to include server limit_conn_zone") } if !strings.Contains(result.Version.MainConfig, "limit_conn_zone $binary_remote_addr zone=openflare_conn_per_ip:10m;") { t.Fatal("expected main config to include ip limit_conn_zone") } if !strings.Contains(result.Version.RenderedConfig, "limit_conn openflare_conn_per_server 120;") { t.Fatal("expected rendered config to include per-server limit_conn") } if !strings.Contains(result.Version.RenderedConfig, "limit_conn openflare_conn_per_ip 12;") { t.Fatal("expected rendered config to include per-ip limit_conn") } if !strings.Contains(result.Version.RenderedConfig, "limit_rate 512k;") { t.Fatal("expected rendered config to include normalized limit_rate") } if !strings.Contains(result.Version.SnapshotJSON, `"limit_rate":"512k"`) { t.Fatal("expected snapshot to include normalized limit_rate") } } func TestPublishConfigVersionRendersHostnameLoadBalancingUpstream(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "hostname-lb.example.com", OriginURL: "http://c1:39010", Upstreams: []string{"http://c2:39010"}, Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_hostname_lb_example_com_1 {") { t.Fatal("expected hostname load balancing route to define named upstream") } if !strings.Contains(result.Version.RenderedConfig, "server c1:39010 max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include primary hostname upstream") } if !strings.Contains(result.Version.RenderedConfig, "server c2:39010 max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include secondary hostname upstream") } if strings.Contains(result.Version.RenderedConfig, " resolve ") { t.Fatal("expected hostname upstreams to avoid resolver-based server parameters") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass http://backend_hostname_lb_example_com_1;") { t.Fatal("expected hostname load balancing route to proxy through named upstream") } } func TestPublishConfigVersionOverridesOriginHostHeader(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "git.arctel.de", OriginURL: "https://git.arctel.net", OriginHost: "git.arctel.net", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, `proxy_set_header Host "git.arctel.net";`) { t.Fatal("expected rendered config to override host header for origin routing") } if !strings.Contains(result.Version.RenderedConfig, "proxy_ssl_server_name on;") { t.Fatal("expected rendered config to enable proxy ssl server name for https origin") } if !strings.Contains(result.Version.RenderedConfig, `proxy_ssl_name "git.arctel.net";`) { t.Fatal("expected rendered config to set proxy ssl name from origin host override") } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_git_arctel_de_1 {") { t.Fatal("expected hostname origin to render named upstream") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://backend_git_arctel_de_1;") { t.Fatal("expected rendered config to proxy through named upstream for hostname origin") } if !strings.Contains(result.Version.SnapshotJSON, `"origin_host":"git.arctel.net"`) { t.Fatal("expected snapshot to include origin_host override") } } func TestPublishConfigVersionUsesNamedUpstreamForOriginBasePath(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "resolver.example.com", OriginURL: "https://origin.internal/api/", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_resolver_example_com_1 {") { t.Fatal("expected hostname origin with base path to still render named upstream") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://backend_resolver_example_com_1/api/;") { t.Fatal("expected rendered config to preserve base path while proxying through named upstream") } } func TestPublishConfigVersionUsesNamedUpstreamForHostnameOrigins(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "resolver-upstream.example.com", OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_resolver_upstream_example_com_1 {") { t.Fatal("expected rendered config to define named upstream for hostname origin") } if !strings.Contains(result.Version.RenderedConfig, "server origin.internal max_fails=3 fail_timeout=10s;") { t.Fatal("expected rendered config to include hostname upstream server entry") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass https://backend_resolver_upstream_example_com_1;") { t.Fatal("expected rendered config to proxy through named upstream for hostname origin") } } func TestPublishConfigVersionUsesNamedUpstreamForIPOrigins(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "ip-origin.example.com", OriginURL: "http://10.0.0.8:8080", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, "upstream backend_ip_origin_example_com_1 {") { t.Fatal("expected rendered config to define named upstream for static IP origins") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass http://backend_ip_origin_example_com_1;") { t.Fatal("expected rendered config to proxy through named upstream for IP origin") } if strings.Contains(result.Version.RenderedConfig, `set $openflare_upstream "http://10.0.0.8:8080"`) { t.Fatal("expected rendered config to avoid runtime resolver variables for IP origin") } } func TestPreviewConfigVersionCanDisableWebsocketHeaders(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "ws-off.example.com", OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if err := model.UpdateOption("OpenRestyWebsocketEnabled", "false"); err != nil { t.Fatalf("UpdateOption OpenRestyWebsocketEnabled failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.RenderedConfig, "proxy_http_version 1.1;") { t.Fatal("expected preview config to keep HTTP/1.1 proxying for named upstream keepalive") } if !strings.Contains(preview.RenderedConfig, `proxy_set_header Connection "";`) { t.Fatal("expected preview config to clear connection header when websocket upgrades are disabled") } if strings.Contains(preview.RenderedConfig, "proxy_set_header Upgrade $http_upgrade;") { t.Fatal("expected preview config to omit websocket upgrade header when disabled") } } func TestPreviewAndDiffConfigVersion(t *testing.T) { setupServiceTestDB(t) if err := model.UpdateOption("OpenRestyWebsocketEnabled", "true"); err != nil { t.Fatalf("UpdateOption OpenRestyWebsocketEnabled failed: %v", err) } stableRoute, err := CreateProxyRoute(ProxyRouteInput{ Domain: "stable.example.com", OriginURL: "https://origin-a.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute stable failed: %v", err) } modifiedRoute, err := CreateProxyRoute(ProxyRouteInput{ Domain: "api.example.com", OriginURL: "https://origin-api-a.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute modified failed: %v", err) } removedRoute, err := CreateProxyRoute(ProxyRouteInput{ Domain: "old.example.com", OriginURL: "https://origin-old.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute removed failed: %v", err) } if _, err = PublishConfigVersion("root", false); err != nil { t.Fatalf("initial PublishConfigVersion failed: %v", err) } if _, err = UpdateProxyRoute(modifiedRoute.ID, ProxyRouteInput{ Domain: "api.example.com", OriginURL: "https://origin-api-b.internal", Enabled: true, CustomHeaders: []ProxyRouteCustomHeaderInput{ {Key: "X-Release", Value: "candidate"}, }, }); err != nil { t.Fatalf("UpdateProxyRoute failed: %v", err) } if _, err = UpdateProxyRoute(removedRoute.ID, ProxyRouteInput{ Domain: "old.example.com", OriginURL: "https://origin-old.internal", Enabled: false, }); err != nil { t.Fatalf("disable removed route failed: %v", err) } if _, err = CreateProxyRoute(ProxyRouteInput{ Domain: "new.example.com", OriginURL: "https://origin-new.internal", Enabled: true, }); err != nil { t.Fatalf("CreateProxyRoute new failed: %v", err) } if _, err = UpdateProxyRoute(stableRoute.ID, ProxyRouteInput{ Domain: stableRoute.Domain, OriginURL: stableRoute.OriginURL, Enabled: true, Remark: "remark only change", }); err != nil { t.Fatalf("UpdateProxyRoute stable failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.MainConfig, "include __OPENFLARE_ROUTE_CONFIG__;") { t.Fatal("expected preview main config to include managed route config placeholder") } if !strings.Contains(preview.MainConfig, "log_by_lua_file __OPENFLARE_LUA_DIR__/observability/log.lua;") { t.Fatal("expected preview main config to include managed openresty lua log hook") } if !strings.Contains(preview.RenderedConfig, `proxy_set_header X-Release "candidate";`) { t.Fatal("expected preview config to include modified custom header") } if preview.RouteCount != 3 { t.Fatalf("expected 3 enabled routes in preview, got %d", preview.RouteCount) } diff, err := DiffConfigVersion() if err != nil { t.Fatalf("DiffConfigVersion failed: %v", err) } if len(diff.AddedDomains) != 1 || diff.AddedDomains[0] != "new.example.com" { t.Fatalf("unexpected added domains: %#v", diff.AddedDomains) } if len(diff.RemovedDomains) != 1 || diff.RemovedDomains[0] != "old.example.com" { t.Fatalf("unexpected removed domains: %#v", diff.RemovedDomains) } if len(diff.ModifiedDomains) != 1 || diff.ModifiedDomains[0] != "api.example.com" { t.Fatalf("unexpected modified domains: %#v", diff.ModifiedDomains) } if diff.MainConfigChanged { t.Fatal("expected main config to remain unchanged when only routes change") } if err = model.UpdateOption("OpenRestyProxyReadTimeout", "120"); err != nil { t.Fatalf("UpdateOption failed: %v", err) } if err = model.UpdateOption("OpenRestyWebsocketEnabled", "false"); err != nil { t.Fatalf("UpdateOption OpenRestyWebsocketEnabled failed: %v", err) } diff, err = DiffConfigVersion() if err != nil { t.Fatalf("DiffConfigVersion after option change failed: %v", err) } if !diff.MainConfigChanged { t.Fatal("expected main config change after OpenResty option update") } if len(diff.ChangedOptionKeys) == 0 || diff.ChangedOptionKeys[0] == "" { t.Fatal("expected changed OpenResty option keys to be reported") } if len(diff.ChangedOptionDetails) == 0 { t.Fatal("expected changed OpenResty option details to be reported") } found := false foundWebsocket := false for _, item := range diff.ChangedOptionDetails { if item.Key == "OpenRestyProxyReadTimeout" { found = true if item.PreviousValue != "60" || item.CurrentValue != "120" { t.Fatalf("unexpected option diff values: %+v", item) } } if item.Key == "OpenRestyWebsocketEnabled" { foundWebsocket = true if item.PreviousValue != "true" || item.CurrentValue != "false" { t.Fatalf("unexpected websocket option diff values: %+v", item) } } } if !found { t.Fatal("expected OpenRestyProxyReadTimeout diff detail") } if !foundWebsocket { t.Fatal("expected OpenRestyWebsocketEnabled diff detail") } } func TestPublishConfigVersionDetectsPoWChanges(t *testing.T) { setupServiceTestDB(t) route, err := CreateProxyRoute(ProxyRouteInput{ Domain: "pow.example.com", OriginURL: "https://origin.internal", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } firstRelease, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("initial PublishConfigVersion failed: %v", err) } if !strings.Contains(firstRelease.Version.SupportFilesJSON, `"path":"waf_config.json"`) { t.Fatal("expected publish to include waf_config.json support file") } group, err := CreateWAFRuleGroup(WAFRuleGroupInput{ Name: "pow group", Enabled: true, BlockStatusCode: 418, PoWEnabled: true, PoWConfig: json.RawMessage(`{"difficulty":5,"algorithm":"slow","session_ttl":7200,"challenge_ttl":180,"whitelist":{"ips":["127.0.0.1"],"ip_cidrs":[],"paths":[],"path_regexes":[],"user_agents":[]},"blacklist":{"ips":[],"ip_cidrs":[],"paths":["/login"],"path_regexes":[],"user_agents":[]}}`), }) if err != nil { t.Fatalf("CreateWAFRuleGroup failed: %v", err) } if _, err = ReplaceWAFSiteRuleGroups(route.ID, []uint{group.ID}); err != nil { t.Fatalf("ReplaceWAFSiteRuleGroups failed: %v", err) } diff, err := DiffConfigVersion() if err != nil { t.Fatalf("DiffConfigVersion failed: %v", err) } if !diff.WAFConfigChanged { t.Fatal("expected PoW change (via WAF Rule Group) to trigger WAF config change") } secondRelease, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion after PoW change failed: %v", err) } if firstRelease.Version.Checksum == secondRelease.Version.Checksum { t.Fatal("expected PoW change to alter published checksum") } if !strings.Contains(secondRelease.Version.SnapshotJSON, `"pow_enabled":true`) { t.Fatal("expected snapshot to persist PoW enabled state") } if !strings.Contains(secondRelease.Version.MainConfig, "lua_shared_dict openflare_pow_config 1m;") { t.Fatal("expected main config to declare shared dict for pow config") } if !strings.Contains(secondRelease.Version.RenderedConfig, "location /.within.website/x/cmd/anubis/static/ {") { t.Fatal("expected rendered config to expose anubis static location") } if strings.Contains(secondRelease.Version.RenderedConfig, "location /.within.website/x/cmd/anubis/static/static/ {") { t.Fatal("expected rendered config to avoid duplicate static path segment") } if !strings.Contains(secondRelease.Version.RenderedConfig, "application/javascript js mjs;") { t.Fatal("expected rendered config to serve Anubis module scripts with a JavaScript MIME type") } if !strings.Contains(secondRelease.Version.RenderedConfig, " if not string.find(package.path, \"__OPENFLARE_LUA_DIR__/?.lua\", 1, true) then\n package.path = \"__OPENFLARE_LUA_DIR__/?.lua;__OPENFLARE_LUA_DIR__/?/init.lua;\" .. package.path\n end\n require(\"waf.runtime\").check()\n if ngx.ctx.openflare_waf_blocked then\n return\n end\n require(\"pow.runtime\").check()") { t.Fatal("expected combined WAF and PoW access handler to short-circuit before PoW") } locationStart := strings.Index(secondRelease.Version.RenderedConfig, " location / {\n") if locationStart < 0 { t.Fatal("expected rendered config to include root proxy location") } locationEnd := strings.Index(secondRelease.Version.RenderedConfig[locationStart:], " }\n") if locationEnd < 0 { t.Fatal("expected rendered config to close root proxy location") } rootLocationBlock := secondRelease.Version.RenderedConfig[locationStart : locationStart+locationEnd] if strings.Contains(rootLocationBlock, "access_by_lua") { t.Fatal("expected root proxy location to avoid mixing access_by_lua with proxy_pass") } if !strings.Contains(secondRelease.Version.SnapshotJSON, `"difficulty":5`) { t.Fatal("expected snapshot to persist PoW config") } var supportFiles []SupportFile if err := json.Unmarshal([]byte(secondRelease.Version.SupportFilesJSON), &supportFiles); err != nil { t.Fatalf("failed to decode support files: %v", err) } foundWafSupportFile := false for _, file := range supportFiles { if file.Path != "waf_config.json" { continue } foundWafSupportFile = true if !strings.Contains(file.Content, `"difficulty":5`) { t.Fatalf("expected waf support file to persist pow config, got %s", file.Content) } } if !foundWafSupportFile { t.Fatal("expected publish to include waf_config.json support file") } } func TestPublishConfigVersionRendersBasicAuthWithPoW(t *testing.T) { setupServiceTestDB(t) certPEM, keyPEM := generateCertificatePair(t, []string{"xbot.example.com"}) certificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "xbot-example", CertPEM: certPEM, KeyPEM: keyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate failed: %v", err) } route, err := CreateProxyRoute(ProxyRouteInput{ Domain: "xbot.example.com", OriginURL: "http://c1:36185", Enabled: true, EnableHTTPS: true, CertID: &certificate.ID, RedirectHTTP: true, BasicAuthEnabled: true, BasicAuthUsername: "admin", BasicAuthPassword: "123", }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } group, err := CreateWAFRuleGroup(WAFRuleGroupInput{ Name: "pow group", Enabled: true, BlockStatusCode: 418, PoWEnabled: true, PoWConfig: json.RawMessage(`{"difficulty":4,"algorithm":"fast","session_ttl":600,"challenge_ttl":300,"whitelist":{"ips":[],"ip_cidrs":[],"paths":[],"path_regexes":[],"user_agents":[]},"blacklist":{"ips":[],"ip_cidrs":[],"paths":[],"path_regexes":[],"user_agents":[]}}`), }) if err != nil { t.Fatalf("CreateWAFRuleGroup failed: %v", err) } if _, err = ReplaceWAFSiteRuleGroups(route.ID, []uint{group.ID}); err != nil { t.Fatalf("ReplaceWAFSiteRuleGroups failed: %v", err) } result, err := PublishConfigVersion("root", false) if err != nil { t.Fatalf("PublishConfigVersion failed: %v", err) } if !strings.Contains(result.Version.RenderedConfig, `if auth ~= "Basic YWRtaW46MTIz" then`) { t.Fatal("expected rendered config to include encoded basic auth credentials") } if !strings.Contains(result.Version.RenderedConfig, " return ngx.exit(401)\n end\n }\n") { t.Fatal("expected rendered basic auth Lua block to close the if statement before the nginx block") } if !strings.Contains(result.Version.RenderedConfig, `require("pow.runtime").check()`) { t.Fatal("expected PoW access handler to remain at server scope") } if !strings.Contains(result.Version.RenderedConfig, "proxy_pass http://backend_xbot_example_com_1;") { t.Fatal("expected proxy_pass to stay in the root location after basic auth") } if !strings.Contains(result.Version.SnapshotJSON, `"basic_auth_enabled":true`) { t.Fatal("expected snapshot to include basic auth enabled state") } if !strings.Contains(result.Version.SnapshotJSON, `"basic_auth_username":"admin"`) { t.Fatal("expected snapshot to include basic auth username") } if !strings.Contains(result.Version.SnapshotJSON, `"basic_auth_password":"123"`) { t.Fatal("expected snapshot to include basic auth password") } } func TestDiffConfigVersionDetectsBasicAuthChanges(t *testing.T) { setupServiceTestDB(t) route, err := CreateProxyRoute(ProxyRouteInput{ Domain: "auth.example.com", OriginURL: "http://c1:8080", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if _, err = PublishConfigVersion("root", false); err != nil { t.Fatalf("initial PublishConfigVersion failed: %v", err) } _, err = UpdateProxyRoute(route.ID, ProxyRouteInput{ Domain: route.Domain, OriginURL: route.OriginURL, Enabled: true, BasicAuthEnabled: true, BasicAuthUsername: "admin", BasicAuthPassword: "123", }) if err != nil { t.Fatalf("UpdateProxyRoute failed: %v", err) } diff, err := DiffConfigVersion() if err != nil { t.Fatalf("DiffConfigVersion failed: %v", err) } if len(diff.ModifiedDomains) != 1 || diff.ModifiedDomains[0] != "auth.example.com" { t.Fatalf("expected basic auth change to mark domain as modified, got %#v", diff.ModifiedDomains) } if len(diff.ModifiedSites) != 1 || diff.ModifiedSites[0] != "auth.example.com" { t.Fatalf("expected basic auth change to mark site as modified, got %#v", diff.ModifiedSites) } } func TestRenderConfigUsesDefaultServerFallback(t *testing.T) { setupServiceTestDB(t) _, err := CreateProxyRoute(ProxyRouteInput{ Domain: "git.arctel.net", OriginURL: "http://127.0.0.1:8080", Enabled: true, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } 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 avoid per-route host guard") } 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, "listen 443 ssl default_server;") { t.Fatal("expected preview main config to include default https 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 421;") { t.Fatal("expected preview main config to return 421 for unmatched hosts") } if !strings.Contains(preview.MainConfig, "ssl_reject_handshake on;") { t.Fatal("expected preview main config to reject unmatched https handshakes") } } func TestCreateTLSCertificateRejectsInvalidPEM(t *testing.T) { setupServiceTestDB(t) _, err := CreateTLSCertificate(TLSCertificateInput{ Name: "broken-cert", CertPEM: "invalid", KeyPEM: "invalid", }) if err == nil { t.Fatal("expected invalid pem to fail") } } func TestOpenRestyMainConfigTemplateRenderAndValidate(t *testing.T) { setupServiceTestDB(t) customTemplate := strings.ReplaceAll( common.OpenRestyMainConfigTemplate, "pid logs/nginx.pid;", "pid logs/nginx.pid;\nworker_shutdown_timeout 10s;", ) if err := ValidateOpenRestyMainConfigTemplate(customTemplate); err != nil { t.Fatalf("ValidateOpenRestyMainConfigTemplate failed: %v", err) } if err := model.UpdateOption("OpenRestyMainConfigTemplate", customTemplate); err != nil { t.Fatalf("UpdateOption OpenRestyMainConfigTemplate failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.MainConfig, "worker_shutdown_timeout 10s;") { t.Fatal("expected preview main config to include custom template content") } if strings.Contains(preview.MainConfig, "{{OpenRestyWorkerProcesses}}") { t.Fatal("expected preview main config placeholders to be rendered") } if !strings.Contains(preview.MainConfig, "include __OPENFLARE_ROUTE_CONFIG__;") { t.Fatal("expected preview main config to preserve managed route include") } 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, "error_log __OPENFLARE_ERROR_LOG__ warn;") { t.Fatal("expected preview main config to preserve managed error 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") } if !strings.Contains(preview.MainConfig, "listen 443 ssl default_server;") { t.Fatal("expected preview main config to preserve managed default https server block") } if !strings.Contains(preview.MainConfig, "ssl_reject_handshake on;") { t.Fatal("expected preview main config to preserve managed https handshake rejection") } invalidTemplate := strings.ReplaceAll( common.OpenRestyMainConfigTemplate, "{{OpenRestyRouteConfigInclude}}", "", ) if err := ValidateOpenRestyMainConfigTemplate(invalidTemplate); err == nil { t.Fatal("expected template without managed route placeholder to fail validation") } invalidTemplate = strings.ReplaceAll( common.OpenRestyMainConfigTemplate, "{{OpenRestyAccessLogPath}}", "", ) 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") } invalidTemplate = strings.ReplaceAll( common.OpenRestyMainConfigTemplate, "{{OpenRestyErrorLogPath}}", "", ) if err := ValidateOpenRestyMainConfigTemplate(invalidTemplate); err == nil { t.Fatal("expected template without managed error log placeholder to fail validation") } } func TestOpenRestyCommonRequestOptionsRender(t *testing.T) { setupServiceTestDB(t) if err := model.UpdateOption("OpenRestyClientMaxBodySize", "128m"); err != nil { t.Fatalf("UpdateOption OpenRestyClientMaxBodySize failed: %v", err) } if err := model.UpdateOption("OpenRestyLargeClientHeaderBuffers", "8 32k"); err != nil { t.Fatalf("UpdateOption OpenRestyLargeClientHeaderBuffers failed: %v", err) } if err := model.UpdateOption("OpenRestyProxyRequestBufferingEnabled", "false"); err != nil { t.Fatalf("UpdateOption OpenRestyProxyRequestBufferingEnabled failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.MainConfig, "client_max_body_size 128m;") { t.Fatal("expected preview main config to include client_max_body_size") } if !strings.Contains(preview.MainConfig, "large_client_header_buffers 8 32k;") { t.Fatal("expected preview main config to include large_client_header_buffers") } if !strings.Contains(preview.MainConfig, "proxy_request_buffering off;") { t.Fatal("expected preview main config to include proxy_request_buffering off") } } func TestOpenRestyProxyRequestBufferingDefaultsToOff(t *testing.T) { setupServiceTestDB(t) preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.MainConfig, "proxy_request_buffering off;") { t.Fatal("expected preview main config to default proxy_request_buffering to off") } } func TestPreviewConfigVersionSupportsHTTP3(t *testing.T) { setupServiceTestDB(t) appCertPEM, appKeyPEM := generateCertificatePair(t, []string{"h3.example.com"}) appCertificate, err := CreateTLSCertificate(TLSCertificateInput{ Name: "h3-cert", CertPEM: appCertPEM, KeyPEM: appKeyPEM, }) if err != nil { t.Fatalf("CreateTLSCertificate failed: %v", err) } _, err = CreateProxyRoute(ProxyRouteInput{ SiteName: "h3-site", Domains: []string{"h3.example.com"}, OriginURL: "https://origin.internal", Enabled: true, EnableHTTPS: true, CertID: &appCertificate.ID, DomainCertIDs: []uint{appCertificate.ID}, }) if err != nil { t.Fatalf("CreateProxyRoute failed: %v", err) } if err := model.UpdateOption("OpenRestyHTTP3Enabled", "true"); err != nil { t.Fatalf("UpdateOption OpenRestyHTTP3Enabled failed: %v", err) } preview, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if !strings.Contains(preview.MainConfig, "listen 443 ssl default_server;\n listen 443 quic reuseport default_server;") { t.Fatalf("expected default server block to contain quic reuseport listener, main config: %s", preview.MainConfig) } if !strings.Contains(preview.RenderedConfig, "listen 443 quic;") { t.Fatalf("expected routing server block to contain listen 443 quic, rendered config: %s", preview.RenderedConfig) } if !strings.Contains(preview.RenderedConfig, "add_header Alt-Svc 'h3=\":443\"; ma=86400';") { t.Fatalf("expected routing server block to contain Alt-Svc header, rendered config: %s", preview.RenderedConfig) } if !strings.Contains(preview.RenderedConfig, "listen 443 ssl;") { t.Fatal("expected standard listen 443 ssl to be preserved") } if !strings.Contains(preview.RenderedConfig, "http2 on;") { t.Fatal("expected standard http2 on to be preserved") } if err := model.UpdateOption("OpenRestyHTTP3Enabled", "false"); err != nil { t.Fatalf("UpdateOption OpenRestyHTTP3Enabled failed: %v", err) } previewOff, err := PreviewConfigVersion() if err != nil { t.Fatalf("PreviewConfigVersion failed: %v", err) } if strings.Contains(previewOff.MainConfig, "listen 443 quic reuseport default_server;") { t.Fatal("expected default server block to omit quic listener when disabled") } if strings.Contains(previewOff.RenderedConfig, "listen 443 quic;") { t.Fatal("expected routing server block to omit quic listener when disabled") } if strings.Contains(previewOff.RenderedConfig, "add_header Alt-Svc") { t.Fatal("expected routing server block to omit Alt-Svc header when disabled") } } func setupServiceTestDB(t *testing.T) { t.Helper() nodeAccessTokenCache.reset() common.SQLitePath = filepath.Join(t.TempDir(), "service.db") if err := model.InitDB(); err != nil { t.Fatalf("failed to init db: %v", err) } t.Cleanup(func() { nodeAccessTokenCache.reset() if err := model.CloseDB(); err != nil { t.Fatalf("failed to close db: %v", err) } }) } func generateCertificatePair(t *testing.T, dnsNames []string) (string, string) { t.Helper() privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("GenerateKey failed: %v", err) } template := &x509.Certificate{ Subject: pkix.Name{ CommonName: dnsNames[0], }, DNSNames: dnsNames, NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, IsCA: false, SerialNumber: big.NewInt(time.Now().UnixNano()), } certDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey) if err != nil { t.Fatalf("CreateCertificate failed: %v", err) } certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) return string(certPEM), string(keyPEM) }