diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index 30cf9d41..3ad6aa4c 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -693,16 +693,18 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderTypeLocal:
{
deployer, err := pLocal.NewDeployer(&pLocal.DeployerConfig{
- ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderExtendedConfig, "shellEnv")),
- PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"),
- PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"),
- OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))),
- OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"),
- OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"),
- PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"),
- JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"),
- JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"),
- JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"),
+ ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderExtendedConfig, "shellEnv")),
+ PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"),
+ PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"),
+ OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))),
+ OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"),
+ OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"),
+ OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"),
+ OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"),
+ PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"),
+ JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"),
+ JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"),
+ JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"),
})
return deployer, err
}
@@ -819,22 +821,24 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
- SshHost: access.Host,
- SshPort: access.Port,
- SshUsername: access.Username,
- SshPassword: access.Password,
- SshKey: access.Key,
- SshKeyPassphrase: access.KeyPassphrase,
- UseSCP: maputil.GetBool(options.ProviderExtendedConfig, "useSCP"),
- PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"),
- PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"),
- OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))),
- OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"),
- OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"),
- PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"),
- JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"),
- JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"),
- JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"),
+ SshHost: access.Host,
+ SshPort: access.Port,
+ SshUsername: access.Username,
+ SshPassword: access.Password,
+ SshKey: access.Key,
+ SshKeyPassphrase: access.KeyPassphrase,
+ UseSCP: maputil.GetBool(options.ProviderExtendedConfig, "useSCP"),
+ PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"),
+ PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"),
+ OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))),
+ OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"),
+ OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"),
+ OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"),
+ OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"),
+ PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"),
+ JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"),
+ JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"),
+ JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"),
})
return deployer, err
}
diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go
index 3dd202a3..3425f05e 100644
--- a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go
+++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go
@@ -57,7 +57,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
// 提取 Edgio 所需的服务端证书和中间证书内容
- privateCertPEM, intermediateCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
+ serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
if err != nil {
return nil, err
}
@@ -66,8 +66,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
// REF: https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
uploadTlsCertReq := edgiodtos.UploadTlsCertRequest{
EnvironmentID: d.config.EnvironmentId,
- PrimaryCert: privateCertPEM,
- IntermediateCert: intermediateCertPEM,
+ PrimaryCert: serverCertPEM,
+ IntermediateCert: intermediaCertPEM,
PrivateKey: privkeyPEM,
}
uploadTlsCertResp, err := d.sdkClient.UploadTlsCert(uploadTlsCertReq)
diff --git a/internal/pkg/core/deployer/providers/local/local.go b/internal/pkg/core/deployer/providers/local/local.go
index 77f96543..a71ad9d3 100644
--- a/internal/pkg/core/deployer/providers/local/local.go
+++ b/internal/pkg/core/deployer/providers/local/local.go
@@ -25,6 +25,12 @@ type DeployerConfig struct {
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
// 输出证书文件路径。
OutputCertPath string `json:"outputCertPath,omitempty"`
+ // 输出服务器证书文件路径。
+ // 选填。
+ OutputServerCertPath string `json:"outputServerCertPath,omitempty"`
+ // 输出中间证书文件路径。
+ // 选填。
+ OutputIntermediaCertPath string `json:"outputIntermediaCertPath,omitempty"`
// 输出私钥文件路径。
OutputKeyPath string `json:"outputKeyPath,omitempty"`
// PFX 导出密码。
@@ -69,6 +75,12 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
+ // 提取服务器证书和中间证书
+ serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
+ if err != nil {
+ return nil, fmt.Errorf("failed to extract certs: %w", err)
+ }
+
// 执行前置命令
if d.config.PreCommand != "" {
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PreCommand)
@@ -86,6 +98,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
}
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
+ if d.config.OutputServerCertPath != "" {
+ if err := fileutil.WriteString(d.config.OutputServerCertPath, serverCertPEM); err != nil {
+ return nil, fmt.Errorf("failed to save server certificate file: %w", err)
+ }
+ d.logger.Info("ssl server certificate file saved", slog.String("path", d.config.OutputServerCertPath))
+ }
+
+ if d.config.OutputIntermediaCertPath != "" {
+ if err := fileutil.WriteString(d.config.OutputIntermediaCertPath, intermediaCertPEM); err != nil {
+ return nil, fmt.Errorf("failed to save intermedia certificate file: %w", err)
+ }
+ d.logger.Info("ssl intermedia certificate file saved", slog.String("path", d.config.OutputIntermediaCertPath))
+ }
+
if err := fileutil.WriteString(d.config.OutputKeyPath, privkeyPEM); err != nil {
return nil, fmt.Errorf("failed to save private key file: %w", err)
}
diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go
index 4b8b433d..cf09214b 100644
--- a/internal/pkg/core/deployer/providers/ssh/ssh.go
+++ b/internal/pkg/core/deployer/providers/ssh/ssh.go
@@ -41,6 +41,12 @@ type DeployerConfig struct {
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
// 输出证书文件路径。
OutputCertPath string `json:"outputCertPath,omitempty"`
+ // 输出服务器证书文件路径。
+ // 选填。
+ OutputServerCertPath string `json:"outputServerCertPath,omitempty"`
+ // 输出中间证书文件路径。
+ // 选填。
+ OutputIntermediaCertPath string `json:"outputIntermediaCertPath,omitempty"`
// 输出私钥文件路径。
OutputKeyPath string `json:"outputKeyPath,omitempty"`
// PFX 导出密码。
@@ -85,6 +91,12 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
+ // 提取服务器证书和中间证书
+ serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
+ if err != nil {
+ return nil, fmt.Errorf("failed to extract certs: %w", err)
+ }
+
// 连接
client, err := createSshClient(
d.config.SshHost,
@@ -118,6 +130,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
}
d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath))
+ if d.config.OutputServerCertPath != "" {
+ if err := writeFileString(client, d.config.UseSCP, d.config.OutputServerCertPath, serverCertPEM); err != nil {
+ return nil, fmt.Errorf("failed to save server certificate file: %w", err)
+ }
+ d.logger.Info("ssl server certificate file uploaded", slog.String("path", d.config.OutputServerCertPath))
+ }
+
+ if d.config.OutputIntermediaCertPath != "" {
+ if err := writeFileString(client, d.config.UseSCP, d.config.OutputIntermediaCertPath, intermediaCertPEM); err != nil {
+ return nil, fmt.Errorf("failed to save intermedia certificate file: %w", err)
+ }
+ d.logger.Info("ssl intermedia certificate file uploaded", slog.String("path", d.config.OutputIntermediaCertPath))
+ }
+
if err := writeFileString(client, d.config.UseSCP, d.config.OutputKeyPath, privkeyPEM); err != nil {
return nil, fmt.Errorf("failed to upload private key file: %w", err)
}
diff --git a/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go
index 07b2eaaa..418b2c1a 100644
--- a/internal/pkg/core/deployer/providers/webhook/webhook.go
+++ b/internal/pkg/core/deployer/providers/webhook/webhook.go
@@ -75,6 +75,12 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return nil, fmt.Errorf("failed to parse x509: %w", err)
}
+ // 提取服务器证书和中间证书
+ serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
+ if err != nil {
+ return nil, fmt.Errorf("failed to extract certs: %w", err)
+ }
+
// 处理 Webhook URL
webhookUrl, err := url.Parse(d.config.WebhookUrl)
if err != nil {
@@ -134,6 +140,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
replaceJsonValueRecursively(webhookData, "${DOMAIN}", certX509.Subject.CommonName)
replaceJsonValueRecursively(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";"))
replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPEM)
+ replaceJsonValueRecursively(webhookData, "${SERVER_CERTIFICATE}", serverCertPEM)
+ replaceJsonValueRecursively(webhookData, "${INTERMEDIA_CERTIFICATE}", intermediaCertPEM)
replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPEM)
if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART {
diff --git a/internal/pkg/utils/cert/extractor.go b/internal/pkg/utils/cert/extractor.go
index 110f4772..94d0a8da 100644
--- a/internal/pkg/utils/cert/extractor.go
+++ b/internal/pkg/utils/cert/extractor.go
@@ -12,9 +12,9 @@ import (
//
// 出参:
// - serverCertPEM: 服务器证书的 PEM 内容。
-// - interCertPEM: 中间证书的 PEM 内容。
+// - intermediaCertPEM: 中间证书的 PEM 内容。
// - err: 错误。
-func ExtractCertificatesFromPEM(certPEM string) (serverCertPEM string, interCertPEM string, err error) {
+func ExtractCertificatesFromPEM(certPEM string) (serverCertPEM string, intermediaCertPEM string, err error) {
pemBlocks := make([]*pem.Block, 0)
pemData := []byte(certPEM)
for {
@@ -28,7 +28,7 @@ func ExtractCertificatesFromPEM(certPEM string) (serverCertPEM string, interCert
}
serverCertPEM = ""
- interCertPEM = ""
+ intermediaCertPEM = ""
if len(pemBlocks) == 0 {
return "", "", errors.New("failed to decode PEM block")
@@ -40,9 +40,9 @@ func ExtractCertificatesFromPEM(certPEM string) (serverCertPEM string, interCert
if len(pemBlocks) > 1 {
for i := 1; i < len(pemBlocks); i++ {
- interCertPEM += string(pem.EncodeToMemory(pemBlocks[i]))
+ intermediaCertPEM += string(pem.EncodeToMemory(pemBlocks[i]))
}
}
- return serverCertPEM, interCertPEM, nil
+ return serverCertPEM, intermediaCertPEM, nil
}
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
index e71cd639..700f0b09 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
@@ -11,14 +11,16 @@ import { CERTIFICATE_FORMATS } from "@/domain/certificate";
type DeployNodeConfigFormLocalConfigFieldValues = Nullish<{
format: string;
certPath: string;
- keyPath?: string | null;
- pfxPassword?: string | null;
- jksAlias?: string | null;
- jksKeypass?: string | null;
- jksStorepass?: string | null;
- shellEnv?: string | null;
- preCommand?: string | null;
- postCommand?: string | null;
+ certPathForServerOnly?: string;
+ certPathForIntermediaOnly?: string;
+ keyPath?: string;
+ pfxPassword?: string;
+ jksAlias?: string;
+ jksKeypass?: string;
+ jksStorepass?: string;
+ shellEnv?: string;
+ preCommand?: string;
+ postCommand?: string;
}>;
export type DeployNodeConfigFormLocalConfigProps = {
@@ -160,6 +162,16 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
.min(1, t("workflow_node.deploy.form.local_cert_path.tooltip"))
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
+ certPathForServerOnly: z
+ .string()
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim()
+ .nullish(),
+ certPathForIntermediaOnly: z
+ .string()
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim()
+ .nullish(),
keyPath: z
.string()
.max(256, t("common.errmsg.string_max", { max: 256 }))
@@ -326,6 +338,24 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
>
+
+
}
+ >
+
+
+
+ }
+ >
+
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
index 287baaeb..6a8d01c9 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
@@ -13,13 +13,15 @@ import { initPresetScript } from "./DeployNodeConfigFormLocalConfig";
type DeployNodeConfigFormSSHConfigFieldValues = Nullish<{
format: string;
certPath: string;
- keyPath?: string | null;
- pfxPassword?: string | null;
- jksAlias?: string | null;
- jksKeypass?: string | null;
- jksStorepass?: string | null;
- preCommand?: string | null;
- postCommand?: string | null;
+ certPathForServerOnly?: string;
+ certPathForIntermediaOnly?: string;
+ keyPath?: string;
+ pfxPassword?: string;
+ jksAlias?: string;
+ jksKeypass?: string;
+ jksStorepass?: string;
+ preCommand?: string;
+ postCommand?: string;
useSCP?: boolean;
}>;
@@ -61,6 +63,16 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
.trim()
.nullish()
.refine((v) => fieldFormat !== FORMAT_PEM || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_key_path.tooltip") }),
+ certPathForServerOnly: z
+ .string()
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim()
+ .nullish(),
+ certPathForIntermediaOnly: z
+ .string()
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim()
+ .nullish(),
pfxPassword: z
.string()
.max(64, t("common.errmsg.string_max", { max: 256 }))
@@ -207,6 +219,24 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
>
+
+ }
+ >
+
+
+
+ }
+ >
+
+
@@ -249,10 +279,6 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
-
-
-
-