添加SSL证书管理功能,包括安装、续期、列出、撤销和申请证书的命令,同时更新依赖项和修复磁盘使用情况计算逻辑。
This commit is contained in:
@@ -1,10 +1,260 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"agent-wdd/log"
|
||||
"agent-wdd/op"
|
||||
"agent-wdd/utils"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
acmeShUrl = "https://raw.githubusercontent.com/acmesh-official/acme.sh/master/acme.sh"
|
||||
CF_Token = "oXJRP5XI8Zhipa_PtYtB_jy6qWL0I9BosrJEYE8p"
|
||||
CF_Account_ID = "dfaadeb83406ef5ad35da02617af9191"
|
||||
CF_Zone_ID = "511894a4f1357feb905e974e16241ebb"
|
||||
)
|
||||
|
||||
// addAcmeSubcommands acme的相关任务
|
||||
func addAcmeSubcommands(cmd *cobra.Command) {
|
||||
|
||||
// install
|
||||
installCmd := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "安装acme",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("安装acme")
|
||||
|
||||
// 检查是否安装acme
|
||||
if utils.FileExistAndNotNull("/usr/local/bin/acme.sh") {
|
||||
log.Info("acme已安装")
|
||||
return
|
||||
}
|
||||
|
||||
// 下载 这个文件到 /usr/local/bin/acme.sh
|
||||
ok, err := utils.DownloadFile(
|
||||
acmeShUrl,
|
||||
"/usr/local/bin/acme.sh",
|
||||
)
|
||||
if !ok {
|
||||
log.Error("下载acme.sh失败", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置权限
|
||||
utils.PermissionFileExecute("/usr/local/bin/acme.sh")
|
||||
|
||||
// 执行安装命令
|
||||
op.RealTimeCommandExecutor([]string{
|
||||
"/usr/local/bin/acme.sh",
|
||||
"--install-online",
|
||||
"ice@gmail.com",
|
||||
})
|
||||
|
||||
log.Info("acme安装成功")
|
||||
},
|
||||
}
|
||||
|
||||
// renew
|
||||
renewCmd := &cobra.Command{
|
||||
Use: "renew",
|
||||
Short: "acme续期",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("acme续期")
|
||||
|
||||
domain_name := args[0]
|
||||
// 检查domain_name是否是有效的域名
|
||||
if !strings.HasSuffix(domain_name, "107421.xyz") {
|
||||
log.Error("只支持续期107421.xyz的域名")
|
||||
return
|
||||
}
|
||||
|
||||
// 注入环境变量
|
||||
os.Setenv("CF_Token", CF_Token)
|
||||
os.Setenv("CF_Account_ID", CF_Account_ID)
|
||||
os.Setenv("CF_Zone_ID", CF_Zone_ID)
|
||||
|
||||
// 执行命令
|
||||
op.RealTimeCommandExecutor([]string{
|
||||
"/root/.acme.sh/acme.sh",
|
||||
"--renew",
|
||||
"-d",
|
||||
domain_name,
|
||||
})
|
||||
|
||||
// 删除环境变量
|
||||
os.Unsetenv("CF_Token")
|
||||
os.Unsetenv("CF_Account_ID")
|
||||
os.Unsetenv("CF_Zone_ID")
|
||||
|
||||
log.Info("续期acme成功")
|
||||
},
|
||||
}
|
||||
|
||||
// list
|
||||
listCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "列出acme全部的证书",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("列出acme全部的证书")
|
||||
|
||||
// 执行命令
|
||||
ok, output := op.SingleLineCommandExecutor([]string{"/root/.acme.sh/acme.sh", "--list"})
|
||||
if !ok {
|
||||
log.Error("列出acme全部的证书失败", output)
|
||||
return
|
||||
}
|
||||
|
||||
utils.BeautifulPrintListWithTitle(output, "列出acme全部的证书")
|
||||
|
||||
// 获取当前时间
|
||||
now := time.Now()
|
||||
// 设置30天的期限
|
||||
expiryLimit := now.AddDate(0, 0, 30)
|
||||
|
||||
log.Info("以下证书将在30天内过期:")
|
||||
foundExpiring := false
|
||||
|
||||
// 跳过标题行
|
||||
for i := 1; i < len(output); i++ {
|
||||
line := strings.TrimSpace(output[i])
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 分割行内容
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 6 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取域名和更新时间
|
||||
domainName := fields[0]
|
||||
renewDateStr := fields[len(fields)-1]
|
||||
|
||||
// 解析更新时间
|
||||
renewDate, err := time.Parse(time.RFC3339, renewDateStr)
|
||||
if err != nil {
|
||||
log.Error("解析时间失败: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否在30天内过期
|
||||
if renewDate.Before(expiryLimit) {
|
||||
log.Info("域名: %s, 更新时间: %s", domainName, renewDate.Format("2006-01-02"))
|
||||
foundExpiring = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundExpiring {
|
||||
log.Info("没有找到30天内即将过期的证书")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// revoke
|
||||
revokeCmd := &cobra.Command{
|
||||
Use: "revoke",
|
||||
Short: "撤销acme",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("撤销acme")
|
||||
|
||||
// 执行命令
|
||||
op.RealTimeCommandExecutor([]string{"acme.sh", "revoke"})
|
||||
},
|
||||
}
|
||||
|
||||
// 申请一个证书
|
||||
applyCmd := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "申请一个证书",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("申请一个证书")
|
||||
|
||||
domain_name := args[0]
|
||||
// 检查domain_name是否是有效的域名
|
||||
if !strings.HasSuffix(domain_name, "107421.xyz") {
|
||||
log.Error("只支持申请107421.xyz的域名")
|
||||
return
|
||||
}
|
||||
|
||||
// 注入环境变量
|
||||
os.Setenv("CF_Token", CF_Token)
|
||||
os.Setenv("CF_Account_ID", CF_Account_ID)
|
||||
os.Setenv("CF_Zone_ID", CF_Zone_ID)
|
||||
|
||||
// 执行命令
|
||||
op.RealTimeCommandExecutor([]string{
|
||||
"/root/.acme.sh/acme.sh",
|
||||
"--issue",
|
||||
"--dns",
|
||||
"dns_cf",
|
||||
"-d",
|
||||
domain_name,
|
||||
"--keylength",
|
||||
"ec-256",
|
||||
})
|
||||
|
||||
// 删除环境变量
|
||||
os.Unsetenv("CF_Token")
|
||||
os.Unsetenv("CF_Account_ID")
|
||||
os.Unsetenv("CF_Zone_ID")
|
||||
|
||||
log.Info("申请证书成功")
|
||||
},
|
||||
}
|
||||
|
||||
// 安装证书
|
||||
installNginxCmd := &cobra.Command{
|
||||
Use: "nginx",
|
||||
Short: "安装nginx证书",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("安装nginx证书")
|
||||
|
||||
domain_name := args[0]
|
||||
// 检查domain_name是否是有效的域名
|
||||
if !strings.HasSuffix(domain_name, "107421.xyz") {
|
||||
log.Error("只支持安装107421.xyz的域名")
|
||||
return
|
||||
}
|
||||
|
||||
nginx_conf_dir := "/etc/nginx/conf.d/ssl_key/"
|
||||
nginx_ssl_key_full_path := nginx_conf_dir + domain_name + ".key.pem"
|
||||
nginx_ssl_cert_full_path := nginx_conf_dir + domain_name + ".cert.pem"
|
||||
|
||||
// 检查nginx_conf_dir_full_path是否存在
|
||||
utils.CreateFolder(nginx_conf_dir)
|
||||
|
||||
// 执行命令
|
||||
op.RealTimeCommandExecutor([]string{
|
||||
"/root/.acme.sh/acme.sh",
|
||||
"--install-cert",
|
||||
"-d",
|
||||
domain_name,
|
||||
"--key-file",
|
||||
nginx_ssl_key_full_path,
|
||||
"--fullchain-file",
|
||||
nginx_ssl_cert_full_path,
|
||||
"--reloadcmd",
|
||||
"systemctl restart nginx --force",
|
||||
})
|
||||
|
||||
log.Info("安装nginx证书成功")
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
installCmd,
|
||||
renewCmd,
|
||||
listCmd,
|
||||
revokeCmd,
|
||||
applyCmd,
|
||||
installNginxCmd,
|
||||
)
|
||||
}
|
||||
|
||||
258
agent-wdd/cmd/CertManager.go
Normal file
258
agent-wdd/cmd/CertManager.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"agent-wdd/cert_manager_wdd"
|
||||
"agent-wdd/log"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// 证书管理命令选项
|
||||
cfAPIToken string
|
||||
certDir string
|
||||
emailAddr string
|
||||
caServer string
|
||||
daysRenewal int
|
||||
)
|
||||
|
||||
// 初始化证书管理配置
|
||||
func initCertManagerConfig() *cert_manager_wdd.CertManager {
|
||||
// 如果未指定API令牌,提示用户
|
||||
if cfAPIToken == "" {
|
||||
log.Error("未指定Cloudflare API令牌,请使用--token参数设置")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 如果未指定邮箱地址,使用默认值
|
||||
if emailAddr == "" {
|
||||
emailAddr = "cert@example.com"
|
||||
log.Warning("未指定邮箱地址,使用默认值: %s", emailAddr)
|
||||
}
|
||||
|
||||
// 创建证书管理器
|
||||
certManager := cert_manager_wdd.NewCertManager(certDir, cfAPIToken, emailAddr)
|
||||
|
||||
// 设置证书更新阈值天数
|
||||
if daysRenewal > 0 {
|
||||
certManager.DaysBeforeRenewal = daysRenewal
|
||||
}
|
||||
|
||||
// 设置CA服务器
|
||||
if caServer != "" {
|
||||
switch strings.ToLower(caServer) {
|
||||
case "letsencrypt":
|
||||
certManager.SetCAServer(cert_manager_wdd.LetsEncryptCA)
|
||||
case "zerossl":
|
||||
certManager.SetCAServer(cert_manager_wdd.ZeroSSLCA)
|
||||
default:
|
||||
log.Warning("未知的CA服务器: %s,使用默认值", caServer)
|
||||
}
|
||||
}
|
||||
|
||||
return certManager
|
||||
}
|
||||
|
||||
// 添加证书管理命令
|
||||
func addCertManagerSubcommands(cmd *cobra.Command) {
|
||||
// 全局标志
|
||||
cmd.PersistentFlags().StringVar(&cfAPIToken, "token", "", "Cloudflare API令牌")
|
||||
cmd.PersistentFlags().StringVar(&certDir, "cert-dir", "", "证书保存目录")
|
||||
cmd.PersistentFlags().StringVar(&emailAddr, "email", "", "申请证书使用的邮箱")
|
||||
cmd.PersistentFlags().StringVar(&caServer, "ca", "letsencrypt", "CA服务器 (letsencrypt, zerossl)")
|
||||
cmd.PersistentFlags().IntVar(&daysRenewal, "days", 30, "证书更新阈值天数")
|
||||
|
||||
// 申请证书命令
|
||||
applyCmd := &cobra.Command{
|
||||
Use: "apply [域名]",
|
||||
Short: "申请SSL证书",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
domain := args[0]
|
||||
log.Info("开始为域名 %s 申请证书", domain)
|
||||
|
||||
certManager := initCertManagerConfig()
|
||||
certInfo, err := certManager.ApplyCertificate(domain)
|
||||
if err != nil {
|
||||
log.Error("证书申请失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
printCertificateInfo(certInfo)
|
||||
log.Info("证书申请成功")
|
||||
},
|
||||
}
|
||||
|
||||
// 列出证书命令
|
||||
listCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "列出所有证书",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("列出所有证书")
|
||||
|
||||
certManager := initCertManagerConfig()
|
||||
certs, err := certManager.GetAllCerts()
|
||||
if err != nil {
|
||||
log.Error("获取证书列表失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
log.Info("未找到任何证书")
|
||||
return
|
||||
}
|
||||
|
||||
printCertificatesList(certs)
|
||||
},
|
||||
}
|
||||
|
||||
// 查看证书命令
|
||||
showCmd := &cobra.Command{
|
||||
Use: "show [域名]",
|
||||
Short: "查看指定证书的详细信息",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
domain := args[0]
|
||||
log.Info("查看域名 %s 的证书信息", domain)
|
||||
|
||||
certManager := initCertManagerConfig()
|
||||
certs, err := certManager.GetAllCerts()
|
||||
if err != nil {
|
||||
log.Error("获取证书列表失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var targetCert *cert_manager_wdd.CertInfo
|
||||
for _, cert := range certs {
|
||||
if cert.Domain == domain {
|
||||
targetCert = &cert
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if targetCert == nil {
|
||||
log.Error("未找到域名 %s 的证书", domain)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
printCertificateDetailInfo(targetCert)
|
||||
},
|
||||
}
|
||||
|
||||
// 更新证书命令
|
||||
renewCmd := &cobra.Command{
|
||||
Use: "renew [域名]",
|
||||
Short: "更新指定证书",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
domain := args[0]
|
||||
log.Info("更新域名 %s 的证书", domain)
|
||||
|
||||
certManager := initCertManagerConfig()
|
||||
certInfo, err := certManager.RenewCertificate(domain)
|
||||
if err != nil {
|
||||
log.Error("证书更新失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
printCertificateInfo(certInfo)
|
||||
log.Info("证书更新成功")
|
||||
},
|
||||
}
|
||||
|
||||
// 更新所有需要更新的证书
|
||||
renewAllCmd := &cobra.Command{
|
||||
Use: "renew-all",
|
||||
Short: "更新所有需要更新的证书",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("更新所有需要更新的证书")
|
||||
|
||||
certManager := initCertManagerConfig()
|
||||
certs, err := certManager.GetAllCerts()
|
||||
if err != nil {
|
||||
log.Error("获取证书列表失败: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
needRenewalCount := 0
|
||||
for _, cert := range certs {
|
||||
if cert.NeedsRenewal {
|
||||
needRenewalCount++
|
||||
log.Info("证书 %s 需要更新 (剩余 %d 天)", cert.Domain, cert.DaysRemaining)
|
||||
certInfo, err := certManager.RenewCertificate(cert.Domain)
|
||||
if err != nil {
|
||||
log.Error("更新证书 %s 失败: %v", cert.Domain, err)
|
||||
continue
|
||||
}
|
||||
log.Info("证书 %s 更新成功", certInfo.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if needRenewalCount == 0 {
|
||||
log.Info("没有需要更新的证书")
|
||||
} else {
|
||||
log.Info("共更新 %d 个证书", needRenewalCount)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// 将所有子命令添加到主命令
|
||||
cmd.AddCommand(applyCmd, listCmd, showCmd, renewCmd, renewAllCmd)
|
||||
}
|
||||
|
||||
// 打印证书信息
|
||||
func printCertificateInfo(certInfo *cert_manager_wdd.CertInfo) {
|
||||
fmt.Printf("域名: %s\n", certInfo.Domain)
|
||||
fmt.Printf("注册时间: %s\n", certInfo.RegisteredAt.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf("到期时间: %s\n", certInfo.ExpiresAt.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf("剩余天数: %d\n", certInfo.DaysRemaining)
|
||||
fmt.Printf("证书路径: %s\n", certInfo.CertPath)
|
||||
fmt.Printf("密钥路径: %s\n", certInfo.KeyPath)
|
||||
fmt.Printf("CA服务器: %s\n", certInfo.CAName)
|
||||
fmt.Printf("是否需要更新: %v\n", certInfo.NeedsRenewal)
|
||||
fmt.Printf("是否为通配符证书: %v\n", certInfo.WildcardCert)
|
||||
}
|
||||
|
||||
// 打印证书详细信息
|
||||
func printCertificateDetailInfo(certInfo *cert_manager_wdd.CertInfo) {
|
||||
fmt.Printf("=============== 证书详情 ===============\n")
|
||||
printCertificateInfo(certInfo)
|
||||
fmt.Printf("=======================================\n")
|
||||
|
||||
// 打印证书内容(可选)
|
||||
fmt.Printf("\n证书内容:\n")
|
||||
certContent, err := os.ReadFile(certInfo.CertPath)
|
||||
if err == nil {
|
||||
fmt.Println(string(certContent))
|
||||
} else {
|
||||
fmt.Printf("无法读取证书内容: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 打印证书列表
|
||||
func printCertificatesList(certs []cert_manager_wdd.CertInfo) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "域名\t到期时间\t剩余天数\t需要更新\tCA服务器")
|
||||
fmt.Fprintln(w, "------\t--------\t--------\t--------\t--------")
|
||||
|
||||
for _, cert := range certs {
|
||||
needsRenewal := "否"
|
||||
if cert.NeedsRenewal {
|
||||
needsRenewal = "是"
|
||||
}
|
||||
|
||||
expiresAt := cert.ExpiresAt.Format("2006-01-02")
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\n",
|
||||
cert.Domain,
|
||||
expiresAt,
|
||||
cert.DaysRemaining,
|
||||
needsRenewal,
|
||||
cert.CAName)
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
}
|
||||
@@ -104,6 +104,13 @@ func Execute() {
|
||||
|
||||
addDownloadSubcommands(downloadCmd)
|
||||
|
||||
// 11. cert命令
|
||||
certCmd := &cobra.Command{
|
||||
Use: "cert",
|
||||
Short: "SSL证书管理",
|
||||
}
|
||||
addCertManagerSubcommands(certCmd)
|
||||
|
||||
helpCmd := &cobra.Command{
|
||||
Use: "help",
|
||||
Short: "帮助信息",
|
||||
@@ -131,6 +138,7 @@ func Execute() {
|
||||
versionCmd,
|
||||
configCmd,
|
||||
downloadCmd,
|
||||
certCmd,
|
||||
helpCmd,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user