acme.go 3.5 KB

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