提交
This commit is contained in:
153
pkg/certificate/certificate.go
Normal file
153
pkg/certificate/certificate.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
certURL = "https://data-1251486259.cos.ap-beijing.myqcloud.com/copilot-ssl/ssl.pem"
|
||||
keyURL = "https://data-1251486259.cos.ap-beijing.myqcloud.com/copilot-ssl/ssl.key"
|
||||
certPath = "cert/ssl.pem"
|
||||
keyPath = "cert/ssl.key"
|
||||
checkInterval = 1 * time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
mutex sync.Mutex
|
||||
stopChan chan struct{}
|
||||
httpsServerReload chan struct{} // 用于通知需要重载服务器
|
||||
)
|
||||
|
||||
// InitCertificates 初始化证书管理
|
||||
func InitCertificates() (string, string, chan struct{}, error) {
|
||||
// 确保证书目录存在
|
||||
if err := os.MkdirAll(filepath.Dir(certPath), 0755); err != nil {
|
||||
return "", "", nil, fmt.Errorf("failed to create cert directory: %v", err)
|
||||
}
|
||||
|
||||
httpsServerReload = make(chan struct{}, 1)
|
||||
|
||||
// 首次检查和更新证书
|
||||
if err := checkAndUpdateCertificates(); err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
// 启动定时检查
|
||||
startPeriodicCheck()
|
||||
|
||||
return certPath, keyPath, httpsServerReload, nil
|
||||
}
|
||||
|
||||
// StopPeriodicCheck 停止定时检查
|
||||
func StopPeriodicCheck() {
|
||||
if stopChan != nil {
|
||||
close(stopChan)
|
||||
}
|
||||
}
|
||||
|
||||
func startPeriodicCheck() {
|
||||
stopChan = make(chan struct{})
|
||||
go func() {
|
||||
ticker := time.NewTicker(checkInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := checkAndUpdateCertificates(); err != nil {
|
||||
log.Printf("Error checking certificates: %v", err)
|
||||
}
|
||||
case <-stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func checkAndUpdateCertificates() error {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
needsUpdate, err := certificateNeedsUpdate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check certificate: %v", err)
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
if err := downloadFile(certURL, certPath); err != nil {
|
||||
return fmt.Errorf("failed to download certificate: %v", err)
|
||||
}
|
||||
if err := downloadFile(keyURL, keyPath); err != nil {
|
||||
return fmt.Errorf("failed to download key: %v", err)
|
||||
}
|
||||
|
||||
// 通知需要重载服务器
|
||||
select {
|
||||
case httpsServerReload <- struct{}{}:
|
||||
log.Println("Certificates updated, triggering server reload")
|
||||
default:
|
||||
// 如果通道已满,说明已经有一个重载信号在等待处理
|
||||
log.Println("Server reload already pending")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func certificateNeedsUpdate() (bool, error) {
|
||||
if !fileExists(certPath) || !fileExists(keyPath) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
certData, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read certificate: %v", err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return time.Now().Add(24 * time.Hour).After(cert.NotAfter), nil
|
||||
}
|
||||
|
||||
func downloadFile(url string, filepath string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
|
||||
out, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
Reference in New Issue
Block a user