mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 15:51:11 +08:00
Add files via upload
This commit is contained in:
94
backend/internal/cert/apply/account.go
Normal file
94
backend/internal/cert/apply/account.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MyUser struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (u *MyUser) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
|
||||
func (u *MyUser) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
}
|
||||
|
||||
func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
}
|
||||
|
||||
func SaveUserToDB(db *public.Sqlite, user *MyUser) error {
|
||||
keyBytes, err := x509.MarshalPKCS8PrivateKey(user.key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
regBytes := []byte("")
|
||||
if user.Registration != nil {
|
||||
regBytes, err = json.Marshal(user.Registration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: keyBytes,
|
||||
})
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = db.Insert(map[string]interface{}{
|
||||
"email": user.Email,
|
||||
"private_key": string(pemBytes),
|
||||
"reg": regBytes,
|
||||
"create_time": now,
|
||||
"update_time": now,
|
||||
"type": "Let's Encrypt",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func LoadUserFromDB(db *public.Sqlite, email string) (*MyUser, error) {
|
||||
data, err := db.Where(`email=?`, []interface{}{email}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
regStr, ok := data[0]["reg"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid reg data")
|
||||
}
|
||||
regBytes := []byte(regStr)
|
||||
privPEM, ok := data[0]["private_key"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid private key data")
|
||||
}
|
||||
privateKey, err := public.ParsePrivateKey([]byte(privPEM))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var reg *registration.Resource
|
||||
if len(regBytes) > 0 {
|
||||
reg = ®istration.Resource{}
|
||||
if err := json.Unmarshal(regBytes, reg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &MyUser{
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
Registration: reg,
|
||||
}, nil
|
||||
}
|
||||
245
backend/internal/cert/apply/apply.go
Normal file
245
backend/internal/cert/apply/apply.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/internal/access"
|
||||
"ALLinSSL/backend/internal/cert"
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func GetDNSProvider(providerName string, creds map[string]string) (challenge.Provider, error) {
|
||||
switch providerName {
|
||||
case "tencentcloud":
|
||||
config := tencentcloud.NewDefaultConfig()
|
||||
config.SecretID = creds["secret_id"]
|
||||
config.SecretKey = creds["secret_key"]
|
||||
return tencentcloud.NewDNSProviderConfig(config)
|
||||
|
||||
// case "cloudflare":
|
||||
// config := cloudflare.NewDefaultConfig()
|
||||
// config.AuthToken = creds["CLOUDFLARE_API_TOKEN"]
|
||||
// return cloudflare.NewDNSProviderConfig(config)
|
||||
|
||||
case "aliyun":
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.APIKey = creds["access_key"]
|
||||
config.SecretKey = creds["access_secret"]
|
||||
return alidns.NewDNSProviderConfig(config)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName)
|
||||
}
|
||||
}
|
||||
|
||||
func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
db, err := GetSqlite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
// 获取上次申请的证书
|
||||
runId, ok := cfg["_runId"].(string)
|
||||
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 {
|
||||
endTimeStr, ok := certs[i]["end_time"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
endTime, _ := time.Parse(layout, endTimeStr)
|
||||
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("上次证书申请成功,剩余天数:%d 大于%d天,已跳过申请复用此证书", int(maxDays), cfgEnd))
|
||||
return map[string]any{
|
||||
"cert": certObj["cert"],
|
||||
"key": certObj["key"],
|
||||
"issuerCert": certObj["issuer_cert"],
|
||||
}, 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))
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 DNS provider 失败: %v", err)
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
|
||||
// 跳过预检查
|
||||
return true, nil
|
||||
}),
|
||||
dns01.AddRecursiveNameservers([]string{
|
||||
"8.8.8.8:53",
|
||||
"1.1.1.1:53",
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fmt.Println(strings.Split(domains, ","))
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: strings.Split(domains, ","),
|
||||
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