fix: ACME HTTP-01 via nginx config without HTTPS redirect

This commit is contained in:
orohi
2026-06-17 06:12:54 +03:00
parent f2c05d15ec
commit 0000a8974d
8 changed files with 239 additions and 26 deletions
+49 -19
View File
@@ -21,6 +21,7 @@ import (
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
)
@@ -33,42 +34,55 @@ type ACMEUser struct {
func (u *ACMEUser) GetEmail() string { return u.Email }
func (u *ACMEUser) GetRegistration() *registration.Resource { return u.Registration }
func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { return u.key }
func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey { return u.key }
type webrootProvider struct {
webroot string
store *ChallengeStore
type challengeProvider struct {
webroot string
store *ChallengeStore
httpAddr string
httpSrv *http01.ProviderServer
}
func (p *webrootProvider) Present(domain, token, keyAuth string) error {
func (p *challengeProvider) Present(domain, token, keyAuth string) error {
p.store.Set(token, keyAuth)
dir := filepath.Join(p.webroot, ".well-known", "acme-challenge")
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
return os.WriteFile(filepath.Join(dir, token), []byte(keyAuth), 0o644)
if err := os.WriteFile(filepath.Join(dir, token), []byte(keyAuth), 0o644); err != nil {
return err
}
if p.httpSrv != nil {
return p.httpSrv.Present(domain, token, keyAuth)
}
return nil
}
func (p *webrootProvider) CleanUp(domain, token, keyAuth string) error {
func (p *challengeProvider) CleanUp(domain, token, keyAuth string) error {
p.store.Delete(token)
_ = os.Remove(filepath.Join(p.webroot, ".well-known", "acme-challenge", token))
if p.httpSrv != nil {
return p.httpSrv.CleanUp(domain, token, keyAuth)
}
return nil
}
type Issuer struct {
staging bool
storageDir string
store *ChallengeStore
mu sync.Mutex
clients map[string]*lego.Client
staging bool
storageDir string
acmeHTTPAddr string
store *ChallengeStore
mu sync.Mutex
clients map[string]*lego.Client
}
func NewIssuer(staging bool, storageDir string, store *ChallengeStore) *Issuer {
func NewIssuer(staging bool, storageDir, acmeHTTPAddr string, store *ChallengeStore) *Issuer {
return &Issuer{
staging: staging,
storageDir: storageDir,
store: store,
clients: make(map[string]*lego.Client),
staging: staging,
storageDir: storageDir,
acmeHTTPAddr: acmeHTTPAddr,
store: store,
clients: make(map[string]*lego.Client),
}
}
@@ -83,7 +97,15 @@ func (i *Issuer) Obtain(ctx context.Context, email, domain, webroot string) (*ce
return nil, err
}
provider := &webrootProvider{webroot: webroot, store: i.store}
provider := &challengeProvider{webroot: webroot, store: i.store}
if i.acmeHTTPAddr != "" {
port := "80"
if strings.Contains(i.acmeHTTPAddr, ":") {
port = strings.TrimPrefix(i.acmeHTTPAddr, ":")
}
provider.httpSrv = http01.NewProviderServer("", port)
}
if err := client.Challenge.SetHTTP01Provider(provider); err != nil {
return nil, err
}
@@ -95,7 +117,7 @@ func (i *Issuer) Obtain(ctx context.Context, email, domain, webroot string) (*ce
res, err := client.Certificate.Obtain(request)
if err != nil {
return nil, fmt.Errorf("acme obtain: %w", err)
return nil, fmt.Errorf("acme obtain: %w%s", err, tlsErrorHint(err))
}
if err := i.saveToDisk(domain, res); err != nil {
@@ -105,6 +127,14 @@ func (i *Issuer) Obtain(ctx context.Context, email, domain, webroot string) (*ce
return res, nil
}
func tlsErrorHint(err error) string {
msg := err.Error()
if strings.Contains(msg, "error:tls") || strings.Contains(msg, "remote error: tls") {
return " — домен перенаправляет HTTP→HTTPS; нужен nginx location для /.well-known/acme-challenge/ без редиректа"
}
return ""
}
func (i *Issuer) getClient(email string) (*lego.Client, error) {
i.mu.Lock()
defer i.mu.Unlock()