Merge pull request #406 from Sickjuicy/main

Domain Name Server Option
This commit is contained in:
Toby Chui 2024-12-06 19:29:29 +08:00 committed by GitHub
commit 4d163fe80f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 84 additions and 16 deletions

View File

@ -21,6 +21,7 @@ import (
"github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01" "github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration" "github.com/go-acme/lego/v4/registration"
@ -29,12 +30,20 @@ import (
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
var defaultNameservers = []string{
"8.8.8.8:53", // Google DNS
"8.8.4.4:53", // Google DNS
"1.1.1.1:53", // Cloudflare DNS
"1.0.0.1:53", // Cloudflare DNS
}
type CertificateInfoJSON struct { type CertificateInfoJSON struct {
AcmeName string `json:"acme_name"` //ACME provider name AcmeName string `json:"acme_name"` //ACME provider name
AcmeUrl string `json:"acme_url"` //Custom ACME URL (if any) AcmeUrl string `json:"acme_url"` //Custom ACME URL (if any)
SkipTLS bool `json:"skip_tls"` //Skip TLS verification of upstream SkipTLS bool `json:"skip_tls"` //Skip TLS verification of upstream
UseDNS bool `json:"dns"` //Use DNS challenge UseDNS bool `json:"dns"` //Use DNS challenge
PropTimeout int `json:"prop_time"` //Propagation timeout PropTimeout int `json:"prop_time"` //Propagation timeout
DNSServers []string `json:"dnsServers"` // DNS servers
} }
// ACMEUser represents a user in the ACME system. // ACMEUser represents a user in the ACME system.
@ -94,7 +103,7 @@ func (a *ACMEHandler) Close() error {
} }
// ObtainCert obtains a certificate for the specified domains. // ObtainCert obtains a certificate for the specified domains.
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool, propagationTimeout int) (bool, error) { func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool, propagationTimeout int, dnsServers string) (bool, error) {
a.Logf("Obtaining certificate for: "+strings.Join(domains, ", "), nil) a.Logf("Obtaining certificate for: "+strings.Join(domains, ", "), nil)
// generate private key // generate private key
@ -164,15 +173,31 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
return false, err return false, err
} }
// Load certificate info from JSON file
certInfo, err := LoadCertInfoJSON(fmt.Sprintf("./conf/certs/%s.json", certificateName))
if err == nil {
useDNS = certInfo.UseDNS
if dnsServers == "" && certInfo.DNSServers != nil && len(certInfo.DNSServers) > 0 {
dnsServers = strings.Join(certInfo.DNSServers, ",")
}
propagationTimeout = certInfo.PropTimeout
}
// Clean DNS servers
dnsNameservers := strings.Split(dnsServers, ",")
for i := range dnsNameservers {
dnsNameservers[i] = strings.TrimSpace(dnsNameservers[i])
}
// setup how to receive challenge // setup how to receive challenge
if useDNS { if useDNS {
if !a.Database.TableExists("acme") { if !a.Database.TableExists("acme") {
a.Database.NewTable("acme") a.Database.NewTable("acme")
return false, errors.New("DNS Provider and DNS Credenital configuration required for ACME Provider (Error -1)") return false, errors.New("DNS Provider and DNS Credential configuration required for ACME Provider (Error -1)")
} }
if !a.Database.KeyExists("acme", certificateName+"_dns_provider") || !a.Database.KeyExists("acme", certificateName+"_dns_credentials") { if !a.Database.KeyExists("acme", certificateName+"_dns_provider") || !a.Database.KeyExists("acme", certificateName+"_dns_credentials") {
return false, errors.New("DNS Provider and DNS Credenital configuration required for ACME Provider (Error -2)") return false, errors.New("DNS Provider and DNS Credential configuration required for ACME Provider (Error -2)")
} }
var dnsCredentials string var dnsCredentials string
@ -195,7 +220,13 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
return false, err return false, err
} }
err = client.Challenge.SetDNS01Provider(provider) if len(dnsNameservers) > 0 && dnsNameservers[0] != "" {
a.Logf("Using DNS servers: "+strings.Join(dnsNameservers, ", "), nil)
err = client.Challenge.SetDNS01Provider(provider, dns01.AddRecursiveNameservers(dnsNameservers))
} else {
// Use default DNS-01 nameservers if dnsServers is empty
err = client.Challenge.SetDNS01Provider(provider, dns01.AddRecursiveNameservers(defaultNameservers))
}
if err != nil { if err != nil {
a.Logf("Failed to resolve DNS01 Provider", err) a.Logf("Failed to resolve DNS01 Provider", err)
return false, err return false, err
@ -292,12 +323,13 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
} }
// Save certificate's ACME info for renew usage // Save certificate's ACME info for renew usage
certInfo := &CertificateInfoJSON{ certInfo = &CertificateInfoJSON{
AcmeName: caName, AcmeName: caName,
AcmeUrl: caUrl, AcmeUrl: caUrl,
SkipTLS: skipTLS, SkipTLS: skipTLS,
UseDNS: useDNS, UseDNS: useDNS,
PropTimeout: propagationTimeout, PropTimeout: propagationTimeout,
DNSServers: dnsNameservers,
} }
certInfoBytes, err := json.Marshal(certInfo) certInfoBytes, err := json.Marshal(certInfo)
@ -484,7 +516,21 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
for _, domain := range domains { for _, domain := range domains {
cleanedDomains = append(cleanedDomains, strings.TrimSpace(domain)) cleanedDomains = append(cleanedDomains, strings.TrimSpace(domain))
} }
result, err := a.ObtainCert(cleanedDomains, filename, email, ca, caUrl, skipTLS, dns, propagationTimeout)
// Extract DNS servers from the request
var dnsServers []string
dnsServersPara, err := utils.PostPara(r, "dnsServers")
if err == nil && dnsServersPara != "" {
dnsServers = strings.Split(dnsServersPara, ",")
for i := range dnsServers {
dnsServers[i] = strings.TrimSpace(dnsServers[i])
}
}
// Convert DNS servers slice to a single string
dnsServersString := strings.Join(dnsServers, ",")
result, err := a.ObtainCert(cleanedDomains, filename, email, ca, caUrl, skipTLS, dns, propagationTimeout, dnsServersString)
if err != nil { if err != nil {
utils.SendErrorResponse(w, jsonEscape(err.Error())) utils.SendErrorResponse(w, jsonEscape(err.Error()))
return return
@ -527,5 +573,10 @@ func LoadCertInfoJSON(filename string) (*CertificateInfoJSON, error) {
return nil, err return nil, err
} }
// Clean DNS servers
for i := range certInfo.DNSServers {
certInfo.DNSServers[i] = strings.TrimSpace(certInfo.DNSServers[i])
}
return certInfo, nil return certInfo, nil
} }

View File

@ -26,6 +26,7 @@ type AutoRenewConfig struct {
Email string //Email for acme Email string //Email for acme
RenewAll bool //Renew all or selective renew with the slice below RenewAll bool //Renew all or selective renew with the slice below
FilesToRenew []string //If RenewAll is false, renew these certificate files FilesToRenew []string //If RenewAll is false, renew these certificate files
DNSServers string // DNS servers
} }
type AutoRenewer struct { type AutoRenewer struct {
@ -390,7 +391,13 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
certInfo.PropTimeout = 300 certInfo.PropTimeout = 300
} }
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS, certInfo.UseDNS, certInfo.PropTimeout) // Extract DNS servers from the certificate info if available
var dnsServers string
if len(certInfo.DNSServers) > 0 {
dnsServers = strings.Join(certInfo.DNSServers, ",")
}
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS, certInfo.UseDNS, certInfo.PropTimeout, dnsServers)
if err != nil { if err != nil {
a.Logf("Renew "+fileName+"("+strings.Join(expiredCert.Domains, ",")+") failed", err) a.Logf("Renew "+fileName+"("+strings.Join(expiredCert.Domains, ",")+") failed", err)
} else { } else {
@ -459,12 +466,18 @@ func (a *AutoRenewer) HandleSetDNS(w http.ResponseWriter, r *http.Request) {
return return
} }
dnsServers, err := utils.PostPara(r, "dnsServers")
if err != nil {
dnsServers = ""
}
if !a.AcmeHandler.Database.TableExists("acme") { if !a.AcmeHandler.Database.TableExists("acme") {
a.AcmeHandler.Database.NewTable("acme") a.AcmeHandler.Database.NewTable("acme")
} }
a.AcmeHandler.Database.Write("acme", filename+"_dns_provider", dnsProvider) a.AcmeHandler.Database.Write("acme", filename+"_dns_provider", dnsProvider)
a.AcmeHandler.Database.Write("acme", filename+"_dns_credentials", dnsCredentials) a.AcmeHandler.Database.Write("acme", filename+"_dns_credentials", dnsCredentials)
a.AcmeHandler.Database.Write("acme", filename+"_dns_servers", dnsServers)
utils.SendOK(w) utils.SendOK(w)

View File

@ -161,6 +161,11 @@
</div> </div>
--> -->
</div> </div>
<div class="field dnsChallengeOnly" style="display:none;">
<label>Domain Name Server (optional)</label>
<input id="dnsInput" type="text" placeholder="ns.example.com">
<small>If you have more than one DNS server, enter them separated by commas (e.g. ns1.example.com,ns2.example.com)</small>
</div>
<div class="field" id="caInput" style="display:none;"> <div class="field" id="caInput" style="display:none;">
<label>ACME Server URL</label> <label>ACME Server URL</label>
<input id="caURL" type="text" placeholder="https://example.com/acme/dictionary"> <input id="caURL" type="text" placeholder="https://example.com/acme/dictionary">
@ -725,8 +730,7 @@
if (callback != undefined){callback(false);} if (callback != undefined){callback(false);}
return; return;
} }
var ca = $("#ca").dropdown("get value"); var ca = $("#ca").dropdown("get value");
var caURL = ""; var caURL = "";
if (ca == "Custom ACME Server") { if (ca == "Custom ACME Server") {
@ -734,9 +738,9 @@
caURL = $("#caURL").val(); caURL = $("#caURL").val();
} }
var dns = $("#useDnsChallenge")[0].checked; var dns = $("#useDnsChallenge")[0].checked;
var skipTLSValue = $("#skipTLSCheckbox")[0].checked; var skipTLSValue = $("#skipTLSCheckbox")[0].checked;
var dnsServers = $("#dnsInput").val(); // Erfassen der DNS-Server
$.ajax({ $.ajax({
url: "/api/acme/obtainCert", url: "/api/acme/obtainCert",
@ -749,6 +753,7 @@
caURL: caURL, caURL: caURL,
skipTLS: skipTLSValue, skipTLS: skipTLSValue,
dns: dns, dns: dns,
dnsServers: dnsServers // DNS-Server in die Anfrage einfügen
}, },
success: function(response) { success: function(response) {
$("#obtainButton").removeClass("loading").removeClass("disabled"); $("#obtainButton").removeClass("loading").removeClass("disabled");
@ -761,7 +766,6 @@
console.log("Certificate renewed successfully"); console.log("Certificate renewed successfully");
// Show success message // Show success message
parent.msgbox("Certificate renewed successfully"); parent.msgbox("Certificate renewed successfully");
// Renew the parent certificate list // Renew the parent certificate list
parent.initManagedDomainCertificateList(); parent.initManagedDomainCertificateList();