Compare commits

...

2 Commits

Author SHA1 Message Date
75a8fd1bcf 自动续期 2024-10-25 11:51:54 +08:00
784e8b9b2f pem解析 2024-10-25 11:21:55 +08:00
7 changed files with 172 additions and 30 deletions

56
main.go
View File

@ -2,16 +2,54 @@ package main
import ( import (
"acme-mana/src" "acme-mana/src"
"crypto/x509"
"encoding/json"
"encoding/pem"
"github.com/go-acme/lego/v4/certificate"
"log"
"os"
"path"
"path/filepath"
) )
func main() { func main() {
//config := src.GetAppConfig() //src.Start()
//fmt.Println(config) testParseCert()
//src.Apply(config.Domains[0]) }
//fmt.Println(os.Getwd())
//err := os.MkdirAll("cert\\abc", 0777) func testParseCert() {
//if err != nil { // 读取
// fmt.Println(err) dir := src.GetAppConfig().CertDir
//} dir = filepath.Join(dir, "acme.zzzykj.cn")
src.Start() certFile := path.Join(dir, src.CertFileName)
certBytes, err := os.ReadFile(certFile)
if err != nil {
panic(err)
}
block, _ := pem.Decode(certBytes)
if block == nil {
log.Fatalf("Failed to decode PEM block")
return
}
certParse, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Fatalf("Failed to parse certificate: %v", err)
}
//info, err := json.Marshal(certParse)
//if err != nil {
// log.Fatalf("Failed to marshal certificate: %v", err)
//}
certInfo := src.CertInfo{
Cert: certificate.Resource{},
Info: *certParse,
}
info, err := json.Marshal(certInfo)
if err != nil {
log.Fatalf("Failed to marshal certificate: %v", err)
}
log.Println(string(info))
//log.Println(string(info))
//log.Println(certParse)
} }

View File

@ -7,6 +7,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"encoding/pem"
"github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log" "github.com/go-acme/lego/v4/log"
@ -89,7 +90,7 @@ func saveCertFile(cert *certificate.Resource, name string) {
dir = filepath.Join(dir, name) dir = filepath.Join(dir, name)
_, err := os.Stat(dir) _, err := os.Stat(dir)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err := os.MkdirAll(dir, 0644) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
log.Infof("创建目录 %s 失败", dir) log.Infof("创建目录 %s 失败", dir)
log.Fatal(err) log.Fatal(err)
@ -97,18 +98,24 @@ func saveCertFile(cert *certificate.Resource, name string) {
log.Infof("创建目录 %s", dir) log.Infof("创建目录 %s", dir)
} }
err = os.WriteFile(path.Join(dir, "cert.crt"), cert.Certificate, 0644) certBytes := cert.Certificate
err = os.WriteFile(path.Join(dir, CertFileName), certBytes, 0755)
if err != nil { if err != nil {
log.Fatalf("Failed to save certificate: %v", err) log.Fatalf("Failed to save certificate: %v", err)
} }
err = os.WriteFile(path.Join(dir, "cert.key"), cert.PrivateKey, 0644) err = os.WriteFile(path.Join(dir, KeyFileName), cert.PrivateKey, 0755)
if err != nil { if err != nil {
log.Fatalf("Failed to save private key: %v", err) log.Fatalf("Failed to save private key: %v", err)
} }
// 获取 cert 的过期时间 block, _ := pem.Decode(certBytes)
certParse, err := x509.ParseCertificate(cert.Certificate) if block == nil {
log.Fatalf("Failed to decode PEM block")
return
}
certParse, err := x509.ParseCertificate(block.Bytes)
if err != nil { if err != nil {
log.Fatalf("Failed to parse certificate: %v", err) log.Fatalf("Failed to parse certificate: %v", err)
} }
@ -121,7 +128,7 @@ func saveCertFile(cert *certificate.Resource, name string) {
if err != nil { if err != nil {
log.Fatalf("Failed to marshal certificate: %v", err) log.Fatalf("Failed to marshal certificate: %v", err)
} }
err = os.WriteFile(path.Join(dir, "info.json"), certJson, 0644) err = os.WriteFile(path.Join(dir, CertInfoFileName), certJson, 0644)
if err != nil { if err != nil {
log.Fatalf("Failed to save certificate info: %v", err) log.Fatalf("Failed to save certificate info: %v", err)
} }

View File

@ -6,10 +6,7 @@ import (
"os" "os"
) )
/* // InitSocket /*
*
初始化Socket
*/
func InitSocket() { func InitSocket() {
log.Println("Start listen command") log.Println("Start listen command")
// 删除旧的 socket 文件 // 删除旧的 socket 文件

View File

@ -34,6 +34,10 @@ func Start() {
daemonStatus() daemonStatus()
case "dump": case "dump":
dumpConfig() dumpConfig()
case "domains":
showDomains()
case "apply":
applyOnce()
case "-s": case "-s":
daemonCommand() daemonCommand()
default: default:
@ -79,14 +83,14 @@ func daemonStart() {
return return
} }
path, err := os.Executable() workPath, err := os.Executable()
if err != nil { if err != nil {
log.Fatalf("Failed to get executable path: %v", err) log.Fatalf("Failed to get executable path: %v", err)
} }
cmd := exec.Cmd{ cmd := exec.Cmd{
Path: path, Path: workPath,
Args: os.Args, Args: os.Args,
Dir: filepath.Dir(path), Dir: filepath.Dir(workPath),
Env: append(os.Environ(), "GO_DAEMON=1"), Env: append(os.Environ(), "GO_DAEMON=1"),
//Stdin: os.Stdin, //Stdin: os.Stdin,
Stdout: stdout, Stdout: stdout,
@ -97,11 +101,6 @@ func daemonStart() {
SysProcAttr: &syscall.SysProcAttr{}, SysProcAttr: &syscall.SysProcAttr{},
} }
//cmd := exec.Command(os.Args[0])
//cmd.Env = append(os.Environ(), "GO_DAEMON=1")
//cmd.Stdout = os.Stdout
//cmd.Stderr = os.Stderr
//cmd.SysProcAttr = &syscall.SysProcAttr{}
log.Println("Starting daemon...") log.Println("Starting daemon...")
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
@ -166,6 +165,31 @@ func dumpConfig() {
log.Println(string(config)) log.Println(string(config))
} }
func applyOnce() {
if len(os.Args) < 3 {
log.Fatalf("Please enter domain name!")
}
name := os.Args[2]
if name == "" {
log.Fatalf("No domain specified!")
}
domain := GetAppConfig().FindDomain(name)
if domain == nil {
log.Fatalf("Domain not found: %s", name)
}
Apply(*domain)
}
func showDomains() {
domains := GetAppConfig().Domains
// 格式化为json并打印
config, err := json.MarshalIndent(domains, "", " ")
if err != nil {
log.Fatalf("Failed to marshal config: %v", err)
}
log.Println(string(config))
}
/* /*
守护进程接收名称 守护进程接收名称
*/ */

View File

@ -43,7 +43,7 @@ func getCert(c *gin.Context) {
}) })
return return
} }
crtFilePath := path.Join(dir, "cert.crt") crtFilePath := path.Join(dir, CertFileName)
crtContent, err := os.ReadFile(crtFilePath) crtContent, err := os.ReadFile(crtFilePath)
if err != nil { if err != nil {
c.JSON(200, gin.H{ c.JSON(200, gin.H{
@ -54,7 +54,7 @@ func getCert(c *gin.Context) {
} }
crt := string(crtContent) crt := string(crtContent)
keyFilePath := path.Join(dir, "cert.key") keyFilePath := path.Join(dir, KeyFileName)
keyContent, err := os.ReadFile(keyFilePath) keyContent, err := os.ReadFile(keyFilePath)
if err != nil { if err != nil {
c.JSON(200, gin.H{ c.JSON(200, gin.H{

View File

@ -1,7 +1,79 @@
package src package src
import "log" import (
"encoding/json"
"log"
"os"
"path"
"time"
)
var AutoRefreshCertTicker = time.NewTicker(time.Hour)
func AutoRefreshCert() { func AutoRefreshCert() {
log.Println("Start auto refresh cert") log.Println("Start auto refresh cert")
defer AutoRefreshCertTicker.Stop()
for {
select {
case <-AutoRefreshCertTicker.C:
doRefreshCert()
}
}
}
func doRefreshCert() {
domains := GetAppConfig().Domains
for _, domain := range domains {
doRefreshCertOnce(domain)
}
}
func doRefreshCertOnce(domain Domain) {
name := domain.Name
dir := GetAppConfig().CertDir
certDir := path.Join(dir, name)
// 判断文件夹和证书文件是否存在
_, err := os.Stat(certDir)
if os.IsNotExist(err) {
log.Println("Applying for a certificate, Domain: {} certificate directory does not exist!", name)
Apply(domain)
return
}
if existFile(certDir, CertFileName) {
log.Println("Applying for a certificate, Domain: {} {} does not exist!", name, CertFileName)
Apply(domain)
return
}
if existFile(certDir, KeyFileName) {
log.Println("Applying for a certificate, Domain: {} {} does not exist!", name, KeyFileName)
Apply(domain)
return
}
if existFile(certDir, CertInfoFileName) {
log.Println("Applying for a certificate, Domain: {} {} does not exist!", name, CertInfoFileName)
Apply(domain)
return
}
infoFile := path.Join(dir, CertInfoFileName)
infoBytes, err := os.ReadFile(infoFile)
if err != nil {
log.Println("Failed to read cert info file, Domain: {}", name)
}
var certInfo CertInfo
err = json.Unmarshal(infoBytes, &certInfo)
if err != nil {
log.Println("Failed to parse cert info file, Domain: {}", name)
}
log.Println("Checking if the certificate is expired, Domain: {}", name)
if certInfo.Info.NotAfter.Sub(time.Now()) < 7*24*time.Hour {
log.Println("Apply for a certificate that is about to expire, domain name:", name)
Apply(domain)
}
}
func existFile(dir string, fileName string) bool {
f := path.Join(dir, fileName)
_, err := os.Stat(f)
return !os.IsNotExist(err)
} }

View File

@ -3,6 +3,10 @@ package src
const PidFile = "acme-mana.pid" const PidFile = "acme-mana.pid"
const SocketFile = "acme-mana.sock" const SocketFile = "acme-mana.sock"
const CertFileName = "fullchain.pem"
const KeyFileName = "privkey.pem"
const CertInfoFileName = "info.json"
var appConfig AppConfig = ReadConfig() var appConfig AppConfig = ReadConfig()
func GetAppConfig() AppConfig { func GetAppConfig() AppConfig {