2022-11-10 17:44:38 +08:00
|
|
|
package ssl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto"
|
|
|
|
"encoding/json"
|
2023-11-14 18:44:09 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
2022-11-25 00:08:44 +08:00
|
|
|
"github.com/go-acme/lego/v4/acme"
|
|
|
|
"github.com/go-acme/lego/v4/acme/api"
|
2022-11-10 17:44:38 +08:00
|
|
|
"github.com/go-acme/lego/v4/certificate"
|
|
|
|
"github.com/go-acme/lego/v4/challenge"
|
|
|
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
|
|
|
"github.com/go-acme/lego/v4/lego"
|
2022-11-16 10:31:35 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
2022-11-25 16:23:24 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
2022-11-10 17:44:38 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/dns/dnspod"
|
2023-11-17 17:28:09 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/dns/godaddy"
|
|
|
|
"github.com/go-acme/lego/v4/providers/dns/namecheap"
|
|
|
|
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
|
|
|
|
"github.com/go-acme/lego/v4/providers/dns/namesilo"
|
2024-01-22 22:28:52 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
2022-11-24 14:58:29 +08:00
|
|
|
"github.com/go-acme/lego/v4/providers/http/webroot"
|
2022-11-10 17:44:38 +08:00
|
|
|
"github.com/go-acme/lego/v4/registration"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type AcmeUser struct {
|
|
|
|
Email string
|
|
|
|
Registration *registration.Resource
|
2022-11-11 17:41:39 +08:00
|
|
|
Key crypto.PrivateKey
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *AcmeUser) GetEmail() string {
|
|
|
|
return u.Email
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *AcmeUser) GetRegistration() *registration.Resource {
|
|
|
|
return u.Registration
|
|
|
|
}
|
|
|
|
func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
|
2022-11-11 17:41:39 +08:00
|
|
|
return u.Key
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type AcmeClient struct {
|
|
|
|
Config *lego.Config
|
|
|
|
Client *lego.Client
|
|
|
|
User *AcmeUser
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount) (*AcmeClient, error) {
|
|
|
|
if acmeAccount.Email == "" {
|
2022-11-10 17:44:38 +08:00
|
|
|
return nil, errors.New("email can not blank")
|
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
client, err := NewRegisterClient(acmeAccount)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
2023-11-14 18:44:09 +08:00
|
|
|
return client, nil
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type DnsType string
|
|
|
|
|
|
|
|
const (
|
2024-01-22 22:28:52 +08:00
|
|
|
DnsPod DnsType = "DnsPod"
|
|
|
|
AliYun DnsType = "AliYun"
|
|
|
|
CloudFlare DnsType = "CloudFlare"
|
|
|
|
NameSilo DnsType = "NameSilo"
|
|
|
|
NameCheap DnsType = "NameCheap"
|
|
|
|
NameCom DnsType = "NameCom"
|
|
|
|
Godaddy DnsType = "Godaddy"
|
|
|
|
TencentCloud DnsType = "TencentCloud"
|
2022-11-10 17:44:38 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type DNSParam struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Token string `json:"token"`
|
|
|
|
AccessKey string `json:"accessKey"`
|
|
|
|
SecretKey string `json:"secretKey"`
|
|
|
|
Email string `json:"email"`
|
2023-11-17 17:28:09 +08:00
|
|
|
APIkey string `json:"apiKey"`
|
|
|
|
APIUser string `json:"apiUser"`
|
|
|
|
APISecret string `json:"apiSecret"`
|
2024-01-22 22:28:52 +08:00
|
|
|
SecretID string `json:"secretID"`
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
2024-01-09 15:55:34 +08:00
|
|
|
func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) error {
|
2023-11-14 18:44:09 +08:00
|
|
|
var (
|
|
|
|
param DNSParam
|
|
|
|
p challenge.Provider
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if err = json.Unmarshal([]byte(params), ¶m); err != nil {
|
2022-11-10 17:44:38 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-14 18:44:09 +08:00
|
|
|
switch dnsType {
|
|
|
|
case DnsPod:
|
2022-11-10 17:44:38 +08:00
|
|
|
dnsPodConfig := dnspod.NewDefaultConfig()
|
|
|
|
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
2023-11-23 11:00:08 +08:00
|
|
|
dnsPodConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
dnsPodConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
dnsPodConfig.TTL = 3600
|
2022-11-10 17:44:38 +08:00
|
|
|
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
2023-11-14 18:44:09 +08:00
|
|
|
case AliYun:
|
2022-11-16 10:31:35 +08:00
|
|
|
alidnsConfig := alidns.NewDefaultConfig()
|
|
|
|
alidnsConfig.SecretKey = param.SecretKey
|
|
|
|
alidnsConfig.APIKey = param.AccessKey
|
2023-11-23 11:00:08 +08:00
|
|
|
alidnsConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
alidnsConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
alidnsConfig.TTL = 3600
|
2022-11-16 10:31:35 +08:00
|
|
|
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
|
2023-11-14 18:44:09 +08:00
|
|
|
case CloudFlare:
|
2022-11-25 16:23:24 +08:00
|
|
|
cloudflareConfig := cloudflare.NewDefaultConfig()
|
|
|
|
cloudflareConfig.AuthEmail = param.Email
|
2023-12-04 15:44:10 +08:00
|
|
|
cloudflareConfig.AuthToken = param.APIkey
|
2023-11-23 11:00:08 +08:00
|
|
|
cloudflareConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
cloudflareConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
cloudflareConfig.TTL = 3600
|
2022-11-25 16:23:24 +08:00
|
|
|
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
2023-11-17 17:28:09 +08:00
|
|
|
case NameCheap:
|
|
|
|
namecheapConfig := namecheap.NewDefaultConfig()
|
|
|
|
namecheapConfig.APIKey = param.APIkey
|
|
|
|
namecheapConfig.APIUser = param.APIUser
|
2023-11-23 11:00:08 +08:00
|
|
|
namecheapConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
namecheapConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
namecheapConfig.TTL = 3600
|
|
|
|
p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
|
|
|
|
case NameSilo:
|
|
|
|
nameSiloConfig := namesilo.NewDefaultConfig()
|
|
|
|
nameSiloConfig.APIKey = param.APIkey
|
2023-11-23 11:00:08 +08:00
|
|
|
nameSiloConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
nameSiloConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
nameSiloConfig.TTL = 3600
|
|
|
|
p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
|
|
|
|
case Godaddy:
|
|
|
|
godaddyConfig := godaddy.NewDefaultConfig()
|
|
|
|
godaddyConfig.APIKey = param.APIkey
|
|
|
|
godaddyConfig.APISecret = param.APISecret
|
2023-11-23 11:00:08 +08:00
|
|
|
godaddyConfig.PropagationTimeout = 60 * time.Minute
|
|
|
|
godaddyConfig.PollingInterval = 5 * time.Second
|
2023-11-17 17:28:09 +08:00
|
|
|
godaddyConfig.TTL = 3600
|
|
|
|
p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
|
|
|
|
case NameCom:
|
|
|
|
nameComConfig := namedotcom.NewDefaultConfig()
|
|
|
|
nameComConfig.APIToken = param.Token
|
|
|
|
nameComConfig.Username = param.APIUser
|
|
|
|
nameComConfig.PropagationTimeout = 30 * time.Minute
|
|
|
|
nameComConfig.PollingInterval = 30 * time.Second
|
|
|
|
nameComConfig.TTL = 3600
|
|
|
|
p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
|
2024-01-22 22:28:52 +08:00
|
|
|
case TencentCloud:
|
|
|
|
tencentCloudConfig := tencentcloud.NewDefaultConfig()
|
|
|
|
tencentCloudConfig.SecretID = param.SecretID
|
|
|
|
tencentCloudConfig.SecretKey = param.SecretKey
|
|
|
|
tencentCloudConfig.PropagationTimeout = 30 * time.Minute
|
|
|
|
tencentCloudConfig.PollingInterval = 30 * time.Second
|
|
|
|
tencentCloudConfig.TTL = 3600
|
|
|
|
p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
|
2023-11-17 17:28:09 +08:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-11-25 16:23:24 +08:00
|
|
|
}
|
2024-01-09 15:55:34 +08:00
|
|
|
if skipDNSCheck {
|
|
|
|
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute), dns01.DisableCompletePropagationRequirement())
|
|
|
|
}
|
2022-11-10 17:44:38 +08:00
|
|
|
|
2023-11-17 17:28:09 +08:00
|
|
|
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute))
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
2022-11-16 10:31:35 +08:00
|
|
|
|
2023-03-16 18:44:32 +08:00
|
|
|
func (c *AcmeClient) UseManualDns() error {
|
2022-11-16 10:31:35 +08:00
|
|
|
p := &manualDnsProvider{}
|
2023-11-17 17:28:09 +08:00
|
|
|
if err := c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute)); err != nil {
|
2023-03-16 18:44:32 +08:00
|
|
|
return err
|
2022-11-16 10:31:35 +08:00
|
|
|
}
|
2023-03-16 18:44:32 +08:00
|
|
|
return nil
|
2022-11-16 10:31:35 +08:00
|
|
|
}
|
|
|
|
|
2022-11-24 14:58:29 +08:00
|
|
|
func (c *AcmeClient) UseHTTP(path string) error {
|
|
|
|
httpProvider, err := webroot.NewHTTPProvider(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-11-10 17:44:38 +08:00
|
|
|
|
2022-11-24 14:58:29 +08:00
|
|
|
err = c.Client.Challenge.SetHTTP01Provider(httpProvider)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
2023-11-16 14:38:07 +08:00
|
|
|
func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.PrivateKey) (certificate.Resource, error) {
|
2022-11-10 17:44:38 +08:00
|
|
|
request := certificate.ObtainRequest{
|
2023-11-16 14:38:07 +08:00
|
|
|
Domains: domains,
|
|
|
|
Bundle: true,
|
|
|
|
PrivateKey: privateKey,
|
2022-11-10 17:44:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
certificates, err := c.Client.Certificate.Obtain(request)
|
|
|
|
if err != nil {
|
|
|
|
return certificate.Resource{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return *certificates, nil
|
|
|
|
}
|
2022-11-16 10:31:35 +08:00
|
|
|
|
2022-11-17 17:39:33 +08:00
|
|
|
func (c *AcmeClient) RenewSSL(certUrl string) (certificate.Resource, error) {
|
|
|
|
certificates, err := c.Client.Certificate.Get(certUrl, true)
|
|
|
|
if err != nil {
|
|
|
|
return certificate.Resource{}, err
|
|
|
|
}
|
2023-11-17 17:28:09 +08:00
|
|
|
certificates, err = c.Client.Certificate.RenewWithOptions(*certificates, &certificate.RenewOptions{
|
|
|
|
Bundle: true,
|
|
|
|
PreferredChain: "",
|
|
|
|
MustStaple: true,
|
|
|
|
})
|
2022-11-17 17:39:33 +08:00
|
|
|
if err != nil {
|
|
|
|
return certificate.Resource{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return *certificates, nil
|
|
|
|
}
|
|
|
|
|
2022-11-16 10:31:35 +08:00
|
|
|
type Resolve struct {
|
|
|
|
Key string
|
|
|
|
Value string
|
2022-11-25 00:08:44 +08:00
|
|
|
Err string
|
2022-11-16 10:31:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type manualDnsProvider struct {
|
|
|
|
Resolve *Resolve
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-25 00:08:44 +08:00
|
|
|
func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error) {
|
|
|
|
core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
|
|
|
|
if err != nil {
|
2023-03-11 21:46:43 +08:00
|
|
|
return nil, err
|
2022-11-25 00:08:44 +08:00
|
|
|
}
|
|
|
|
order, err := core.Orders.New(domains)
|
|
|
|
if err != nil {
|
2023-03-11 21:46:43 +08:00
|
|
|
return nil, err
|
2022-11-25 00:08:44 +08:00
|
|
|
}
|
|
|
|
resolves := make(map[string]Resolve)
|
|
|
|
resc, errc := make(chan acme.Authorization), make(chan domainError)
|
|
|
|
for _, authzURL := range order.Authorizations {
|
|
|
|
go func(authzURL string) {
|
|
|
|
authz, err := core.Authorizations.Get(authzURL)
|
|
|
|
if err != nil {
|
|
|
|
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
resc <- authz
|
|
|
|
}(authzURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
var responses []acme.Authorization
|
|
|
|
for i := 0; i < len(order.Authorizations); i++ {
|
|
|
|
select {
|
|
|
|
case res := <-resc:
|
|
|
|
responses = append(responses, res)
|
|
|
|
case err := <-errc:
|
|
|
|
resolves[err.Domain] = Resolve{Err: err.Error.Error()}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(resc)
|
|
|
|
close(errc)
|
|
|
|
|
|
|
|
for _, auth := range responses {
|
|
|
|
domain := challenge.GetTargetedDomain(auth)
|
|
|
|
chlng, err := challenge.FindChallenge(challenge.DNS01, auth)
|
|
|
|
if err != nil {
|
|
|
|
resolves[domain] = Resolve{Err: err.Error()}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
keyAuth, err := core.GetKeyAuthorization(chlng.Token)
|
|
|
|
if err != nil {
|
|
|
|
resolves[domain] = Resolve{Err: err.Error()}
|
|
|
|
continue
|
|
|
|
}
|
2023-11-16 14:38:07 +08:00
|
|
|
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
2022-11-25 00:08:44 +08:00
|
|
|
resolves[domain] = Resolve{
|
2023-11-16 14:38:07 +08:00
|
|
|
Key: challengeInfo.FQDN,
|
|
|
|
Value: challengeInfo.Value,
|
2022-11-25 00:08:44 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-16 10:31:35 +08:00
|
|
|
|
2022-11-25 00:08:44 +08:00
|
|
|
return resolves, nil
|
2022-11-16 10:31:35 +08:00
|
|
|
}
|