From 2cd704785c8f2b67b456a14249bee75b8d522162 Mon Sep 17 00:00:00 2001 From: smail Date: Wed, 2 Sep 2026 02:02:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=8D=95=E4=B8=AASSL?= =?UTF-8?q?=E6=B3=9B=E5=9F=9F=E5=90=8D=E8=AF=81=E4=B9=A6=E7=94=B3=E8=AF=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/services/setup/ssl/challenge.go | 6 +- server/services/setup/ssl/ssl.go | 31 ++++++---- server/services/setup/ssl/wildcard_test.go | 72 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 server/services/setup/ssl/wildcard_test.go diff --git a/server/services/setup/ssl/challenge.go b/server/services/setup/ssl/challenge.go index 97fdaa58..3f44cb47 100644 --- a/server/services/setup/ssl/challenge.go +++ b/server/services/setup/ssl/challenge.go @@ -4,6 +4,7 @@ import ( "git.pcux.cn/xuans/Smail/utils/context" "github.com/go-acme/lego/v4/challenge/dns01" log "github.com/sirupsen/logrus" + "strings" "sync" "time" ) @@ -66,7 +67,10 @@ func GetDnsChallengeInstance() *DNSChallenge { } func (h *DNSChallenge) Present(domain, token, keyAuth string) error { - info := dns01.GetChallengeInfo(domain, keyAuth) + challengeDomain := strings.TrimSpace(domain) + challengeDomain = strings.TrimSuffix(challengeDomain, ".") + challengeDomain = strings.TrimPrefix(challengeDomain, "*.") + info := dns01.GetChallengeInfo(challengeDomain, keyAuth) log.Infof("SSL DNS challenge generated for domain=%s", domain) h.mu.Lock() h.authInfo[token] = &authInfo{ diff --git a/server/services/setup/ssl/ssl.go b/server/services/setup/ssl/ssl.go index 44343def..3b03fdab 100644 --- a/server/services/setup/ssl/ssl.go +++ b/server/services/setup/ssl/ssl.go @@ -290,7 +290,7 @@ func writeCertificateFiles(cfg *config.Config, privateKey, certificate, issuer [ if cfg == nil { return errors.New("SSL 配置不能为空") } - reference, err := config.ImportManagedSSL(config.ManagedSSLModeDefault, privateKey, certificate, issuer, sslDomains(cfg)) + reference, err := config.ImportManagedSSL(config.ManagedSSLModeDefault, privateKey, certificate, issuer, wildcardCertificateDomains(cfg)) if err != nil { return err } @@ -328,6 +328,21 @@ func sslDomains(cfg *config.Config) []string { } return domains } +func wildcardCertificateDomains(cfg *config.Config) []string { + if cfg == nil { + return nil + } + + domain := strings.TrimSpace(cfg.Domain) + domain = strings.TrimSuffix(domain, ".") + domain = strings.TrimPrefix(domain, "*.") + if domain == "" { + return nil + } + + return []string{"*." + domain} +} + func renewCertificate(privateKey *ecdsa.PrivateKey, cfg *config.Config) error { myUser := MyUser{ @@ -372,12 +387,7 @@ func renewCertificate(privateKey *ecdsa.PrivateKey, cfg *config.Config) error { myUser.Registration = reg - domains := []string{cfg.WebDomain} - for _, domain := range cfg.Domains { - domains = append(domains, "smtp."+domain) - domains = append(domains, "pop."+domain) - domains = append(domains, "imap."+domain) - } + domains := wildcardCertificateDomains(cfg) request := certificate.ObtainRequest{ Domains: domains, @@ -441,12 +451,7 @@ func generateCertificate(privateKey *ecdsa.PrivateKey, cfg *config.Config, newAc myUser.Registration = reg - domains := []string{cfg.WebDomain} - for _, domain := range cfg.Domains { - domains = append(domains, "smtp."+domain) - domains = append(domains, "pop."+domain) - domains = append(domains, "imap."+domain) - } + domains := wildcardCertificateDomains(cfg) request := certificate.ObtainRequest{ Domains: domains, diff --git a/server/services/setup/ssl/wildcard_test.go b/server/services/setup/ssl/wildcard_test.go new file mode 100644 index 00000000..9d0e449f --- /dev/null +++ b/server/services/setup/ssl/wildcard_test.go @@ -0,0 +1,72 @@ +package ssl + +import ( + "strings" + "testing" + + "git.pcux.cn/xuans/Smail/config" +) + +func TestWildcardCertificateDomains(t *testing.T) { + tests := []struct { + name string + config *config.Config + want []string + }{ + { + name: "configured domain", + config: &config.Config{Domain: "xuans.com", WebDomain: "web.xuans.com", Domains: []string{"xuans.com", "other.example"}}, + want: []string{"*.xuans.com"}, + }, + { + name: "trimmed wildcard domain", + config: &config.Config{Domain: " *.xuans.com. "}, + want: []string{"*.xuans.com"}, + }, + { + name: "empty domain", + config: &config.Config{}, + want: nil, + }, + { + name: "nil config", + config: nil, + want: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := wildcardCertificateDomains(test.config) + if len(got) != len(test.want) { + t.Fatalf("got %d domains, want %d: %v", len(got), len(test.want), got) + } + for index := range test.want { + if got[index] != test.want[index] { + t.Fatalf("got domain %q, want %q", got[index], test.want[index]) + } + } + }) + } +} + +func TestDNSChallengeWildcardProducesSingleRecord(t *testing.T) { + challenge := &DNSChallenge{authInfo: map[string]*authInfo{}} + if err := challenge.Present("*.xuans.com", "token", "key-auth"); err != nil { + t.Fatalf("Present returned an error: %v", err) + } + + records := challenge.GetDNSSettings(nil) + if len(records) != 1 { + t.Fatalf("got %d DNS records, want 1", len(records)) + } + if records[0].Type != "TXT" { + t.Fatalf("got record type %q, want TXT", records[0].Type) + } + if strings.TrimSuffix(records[0].Host, ".") != "_acme-challenge.xuans.com" { + t.Fatalf("got record host %q, want _acme-challenge.xuans.com", records[0].Host) + } + if records[0].Value == "" { + t.Fatal("DNS record value must not be empty") + } +}