|
|
|
@@ -1,3 +1,6 @@
|
|
|
|
|
// Copyright 2026 Arctel.net
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
|
|
// Package openresty renders OpenResty configuration from proxy route definitions.
|
|
|
|
|
package openresty
|
|
|
|
|
|
|
|
|
@@ -14,6 +17,7 @@ import (
|
|
|
|
|
"path"
|
|
|
|
|
"regexp"
|
|
|
|
|
"sort"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@@ -103,7 +107,7 @@ func RenderRouteConfig(doc Document, certificateFiles []SupportFile) (string, er
|
|
|
|
|
displayName := resolveRouteSiteName(route)
|
|
|
|
|
cacheConfig := routeCacheConfig{Enabled: route.CacheEnabled, Policy: route.CachePolicy, Rules: route.CacheRules}
|
|
|
|
|
limitConfig := mergeRouteLimitConfig(route, doc.OpenRestyConfig)
|
|
|
|
|
powEnabled, _ := getPoWConfigForRoute(route.ID, doc.WAF)
|
|
|
|
|
powEnabled := getPoWConfigForRoute(route.ID, doc.WAF)
|
|
|
|
|
if normalizeRouteUpstreamType(route.UpstreamType) == routeUpstreamTypePages {
|
|
|
|
|
if err := renderPagesRoute(&builder, route, displayName, serverNames, certificates, limitConfig, powEnabled, doc.OpenRestyConfig); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
@@ -168,32 +172,32 @@ func DedupeSupportFiles(files []SupportFile) []SupportFile {
|
|
|
|
|
func renderMainConfigTemplate(templateText string, cfg ConfigSnapshot, limitReqRates []string) string {
|
|
|
|
|
replacer := strings.NewReplacer(
|
|
|
|
|
"{{OpenRestyWorkerProcesses}}", cfg.WorkerProcesses,
|
|
|
|
|
"{{OpenRestyWorkerConnections}}", fmt.Sprintf("%d", cfg.WorkerConnections),
|
|
|
|
|
"{{OpenRestyWorkerRlimitNofile}}", fmt.Sprintf("%d", cfg.WorkerRlimitNofile),
|
|
|
|
|
"{{OpenRestyWorkerConnections}}", strconv.Itoa(cfg.WorkerConnections),
|
|
|
|
|
"{{OpenRestyWorkerRlimitNofile}}", strconv.Itoa(cfg.WorkerRlimitNofile),
|
|
|
|
|
"{{OpenRestyConnectionUpgradeMap}}", renderConnectionUpgradeMap(),
|
|
|
|
|
"{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(cfg.DefaultServerReturnStatus, cfg.HTTP3Enabled),
|
|
|
|
|
"{{OpenRestyAccessLogPath}}", AccessLogPlaceholder,
|
|
|
|
|
"{{OpenRestyErrorLogPath}}", ErrorLogPlaceholder,
|
|
|
|
|
"{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)),
|
|
|
|
|
"{{OpenRestyEventsMultiAcceptDirective}}", renderTemplateDirective(cfg.EventsMultiAcceptEnabled, "multi_accept on;"),
|
|
|
|
|
"{{OpenRestyKeepaliveTimeout}}", fmt.Sprintf("%d", cfg.KeepaliveTimeout),
|
|
|
|
|
"{{OpenRestyKeepaliveRequests}}", fmt.Sprintf("%d", cfg.KeepaliveRequests),
|
|
|
|
|
"{{OpenRestyClientHeaderTimeout}}", fmt.Sprintf("%d", cfg.ClientHeaderTimeout),
|
|
|
|
|
"{{OpenRestyClientBodyTimeout}}", fmt.Sprintf("%d", cfg.ClientBodyTimeout),
|
|
|
|
|
"{{OpenRestyKeepaliveTimeout}}", strconv.Itoa(cfg.KeepaliveTimeout),
|
|
|
|
|
"{{OpenRestyKeepaliveRequests}}", strconv.Itoa(cfg.KeepaliveRequests),
|
|
|
|
|
"{{OpenRestyClientHeaderTimeout}}", strconv.Itoa(cfg.ClientHeaderTimeout),
|
|
|
|
|
"{{OpenRestyClientBodyTimeout}}", strconv.Itoa(cfg.ClientBodyTimeout),
|
|
|
|
|
"{{OpenRestyClientMaxBodySize}}", cfg.ClientMaxBodySize,
|
|
|
|
|
"{{OpenRestyLargeClientHeaderBuffers}}", cfg.LargeClientHeaderBuffers,
|
|
|
|
|
"{{OpenRestySendTimeout}}", fmt.Sprintf("%d", cfg.SendTimeout),
|
|
|
|
|
"{{OpenRestyProxyConnectTimeout}}", fmt.Sprintf("%d", cfg.ProxyConnectTimeout),
|
|
|
|
|
"{{OpenRestyProxySendTimeout}}", fmt.Sprintf("%d", cfg.ProxySendTimeout),
|
|
|
|
|
"{{OpenRestyProxyReadTimeout}}", fmt.Sprintf("%d", cfg.ProxyReadTimeout),
|
|
|
|
|
"{{OpenRestySendTimeout}}", strconv.Itoa(cfg.SendTimeout),
|
|
|
|
|
"{{OpenRestyProxyConnectTimeout}}", strconv.Itoa(cfg.ProxyConnectTimeout),
|
|
|
|
|
"{{OpenRestyProxySendTimeout}}", strconv.Itoa(cfg.ProxySendTimeout),
|
|
|
|
|
"{{OpenRestyProxyReadTimeout}}", strconv.Itoa(cfg.ProxyReadTimeout),
|
|
|
|
|
"{{OpenRestyProxyRequestBuffering}}", onOff(cfg.ProxyRequestBuffering),
|
|
|
|
|
"{{OpenRestyProxyBuffering}}", onOff(cfg.ProxyBufferingEnabled),
|
|
|
|
|
"{{OpenRestyProxyBuffers}}", cfg.ProxyBuffers,
|
|
|
|
|
"{{OpenRestyProxyBufferSize}}", cfg.ProxyBufferSize,
|
|
|
|
|
"{{OpenRestyProxyBusyBuffersSize}}", cfg.ProxyBusyBuffersSize,
|
|
|
|
|
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
|
|
|
|
|
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength),
|
|
|
|
|
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel),
|
|
|
|
|
"{{OpenRestyGzipMinLength}}", strconv.Itoa(cfg.GzipMinLength),
|
|
|
|
|
"{{OpenRestyGzipCompLevel}}", strconv.Itoa(cfg.GzipCompLevel),
|
|
|
|
|
"{{OpenRestyResolverDirective}}", renderTemplateDirective(cfg.Resolvers != "", fmt.Sprintf("resolver %s;", cfg.Resolvers)),
|
|
|
|
|
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg, limitReqRates),
|
|
|
|
|
"{{OpenRestyRouteConfigInclude}}", RouteConfigPlaceholder,
|
|
|
|
@@ -414,7 +418,7 @@ func pagesFallbackPath(deployment *PagesDeployment) string {
|
|
|
|
|
if value == "/" || strings.HasSuffix(value, "/") || strings.Contains(value, "\\") || strings.ContainsAny(value, "\"';") || strings.ContainsAny(value, " \t\r\n") {
|
|
|
|
|
return indexHTML
|
|
|
|
|
}
|
|
|
|
|
for _, segment := range strings.Split(value, "/") {
|
|
|
|
|
for segment := range strings.SplitSeq(value, "/") {
|
|
|
|
|
if segment == "." || segment == ".." {
|
|
|
|
|
return indexHTML
|
|
|
|
|
}
|
|
|
|
@@ -765,7 +769,7 @@ func renderDefaultServerBlock(statusCode int, http3Enabled bool) string {
|
|
|
|
|
" }",
|
|
|
|
|
"",
|
|
|
|
|
" server {",
|
|
|
|
|
fmt.Sprintf(" listen 443 ssl default_server;%s", h3Default),
|
|
|
|
|
" listen 443 ssl default_server;" + h3Default,
|
|
|
|
|
" server_name _;",
|
|
|
|
|
"",
|
|
|
|
|
" ssl_reject_handshake on;",
|
|
|
|
@@ -826,7 +830,7 @@ func validateCertificateCoverage(certPEM string, domains []string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig) {
|
|
|
|
|
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) bool {
|
|
|
|
|
enabledGroups := make(map[uint]WAFRuleGroup, len(snapshot.RuleGroups))
|
|
|
|
|
globalGroupIDs := make([]uint, 0)
|
|
|
|
|
for _, group := range snapshot.RuleGroups {
|
|
|
|
@@ -856,10 +860,10 @@ func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig)
|
|
|
|
|
for _, groupID := range activeGroupIDs {
|
|
|
|
|
group := enabledGroups[groupID]
|
|
|
|
|
if graphContainsNodeType(group.Graph, "pow") {
|
|
|
|
|
return true, nil
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false, nil
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func graphContainsNodeType(graph WAFRuleGraph, nodeType string) bool {
|
|
|
|
@@ -946,7 +950,7 @@ func buildPathPrefixMatchPattern(rules []string) string {
|
|
|
|
|
parts = append(parts, "/")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
parts = append(parts, fmt.Sprintf("%s(?:/|$)", regexp.QuoteMeta(trimmed)))
|
|
|
|
|
parts = append(parts, regexp.QuoteMeta(trimmed)+"(?:/|$)")
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("^(?:%s)", strings.Join(parts, "|"))
|
|
|
|
|
}
|
|
|
|
|