mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-16 19:32:00 +08:00
【调整】暗色主题样式
This commit is contained in:
808
frontend/allinssl/backend/internal/cert/apply/apply.go
Normal file
808
frontend/allinssl/backend/internal/cert/apply/apply.go
Normal file
@@ -0,0 +1,808 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/internal/access"
|
||||
"ALLinSSL/backend/internal/cert"
|
||||
"ALLinSSL/backend/internal/cert/apply/lego/bt"
|
||||
"ALLinSSL/backend/internal/cert/apply/lego/jdcloud"
|
||||
"ALLinSSL/backend/internal/cert/apply/lego/webhook"
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
azcorecloud "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"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"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/azuredns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/baiducloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/bunny"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/constellix"
|
||||
"github.com/go-acme/lego/v4/providers/dns/gcore"
|
||||
"github.com/go-acme/lego/v4/providers/dns/godaddy"
|
||||
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||
"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"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ns1"
|
||||
"github.com/go-acme/lego/v4/providers/dns/rainyun"
|
||||
"github.com/go-acme/lego/v4/providers/dns/route53"
|
||||
"github.com/go-acme/lego/v4/providers/dns/spaceship"
|
||||
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/volcengine"
|
||||
"github.com/go-acme/lego/v4/providers/dns/westcn"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
var AlgorithmMap = map[string]certcrypto.KeyType{
|
||||
"RSA2048": certcrypto.RSA2048,
|
||||
"RSA3072": certcrypto.RSA3072,
|
||||
"RSA4096": certcrypto.RSA4096,
|
||||
"RSA8192": certcrypto.RSA8192,
|
||||
"EC256": certcrypto.EC256,
|
||||
"EC384": certcrypto.EC384,
|
||||
}
|
||||
|
||||
var CADirURLMap = map[string]string{
|
||||
"Let's Encrypt": "https://acme-v02.api.letsencrypt.org/directory",
|
||||
"zerossl": "https://acme.zerossl.com/v2/DV90",
|
||||
"google": "https://dv.acme-v02.api.pki.goog/directory",
|
||||
"sslcom": "https://acme.ssl.com/sslcom-dv-rsa",
|
||||
"sslcom-rsa": "https://acme.ssl.com/sslcom-dv-rsa",
|
||||
"sslcom-ecc": "https://acme.ssl.com/sslcom-dv-ecc",
|
||||
"buypass": "https://api.buypass.com/acme/directory",
|
||||
}
|
||||
|
||||
func GetSqlite() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/accounts.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.TableName = "accounts"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func GetDNSProvider(providerName string, creds map[string]string, httpClient *http.Client, maxWait time.Duration) (challenge.Provider, error) {
|
||||
switch providerName {
|
||||
case "tencentcloud":
|
||||
config := tencentcloud.NewDefaultConfig()
|
||||
config.SecretID = creds["secret_id"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return tencentcloud.NewDNSProviderConfig(config)
|
||||
case "cloudflare":
|
||||
config := cloudflare.NewDefaultConfig()
|
||||
if creds["email"] == "" {
|
||||
config.AuthToken = creds["api_key"]
|
||||
} else {
|
||||
config.AuthEmail = creds["email"]
|
||||
config.AuthKey = creds["api_key"]
|
||||
}
|
||||
config.PropagationTimeout = maxWait
|
||||
return cloudflare.NewDNSProviderConfig(config)
|
||||
case "aliyun":
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.APIKey = creds["access_key_id"]
|
||||
config.SecretKey = creds["access_key_secret"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return alidns.NewDNSProviderConfig(config)
|
||||
case "huaweicloud":
|
||||
config := huaweicloud.NewDefaultConfig()
|
||||
config.AccessKeyID = creds["access_key"]
|
||||
config.SecretAccessKey = creds["secret_key"]
|
||||
// 不传会报错
|
||||
config.Region = "cn-north-1"
|
||||
config.PropagationTimeout = maxWait
|
||||
return huaweicloud.NewDNSProviderConfig(config)
|
||||
case "baidu":
|
||||
config := baiducloud.NewDefaultConfig()
|
||||
config.AccessKeyID = creds["access_key"]
|
||||
config.SecretAccessKey = creds["secret_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return baiducloud.NewDNSProviderConfig(config)
|
||||
case "westcn":
|
||||
config := westcn.NewDefaultConfig()
|
||||
config.Username = creds["username"]
|
||||
config.Password = creds["password"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return westcn.NewDNSProviderConfig(config)
|
||||
case "volcengine":
|
||||
config := volcengine.NewDefaultConfig()
|
||||
config.AccessKey = creds["access_key"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return volcengine.NewDNSProviderConfig(config)
|
||||
case "godaddy":
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.APISecret = creds["api_secret"]
|
||||
if httpClient != nil {
|
||||
config.HTTPClient = httpClient
|
||||
}
|
||||
config.PropagationTimeout = maxWait
|
||||
return godaddy.NewDNSProviderConfig(config)
|
||||
case "namecheap":
|
||||
config := namecheap.NewDefaultConfig()
|
||||
config.APIUser = creds["api_user"]
|
||||
config.APIKey = creds["api_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return namecheap.NewDNSProviderConfig(config)
|
||||
case "ns1":
|
||||
config := ns1.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return ns1.NewDNSProviderConfig(config)
|
||||
case "cloudns":
|
||||
config := cloudns.NewDefaultConfig()
|
||||
config.AuthID = creds["auth_id"]
|
||||
config.AuthPassword = creds["auth_password"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return cloudns.NewDNSProviderConfig(config)
|
||||
case "aws":
|
||||
config := route53.NewDefaultConfig()
|
||||
config.AccessKeyID = creds["access_key_id"]
|
||||
config.SecretAccessKey = creds["secret_access_key"]
|
||||
config.Region = creds["region"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return route53.NewDNSProviderConfig(config)
|
||||
case "azure":
|
||||
config := azuredns.NewDefaultConfig()
|
||||
config.TenantID = creds["tenant_id"]
|
||||
config.ClientID = creds["client_id"]
|
||||
config.ClientSecret = creds["client_secret"]
|
||||
switch strings.ToLower(creds["environment"]) {
|
||||
case "", "default", "public", "azurecloud":
|
||||
config.Environment = azcorecloud.AzurePublic
|
||||
case "china", "chinacloud", "azurechina", "azurechinacloud":
|
||||
config.Environment = azcorecloud.AzureChina
|
||||
case "usgovernment", "government", "azureusgovernment", "azuregovernment":
|
||||
config.Environment = azcorecloud.AzureGovernment
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 Azure 环境: %s", creds["environment"])
|
||||
}
|
||||
config.PropagationTimeout = maxWait
|
||||
return azuredns.NewDNSProviderConfig(config)
|
||||
case "namesilo":
|
||||
config := namesilo.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return namesilo.NewDNSProviderConfig(config)
|
||||
case "namedotcom":
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.Username = creds["username"]
|
||||
config.APIToken = creds["api_token"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return namedotcom.NewDNSProviderConfig(config)
|
||||
case "bunny":
|
||||
config := bunny.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return bunny.NewDNSProviderConfig(config)
|
||||
case "gcore":
|
||||
config := gcore.NewDefaultConfig()
|
||||
config.APIToken = creds["api_token"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return gcore.NewDNSProviderConfig(config)
|
||||
case "jdcloud":
|
||||
config := jdcloud.NewDefaultConfig()
|
||||
config.AccessKeyID = creds["access_key_id"]
|
||||
config.AccessKeySecret = creds["secret_access_key"]
|
||||
config.RegionId = "cn-north-1"
|
||||
config.PropagationTimeout = maxWait
|
||||
return jdcloud.NewDNSProviderConfig(config)
|
||||
case "constellix":
|
||||
config := constellix.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return constellix.NewDNSProviderConfig(config)
|
||||
case "webhook":
|
||||
config := webhook.NewConfig(creds)
|
||||
config.PropagationTimeout = maxWait
|
||||
return webhook.NewDNSProviderConfig(config)
|
||||
case "spaceship":
|
||||
config := spaceship.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.APISecret = creds["api_secret"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return spaceship.NewDNSProviderConfig(config)
|
||||
case "rainyun":
|
||||
config := rainyun.NewDefaultConfig()
|
||||
config.APIKey = creds["api_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return rainyun.NewDNSProviderConfig(config)
|
||||
case "btdomain":
|
||||
config := bt.NewDefaultConfig()
|
||||
config.AccountID = creds["account_id"]
|
||||
config.AccessKey = creds["access_key"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
if creds["base_url"] != "" {
|
||||
config.BaseURL = creds["base_url"]
|
||||
}
|
||||
config.PropagationTimeout = maxWait
|
||||
return bt.NewDNSProviderConfig(config)
|
||||
//case "edgeone":
|
||||
//config :=
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName)
|
||||
}
|
||||
}
|
||||
|
||||
func GetZeroSSLEabFromEmail(email string, httpClient *http.Client) (map[string]any, error) {
|
||||
APIPath := "https://api.zerossl.com/acme/eab-credentials-email"
|
||||
data := map[string]any{
|
||||
"email": email,
|
||||
}
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("POST", APIPath, strings.NewReader(string(jsonData)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if httpClient == nil {
|
||||
httpClient = &http.Client{}
|
||||
}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("获取ZeroSSL EAB信息失败,状态码:%d", resp.StatusCode)
|
||||
}
|
||||
var result map[string]any
|
||||
err = json.NewDecoder(resp.Body).Decode(&result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析ZeroSSL EAB信息失败:%v", err)
|
||||
}
|
||||
if result["eab_kid"] == nil || result["eab_hmac_key"] == nil {
|
||||
return nil, fmt.Errorf("ZeroSSL EAB信息不完整,缺少kid或hmacEncoded")
|
||||
}
|
||||
return map[string]any{
|
||||
"Kid": result["eab_kid"],
|
||||
"HmacEncoded": result["eab_hmac_key"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getEABFromAccData(accData map[string]any, eabData *map[string]any) bool {
|
||||
if accData == nil {
|
||||
return false
|
||||
}
|
||||
kid := accData["Kid"]
|
||||
hmac := accData["HmacEncoded"]
|
||||
if kid != nil && hmac != nil {
|
||||
*eabData = map[string]any{
|
||||
"Kid": kid,
|
||||
"HmacEncoded": hmac,
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetAcmeClient(email, algorithm, eabId, ca string, httpClient *http.Client, logger *public.Logger) (*lego.Client, error) {
|
||||
var (
|
||||
eabData map[string]any
|
||||
err error
|
||||
)
|
||||
switch eabId {
|
||||
case "":
|
||||
if ca == "" || ca == "letsencrypt" {
|
||||
ca = "Let's Encrypt"
|
||||
}
|
||||
case "let":
|
||||
ca = "Let's Encrypt"
|
||||
case "buy", "buypass":
|
||||
ca = "buypass"
|
||||
default:
|
||||
eabData, err = access.GetEAB(eabId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if eabData == nil {
|
||||
return nil, fmt.Errorf("未找到EAB信息")
|
||||
}
|
||||
if eabData["Kid"] == nil {
|
||||
return nil, fmt.Errorf("Kid不能为空")
|
||||
}
|
||||
if eabData["HmacEncoded"] == nil {
|
||||
return nil, fmt.Errorf("HmacEncoded不能为空")
|
||||
}
|
||||
ca = eabData["ca"].(string)
|
||||
}
|
||||
|
||||
CADirURL := CADirURLMap[ca]
|
||||
if ca == "sslcom" {
|
||||
if algorithm == "EC256" || algorithm == "EC384" {
|
||||
CADirURL = CADirURLMap["sslcom-ecc"]
|
||||
} else {
|
||||
CADirURL = CADirURLMap["sslcom-rsa"]
|
||||
}
|
||||
}
|
||||
db, err := GetSqlite()
|
||||
var accData map[string]any
|
||||
if err != nil {
|
||||
logger.Debug("获取数据库连接失败", err)
|
||||
if ca != "Let's Encrypt" && ca != "zerossl" && ca != "buypass" {
|
||||
return nil, fmt.Errorf("当前CA【%s】 需要从数据库获取预设账号,但是连接数据库失败,请稍后重试,err:%w", ca, err)
|
||||
}
|
||||
} else {
|
||||
defer db.Close()
|
||||
accData, err = GetAccount(db, email, ca)
|
||||
if err != nil || accData == nil {
|
||||
logger.Debug("获取acme账号信息失败")
|
||||
if ca != "Let's Encrypt" && ca != "zerossl" && ca != "buypass" {
|
||||
return nil, fmt.Errorf("未找到%s账号信息,请先在账号管理中添加%s账号, email:%s", ca, ca, email)
|
||||
}
|
||||
}
|
||||
if CADirURL == "" {
|
||||
accCADirURL, ok := accData["CADirURL"].(string)
|
||||
if !ok || accCADirURL == "" {
|
||||
logger.Debug("未找到此CA的请求地址")
|
||||
return nil, fmt.Errorf("未找到CA【%s】请求地址,请先在账号管理中检查%s账号, email:%s", ca, ca, email)
|
||||
}
|
||||
CADirURL = accCADirURL
|
||||
}
|
||||
}
|
||||
user := GetAcmeUser(email, logger, accData)
|
||||
config := lego.NewConfig(user)
|
||||
config.Certificate.KeyType = AlgorithmMap[algorithm]
|
||||
config.CADirURL = CADirURL
|
||||
if httpClient != nil {
|
||||
config.HTTPClient = httpClient
|
||||
}
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Registration == nil {
|
||||
logger.Debug("正在注册账号:" + email)
|
||||
if eabData == nil {
|
||||
// 走新的逻辑,eab已合并到账号中
|
||||
if !getEABFromAccData(accData, &eabData) {
|
||||
switch ca {
|
||||
case "zerossl":
|
||||
eabData, err = GetZeroSSLEabFromEmail(email, httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取ZeroSSL EAB信息失败: %v", err)
|
||||
}
|
||||
case "sslcom", "google":
|
||||
return nil, fmt.Errorf("未找到EAB信息,请在账号管理中添加%s账号", ca)
|
||||
}
|
||||
}
|
||||
}
|
||||
var (
|
||||
reg *registration.Resource
|
||||
Kid, HmacEncoded string
|
||||
)
|
||||
if eabData != nil {
|
||||
Kid = eabData["Kid"].(string)
|
||||
HmacEncoded = eabData["HmacEncoded"].(string)
|
||||
}
|
||||
if Kid != "" && HmacEncoded != "" {
|
||||
Kid := eabData["Kid"].(string)
|
||||
HmacEncoded := eabData["HmacEncoded"].(string)
|
||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: Kid,
|
||||
HmacEncoded: HmacEncoded,
|
||||
})
|
||||
} else {
|
||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
err = SaveUserToDB(db, user, ca)
|
||||
if err != nil {
|
||||
logger.Debug("acme账号注册成功,但保存到数据库失败", err)
|
||||
}
|
||||
logger.Debug("acme账号注册并保存成功")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func GetCert(runId string, domainArr []string, endDay int, logger *public.Logger) (map[string]any, error) {
|
||||
if runId == "" {
|
||||
return nil, fmt.Errorf("参数错误:_runId")
|
||||
}
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.TableName = "workflow_history"
|
||||
defer s.Close()
|
||||
// 查询 workflowId
|
||||
wh, err := s.Where("id=?", []interface{}{runId}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(wh) <= 0 {
|
||||
return nil, fmt.Errorf("未获取到对应的workflowId")
|
||||
}
|
||||
s.TableName = "cert"
|
||||
certs, err := s.Where("workflow_id=?", []interface{}{wh[0]["workflow_id"]}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(certs) <= 0 {
|
||||
return nil, fmt.Errorf("未获取到当前工作流下的证书")
|
||||
}
|
||||
layout := "2006-01-02 15:04:05"
|
||||
var maxDays float64
|
||||
var maxItem map[string]any
|
||||
for i := range certs {
|
||||
if !public.ContainsAllIgnoreBRepeats(strings.Split(certs[i]["domains"].(string), ","), domainArr) {
|
||||
continue
|
||||
}
|
||||
endTimeStr, ok := certs[i]["end_time"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
endTime, err := time.Parse(layout, endTimeStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
diff := endTime.Sub(time.Now()).Hours() / 24
|
||||
if diff > maxDays {
|
||||
maxDays = diff
|
||||
maxItem = certs[i]
|
||||
}
|
||||
}
|
||||
if maxItem == nil {
|
||||
return nil, fmt.Errorf("未获取到对应的证书")
|
||||
}
|
||||
if int(maxDays) <= endDay {
|
||||
return nil, fmt.Errorf("证书已过期或即将过期,剩余天数:%d 小于%d天", int(maxDays), endDay)
|
||||
}
|
||||
// 证书未过期,直接返回
|
||||
logger.Debug(fmt.Sprintf("上次证书申请成功,域名:%s,剩余天数:%d 大于%d天,已跳过申请复用此证书", maxItem["domains"], int(maxDays), endDay))
|
||||
return map[string]any{
|
||||
"cert": maxItem["cert"],
|
||||
"key": maxItem["key"],
|
||||
"issuerCert": maxItem["issuer_cert"],
|
||||
"skip": true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
log.Logger = logger.GetLogger()
|
||||
var err error
|
||||
email, ok := cfg["email"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:email")
|
||||
}
|
||||
domains, ok := cfg["domains"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:domains")
|
||||
}
|
||||
providerStr, ok := cfg["provider"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:provider")
|
||||
}
|
||||
endDay := 30
|
||||
switch v := cfg["end_day"].(type) {
|
||||
case float64:
|
||||
endDay = int(v)
|
||||
case int:
|
||||
endDay = v
|
||||
case string:
|
||||
if v != "" {
|
||||
endDay, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("参数错误:end_day")
|
||||
}
|
||||
}
|
||||
case int64:
|
||||
endDay = int(v)
|
||||
}
|
||||
algorithm, ok := cfg["algorithm"].(string)
|
||||
if !ok {
|
||||
algorithm = "RSA2048"
|
||||
}
|
||||
var httpClient *http.Client
|
||||
proxy, ok := cfg["proxy"].(string)
|
||||
if ok && proxy != "" {
|
||||
// 构建代理 HTTP 客户端
|
||||
proxyURL, err := url.Parse(proxy) // 替换为你的代理地址
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的代理地址: %v", err)
|
||||
}
|
||||
httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
var eabId string
|
||||
switch v := cfg["eabId"].(type) {
|
||||
case float64:
|
||||
eabId = strconv.Itoa(int(v))
|
||||
case string:
|
||||
eabId = v
|
||||
default:
|
||||
eabId = ""
|
||||
}
|
||||
ca, ok := cfg["ca"].(string)
|
||||
if !ok {
|
||||
ca = ""
|
||||
}
|
||||
|
||||
var providerID string
|
||||
switch v := cfg["provider_id"].(type) {
|
||||
case float64:
|
||||
providerID = strconv.Itoa(int(v))
|
||||
case string:
|
||||
providerID = v
|
||||
default:
|
||||
return nil, fmt.Errorf("参数错误:provider_id")
|
||||
}
|
||||
var NameServers []string
|
||||
if cfg["name_server"] == nil {
|
||||
NameServers = []string{
|
||||
"8.8.8.8:53",
|
||||
"1.1.1.1:53",
|
||||
}
|
||||
} else {
|
||||
if nameServerStr, ok := cfg["name_server"].(string); ok {
|
||||
NameServers = strings.Split(nameServerStr, ",")
|
||||
for i := range NameServers {
|
||||
NameServers[i] = strings.TrimSpace(NameServers[i])
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("参数错误:name_server")
|
||||
}
|
||||
}
|
||||
|
||||
var skipCheck bool
|
||||
if cfg["skip_check"] == nil {
|
||||
// 默认跳过预检查
|
||||
skipCheck = false
|
||||
} else {
|
||||
switch v := cfg["skip_check"].(type) {
|
||||
case int:
|
||||
if v > 0 {
|
||||
skipCheck = true
|
||||
} else {
|
||||
skipCheck = false
|
||||
}
|
||||
case float64:
|
||||
if v > 0 {
|
||||
skipCheck = true
|
||||
} else {
|
||||
skipCheck = false
|
||||
}
|
||||
case string:
|
||||
if v == "true" || v == "1" {
|
||||
skipCheck = true
|
||||
} else {
|
||||
skipCheck = false
|
||||
}
|
||||
case bool:
|
||||
skipCheck = v
|
||||
default:
|
||||
return nil, fmt.Errorf("参数错误:skip_check")
|
||||
}
|
||||
}
|
||||
var ignoreCheck bool
|
||||
if cfg["ignore_check"] == nil {
|
||||
// 默认不忽略预检查
|
||||
ignoreCheck = false
|
||||
} else {
|
||||
switch v := cfg["ignore_check"].(type) {
|
||||
case int:
|
||||
if v > 0 {
|
||||
ignoreCheck = true
|
||||
} else {
|
||||
ignoreCheck = false
|
||||
}
|
||||
case float64:
|
||||
if v > 0 {
|
||||
ignoreCheck = true
|
||||
} else {
|
||||
ignoreCheck = false
|
||||
}
|
||||
case string:
|
||||
if v == "true" || v == "1" {
|
||||
ignoreCheck = true
|
||||
} else {
|
||||
ignoreCheck = false
|
||||
}
|
||||
case bool:
|
||||
ignoreCheck = v
|
||||
default:
|
||||
return nil, fmt.Errorf("参数错误:ignore_check")
|
||||
}
|
||||
}
|
||||
var closeCname bool
|
||||
if cfg["close_cname"] == nil {
|
||||
// 默认开启CNAME跟随
|
||||
closeCname = false
|
||||
} else {
|
||||
switch v := cfg["close_cname"].(type) {
|
||||
case int:
|
||||
if v > 0 {
|
||||
closeCname = true
|
||||
} else {
|
||||
closeCname = false
|
||||
}
|
||||
case float64:
|
||||
if v > 0 {
|
||||
closeCname = true
|
||||
} else {
|
||||
closeCname = false
|
||||
}
|
||||
case string:
|
||||
if v == "true" || v == "1" {
|
||||
closeCname = true
|
||||
} else {
|
||||
closeCname = false
|
||||
}
|
||||
case bool:
|
||||
closeCname = v
|
||||
default:
|
||||
return nil, fmt.Errorf("参数错误:close_cname")
|
||||
}
|
||||
}
|
||||
var maxWait time.Duration
|
||||
if cfg["max_wait"] == nil {
|
||||
// 默认最大等待时间为2分钟
|
||||
maxWait = 2 * time.Minute
|
||||
} else {
|
||||
switch v := cfg["max_wait"].(type) {
|
||||
case int:
|
||||
maxWait = time.Duration(v) * time.Second
|
||||
case float64:
|
||||
maxWait = time.Duration(v) * time.Second
|
||||
case string:
|
||||
maxWait = 2 * time.Minute // 默认值
|
||||
if v != "" {
|
||||
d, err := strconv.Atoi(v)
|
||||
if err == nil {
|
||||
maxWait = time.Duration(d) * time.Second
|
||||
}
|
||||
}
|
||||
default:
|
||||
maxWait = 2 * time.Minute
|
||||
}
|
||||
}
|
||||
|
||||
domainArr := strings.Split(domains, ",")
|
||||
for i := range domainArr {
|
||||
domainArr[i] = strings.TrimSpace(domainArr[i])
|
||||
}
|
||||
|
||||
// 获取上次申请的证书
|
||||
runId, ok := cfg["_runId"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:_runId")
|
||||
}
|
||||
certData, err := GetCert(runId, domainArr, endDay, logger)
|
||||
if err != nil {
|
||||
logger.Debug("未获取到符合条件的本地证书:" + err.Error())
|
||||
} else {
|
||||
return certData, nil
|
||||
}
|
||||
logger.Debug("正在申请证书,域名: " + domains)
|
||||
os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(closeCname))
|
||||
// 创建 ACME 客户端
|
||||
client, err := GetAcmeClient(email, algorithm, eabId, ca, httpClient, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 获取 DNS 验证提供者
|
||||
providerData, err := access.GetAccess(providerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
providerConfigStr, ok := providerData["config"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("api配置错误")
|
||||
}
|
||||
// 解析 JSON 配置
|
||||
var providerConfig map[string]string
|
||||
err = json.Unmarshal([]byte(providerConfigStr), &providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// DNS 验证
|
||||
provider, err := GetDNSProvider(providerStr, providerConfig, httpClient, maxWait)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 DNS provider 失败: %v", err)
|
||||
}
|
||||
|
||||
if skipCheck {
|
||||
// 跳过预检查
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
|
||||
return true, nil
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
start := time.Now()
|
||||
if ignoreCheck {
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.AddRecursiveNameservers(NameServers),
|
||||
dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
|
||||
ok, err := check(fqdn, value)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] DNS precheck error for %s: %v", fqdn, err)
|
||||
if elapsed >= maxWait {
|
||||
log.Printf("[WARN] Precheck error but forcing continue due to timeout for %s", fqdn)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
if ok {
|
||||
log.Printf("[OK] TXT record for %s is present.", fqdn)
|
||||
return true, nil
|
||||
}
|
||||
if elapsed >= maxWait {
|
||||
log.Printf("[WARN] TXT record for %s not found after %v, forcing continue.", fqdn, elapsed)
|
||||
return true, nil
|
||||
}
|
||||
log.Printf("[INFO] TXT record for %s not yet found, waiting... elapsed %v", fqdn, elapsed)
|
||||
return false, nil
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.AddRecursiveNameservers(NameServers),
|
||||
)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fmt.Println(strings.Split(domains, ","))
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domainArr,
|
||||
Bundle: true,
|
||||
}
|
||||
certObj, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certStr := string(certObj.Certificate)
|
||||
keyStr := string(certObj.PrivateKey)
|
||||
issuerCertStr := string(certObj.IssuerCertificate)
|
||||
|
||||
// 保存证书和私钥
|
||||
data := map[string]any{
|
||||
"cert": certStr,
|
||||
"key": keyStr,
|
||||
"issuerCert": issuerCertStr,
|
||||
}
|
||||
|
||||
_, err = cert.SaveCert("workflow", keyStr, certStr, issuerCertStr, runId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user