refactor(nginx): 抽取共享 Nginx 模板包并发布 v1.5.1

This commit is contained in:
2026-09-28 15:37:14 +08:00
parent faf43293e5
commit 1ada58af02
17 changed files with 437 additions and 130 deletions
+10 -6
View File
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/fonu/fonu/internal/config"
"github.com/fonu/fonu/internal/nginxtmpl"
"github.com/fonu/fonu/internal/proxy"
)
@@ -171,9 +172,13 @@ func appendSSLServerBlock(b *strings.Builder, cfg config.Config, port int, serve
writeListenDirectives(&block, port, true, rule.ListenIPv4, rule.ListenIPv6)
block.WriteString(fmt.Sprintf(" server_name %s;\n\n", serverNames))
writeCommentLine(&block, 4, "SSL 证书")
block.WriteString(fmt.Sprintf(" ssl_certificate %s;\n", absNginxPath(cert.CertPath)))
block.WriteString(fmt.Sprintf(" ssl_certificate_key %s;\n", absNginxPath(cert.KeyPath)))
writeTLSProtocols(&block, rule.Security)
writeTLSProtocolComment(&block, rule.Security)
block.WriteString(nginxtmpl.SSLDirectives(nginxtmpl.TLSConfig{
CertPath: absNginxPath(cert.CertPath),
KeyPath: absNginxPath(cert.KeyPath),
TLS13Only: rule.Security.TLSMin13Only,
}))
block.WriteString("\n")
writeServerSecurityHeaders(&block, rule.Security, true)
writeErrorPages(&block, cfg)
writeLocationWithSecurity(&block, cfg, rule, opts)
@@ -217,11 +222,10 @@ func writeProxyLocationBody(b *strings.Builder, upstream string, sec proxy.Secur
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Real-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
%s
proxy_buffering off;
proxy_read_timeout 3600s;
`, sslBlock, upstream, hostHeader))
`, sslBlock, upstream, hostHeader, nginxtmpl.UpgradeHeaders(" ", "$connection_upgrade")))
}
func writeListenDirectives(b *strings.Builder, port int, ssl bool, ipv4 bool, ipv6 bool) {
+37 -41
View File
@@ -6,18 +6,19 @@ import (
"strings"
"github.com/fonu/fonu/internal/config"
"github.com/fonu/fonu/internal/nginxtmpl"
"github.com/fonu/fonu/internal/proxy"
)
type GenerateOptions struct {
TrustedProxy TrustedProxyConfig
GlobalIPBlacklist []string
GlobalIPWhitelist []string
ChinaCIDRAvailable bool
ChinaCIDRPathOverride string
GlobalCustomOverride string
GlobalCustomPathOverride string
RuleCustomOverrides map[int64]string
TrustedProxy TrustedProxyConfig
GlobalIPBlacklist []string
GlobalIPWhitelist []string
ChinaCIDRAvailable bool
ChinaCIDRPathOverride string
GlobalCustomOverride string
GlobalCustomPathOverride string
RuleCustomOverrides map[int64]string
}
func writeGlobalAllowList(b *strings.Builder, cidrs []string) {
@@ -52,11 +53,11 @@ func writeLimitZones(b *strings.Builder, rules []proxy.Rule) {
writeLimitZoneComments(b, rule)
}
if rule.Security.RateLimitEnabled() {
rl := rule.Security.RateLimit
b.WriteString(fmt.Sprintf(" limit_req_zone $binary_remote_addr zone=fonu_rule_%d_req:10m rate=%dr/s;\n", rule.ID, rl.Rate))
zone := fmt.Sprintf("fonu_rule_%d_req", rule.ID)
writeIndentedZone(b, nginxtmpl.RateLimitZone(zone, rule.Security.RateLimit.Rate))
}
if rule.Security.ConnLimitEnabled() {
b.WriteString(fmt.Sprintf(" limit_conn_zone $binary_remote_addr zone=fonu_rule_%d_conn:10m;\n", rule.ID))
writeIndentedZone(b, nginxtmpl.ConnLimitZone(fmt.Sprintf("fonu_rule_%d_conn", rule.ID)))
}
}
if hasLimitZones(rules) {
@@ -64,6 +65,13 @@ func writeLimitZones(b *strings.Builder, rules []proxy.Rule) {
}
}
func writeIndentedZone(b *strings.Builder, line string) {
if strings.TrimSpace(line) == "" {
return
}
b.WriteString(" " + line)
}
func hasLimitZones(rules []proxy.Rule) bool {
for _, rule := range rules {
if rule.Enabled && (rule.Security.RateLimitEnabled() || rule.Security.ConnLimitEnabled()) {
@@ -135,18 +143,18 @@ func writeChinaOnlyBypassGeo(b *strings.Builder, rules []proxy.Rule, opts Genera
}
}
func writeChinaOnlyCheck(b *strings.Builder, rule proxy.Rule, opts GenerateOptions) {
func chinaOnlyCheckDirectives(rule proxy.Rule, opts GenerateOptions) string {
sec := rule.Security.Normalize()
if !sec.ChinaOnly {
return
return ""
}
if !opts.ChinaCIDRAvailable {
b.WriteString(` if ($fonu_is_private = 0) {
return ` if ($fonu_is_private = 0) {
return 403;
}
`)
return
`
}
var b strings.Builder
b.WriteString(` set $fonu_china_block 0;
if ($fonu_is_china = 0) {
set $fonu_china_block 1;
@@ -163,6 +171,7 @@ func writeChinaOnlyCheck(b *strings.Builder, rule proxy.Rule, opts GenerateOptio
return 403;
}
`)
return b.String()
}
func writeServerSecurityHeaders(b *strings.Builder, sec proxy.SecurityConfig, https bool) {
@@ -170,20 +179,8 @@ func writeServerSecurityHeaders(b *strings.Builder, sec proxy.SecurityConfig, ht
return
}
writeSecurityHeadersComment(b)
b.WriteString(` add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
`)
}
func writeTLSProtocols(b *strings.Builder, sec proxy.SecurityConfig) {
writeTLSProtocolComment(b, sec)
if sec.TLSMin13Only {
b.WriteString(" ssl_protocols TLSv1.3;\n\n")
} else {
b.WriteString(" ssl_protocols TLSv1.2 TLSv1.3;\n\n")
}
b.WriteString(nginxtmpl.ServerSecurityHeaders(nginxtmpl.SecurityHeaders{Enabled: true, HTTPS: true}))
b.WriteString("\n")
}
func writeErrorPages(b *strings.Builder, cfg config.Config) {
@@ -216,25 +213,24 @@ func writeLocationSecurity(b *strings.Builder, cfg config.Config, rule proxy.Rul
sec := rule.Security.Normalize()
writeLocationSecurityComments(b, rule, opts)
for _, cidr := range sec.IPBlacklist {
b.WriteString(fmt.Sprintf(" deny %s;\n", cidr))
acl := nginxtmpl.AccessControl{
Deny: sec.IPBlacklist,
ChinaDeny: chinaOnlyCheckDirectives(rule, opts),
}
writeChinaOnlyCheck(b, rule, opts)
if sec.WhitelistEnabled() {
for _, cidr := range sec.IPWhitelist {
b.WriteString(fmt.Sprintf(" allow %s;\n", cidr))
}
b.WriteString(" deny all;\n")
acl.Allow = sec.IPWhitelist
}
b.WriteString(nginxtmpl.AccessDirectives(acl, " "))
limits := nginxtmpl.LimitConfig{Zone: fmt.Sprintf("fonu_rule_%d", rule.ID)}
if sec.ConnLimitEnabled() {
b.WriteString(fmt.Sprintf(" limit_conn fonu_rule_%d_conn %d;\n", rule.ID, sec.ConnLimit.Max))
limits.Conn = sec.ConnLimit.Max
}
if sec.RateLimitEnabled() {
b.WriteString(fmt.Sprintf(" limit_req zone=fonu_rule_%d_req burst=%d nodelay;\n", rule.ID, sec.RateLimit.Burst))
limits.Rate = sec.RateLimit.Rate
limits.Burst = sec.RateLimit.Burst
}
b.WriteString(nginxtmpl.LimitDirectives(limits, " "))
if sec.BasicAuthEnabled() {
path := absNginxPath(HtpasswdPath(cfg, rule.ID))