diff --git a/common/tls/acme.go b/common/tls/acme.go index d576fc6b..02e87e5f 100644 --- a/common/tls/acme.go +++ b/common/tls/acme.go @@ -17,6 +17,8 @@ import ( "github.com/libdns/acmedns" "github.com/libdns/alidns" "github.com/libdns/cloudflare" + "github.com/libdns/dnspod" + "github.com/libdns/tencentcloud" "github.com/mholt/acmez/v3/acme" "go.uber.org/zap" "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 != "" { var solver certmagic.DNS01Solver - switch dnsOptions.Provider { + switch C.NormalizeACMEDNSProvider(dnsOptions.Provider) { case C.DNSProviderAliDNS: solver.DNSProvider = &alidns.Provider{ CredentialInfo: alidns.CredentialInfo{ @@ -96,6 +98,17 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound APIToken: dnsOptions.CloudflareOptions.APIToken, 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: solver.DNSProvider = &acmedns.Provider{ Username: dnsOptions.ACMEDNSOptions.Username, diff --git a/constant/dns.go b/constant/dns.go index c7cd0d03..d30f56df 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -1,5 +1,7 @@ package constant +import "strings" + const ( DefaultDNSTTL = 600 ) @@ -33,4 +35,25 @@ const ( DNSProviderAliDNS = "alidns" DNSProviderCloudflare = "cloudflare" 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)) + } +} diff --git a/go.mod b/go.mod index 4a9c656a..3b54027a 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,9 @@ require ( github.com/libdns/acmedns v0.5.0 github.com/libdns/alidns v1.0.6 github.com/libdns/cloudflare v0.2.2 + github.com/libdns/dnspod v0.0.3 github.com/libdns/libdns v1.1.1 + github.com/libdns/tencentcloud v1.4.3 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/mdlayher/netlink v1.9.0 github.com/metacubex/utls v1.8.4 diff --git a/option/acme.go b/option/acme.go index ea9349b7..4eb8638d 100644 --- a/option/acme.go +++ b/option/acme.go @@ -28,34 +28,43 @@ type ACMECertificateProviderOptions struct { } type _ACMEProviderDNS01ChallengeOptions struct { - TTL badoption.Duration `json:"ttl,omitempty"` - PropagationDelay badoption.Duration `json:"propagation_delay,omitempty"` - PropagationTimeout badoption.Duration `json:"propagation_timeout,omitempty"` - Resolvers badoption.Listable[string] `json:"resolvers,omitempty"` - OverrideDomain string `json:"override_domain,omitempty"` - Provider string `json:"provider,omitempty"` - AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"` - CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"` - ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"` + TTL badoption.Duration `json:"ttl,omitempty"` + PropagationDelay badoption.Duration `json:"propagation_delay,omitempty"` + PropagationTimeout badoption.Duration `json:"propagation_timeout,omitempty"` + Resolvers badoption.Listable[string] `json:"resolvers,omitempty"` + OverrideDomain string `json:"override_domain,omitempty"` + Provider string `json:"provider,omitempty"` + AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"` + CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"` + ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"` + TencentCloudOptions ACMEDNS01TencentCloudOptions `json:"-"` + DNSPodOptions ACMEDNS01DNSPodOptions `json:"-"` } type ACMEProviderDNS01ChallengeOptions _ACMEProviderDNS01ChallengeOptions func (o ACMEProviderDNS01ChallengeOptions) MarshalJSON() ([]byte, error) { + provider := C.NormalizeACMEDNSProvider(o.Provider) var v any - switch o.Provider { + switch provider { case C.DNSProviderAliDNS: v = o.AliDNSOptions case C.DNSProviderCloudflare: v = o.CloudflareOptions case C.DNSProviderACMEDNS: v = o.ACMEDNSOptions + case C.DNSProviderTencentCloud: + v = o.TencentCloudOptions + case C.DNSProviderDNSPod: + v = o.DNSPodOptions case "": return nil, E.New("missing provider type") default: 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 { @@ -63,6 +72,7 @@ func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { if err != nil { return err } + o.Provider = C.NormalizeACMEDNSProvider(o.Provider) var v any switch o.Provider { case C.DNSProviderAliDNS: @@ -71,6 +81,10 @@ func (o *ACMEProviderDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { v = &o.CloudflareOptions case C.DNSProviderACMEDNS: v = &o.ACMEDNSOptions + case C.DNSProviderTencentCloud: + v = &o.TencentCloudOptions + case C.DNSProviderDNSPod: + v = &o.DNSPodOptions case "": return E.New("missing provider type") default: diff --git a/option/tls_acme.go b/option/tls_acme.go index 6dd8fa70..47ab9b19 100644 --- a/option/tls_acme.go +++ b/option/tls_acme.go @@ -28,29 +28,38 @@ type ACMEExternalAccountOptions struct { } type _ACMEDNS01ChallengeOptions struct { - Provider string `json:"provider,omitempty"` - AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"` - CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"` - ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"` + Provider string `json:"provider,omitempty"` + AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"` + CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"` + ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"` + TencentCloudOptions ACMEDNS01TencentCloudOptions `json:"-"` + DNSPodOptions ACMEDNS01DNSPodOptions `json:"-"` } type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) { + provider := C.NormalizeACMEDNSProvider(o.Provider) var v any - switch o.Provider { + switch provider { case C.DNSProviderAliDNS: v = o.AliDNSOptions case C.DNSProviderCloudflare: v = o.CloudflareOptions case C.DNSProviderACMEDNS: v = o.ACMEDNSOptions + case C.DNSProviderTencentCloud: + v = o.TencentCloudOptions + case C.DNSProviderDNSPod: + v = o.DNSPodOptions case "": return nil, E.New("missing provider type") default: 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 { @@ -58,6 +67,7 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { if err != nil { return err } + o.Provider = C.NormalizeACMEDNSProvider(o.Provider) var v any switch o.Provider { case C.DNSProviderAliDNS: @@ -66,6 +76,10 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { v = &o.CloudflareOptions case C.DNSProviderACMEDNS: v = &o.ACMEDNSOptions + case C.DNSProviderTencentCloud: + v = &o.TencentCloudOptions + case C.DNSProviderDNSPod: + v = &o.DNSPodOptions default: return E.New("unknown provider type: " + o.Provider) } @@ -94,3 +108,14 @@ type ACMEDNS01ACMEDNSOptions struct { Subdomain string `json:"subdomain,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"` +} diff --git a/service/acme/service.go b/service/acme/service.go index 8286a197..fd346e46 100644 --- a/service/acme/service.go +++ b/service/acme/service.go @@ -30,7 +30,9 @@ import ( "github.com/caddyserver/zerossl" "github.com/libdns/alidns" "github.com/libdns/cloudflare" + "github.com/libdns/dnspod" "github.com/libdns/libdns" + "github.com/libdns/tencentcloud" "github.com/mholt/acmez/v3/acme" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -224,7 +226,7 @@ func newDNSSolver(dnsOptions *option.ACMEProviderDNS01ChallengeOptions, logger * Logger: logger.Named("dns_manager"), }, } - switch dnsOptions.Provider { + switch C.NormalizeACMEDNSProvider(dnsOptions.Provider) { case C.DNSProviderAliDNS: solver.DNSProvider = &alidns.Provider{ CredentialInfo: alidns.CredentialInfo{ @@ -240,6 +242,17 @@ func newDNSSolver(dnsOptions *option.ACMEProviderDNS01ChallengeOptions, logger * ZoneToken: dnsOptions.CloudflareOptions.ZoneToken, 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: solver.DNSProvider = &acmeDNSProvider{ username: dnsOptions.ACMEDNSOptions.Username, diff --git a/service/xboard/service.go b/service/xboard/service.go index 40df7cd0..d2d152ba 100644 --- a/service/xboard/service.go +++ b/service/xboard/service.go @@ -467,9 +467,9 @@ func hasUsableServerTLS(tlsOptions option.InboundTLSOptions) bool { (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 { - return false + return false, "acme disabled: no auto_tls and no cert_config" } mode := "" if certConfig != nil { @@ -479,54 +479,98 @@ func applyACMEConfig(tlsOptions *option.InboundTLSOptions, certConfig *XCertConf mode = "http" } if mode != "http" && mode != "dns" { - return false + return false, "unsupported cert_mode: " + mode } domain = strings.TrimSpace(domain) if domain == "" { - return false + return false, "missing domain/server_name for ACME" } acmeOptions := &option.InboundACMEOptions{ - Domain: badoption.Listable[string]{domain}, - DataDirectory: "acme", - DefaultServerName: domain, - DisableHTTPChallenge: true, + Domain: badoption.Listable[string]{domain}, + DataDirectory: "acme", + DefaultServerName: domain, } if certConfig != nil { acmeOptions.Email = strings.TrimSpace(certConfig.Email) } - if listenPort > 0 && listenPort != 443 { - acmeOptions.AlternativeTLSPort = uint16(listenPort) - } - if mode == "dns" && certConfig != nil { - dnsProvider := strings.ToLower(strings.TrimSpace(certConfig.DNSProvider)) - if dnsProvider == "" { - return false + switch mode { + case "http": + acmeOptions.DisableHTTPChallenge = false + acmeOptions.DisableTLSALPNChallenge = true + if certConfig != nil && certConfig.HTTPPort > 0 && certConfig.HTTPPort != 80 { + acmeOptions.AlternativeHTTPPort = uint16(certConfig.HTTPPort) } + case "dns": acmeOptions.DisableHTTPChallenge = 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} switch dnsProvider { case C.DNSProviderCloudflare: 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"]) + 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: 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.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"]) + 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: dns01.ACMEDNSOptions.Username = certConfig.DNSEnv["ACMEDNS_USERNAME"] dns01.ACMEDNSOptions.Password = certConfig.DNSEnv["ACMEDNS_PASSWORD"] dns01.ACMEDNSOptions.Subdomain = certConfig.DNSEnv["ACMEDNS_SUBDOMAIN"] 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: - return false + return false, "unsupported dns_provider: " + dnsProvider } acmeOptions.DNS01Challenge = dns01 } 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 { @@ -1067,11 +1111,12 @@ func (s *Service) setupNode() error { autoTLSDomain = tlsSettings.ServerName } hasACME := false + acmeReason := "" 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" { - 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 { s.logger.Info("Xboard ACME configured for domain ", autoTLSDomain) diff --git a/service/xboard/service_test.go b/service/xboard/service_test.go index 9ae25d48..3ec11d67 100644 --- a/service/xboard/service_test.go +++ b/service/xboard/service_test.go @@ -3,6 +3,7 @@ package xboard import ( "testing" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "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" { t.Fatalf("ACME domains = %+v", tlsOptions.ACME.Domain) } - if tlsOptions.ACME.AlternativeTLSPort != 8443 { - t.Fatalf("AlternativeTLSPort = %d, want 8443", tlsOptions.ACME.AlternativeTLSPort) + if tlsOptions.ACME.DisableHTTPChallenge { + t.Fatal("DisableHTTPChallenge should be false for auto_tls/http mode") } - if !tlsOptions.ACME.DisableHTTPChallenge { - t.Fatal("DisableHTTPChallenge should be true for inline ACME") + if !tlsOptions.ACME.DisableTLSALPNChallenge { + 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) { if hasUsableServerTLS(option.InboundTLSOptions{}) { t.Fatal("empty TLS options should not be usable")