修复AnyTLS无法配置证书的错误
This commit is contained in:
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/libdns/acmedns"
|
"github.com/libdns/acmedns"
|
||||||
"github.com/libdns/alidns"
|
"github.com/libdns/alidns"
|
||||||
"github.com/libdns/cloudflare"
|
"github.com/libdns/cloudflare"
|
||||||
|
"github.com/libdns/dnspod"
|
||||||
|
"github.com/libdns/tencentcloud"
|
||||||
"github.com/mholt/acmez/v3/acme"
|
"github.com/mholt/acmez/v3/acme"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
@@ -81,7 +83,7 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
|
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
|
||||||
var solver certmagic.DNS01Solver
|
var solver certmagic.DNS01Solver
|
||||||
switch dnsOptions.Provider {
|
switch C.NormalizeACMEDNSProvider(dnsOptions.Provider) {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
solver.DNSProvider = &alidns.Provider{
|
solver.DNSProvider = &alidns.Provider{
|
||||||
CredentialInfo: alidns.CredentialInfo{
|
CredentialInfo: alidns.CredentialInfo{
|
||||||
@@ -96,6 +98,17 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
|||||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||||
}
|
}
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
solver.DNSProvider = &tencentcloud.Provider{
|
||||||
|
SecretId: dnsOptions.TencentCloudOptions.SecretID,
|
||||||
|
SecretKey: dnsOptions.TencentCloudOptions.SecretKey,
|
||||||
|
SessionToken: dnsOptions.TencentCloudOptions.SessionToken,
|
||||||
|
Region: dnsOptions.TencentCloudOptions.Region,
|
||||||
|
}
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
solver.DNSProvider = &dnspod.Provider{
|
||||||
|
APIToken: dnsOptions.DNSPodOptions.APIToken,
|
||||||
|
}
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
solver.DNSProvider = &acmedns.Provider{
|
solver.DNSProvider = &acmedns.Provider{
|
||||||
Username: dnsOptions.ACMEDNSOptions.Username,
|
Username: dnsOptions.ACMEDNSOptions.Username,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultDNSTTL = 600
|
DefaultDNSTTL = 600
|
||||||
)
|
)
|
||||||
@@ -33,4 +35,25 @@ const (
|
|||||||
DNSProviderAliDNS = "alidns"
|
DNSProviderAliDNS = "alidns"
|
||||||
DNSProviderCloudflare = "cloudflare"
|
DNSProviderCloudflare = "cloudflare"
|
||||||
DNSProviderACMEDNS = "acmedns"
|
DNSProviderACMEDNS = "acmedns"
|
||||||
|
DNSProviderTencentCloud = "tencentcloud"
|
||||||
|
DNSProviderDNSPod = "dnspod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NormalizeACMEDNSProvider(provider string) string {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(provider)) {
|
||||||
|
case "", DNSProviderAliDNS, DNSProviderCloudflare, DNSProviderACMEDNS:
|
||||||
|
return strings.ToLower(strings.TrimSpace(provider))
|
||||||
|
case "aliyun":
|
||||||
|
return DNSProviderAliDNS
|
||||||
|
case "cf":
|
||||||
|
return DNSProviderCloudflare
|
||||||
|
case "acme-dns":
|
||||||
|
return DNSProviderACMEDNS
|
||||||
|
case "tencent", "tencentcloud", "dnspod-tencentcloud", "qcloud":
|
||||||
|
return DNSProviderTencentCloud
|
||||||
|
case "dnspod":
|
||||||
|
return DNSProviderDNSPod
|
||||||
|
default:
|
||||||
|
return strings.ToLower(strings.TrimSpace(provider))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -20,7 +20,9 @@ require (
|
|||||||
github.com/libdns/acmedns v0.5.0
|
github.com/libdns/acmedns v0.5.0
|
||||||
github.com/libdns/alidns v1.0.6
|
github.com/libdns/alidns v1.0.6
|
||||||
github.com/libdns/cloudflare v0.2.2
|
github.com/libdns/cloudflare v0.2.2
|
||||||
|
github.com/libdns/dnspod v0.0.3
|
||||||
github.com/libdns/libdns v1.1.1
|
github.com/libdns/libdns v1.1.1
|
||||||
|
github.com/libdns/tencentcloud v1.4.3
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/mdlayher/netlink v1.9.0
|
github.com/mdlayher/netlink v1.9.0
|
||||||
github.com/metacubex/utls v1.8.4
|
github.com/metacubex/utls v1.8.4
|
||||||
|
|||||||
@@ -28,34 +28,43 @@ type ACMECertificateProviderOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type _ACMEProviderDNS01ChallengeOptions struct {
|
type _ACMEProviderDNS01ChallengeOptions struct {
|
||||||
TTL badoption.Duration `json:"ttl,omitempty"`
|
TTL badoption.Duration `json:"ttl,omitempty"`
|
||||||
PropagationDelay badoption.Duration `json:"propagation_delay,omitempty"`
|
PropagationDelay badoption.Duration `json:"propagation_delay,omitempty"`
|
||||||
PropagationTimeout badoption.Duration `json:"propagation_timeout,omitempty"`
|
PropagationTimeout badoption.Duration `json:"propagation_timeout,omitempty"`
|
||||||
Resolvers badoption.Listable[string] `json:"resolvers,omitempty"`
|
Resolvers badoption.Listable[string] `json:"resolvers,omitempty"`
|
||||||
OverrideDomain string `json:"override_domain,omitempty"`
|
OverrideDomain string `json:"override_domain,omitempty"`
|
||||||
Provider string `json:"provider,omitempty"`
|
Provider string `json:"provider,omitempty"`
|
||||||
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
||||||
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
||||||
ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"`
|
ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"`
|
||||||
|
TencentCloudOptions ACMEDNS01TencentCloudOptions `json:"-"`
|
||||||
|
DNSPodOptions ACMEDNS01DNSPodOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ACMEProviderDNS01ChallengeOptions _ACMEProviderDNS01ChallengeOptions
|
type ACMEProviderDNS01ChallengeOptions _ACMEProviderDNS01ChallengeOptions
|
||||||
|
|
||||||
func (o ACMEProviderDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
func (o ACMEProviderDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
provider := C.NormalizeACMEDNSProvider(o.Provider)
|
||||||
var v any
|
var v any
|
||||||
switch o.Provider {
|
switch provider {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
v = o.AliDNSOptions
|
v = o.AliDNSOptions
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
v = o.CloudflareOptions
|
v = o.CloudflareOptions
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
v = o.ACMEDNSOptions
|
v = o.ACMEDNSOptions
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
v = o.TencentCloudOptions
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
v = o.DNSPodOptions
|
||||||
case "":
|
case "":
|
||||||
return nil, E.New("missing provider type")
|
return nil, E.New("missing provider type")
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown provider type: ", o.Provider)
|
return nil, E.New("unknown provider type: ", o.Provider)
|
||||||
}
|
}
|
||||||
return badjson.MarshallObjects((_ACMEProviderDNS01ChallengeOptions)(o), v)
|
copyValue := (_ACMEProviderDNS01ChallengeOptions)(o)
|
||||||
|
copyValue.Provider = provider
|
||||||
|
return badjson.MarshallObjects(copyValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
@@ -63,6 +72,7 @@ func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
o.Provider = C.NormalizeACMEDNSProvider(o.Provider)
|
||||||
var v any
|
var v any
|
||||||
switch o.Provider {
|
switch o.Provider {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
@@ -71,6 +81,10 @@ func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
v = &o.CloudflareOptions
|
v = &o.CloudflareOptions
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
v = &o.ACMEDNSOptions
|
v = &o.ACMEDNSOptions
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
v = &o.TencentCloudOptions
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
v = &o.DNSPodOptions
|
||||||
case "":
|
case "":
|
||||||
return E.New("missing provider type")
|
return E.New("missing provider type")
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -28,29 +28,38 @@ type ACMEExternalAccountOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type _ACMEDNS01ChallengeOptions struct {
|
type _ACMEDNS01ChallengeOptions struct {
|
||||||
Provider string `json:"provider,omitempty"`
|
Provider string `json:"provider,omitempty"`
|
||||||
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
||||||
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
||||||
ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"`
|
ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"`
|
||||||
|
TencentCloudOptions ACMEDNS01TencentCloudOptions `json:"-"`
|
||||||
|
DNSPodOptions ACMEDNS01DNSPodOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
|
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
|
||||||
|
|
||||||
func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
provider := C.NormalizeACMEDNSProvider(o.Provider)
|
||||||
var v any
|
var v any
|
||||||
switch o.Provider {
|
switch provider {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
v = o.AliDNSOptions
|
v = o.AliDNSOptions
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
v = o.CloudflareOptions
|
v = o.CloudflareOptions
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
v = o.ACMEDNSOptions
|
v = o.ACMEDNSOptions
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
v = o.TencentCloudOptions
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
v = o.DNSPodOptions
|
||||||
case "":
|
case "":
|
||||||
return nil, E.New("missing provider type")
|
return nil, E.New("missing provider type")
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown provider type: " + o.Provider)
|
return nil, E.New("unknown provider type: " + o.Provider)
|
||||||
}
|
}
|
||||||
return badjson.MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
|
copyValue := (_ACMEDNS01ChallengeOptions)(o)
|
||||||
|
copyValue.Provider = provider
|
||||||
|
return badjson.MarshallObjects(copyValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
@@ -58,6 +67,7 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
o.Provider = C.NormalizeACMEDNSProvider(o.Provider)
|
||||||
var v any
|
var v any
|
||||||
switch o.Provider {
|
switch o.Provider {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
@@ -66,6 +76,10 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
v = &o.CloudflareOptions
|
v = &o.CloudflareOptions
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
v = &o.ACMEDNSOptions
|
v = &o.ACMEDNSOptions
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
v = &o.TencentCloudOptions
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
v = &o.DNSPodOptions
|
||||||
default:
|
default:
|
||||||
return E.New("unknown provider type: " + o.Provider)
|
return E.New("unknown provider type: " + o.Provider)
|
||||||
}
|
}
|
||||||
@@ -94,3 +108,14 @@ type ACMEDNS01ACMEDNSOptions struct {
|
|||||||
Subdomain string `json:"subdomain,omitempty"`
|
Subdomain string `json:"subdomain,omitempty"`
|
||||||
ServerURL string `json:"server_url,omitempty"`
|
ServerURL string `json:"server_url,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ACMEDNS01TencentCloudOptions struct {
|
||||||
|
SecretID string `json:"secret_id,omitempty"`
|
||||||
|
SecretKey string `json:"secret_key,omitempty"`
|
||||||
|
SessionToken string `json:"session_token,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACMEDNS01DNSPodOptions struct {
|
||||||
|
APIToken string `json:"api_token,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ import (
|
|||||||
"github.com/caddyserver/zerossl"
|
"github.com/caddyserver/zerossl"
|
||||||
"github.com/libdns/alidns"
|
"github.com/libdns/alidns"
|
||||||
"github.com/libdns/cloudflare"
|
"github.com/libdns/cloudflare"
|
||||||
|
"github.com/libdns/dnspod"
|
||||||
"github.com/libdns/libdns"
|
"github.com/libdns/libdns"
|
||||||
|
"github.com/libdns/tencentcloud"
|
||||||
"github.com/mholt/acmez/v3/acme"
|
"github.com/mholt/acmez/v3/acme"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
@@ -224,7 +226,7 @@ func newDNSSolver(dnsOptions *option.ACMEProviderDNS01ChallengeOptions, logger *
|
|||||||
Logger: logger.Named("dns_manager"),
|
Logger: logger.Named("dns_manager"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch dnsOptions.Provider {
|
switch C.NormalizeACMEDNSProvider(dnsOptions.Provider) {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
solver.DNSProvider = &alidns.Provider{
|
solver.DNSProvider = &alidns.Provider{
|
||||||
CredentialInfo: alidns.CredentialInfo{
|
CredentialInfo: alidns.CredentialInfo{
|
||||||
@@ -240,6 +242,17 @@ func newDNSSolver(dnsOptions *option.ACMEProviderDNS01ChallengeOptions, logger *
|
|||||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||||
HTTPClient: httpClient,
|
HTTPClient: httpClient,
|
||||||
}
|
}
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
solver.DNSProvider = &tencentcloud.Provider{
|
||||||
|
SecretId: dnsOptions.TencentCloudOptions.SecretID,
|
||||||
|
SecretKey: dnsOptions.TencentCloudOptions.SecretKey,
|
||||||
|
SessionToken: dnsOptions.TencentCloudOptions.SessionToken,
|
||||||
|
Region: dnsOptions.TencentCloudOptions.Region,
|
||||||
|
}
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
solver.DNSProvider = &dnspod.Provider{
|
||||||
|
APIToken: dnsOptions.DNSPodOptions.APIToken,
|
||||||
|
}
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
solver.DNSProvider = &acmeDNSProvider{
|
solver.DNSProvider = &acmeDNSProvider{
|
||||||
username: dnsOptions.ACMEDNSOptions.Username,
|
username: dnsOptions.ACMEDNSOptions.Username,
|
||||||
|
|||||||
@@ -467,9 +467,9 @@ func hasUsableServerTLS(tlsOptions option.InboundTLSOptions) bool {
|
|||||||
(len(tlsOptions.Certificate) > 0 && len(tlsOptions.Key) > 0)
|
(len(tlsOptions.Certificate) > 0 && len(tlsOptions.Key) > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyACMEConfig(tlsOptions *option.InboundTLSOptions, certConfig *XCertConfig, autoTLS bool, domain string, listenPort int) bool {
|
func applyACMEConfigDetailed(tlsOptions *option.InboundTLSOptions, certConfig *XCertConfig, autoTLS bool, domain string, listenPort int) (bool, string) {
|
||||||
if !autoTLS && certConfig == nil {
|
if !autoTLS && certConfig == nil {
|
||||||
return false
|
return false, "acme disabled: no auto_tls and no cert_config"
|
||||||
}
|
}
|
||||||
mode := ""
|
mode := ""
|
||||||
if certConfig != nil {
|
if certConfig != nil {
|
||||||
@@ -479,54 +479,98 @@ func applyACMEConfig(tlsOptions *option.InboundTLSOptions, certConfig *XCertConf
|
|||||||
mode = "http"
|
mode = "http"
|
||||||
}
|
}
|
||||||
if mode != "http" && mode != "dns" {
|
if mode != "http" && mode != "dns" {
|
||||||
return false
|
return false, "unsupported cert_mode: " + mode
|
||||||
}
|
}
|
||||||
domain = strings.TrimSpace(domain)
|
domain = strings.TrimSpace(domain)
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
return false
|
return false, "missing domain/server_name for ACME"
|
||||||
}
|
}
|
||||||
|
|
||||||
acmeOptions := &option.InboundACMEOptions{
|
acmeOptions := &option.InboundACMEOptions{
|
||||||
Domain: badoption.Listable[string]{domain},
|
Domain: badoption.Listable[string]{domain},
|
||||||
DataDirectory: "acme",
|
DataDirectory: "acme",
|
||||||
DefaultServerName: domain,
|
DefaultServerName: domain,
|
||||||
DisableHTTPChallenge: true,
|
|
||||||
}
|
}
|
||||||
if certConfig != nil {
|
if certConfig != nil {
|
||||||
acmeOptions.Email = strings.TrimSpace(certConfig.Email)
|
acmeOptions.Email = strings.TrimSpace(certConfig.Email)
|
||||||
}
|
}
|
||||||
if listenPort > 0 && listenPort != 443 {
|
switch mode {
|
||||||
acmeOptions.AlternativeTLSPort = uint16(listenPort)
|
case "http":
|
||||||
}
|
acmeOptions.DisableHTTPChallenge = false
|
||||||
if mode == "dns" && certConfig != nil {
|
acmeOptions.DisableTLSALPNChallenge = true
|
||||||
dnsProvider := strings.ToLower(strings.TrimSpace(certConfig.DNSProvider))
|
if certConfig != nil && certConfig.HTTPPort > 0 && certConfig.HTTPPort != 80 {
|
||||||
if dnsProvider == "" {
|
acmeOptions.AlternativeHTTPPort = uint16(certConfig.HTTPPort)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
case "dns":
|
||||||
acmeOptions.DisableHTTPChallenge = true
|
acmeOptions.DisableHTTPChallenge = true
|
||||||
acmeOptions.DisableTLSALPNChallenge = true
|
acmeOptions.DisableTLSALPNChallenge = true
|
||||||
|
if listenPort > 0 && listenPort != 443 {
|
||||||
|
acmeOptions.AlternativeTLSPort = uint16(listenPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mode == "dns" && certConfig != nil {
|
||||||
|
dnsProvider := C.NormalizeACMEDNSProvider(certConfig.DNSProvider)
|
||||||
|
if dnsProvider == "" {
|
||||||
|
return false, "missing dns_provider for cert_mode=dns"
|
||||||
|
}
|
||||||
dns01 := &option.ACMEDNS01ChallengeOptions{Provider: dnsProvider}
|
dns01 := &option.ACMEDNS01ChallengeOptions{Provider: dnsProvider}
|
||||||
switch dnsProvider {
|
switch dnsProvider {
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
dns01.CloudflareOptions.APIToken = firstNonEmpty(certConfig.DNSEnv["CF_API_TOKEN"], certConfig.DNSEnv["CLOUDFLARE_API_TOKEN"])
|
dns01.CloudflareOptions.APIToken = firstNonEmpty(certConfig.DNSEnv["CF_API_TOKEN"], certConfig.DNSEnv["CLOUDFLARE_API_TOKEN"])
|
||||||
dns01.CloudflareOptions.ZoneToken = firstNonEmpty(certConfig.DNSEnv["CF_ZONE_TOKEN"], certConfig.DNSEnv["CLOUDFLARE_ZONE_TOKEN"])
|
dns01.CloudflareOptions.ZoneToken = firstNonEmpty(certConfig.DNSEnv["CF_ZONE_TOKEN"], certConfig.DNSEnv["CLOUDFLARE_ZONE_TOKEN"])
|
||||||
|
if dns01.CloudflareOptions.APIToken == "" && dns01.CloudflareOptions.ZoneToken == "" {
|
||||||
|
return false, "cloudflare dns challenge requires CF_API_TOKEN/CLOUDFLARE_API_TOKEN or CF_ZONE_TOKEN/CLOUDFLARE_ZONE_TOKEN"
|
||||||
|
}
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
dns01.AliDNSOptions.AccessKeyID = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_ACCESS_KEY_ID"], certConfig.DNSEnv["ALI_ACCESS_KEY_ID"])
|
dns01.AliDNSOptions.AccessKeyID = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_ACCESS_KEY_ID"], certConfig.DNSEnv["ALI_ACCESS_KEY_ID"])
|
||||||
dns01.AliDNSOptions.AccessKeySecret = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_ACCESS_KEY_SECRET"], certConfig.DNSEnv["ALI_ACCESS_KEY_SECRET"])
|
dns01.AliDNSOptions.AccessKeySecret = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_ACCESS_KEY_SECRET"], certConfig.DNSEnv["ALI_ACCESS_KEY_SECRET"])
|
||||||
dns01.AliDNSOptions.RegionID = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_REGION_ID"], certConfig.DNSEnv["ALI_REGION_ID"])
|
dns01.AliDNSOptions.RegionID = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_REGION_ID"], certConfig.DNSEnv["ALI_REGION_ID"])
|
||||||
dns01.AliDNSOptions.SecurityToken = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_SECURITY_TOKEN"], certConfig.DNSEnv["ALI_SECURITY_TOKEN"])
|
dns01.AliDNSOptions.SecurityToken = firstNonEmpty(certConfig.DNSEnv["ALICLOUD_SECURITY_TOKEN"], certConfig.DNSEnv["ALI_SECURITY_TOKEN"])
|
||||||
|
if dns01.AliDNSOptions.AccessKeyID == "" || dns01.AliDNSOptions.AccessKeySecret == "" {
|
||||||
|
return false, "alidns dns challenge requires ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET"
|
||||||
|
}
|
||||||
|
case C.DNSProviderTencentCloud:
|
||||||
|
dns01.TencentCloudOptions.SecretID = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SECRET_ID"], certConfig.DNSEnv["TENCENT_SECRET_ID"], certConfig.DNSEnv["SECRET_ID"])
|
||||||
|
dns01.TencentCloudOptions.SecretKey = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SECRET_KEY"], certConfig.DNSEnv["TENCENT_SECRET_KEY"], certConfig.DNSEnv["SECRET_KEY"])
|
||||||
|
dns01.TencentCloudOptions.SessionToken = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SESSION_TOKEN"], certConfig.DNSEnv["TENCENT_SESSION_TOKEN"], certConfig.DNSEnv["SESSION_TOKEN"])
|
||||||
|
dns01.TencentCloudOptions.Region = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_REGION"], certConfig.DNSEnv["TENCENT_REGION"], certConfig.DNSEnv["REGION"])
|
||||||
|
if dns01.TencentCloudOptions.SecretID == "" || dns01.TencentCloudOptions.SecretKey == "" {
|
||||||
|
return false, "tencentcloud dns challenge requires TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY"
|
||||||
|
}
|
||||||
|
case C.DNSProviderDNSPod:
|
||||||
|
dns01.DNSPodOptions.APIToken = firstNonEmpty(certConfig.DNSEnv["DNSPOD_TOKEN"], certConfig.DNSEnv["API_TOKEN"])
|
||||||
|
if dns01.DNSPodOptions.APIToken == "" {
|
||||||
|
tencentSecretID := firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SECRET_ID"], certConfig.DNSEnv["TENCENT_SECRET_ID"], certConfig.DNSEnv["SECRET_ID"])
|
||||||
|
tencentSecretKey := firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SECRET_KEY"], certConfig.DNSEnv["TENCENT_SECRET_KEY"], certConfig.DNSEnv["SECRET_KEY"])
|
||||||
|
if tencentSecretID == "" || tencentSecretKey == "" {
|
||||||
|
return false, "dnspod dns challenge requires DNSPOD_TOKEN or TencentCloud SecretID/SecretKey"
|
||||||
|
}
|
||||||
|
dns01.Provider = C.DNSProviderTencentCloud
|
||||||
|
dns01.TencentCloudOptions.SecretID = tencentSecretID
|
||||||
|
dns01.TencentCloudOptions.SecretKey = tencentSecretKey
|
||||||
|
dns01.TencentCloudOptions.SessionToken = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_SESSION_TOKEN"], certConfig.DNSEnv["TENCENT_SESSION_TOKEN"], certConfig.DNSEnv["SESSION_TOKEN"])
|
||||||
|
dns01.TencentCloudOptions.Region = firstNonEmpty(certConfig.DNSEnv["TENCENTCLOUD_REGION"], certConfig.DNSEnv["TENCENT_REGION"], certConfig.DNSEnv["REGION"])
|
||||||
|
}
|
||||||
case C.DNSProviderACMEDNS:
|
case C.DNSProviderACMEDNS:
|
||||||
dns01.ACMEDNSOptions.Username = certConfig.DNSEnv["ACMEDNS_USERNAME"]
|
dns01.ACMEDNSOptions.Username = certConfig.DNSEnv["ACMEDNS_USERNAME"]
|
||||||
dns01.ACMEDNSOptions.Password = certConfig.DNSEnv["ACMEDNS_PASSWORD"]
|
dns01.ACMEDNSOptions.Password = certConfig.DNSEnv["ACMEDNS_PASSWORD"]
|
||||||
dns01.ACMEDNSOptions.Subdomain = certConfig.DNSEnv["ACMEDNS_SUBDOMAIN"]
|
dns01.ACMEDNSOptions.Subdomain = certConfig.DNSEnv["ACMEDNS_SUBDOMAIN"]
|
||||||
dns01.ACMEDNSOptions.ServerURL = certConfig.DNSEnv["ACMEDNS_SERVER_URL"]
|
dns01.ACMEDNSOptions.ServerURL = certConfig.DNSEnv["ACMEDNS_SERVER_URL"]
|
||||||
|
if dns01.ACMEDNSOptions.Username == "" || dns01.ACMEDNSOptions.Password == "" || dns01.ACMEDNSOptions.Subdomain == "" || dns01.ACMEDNSOptions.ServerURL == "" {
|
||||||
|
return false, "acmedns dns challenge requires username, password, subdomain and server_url"
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false
|
return false, "unsupported dns_provider: " + dnsProvider
|
||||||
}
|
}
|
||||||
acmeOptions.DNS01Challenge = dns01
|
acmeOptions.DNS01Challenge = dns01
|
||||||
}
|
}
|
||||||
tlsOptions.ACME = acmeOptions
|
tlsOptions.ACME = acmeOptions
|
||||||
return true
|
return true, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyACMEConfig(tlsOptions *option.InboundTLSOptions, certConfig *XCertConfig, autoTLS bool, domain string, listenPort int) bool {
|
||||||
|
ok, _ := applyACMEConfigDetailed(tlsOptions, certConfig, autoTLS, domain, listenPort)
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergedTLSSettings(inner XInnerConfig, config *XNodeConfig) *XTLSSettings {
|
func mergedTLSSettings(inner XInnerConfig, config *XNodeConfig) *XTLSSettings {
|
||||||
@@ -1067,11 +1111,12 @@ func (s *Service) setupNode() error {
|
|||||||
autoTLSDomain = tlsSettings.ServerName
|
autoTLSDomain = tlsSettings.ServerName
|
||||||
}
|
}
|
||||||
hasACME := false
|
hasACME := false
|
||||||
|
acmeReason := ""
|
||||||
if !hasCertificate {
|
if !hasCertificate {
|
||||||
hasACME = applyACMEConfig(&tlsOptions, certConfig, inner.AutoTLS || configAutoTLS, autoTLSDomain, inner.Port)
|
hasACME, acmeReason = applyACMEConfigDetailed(&tlsOptions, certConfig, inner.AutoTLS || configAutoTLS, autoTLSDomain, inner.Port)
|
||||||
}
|
}
|
||||||
if certConfig != nil && !hasCertificate && !hasACME && certConfig.CertMode != "" && certConfig.CertMode != "none" {
|
if certConfig != nil && !hasCertificate && !hasACME && certConfig.CertMode != "" && certConfig.CertMode != "none" {
|
||||||
s.logger.Warn("Xboard cert_config present but unsupported or incomplete for local TLS. cert_mode=", certConfig.CertMode)
|
s.logger.Warn("Xboard cert_config present but unsupported or incomplete for local TLS. cert_mode=", certConfig.CertMode, ", reason=", acmeReason)
|
||||||
}
|
}
|
||||||
if hasACME {
|
if hasACME {
|
||||||
s.logger.Info("Xboard ACME configured for domain ", autoTLSDomain)
|
s.logger.Info("Xboard ACME configured for domain ", autoTLSDomain)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package xboard
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common/json/badoption"
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
)
|
)
|
||||||
@@ -158,11 +159,11 @@ func TestApplyACMEConfigFromAutoTLS(t *testing.T) {
|
|||||||
if len(tlsOptions.ACME.Domain) != 1 || tlsOptions.ACME.Domain[0] != "example.com" {
|
if len(tlsOptions.ACME.Domain) != 1 || tlsOptions.ACME.Domain[0] != "example.com" {
|
||||||
t.Fatalf("ACME domains = %+v", tlsOptions.ACME.Domain)
|
t.Fatalf("ACME domains = %+v", tlsOptions.ACME.Domain)
|
||||||
}
|
}
|
||||||
if tlsOptions.ACME.AlternativeTLSPort != 8443 {
|
if tlsOptions.ACME.DisableHTTPChallenge {
|
||||||
t.Fatalf("AlternativeTLSPort = %d, want 8443", tlsOptions.ACME.AlternativeTLSPort)
|
t.Fatal("DisableHTTPChallenge should be false for auto_tls/http mode")
|
||||||
}
|
}
|
||||||
if !tlsOptions.ACME.DisableHTTPChallenge {
|
if !tlsOptions.ACME.DisableTLSALPNChallenge {
|
||||||
t.Fatal("DisableHTTPChallenge should be true for inline ACME")
|
t.Fatal("DisableTLSALPNChallenge should be true for auto_tls/http mode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +194,71 @@ func TestApplyACMEConfigFromDNSCertMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyACMEConfigFromTencentCloudDNSCertMode(t *testing.T) {
|
||||||
|
var tlsOptions option.InboundTLSOptions
|
||||||
|
ok := applyACMEConfig(&tlsOptions, &XCertConfig{
|
||||||
|
CertMode: "dns",
|
||||||
|
Domain: "code.littlediary.cn",
|
||||||
|
DNSProvider: "tencentcloud",
|
||||||
|
DNSEnv: map[string]string{
|
||||||
|
"TENCENTCLOUD_SECRET_ID": "sid",
|
||||||
|
"TENCENTCLOUD_SECRET_KEY": "skey",
|
||||||
|
},
|
||||||
|
}, false, "code.littlediary.cn", 45365)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("applyACMEConfig() returned false")
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME == nil || tlsOptions.ACME.DNS01Challenge == nil {
|
||||||
|
t.Fatal("DNS01Challenge not configured")
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME.DNS01Challenge.Provider != C.DNSProviderTencentCloud {
|
||||||
|
t.Fatalf("DNS provider = %q", tlsOptions.ACME.DNS01Challenge.Provider)
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME.DNS01Challenge.TencentCloudOptions.SecretID != "sid" {
|
||||||
|
t.Fatalf("TencentCloud SecretID = %q", tlsOptions.ACME.DNS01Challenge.TencentCloudOptions.SecretID)
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME.DNS01Challenge.TencentCloudOptions.SecretKey != "skey" {
|
||||||
|
t.Fatalf("TencentCloud SecretKey = %q", tlsOptions.ACME.DNS01Challenge.TencentCloudOptions.SecretKey)
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME.AlternativeTLSPort != 45365 {
|
||||||
|
t.Fatalf("AlternativeTLSPort = %d, want 45365", tlsOptions.ACME.AlternativeTLSPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplyACMEConfigFromDNSPodAliasWithTencentCredentials(t *testing.T) {
|
||||||
|
var tlsOptions option.InboundTLSOptions
|
||||||
|
ok := applyACMEConfig(&tlsOptions, &XCertConfig{
|
||||||
|
CertMode: "dns",
|
||||||
|
Domain: "code.littlediary.cn",
|
||||||
|
DNSProvider: "dnspod",
|
||||||
|
DNSEnv: map[string]string{
|
||||||
|
"TENCENTCLOUD_SECRET_ID": "sid",
|
||||||
|
"TENCENTCLOUD_SECRET_KEY": "skey",
|
||||||
|
},
|
||||||
|
}, false, "code.littlediary.cn", 443)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("applyACMEConfig() returned false")
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME == nil || tlsOptions.ACME.DNS01Challenge == nil {
|
||||||
|
t.Fatal("DNS01Challenge not configured")
|
||||||
|
}
|
||||||
|
if tlsOptions.ACME.DNS01Challenge.Provider != C.DNSProviderTencentCloud {
|
||||||
|
t.Fatalf("DNS provider = %q, want %q", tlsOptions.ACME.DNS01Challenge.Provider, C.DNSProviderTencentCloud)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergedTLSSettingsUsesTopLevelServerName(t *testing.T) {
|
||||||
|
tlsSettings := mergedTLSSettings(XInnerConfig{}, &XNodeConfig{
|
||||||
|
ServerName: "code.littlediary.cn",
|
||||||
|
})
|
||||||
|
if tlsSettings == nil {
|
||||||
|
t.Fatal("mergedTLSSettings() returned nil")
|
||||||
|
}
|
||||||
|
if tlsSettings.ServerName != "code.littlediary.cn" {
|
||||||
|
t.Fatalf("ServerName = %q, want %q", tlsSettings.ServerName, "code.littlediary.cn")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHasUsableServerTLS(t *testing.T) {
|
func TestHasUsableServerTLS(t *testing.T) {
|
||||||
if hasUsableServerTLS(option.InboundTLSOptions{}) {
|
if hasUsableServerTLS(option.InboundTLSOptions{}) {
|
||||||
t.Fatal("empty TLS options should not be usable")
|
t.Fatal("empty TLS options should not be usable")
|
||||||
|
|||||||
Reference in New Issue
Block a user