下载证书附带pfx

ssh部署兼容带密码的密钥登录
ssh部署日志新增标准和错误输出
pull/295/head
v-me-50 2025-06-24 09:49:21 +08:00
parent f499afc060
commit 58e4c0ff11
5 changed files with 121 additions and 34 deletions

View File

@ -98,30 +98,61 @@ func DownloadCert(c *gin.Context) {
buf := new(bytes.Buffer)
zipWriter := zip.NewWriter(buf)
for filename, content := range certData {
if filename == "cert" || filename == "key" {
writer, err := zipWriter.Create(filename + ".pem")
if err != nil {
public.FailMsg(c, err.Error())
return
}
_, err = writer.Write([]byte(content))
if err != nil {
public.FailMsg(c, err.Error())
return
}
// 写入PEM文件
// cert.pem
certStr := certData["cert"]
certWriter, err := zipWriter.Create("Nginx/cert.pem")
if err != nil {
public.FailMsg(c, err.Error())
return
}
if _, err := certWriter.Write([]byte(certStr)); err != nil {
public.FailMsg(c, err.Error())
return
}
// key.pem
keyStr := certData["key"]
keyWriter, err := zipWriter.Create("Nginx/key.pem")
if err != nil {
public.FailMsg(c, err.Error())
return
}
if _, err := keyWriter.Write([]byte(keyStr)); err != nil {
public.FailMsg(c, err.Error())
return
}
// cert.pfx
pfxPassword := "allinssl"
pfxData, err := public.PEMToPFX(certStr, keyStr, pfxPassword)
if err == nil && len(pfxData) > 0 {
pfxWriter, err := zipWriter.Create("IIS/cert.pfx")
if err != nil {
public.FailMsg(c, err.Error())
return
}
if _, err := pfxWriter.Write(pfxData); err != nil {
public.FailMsg(c, err.Error())
return
}
txtWriter, err := zipWriter.Create("IIS/passwd.txt")
if err != nil {
public.FailMsg(c, err.Error())
return
}
if _, err := txtWriter.Write([]byte(pfxPassword)); err != nil {
public.FailMsg(c, err.Error())
return
}
}
// 关闭 zipWriter
if err := zipWriter.Close(); err != nil {
public.FailMsg(c, err.Error())
return
}
// 设置响应头
zipName := strings.ReplaceAll(certData["domains"], ".", "_")
zipName = strings.ReplaceAll(zipName, ",", "-")
c.Header("Content-Type", "application/zip")
c.Header("Content-Disposition", "attachment; filename="+zipName+".zip")
c.Data(200, "application/zip", buf.Bytes())

View File

@ -53,7 +53,7 @@ func Deploy(cfg map[string]any, logger *public.Logger) error {
return Deploy1panelSite(cfg)
case "ssh":
logger.Debug("使用ssh部署到指定路径...")
return DeploySSH(cfg)
return DeploySSH(cfg, logger)
case "aliyun-cdn":
logger.Debug("部署到阿里云CDN...")
return DeployAliCdn(cfg)

View File

@ -30,25 +30,34 @@ func buildAuthMethods(password, privateKey string) ([]ssh.AuthMethod, error) {
var methods []ssh.AuthMethod
if privateKey != "" {
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("unable to parse private key: %v", err)
var signer ssh.Signer
var err error
if password != "" {
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(password))
if err != nil {
return nil, fmt.Errorf("无法解析带密码的私钥: %v", err)
}
} else {
signer, err = ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return nil, fmt.Errorf("无法解析私钥: %v", err)
}
}
methods = append(methods, ssh.PublicKeys(signer))
}
if password != "" {
if password != "" && privateKey == "" {
methods = append(methods, ssh.Password(password))
}
if len(methods) == 0 {
return nil, fmt.Errorf("no authentication methods provided")
return nil, fmt.Errorf("未提供有效的认证方式")
}
return methods, nil
}
func writeMultipleFilesViaSSH(config SSHConfig, files []RemoteFile, preCmd, postCmd string) error {
func writeMultipleFilesViaSSH(config SSHConfig, files []RemoteFile, preCmd, postCmd string, logger *public.Logger) error {
var port string
switch v := config.Port.(type) {
case float64:
@ -91,8 +100,9 @@ func writeMultipleFilesViaSSH(config SSHConfig, files []RemoteFile, preCmd, post
return fmt.Errorf("会话创建失败: %v", err)
}
defer session.Close()
var script bytes.Buffer
var script, stdoutBuf, stderrBuf bytes.Buffer
session.Stdout = &stdoutBuf
session.Stderr = &stderrBuf
if preCmd != "" {
script.WriteString(preCmd + " && ")
@ -115,14 +125,14 @@ func writeMultipleFilesViaSSH(config SSHConfig, files []RemoteFile, preCmd, post
cmd := script.String()
if err := session.Run(cmd); err != nil {
return fmt.Errorf("运行出错: %v", err)
}
err = session.Run(cmd)
logger.Debug("[STDOUT]", stdoutBuf.String())
logger.Debug("[STDERR]", stderrBuf.String())
return nil
return err
}
func DeploySSH(cfg map[string]any) error {
func DeploySSH(cfg map[string]any, logger *public.Logger) error {
cert, ok := cfg["certificate"].(map[string]any)
if !ok {
return fmt.Errorf("证书不存在")
@ -180,7 +190,7 @@ func DeploySSH(cfg map[string]any) error {
{Path: certPath, Content: certPem},
{Path: keyPath, Content: keyPem},
}
err = writeMultipleFilesViaSSH(providerConfig, files, beforeCmd, afterCmd)
err = writeMultipleFilesViaSSH(providerConfig, files, beforeCmd, afterCmd, logger)
if err != nil {
return fmt.Errorf("SSH 部署失败: %v", err)
}

View File

@ -48,7 +48,7 @@ func NotifyWorkWx(params map[string]any) error {
return err
}
configStr := providerData["config"].(string)
fmt.Println(configStr)
//fmt.Println(configStr)
var config map[string]string
err = json.Unmarshal([]byte(configStr), &config)
if err != nil {

View File

@ -12,6 +12,8 @@ import (
"encoding/pem"
"errors"
"fmt"
"software.sslmate.com/src/go-pkcs12"
"strings"
"time"
)
@ -138,3 +140,47 @@ func GetSHA256(certStr string) (string, error) {
sha256Hash := sha256.Sum256(cert.Raw)
return hex.EncodeToString(sha256Hash[:]), nil
}
// PEMToPFX 将PEM格式的证书和私钥转换为PFX格式
func PEMToPFX(certPEM, keyPEM, pfxPassword string) ([]byte, error) {
// 使用默认密码如果未提供
if pfxPassword == "" {
pfxPassword = "allinssl"
}
// 解析证书PEM
certBlock, _ := pem.Decode([]byte(certPEM))
if certBlock == nil || certBlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("无效的证书PEM格式")
}
// 解析私钥PEM
keyBlock, _ := pem.Decode([]byte(keyPEM))
if keyBlock == nil || (!strings.Contains(keyBlock.Type, "PRIVATE KEY")) {
return nil, fmt.Errorf("无效的私钥PEM格式")
}
// 解析X.509证书
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("解析证书失败: %v", err)
}
// 尝试解析私钥(PKCS8或PKCS1格式)
var privKey interface{}
privKey, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err != nil {
privKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %v", err)
}
}
// 编码为PFX格式
pfxData, err := pkcs12.Encode(rand.Reader, privKey, cert, nil, pfxPassword)
if err != nil {
return nil, fmt.Errorf("编码PFX失败: %v", err)
}
return pfxData, nil
}