Add ACME DNS01 challenge support via libdns

This commit is contained in:
世界 2023-09-16 21:37:22 +08:00
parent c1ffcf365e
commit d17e93384b
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
10 changed files with 170 additions and 2 deletions

View File

@ -9,10 +9,13 @@ import (
"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"
"github.com/caddyserver/certmagic"
"github.com/libdns/alidns"
"github.com/libdns/cloudflare"
"github.com/mholt/acmez/acme"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -74,6 +77,21 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
AltTLSALPNPort: int(options.AlternativeTLSPort),
Logger: config.Logger,
}
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,
}
}
}
if options.ExternalAccount != nil && options.ExternalAccount.KeyID != "" {
acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
}

6
constant/dns.go Normal file
View File

@ -0,0 +1,6 @@
package constant
const (
DNSProviderAliDNS = "alidns"
DNSProviderCloudflare = "cloudflare"
)

View File

@ -0,0 +1,31 @@
### Structure
```json
{
"provider": "",
... // Provider Fields
}
```
### Provider Fields
#### Alibaba Cloud DNS
```json
{
"provider": "alidns",
"access_key_id": "",
"access_key_secret": "",
"region_id": ""
}
```
#### Cloudflare
```json
{
"provider": "cloudflare",
"api_token": ""
}
```

View File

@ -0,0 +1,31 @@
### 结构
```json
{
"provider": "",
... // 提供商字段
}
```
### 提供商字段
#### Alibaba Cloud DNS
```json
{
"provider": "alidns",
"access_key_id": "",
"access_key_secret": "",
"region_id": ""
}
```
#### Cloudflare
```json
{
"provider": "cloudflare",
"api_token": ""
}
```

View File

@ -25,7 +25,8 @@
"external_account": {
"key_id": "",
"mac_key": ""
}
},
"dns01_challenge": {}
},
"ech": {
"enabled": false,
@ -348,6 +349,12 @@ The key identifier.
The MAC key.
#### dns01_challenge
ACME DNS01 challenge field. If configured, other challenge methods will be disabled.
See [DNS01 Challenge Fields](/configuration/shared/dns01_challenge) for details.
### Reality Fields
!!! warning ""

View File

@ -25,7 +25,8 @@
"external_account": {
"key_id": "",
"mac_key": ""
}
},
"dns01_challenge": {}
},
"ech": {
"enabled": false,
@ -339,6 +340,12 @@ EAB外部帐户绑定包含将 ACME 帐户绑定或映射到其他已知
MAC 密钥。
#### dns01_challenge
ACME DNS01 验证字段。如果配置,将禁用其他验证方法。
参阅 [DNS01 验证字段](/configuration/shared/dns01_challenge)。
### Reality 字段
!!! warning ""

2
go.mod
View File

@ -15,6 +15,8 @@ require (
github.com/go-chi/render v1.0.3
github.com/gofrs/uuid/v5 v5.0.0
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mholt/acmez v1.2.0
github.com/miekg/dns v1.1.55

5
go.sum
View File

@ -68,6 +68,11 @@ github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic=
github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8=
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=

View File

@ -71,6 +71,7 @@ nav:
- Listen Fields: configuration/shared/listen.md
- Dial Fields: configuration/shared/dial.md
- TLS: configuration/shared/tls.md
- DNS01 Challenge Fields: configuration/shared/dns01_challenge.md
- Multiplex: configuration/shared/multiplex.md
- V2Ray Transport: configuration/shared/v2ray-transport.md
- UDP over TCP: configuration/shared/udp-over-tcp.md
@ -193,6 +194,7 @@ plugins:
Shared: 通用
Listen Fields: 监听字段
Dial Fields: 拨号字段
DNS01 Challenge Fields: DNS01 验证字段
Multiplex: 多路复用
V2Ray Transport: V2Ray 传输层

View File

@ -1,5 +1,11 @@
package option
import (
"github.com/sagernet/sing-box/common/json"
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
)
type InboundACMEOptions struct {
Domain Listable[string] `json:"domain,omitempty"`
DataDirectory string `json:"data_directory,omitempty"`
@ -11,9 +17,62 @@ type InboundACMEOptions struct {
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"`
}
type ACMEExternalAccountOptions struct {
KeyID string `json:"key_id,omitempty"`
MACKey string `json:"mac_key,omitempty"`
}
type _ACMEDNS01ChallengeOptions struct {
Provider string `json:"provider,omitempty"`
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
}
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
var v any
switch o.Provider {
case C.DNSProviderAliDNS:
v = o.AliDNSOptions
case C.DNSProviderCloudflare:
v = o.CloudflareOptions
default:
return nil, E.New("unknown provider type: " + o.Provider)
}
return MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
}
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_ACMEDNS01ChallengeOptions)(o))
if err != nil {
return err
}
var v any
switch o.Provider {
case C.DNSProviderAliDNS:
v = &o.AliDNSOptions
case C.DNSProviderCloudflare:
v = &o.CloudflareOptions
default:
return E.New("unknown provider type: " + o.Provider)
}
err = UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v)
if err != nil {
return E.Cause(err, "DNS01 challenge options")
}
return nil
}
type ACMEDNS01AliDNSOptions struct {
AccessKeyID string `json:"access_key_id,omitempty"`
AccessKeySecret string `json:"access_key_secret,omitempty"`
RegionID string `json:"region_id,omitempty"`
}
type ACMEDNS01CloudflareOptions struct {
APIToken string `json:"api_token,omitempty"`
}