diff --git a/pkg/core/certifier/challengers/http01/ftp/internal/lego.go b/pkg/core/certifier/challengers/http01/ftp/internal/lego.go index c037ffcd7..49ffbdf6f 100644 --- a/pkg/core/certifier/challengers/http01/ftp/internal/lego.go +++ b/pkg/core/certifier/challengers/http01/ftp/internal/lego.go @@ -3,12 +3,15 @@ package internal import ( "context" "fmt" + "log/slog" "path/filepath" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/http01" + "github.com/go-acme/lego/v5/log" "github.com/certimate-go/certimate/internal/tools/ftp" + xfilepath "github.com/certimate-go/certimate/pkg/utils/filepath" ) var _ challenge.Provider = (*HTTPProvider)(nil) @@ -37,6 +40,10 @@ func NewHTTPProviderConfig(config *Config) (*HTTPProvider, error) { return nil, fmt.Errorf("the configuration of the acme challenge provider is nil") } + if config.WebRootPath == "" { + return nil, fmt.Errorf("ftp: webroot path must be set") + } + return &HTTPProvider{ config: config, }, nil @@ -48,10 +55,14 @@ func (p *HTTPProvider) Present(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ftp: failed to create FTP client: %w", err) } - defer client.Quit() + log.Info("ftp: ftp connected") + defer func() { + client.Quit() + log.Info("ftp: ftp closed") + }() - challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) - challengeDir := filepath.Dir(challengePath) + challengePath := xfilepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) + challengeDir := xfilepath.Dir(challengePath) challengeFile := filepath.Base(challengePath) if err := client.MkdirAll(ctx, challengeDir); err != nil { return fmt.Errorf("ftp: failed to create the \".well-known\" directory: %w", err) @@ -63,6 +74,8 @@ func (p *HTTPProvider) Present(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ftp: failed to write file for HTTP challenge: %w", err) } + log.Info("ftp: authz file uploaded", slog.String("path", challengePath)) + return nil } @@ -72,10 +85,14 @@ func (p *HTTPProvider) CleanUp(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ftp: failed to create FTP client: %w", err) } - defer client.Quit() + log.Info("ftp: ftp connected") + defer func() { + client.Quit() + log.Info("ftp: ftp closed") + }() - challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) - challengeDir := filepath.Dir(challengePath) + challengePath := xfilepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) + challengeDir := xfilepath.Dir(challengePath) challengeFile := filepath.Base(challengePath) if err := client.ChangeDir(ctx, challengeDir); err != nil { return fmt.Errorf("ftp: failed to change to the \".well-known\" directory: %w", err) @@ -84,6 +101,8 @@ func (p *HTTPProvider) CleanUp(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ftp: failed to remove file after HTTP challenge: %w", err) } + log.Info("ftp: authz file removed", slog.String("path", challengePath)) + return nil } diff --git a/pkg/core/certifier/challengers/http01/local/local.go b/pkg/core/certifier/challengers/http01/local/local.go index 861be77de..5d0783cc6 100644 --- a/pkg/core/certifier/challengers/http01/local/local.go +++ b/pkg/core/certifier/challengers/http01/local/local.go @@ -18,6 +18,10 @@ func NewChallenger(config *ChallengerConfig) (core.ACMEChallenger, error) { return nil, fmt.Errorf("the configuration of the acme challenge provider is nil") } + if config.WebRootPath == "" { + return nil, fmt.Errorf("local: webroot path must be set") + } + provider, err := webroot.NewHTTPProvider(config.WebRootPath) if err != nil { return nil, err diff --git a/pkg/core/certifier/challengers/http01/s3/internal/lego.go b/pkg/core/certifier/challengers/http01/s3/internal/lego.go index 7a7df4044..5106c94be 100644 --- a/pkg/core/certifier/challengers/http01/s3/internal/lego.go +++ b/pkg/core/certifier/challengers/http01/s3/internal/lego.go @@ -3,10 +3,12 @@ package internal import ( "context" "fmt" + "log/slog" "strings" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/http01" + "github.com/go-acme/lego/v5/log" "github.com/certimate-go/certimate/internal/tools/s3" ) @@ -52,6 +54,8 @@ func (p *HTTPProvider) Present(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("s3: failed to upload file for HTTP challenge: %w", err) } + log.Info("s3: authz file uploaded", slog.String("bucket", p.config.Bucket), slog.String("object", objectKey)) + return nil } @@ -66,6 +70,8 @@ func (p *HTTPProvider) CleanUp(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("s3: failed to remove file after HTTP challenge: %w", err) } + log.Info("s3: authz file removed", slog.String("bucket", p.config.Bucket), slog.String("object", objectKey)) + return nil } diff --git a/pkg/core/certifier/challengers/http01/ssh/internal/ssh.go b/pkg/core/certifier/challengers/http01/ssh/internal/ssh.go index 998646ec9..d52218f55 100644 --- a/pkg/core/certifier/challengers/http01/ssh/internal/ssh.go +++ b/pkg/core/certifier/challengers/http01/ssh/internal/ssh.go @@ -3,12 +3,14 @@ package internal import ( "context" "fmt" - "path/filepath" + "log/slog" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/http01" + "github.com/go-acme/lego/v5/log" "github.com/certimate-go/certimate/internal/tools/ssh" + xfilepath "github.com/certimate-go/certimate/pkg/utils/filepath" xssh "github.com/certimate-go/certimate/pkg/utils/ssh" ) @@ -25,9 +27,8 @@ func NewDefaultConfig() *Config { defaultCfg := ssh.NewDefaultConfig() return &Config{ - Config: *defaultCfg, - UseSCP: false, - WebRootPath: "/var/www/html", + Config: *defaultCfg, + UseSCP: false, } } @@ -40,6 +41,10 @@ func NewHTTPProviderConfig(config *Config) (*HTTPProvider, error) { return nil, fmt.Errorf("the configuration of the acme challenge provider is nil") } + if config.WebRootPath == "" { + return nil, fmt.Errorf("ssh: webroot path must be set") + } + return &HTTPProvider{ config: config, }, nil @@ -51,13 +56,19 @@ func (p *HTTPProvider) Present(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ssh: failed to create SSH client: %w", err) } - defer client.Close() + log.Info("ssh: ssh connected") + defer func() { + client.Close() + log.Info("ssh: ssh closed") + }() - challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) + challengePath := xfilepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) if err := xssh.WriteRemoteString(client.RawClient(), challengePath, keyAuth, p.config.UseSCP); err != nil { return fmt.Errorf("ssh: failed to write file for HTTP challenge: %w", err) } + log.Info("ssh: authz file uploaded", slog.String("path", challengePath)) + return nil } @@ -67,14 +78,20 @@ func (p *HTTPProvider) CleanUp(ctx context.Context, domain, token, keyAuth strin return fmt.Errorf("ssh: failed to create SSH client: %w", err) } - defer client.Close() + log.Info("ssh: ssh connected") + defer func() { + client.Close() + log.Info("ssh: ssh closed") + }() // 删除质询文件 - challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) + challengePath := xfilepath.Join(p.config.WebRootPath, http01.ChallengePath(token)) if err := xssh.RemoveRemote(client.RawClient(), challengePath, p.config.UseSCP); err != nil { return fmt.Errorf("ssh: failed to remove file after HTTP challenge: %w", err) } + log.Info("ssh: authz file removed", slog.String("path", challengePath)) + return nil } diff --git a/pkg/core/certifier/challengers/http01/ssh/ssh.go b/pkg/core/certifier/challengers/http01/ssh/ssh.go index a7b0738ed..059773c08 100644 --- a/pkg/core/certifier/challengers/http01/ssh/ssh.go +++ b/pkg/core/certifier/challengers/http01/ssh/ssh.go @@ -65,6 +65,7 @@ func NewChallenger(config *ChallengerConfig) (core.ACMEChallenger, error) { } providerConfig.JumpServers = append(providerConfig.JumpServers, jumpServerCfg) } + providerConfig.WebRootPath = config.WebRootPath provider, err := internal.NewHTTPProviderConfig(providerConfig) if err != nil { diff --git a/pkg/utils/filepath/path.go b/pkg/utils/filepath/path.go index 78c847aa5..9497dfc7c 100644 --- a/pkg/utils/filepath/path.go +++ b/pkg/utils/filepath/path.go @@ -5,6 +5,11 @@ import ( "strings" ) +const ( + separatorOnWindows = "\\" + separatorOnUnix = "/" +) + // 与标准库中的 [filepath.Dir] 类似,但会尝试保留原有的路径分隔符。 // // 入参: @@ -13,21 +18,46 @@ import ( // 出参: // - 目录路径。 func Dir(path string) string { - const SEP_WIN = "\\" - const SEP_UNIX = "/" - - sep := SEP_UNIX - if strings.Contains(path, SEP_WIN) && !strings.Contains(path, SEP_UNIX) { - sep = SEP_WIN + sep := string(stdfilepath.Separator) + if strings.Contains(path, separatorOnWindows) && !strings.Contains(path, separatorOnUnix) { + sep = separatorOnWindows + } else if strings.Contains(path, separatorOnUnix) && !strings.Contains(path, separatorOnWindows) { + sep = separatorOnUnix } dir := stdfilepath.Dir(path) + return normalizePath(sep, dir) +} - if sep != SEP_UNIX && strings.Contains(dir, SEP_UNIX) { - dir = strings.ReplaceAll(dir, SEP_UNIX, sep) - } else if sep != SEP_WIN && strings.Contains(dir, SEP_WIN) { - dir = strings.ReplaceAll(dir, SEP_WIN, sep) +// 与标准库中的 [filepath.Join] 类似,但会尝试保留原有的路径分隔符。 +// +// 入参: +// - elem: 路径元素。 +// +// 出参: +// - 连接后的路径。 +func Join(elem ...string) string { + sep := string(stdfilepath.Separator) + for _, e := range elem { + if strings.Contains(e, separatorOnWindows) && !strings.Contains(e, separatorOnUnix) { + sep = separatorOnWindows + break + } else if strings.Contains(e, separatorOnUnix) && !strings.Contains(e, separatorOnWindows) { + sep = separatorOnUnix + break + } } - return dir + path := stdfilepath.Join(elem...) + return normalizePath(sep, path) +} + +func normalizePath(separator, path string) string { + if separator != separatorOnUnix && strings.Contains(path, separatorOnUnix) { + path = strings.ReplaceAll(path, separatorOnUnix, separator) + } else if separator != separatorOnWindows && strings.Contains(path, separatorOnWindows) { + path = strings.ReplaceAll(path, separatorOnWindows, separator) + } + + return path }