github.com/sagernet/sing-box@v1.9.0-rc.20/common/tls/acme.go (about) 1 //go:build with_acme 2 3 package tls 4 5 import ( 6 "context" 7 "crypto/tls" 8 "os" 9 "strings" 10 11 "github.com/sagernet/sing-box/adapter" 12 C "github.com/sagernet/sing-box/constant" 13 "github.com/sagernet/sing-box/option" 14 E "github.com/sagernet/sing/common/exceptions" 15 16 "github.com/caddyserver/certmagic" 17 "github.com/libdns/alidns" 18 "github.com/libdns/cloudflare" 19 "github.com/mholt/acmez/acme" 20 "go.uber.org/zap" 21 "go.uber.org/zap/zapcore" 22 ) 23 24 type acmeWrapper struct { 25 ctx context.Context 26 cfg *certmagic.Config 27 cache *certmagic.Cache 28 domain []string 29 } 30 31 func (w *acmeWrapper) Start() error { 32 return w.cfg.ManageSync(w.ctx, w.domain) 33 } 34 35 func (w *acmeWrapper) Close() error { 36 w.cache.Stop() 37 return nil 38 } 39 40 func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Config, adapter.Service, error) { 41 var acmeServer string 42 switch options.Provider { 43 case "", "letsencrypt": 44 acmeServer = certmagic.LetsEncryptProductionCA 45 case "zerossl": 46 acmeServer = certmagic.ZeroSSLProductionCA 47 default: 48 if !strings.HasPrefix(options.Provider, "https://") { 49 return nil, nil, E.New("unsupported acme provider: " + options.Provider) 50 } 51 acmeServer = options.Provider 52 } 53 var storage certmagic.Storage 54 if options.DataDirectory != "" { 55 storage = &certmagic.FileStorage{ 56 Path: options.DataDirectory, 57 } 58 } else { 59 storage = certmagic.Default.Storage 60 } 61 config := &certmagic.Config{ 62 DefaultServerName: options.DefaultServerName, 63 Storage: storage, 64 Logger: zap.New(zapcore.NewCore( 65 zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()), 66 os.Stderr, 67 zap.InfoLevel, 68 )), 69 } 70 acmeConfig := certmagic.ACMEIssuer{ 71 CA: acmeServer, 72 Email: options.Email, 73 Agreed: true, 74 DisableHTTPChallenge: options.DisableHTTPChallenge, 75 DisableTLSALPNChallenge: options.DisableTLSALPNChallenge, 76 AltHTTPPort: int(options.AlternativeHTTPPort), 77 AltTLSALPNPort: int(options.AlternativeTLSPort), 78 Logger: config.Logger, 79 } 80 if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" { 81 var solver certmagic.DNS01Solver 82 switch dnsOptions.Provider { 83 case C.DNSProviderAliDNS: 84 solver.DNSProvider = &alidns.Provider{ 85 AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID, 86 AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret, 87 RegionID: dnsOptions.AliDNSOptions.RegionID, 88 } 89 case C.DNSProviderCloudflare: 90 solver.DNSProvider = &cloudflare.Provider{ 91 APIToken: dnsOptions.CloudflareOptions.APIToken, 92 } 93 default: 94 return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider) 95 } 96 acmeConfig.DNS01Solver = &solver 97 } 98 if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" { 99 acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount) 100 } 101 config.Issuers = []certmagic.Issuer{certmagic.NewACMEIssuer(config, acmeConfig)} 102 cache := certmagic.NewCache(certmagic.CacheOptions{ 103 GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) { 104 return config, nil 105 }, 106 }) 107 config = certmagic.New(cache, *config) 108 var tlsConfig *tls.Config 109 if acmeConfig.DisableTLSALPNChallenge || acmeConfig.DNS01Solver != nil { 110 tlsConfig = &tls.Config{ 111 GetCertificate: config.GetCertificate, 112 } 113 } else { 114 tlsConfig = &tls.Config{ 115 GetCertificate: config.GetCertificate, 116 NextProtos: []string{ACMETLS1Protocol}, 117 } 118 } 119 return tlsConfig, &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil 120 }