sing-box/common/tls/acme.go

121 lines
3.5 KiB
Go
Raw Normal View History

//go:build with_acme
2022-09-09 18:45:10 +08:00
package tls
import (
"context"
"crypto/tls"
2022-12-06 13:36:42 +08:00
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
2022-08-24 17:04:15 +08:00
2022-09-13 16:18:39 +08:00
"github.com/caddyserver/certmagic"
"github.com/libdns/alidns"
"github.com/libdns/cloudflare"
2022-08-24 17:04:15 +08:00
"github.com/mholt/acmez/acme"
2022-12-06 13:36:42 +08:00
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type acmeWrapper struct {
ctx context.Context
cfg *certmagic.Config
2023-07-20 20:03:20 +08:00
cache *certmagic.Cache
domain []string
}
func (w *acmeWrapper) Start() error {
return w.cfg.ManageSync(w.ctx, w.domain)
}
func (w *acmeWrapper) Close() error {
2023-07-20 20:03:20 +08:00
w.cache.Stop()
return nil
}
2022-08-21 19:36:08 +08:00
func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Config, adapter.Service, error) {
var acmeServer string
switch options.Provider {
case "", "letsencrypt":
acmeServer = certmagic.LetsEncryptProductionCA
case "zerossl":
acmeServer = certmagic.ZeroSSLProductionCA
default:
if !strings.HasPrefix(options.Provider, "https://") {
return nil, nil, E.New("unsupported acme provider: " + options.Provider)
}
acmeServer = options.Provider
}
var storage certmagic.Storage
if options.DataDirectory != "" {
storage = &certmagic.FileStorage{
Path: options.DataDirectory,
}
2022-08-19 18:05:26 +08:00
} else {
storage = certmagic.Default.Storage
}
2022-08-19 18:05:26 +08:00
config := &certmagic.Config{
DefaultServerName: options.DefaultServerName,
2022-08-19 18:05:26 +08:00
Storage: storage,
2022-12-06 13:36:42 +08:00
Logger: zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()),
os.Stderr,
zap.InfoLevel,
)),
2022-08-19 18:05:26 +08:00
}
2022-08-24 17:04:15 +08:00
acmeConfig := certmagic.ACMEIssuer{
CA: acmeServer,
Email: options.Email,
Agreed: true,
DisableHTTPChallenge: options.DisableHTTPChallenge,
DisableTLSALPNChallenge: options.DisableTLSALPNChallenge,
AltHTTPPort: int(options.AlternativeHTTPPort),
AltTLSALPNPort: int(options.AlternativeTLSPort),
2022-12-06 13:36:42 +08:00
Logger: config.Logger,
2022-08-24 17:04:15 +08:00
}
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
var solver certmagic.DNS01Solver
switch dnsOptions.Provider {
case C.DNSProviderAliDNS:
solver.DNSProvider = &alidns.Provider{
AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
RegionID: dnsOptions.AliDNSOptions.RegionID,
}
case C.DNSProviderCloudflare:
solver.DNSProvider = &cloudflare.Provider{
APIToken: dnsOptions.CloudflareOptions.APIToken,
}
2023-09-23 17:49:56 +08:00
default:
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
}
2023-09-23 17:49:56 +08:00
acmeConfig.DNS01Solver = &solver
}
2022-12-06 13:36:42 +08:00
if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" {
2022-08-24 17:04:15 +08:00
acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
2022-08-19 18:05:26 +08:00
}
2022-08-24 17:04:15 +08:00
config.Issuers = []certmagic.Issuer{certmagic.NewACMEIssuer(config, acmeConfig)}
2023-07-20 20:03:20 +08:00
cache := certmagic.NewCache(certmagic.CacheOptions{
2022-08-19 18:05:26 +08:00
GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
return config, nil
},
2023-07-20 20:03:20 +08:00
})
config = certmagic.New(cache, *config)
2023-12-24 18:01:12 +08:00
var tlsConfig *tls.Config
if acmeConfig.DisableTLSALPNChallenge || acmeConfig.DNS01Solver != nil {
tlsConfig = &tls.Config{
GetCertificate: config.GetCertificate,
}
} else {
tlsConfig = &tls.Config{
GetCertificate: config.GetCertificate,
NextProtos: []string{ACMETLS1Protocol},
}
}
return tlsConfig, &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil
}