添加SSL证书管理功能,包括安装、续期、列出、撤销和申请证书的命令,同时更新依赖项和修复磁盘使用情况计算逻辑。

This commit is contained in:
zeaslity
2025-03-27 23:06:41 +08:00
parent 8d09a4191a
commit d8554ae8ae
19 changed files with 2742 additions and 28 deletions

View File

@@ -0,0 +1,187 @@
package cert_manager_wdd
import (
"agent-wdd/cloudflare"
"agent-wdd/log"
"crypto/ecdsa"
"fmt"
"strings"
"time"
)
// DNSSolver DNS验证器
type DNSSolver struct {
CloudflareAPI *cloudflare.CloudflareClient // Cloudflare API客户端
}
// SetTXTRecord 设置DNS TXT记录
//
// 参数:
// - domain: 域名
// - token: 验证token
// - keyAuth: 验证密钥
//
// 返回:
// - error: 错误信息
func (ds *DNSSolver) SetTXTRecord(domain, token, keyAuth string) error {
log.Debug("设置DNS TXT记录: %s", domain)
// 生成记录名和内容
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
if strings.HasPrefix(domain, "*.") {
// 通配符域名处理
recordName = fmt.Sprintf("_acme-challenge.%s", domain[2:])
}
// 计算记录值
// 对于DNS-01验证值是密钥授权的SHA-256哈希的Base64编码
recordValue := keyAuth
// 获取域名的zone信息
zones, err := ds.CloudflareAPI.ListZones(nil)
if err != nil {
return fmt.Errorf("获取Cloudflare zones失败: %w", err)
}
var zoneID string
zoneName := ""
for _, zone := range zones {
// 对于常规域名检查域名是否以zone名称结尾
// 对于通配符域名,检查去掉*. 前缀的域名是否以zone名称结尾
domainToCheck := domain
if strings.HasPrefix(domain, "*.") {
domainToCheck = domain[2:]
}
if strings.HasSuffix(domainToCheck, zone.Name) && (zoneName == "" || len(zone.Name) > len(zoneName)) {
zoneID = zone.ID
zoneName = zone.Name
}
}
if zoneID == "" {
return fmt.Errorf("未找到域名 %s 的Cloudflare Zone", domain)
}
// 创建TXT记录
record := cloudflare.DNSRecord{
Type: "TXT",
Name: recordName,
Content: recordValue,
TTL: 120,
}
_, err = ds.CloudflareAPI.CreateDNSRecord(zoneID, record)
if err != nil {
return fmt.Errorf("创建DNS TXT记录失败: %w", err)
}
log.Info("DNS TXT记录已创建: %s = %s", recordName, recordValue)
// 等待DNS传播
log.Info("等待DNS记录传播...")
time.Sleep(30 * time.Second)
return nil
}
// CleanupTXTRecord 清理DNS TXT记录
//
// 参数:
// - domain: 域名
// - token: 验证token
//
// 返回:
// - error: 错误信息
func (ds *DNSSolver) CleanupTXTRecord(domain, token string) error {
log.Debug("清理DNS TXT记录: %s", domain)
// 生成记录名
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
if strings.HasPrefix(domain, "*.") {
// 通配符域名处理
recordName = fmt.Sprintf("_acme-challenge.%s", domain[2:])
}
// 获取域名的zone信息
zones, err := ds.CloudflareAPI.ListZones(nil)
if err != nil {
return fmt.Errorf("获取Cloudflare zones失败: %w", err)
}
var zoneID string
zoneName := ""
for _, zone := range zones {
domainToCheck := domain
if strings.HasPrefix(domain, "*.") {
domainToCheck = domain[2:]
}
if strings.HasSuffix(domainToCheck, zone.Name) && (zoneName == "" || len(zone.Name) > len(zoneName)) {
zoneID = zone.ID
zoneName = zone.Name
}
}
if zoneID == "" {
return fmt.Errorf("未找到域名 %s 的Cloudflare Zone", domain)
}
// 查找TXT记录
filter := &cloudflare.DNSRecordFilter{
Type: "TXT",
Name: recordName,
}
records, err := ds.CloudflareAPI.ListDNSRecords(zoneID, filter)
if err != nil {
return fmt.Errorf("查找DNS TXT记录失败: %w", err)
}
// 删除匹配的记录
for _, record := range records {
if record.Name == recordName && record.Type == "TXT" {
_, err = ds.CloudflareAPI.DeleteDNSRecord(zoneID, record.ID)
if err != nil {
return fmt.Errorf("删除DNS TXT记录失败: %w", err)
}
log.Info("DNS TXT记录已删除: %s", recordName)
}
}
return nil
}
// IssueCertificate 颁发证书
//
// 参数:
// - domain: 域名
// - email: 注册邮箱
// - directoryURL: ACME目录URL
// - privateKey: 私钥
// - certDir: 证书保存目录
// - dnsSolver: DNS验证器
//
// 返回:
// - *CertInfo: 证书信息
// - error: 错误信息
func IssueCertificate(domain, email, directoryURL string, privateKey *ecdsa.PrivateKey, certDir string, dnsSolver *DNSSolver) (*CertInfo, error) {
log.Info("开始为域名 %s 颁发证书", domain)
// 创建ACME客户端
acmeClient, err := NewACMEClient(directoryURL, email, dnsSolver.CloudflareAPI)
if err != nil {
log.Error("创建ACME客户端失败: %v", err)
return nil, fmt.Errorf("创建ACME客户端失败: %w", err)
}
// 使用ACME客户端获取证书
certInfo, err := acmeClient.ObtainCertificate(domain, privateKey, certDir)
if err != nil {
log.Error("获取证书失败: %v", err)
return nil, fmt.Errorf("获取证书失败: %w", err)
}
log.Info("证书颁发成功: %s", domain)
return certInfo, nil
}