chore: move '/internal/pkg' to '/pkg'
This commit is contained in:
17
pkg/core/ssl-deployer/providers/local/defines.go
Normal file
17
pkg/core/ssl-deployer/providers/local/defines.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package local
|
||||
|
||||
type OutputFormatType string
|
||||
|
||||
const (
|
||||
OUTPUT_FORMAT_PEM = OutputFormatType("PEM")
|
||||
OUTPUT_FORMAT_PFX = OutputFormatType("PFX")
|
||||
OUTPUT_FORMAT_JKS = OutputFormatType("JKS")
|
||||
)
|
||||
|
||||
type ShellEnvType string
|
||||
|
||||
const (
|
||||
SHELL_ENV_SH = ShellEnvType("sh")
|
||||
SHELL_ENV_CMD = ShellEnvType("cmd")
|
||||
SHELL_ENV_POWERSHELL = ShellEnvType("powershell")
|
||||
)
|
||||
194
pkg/core/ssl-deployer/providers/local/local.go
Normal file
194
pkg/core/ssl-deployer/providers/local/local.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
xcert "github.com/usual2970/certimate/pkg/utils/cert"
|
||||
xfile "github.com/usual2970/certimate/pkg/utils/file"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// Shell 执行环境。
|
||||
// 零值时根据操作系统决定。
|
||||
ShellEnv ShellEnvType `json:"shellEnv,omitempty"`
|
||||
// 前置命令。
|
||||
PreCommand string `json:"preCommand,omitempty"`
|
||||
// 后置命令。
|
||||
PostCommand string `json:"postCommand,omitempty"`
|
||||
// 输出证书格式。
|
||||
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 导出密码。
|
||||
// 证书格式为 PFX 时必填。
|
||||
PfxPassword string `json:"pfxPassword,omitempty"`
|
||||
// JKS 别名。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksAlias string `json:"jksAlias,omitempty"`
|
||||
// JKS 密钥密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksKeypass string `json:"jksKeypass,omitempty"`
|
||||
// JKS 存储密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksStorepass string `json:"jksStorepass,omitempty"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
|
||||
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
// 提取服务器证书和中间证书
|
||||
serverCertPEM, intermediaCertPEM, err := xcert.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)
|
||||
d.logger.Debug("run pre-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute pre-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 写入证书和私钥文件
|
||||
switch d.config.OutputFormat {
|
||||
case OUTPUT_FORMAT_PEM:
|
||||
if err := xfile.WriteString(d.config.OutputCertPath, certPEM); err != nil {
|
||||
return nil, fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
|
||||
|
||||
if d.config.OutputServerCertPath != "" {
|
||||
if err := xfile.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 := xfile.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 := xfile.WriteString(d.config.OutputKeyPath, privkeyPEM); err != nil {
|
||||
return nil, fmt.Errorf("failed to save private key file: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl private key file saved", slog.String("path", d.config.OutputKeyPath))
|
||||
|
||||
case OUTPUT_FORMAT_PFX:
|
||||
pfxData, err := xcert.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, d.config.PfxPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to transform certificate to PFX: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl certificate transformed to pfx")
|
||||
|
||||
if err := xfile.Write(d.config.OutputCertPath, pfxData); err != nil {
|
||||
return nil, fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
|
||||
|
||||
case OUTPUT_FORMAT_JKS:
|
||||
jksData, err := xcert.TransformCertificateFromPEMToJKS(certPEM, privkeyPEM, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to transform certificate to JKS: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl certificate transformed to jks")
|
||||
|
||||
if err := xfile.Write(d.config.OutputCertPath, jksData); err != nil {
|
||||
return nil, fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported output format '%s'", d.config.OutputFormat)
|
||||
}
|
||||
|
||||
// 执行后置命令
|
||||
if d.config.PostCommand != "" {
|
||||
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PostCommand)
|
||||
d.logger.Debug("run post-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute post-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func execCommand(shellEnv ShellEnvType, command string) (string, string, error) {
|
||||
var cmd *exec.Cmd
|
||||
|
||||
switch shellEnv {
|
||||
case SHELL_ENV_SH:
|
||||
cmd = exec.Command("sh", "-c", command)
|
||||
|
||||
case SHELL_ENV_CMD:
|
||||
cmd = exec.Command("cmd", "/C", command)
|
||||
|
||||
case SHELL_ENV_POWERSHELL:
|
||||
cmd = exec.Command("powershell", "-Command", command)
|
||||
|
||||
case ShellEnvType(""):
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("cmd", "/C", command)
|
||||
} else {
|
||||
cmd = exec.Command("sh", "-c", command)
|
||||
}
|
||||
|
||||
default:
|
||||
return "", "", fmt.Errorf("unsupported shell env '%s'", shellEnv)
|
||||
}
|
||||
|
||||
stdoutBuf := bytes.NewBuffer(nil)
|
||||
cmd.Stdout = stdoutBuf
|
||||
stderrBuf := bytes.NewBuffer(nil)
|
||||
cmd.Stderr = stderrBuf
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return stdoutBuf.String(), stderrBuf.String(), fmt.Errorf("failed to execute command: %w", err)
|
||||
}
|
||||
|
||||
return stdoutBuf.String(), stderrBuf.String(), nil
|
||||
}
|
||||
198
pkg/core/ssl-deployer/providers/local/local_test.go
Normal file
198
pkg/core/ssl-deployer/providers/local/local_test.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package local_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/local"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fOutputCertPath string
|
||||
fOutputKeyPath string
|
||||
fPfxPassword string
|
||||
fJksAlias string
|
||||
fJksKeypass string
|
||||
fJksStorepass string
|
||||
fShellEnv string
|
||||
fPreCommand string
|
||||
fPostCommand string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_SSLDEPLOYER_LOCAL_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fOutputCertPath, argsPrefix+"OUTPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fOutputKeyPath, argsPrefix+"OUTPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fPfxPassword, argsPrefix+"PFXPASSWORD", "", "")
|
||||
flag.StringVar(&fJksAlias, argsPrefix+"JKSALIAS", "", "")
|
||||
flag.StringVar(&fJksKeypass, argsPrefix+"JKSKEYPASS", "", "")
|
||||
flag.StringVar(&fJksStorepass, argsPrefix+"JKSSTOREPASS", "", "")
|
||||
flag.StringVar(&fShellEnv, argsPrefix+"SHELLENV", "", "")
|
||||
flag.StringVar(&fPreCommand, argsPrefix+"PRECOMMAND", "", "")
|
||||
flag.StringVar(&fPostCommand, argsPrefix+"POSTCOMMAND", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./local_test.go -args \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_OUTPUTCERTPATH="/path/to/your-output-cert" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_OUTPUTKEYPATH="/path/to/your-output-key" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_PFXPASSWORD="your-pfx-password" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_JKSALIAS="your-jks-alias" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_JKSKEYPASS="your-jks-keypass" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_JKSSTOREPASS="your-jks-storepass" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_SHELLENV="sh" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_PRECOMMAND="echo 'hello world'" \
|
||||
--CERTIMATE_SSLDEPLOYER_LOCAL_POSTCOMMAND="echo 'bye-bye world'"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy_PEM", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("OUTPUTCERTPATH: %v", fOutputCertPath),
|
||||
fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath),
|
||||
fmt.Sprintf("SHELLENV: %v", fShellEnv),
|
||||
fmt.Sprintf("PRECOMMAND: %v", fPreCommand),
|
||||
fmt.Sprintf("POSTCOMMAND: %v", fPostCommand),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
OutputFormat: provider.OUTPUT_FORMAT_PEM,
|
||||
OutputCertPath: fOutputCertPath + ".pem",
|
||||
OutputKeyPath: fOutputKeyPath + ".pem",
|
||||
ShellEnv: provider.ShellEnvType(fShellEnv),
|
||||
PreCommand: fPreCommand,
|
||||
PostCommand: fPostCommand,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fstat1, err := os.Stat(fOutputCertPath + ".pem")
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
} else if fstat1.Size() == 0 {
|
||||
t.Errorf("err: empty output certificate file")
|
||||
return
|
||||
}
|
||||
|
||||
fstat2, err := os.Stat(fOutputKeyPath + ".pem")
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
} else if fstat2.Size() == 0 {
|
||||
t.Errorf("err: empty output private key file")
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
|
||||
t.Run("Deploy_PFX", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("OUTPUTCERTPATH: %v", fOutputCertPath),
|
||||
fmt.Sprintf("PFXPASSWORD: %v", fPfxPassword),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
OutputFormat: provider.OUTPUT_FORMAT_PFX,
|
||||
OutputCertPath: fOutputCertPath + ".pfx",
|
||||
PfxPassword: fPfxPassword,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fstat, err := os.Stat(fOutputCertPath + ".pfx")
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
} else if fstat.Size() == 0 {
|
||||
t.Errorf("err: empty output certificate file")
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
|
||||
t.Run("Deploy_JKS", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("OUTPUTCERTPATH: %v", fOutputCertPath),
|
||||
fmt.Sprintf("JKSALIAS: %v", fJksAlias),
|
||||
fmt.Sprintf("JKSKEYPASS: %v", fJksKeypass),
|
||||
fmt.Sprintf("JKSSTOREPASS: %v", fJksStorepass),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
OutputFormat: provider.OUTPUT_FORMAT_JKS,
|
||||
OutputCertPath: fOutputCertPath + ".jks",
|
||||
JksAlias: fJksAlias,
|
||||
JksKeypass: fJksKeypass,
|
||||
JksStorepass: fJksStorepass,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fstat, err := os.Stat(fOutputCertPath + ".jks")
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
} else if fstat.Size() == 0 {
|
||||
t.Errorf("err: empty output certificate file")
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user