Skip to content

Commit 6f9a132

Browse files
feat: support custom DNS server for manual DNS mode when applying certificates (#11512)
1 parent eb4eb31 commit 6f9a132

File tree

1 file changed

+89
-5
lines changed

1 file changed

+89
-5
lines changed

agent/utils/ssl/manual_client.go

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"fmt"
1313
"github.com/1Panel-dev/1Panel/agent/app/model"
1414
"github.com/go-acme/lego/v4/certificate"
15+
"github.com/miekg/dns"
1516
"golang.org/x/crypto/acme"
1617
"log"
1718
"net"
@@ -123,7 +124,7 @@ func queryDNSRecords(domain string) (map[string]string, error) {
123124
return records, nil
124125
}
125126

126-
func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string) error {
127+
func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string, nameservers []string) error {
127128
authz, err := c.client.GetAuthorization(ctx, authzURL)
128129
if err != nil {
129130
return fmt.Errorf("failed to get authorization: %v", err)
@@ -158,9 +159,22 @@ func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string)
158159

159160
for {
160161
c.logger.Printf("[INFO] [%s] acme: Checking DNS record propagation.", domain)
161-
currentRecords, err := queryDNSRecords(domain)
162-
if err != nil {
163-
return fmt.Errorf("failed to query DNS records: %v", err)
162+
var currentRecords map[string]string
163+
var queryErr error
164+
if len(nameservers) == 0 {
165+
currentRecords, queryErr = queryDNSRecords(domain)
166+
} else {
167+
var errs []string
168+
for _, nameserver := range nameservers {
169+
currentRecords, queryErr = queryDNSRecordsWithResolver(ctx, c.logger, domain, nameserver)
170+
errs = append(errs, fmt.Sprintf("%s: %v", nameserver, queryErr))
171+
}
172+
if queryErr != nil && len(errs) > 0 {
173+
queryErr = fmt.Errorf("all nameservers failed: %s", strings.Join(errs, "; "))
174+
}
175+
}
176+
if currentRecords == nil && queryErr != nil {
177+
return fmt.Errorf("failed to query DNS records: %v", queryErr)
164178
}
165179
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
166180
providedRecord, exists := currentRecords[recordName]
@@ -262,7 +276,7 @@ func (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model
262276
defer delete(Orders, websiteSSL.ID)
263277

264278
for _, authzURL := range order.AuthzURLs {
265-
if err := c.handleAuthorization(ctx, authzURL); err != nil {
279+
if err := c.handleAuthorization(ctx, authzURL, getNameservers(*websiteSSL)); err != nil {
266280
return res, err
267281
}
268282
}
@@ -306,3 +320,73 @@ func (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model
306320
}
307321
return resource, nil
308322
}
323+
324+
func getNameservers(websiteSSL model.WebsiteSSL) []string {
325+
var nameservers []string
326+
if websiteSSL.Nameserver1 != "" {
327+
nameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver1))
328+
}
329+
if websiteSSL.Nameserver2 != "" {
330+
nameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver2))
331+
}
332+
return nameservers
333+
}
334+
335+
func handleNameserver(nameserver string) string {
336+
if strings.Contains(nameserver, ":") {
337+
return nameserver
338+
}
339+
return fmt.Sprintf("%s:53", nameserver)
340+
}
341+
342+
func queryDNSRecordsWithResolver(ctx context.Context, logger *log.Logger, domain string, dnsServer string) (map[string]string, error) {
343+
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
344+
c := new(dns.Client)
345+
c.Timeout = 10 * time.Second
346+
c.Net = "udp"
347+
348+
m := new(dns.Msg)
349+
m.SetQuestion(dns.Fqdn(recordName), dns.TypeTXT)
350+
m.RecursionDesired = true
351+
352+
r, _, err := c.ExchangeContext(ctx, m, dnsServer)
353+
if isNetworkError(err) {
354+
logger.Printf("[WARN] Network error occurred while querying DNS: %v", err)
355+
return nil, nil
356+
}
357+
if err != nil {
358+
return nil, fmt.Errorf("DNS query failed: %w", err)
359+
}
360+
361+
if r.Rcode == dns.RcodeNameError {
362+
logger.Printf("[INFO] DNS record does not exist yet (NXDOMAIN)")
363+
return nil, nil
364+
}
365+
366+
if r.Rcode != dns.RcodeSuccess {
367+
return nil, fmt.Errorf("DNS query failed with code: %s", dns.RcodeToString[r.Rcode])
368+
}
369+
370+
records := make(map[string]string)
371+
372+
for _, answer := range r.Answer {
373+
if txt, ok := answer.(*dns.TXT); ok {
374+
if len(txt.Txt) > 0 {
375+
records[recordName] = txt.Txt[0]
376+
break
377+
}
378+
}
379+
}
380+
381+
return records, nil
382+
}
383+
384+
func isNetworkError(err error) bool {
385+
if err == nil {
386+
return false
387+
}
388+
return strings.Contains(err.Error(), "i/o timeout") ||
389+
strings.Contains(err.Error(), "connection refused") ||
390+
strings.Contains(err.Error(), "network is unreachable") ||
391+
strings.Contains(err.Error(), "no route to host")
392+
}

0 commit comments

Comments
 (0)