修复AnyTLS无法配置证书的错误

This commit is contained in:
CN-JS-HuiBai
2026-04-15 18:57:33 +08:00
parent abc7c0d933
commit a2a3ba12b3
8 changed files with 243 additions and 42 deletions

View File

@@ -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,

View File

@@ -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))
}
}

2
go.mod
View File

@@ -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

View File

@@ -37,25 +37,34 @@ type _ACMEProviderDNS01ChallengeOptions struct {
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:

View File

@@ -32,25 +32,34 @@ type _ACMEDNS01ChallengeOptions struct {
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"`
}

View File

@@ -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,

View File

@@ -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,
}
if certConfig != nil {
acmeOptions.Email = strings.TrimSpace(certConfig.Email)
}
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 := strings.ToLower(strings.TrimSpace(certConfig.DNSProvider))
if dnsProvider == "" {
return false
}
acmeOptions.DisableHTTPChallenge = true
acmeOptions.DisableTLSALPNChallenge = true
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)

View File

@@ -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")