feat: per-site SSL email and error display
This commit is contained in:
+30
-25
@@ -6,7 +6,9 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -31,7 +33,7 @@ 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
|
||||
@@ -54,29 +56,29 @@ func (p *webrootProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
}
|
||||
|
||||
type Issuer struct {
|
||||
email string
|
||||
staging bool
|
||||
storageDir string
|
||||
store *ChallengeStore
|
||||
mu sync.Mutex
|
||||
client *lego.Client
|
||||
staging bool
|
||||
storageDir string
|
||||
store *ChallengeStore
|
||||
mu sync.Mutex
|
||||
clients map[string]*lego.Client
|
||||
}
|
||||
|
||||
func NewIssuer(email string, staging bool, storageDir string, store *ChallengeStore) *Issuer {
|
||||
func NewIssuer(staging bool, storageDir string, store *ChallengeStore) *Issuer {
|
||||
return &Issuer{
|
||||
email: email,
|
||||
staging: staging,
|
||||
storageDir: storageDir,
|
||||
store: store,
|
||||
clients: make(map[string]*lego.Client),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Issuer) Obtain(ctx context.Context, domain, webroot string) (*certificate.Resource, error) {
|
||||
if i.email == "" {
|
||||
return nil, errors.New("ACME_EMAIL is not configured")
|
||||
func (i *Issuer) Obtain(ctx context.Context, email, domain, webroot string) (*certificate.Resource, error) {
|
||||
email = strings.TrimSpace(strings.ToLower(email))
|
||||
if email == "" {
|
||||
return nil, errors.New("ssl email is not configured")
|
||||
}
|
||||
|
||||
client, err := i.getClient()
|
||||
client, err := i.getClient(email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,15 +105,15 @@ func (i *Issuer) Obtain(ctx context.Context, domain, webroot string) (*certifica
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (i *Issuer) getClient() (*lego.Client, error) {
|
||||
func (i *Issuer) getClient(email string) (*lego.Client, error) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
if i.client != nil {
|
||||
return i.client, nil
|
||||
if client, ok := i.clients[email]; ok {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
user, err := i.loadOrCreateUser()
|
||||
user, err := i.loadOrCreateUser(email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -140,18 +142,21 @@ func (i *Issuer) getClient() (*lego.Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
i.client = client
|
||||
i.clients[email] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (i *Issuer) accountPath() string {
|
||||
return filepath.Join(i.storageDir, "acme", "account.pem")
|
||||
func (i *Issuer) accountPath(email string) string {
|
||||
sum := sha256.Sum256([]byte(strings.ToLower(email)))
|
||||
id := hex.EncodeToString(sum[:8])
|
||||
return filepath.Join(i.storageDir, "acme", id, "account.pem")
|
||||
}
|
||||
|
||||
func (i *Issuer) loadOrCreateUser() (*ACMEUser, error) {
|
||||
user := &ACMEUser{Email: i.email}
|
||||
func (i *Issuer) loadOrCreateUser(email string) (*ACMEUser, error) {
|
||||
user := &ACMEUser{Email: email}
|
||||
path := i.accountPath(email)
|
||||
|
||||
if data, err := os.ReadFile(i.accountPath()); err == nil {
|
||||
if data, err := os.ReadFile(path); err == nil {
|
||||
block, _ := pem.Decode(data)
|
||||
if block != nil {
|
||||
key, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
@@ -168,7 +173,7 @@ func (i *Issuer) loadOrCreateUser() (*ACMEUser, error) {
|
||||
}
|
||||
user.key = key
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(i.accountPath()), 0o700); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := i.saveAccount(user); err != nil {
|
||||
@@ -183,7 +188,7 @@ func (i *Issuer) saveAccount(user *ACMEUser) error {
|
||||
return err
|
||||
}
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: der})
|
||||
return os.WriteFile(i.accountPath(), pemBytes, 0o600)
|
||||
return os.WriteFile(i.accountPath(user.Email), pemBytes, 0o600)
|
||||
}
|
||||
|
||||
func (i *Issuer) saveToDisk(domain string, res *certificate.Resource) error {
|
||||
|
||||
Reference in New Issue
Block a user