Added Custom Name Server Option

This commit is contained in:
sickjuicy 2024-11-26 23:30:24 +01:00
parent 4a4483e09d
commit e6b2d458f7
3 changed files with 50 additions and 49 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"
@ -30,11 +31,12 @@ import (
) )
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 +96,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
@ -114,7 +116,6 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
config := lego.NewConfig(&adminUser) config := lego.NewConfig(&adminUser)
// skip TLS verify if need // skip TLS verify if need
// Ref: https://github.com/go-acme/lego/blob/6af2c756ac73a9cb401621afca722d0f4112b1b8/lego/client_config.go#L74
if skipTLS { if skipTLS {
a.Logf("Ignoring TLS/SSL Verification Error for ACME Server", nil) a.Logf("Ignoring TLS/SSL Verification Error for ACME Server", nil)
config.HTTPClient.Transport = &http.Transport{ config.HTTPClient.Transport = &http.Transport{
@ -150,7 +151,6 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
config.CADirURL = caLinkOverwrite config.CADirURL = caLinkOverwrite
a.Logf("Using "+caLinkOverwrite+" for CA Directory URL", nil) a.Logf("Using "+caLinkOverwrite+" for CA Directory URL", nil)
} else { } else {
// (caName == "" || caUrl == "") will use default acme
config.CADirURL = a.DefaultAcmeServer config.CADirURL = a.DefaultAcmeServer
a.Logf("Using Default ACME "+a.DefaultAcmeServer+" for CA Directory URL", nil) a.Logf("Using Default ACME "+a.DefaultAcmeServer+" for CA Directory URL", nil)
} }
@ -168,11 +168,11 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
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 +195,11 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
return false, err return false, err
} }
err = client.Challenge.SetDNS01Provider(provider) if len(dnsServers) > 0 {
err = client.Challenge.SetDNS01Provider(provider, dns01.AddRecursiveNameservers(dnsServers))
} else {
err = client.Challenge.SetDNS01Provider(provider)
}
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
@ -209,19 +213,9 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
} }
// New users will need to register // New users will need to register
/*
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
log.Println(err)
return false, err
}
*/
var reg *registration.Resource var reg *registration.Resource
// New users will need to register
if client.GetExternalAccountRequired() { if client.GetExternalAccountRequired() {
a.Logf("External Account Required for this ACME Provider", nil) a.Logf("External Account Required for this ACME Provider", nil)
// IF KID and HmacEncoded is overidden
if !a.Database.TableExists("acme") { if !a.Database.TableExists("acme") {
a.Database.NewTable("acme") a.Database.NewTable("acme")
return false, errors.New("kid and HmacEncoded configuration required for ACME Provider (Error -1)") return false, errors.New("kid and HmacEncoded configuration required for ACME Provider (Error -1)")
@ -257,7 +251,6 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
a.Logf("Register with external account binder failed", err) a.Logf("Register with external account binder failed", err)
return false, err return false, err
} }
//return false, errors.New("External Account Required for this ACME Provider.")
} else { } else {
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil { if err != nil {
@ -298,6 +291,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
SkipTLS: skipTLS, SkipTLS: skipTLS,
UseDNS: useDNS, UseDNS: useDNS,
PropTimeout: propagationTimeout, PropTimeout: propagationTimeout,
DNSServers: dnsServers,
} }
certInfoBytes, err := json.Marshal(certInfo) certInfoBytes, err := json.Marshal(certInfo)
@ -479,12 +473,22 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
} }
} }
// 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])
}
}
//Clean spaces in front or behind each domain //Clean spaces in front or behind each domain
cleanedDomains := []string{} cleanedDomains := []string{}
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) result, err := a.ObtainCert(cleanedDomains, filename, email, ca, caUrl, skipTLS, dns, propagationTimeout, dnsServers)
if err != nil { if err != nil {
utils.SendErrorResponse(w, jsonEscape(err.Error())) utils.SendErrorResponse(w, jsonEscape(err.Error()))
return return

View File

@ -354,7 +354,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
return a.renewExpiredDomains(expiredCertList) return a.renewExpiredDomains(expiredCertList)
} }
// Close the auto renewer
func (a *AutoRenewer) Close() { func (a *AutoRenewer) Close() {
if a.TickerstopChan != nil { if a.TickerstopChan != nil {
a.TickerstopChan <- true a.TickerstopChan <- true
@ -384,13 +383,19 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
} }
} }
//For upgrading config from older version of Zoraxy which don't have timeout // For upgrading config from older version of Zoraxy which don't have timeout
if certInfo.PropTimeout == 0 { if certInfo.PropTimeout == 0 {
//Set default timeout // Set default timeout
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 certInfo.DNSServers != nil {
dnsServers = 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 {

View File

@ -25,8 +25,6 @@
</style> </style>
</head> </head>
<body> <body>
<link rel="stylesheet" href="../darktheme.css">
<script src="../script/darktheme.js"></script>
<br> <br>
<div class="ui container"> <div class="ui container">
<div class="ui header"> <div class="ui header">
@ -52,7 +50,7 @@
</div> </div>
<small>If you don't want to share your private email address, you can also fill in an email address that point to a mailbox not exists on your domain.</small> <small>If you don't want to share your private email address, you can also fill in an email address that point to a mailbox not exists on your domain.</small>
</div> </div>
<div class="ui basic segment advanceoptions"> <div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
<div class="ui accordion advanceSettings"> <div class="ui accordion advanceSettings">
<div class="title"> <div class="title">
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
@ -137,6 +135,13 @@
<!-- Auto populate moved to acmedns module and initDNSProviderList() --> <!-- Auto populate moved to acmedns module and initDNSProviderList() -->
</div> </div>
</div> </div>
<div class="ui form">
<div class="field">
<label>Domain Name Server (optional)</label>
<input id="dnsInput" type="text" placeholder="ns.example.com" onkeyup="handlePostInputAutomation();">
<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> </div>
<div class="field dnsChallengeOnly" style="display:none;"> <div class="field dnsChallengeOnly" style="display:none;">
<div class="ui divider"></div> <div class="ui divider"></div>
@ -439,15 +444,11 @@
let optionalFieldsHTML = ""; let optionalFieldsHTML = "";
for (const [key, datatype] of Object.entries(data)) { for (const [key, datatype] of Object.entries(data)) {
if (datatype == "int"){ if (datatype == "int"){
let defaultValue = 10; $("#dnsProviderAPIFields").append(`<div class="ui fluid labeled dnsConfigField input" key="${key}" style="margin-top: 0.2em;">
if (key == "HTTPTimeout"){
defaultValue = 300;
}
$("#dnsProviderAPIFields").append(`<div class="ui fluid labeled dnsConfigField input typeint" key="${key}" style="margin-top: 0.2em;">
<div class="ui basic blue label" style="font-weight: 300;"> <div class="ui basic blue label" style="font-weight: 300;">
${key} ${key}
</div> </div>
<input type="number" value="${defaultValue}"> <input type="number" value="300">
</div>`); </div>`);
}else if (datatype == "bool"){ }else if (datatype == "bool"){
booleanFieldsHTML += (`<div class="ui checkbox dnsConfigField" key="${key}" style="margin-top: 1em !important; padding-left: 0.4em;"> booleanFieldsHTML += (`<div class="ui checkbox dnsConfigField" key="${key}" style="margin-top: 1em !important; padding-left: 0.4em;">
@ -606,12 +607,8 @@
//Boolean option //Boolean option
let checked = $(this).find("input")[0].checked; let checked = $(this).find("input")[0].checked;
dnsCredentials[thisKey] = checked; dnsCredentials[thisKey] = checked;
}else if ($(this).hasClass("typeint")){
//Int options
let value = $(this).find("input").val();
dnsCredentials[thisKey] = parseInt(value);
}else{ }else{
//String options //String or int options
let value = $(this).find("input").val().trim(); let value = $(this).find("input").val().trim();
dnsCredentials[thisKey] = value; dnsCredentials[thisKey] = value;
} }
@ -725,8 +722,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 +730,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(); // New line: Read DNS servers from input field
$.ajax({ $.ajax({
url: "/api/acme/obtainCert", url: "/api/acme/obtainCert",
@ -749,22 +745,18 @@
caURL: caURL, caURL: caURL,
skipTLS: skipTLSValue, skipTLS: skipTLSValue,
dns: dns, dns: dns,
dnsServers: dnsServers // New line: Include DNS servers in the request
}, },
success: function(response) { success: function(response) {
$("#obtainButton").removeClass("loading").removeClass("disabled"); $("#obtainButton").removeClass("loading").removeClass("disabled");
if (response.error) { if (response.error) {
console.log("Error:", response.error); console.log("Error:", response.error);
// Show error message
parent.msgbox(response.error, false, 12000); parent.msgbox(response.error, false, 12000);
if (callback != undefined){callback(false);} if (callback != undefined){callback(false);}
} else { } else {
console.log("Certificate renewed successfully"); console.log("Certificate renewed successfully");
// Show success message
parent.msgbox("Certificate renewed successfully"); parent.msgbox("Certificate renewed successfully");
// Renew the parent certificate list
parent.initManagedDomainCertificateList(); parent.initManagedDomainCertificateList();
if (callback != undefined){callback(true);} if (callback != undefined){callback(true);}
} }
}, },