mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 07:41:10 +08:00
1.新增eab列表
2.申请证书新增http代理、新增ca选择(zerossl、google)、新增证书算法选择 3.修复数据库连接内存泄漏
This commit is contained in:
@@ -29,7 +29,7 @@ func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
}
|
||||
|
||||
func SaveUserToDB(db *public.Sqlite, user *MyUser) error {
|
||||
func SaveUserToDB(db *public.Sqlite, user *MyUser, Type string) error {
|
||||
keyBytes, err := x509.MarshalPKCS8PrivateKey(user.key)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -53,13 +53,13 @@ func SaveUserToDB(db *public.Sqlite, user *MyUser) error {
|
||||
"reg": regBytes,
|
||||
"create_time": now,
|
||||
"update_time": now,
|
||||
"type": "Let's Encrypt",
|
||||
"type": Type,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func LoadUserFromDB(db *public.Sqlite, email string) (*MyUser, error) {
|
||||
data, err := db.Where(`email=?`, []interface{}{email}).Select()
|
||||
func LoadUserFromDB(db *public.Sqlite, email string, Type string) (*MyUser, error) {
|
||||
data, err := db.Where(`email=? and type=?`, []interface{}{email, Type}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -22,17 +22,33 @@ import (
|
||||
"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"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
func GetSqlite() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "_accounts"
|
||||
return s, nil
|
||||
}
|
||||
@@ -77,17 +93,179 @@ func GetDNSProvider(providerName string, creds map[string]string) (challenge.Pro
|
||||
config.SecretKey = creds["secret_key"]
|
||||
return volcengine.NewDNSProviderConfig(config)
|
||||
|
||||
// case "godaddy":
|
||||
// config := godaddy.NewDefaultConfig()
|
||||
// config.APIKey = creds["api_key"]
|
||||
// config.APISecret = creds["api_secret"]
|
||||
// return godaddy.NewDNSProviderConfig(config)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAcmeClient(db *public.Sqlite, email, algorithm, ca, proxy, eabId string, logger *public.Logger) (*lego.Client, error) {
|
||||
user, err := LoadUserFromDB(db, email, ca)
|
||||
if err != nil {
|
||||
logger.Debug("acme账号不存在,注册新账号")
|
||||
privateKey, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
user = &MyUser{
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(user)
|
||||
config.Certificate.KeyType = AlgorithmMap[algorithm]
|
||||
config.CADirURL = CADirURLMap[ca]
|
||||
if 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),
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
config.HTTPClient = httpClient
|
||||
}
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("正在注册账号:" + email)
|
||||
var reg *registration.Resource
|
||||
switch ca {
|
||||
case "Let's Encrypt":
|
||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
case "zerossl", "google":
|
||||
// 获取EAB参数
|
||||
var eabData map[string]any
|
||||
if eabId == "" {
|
||||
data, err := access.GetAllEAB(ca)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) <= 0 {
|
||||
return nil, fmt.Errorf("未找到EAB信息")
|
||||
}
|
||||
eabData = data[0]
|
||||
} else {
|
||||
eabData, err = access.GetEAB(eabId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if eabData == nil {
|
||||
return nil, fmt.Errorf("未找到EAB信息")
|
||||
}
|
||||
}
|
||||
Kid := eabData["kid"].(string)
|
||||
HmacEncoded := eabData["HmacEncoded"].(string)
|
||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: Kid,
|
||||
HmacEncoded: HmacEncoded,
|
||||
})
|
||||
default:
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("acme账号注册并保存成功")
|
||||
return client, nil
|
||||
} else {
|
||||
config := lego.NewConfig(user)
|
||||
config.Certificate.KeyType = AlgorithmMap[algorithm]
|
||||
config.CADirURL = CADirURLMap[ca]
|
||||
if 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),
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
config.HTTPClient = httpClient
|
||||
}
|
||||
|
||||
// 初始化 ACME 客户端
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
db, err := GetSqlite()
|
||||
if err != nil {
|
||||
@@ -107,6 +285,44 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
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"
|
||||
}
|
||||
ca, ok := cfg["ca"].(string)
|
||||
if !ok {
|
||||
ca = "Let's Encrypt"
|
||||
}
|
||||
proxy, ok := cfg["proxy"].(string)
|
||||
if !ok {
|
||||
proxy = ""
|
||||
}
|
||||
var eabId string
|
||||
switch v := cfg["eabId"].(type) {
|
||||
case float64:
|
||||
eabId = strconv.Itoa(int(v))
|
||||
case string:
|
||||
eabId = v
|
||||
default:
|
||||
eabId = ""
|
||||
}
|
||||
|
||||
var providerID string
|
||||
switch v := cfg["provider_id"].(type) {
|
||||
case float64:
|
||||
@@ -178,100 +394,15 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("参数错误:_runId")
|
||||
}
|
||||
if runId != "" {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
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 {
|
||||
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 {
|
||||
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]
|
||||
}
|
||||
}
|
||||
certObj := maxItem
|
||||
// 判断证书是否过期
|
||||
cfgEnd, ok := cfg["end_day"].(int)
|
||||
if !ok || cfgEnd <= 0 {
|
||||
cfgEnd = 30
|
||||
}
|
||||
|
||||
if int(maxDays) > cfgEnd {
|
||||
// 证书未过期,直接返回
|
||||
logger.Debug(fmt.Sprintf("上次证书申请成功,域名:%s,剩余天数:%d 大于%d天,已跳过申请复用此证书", certObj["domains"], int(maxDays), cfgEnd))
|
||||
return map[string]any{
|
||||
"cert": certObj["cert"],
|
||||
"key": certObj["key"],
|
||||
"issuerCert": certObj["issuer_cert"],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
certData, err := GetCert(runId, domainArr, endDay, logger)
|
||||
if err != nil {
|
||||
logger.Debug("未获取到符合条件的本地证书:" + err.Error())
|
||||
} else {
|
||||
return certData, nil
|
||||
}
|
||||
logger.Debug("正在申请证书,域名: " + domains)
|
||||
|
||||
user, err := LoadUserFromDB(db, email)
|
||||
if err != nil {
|
||||
logger.Debug("acme账号不存在,注册新账号")
|
||||
privateKey, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
user = &MyUser{
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(user)
|
||||
config.Certificate.KeyType = certcrypto.EC384
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("正在注册账号:" + email)
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Registration = reg
|
||||
|
||||
err = SaveUserToDB(db, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("账号注册并保存成功")
|
||||
}
|
||||
|
||||
// 初始化 ACME 客户端
|
||||
client, err := lego.NewClient(lego.NewConfig(user))
|
||||
// 创建 ACME 客户端
|
||||
client, err := GetAcmeClient(db, email, algorithm, ca, proxy, eabId, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ func GetSqlite() (*public.Sqlite, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "cert"
|
||||
return s, nil
|
||||
}
|
||||
@@ -26,7 +25,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
@@ -35,7 +34,7 @@ func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
limits[1] = p * limit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("domains like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("domains like ?", []interface{}{"%" + search + "%"}).Limit(limits).Order("create_time", "desc").Select()
|
||||
@@ -68,7 +67,6 @@ func AddCert(source, key, cert, issuer, issuerCert, domains, sha256, historyId,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Connect()
|
||||
s.TableName = "workflow_history"
|
||||
defer s.Close()
|
||||
// 查询 workflowId
|
||||
@@ -80,7 +78,7 @@ func AddCert(source, key, cert, issuer, issuerCert, domains, sha256, historyId,
|
||||
workflowId = wh[0]["workflow_id"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Insert(map[string]any{
|
||||
"source": source,
|
||||
@@ -108,7 +106,7 @@ func SaveCert(source, key, cert, issuerCert, historyId string) (string, error) {
|
||||
if err := public.ValidateSSLCertificate(cert, key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
certObj, err := public.ParseCertificate([]byte(cert))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析证书失败: %v", err)
|
||||
@@ -121,23 +119,23 @@ func SaveCert(source, key, cert, issuerCert, historyId string) (string, error) {
|
||||
if d, _ := GetCert(sha256); d != nil {
|
||||
return sha256, nil
|
||||
}
|
||||
|
||||
|
||||
domainSet := make(map[string]bool)
|
||||
|
||||
|
||||
if certObj.Subject.CommonName != "" {
|
||||
domainSet[certObj.Subject.CommonName] = true
|
||||
}
|
||||
for _, dns := range certObj.DNSNames {
|
||||
domainSet[dns] = true
|
||||
}
|
||||
|
||||
|
||||
// 转成切片并拼接成逗号分隔的字符串
|
||||
var domains []string
|
||||
for domain := range domainSet {
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
domainList := strings.Join(domains, ",")
|
||||
|
||||
|
||||
// 提取 CA 名称(Issuer 的组织名)
|
||||
caName := "UNKNOWN"
|
||||
if len(certObj.Issuer.Organization) > 0 {
|
||||
@@ -149,7 +147,7 @@ func SaveCert(source, key, cert, issuerCert, historyId string) (string, error) {
|
||||
startTime := certObj.NotBefore.Format("2006-01-02 15:04:05")
|
||||
endTime := certObj.NotAfter.Format("2006-01-02 15:04:05")
|
||||
endDay := fmt.Sprintf("%d", int(certObj.NotAfter.Sub(time.Now()).Hours()/24))
|
||||
|
||||
|
||||
err = AddCert(source, key, cert, caName, issuerCert, domainList, sha256, historyId, startTime, endTime, endDay)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("保存证书失败: %v", err)
|
||||
@@ -171,7 +169,7 @@ func DelCert(id string) error {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -185,7 +183,7 @@ func GetCert(id string) (map[string]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
|
||||
res, err := s.Where("id=? or sha256=?", []interface{}{id, id}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -193,13 +191,13 @@ func GetCert(id string) (map[string]string, error) {
|
||||
if len(res) == 0 {
|
||||
return nil, fmt.Errorf("证书不存在")
|
||||
}
|
||||
|
||||
|
||||
data := map[string]string{
|
||||
"domains": res[0]["domains"].(string),
|
||||
"cert": res[0]["cert"].(string),
|
||||
"key": res[0]["key"].(string),
|
||||
}
|
||||
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user