test: add SMTP configuration tests and multi-credential DNS provider support
This commit is contained in:
@@ -3,20 +3,50 @@ package caddy
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/models"
|
||||
"github.com/Wikid82/charon/backend/pkg/dnsprovider"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
_ "github.com/Wikid82/charon/backend/pkg/dnsprovider/builtin" // Auto-register DNS providers
|
||||
)
|
||||
|
||||
type multiCredTestProvider struct{}
|
||||
|
||||
func (p *multiCredTestProvider) Type() string { return "testmulti" }
|
||||
func (p *multiCredTestProvider) Metadata() dnsprovider.ProviderMetadata {
|
||||
return dnsprovider.ProviderMetadata{Type: p.Type(), Name: "Test Multi", IsBuiltIn: true}
|
||||
}
|
||||
func (p *multiCredTestProvider) Init() error { return nil }
|
||||
func (p *multiCredTestProvider) Cleanup() error { return nil }
|
||||
func (p *multiCredTestProvider) RequiredCredentialFields() []dnsprovider.CredentialFieldSpec {
|
||||
return nil
|
||||
}
|
||||
func (p *multiCredTestProvider) OptionalCredentialFields() []dnsprovider.CredentialFieldSpec {
|
||||
return nil
|
||||
}
|
||||
func (p *multiCredTestProvider) ValidateCredentials(creds map[string]string) error { return nil }
|
||||
func (p *multiCredTestProvider) TestCredentials(creds map[string]string) error { return nil }
|
||||
func (p *multiCredTestProvider) SupportsMultiCredential() bool { return true }
|
||||
func (p *multiCredTestProvider) BuildCaddyConfig(creds map[string]string) map[string]any {
|
||||
return map[string]any{"name": p.Type(), "token": creds["token"]}
|
||||
}
|
||||
func (p *multiCredTestProvider) BuildCaddyConfigForZone(baseDomain string, creds map[string]string) map[string]any {
|
||||
return map[string]any{"name": p.Type(), "zone": baseDomain, "token": creds["token"]}
|
||||
}
|
||||
func (p *multiCredTestProvider) PropagationTimeout() time.Duration { return 2 * time.Second }
|
||||
func (p *multiCredTestProvider) PollingInterval() time.Duration { return 1 * time.Second }
|
||||
|
||||
func mustProviderID(v uint) *uint { return &v }
|
||||
|
||||
func TestGenerateConfig_DNSChallenge_LetsEncrypt_StagingCAAndPropagationTimeout(t *testing.T) {
|
||||
providerID := uint(1)
|
||||
host := models.ProxyHost{
|
||||
Enabled: true,
|
||||
DomainNames: "*.example.com,example.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -86,7 +116,7 @@ func TestGenerateConfig_DNSChallenge_ZeroSSL_IssuerShape(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.example.net",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -137,7 +167,7 @@ func TestGenerateConfig_DNSChallenge_SkipsPolicyWhenProviderConfigMissing(t *tes
|
||||
Enabled: true,
|
||||
DomainNames: "*.example.org",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -229,7 +259,7 @@ func TestGenerateConfig_MultiCredential_ZoneSpecificPolicies(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.zone1.com,zone1.com,*.zone2.com,zone2.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -284,7 +314,7 @@ func TestGenerateConfig_MultiCredential_ZeroSSL_Issuer(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.zerossl-test.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -337,7 +367,7 @@ func TestGenerateConfig_MultiCredential_BothIssuers(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.both-test.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -394,7 +424,7 @@ func TestGenerateConfig_MultiCredential_ACMEStaging(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.staging-test.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -449,7 +479,7 @@ func TestGenerateConfig_MultiCredential_NoMatchingDomains(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.actual.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -496,7 +526,7 @@ func TestGenerateConfig_MultiCredential_ProviderTypeNotFound(t *testing.T) {
|
||||
Enabled: true,
|
||||
DomainNames: "*.unknown-provider.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "nonexistent_provider"},
|
||||
DNSProviderID: func() *uint { v := providerID; return &v }(),
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
@@ -525,3 +555,338 @@ func TestGenerateConfig_MultiCredential_ProviderTypeNotFound(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
}
|
||||
|
||||
func TestGenerateConfig_MultiCredential_SupportsMultiCredential_UsesZoneConfigAndStagingBothIssuers(t *testing.T) {
|
||||
if err := dnsprovider.Global().Register(&multiCredTestProvider{}); err == nil {
|
||||
t.Cleanup(func() { dnsprovider.Global().Unregister("testmulti") })
|
||||
}
|
||||
|
||||
providerID := uint(16)
|
||||
host := models.ProxyHost{
|
||||
Enabled: true,
|
||||
DomainNames: "*.z1.com,z1.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "testmulti"},
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"both",
|
||||
true,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
[]DNSProviderConfig{{
|
||||
ID: providerID,
|
||||
ProviderType: "testmulti",
|
||||
UseMultiCredentials: true,
|
||||
ZoneCredentials: map[string]map[string]string{
|
||||
"z1.com": {"token": "tok-z1"},
|
||||
},
|
||||
}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
require.NotNil(t, conf.Apps.TLS)
|
||||
require.NotNil(t, conf.Apps.TLS.Automation)
|
||||
require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)
|
||||
|
||||
var foundCA string
|
||||
var foundProvider map[string]any
|
||||
for _, p := range conf.Apps.TLS.Automation.Policies {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, it := range p.IssuersRaw {
|
||||
issuer, ok := it.(map[string]any)
|
||||
if !ok || issuer["module"] != "acme" {
|
||||
continue
|
||||
}
|
||||
if ca, ok := issuer["ca"].(string); ok {
|
||||
foundCA = ca
|
||||
}
|
||||
ch, _ := issuer["challenges"].(map[string]any)
|
||||
dnsCh, _ := ch["dns"].(map[string]any)
|
||||
prov, _ := dnsCh["provider"].(map[string]any)
|
||||
foundProvider = prov
|
||||
break
|
||||
}
|
||||
if foundProvider != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", foundCA)
|
||||
require.NotNil(t, foundProvider)
|
||||
require.Equal(t, "z1.com", foundProvider["zone"], "expected zone-specific provider config")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DNSChallenge_SingleCredential_BothIssuers_ACMEStaging(t *testing.T) {
|
||||
providerID := uint(17)
|
||||
host := models.ProxyHost{
|
||||
Enabled: true,
|
||||
DomainNames: "*.both-staging.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"both",
|
||||
true,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
[]DNSProviderConfig{{
|
||||
ID: providerID,
|
||||
ProviderType: "cloudflare",
|
||||
PropagationTimeout: 1,
|
||||
Credentials: map[string]string{"api_token": "tok"},
|
||||
}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
|
||||
var foundIssuer map[string]any
|
||||
for _, p := range conf.Apps.TLS.Automation.Policies {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, s := range p.Subjects {
|
||||
if s != "*.both-staging.com" {
|
||||
continue
|
||||
}
|
||||
for _, it := range p.IssuersRaw {
|
||||
if m, ok := it.(map[string]any); ok && m["module"] == "acme" {
|
||||
foundIssuer = m
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundIssuer != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
require.NotNil(t, foundIssuer)
|
||||
require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", foundIssuer["ca"])
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DNSChallenge_SingleCredential_ProviderTypeNotFound_SkipsPolicy(t *testing.T) {
|
||||
providerID := uint(18)
|
||||
host := models.ProxyHost{
|
||||
Enabled: true,
|
||||
DomainNames: "*.missing-registry.com",
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "not_registered"},
|
||||
DNSProviderID: mustProviderID(providerID),
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"letsencrypt",
|
||||
false,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
[]DNSProviderConfig{{
|
||||
ID: providerID,
|
||||
ProviderType: "not_registered",
|
||||
PropagationTimeout: 1,
|
||||
Credentials: map[string]string{"token": "x"},
|
||||
}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DefaultPolicy_LetsEncrypt_StagingCA(t *testing.T) {
|
||||
host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.1"}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"letsencrypt",
|
||||
true,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
require.NotNil(t, conf.Apps.TLS)
|
||||
require.NotNil(t, conf.Apps.TLS.Automation)
|
||||
require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)
|
||||
|
||||
found := false
|
||||
for _, p := range conf.Apps.TLS.Automation.Policies {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, it := range p.IssuersRaw {
|
||||
if m, ok := it.(map[string]any); ok && m["module"] == "acme" {
|
||||
require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", m["ca"])
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found)
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DefaultPolicy_ZeroSSL_Issuer(t *testing.T) {
|
||||
host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.2"}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"zerossl",
|
||||
false,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
require.NotNil(t, conf.Apps.TLS)
|
||||
require.NotNil(t, conf.Apps.TLS.Automation)
|
||||
|
||||
found := false
|
||||
for _, p := range conf.Apps.TLS.Automation.Policies {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, it := range p.IssuersRaw {
|
||||
if m, ok := it.(map[string]any); ok && m["module"] == "zerossl" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found)
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DefaultPolicy_BothIssuers_StagingCA(t *testing.T) {
|
||||
host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.3"}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"acme@example.com",
|
||||
"",
|
||||
"",
|
||||
true,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
|
||||
caFound := false
|
||||
zeroFound := false
|
||||
for _, p := range conf.Apps.TLS.Automation.Policies {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
for _, it := range p.IssuersRaw {
|
||||
m, ok := it.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch m["module"] {
|
||||
case "acme":
|
||||
if m["ca"] == "https://acme-staging-v02.api.letsencrypt.org/directory" {
|
||||
caFound = true
|
||||
}
|
||||
case "zerossl":
|
||||
zeroFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, caFound)
|
||||
require.True(t, zeroFound)
|
||||
}
|
||||
|
||||
func TestGenerateConfig_IPSubjects_InitializesTLSAppAndAutomation(t *testing.T) {
|
||||
providerID := uint(19)
|
||||
host := models.ProxyHost{
|
||||
Enabled: true,
|
||||
UUID: "ip-host",
|
||||
DomainNames: "1.2.3.4",
|
||||
ForwardHost: "app",
|
||||
ForwardPort: 8080,
|
||||
DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
|
||||
}
|
||||
|
||||
conf, err := GenerateConfig(
|
||||
[]models.ProxyHost{host},
|
||||
t.TempDir(),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
false, false, false, false,
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&models.SecurityConfig{},
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
require.NotNil(t, conf.Apps.TLS)
|
||||
require.NotNil(t, conf.Apps.TLS.Automation)
|
||||
require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)
|
||||
}
|
||||
|
||||
func TestGetAccessLogPath_DockerEnv_UsesCrowdSecPath(t *testing.T) {
|
||||
const dockerMarker = "/.dockerenv"
|
||||
if err := os.WriteFile(dockerMarker, []byte("test"), 0o600); err != nil {
|
||||
t.Skipf("cannot create %s: %v", dockerMarker, err)
|
||||
}
|
||||
t.Cleanup(func() { _ = os.Remove(dockerMarker) })
|
||||
|
||||
path := getAccessLogPath(t.TempDir(), false)
|
||||
require.Equal(t, "/var/log/caddy/access.log", path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user