mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +02:00

- save ACME private key to reuse previous registered ACME account - properly renew certificate with `Certificate.RenewWithOptions` instead of re-obtaining with `Certificate.Obtain`
114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package autocert
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"os"
|
|
|
|
"github.com/go-acme/lego/v4/certcrypto"
|
|
"github.com/go-acme/lego/v4/lego"
|
|
E "github.com/yusing/go-proxy/internal/error"
|
|
"github.com/yusing/go-proxy/internal/logging"
|
|
"github.com/yusing/go-proxy/internal/utils"
|
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
|
|
|
"github.com/yusing/go-proxy/internal/config/types"
|
|
)
|
|
|
|
type Config types.AutoCertConfig
|
|
|
|
var (
|
|
ErrMissingDomain = E.New("missing field 'domains'")
|
|
ErrMissingEmail = E.New("missing field 'email'")
|
|
ErrMissingProvider = E.New("missing field 'provider'")
|
|
ErrUnknownProvider = E.New("unknown provider")
|
|
)
|
|
|
|
func NewConfig(cfg *types.AutoCertConfig) *Config {
|
|
if cfg.CertPath == "" {
|
|
cfg.CertPath = CertFileDefault
|
|
}
|
|
if cfg.KeyPath == "" {
|
|
cfg.KeyPath = KeyFileDefault
|
|
}
|
|
if cfg.Provider == "" {
|
|
cfg.Provider = ProviderLocal
|
|
}
|
|
if cfg.ACMEKeyPath == "" {
|
|
cfg.ACMEKeyPath = ACMEKeyFileDefault
|
|
}
|
|
return (*Config)(cfg)
|
|
}
|
|
|
|
func (cfg *Config) GetProvider() (*Provider, E.Error) {
|
|
b := E.NewBuilder("autocert errors")
|
|
|
|
if cfg.Provider != ProviderLocal {
|
|
if len(cfg.Domains) == 0 {
|
|
b.Add(ErrMissingDomain)
|
|
}
|
|
if cfg.Provider == "" {
|
|
b.Add(ErrMissingProvider)
|
|
}
|
|
if cfg.Email == "" {
|
|
b.Add(ErrMissingEmail)
|
|
}
|
|
// check if provider is implemented
|
|
_, ok := providersGenMap[cfg.Provider]
|
|
if !ok {
|
|
b.Add(ErrUnknownProvider.
|
|
Subject(cfg.Provider).
|
|
Withf(strutils.DoYouMean(utils.NearestField(cfg.Provider, providersGenMap))))
|
|
}
|
|
}
|
|
|
|
if b.HasError() {
|
|
return nil, b.Error()
|
|
}
|
|
|
|
var privKey *ecdsa.PrivateKey
|
|
var err error
|
|
|
|
if privKey, err = cfg.loadACMEKey(); err != nil {
|
|
logging.Err(err).Msg("load ACME private key failed, generating one...")
|
|
privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, E.New("generate ACME private key").With(err)
|
|
}
|
|
if err = cfg.saveACMEKey(privKey); err != nil {
|
|
return nil, E.New("save ACME private key").With(err)
|
|
}
|
|
}
|
|
|
|
user := &User{
|
|
Email: cfg.Email,
|
|
key: privKey,
|
|
}
|
|
|
|
legoCfg := lego.NewConfig(user)
|
|
legoCfg.Certificate.KeyType = certcrypto.RSA2048
|
|
|
|
return &Provider{
|
|
cfg: cfg,
|
|
user: user,
|
|
legoCfg: legoCfg,
|
|
}, nil
|
|
}
|
|
|
|
func (cfg *Config) loadACMEKey() (*ecdsa.PrivateKey, error) {
|
|
data, err := os.ReadFile(cfg.ACMEKeyPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return x509.ParseECPrivateKey(data)
|
|
}
|
|
|
|
func (cfg *Config) saveACMEKey(key *ecdsa.PrivateKey) error {
|
|
data, err := x509.MarshalECPrivateKey(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(cfg.ACMEKeyPath, data, 0o600)
|
|
}
|