mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 09:21:56 +08:00
Fix Azure KeyVault bug
This commit is contained in:
parent
88b90986b1
commit
364ceb2399
@ -297,11 +297,12 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
|
||||
switch options.Provider {
|
||||
case domain.DeployProviderTypeAzureKeyVault:
|
||||
deployer, err := pAzureKeyVault.NewDeployer(&pAzureKeyVault.DeployerConfig{
|
||||
TenantId: access.TenantId,
|
||||
ClientId: access.ClientId,
|
||||
ClientSecret: access.ClientSecret,
|
||||
CloudName: access.CloudName,
|
||||
KeyVaultName: maputil.GetString(options.ProviderDeployConfig, "keyvaultName"),
|
||||
TenantId: access.TenantId,
|
||||
ClientId: access.ClientId,
|
||||
ClientSecret: access.ClientSecret,
|
||||
CloudName: access.CloudName,
|
||||
KeyVaultName: maputil.GetString(options.ProviderDeployConfig, "keyvaultName"),
|
||||
CertificateName: maputil.GetString(options.ProviderDeployConfig, "certificateName"),
|
||||
})
|
||||
return deployer, err
|
||||
|
||||
|
@ -22,6 +22,8 @@ type DeployerConfig struct {
|
||||
CloudName string `json:"cloudName,omitempty"`
|
||||
// Key Vault 名称。
|
||||
KeyVaultName string `json:"keyvaultName"`
|
||||
// Certificate 名称。
|
||||
CertificateName string `json:"certificateName"`
|
||||
}
|
||||
|
||||
type DeployerProvider struct {
|
||||
@ -38,11 +40,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
}
|
||||
|
||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||
TenantId: config.TenantId,
|
||||
ClientId: config.ClientId,
|
||||
ClientSecret: config.ClientSecret,
|
||||
CloudName: config.CloudName,
|
||||
KeyVaultName: config.KeyVaultName,
|
||||
TenantId: config.TenantId,
|
||||
ClientId: config.ClientId,
|
||||
ClientSecret: config.ClientSecret,
|
||||
CloudName: config.CloudName,
|
||||
KeyVaultName: config.KeyVaultName,
|
||||
CertificateName: config.CertificateName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
||||
|
@ -3,6 +3,7 @@
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
@ -29,6 +30,8 @@ type UploaderConfig struct {
|
||||
CloudName string `json:"cloudName,omitempty"`
|
||||
// Key Vault 名称。
|
||||
KeyVaultName string `json:"keyvaultName"`
|
||||
// Certificate 名称。
|
||||
CertificateName string `json:"certificateName,omitempty"`
|
||||
}
|
||||
|
||||
type UploaderProvider struct {
|
||||
@ -88,6 +91,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
|
||||
}
|
||||
|
||||
for _, certItem := range page.Value {
|
||||
// 如果已经指定了证书名称,则跳过证书名称不匹配的证书
|
||||
if u.config.CertificateName != "" && certItem.ID.Name() != u.config.CertificateName {
|
||||
continue
|
||||
}
|
||||
|
||||
// 先对比证书有效期
|
||||
if certItem.Attributes == nil {
|
||||
continue
|
||||
@ -138,16 +146,28 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新证书名(需符合 Azure 命名规则)
|
||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||
certName := u.config.CertificateName
|
||||
if certName == "" {
|
||||
// 未指定证书名称时,生成包含timestamp的新证书名(需符合 Azure 命名规则)
|
||||
certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||
}
|
||||
|
||||
// Azure Key Vault 不支持导入带有Certificiate Chain的PEM证书。
|
||||
// Issue Link: https://github.com/Azure/azure-cli/issues/19017
|
||||
// 暂时的解决方法是,将 PEM 证书转换成 PFX 格式,然后再导入。
|
||||
pfxCert, err := certutil.TransformCertificateFromPEMToPFX(certPem, privkeyPem, "")
|
||||
if err != nil {
|
||||
u.logger.Error("failed to transform certificate from PEM to PFX", slog.String("certPem", certPem), slog.String("privkeyPem", privkeyPem))
|
||||
return nil, xerrors.Wrap(err, "failed to transform certificate from PEM to PFX")
|
||||
}
|
||||
|
||||
// 导入证书
|
||||
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/import-certificate/import-certificate
|
||||
importCertificateParams := azcertificates.ImportCertificateParameters{
|
||||
Base64EncodedCertificate: to.Ptr(certPem),
|
||||
Base64EncodedCertificate: to.Ptr(base64.StdEncoding.EncodeToString(pfxCert)),
|
||||
CertificatePolicy: &azcertificates.CertificatePolicy{
|
||||
SecretProperties: &azcertificates.SecretProperties{
|
||||
ContentType: to.Ptr("application/x-pem-file"),
|
||||
ContentType: to.Ptr("application/x-pkcs12"),
|
||||
},
|
||||
},
|
||||
Tags: map[string]*string{
|
||||
|
@ -0,0 +1,87 @@
|
||||
package azurekeyvault_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/azure-keyvault"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fTenantId string
|
||||
fAccessKeyId string
|
||||
fSecretAccessKey string
|
||||
fKeyVaultName string
|
||||
fCertificateName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_UPLOADER_AZUREKEYVAULT_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fTenantId, argsPrefix+"TENANTID", "", "")
|
||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||
flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
|
||||
flag.StringVar(&fKeyVaultName, argsPrefix+"KEYVAULTNAME", "", "")
|
||||
flag.StringVar(&fCertificateName, argsPrefix+"CERTIFICATENAME", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./azure_keyvault_test.go -args \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_TENANTID="your-tenant-id" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_ACCESSKEYID="your-app-registration-client-id" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_SECRETACCESSKEY="your-app-registration-client-secret" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_KEYVAULTNAME="your-keyvault-name" \
|
||||
--CERTIMATE_UPLOADER_AZUREKEYVAULT_CERTIFICATENAME="your-certificate-name"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("TENANTID: %v", fTenantId),
|
||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||
fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey),
|
||||
fmt.Sprintf("KEYVAULTNAME: %v", fKeyVaultName),
|
||||
fmt.Sprintf("CERTIFICATENAME: %v", fCertificateName),
|
||||
}, "\n"))
|
||||
|
||||
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
||||
TenantId: fTenantId,
|
||||
ClientId: fAccessKeyId,
|
||||
ClientSecret: fSecretAccessKey,
|
||||
KeyVaultName: fKeyVaultName,
|
||||
CertificateName: fCertificateName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
sres, _ := json.Marshal(res)
|
||||
t.Logf("ok: %s", string(sres))
|
||||
})
|
||||
}
|
@ -2,9 +2,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
import { validAzureKeyVaultCertificateName } from "@/utils/validators";
|
||||
|
||||
type DeployNodeConfigFormAzureKeyVaultConfigFieldValues = Nullish<{
|
||||
keyvaultName: string;
|
||||
certificateName?: string;
|
||||
}>;
|
||||
|
||||
export type DeployNodeConfigFormAzureKeyVaultConfigProps = {
|
||||
@ -33,6 +35,13 @@ const DeployNodeConfigFormAzureKeyVaultConfig = ({
|
||||
.string({ message: t("workflow_node.deploy.form.azure_keyvault_name.placeholder") })
|
||||
.nonempty(t("workflow_node.deploy.form.azure_keyvault_name.placeholder"))
|
||||
.trim(),
|
||||
certificateName: z
|
||||
.string({ message: t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder") })
|
||||
.nullish()
|
||||
.refine((v) =>{
|
||||
if (!v) return true;
|
||||
return validAzureKeyVaultCertificateName(v);
|
||||
}, t("common.errmsg.azure_keyvault_certificate_name_invalid")),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
@ -57,6 +66,14 @@ const DeployNodeConfigFormAzureKeyVaultConfig = ({
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.azure_keyvault_name.placeholder")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="certificateName"
|
||||
label={t("workflow_node.deploy.form.azure_keyvault_certificate_name.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
@ -35,6 +35,7 @@
|
||||
"common.errmsg.port_invalid": "Please enter a valid port",
|
||||
"common.errmsg.ip_invalid": "Please enter a valid IP address",
|
||||
"common.errmsg.url_invalid": "Please enter a valid URL",
|
||||
"common.errmsg.azure_keyvault_certificate_name_invalid": "Certificate name can only contain letters, numbers, and hyphens (-), with a length limit of 1 to 127 characters",
|
||||
|
||||
"common.notifier.bark": "Bark",
|
||||
"common.notifier.dingtalk": "DingTalk",
|
||||
|
@ -234,6 +234,9 @@
|
||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault name",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "Please enter Azure KeyVault name",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.label": "Azure KeyVault certificate name (Optional)",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder": "Please enter Azure KeyVault certificate name",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip": "If not filled in, a default name with a timestamp will be automatically generated.",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "Resource type",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "Please select resource type",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.option.loadbalancer.label": "BLB load balancer",
|
||||
|
@ -35,6 +35,7 @@
|
||||
"common.errmsg.port_invalid": "请输入正确的端口号",
|
||||
"common.errmsg.ip_invalid": "请输入正确的 IP 地址",
|
||||
"common.errmsg.url_invalid": "请输入正确的 URL 地址",
|
||||
"common.errmsg.azure_keyvault_certificate_name_invalid": "证书名称只能包含字母、数字和连字符(-),长度限制为 1 到 127 个字符",
|
||||
|
||||
"common.notifier.bark": "Bark",
|
||||
"common.notifier.dingtalk": "钉钉",
|
||||
|
@ -233,6 +233,9 @@
|
||||
"workflow_node.deploy.form.azure_keyvault_name.label": "Azure KeyVault 名称",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.placeholder": "请输入 Azure KeyVault 名称",
|
||||
"workflow_node.deploy.form.azure_keyvault_name.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/key-vault/general/about-keys-secrets-certificates</a>",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.label": "Azure KeyVault 证书名称 (可选)",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder": "请输入 Azure KeyVault 证书名称",
|
||||
"workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip": "不填写时,会自动生成带时间戳的默认名称。",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "证书替换方式",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "请选择证书替换方式",
|
||||
"workflow_node.deploy.form.baiducloud_appblb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/SSL 监听的证书",
|
||||
|
@ -9,6 +9,11 @@ export const validDomainName = (value: string, { allowWildcard = false }: { allo
|
||||
return re.test(value);
|
||||
};
|
||||
|
||||
export const validAzureKeyVaultCertificateName = (value: string) => {
|
||||
const re = /^[a-zA-Z0-9-]{1,127}$/;
|
||||
return re.test(value);
|
||||
}
|
||||
|
||||
export const validEmailAddress = (value: string) => {
|
||||
const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
return re.test(value);
|
||||
|
Loading…
x
Reference in New Issue
Block a user