feat: support configuring 'subscriptionId' and 'resourceGroupName' for azure credentials

This commit is contained in:
Fu Diwei
2025-12-22 13:42:11 +08:00
parent 1e9e44fc5c
commit 58b3180043
8 changed files with 61 additions and 56 deletions
@@ -21,6 +21,8 @@ func init() {
TenantId: credentials.TenantId,
ClientId: credentials.ClientId,
ClientSecret: credentials.ClientSecret,
SubscriptionId: credentials.SubscriptionId,
ResourceGroupName: credentials.ResourceGroupName,
CloudName: credentials.CloudName,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
+6 -4
View File
@@ -87,10 +87,12 @@ type AccessConfigForAWS struct {
}
type AccessConfigForAzure struct {
TenantId string `json:"tenantId"`
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
CloudName string `json:"cloudName,omitempty"`
TenantId string `json:"tenantId"`
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
SubscriptionId string `json:"subscriptionId,omitempty"`
ResourceGroupName string `json:"resourceGroupName,omitempty"`
CloudName string `json:"cloudName,omitempty"`
}
type AccessConfigForBaiduCloud struct {
@@ -14,6 +14,8 @@ type ChallengerConfig struct {
TenantId string `json:"tenantId"`
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
SubscriptionId string `json:"subscriptionId,omitempty"`
ResourceGroupName string `json:"resourceGroupName,omitempty"`
CloudName string `json:"cloudName,omitempty"`
DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int `json:"dnsTTL,omitempty"`
@@ -29,6 +31,8 @@ func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
providerConfig.TenantID = config.TenantId
providerConfig.ClientID = config.ClientId
providerConfig.ClientSecret = config.ClientSecret
providerConfig.SubscriptionID = config.SubscriptionId
providerConfig.ResourceGroup = config.ResourceGroupName
if config.CloudName != "" {
env, err := azenv.GetCloudEnvConfiguration(config.CloudName)
if err != nil {
@@ -44,7 +44,7 @@ func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
return nil, errors.New("the configuration of the certmgr provider is nil")
}
client, err := createSDKClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName)
client, err := createSDKClient(config.CloudName, config.TenantId, config.ClientId, config.ClientSecret, config.KeyVaultName)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
@@ -72,14 +72,12 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*cert
}
// 生成 Azure 业务参数
const TAG_CERTCN = "certimate/cert-cn"
const TAG_CERTSN = "certimate/cert-sn"
certCN := certX509.Subject.CommonName
certSN := certX509.SerialNumber.Text(16)
// 获取证书列表,避免重复上传
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificates/get-certificates
listCertificatesPager := c.sdkClient.NewListCertificatePropertiesPager(nil)
listCertificatesPager := c.sdkClient.NewListCertificatePropertiesPager(&azcertificates.ListCertificatePropertiesOptions{})
for listCertificatesPager.More() {
page, err := listCertificatesPager.NextPage(ctx)
if err != nil {
@@ -99,14 +97,14 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*cert
}
// 对比 Tag 中的通用名称
if v, ok := certItem.Tags[TAG_CERTCN]; !ok || v == nil {
if v, ok := certItem.Tags[kvTagCertCN]; !ok || v == nil {
continue
} else if *v != certCN {
continue
}
// 对比 Tag 中的序列号
if v, ok := certItem.Tags[TAG_CERTSN]; !ok || v == nil {
if v, ok := certItem.Tags[kvTagCertSN]; !ok || v == nil {
continue
} else if *v != certSN {
continue
@@ -153,8 +151,8 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*cert
},
},
Tags: map[string]*string{
TAG_CERTCN: to.Ptr(certCN),
TAG_CERTSN: to.Ptr(certSN),
kvTagCertCN: to.Ptr(certCN),
kvTagCertSN: to.Ptr(certSN),
},
}
importCertificateResp, err := c.sdkClient.ImportCertificate(ctx, certName, importCertificateParams, nil)
@@ -208,8 +206,8 @@ func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, pri
},
},
Tags: map[string]*string{
"certimate/cert-cn": to.Ptr(certX509.Subject.CommonName),
"certimate/cert-sn": to.Ptr(certX509.SerialNumber.Text(16)),
kvTagCertCN: to.Ptr(certX509.Subject.CommonName),
kvTagCertSN: to.Ptr(certX509.SerialNumber.Text(16)),
},
}
importCertificateResp, err := c.sdkClient.ImportCertificate(ctx, certIdOrName, importCertificateParams, nil)
@@ -221,13 +219,18 @@ func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, pri
return &certmgr.OperateResult{}, nil
}
func createSDKClient(tenantId, clientId, clientSecret, cloudName, keyvaultName string) (*azcertificates.Client, error) {
const (
kvTagCertCN = "certimate/cert-cn"
kvTagCertSN = "certimate/cert-sn"
)
func createSDKClient(cloudName, tenantId, clientId, clientSecret, keyvaultName string) (*azcertificates.Client, error) {
env, err := azenv.GetCloudEnvConfiguration(cloudName)
if err != nil {
return nil, err
}
clientOptions := azcore.ClientOptions{Cloud: env}
clientOptions := azcore.ClientOptions{Cloud: env}
credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret,
&azidentity.ClientSecretCredentialOptions{ClientOptions: clientOptions})
if err != nil {
@@ -6,14 +6,9 @@ import (
"fmt"
"log/slog"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates"
"github.com/certimate-go/certimate/pkg/core/certmgr"
mcertmgr "github.com/certimate-go/certimate/pkg/core/certmgr/providers/azure-keyvault"
"github.com/certimate-go/certimate/pkg/core/deployer"
azenv "github.com/certimate-go/certimate/pkg/sdk3rd/azure/env"
)
type DeployerConfig struct {
@@ -35,7 +30,6 @@ type DeployerConfig struct {
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *azcertificates.Client
sdkCertmgr certmgr.Provider
}
@@ -46,11 +40,6 @@ func NewDeployer(config *DeployerConfig) (*Deployer, error) {
return nil, errors.New("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := mcertmgr.NewCertmgr(&mcertmgr.CertmgrConfig{
TenantId: config.TenantId,
ClientId: config.ClientId,
@@ -65,7 +54,6 @@ func NewDeployer(config *DeployerConfig) (*Deployer, error) {
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
@@ -101,31 +89,3 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
return &deployer.DeployResult{}, nil
}
func createSDKClient(tenantId, clientId, clientSecret, cloudName, keyvaultName string) (*azcertificates.Client, error) {
env, err := azenv.GetCloudEnvConfiguration(cloudName)
if err != nil {
return nil, err
}
clientOptions := azcore.ClientOptions{Cloud: env}
credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret,
&azidentity.ClientSecretCredentialOptions{ClientOptions: clientOptions})
if err != nil {
return nil, err
}
endpoint := fmt.Sprintf("https://%s.vault.azure.net", keyvaultName)
if azenv.IsUSGovernmentEnv(cloudName) {
endpoint = fmt.Sprintf("https://%s.vault.usgovcloudapi.net", keyvaultName)
} else if azenv.IsChinaEnv(cloudName) {
endpoint = fmt.Sprintf("https://%s.vault.azure.cn", keyvaultName)
}
client, err := azcertificates.NewClient(endpoint, credential, nil)
if err != nil {
return nil, err
}
return client, nil
}
@@ -47,6 +47,26 @@ const AccessConfigFormFieldsProviderAzure = () => {
<Input.Password autoComplete="new-password" placeholder={t("access.form.azure_client_secret.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "subscriptionId"]}
initialValue={initialValues.subscriptionId}
label={t("access.form.azure_subscription_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.azure_subscription_id.tooltip") }}></span>}
>
<Input allowClear autoComplete="new-password" placeholder={t("access.form.azure_subscription_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceGroupName"]}
initialValue={initialValues.resourceGroupName}
label={t("access.form.azure_resource_group_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.azure_resource_group_name.tooltip") }}></span>}
>
<Input allowClear placeholder={t("access.form.azure_resource_group_name.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "cloudName"]}
initialValue={initialValues.cloudName}
@@ -81,6 +101,8 @@ const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) =
tenantId: z.string().nonempty(t("access.form.azure_tenant_id.placeholder")),
clientId: z.string().nonempty(t("access.form.azure_client_id.placeholder")),
clientSecret: z.string().nonempty(t("access.form.azure_client_secret.placeholder")),
subscriptionId: z.string().nullish(),
resourceGroupName: z.string().nullish(),
cloudName: z.string().nullish(),
});
};
+6
View File
@@ -136,6 +136,12 @@
"access.form.azure_client_secret.label": "Azure client secret",
"access.form.azure_client_secret.placeholder": "Please enter Azure client secret",
"access.form.azure_client_secret.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/azure-monitor/logs/api/register-app-for-token\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/azure-monitor/logs/api/register-app-for-token</a>",
"access.form.azure_subscription_id.label": "Azure subscription ID (Optional)",
"access.form.azure_subscription_id.placeholder": "Please enter Azure subscription ID",
"access.form.azure_subscription_id.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id</a>",
"access.form.azure_resource_group_name.label": "Azure resource group name (Optional)",
"access.form.azure_resource_group_name.placeholder": "Please enter Azure resource group name",
"access.form.azure_resource_group_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal</a>",
"access.form.azure_cloud_name.label": "Azure sovereign cloud name (Optional)",
"access.form.azure_cloud_name.placeholder": "Please enter Azure sovereign cloud name (e.g. public)",
"access.form.azure_cloud_name.tooltip": "For more information, see <a href=\"https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/sovereign-clouds\" target=\"_blank\">https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/sovereign-clouds</a>",
+6
View File
@@ -136,6 +136,12 @@
"access.form.azure_client_secret.label": "Azure 客户端密码",
"access.form.azure_client_secret.placeholder": "请输入 Azure 客户端密码",
"access.form.azure_client_secret.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token</a>",
"access.form.azure_subscription_id.label": "Azure 订阅 ID(可选)",
"access.form.azure_subscription_id.placeholder": "请输入 Azure 订阅 ID",
"access.form.azure_subscription_id.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/azure-portal/get-subscription-tenant-id\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/azure-portal/get-subscription-tenant-id</a>",
"access.form.azure_resource_group_name.label": "Azure 资源组名称(可选)",
"access.form.azure_resource_group_name.placeholder": "请输入 Azure 资源组名称",
"access.form.azure_resource_group_name.tooltip": "这是什么?请参阅 <a href=\"https://docs.azure.cn/zh-cn/azure-resource-manager/management/manage-resource-groups-portal\" target=\"_blank\">https://docs.azure.cn/zh-cn/azure-resource-manager/management/manage-resource-groups-portal</a>",
"access.form.azure_cloud_name.label": "Azure 主权云环境(可选)",
"access.form.azure_cloud_name.placeholder": "请输入 Azure 主权云环境(例如:public",
"access.form.azure_cloud_name.tooltip": "这是什么?请参阅 <a href=\"https://learn.microsoft.com/zh-cn/azure/developer/azure-developer-cli/sovereign-clouds\" target=\"_blank\">https://learn.microsoft.com/zh-cn/azure/developer/azure-developer-cli/sovereign-clouds</a>",