Files
EdgeAPI/internal/tasks/ssl_cert_update_ocsp_task.go

199 lines
5.1 KiB
Go
Raw Normal View History

2024-05-17 18:27:26 +08:00
// Copyright 2022 GoEdge CDN goedge.cdn@gmail.com. All rights reserved.
2022-03-10 11:54:35 +08:00
package tasks
import (
"bytes"
"crypto"
"crypto/tls"
"crypto/x509"
"errors"
2023-08-11 16:13:33 +08:00
"fmt"
2024-07-27 14:15:25 +08:00
"io"
"net/http"
"time"
2022-03-10 11:54:35 +08:00
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/iwind/TeaGo/dbs"
"golang.org/x/crypto/ocsp"
)
func init() {
dbs.OnReadyDone(func() {
goman.New(func() {
NewSSLCertUpdateOCSPTask(1 * time.Minute).Start()
2022-03-10 11:54:35 +08:00
})
})
}
type SSLCertUpdateOCSPTask struct {
BaseTask
2022-03-18 17:08:51 +08:00
ticker *time.Ticker
httpClient *http.Client
2022-03-10 11:54:35 +08:00
}
func NewSSLCertUpdateOCSPTask(duration time.Duration) *SSLCertUpdateOCSPTask {
2022-03-10 11:54:35 +08:00
return &SSLCertUpdateOCSPTask{
ticker: time.NewTicker(duration),
2022-03-18 17:08:51 +08:00
httpClient: utils.SharedHttpClient(5 * time.Second),
2022-03-10 11:54:35 +08:00
}
}
func (this *SSLCertUpdateOCSPTask) Start() {
for range this.ticker.C {
err := this.Loop()
2022-03-10 11:54:35 +08:00
if err != nil {
this.logErr("SSLCertUpdateOCSPTask", err.Error())
2022-03-10 11:54:35 +08:00
}
}
}
func (this *SSLCertUpdateOCSPTask) Loop() error {
// 检查是否为主节点
if !this.IsPrimaryNode() {
return nil
2022-03-10 11:54:35 +08:00
}
var tx *dbs.Tx
// TODO 将来可以设置单次任务条数
2022-03-18 17:08:51 +08:00
var size int64 = 60
2022-03-18 20:21:24 +08:00
var maxTries = 5
certs, err := models.SharedSSLCertDAO.ListCertsToUpdateOCSP(tx, maxTries, size)
2022-03-10 11:54:35 +08:00
if err != nil {
2023-08-11 16:13:33 +08:00
return fmt.Errorf("list certs failed: %w", err)
2022-03-10 11:54:35 +08:00
}
2022-03-18 17:08:51 +08:00
if len(certs) == 0 {
return nil
}
2022-03-10 11:54:35 +08:00
2022-03-18 17:08:51 +08:00
// 锁定
for _, cert := range certs {
err := models.SharedSSLCertDAO.PrepareCertOCSPUpdating(tx, int64(cert.Id))
if err != nil {
2023-08-11 16:13:33 +08:00
return fmt.Errorf("prepare cert ocsp updating failed: %w", err)
2022-03-18 17:08:51 +08:00
}
}
2022-03-10 11:54:35 +08:00
for _, cert := range certs {
2022-03-18 20:21:24 +08:00
ocspData, expiresAt, err := this.UpdateCertOCSP(cert)
2022-03-10 11:54:35 +08:00
var errString = ""
2022-03-18 20:21:24 +08:00
var hasErr = false
2022-03-10 11:54:35 +08:00
if err != nil {
errString = err.Error()
2022-03-18 20:21:24 +08:00
hasErr = true
2022-03-11 17:39:08 +08:00
remotelogs.Warn("SSLCertUpdateOCSPTask", "update ocsp failed: "+errString)
2022-03-10 11:54:35 +08:00
}
2022-03-18 20:21:24 +08:00
err = models.SharedSSLCertDAO.UpdateCertOCSP(tx, int64(cert.Id), ocspData, expiresAt, hasErr, errString)
2022-03-10 11:54:35 +08:00
if err != nil {
2023-08-11 16:13:33 +08:00
return fmt.Errorf("update ocsp failed: %w", err)
2022-03-10 11:54:35 +08:00
}
}
return nil
}
2022-03-18 17:08:51 +08:00
// UpdateCertOCSP 更新单个证书OCSP
2022-03-18 20:21:24 +08:00
func (this *SSLCertUpdateOCSPTask) UpdateCertOCSP(certOne *models.SSLCert) (ocspData []byte, expiresAt int64, err error) {
2022-03-22 22:11:32 +08:00
if certOne.IsCA || len(certOne.CertData) == 0 || len(certOne.KeyData) == 0 {
2022-03-10 11:54:35 +08:00
return
}
2022-03-22 19:30:30 +08:00
keyPair, err := tls.X509KeyPair(certOne.CertData, certOne.KeyData)
2022-03-10 11:54:35 +08:00
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("parse certificate failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
if len(keyPair.Certificate) == 0 {
2022-03-18 20:21:24 +08:00
return nil, 0, nil
2022-03-10 11:54:35 +08:00
}
var certData = keyPair.Certificate[0]
cert, err := x509.ParseCertificate(certData)
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("parse certificate block failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
// 是否已过期
var now = time.Now()
if cert.NotBefore.After(now) || cert.NotAfter.Before(now) {
2022-03-18 20:21:24 +08:00
return nil, 0, nil
2022-03-10 11:54:35 +08:00
}
if len(cert.IssuingCertificateURL) == 0 || len(cert.OCSPServer) == 0 {
2022-03-18 20:21:24 +08:00
return nil, 0, nil
2022-03-10 11:54:35 +08:00
}
if len(cert.DNSNames) == 0 {
2022-03-18 20:21:24 +08:00
return nil, 0, nil
2022-03-10 11:54:35 +08:00
}
var issuerURL = cert.IssuingCertificateURL[0]
var ocspServerURL = cert.OCSPServer[0]
issuerReq, err := http.NewRequest(http.MethodGet, issuerURL, nil)
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("request issuer certificate failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
issuerReq.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
2022-03-18 17:08:51 +08:00
issuerResp, err := this.httpClient.Do(issuerReq)
2022-03-10 11:54:35 +08:00
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("request issuer certificate failed: '" + issuerURL + "': " + err.Error())
2022-03-10 11:54:35 +08:00
}
defer func() {
_ = issuerResp.Body.Close()
}()
2022-08-04 11:41:42 +08:00
issuerData, err := io.ReadAll(issuerResp.Body)
2022-03-10 11:54:35 +08:00
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("read issuer certificate failed: '" + issuerURL + "': " + err.Error())
2022-03-10 11:54:35 +08:00
}
issuerCert, err := x509.ParseCertificate(issuerData)
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("parse issuer certificate failed: '" + issuerURL + "': " + err.Error())
2022-03-10 11:54:35 +08:00
}
buf, err := ocsp.CreateRequest(cert, issuerCert, &ocsp.RequestOptions{
Hash: crypto.SHA1,
})
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("create ocsp request failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
ocspReq, err := http.NewRequest(http.MethodPost, ocspServerURL, bytes.NewBuffer(buf))
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("request ocsp failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
ocspReq.Header.Set("Content-Type", "application/ocsp-request")
ocspReq.Header.Set("Accept", "application/ocsp-response")
2022-03-18 17:08:51 +08:00
ocspResp, err := this.httpClient.Do(ocspReq)
2022-03-10 11:54:35 +08:00
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("request ocsp failed: '" + ocspServerURL + "': " + err.Error())
2022-03-10 11:54:35 +08:00
}
defer func() {
_ = ocspResp.Body.Close()
}()
2022-08-04 11:41:42 +08:00
respData, err := io.ReadAll(ocspResp.Body)
2022-03-10 11:54:35 +08:00
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("read ocsp failed: '" + ocspServerURL + "': " + err.Error())
2022-03-10 11:54:35 +08:00
}
ocspResult, err := ocsp.ParseResponse(respData, issuerCert)
if err != nil {
2022-03-18 20:21:24 +08:00
return nil, 0, errors.New("decode ocsp failed: " + err.Error())
2022-03-10 11:54:35 +08:00
}
// 只返回Good的ocsp
if ocspResult.Status == ocsp.Good {
2022-03-18 20:21:24 +08:00
return respData, ocspResult.NextUpdate.Unix(), nil
2022-03-10 11:54:35 +08:00
}
2022-03-18 20:21:24 +08:00
return nil, 0, nil
2022-03-10 11:54:35 +08:00
}