1
0

acme.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. //go:build with_acme
  2. package tls
  3. import (
  4. "context"
  5. "crypto/tls"
  6. "strings"
  7. "github.com/sagernet/sing-box/adapter"
  8. C "github.com/sagernet/sing-box/constant"
  9. "github.com/sagernet/sing-box/option"
  10. E "github.com/sagernet/sing/common/exceptions"
  11. "github.com/sagernet/sing/common/logger"
  12. "github.com/caddyserver/certmagic"
  13. "github.com/libdns/alidns"
  14. "github.com/libdns/cloudflare"
  15. "github.com/mholt/acmez/v3/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. type acmeLogWriter struct {
  33. logger logger.Logger
  34. }
  35. func (w *acmeLogWriter) Write(p []byte) (n int, err error) {
  36. logLine := strings.ReplaceAll(string(p), " ", ": ")
  37. switch {
  38. case strings.HasPrefix(logLine, "error: "):
  39. w.logger.Error(logLine[7:])
  40. case strings.HasPrefix(logLine, "warn: "):
  41. w.logger.Warn(logLine[6:])
  42. case strings.HasPrefix(logLine, "info: "):
  43. w.logger.Info(logLine[6:])
  44. case strings.HasPrefix(logLine, "debug: "):
  45. w.logger.Debug(logLine[7:])
  46. default:
  47. w.logger.Debug(logLine)
  48. }
  49. return len(p), nil
  50. }
  51. func (w *acmeLogWriter) Sync() error {
  52. return nil
  53. }
  54. func encoderConfig() zapcore.EncoderConfig {
  55. config := zap.NewProductionEncoderConfig()
  56. config.TimeKey = zapcore.OmitKey
  57. return config
  58. }
  59. func startACME(ctx context.Context, logger logger.Logger, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, error) {
  60. var acmeServer string
  61. switch options.Provider {
  62. case "", "letsencrypt":
  63. acmeServer = certmagic.LetsEncryptProductionCA
  64. case "zerossl":
  65. acmeServer = certmagic.ZeroSSLProductionCA
  66. default:
  67. if !strings.HasPrefix(options.Provider, "https://") {
  68. return nil, nil, E.New("unsupported acme provider: " + options.Provider)
  69. }
  70. acmeServer = options.Provider
  71. }
  72. var storage certmagic.Storage
  73. if options.DataDirectory != "" {
  74. storage = &certmagic.FileStorage{
  75. Path: options.DataDirectory,
  76. }
  77. } else {
  78. storage = certmagic.Default.Storage
  79. }
  80. zapLogger := zap.New(zapcore.NewCore(
  81. zapcore.NewConsoleEncoder(encoderConfig()),
  82. &acmeLogWriter{logger: logger},
  83. zap.DebugLevel,
  84. ))
  85. config := &certmagic.Config{
  86. DefaultServerName: options.DefaultServerName,
  87. Storage: storage,
  88. Logger: zapLogger,
  89. }
  90. acmeConfig := certmagic.ACMEIssuer{
  91. CA: acmeServer,
  92. Email: options.Email,
  93. Agreed: true,
  94. DisableHTTPChallenge: options.DisableHTTPChallenge,
  95. DisableTLSALPNChallenge: options.DisableTLSALPNChallenge,
  96. AltHTTPPort: int(options.AlternativeHTTPPort),
  97. AltTLSALPNPort: int(options.AlternativeTLSPort),
  98. Logger: zapLogger,
  99. }
  100. if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
  101. var solver certmagic.DNS01Solver
  102. switch dnsOptions.Provider {
  103. case C.DNSProviderAliDNS:
  104. solver.DNSProvider = &alidns.Provider{
  105. AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
  106. AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
  107. RegionID: dnsOptions.AliDNSOptions.RegionID,
  108. }
  109. case C.DNSProviderCloudflare:
  110. solver.DNSProvider = &cloudflare.Provider{
  111. APIToken: dnsOptions.CloudflareOptions.APIToken,
  112. }
  113. default:
  114. return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
  115. }
  116. acmeConfig.DNS01Solver = &solver
  117. }
  118. if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" {
  119. acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
  120. }
  121. config.Issuers = []certmagic.Issuer{certmagic.NewACMEIssuer(config, acmeConfig)}
  122. cache := certmagic.NewCache(certmagic.CacheOptions{
  123. GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
  124. return config, nil
  125. },
  126. Logger: zapLogger,
  127. })
  128. config = certmagic.New(cache, *config)
  129. var tlsConfig *tls.Config
  130. if acmeConfig.DisableTLSALPNChallenge || acmeConfig.DNS01Solver != nil {
  131. tlsConfig = &tls.Config{
  132. GetCertificate: config.GetCertificate,
  133. }
  134. } else {
  135. tlsConfig = &tls.Config{
  136. GetCertificate: config.GetCertificate,
  137. NextProtos: []string{ACMETLS1Protocol},
  138. }
  139. }
  140. return tlsConfig, &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil
  141. }