Merge branch 'feat/new-workflow' of github.com:fudiwei/certimate into next
This commit is contained in:
@@ -173,8 +173,6 @@ func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) erro
|
||||
_, err := d.client.ModifyDomainResolution(request)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
|
||||
xerrors "github.com/pkg/errors"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
||||
edgsdk "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7"
|
||||
edgsdkDtos "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos"
|
||||
)
|
||||
@@ -57,7 +57,10 @@ func NewWithLogger(config *EdgioApplicationsDeployerConfig, logger logger.Logger
|
||||
|
||||
func (d *EdgioApplicationsDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||
// 提取 Edgio 所需的服务端证书和中间证书内容
|
||||
privateCertPem, intermediateCertPem := extractCertChains(certPem)
|
||||
privateCertPem, intermediateCertPem, err := certs.ExtractCertificatesFromPEM(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 上传 TLS 证书
|
||||
// REF: https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
|
||||
@@ -81,32 +84,3 @@ func createSdkClient(clientId, clientSecret string) (*edgsdk.EdgioClient, error)
|
||||
client := edgsdk.NewEdgioClient(clientId, clientSecret, "", "")
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func extractCertChains(certPem string) (primaryCertPem string, intermediateCertPem string) {
|
||||
pemBlocks := make([]*pem.Block, 0)
|
||||
pemData := []byte(certPem)
|
||||
for {
|
||||
block, rest := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
pemBlocks = append(pemBlocks, block)
|
||||
pemData = rest
|
||||
}
|
||||
|
||||
primaryCertPem = ""
|
||||
intermediateCertPem = ""
|
||||
|
||||
if len(pemBlocks) > 0 {
|
||||
primaryCertPem = string(pem.EncodeToMemory(pemBlocks[0]))
|
||||
}
|
||||
|
||||
if len(pemBlocks) > 1 {
|
||||
for i := 1; i < len(pemBlocks); i++ {
|
||||
intermediateCertPem += string(pem.EncodeToMemory(pemBlocks[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return primaryCertPem, intermediateCertPem
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (d *QiniuCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPe
|
||||
|
||||
// 获取域名信息
|
||||
// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
|
||||
getDomainInfoResp, err := d.sdkClient.GetDomainInfo(domain)
|
||||
getDomainInfoResp, err := d.sdkClient.GetDomainInfo(context.TODO(), domain)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDomainInfo'")
|
||||
}
|
||||
@@ -88,14 +88,14 @@ func (d *QiniuCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPe
|
||||
// 判断域名是否已启用 HTTPS。如果已启用,修改域名证书;否则,启用 HTTPS
|
||||
// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
|
||||
if getDomainInfoResp.Https != nil && getDomainInfoResp.Https.CertID != "" {
|
||||
modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
|
||||
modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
|
||||
}
|
||||
|
||||
d.logger.Logt("已修改域名证书", modifyDomainHttpsConfResp)
|
||||
} else {
|
||||
enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(domain, upres.CertId, true, true)
|
||||
enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(context.TODO(), domain, upres.CertId, true, true)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'")
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (u *QiniuSSLCertUploader) Upload(ctx context.Context, certPem string, privk
|
||||
|
||||
// 上传新证书
|
||||
// REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate
|
||||
uploadSslCertResp, err := u.sdkClient.UploadSslCert(certName, certX509.Subject.CommonName, certPem, privkeyPem)
|
||||
uploadSslCertResp, err := u.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPem, privkeyPem)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadSslCert'")
|
||||
}
|
||||
|
||||
48
internal/pkg/utils/certs/extractor.go
Normal file
48
internal/pkg/utils/certs/extractor.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// 从 PEM 编码的证书字符串解析并提取服务器证书和中间证书。
|
||||
//
|
||||
// 入参:
|
||||
// - certPem: 证书 PEM 内容。
|
||||
//
|
||||
// 出参:
|
||||
// - serverCertPem: 服务器证书的 PEM 内容。
|
||||
// - interCertPem: 中间证书的 PEM 内容。
|
||||
// - err: 错误。
|
||||
func ExtractCertificatesFromPEM(certPem string) (serverCertPem string, interCertPem string, err error) {
|
||||
pemBlocks := make([]*pem.Block, 0)
|
||||
pemData := []byte(certPem)
|
||||
for {
|
||||
block, rest := pem.Decode(pemData)
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
break
|
||||
}
|
||||
|
||||
pemBlocks = append(pemBlocks, block)
|
||||
pemData = rest
|
||||
}
|
||||
|
||||
serverCertPem = ""
|
||||
interCertPem = ""
|
||||
|
||||
if len(pemBlocks) == 0 {
|
||||
return "", "", errors.New("failed to decode PEM block")
|
||||
}
|
||||
|
||||
if len(pemBlocks) > 0 {
|
||||
serverCertPem = string(pem.EncodeToMemory(pemBlocks[0]))
|
||||
}
|
||||
|
||||
if len(pemBlocks) > 1 {
|
||||
for i := 1; i < len(pemBlocks); i++ {
|
||||
interCertPem += string(pem.EncodeToMemory(pemBlocks[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return serverCertPem, interCertPem, nil
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// 从 PEM 编码的证书字符串解析并返回一个 x509.Certificate 对象。
|
||||
// PEM 内容可能是包含多张证书的证书链,但只返回第一个证书(即服务器证书)。
|
||||
//
|
||||
// 入参:
|
||||
// - certPem: 证书 PEM 内容。
|
||||
|
||||
@@ -183,7 +183,7 @@ func GetValueOrDefaultAsBool(dict map[string]any, key string, defaultValue bool)
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 将字典解码为指定类型的结构体。
|
||||
// 将字典填充到指定类型的结构体。
|
||||
// 与 [json.Unmarshal] 类似,但传入的是一个 [map[string]interface{}] 对象而非 JSON 格式的字符串。
|
||||
//
|
||||
// 入参:
|
||||
@@ -191,8 +191,8 @@ func GetValueOrDefaultAsBool(dict map[string]any, key string, defaultValue bool)
|
||||
// - output: 结构体指针。
|
||||
//
|
||||
// 出参:
|
||||
// - 错误信息。如果解码失败,则返回错误信息。
|
||||
func Decode(dict map[string]any, output any) error {
|
||||
// - 错误信息。如果填充失败,则返回错误信息。
|
||||
func Populate(dict map[string]any, output any) error {
|
||||
config := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: output,
|
||||
@@ -207,3 +207,8 @@ func Decode(dict map[string]any, output any) error {
|
||||
|
||||
return decoder.Decode(dict)
|
||||
}
|
||||
|
||||
// Deprecated: Use [Populate] instead.
|
||||
func Decode(dict map[string]any, output any) error {
|
||||
return Populate(dict, output)
|
||||
}
|
||||
|
||||
2
internal/pkg/vendors/gname-sdk/client.go
vendored
2
internal/pkg/vendors/gname-sdk/client.go
vendored
@@ -150,7 +150,7 @@ func (c *GnameClient) sendRequestWithResult(path string, params map[string]any,
|
||||
if err := json.Unmarshal(resp.Body(), &jsonResp); err != nil {
|
||||
return fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
if err := maps.Decode(jsonResp, &result); err != nil {
|
||||
if err := maps.Populate(jsonResp, &result); err != nil {
|
||||
return fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
|
||||
29
internal/pkg/vendors/qiniu-sdk/auth.go
vendored
Normal file
29
internal/pkg/vendors/qiniu-sdk/auth.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package qiniusdk
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth"
|
||||
)
|
||||
|
||||
type transport struct {
|
||||
http.RoundTripper
|
||||
mac *auth.Credentials
|
||||
}
|
||||
|
||||
func newTransport(mac *auth.Credentials, tr http.RoundTripper) *transport {
|
||||
if tr == nil {
|
||||
tr = http.DefaultTransport
|
||||
}
|
||||
return &transport{tr, mac}
|
||||
}
|
||||
|
||||
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
token, err := t.mac.SignRequestV2(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Qiniu "+token)
|
||||
return t.RoundTripper.RoundTrip(req)
|
||||
}
|
||||
122
internal/pkg/vendors/qiniu-sdk/client.go
vendored
122
internal/pkg/vendors/qiniu-sdk/client.go
vendored
@@ -1,48 +1,40 @@
|
||||
package qiniusdk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth"
|
||||
"github.com/qiniu/go-sdk/v7/client"
|
||||
)
|
||||
|
||||
const qiniuHost = "https://api.qiniu.com"
|
||||
|
||||
type Client struct {
|
||||
mac *auth.Credentials
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
func NewClient(mac *auth.Credentials) *Client {
|
||||
if mac == nil {
|
||||
mac = auth.Default()
|
||||
}
|
||||
return &Client{mac: mac}
|
||||
|
||||
client := client.DefaultClient
|
||||
client.Transport = newTransport(mac, nil)
|
||||
return &Client{client: &client}
|
||||
}
|
||||
|
||||
func (c *Client) GetDomainInfo(domain string) (*GetDomainInfoResponse, error) {
|
||||
respBytes, err := c.sendReq(http.MethodGet, fmt.Sprintf("domain/%s", domain), nil)
|
||||
if err != nil {
|
||||
func (c *Client) GetDomainInfo(ctx context.Context, domain string) (*GetDomainInfoResponse, error) {
|
||||
resp := new(GetDomainInfoResponse)
|
||||
if err := c.client.Call(ctx, resp, http.MethodGet, c.urlf("domain/%s", domain), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &GetDomainInfoResponse{}
|
||||
err = json.Unmarshal(respBytes, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 {
|
||||
return nil, fmt.Errorf("code: %d, error: %s", *resp.Code, *resp.Error)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) ModifyDomainHttpsConf(domain, certId string, forceHttps, http2Enable bool) (*ModifyDomainHttpsConfResponse, error) {
|
||||
func (c *Client) ModifyDomainHttpsConf(ctx context.Context, domain string, certId string, forceHttps bool, http2Enable bool) (*ModifyDomainHttpsConfResponse, error) {
|
||||
req := &ModifyDomainHttpsConfRequest{
|
||||
DomainInfoHttpsData: DomainInfoHttpsData{
|
||||
CertID: certId,
|
||||
@@ -50,30 +42,14 @@ func (c *Client) ModifyDomainHttpsConf(domain, certId string, forceHttps, http2E
|
||||
Http2Enable: http2Enable,
|
||||
},
|
||||
}
|
||||
|
||||
reqBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
resp := new(ModifyDomainHttpsConfResponse)
|
||||
if err := c.client.CallWithJson(ctx, resp, http.MethodPut, c.urlf("domain/%s/httpsconf", domain), nil, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err := c.sendReq(http.MethodPut, fmt.Sprintf("domain/%s/httpsconf", domain), bytes.NewReader(reqBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ModifyDomainHttpsConfResponse{}
|
||||
err = json.Unmarshal(respBytes, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 {
|
||||
return nil, fmt.Errorf("code: %d, error: %s", *resp.Code, *resp.Error)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnableDomainHttps(domain, certId string, forceHttps, http2Enable bool) (*EnableDomainHttpsResponse, error) {
|
||||
func (c *Client) EnableDomainHttps(ctx context.Context, domain string, certId string, forceHttps bool, http2Enable bool) (*EnableDomainHttpsResponse, error) {
|
||||
req := &EnableDomainHttpsRequest{
|
||||
DomainInfoHttpsData: DomainInfoHttpsData{
|
||||
CertID: certId,
|
||||
@@ -81,83 +57,29 @@ func (c *Client) EnableDomainHttps(domain, certId string, forceHttps, http2Enabl
|
||||
Http2Enable: http2Enable,
|
||||
},
|
||||
}
|
||||
|
||||
reqBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
resp := new(EnableDomainHttpsResponse)
|
||||
if err := c.client.CallWithJson(ctx, resp, http.MethodPut, c.urlf("domain/%s/sslize", domain), nil, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err := c.sendReq(http.MethodPut, fmt.Sprintf("domain/%s/sslize", domain), bytes.NewReader(reqBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EnableDomainHttpsResponse{}
|
||||
err = json.Unmarshal(respBytes, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 {
|
||||
return nil, fmt.Errorf("code: %d, error: %s", *resp.Code, *resp.Error)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) UploadSslCert(name, commonName, certificate, privateKey string) (*UploadSslCertResponse, error) {
|
||||
func (c *Client) UploadSslCert(ctx context.Context, name string, commonName string, certificate string, privateKey string) (*UploadSslCertResponse, error) {
|
||||
req := &UploadSslCertRequest{
|
||||
Name: name,
|
||||
CommonName: commonName,
|
||||
Certificate: certificate,
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
|
||||
reqBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
resp := new(UploadSslCertResponse)
|
||||
if err := c.client.CallWithJson(ctx, resp, http.MethodPost, c.urlf("sslcert"), nil, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err := c.sendReq(http.MethodPost, "sslcert", bytes.NewReader(reqBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &UploadSslCertResponse{}
|
||||
err = json.Unmarshal(respBytes, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 {
|
||||
return nil, fmt.Errorf("qiniu api error, code: %d, error: %s", *resp.Code, *resp.Error)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) sendReq(method string, path string, body io.Reader) ([]byte, error) {
|
||||
func (c *Client) urlf(pathf string, pathargs ...any) string {
|
||||
path := fmt.Sprintf(pathf, pathargs...)
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("%s/%s", qiniuHost, path), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
if err := c.mac.AddToken(auth.TokenQBox, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
r, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
return qiniuHost + "/" + path
|
||||
}
|
||||
|
||||
2
internal/pkg/vendors/qiniu-sdk/models.go
vendored
2
internal/pkg/vendors/qiniu-sdk/models.go
vendored
@@ -13,7 +13,7 @@ type UploadSslCertRequest struct {
|
||||
}
|
||||
|
||||
type UploadSslCertResponse struct {
|
||||
*BaseResponse
|
||||
BaseResponse
|
||||
CertID string `json:"certID"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user