Files
sing-box-extended-mirror/common/tls/acme.go
T

172 lines
5.0 KiB
Go
Raw Normal View History

//go:build with_acme
2022-09-09 18:45:10 +08:00
package tls
import (
"context"
"crypto/tls"
2026-07-15 19:31:11 +08:00
"os"
2026-05-14 22:53:22 +08:00
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
2023-09-16 21:37:22 +08:00
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
2025-06-29 18:44:11 +08:00
"github.com/sagernet/sing/common/logger"
2026-07-15 19:31:11 +08:00
"github.com/sagernet/sing/service/filemanager"
2022-08-24 17:04:15 +08:00
2022-09-13 16:18:39 +08:00
"github.com/caddyserver/certmagic"
2026-01-17 06:52:43 -06:00
"github.com/libdns/acmedns"
2023-09-16 21:37:22 +08:00
"github.com/libdns/alidns"
"github.com/libdns/cloudflare"
2025-05-05 13:35:04 +08:00
"github.com/mholt/acmez/v3/acme"
2022-12-06 13:36:42 +08:00
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type acmeWrapper struct {
2026-07-15 20:50:36 +08:00
ctx context.Context
cfg *certmagic.Config
cache *certmagic.Cache
zapLogger *zap.Logger
dataDirectory string
domain []string
}
func (w *acmeWrapper) Start() error {
2026-07-15 20:50:36 +08:00
if w.dataDirectory != "" {
err := filemanager.MkdirAll(w.ctx, w.dataDirectory, 0o700)
if err != nil {
return E.Cause(err, "create ACME data directory")
}
}
config := w.cfg
cache := certmagic.NewCache(certmagic.CacheOptions{
GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
return config, nil
},
Logger: w.zapLogger,
})
config = certmagic.New(cache, *config)
w.cfg = config
w.cache = cache
return w.cfg.ManageSync(w.ctx, w.domain)
}
func (w *acmeWrapper) Close() error {
2026-07-15 20:50:36 +08:00
if w.cache != nil {
w.cache.Stop()
}
return nil
}
2026-07-15 20:50:36 +08:00
func (w *acmeWrapper) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
return w.cfg.GetCertificate(hello)
}
2025-03-29 17:24:34 +08:00
func startACME(ctx context.Context, logger logger.Logger, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, 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
}
2026-07-15 20:50:36 +08:00
var (
storage certmagic.Storage
dataDirectory string
)
if options.DataDirectory != "" {
2026-07-15 20:50:36 +08:00
dataDirectory = filemanager.BasePath(ctx, os.ExpandEnv(options.DataDirectory))
storage = &certmagic.FileStorage{
2026-07-15 19:31:11 +08:00
Path: dataDirectory,
}
2022-08-19 18:05:26 +08:00
} else {
storage = certmagic.Default.Storage
}
2025-06-29 18:44:11 +08:00
zapLogger := zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(ACMEEncoderConfig()),
&ACMELogWriter{Logger: logger},
2025-06-29 18:44:11 +08:00
zap.DebugLevel,
))
2022-08-19 18:05:26 +08:00
config := &certmagic.Config{
DefaultServerName: options.DefaultServerName,
2022-08-19 18:05:26 +08:00
Storage: storage,
2025-06-29 18:44:11 +08:00
Logger: zapLogger,
2022-08-19 18:05:26 +08:00
}
profile := options.Profile
2026-05-14 22:53:22 +08:00
if profile == "" && acmeServer == certmagic.LetsEncryptProductionCA && slices.ContainsFunc(options.Domain, certmagic.SubjectIsIP) {
profile = "shortlived"
}
2022-08-24 17:04:15 +08:00
acmeConfig := certmagic.ACMEIssuer{
CA: acmeServer,
Email: options.Email,
Agreed: true,
Profile: profile,
2022-08-24 17:04:15 +08:00
DisableHTTPChallenge: options.DisableHTTPChallenge,
DisableTLSALPNChallenge: options.DisableTLSALPNChallenge,
AltHTTPPort: int(options.AlternativeHTTPPort),
AltTLSALPNPort: int(options.AlternativeTLSPort),
2025-06-29 18:44:11 +08:00
Logger: zapLogger,
2022-08-24 17:04:15 +08:00
}
2023-09-16 21:37:22 +08:00
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
var solver certmagic.DNS01Solver
switch dnsOptions.Provider {
case C.DNSProviderAliDNS:
solver.DNSProvider = &alidns.Provider{
2025-12-21 23:11:00 +08:00
CredentialInfo: alidns.CredentialInfo{
AccessKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
AccessKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
RegionID: dnsOptions.AliDNSOptions.RegionID,
SecurityToken: dnsOptions.AliDNSOptions.SecurityToken,
},
2023-09-16 21:37:22 +08:00
}
case C.DNSProviderCloudflare:
solver.DNSProvider = &cloudflare.Provider{
2025-12-21 23:11:00 +08:00
APIToken: dnsOptions.CloudflareOptions.APIToken,
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
2023-09-16 21:37:22 +08:00
}
2026-01-17 06:52:43 -06:00
case C.DNSProviderACMEDNS:
solver.DNSProvider = &acmedns.Provider{
Username: dnsOptions.ACMEDNSOptions.Username,
Password: dnsOptions.ACMEDNSOptions.Password,
Subdomain: dnsOptions.ACMEDNSOptions.Subdomain,
ServerURL: dnsOptions.ACMEDNSOptions.ServerURL,
}
2023-09-23 17:49:56 +08:00
default:
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
2023-09-16 21:37:22 +08:00
}
2023-09-23 17:49:56 +08:00
acmeConfig.DNS01Solver = &solver
2023-09-16 21:37:22 +08:00
}
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)}
2026-07-15 20:50:36 +08:00
wrapper := &acmeWrapper{
ctx: ctx,
cfg: config,
zapLogger: zapLogger,
dataDirectory: dataDirectory,
domain: options.Domain,
}
2023-12-24 18:01:12 +08:00
var tlsConfig *tls.Config
if acmeConfig.DisableTLSALPNChallenge || acmeConfig.DNS01Solver != nil {
tlsConfig = &tls.Config{
2026-07-15 20:50:36 +08:00
GetCertificate: wrapper.GetCertificate,
2023-12-24 18:01:12 +08:00
}
} else {
tlsConfig = &tls.Config{
2026-07-15 20:50:36 +08:00
GetCertificate: wrapper.GetCertificate,
NextProtos: []string{C.ACMETLS1Protocol},
2023-12-24 18:01:12 +08:00
}
}
2026-07-15 20:50:36 +08:00
return tlsConfig, wrapper, nil
}