mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
1 Commits
unstable
...
v1.14.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
734f3c9a21 |
14
adapter/certificate_provider.go
Normal file
14
adapter/certificate_provider.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
type CertificateProvider interface {
|
||||
GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
|
||||
}
|
||||
|
||||
type ACMECertificateProvider interface {
|
||||
CertificateProvider
|
||||
GetACMENextProtos() []string
|
||||
}
|
||||
36
box.go
36
box.go
@@ -272,6 +272,24 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize inbound[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, serviceOptions := range options.Services {
|
||||
var tag string
|
||||
if serviceOptions.Tag != "" {
|
||||
tag = serviceOptions.Tag
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
err = serviceManager.Create(
|
||||
ctx,
|
||||
logFactory.NewLogger(F.ToString("service/", serviceOptions.Type, "[", tag, "]")),
|
||||
tag,
|
||||
serviceOptions.Type,
|
||||
serviceOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize service[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, outboundOptions := range options.Outbounds {
|
||||
var tag string
|
||||
if outboundOptions.Tag != "" {
|
||||
@@ -298,24 +316,6 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize outbound[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, serviceOptions := range options.Services {
|
||||
var tag string
|
||||
if serviceOptions.Tag != "" {
|
||||
tag = serviceOptions.Tag
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
err = serviceManager.Create(
|
||||
ctx,
|
||||
logFactory.NewLogger(F.ToString("service/", serviceOptions.Type, "[", tag, "]")),
|
||||
tag,
|
||||
serviceOptions.Type,
|
||||
serviceOptions.Options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize service[", i, "]")
|
||||
}
|
||||
}
|
||||
outboundManager.Initialize(func() (adapter.Outbound, error) {
|
||||
return direct.NewOutbound(
|
||||
ctx,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
package tls
|
||||
|
||||
const ACMETLS1Protocol = "acme-tls/1"
|
||||
import C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
const ACMETLS1Protocol = C.ACMETLS1Protocol
|
||||
|
||||
@@ -18,14 +18,77 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
|
||||
type managedCertificateProvider interface {
|
||||
adapter.CertificateProvider
|
||||
Start() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type acmeServiceCertificateProvider struct {
|
||||
ctx context.Context
|
||||
serviceTag string
|
||||
once sync.Once
|
||||
provider adapter.ACMECertificateProvider
|
||||
resolveErr error
|
||||
}
|
||||
|
||||
func (p *acmeServiceCertificateProvider) Start() error {
|
||||
_, err := p.resolveProvider()
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *acmeServiceCertificateProvider) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *acmeServiceCertificateProvider) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
provider, err := p.resolveProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return provider.GetCertificate(hello)
|
||||
}
|
||||
|
||||
func (p *acmeServiceCertificateProvider) GetACMENextProtos() []string {
|
||||
provider, err := p.resolveProvider()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return provider.GetACMENextProtos()
|
||||
}
|
||||
|
||||
func (p *acmeServiceCertificateProvider) resolveProvider() (adapter.ACMECertificateProvider, error) {
|
||||
p.once.Do(func() {
|
||||
serviceManager := service.FromContext[adapter.ServiceManager](p.ctx)
|
||||
if serviceManager == nil {
|
||||
p.resolveErr = E.New("missing service manager in context")
|
||||
return
|
||||
}
|
||||
providerService, found := serviceManager.Get(p.serviceTag)
|
||||
if !found {
|
||||
p.resolveErr = E.New("certificate provider service not found: ", p.serviceTag)
|
||||
return
|
||||
}
|
||||
provider, ok := providerService.(adapter.ACMECertificateProvider)
|
||||
if !ok {
|
||||
p.resolveErr = E.New("service ", p.serviceTag, " is not an ACME certificate service")
|
||||
return
|
||||
}
|
||||
p.provider = provider
|
||||
})
|
||||
return p.provider, p.resolveErr
|
||||
}
|
||||
|
||||
type STDServerConfig struct {
|
||||
access sync.RWMutex
|
||||
config *tls.Config
|
||||
logger log.Logger
|
||||
certificateProvider managedCertificateProvider
|
||||
acmeService adapter.SimpleLifecycle
|
||||
certificate []byte
|
||||
key []byte
|
||||
@@ -53,18 +116,17 @@ func (c *STDServerConfig) SetServerName(serverName string) {
|
||||
func (c *STDServerConfig) NextProtos() []string {
|
||||
c.access.RLock()
|
||||
defer c.access.RUnlock()
|
||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
if c.hasACMEALPN() && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
return c.config.NextProtos[1:]
|
||||
} else {
|
||||
return c.config.NextProtos
|
||||
}
|
||||
return c.config.NextProtos
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
config := c.config.Clone()
|
||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
if c.hasACMEALPN() && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||
config.NextProtos = append(c.config.NextProtos[:1], nextProto...)
|
||||
} else {
|
||||
config.NextProtos = nextProto
|
||||
@@ -72,6 +134,18 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.config = config
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) hasACMEALPN() bool {
|
||||
if c.acmeService != nil {
|
||||
return true
|
||||
}
|
||||
if c.certificateProvider != nil {
|
||||
if acmeProvider, isACME := c.certificateProvider.(adapter.ACMECertificateProvider); isACME {
|
||||
return len(acmeProvider.GetACMENextProtos()) > 0
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) STDConfig() (*STDConfig, error) {
|
||||
return c.config, nil
|
||||
}
|
||||
@@ -91,15 +165,24 @@ func (c *STDServerConfig) Clone() Config {
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Start() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Start()
|
||||
} else {
|
||||
err := c.startWatcher()
|
||||
if c.certificateProvider != nil {
|
||||
err := c.certificateProvider.Start()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
c.updateProviderNextProtos()
|
||||
}
|
||||
if c.acmeService != nil {
|
||||
err := c.acmeService.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) startWatcher() error {
|
||||
@@ -203,23 +286,58 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Close() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Close()
|
||||
return common.Close(c.certificateProvider, c.acmeService, c.watcher)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) updateProviderNextProtos() {
|
||||
if c.certificateProvider == nil {
|
||||
return
|
||||
}
|
||||
if c.watcher != nil {
|
||||
return c.watcher.Close()
|
||||
acmeProvider, isACME := c.certificateProvider.(adapter.ACMECertificateProvider)
|
||||
if !isACME {
|
||||
return
|
||||
}
|
||||
return nil
|
||||
nextProtos := acmeProvider.GetACMENextProtos()
|
||||
if len(nextProtos) == 0 {
|
||||
return
|
||||
}
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
config := c.config.Clone()
|
||||
mergedNextProtos := append([]string{}, nextProtos...)
|
||||
for _, nextProto := range config.NextProtos {
|
||||
if !common.Contains(mergedNextProtos, nextProto) {
|
||||
mergedNextProtos = append(mergedNextProtos, nextProto)
|
||||
}
|
||||
}
|
||||
config.NextProtos = mergedNextProtos
|
||||
c.config = config
|
||||
}
|
||||
|
||||
func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
if options.CertificateProvider != nil && options.ACME != nil {
|
||||
return nil, E.New("certificate_provider and acme are mutually exclusive")
|
||||
}
|
||||
var tlsConfig *tls.Config
|
||||
var certificateProvider managedCertificateProvider
|
||||
var acmeService adapter.SimpleLifecycle
|
||||
var err error
|
||||
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||
if options.CertificateProvider != nil {
|
||||
certificateProvider, err = newCertificateProvider(ctx, options.CertificateProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig = &tls.Config{
|
||||
GetCertificate: certificateProvider.GetCertificate,
|
||||
}
|
||||
if options.Insecure {
|
||||
return nil, errInsecureUnused
|
||||
}
|
||||
} else if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||
logger.Warn("inline acme configuration is deprecated, use certificate_provider with an ACME service instead")
|
||||
//nolint:staticcheck
|
||||
tlsConfig, acmeService, err = startACME(ctx, logger, common.PtrValueOrDefault(options.ACME))
|
||||
if err != nil {
|
||||
@@ -272,7 +390,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
||||
certificate []byte
|
||||
key []byte
|
||||
)
|
||||
if acmeService == nil {
|
||||
if certificateProvider == nil && acmeService == nil {
|
||||
if len(options.Certificate) > 0 {
|
||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||
} else if options.CertificatePath != "" {
|
||||
@@ -360,6 +478,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
||||
serverConfig := &STDServerConfig{
|
||||
config: tlsConfig,
|
||||
logger: logger,
|
||||
certificateProvider: certificateProvider,
|
||||
acmeService: acmeService,
|
||||
certificate: certificate,
|
||||
key: key,
|
||||
@@ -387,3 +506,19 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func newCertificateProvider(ctx context.Context, options *option.CertificateProviderOptions) (managedCertificateProvider, error) {
|
||||
switch options.Type {
|
||||
case C.TypeACME:
|
||||
serviceTag := options.ACMEOptions.Service
|
||||
if serviceTag == "" {
|
||||
return nil, E.New("missing ACME service tag in certificate_provider")
|
||||
}
|
||||
return &acmeServiceCertificateProvider{
|
||||
ctx: ctx,
|
||||
serviceTag: serviceTag,
|
||||
}, nil
|
||||
default:
|
||||
return nil, E.New("unknown certificate provider type: ", options.Type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const (
|
||||
TypeCCM = "ccm"
|
||||
TypeOCM = "ocm"
|
||||
TypeOOMKiller = "oom-killer"
|
||||
TypeACME = "acme"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
3
constant/tls.go
Normal file
3
constant/tls.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package constant
|
||||
|
||||
const ACMETLS1Protocol = "acme-tls/1"
|
||||
12
include/acme.go
Normal file
12
include/acme.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build with_acme
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter/service"
|
||||
"github.com/sagernet/sing-box/service/acme"
|
||||
)
|
||||
|
||||
func registerACMEService(registry *service.Registry) {
|
||||
acme.RegisterService(registry)
|
||||
}
|
||||
20
include/acme_stub.go
Normal file
20
include/acme_stub.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !with_acme
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func registerACMEService(registry *service.Registry) {
|
||||
service.Register[option.ACMEServiceOptions](registry, C.TypeACME, func(ctx context.Context, logger log.ContextLogger, tag string, options option.ACMEServiceOptions) (adapter.Service, error) {
|
||||
return nil, E.New(`ACME is not included in this build, rebuild with -tags with_acme`)
|
||||
})
|
||||
}
|
||||
@@ -130,6 +130,7 @@ func ServiceRegistry() *service.Registry {
|
||||
|
||||
resolved.RegisterService(registry)
|
||||
ssmapi.RegisterService(registry)
|
||||
registerACMEService(registry)
|
||||
|
||||
registerDERPService(registry)
|
||||
registerCCMService(registry)
|
||||
|
||||
17
option/acme.go
Normal file
17
option/acme.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type ACMEServiceOptions struct {
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DataDirectory string `json:"data_directory,omitempty"`
|
||||
DefaultServerName string `json:"default_server_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
DisableHTTPChallenge bool `json:"disable_http_challenge,omitempty"`
|
||||
DisableTLSALPNChallenge bool `json:"disable_tls_alpn_challenge,omitempty"`
|
||||
AlternativeHTTPPort uint16 `json:"alternative_http_port,omitempty"`
|
||||
AlternativeTLSPort uint16 `json:"alternative_tls_port,omitempty"`
|
||||
ExternalAccount *ACMEExternalAccountOptions `json:"external_account,omitempty"`
|
||||
DNS01Challenge *ACMEDNS01ChallengeOptions `json:"dns01_challenge,omitempty"`
|
||||
}
|
||||
53
option/certificate_provider.go
Normal file
53
option/certificate_provider.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
)
|
||||
|
||||
type _CertificateProviderOptions struct {
|
||||
Type string `json:"type"`
|
||||
ACMEOptions CertificateProviderACMEOptions `json:"-"`
|
||||
}
|
||||
|
||||
type CertificateProviderOptions _CertificateProviderOptions
|
||||
|
||||
func (o CertificateProviderOptions) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch o.Type {
|
||||
case C.TypeACME:
|
||||
v = o.ACMEOptions
|
||||
case "":
|
||||
return nil, E.New("missing certificate provider type")
|
||||
default:
|
||||
return nil, E.New("unknown certificate provider type: ", o.Type)
|
||||
}
|
||||
return badjson.MarshallObjects((_CertificateProviderOptions)(o), v)
|
||||
}
|
||||
|
||||
func (o *CertificateProviderOptions) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_CertificateProviderOptions)(o))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var v any
|
||||
switch o.Type {
|
||||
case C.TypeACME:
|
||||
v = &o.ACMEOptions
|
||||
case "":
|
||||
return E.New("missing certificate provider type")
|
||||
default:
|
||||
return E.New("unknown certificate provider type: ", o.Type)
|
||||
}
|
||||
err = badjson.UnmarshallExcluded(bytes, (*_CertificateProviderOptions)(o), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CertificateProviderACMEOptions struct {
|
||||
Service string `json:"service"`
|
||||
}
|
||||
@@ -28,9 +28,12 @@ type InboundTLSOptions struct {
|
||||
KeyPath string `json:"key_path,omitempty"`
|
||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||
CertificateProvider *CertificateProviderOptions `json:"certificate_provider,omitempty"`
|
||||
|
||||
// Deprecated: use certificate_provider
|
||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||
}
|
||||
|
||||
type ClientAuthType tls.ClientAuthType
|
||||
|
||||
43
service/acme/logger.go
Normal file
43
service/acme/logger.go
Normal file
@@ -0,0 +1,43 @@
|
||||
//go:build with_acme
|
||||
|
||||
package acme
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type logWriter struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func (w *logWriter) Write(p []byte) (n int, err error) {
|
||||
logLine := strings.ReplaceAll(string(p), " ", ": ")
|
||||
switch {
|
||||
case strings.HasPrefix(logLine, "error: "):
|
||||
w.logger.Error(logLine[7:])
|
||||
case strings.HasPrefix(logLine, "warn: "):
|
||||
w.logger.Warn(logLine[6:])
|
||||
case strings.HasPrefix(logLine, "info: "):
|
||||
w.logger.Info(logLine[6:])
|
||||
case strings.HasPrefix(logLine, "debug: "):
|
||||
w.logger.Debug(logLine[7:])
|
||||
default:
|
||||
w.logger.Debug(logLine)
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *logWriter) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func encoderConfig() zapcore.EncoderConfig {
|
||||
config := zap.NewProductionEncoderConfig()
|
||||
config.TimeKey = zapcore.OmitKey
|
||||
return config
|
||||
}
|
||||
158
service/acme/service.go
Normal file
158
service/acme/service.go
Normal file
@@ -0,0 +1,158 @@
|
||||
//go:build with_acme
|
||||
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/libdns/acmedns"
|
||||
"github.com/libdns/alidns"
|
||||
"github.com/libdns/cloudflare"
|
||||
"github.com/mholt/acmez/v3/acme"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func RegisterService(registry *boxService.Registry) {
|
||||
boxService.Register[option.ACMEServiceOptions](registry, C.TypeACME, NewService)
|
||||
}
|
||||
|
||||
var _ adapter.ACMECertificateProvider = (*Service)(nil)
|
||||
|
||||
type Service struct {
|
||||
boxService.Adapter
|
||||
ctx context.Context
|
||||
logger log.ContextLogger
|
||||
config *certmagic.Config
|
||||
cache *certmagic.Cache
|
||||
domain []string
|
||||
nextProtos []string
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.ACMEServiceOptions) (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, E.New("unsupported ACME provider: ", options.Provider)
|
||||
}
|
||||
acmeServer = options.Provider
|
||||
}
|
||||
var storage certmagic.Storage
|
||||
if options.DataDirectory != "" {
|
||||
storage = &certmagic.FileStorage{
|
||||
Path: options.DataDirectory,
|
||||
}
|
||||
} else {
|
||||
storage = certmagic.Default.Storage
|
||||
}
|
||||
zapLogger := zap.New(zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(encoderConfig()),
|
||||
&logWriter{logger: logger},
|
||||
zap.DebugLevel,
|
||||
))
|
||||
config := &certmagic.Config{
|
||||
DefaultServerName: options.DefaultServerName,
|
||||
Storage: storage,
|
||||
Logger: zapLogger,
|
||||
}
|
||||
acmeIssuer := certmagic.ACMEIssuer{
|
||||
CA: acmeServer,
|
||||
Email: options.Email,
|
||||
Agreed: true,
|
||||
DisableHTTPChallenge: options.DisableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: options.DisableTLSALPNChallenge,
|
||||
AltHTTPPort: int(options.AlternativeHTTPPort),
|
||||
AltTLSALPNPort: int(options.AlternativeTLSPort),
|
||||
Logger: zapLogger,
|
||||
}
|
||||
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
|
||||
var solver certmagic.DNS01Solver
|
||||
switch dnsOptions.Provider {
|
||||
case C.DNSProviderAliDNS:
|
||||
solver.DNSProvider = &alidns.Provider{
|
||||
CredentialInfo: alidns.CredentialInfo{
|
||||
AccessKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccessKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
SecurityToken: dnsOptions.AliDNSOptions.SecurityToken,
|
||||
},
|
||||
}
|
||||
case C.DNSProviderCloudflare:
|
||||
solver.DNSProvider = &cloudflare.Provider{
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||
}
|
||||
case C.DNSProviderACMEDNS:
|
||||
solver.DNSProvider = &acmedns.Provider{
|
||||
Username: dnsOptions.ACMEDNSOptions.Username,
|
||||
Password: dnsOptions.ACMEDNSOptions.Password,
|
||||
Subdomain: dnsOptions.ACMEDNSOptions.Subdomain,
|
||||
ServerURL: dnsOptions.ACMEDNSOptions.ServerURL,
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unsupported ACME DNS01 provider type: ", dnsOptions.Provider)
|
||||
}
|
||||
acmeIssuer.DNS01Solver = &solver
|
||||
}
|
||||
if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" {
|
||||
acmeIssuer.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
|
||||
}
|
||||
config.Issuers = []certmagic.Issuer{certmagic.NewACMEIssuer(config, acmeIssuer)}
|
||||
cache := certmagic.NewCache(certmagic.CacheOptions{
|
||||
GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
|
||||
return config, nil
|
||||
},
|
||||
Logger: zapLogger,
|
||||
})
|
||||
config = certmagic.New(cache, *config)
|
||||
var nextProtos []string
|
||||
if !acmeIssuer.DisableTLSALPNChallenge && acmeIssuer.DNS01Solver == nil {
|
||||
nextProtos = []string{C.ACMETLS1Protocol}
|
||||
}
|
||||
return &Service{
|
||||
Adapter: boxService.NewAdapter(C.TypeACME, tag),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
config: config,
|
||||
cache: cache,
|
||||
domain: options.Domain,
|
||||
nextProtos: nextProtos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return s.config.ManageAsync(s.ctx, s.domain)
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
if s.cache != nil {
|
||||
s.cache.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return s.config.GetCertificate(hello)
|
||||
}
|
||||
|
||||
func (s *Service) GetACMENextProtos() []string {
|
||||
return s.nextProtos
|
||||
}
|
||||
3
service/acme/stub.go
Normal file
3
service/acme/stub.go
Normal file
@@ -0,0 +1,3 @@
|
||||
//go:build !with_acme
|
||||
|
||||
package acme
|
||||
Reference in New Issue
Block a user