diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b913fa48..674363b2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,5 +1,11 @@
# 贡献指南
+
+
+中文 | [English](CONTRIBUTING_EN.md)
+
+
+
非常感谢你抽出时间来帮助改进 Certimate!以下是向 Certimate 提交 Pull Request 时的操作指南。
我们需要保持敏捷和快速迭代,同时也希望确保贡献者能获得尽可能流畅的参与体验。这份贡献指南旨在帮助你熟悉代码库和我们的工作方式,让你可以尽快进入有趣的开发环节。
diff --git a/CONTRIBUTING_EN.md b/CONTRIBUTING_EN.md
index 61a575b6..b5837867 100644
--- a/CONTRIBUTING_EN.md
+++ b/CONTRIBUTING_EN.md
@@ -1,5 +1,11 @@
# Contribution Guide
+
+
+[中文](CONTRIBUTING.md) | English
+
+
+
Thank you for taking the time to improve Certimate! Below is a guide for submitting a PR (Pull Request) to the Certimate repository.
We need to be nimble and ship fast given where we are, but we also want to make sure that contributors like you get as smooth an experience at contributing as possible. We've assembled this contribution guide for that purpose, aiming at getting you familiarized with the codebase & how we work with contributors, so you could quickly jump to the fun part.
diff --git a/internal/certdeploy/deployers/sp_ssh.go b/internal/certdeploy/deployers/sp_ssh.go
index e939e6f1..d2591905 100644
--- a/internal/certdeploy/deployers/sp_ssh.go
+++ b/internal/certdeploy/deployers/sp_ssh.go
@@ -44,10 +44,10 @@ func init() {
PreCommand: xmaps.GetString(options.ProviderExtendedConfig, "preCommand"),
PostCommand: xmaps.GetString(options.ProviderExtendedConfig, "postCommand"),
OutputFormat: ssh.OutputFormatType(xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(ssh.OUTPUT_FORMAT_PEM))),
+ OutputKeyPath: xmaps.GetString(options.ProviderExtendedConfig, "keyPath"),
OutputCertPath: xmaps.GetString(options.ProviderExtendedConfig, "certPath"),
OutputServerCertPath: xmaps.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"),
OutputIntermediaCertPath: xmaps.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"),
- OutputKeyPath: xmaps.GetString(options.ProviderExtendedConfig, "keyPath"),
PfxPassword: xmaps.GetString(options.ProviderExtendedConfig, "pfxPassword"),
JksAlias: xmaps.GetString(options.ProviderExtendedConfig, "jksAlias"),
JksKeypass: xmaps.GetString(options.ProviderExtendedConfig, "jksKeypass"),
diff --git a/internal/workflow/dispatcher/dispatcher.go b/internal/workflow/dispatcher/dispatcher.go
index b53189e6..7345f94f 100644
--- a/internal/workflow/dispatcher/dispatcher.go
+++ b/internal/workflow/dispatcher/dispatcher.go
@@ -101,6 +101,26 @@ func (wd *workflowDispatcher) Bootup(ctx context.Context) error {
}
wd.booted = true
+
+ ticker := time.NewTicker(1 * time.Minute)
+ go func() {
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ // 无需准确获取,不用加锁
+ if len(wd.processingTasks) < wd.concurrency && len(wd.pendingRunQueue) > 0 {
+ wd.tryNextAsync()
+ }
+ default:
+ if !wd.booted {
+ return
+ }
+ }
+ }
+ }()
+
return nil
}
@@ -305,9 +325,9 @@ func (wd *workflowDispatcher) tryExecuteAsync(task *taskInfo) {
})
// 执行工作流
- wd.syslog.Info(fmt.Sprintf("workflow run #%s was started", task.RunId))
+ wd.syslog.Info(fmt.Sprintf("workflow run #%s (work#%s) was started", task.RunId, task.WorkflowId))
we.Invoke(task.ctx, workflowRun.WorkflowId, workflowRun.Id, workflowRun.Graph)
- wd.syslog.Info(fmt.Sprintf("workflow run #%s was stopped", task.RunId))
+ wd.syslog.Info(fmt.Sprintf("workflow run #%s (work#%s) was stopped", task.RunId, task.WorkflowId))
}
func (wd *workflowDispatcher) tryNextAsync() {
@@ -341,6 +361,7 @@ func (wd *workflowDispatcher) tryNextAsync() {
task := &taskInfo{WorkflowId: workflowRun.WorkflowId, RunId: workflowRun.Id, ctx: ctxRun, cancel: ctxCancel}
wd.pendingRunQueue = append(wd.pendingRunQueue[:i], wd.pendingRunQueue[i+1:]...)
wd.processingTasks[pendingRunId] = task
+ wd.syslog.Info(fmt.Sprintf("workflow run #%s (work#%s) is being dispatched ...", task.RunId, task.WorkflowId))
go func() { wd.tryExecuteAsync(task) }()
return
}
diff --git a/pkg/core/ssl-applicator/acme-http01/providers/ssh/ssh.go b/pkg/core/ssl-applicator/acme-http01/providers/ssh/ssh.go
index 9863d6aa..d23055ab 100644
--- a/pkg/core/ssl-applicator/acme-http01/providers/ssh/ssh.go
+++ b/pkg/core/ssl-applicator/acme-http01/providers/ssh/ssh.go
@@ -6,7 +6,6 @@ import (
"net"
"path/filepath"
"strconv"
- "strings"
"github.com/go-acme/lego/v4/challenge/http01"
"golang.org/x/crypto/ssh"
@@ -239,54 +238,17 @@ func (p *provider) createSshClient(conn net.Conn, host string, port int32, authM
}
}
- authentications := make([]ssh.AuthMethod, 0)
switch authMethod {
case AUTH_METHOD_NONE:
- {
- }
+ return xssh.NewClient(conn, host, int(port), username)
case AUTH_METHOD_PASSWORD:
- {
- authentications = append(authentications, ssh.Password(password))
- authentications = append(authentications, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
- if len(questions) == 1 {
- return []string{password}, nil
- }
- return nil, fmt.Errorf("unexpected keyboard interactive question [%s]", strings.Join(questions, ", "))
- }))
- }
+ return xssh.NewClientWithPassword(conn, host, int(port), username, password)
case AUTH_METHOD_KEY:
- {
- var signer ssh.Signer
- var err error
-
- if keyPassphrase != "" {
- signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
- } else {
- signer, err = ssh.ParsePrivateKey([]byte(key))
- }
-
- if err != nil {
- return nil, err
- }
-
- authentications = append(authentications, ssh.PublicKeys(signer))
- }
+ return xssh.NewClientWithKey(conn, host, int(port), username, key, keyPassphrase)
default:
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
}
-
- addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
- sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, &ssh.ClientConfig{
- User: username,
- Auth: authentications,
- HostKeyCallback: ssh.InsecureIgnoreHostKey(),
- })
- if err != nil {
- return nil, err
- }
-
- return ssh.NewClient(sshConn, chans, reqs), nil
}
diff --git a/pkg/core/ssl-deployer/providers/ssh/ssh.go b/pkg/core/ssl-deployer/providers/ssh/ssh.go
index 666cc6c7..3ff297f2 100644
--- a/pkg/core/ssl-deployer/providers/ssh/ssh.go
+++ b/pkg/core/ssl-deployer/providers/ssh/ssh.go
@@ -8,7 +8,6 @@ import (
"log/slog"
"net"
"strconv"
- "strings"
"golang.org/x/crypto/ssh"
@@ -52,6 +51,8 @@ type SSLDeployerProviderConfig struct {
PostCommand string `json:"postCommand,omitempty"`
// 输出证书格式。
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
+ // 输出私钥文件路径。
+ OutputKeyPath string `json:"outputKeyPath,omitempty"`
// 输出证书文件路径。
OutputCertPath string `json:"outputCertPath,omitempty"`
// 输出服务器证书文件路径。
@@ -60,8 +61,6 @@ type SSLDeployerProviderConfig struct {
// 输出中间证书文件路径。
// 选填。
OutputIntermediaCertPath string `json:"outputIntermediaCertPath,omitempty"`
- // 输出私钥文件路径。
- OutputKeyPath string `json:"outputKeyPath,omitempty"`
// PFX 导出密码。
// 证书格式为 PFX 时必填。
PfxPassword string `json:"pfxPassword,omitempty"`
@@ -192,6 +191,11 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
// 上传证书和私钥文件
switch d.config.OutputFormat {
case OUTPUT_FORMAT_PEM:
+ if err := xssh.WriteRemoteString(client, d.config.OutputKeyPath, privkeyPEM, d.config.UseSCP); err != nil {
+ return nil, fmt.Errorf("failed to upload private key file: %w", err)
+ }
+ d.logger.Info("ssl private key file uploaded", slog.String("path", d.config.OutputKeyPath))
+
if err := xssh.WriteRemoteString(client, d.config.OutputCertPath, certPEM, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
@@ -211,11 +215,6 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
d.logger.Info("ssl intermedia certificate file uploaded", slog.String("path", d.config.OutputIntermediaCertPath))
}
- if err := xssh.WriteRemoteString(client, d.config.OutputKeyPath, privkeyPEM, d.config.UseSCP); err != nil {
- return nil, fmt.Errorf("failed to upload private key file: %w", err)
- }
- d.logger.Info("ssl private key file uploaded", slog.String("path", d.config.OutputKeyPath))
-
case OUTPUT_FORMAT_PFX:
pfxData, err := xcert.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, d.config.PfxPassword)
if err != nil {
@@ -282,56 +281,19 @@ func createSshClient(conn net.Conn, host string, port int32, authMethod string,
}
}
- authentications := make([]ssh.AuthMethod, 0)
switch authMethod {
case AUTH_METHOD_NONE:
- {
- }
+ return xssh.NewClient(conn, host, int(port), username)
case AUTH_METHOD_PASSWORD:
- {
- authentications = append(authentications, ssh.Password(password))
- authentications = append(authentications, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
- if len(questions) == 1 {
- return []string{password}, nil
- }
- return nil, fmt.Errorf("unexpected keyboard interactive question [%s]", strings.Join(questions, ", "))
- }))
- }
+ return xssh.NewClientWithPassword(conn, host, int(port), username, password)
case AUTH_METHOD_KEY:
- {
- var signer ssh.Signer
- var err error
-
- if keyPassphrase != "" {
- signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
- } else {
- signer, err = ssh.ParsePrivateKey([]byte(key))
- }
-
- if err != nil {
- return nil, err
- }
-
- authentications = append(authentications, ssh.PublicKeys(signer))
- }
+ return xssh.NewClientWithKey(conn, host, int(port), username, key, keyPassphrase)
default:
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
}
-
- addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
- sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, &ssh.ClientConfig{
- User: username,
- Auth: authentications,
- HostKeyCallback: ssh.InsecureIgnoreHostKey(),
- })
- if err != nil {
- return nil, err
- }
-
- return ssh.NewClient(sshConn, chans, reqs), nil
}
func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) {
diff --git a/pkg/utils/ssh/client.go b/pkg/utils/ssh/client.go
new file mode 100644
index 00000000..af5189b7
--- /dev/null
+++ b/pkg/utils/ssh/client.go
@@ -0,0 +1,59 @@
+package ssh
+
+import (
+ "fmt"
+ "net"
+ "strconv"
+ "strings"
+
+ "golang.org/x/crypto/ssh"
+)
+
+func NewClient(conn net.Conn, host string, port int, username string) (*ssh.Client, error) {
+ authentications := make([]ssh.AuthMethod, 0)
+ return newClientWithAuthMethods(conn, host, port, username, authentications)
+}
+
+func NewClientWithPassword(conn net.Conn, host string, port int, username string, password string) (*ssh.Client, error) {
+ authentications := make([]ssh.AuthMethod, 0)
+ authentications = append(authentications, ssh.Password(password))
+ authentications = append(authentications, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
+ if len(questions) == 1 {
+ return []string{password}, nil
+ }
+ return nil, fmt.Errorf("unexpected keyboard interactive question [%s]", strings.Join(questions, ", "))
+ }))
+ return newClientWithAuthMethods(conn, host, port, username, authentications)
+}
+
+func NewClientWithKey(conn net.Conn, host string, port int, username string, key, keyPassphrase string) (*ssh.Client, error) {
+ var signer ssh.Signer
+ var err error
+ if keyPassphrase != "" {
+ signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
+ } else {
+ signer, err = ssh.ParsePrivateKey([]byte(key))
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ authentications := make([]ssh.AuthMethod, 0)
+ authentications = append(authentications, ssh.PublicKeys(signer))
+ return newClientWithAuthMethods(conn, host, port, username, authentications)
+}
+
+func newClientWithAuthMethods(conn net.Conn, host string, port int, username string, authMethods []ssh.AuthMethod) (*ssh.Client, error) {
+ addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
+
+ sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, &ssh.ClientConfig{
+ User: username,
+ Auth: authMethods,
+ HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return ssh.NewClient(sshConn, chans, reqs), nil
+}
diff --git a/ui/index.html b/ui/index.html
index ba75b047..f056f604 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -4,10 +4,63 @@
+
Certimate - Your Trusted Partner in SSL Automation
-
+