- Introduced optional keepalive settings: `keepalive_idle` and `keepalive_count` in the Server struct. - Implemented UI controls for keepalive settings in System Settings, including validation and persistence. - Added localization support for new keepalive fields in multiple languages. - Created a manual test tracking plan for verifying keepalive controls and their behavior. - Updated existing tests to cover new functionality and ensure proper validation of keepalive inputs. - Ensured safe defaults and fallback behavior for missing or invalid keepalive values.
146 lines
4.0 KiB
Go
146 lines
4.0 KiB
Go
package caddy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGenerateConfig_CustomCertsAndTLS(t *testing.T) {
|
|
hosts := []models.ProxyHost{
|
|
{
|
|
UUID: "h1",
|
|
DomainNames: "a.example.com",
|
|
ForwardHost: "127.0.0.1",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
Certificate: &models.SSLCertificate{ID: 1, UUID: "c1", Name: "CustomCert", Provider: "custom", Certificate: "cert", PrivateKey: "key"},
|
|
CertificateID: ptrUint(1),
|
|
HSTSEnabled: true,
|
|
HSTSSubdomains: true,
|
|
BlockExploits: true,
|
|
Locations: []models.Location{{Path: "/app", ForwardHost: "127.0.0.1", ForwardPort: 8081}},
|
|
},
|
|
}
|
|
cfg, err := GenerateConfig(hosts, "/data/caddy/data", "admin@example.com", "/frontend/dist", "letsencrypt", true, false, false, false, false, "", nil, nil, nil, nil, nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cfg)
|
|
// TLS should be configured
|
|
require.NotNil(t, cfg.Apps.TLS)
|
|
// Custom cert load
|
|
require.NotNil(t, cfg.Apps.TLS.Certificates)
|
|
// One route for the host (with location) plus catch-all -> at least 2 routes
|
|
server := cfg.Apps.HTTP.Servers["charon_server"]
|
|
require.GreaterOrEqual(t, len(server.Routes), 2)
|
|
// Check HSTS header exists in JSON representation
|
|
b, _ := json.Marshal(cfg)
|
|
require.Contains(t, string(b), "Strict-Transport-Security")
|
|
}
|
|
|
|
func ptrUint(v uint) *uint { return &v }
|
|
|
|
func TestGenerateConfig_EmergencyRoutesBypassSecurity(t *testing.T) {
|
|
hosts := []models.ProxyHost{
|
|
{
|
|
UUID: "h1",
|
|
DomainNames: "example.com",
|
|
ForwardHost: "127.0.0.1",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
AccessList: &models.AccessList{
|
|
Enabled: true,
|
|
Type: "whitelist",
|
|
IPRules: `[ { "cidr": "10.0.0.0/8", "description": "allow" } ]`,
|
|
},
|
|
AccessListID: ptrUint(1),
|
|
},
|
|
}
|
|
|
|
secCfg := &models.SecurityConfig{
|
|
WAFMode: "enabled",
|
|
WAFRulesSource: "owasp-crs",
|
|
RateLimitMode: "enabled",
|
|
RateLimitRequests: 10,
|
|
RateLimitWindowSec: 60,
|
|
}
|
|
|
|
rulesets := []models.SecurityRuleSet{
|
|
{Name: "owasp-crs", Content: "SecRuleEngine On"},
|
|
}
|
|
rulesetPaths := map[string]string{"owasp-crs": "/tmp/owasp-crs.conf"}
|
|
|
|
cfg, err := GenerateConfig(hosts, "/data/caddy/data", "admin@example.com", "/frontend/dist", "letsencrypt", false, false, true, true, true, "", rulesets, rulesetPaths, nil, secCfg, nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cfg)
|
|
|
|
server := cfg.Apps.HTTP.Servers["charon_server"]
|
|
require.NotNil(t, server)
|
|
|
|
var emergencyRoute *Route
|
|
for _, route := range server.Routes {
|
|
if route == nil {
|
|
continue
|
|
}
|
|
for _, match := range route.Match {
|
|
for _, path := range match.Path {
|
|
if strings.Contains(path, "/api/v1/emergency") || strings.Contains(path, "/emergency/") {
|
|
emergencyRoute = route
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, emergencyRoute, "expected emergency bypass route")
|
|
|
|
for _, handler := range emergencyRoute.Handle {
|
|
name, _ := handler["handler"].(string)
|
|
require.NotEqual(t, "rate_limit", name)
|
|
require.NotEqual(t, "waf", name)
|
|
require.NotEqual(t, "crowdsec", name)
|
|
}
|
|
}
|
|
|
|
func TestApplyOptionalServerKeepalive_OmitsWhenUnset(t *testing.T) {
|
|
cfg := &Config{
|
|
Apps: Apps{
|
|
HTTP: &HTTPApp{Servers: map[string]*Server{
|
|
"charon_server": {
|
|
Listen: []string{":80", ":443"},
|
|
Routes: []*Route{},
|
|
},
|
|
}},
|
|
},
|
|
}
|
|
|
|
applyOptionalServerKeepalive(cfg, "", 0)
|
|
|
|
server := cfg.Apps.HTTP.Servers["charon_server"]
|
|
require.Nil(t, server.KeepaliveIdle)
|
|
require.Nil(t, server.KeepaliveCount)
|
|
}
|
|
|
|
func TestApplyOptionalServerKeepalive_AppliesValidValues(t *testing.T) {
|
|
cfg := &Config{
|
|
Apps: Apps{
|
|
HTTP: &HTTPApp{Servers: map[string]*Server{
|
|
"charon_server": {
|
|
Listen: []string{":80", ":443"},
|
|
Routes: []*Route{},
|
|
},
|
|
}},
|
|
},
|
|
}
|
|
|
|
applyOptionalServerKeepalive(cfg, "45s", 7)
|
|
|
|
server := cfg.Apps.HTTP.Servers["charon_server"]
|
|
require.NotNil(t, server.KeepaliveIdle)
|
|
require.Equal(t, "45s", *server.KeepaliveIdle)
|
|
require.NotNil(t, server.KeepaliveCount)
|
|
require.Equal(t, 7, *server.KeepaliveCount)
|
|
}
|