mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
refactor(cmd): replace Fatal with error
This commit is contained in:
parent
ce4e7b042a
commit
44bc03ee08
10 changed files with 211 additions and 177 deletions
|
|
@ -56,20 +56,26 @@ func createRenew() *cli.Command {
|
|||
func renew(ctx context.Context, cmd *cli.Command) error {
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
return fmt.Errorf("accounts storage initialization: %w", err)
|
||||
}
|
||||
|
||||
keyType := getKeyType(cmd)
|
||||
keyType, err := getKeyType(cmd.String(flgKeyType))
|
||||
if err != nil {
|
||||
return fmt.Errorf("get the key type: %w", err)
|
||||
}
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
account, err := setupAccount(ctx, keyType, accountsStorage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up account: %w", err)
|
||||
}
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Fatal("The account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
|
||||
return fmt.Errorf("the account %s is not registered", account.Email)
|
||||
}
|
||||
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
return fmt.Errorf("certificates storage initialization: %w", err)
|
||||
}
|
||||
|
||||
meta := map[string]string{
|
||||
|
|
@ -94,10 +100,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, storage.ExtCert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while loading the certificate.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("error while reading the certificate for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
|
@ -110,7 +113,10 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
var client *lego.Client
|
||||
|
||||
if !cmd.Bool(flgARIDisable) {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
client, err = setupClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up client: %w", err)
|
||||
}
|
||||
|
||||
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
|
||||
|
||||
|
|
@ -125,13 +131,14 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
slog.Duration("sleep", ariRenewalTime.Sub(now)),
|
||||
slog.Time("renewalTime", *ariRenewalTime),
|
||||
)
|
||||
|
||||
time.Sleep(ariRenewalTime.Sub(now))
|
||||
}
|
||||
}
|
||||
|
||||
replacesCertID, err = certificate.MakeARICertID(cert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
return fmt.Errorf("error while constructing the ARI CertID for domain %q: %w", domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,11 +152,15 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
}
|
||||
|
||||
if client == nil {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
client, err = setupClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
|
||||
log.Info("acme: Trying renewal.",
|
||||
log.DomainAttr(domain),
|
||||
slog.Int("hoursRemaining", int(timeLeft.Hours())),
|
||||
|
|
@ -160,15 +171,12 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
if cmd.Bool(flgReuseKey) {
|
||||
keyBytes, errR := certsStorage.ReadFile(domain, storage.ExtKey)
|
||||
if errR != nil {
|
||||
log.Fatal("Error while loading the private key.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(errR),
|
||||
)
|
||||
return fmt.Errorf("error while reading the private key for domain %q: %w", domain, errR)
|
||||
}
|
||||
|
||||
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
|
||||
if errR != nil {
|
||||
return errR
|
||||
return fmt.Errorf("error while parsing the private key for domain %q: %w", domain, errR)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,12 +208,15 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
|
||||
certRes, err := client.Certificate.Obtain(ctx, request)
|
||||
if err != nil {
|
||||
log.Fatal("Could not obtain the certificate.", log.ErrorAttr(err))
|
||||
return fmt.Errorf("could not obtain the certificate for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
certRes.Domain = domain
|
||||
|
||||
certsStorage.SaveResource(certRes)
|
||||
err = certsStorage.SaveResource(certRes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not save the resource: %w", err)
|
||||
}
|
||||
|
||||
hook.AddPathToMetadata(meta, certRes.Domain, certRes, certsStorage)
|
||||
|
||||
|
|
@ -215,15 +226,12 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
|
|||
func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *storage.CertificatesStorage, meta map[string]string) error {
|
||||
csr, err := readCSRFile(cmd.String(flgCSR))
|
||||
if err != nil {
|
||||
log.Fatal("Could not read CSR file.",
|
||||
slog.String(flgCSR, cmd.String(flgCSR)),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("could not read CSR file %q: %w", cmd.String(flgCSR), err)
|
||||
}
|
||||
|
||||
domain, err := certcrypto.GetCSRMainDomain(csr)
|
||||
if err != nil {
|
||||
log.Fatal("Could not get CSR main domain.", log.ErrorAttr(err))
|
||||
return fmt.Errorf("could not get CSR main domain: %w", err)
|
||||
}
|
||||
|
||||
// load the cert resource from files.
|
||||
|
|
@ -231,10 +239,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
// as web servers would not be able to work with a combined file.
|
||||
certificates, err := certsStorage.ReadCertificate(domain, storage.ExtCert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while loading the certificate.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("error while reading the certificate for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
cert := certificates[0]
|
||||
|
|
@ -247,7 +252,10 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
var client *lego.Client
|
||||
|
||||
if !cmd.Bool(flgARIDisable) {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
client, err = setupClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up client: %w", err)
|
||||
}
|
||||
|
||||
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
|
||||
|
||||
|
|
@ -262,13 +270,14 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
slog.Duration("sleep", ariRenewalTime.Sub(now)),
|
||||
slog.Time("renewalTime", *ariRenewalTime),
|
||||
)
|
||||
|
||||
time.Sleep(ariRenewalTime.Sub(now))
|
||||
}
|
||||
}
|
||||
|
||||
replacesCertID, err = certificate.MakeARICertID(cert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
return fmt.Errorf("error while constructing the ARI CertID for domain %q: %w", domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,11 +286,15 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
}
|
||||
|
||||
if client == nil {
|
||||
client = setupClient(cmd, account, keyType)
|
||||
client, err = setupClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
||||
|
||||
log.Info("acme: Trying renewal.",
|
||||
log.DomainAttr(domain),
|
||||
slog.Int("hoursRemaining", int(timeLeft.Hours())),
|
||||
|
|
@ -295,10 +308,13 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
|
|||
|
||||
certRes, err := client.Certificate.ObtainForCSR(ctx, request)
|
||||
if err != nil {
|
||||
log.Fatal("Could not obtain the certificate for CSR.", log.ErrorAttr(err))
|
||||
return fmt.Errorf("could not obtain the certificate for CSR: %w", err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(certRes)
|
||||
err = certsStorage.SaveResource(certRes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not save the resource: %w", err)
|
||||
}
|
||||
|
||||
hook.AddPathToMetadata(meta, domain, certRes, certsStorage)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/lego"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
|
@ -21,56 +22,81 @@ func createRevoke() *cli.Command {
|
|||
func revoke(ctx context.Context, cmd *cli.Command) error {
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
return fmt.Errorf("accounts storage initialization: %w", err)
|
||||
}
|
||||
|
||||
keyType := getKeyType(cmd)
|
||||
keyType, err := getKeyType(cmd.String(flgKeyType))
|
||||
if err != nil {
|
||||
return fmt.Errorf("get the key type: %w", err)
|
||||
}
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
account, err := setupAccount(ctx, keyType, accountsStorage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up account: %w", err)
|
||||
}
|
||||
|
||||
if account.Registration == nil {
|
||||
log.Fatal("Account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
|
||||
return fmt.Errorf("the account %s is not registered", account.Email)
|
||||
}
|
||||
|
||||
client := newClient(cmd, account, keyType)
|
||||
client, err := newClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new client: %w", err)
|
||||
}
|
||||
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
return fmt.Errorf("certificates storage initialization: %w", err)
|
||||
}
|
||||
|
||||
certsStorage.CreateRootFolder()
|
||||
err = certsStorage.CreateRootFolder()
|
||||
if err != nil {
|
||||
return fmt.Errorf("root folder creation: %w", err)
|
||||
}
|
||||
|
||||
reason := cmd.Uint(flgReason)
|
||||
keep := cmd.Bool(flgKeep)
|
||||
|
||||
for _, domain := range cmd.StringSlice(flgDomains) {
|
||||
log.Info("Trying to revoke the certificate.", log.DomainAttr(domain))
|
||||
|
||||
certBytes, err := certsStorage.ReadFile(domain, storage.ExtCert)
|
||||
if err != nil {
|
||||
log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
reason := cmd.Uint(flgReason)
|
||||
|
||||
err = client.Certificate.RevokeWithReason(ctx, certBytes, &reason)
|
||||
if err != nil {
|
||||
log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
log.Info("Certificate was revoked.", log.DomainAttr(domain))
|
||||
|
||||
if cmd.Bool(flgKeep) {
|
||||
return nil
|
||||
}
|
||||
|
||||
certsStorage.CreateArchiveFolder()
|
||||
|
||||
err = certsStorage.MoveToArchive(domain)
|
||||
err := revokeCertificate(ctx, client, certsStorage, domain, reason, keep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Certificate was archived", log.DomainAttr(domain))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func revokeCertificate(ctx context.Context, client *lego.Client, certsStorage *storage.CertificatesStorage, domain string, reason uint, keep bool) error {
|
||||
log.Info("Trying to revoke the certificate.", log.DomainAttr(domain))
|
||||
|
||||
certBytes, err := certsStorage.ReadFile(domain, storage.ExtCert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate reading for domain %s: %w", domain, err)
|
||||
}
|
||||
|
||||
err = client.Certificate.RevokeWithReason(ctx, certBytes, &reason)
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate revocation for domain %s: %w", domain, err)
|
||||
}
|
||||
|
||||
log.Info("The certificate has been revoked.", log.DomainAttr(domain))
|
||||
|
||||
if keep {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = certsStorage.CreateArchiveFolder()
|
||||
if err != nil {
|
||||
return fmt.Errorf("archive folder creation: %w", err)
|
||||
}
|
||||
|
||||
err = certsStorage.MoveToArchive(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("The certificate has been archived.", log.DomainAttr(domain))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,26 +41,35 @@ func createRun() *cli.Command {
|
|||
func run(ctx context.Context, cmd *cli.Command) error {
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
return fmt.Errorf("accounts storage initialization: %w", err)
|
||||
}
|
||||
|
||||
keyType := getKeyType(cmd)
|
||||
keyType, err := getKeyType(cmd.String(flgKeyType))
|
||||
if err != nil {
|
||||
return fmt.Errorf("get the key type: %w", err)
|
||||
}
|
||||
|
||||
account := setupAccount(ctx, keyType, accountsStorage)
|
||||
account, err := setupAccount(ctx, keyType, accountsStorage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up account: %w", err)
|
||||
}
|
||||
|
||||
client := setupClient(cmd, account, keyType)
|
||||
client, err := setupClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set up client: %w", err)
|
||||
}
|
||||
|
||||
if account.Registration == nil {
|
||||
var reg *registration.Resource
|
||||
|
||||
reg, err = registerAccount(ctx, cmd, client)
|
||||
if err != nil {
|
||||
log.Fatal("Could not complete registration.", log.ErrorAttr(err))
|
||||
return fmt.Errorf("could not complete registration: %w", err)
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
if err = accountsStorage.Save(account); err != nil {
|
||||
log.Fatal("Could not save the account file.", log.ErrorAttr(err))
|
||||
return fmt.Errorf("could not save the account file: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
|
||||
|
|
@ -68,19 +77,25 @@ func run(ctx context.Context, cmd *cli.Command) error {
|
|||
|
||||
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Certificates storage", log.ErrorAttr(err))
|
||||
return fmt.Errorf("certificates storage initialization: %w", err)
|
||||
}
|
||||
|
||||
certsStorage.CreateRootFolder()
|
||||
err = certsStorage.CreateRootFolder()
|
||||
if err != nil {
|
||||
return fmt.Errorf("root folder creation: %w", err)
|
||||
}
|
||||
|
||||
cert, err := obtainCertificate(ctx, cmd, client)
|
||||
if err != nil {
|
||||
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
|
||||
// Due to us not returning partial certificate we can just exit here instead of at the end.
|
||||
log.Fatal("Could not obtain certificates", log.ErrorAttr(err))
|
||||
return fmt.Errorf("obtain certificate: %w", err)
|
||||
}
|
||||
|
||||
certsStorage.SaveResource(cert)
|
||||
err = certsStorage.SaveResource(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not save the resource: %w", err)
|
||||
}
|
||||
|
||||
meta := map[string]string{
|
||||
hook.EnvAccountEmail: account.Email,
|
||||
|
|
|
|||
|
|
@ -142,23 +142,17 @@ func (s *AccountsStorage) Save(account *Account) error {
|
|||
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
|
||||
}
|
||||
|
||||
func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.PrivateKey) *Account {
|
||||
func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.PrivateKey) (*Account, error) {
|
||||
fileBytes, err := os.ReadFile(s.accountFilePath)
|
||||
if err != nil {
|
||||
log.Fatal("Could not load the account file.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not read the account file (userID: %s): %w", s.GetUserID(), err)
|
||||
}
|
||||
|
||||
var account Account
|
||||
|
||||
err = json.Unmarshal(fileBytes, &account)
|
||||
if err != nil {
|
||||
log.Fatal("Could not parse the account file.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not parse the account file (userID: %s): %w", s.GetUserID(), err)
|
||||
}
|
||||
|
||||
account.key = privateKey
|
||||
|
|
@ -166,67 +160,52 @@ func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.Pri
|
|||
if account.Registration == nil || account.Registration.Body.Status == "" {
|
||||
reg, err := s.tryRecoverRegistration(ctx, privateKey)
|
||||
if err != nil {
|
||||
log.Fatal("Could not load the account file. Registration is nil.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not load the account file, registration is nil (userID: %s): %w", s.GetUserID(), err)
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
|
||||
err = s.Save(&account)
|
||||
if err != nil {
|
||||
log.Fatal("Could not save the account file. Registration is nil.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not save the account file, registration is nil (userID: %s): %w", s.GetUserID(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return &account
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.PrivateKey {
|
||||
func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) (crypto.PrivateKey, error) {
|
||||
accKeyPath := filepath.Join(s.keysPath, s.GetUserID()+".key")
|
||||
|
||||
if _, err := os.Stat(accKeyPath); os.IsNotExist(err) {
|
||||
// TODO(ldez): debug level?
|
||||
log.Info("No key found for the account. Generating a new private key.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
slog.Any("keyType", keyType),
|
||||
)
|
||||
s.createKeysFolder()
|
||||
|
||||
err := CreateNonExistingFolder(s.keysPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not check/create the directory %q for the account (userID: %s): %w", s.keysPath, s.GetUserID(), err)
|
||||
}
|
||||
|
||||
privateKey, err := generatePrivateKey(accKeyPath, keyType)
|
||||
if err != nil {
|
||||
log.Fatal("Could not generate the RSA private account key.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not generate the private account key (userID: %s): %w", s.GetUserID(), err)
|
||||
}
|
||||
|
||||
// TODO(ldez): debug level?
|
||||
log.Info("Saved key.", slog.String("filepath", accKeyPath))
|
||||
|
||||
return privateKey
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
privateKey, err := LoadPrivateKey(accKeyPath)
|
||||
if err != nil {
|
||||
log.Fatal("Could not load an RSA private key from the file.",
|
||||
slog.String("filepath", accKeyPath),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return nil, fmt.Errorf("could not load the private key from the file %q: %w", accKeyPath, err)
|
||||
}
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
func (s *AccountsStorage) createKeysFolder() {
|
||||
if err := CreateNonExistingFolder(s.keysPath); err != nil {
|
||||
log.Fatal("Could not check/create the directory for the account.",
|
||||
slog.String("userID", s.GetUserID()),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
}
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func (s *AccountsStorage) tryRecoverRegistration(ctx context.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
|
||||
|
|
|
|||
|
|
@ -164,7 +164,8 @@ func TestAccountsStorage_LoadAccount(t *testing.T) {
|
|||
|
||||
storage.accountFilePath = filepath.Join("testdata", accountFileName)
|
||||
|
||||
account := storage.LoadAccount(t.Context(), "")
|
||||
account, err := storage.LoadAccount(t.Context(), "")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Account{
|
||||
Email: "account@example.com",
|
||||
|
|
@ -213,7 +214,8 @@ func TestAccountsStorage_GetPrivateKey(t *testing.T) {
|
|||
|
||||
expectedPath := filepath.Join(test.basePath, baseAccountsRootFolderName, "test@example.com", baseKeysFolderName, "test@example.com.key")
|
||||
|
||||
privateKey := storage.GetPrivateKey(certcrypto.RSA4096)
|
||||
privateKey, err := storage.GetPrivateKey(certcrypto.RSA4096)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.FileExists(t, expectedPath)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package storage
|
|||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -22,24 +23,18 @@ func NewCertificatesReader(basePath string) *CertificatesReader {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *CertificatesReader) ReadResource(domain string) certificate.Resource {
|
||||
func (s *CertificatesReader) ReadResource(domain string) (certificate.Resource, error) {
|
||||
raw, err := s.ReadFile(domain, ExtResource)
|
||||
if err != nil {
|
||||
log.Fatal("Error while loading the metadata.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return certificate.Resource{}, fmt.Errorf("unable to load resource for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
var resource certificate.Resource
|
||||
if err = json.Unmarshal(raw, &resource); err != nil {
|
||||
log.Fatal("Error while marshaling the metadata.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return certificate.Resource{}, fmt.Errorf("unable to unmarshal resource for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
return resource
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func (s *CertificatesReader) ExistsFile(domain, extension string) bool {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import (
|
|||
|
||||
"github.com/go-acme/lego/v5/certificate"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewCertificatesWriter_ReadResource(t *testing.T) {
|
||||
reader := NewCertificatesReader("testdata")
|
||||
|
||||
resource := reader.ReadResource("example.com")
|
||||
resource, err := reader.ReadResource("example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := certificate.Resource{
|
||||
Domain: "example.com",
|
||||
|
|
|
|||
|
|
@ -75,46 +75,38 @@ func NewCertificatesWriter(config CertificatesWriterConfig) (*CertificatesWriter
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *CertificatesWriter) CreateRootFolder() {
|
||||
func (s *CertificatesWriter) CreateRootFolder() error {
|
||||
err := CreateNonExistingFolder(s.rootPath)
|
||||
if err != nil {
|
||||
log.Fatal("Could not check/create the root folder",
|
||||
slog.String("filepath", s.rootPath),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("could not check/create the root folder %q: %w", s.rootPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CertificatesWriter) CreateArchiveFolder() {
|
||||
func (s *CertificatesWriter) CreateArchiveFolder() error {
|
||||
err := CreateNonExistingFolder(s.archivePath)
|
||||
if err != nil {
|
||||
log.Fatal("Could not check/create the archive folder.",
|
||||
slog.String("filepath", s.archivePath),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("could not check/create the archive folder %q: %w", s.archivePath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) {
|
||||
func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) error {
|
||||
domain := certRes.Domain
|
||||
|
||||
// We store the certificate, private key and metadata in different files
|
||||
// as web servers would not be able to work with a combined file.
|
||||
err := s.writeFile(domain, ExtCert, certRes.Certificate)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to save Certificate.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("unable to save the certificate for the domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
if certRes.IssuerCertificate != nil {
|
||||
err = s.writeFile(domain, ExtIssuer, certRes.IssuerCertificate)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to save IssuerCertificate.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("unable to save the issuer certificate for the domain %q: %w", domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,28 +114,24 @@ func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) {
|
|||
if certRes.PrivateKey != nil {
|
||||
err = s.writeCertificateFiles(domain, certRes)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to save PrivateKey.", log.DomainAttr(domain), log.ErrorAttr(err))
|
||||
return fmt.Errorf("unable to save the private key for the domain %q: %w", domain, err)
|
||||
}
|
||||
} else if s.pem || s.pfx {
|
||||
// we don't have the private key; can't write the .pem or .pfx file
|
||||
log.Fatal("Unable to save PEM or PFX without the private key. Are you using a CSR?", log.DomainAttr(domain))
|
||||
return fmt.Errorf("unable to save PEM or PFX without the private key for the domain %q: probable usage of a CSR", domain)
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(certRes, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal("Unable to marshal CertResource.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("unable to marshal the resource for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
err = s.writeFile(domain, ExtResource, jsonBytes)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to save CertResource.",
|
||||
log.DomainAttr(domain),
|
||||
log.ErrorAttr(err),
|
||||
)
|
||||
return fmt.Errorf("unable to save the resource for the domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CertificatesWriter) MoveToArchive(domain string) error {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ func TestCertificatesWriter_CreateRootFolder(t *testing.T) {
|
|||
|
||||
require.NoDirExists(t, writer.rootPath)
|
||||
|
||||
writer.CreateRootFolder()
|
||||
err = writer.CreateRootFolder()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DirExists(t, writer.rootPath)
|
||||
}
|
||||
|
|
@ -32,7 +33,8 @@ func TestCertificatesWriter_CreateArchiveFolder(t *testing.T) {
|
|||
|
||||
require.NoDirExists(t, writer.GetArchivePath())
|
||||
|
||||
writer.CreateArchiveFolder()
|
||||
err = writer.CreateArchiveFolder()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DirExists(t, writer.GetArchivePath())
|
||||
}
|
||||
|
|
@ -53,7 +55,7 @@ func TestCertificatesWriter_SaveResource(t *testing.T) {
|
|||
require.NoFileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.key"))
|
||||
require.NoFileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.json"))
|
||||
|
||||
writer.SaveResource(&certificate.Resource{
|
||||
err = writer.SaveResource(&certificate.Resource{
|
||||
Domain: "example.com",
|
||||
CertURL: "https://acme.example.org/cert/123",
|
||||
CertStableURL: "https://acme.example.org/cert/456",
|
||||
|
|
@ -62,6 +64,7 @@ func TestCertificatesWriter_SaveResource(t *testing.T) {
|
|||
IssuerCertificate: []byte("IssuerCertificate"),
|
||||
CSR: []byte("CSR"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.FileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.crt"))
|
||||
require.FileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.issuer.crt"))
|
||||
|
|
@ -262,8 +265,11 @@ func setupCertificatesWriter(t *testing.T) *CertificatesWriter {
|
|||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
writer.CreateRootFolder()
|
||||
writer.CreateArchiveFolder()
|
||||
err = writer.CreateRootFolder()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = writer.CreateArchiveFolder()
|
||||
require.NoError(t, err)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
|
|
|||
47
cmd/setup.go
47
cmd/setup.go
|
|
@ -5,9 +5,9 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
|
@ -24,35 +24,41 @@ import (
|
|||
)
|
||||
|
||||
// setupClient creates a new client with challenge settings.
|
||||
func setupClient(cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType) *lego.Client {
|
||||
client := newClient(cmd, account, keyType)
|
||||
func setupClient(cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType) (*lego.Client, error) {
|
||||
client, err := newClient(cmd, account, keyType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new client: %w", err)
|
||||
}
|
||||
|
||||
setupChallenges(cmd, client)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func setupAccount(ctx context.Context, keyType certcrypto.KeyType, accountsStorage *storage.AccountsStorage) *storage.Account {
|
||||
privateKey := accountsStorage.GetPrivateKey(keyType)
|
||||
func setupAccount(ctx context.Context, keyType certcrypto.KeyType, accountsStorage *storage.AccountsStorage) (*storage.Account, error) {
|
||||
privateKey, err := accountsStorage.GetPrivateKey(keyType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get private key: %w", err)
|
||||
}
|
||||
|
||||
if accountsStorage.ExistsAccountFilePath() {
|
||||
return accountsStorage.LoadAccount(ctx, privateKey)
|
||||
}
|
||||
|
||||
return storage.NewAccount(accountsStorage.GetEmail(), privateKey)
|
||||
return storage.NewAccount(accountsStorage.GetEmail(), privateKey), nil
|
||||
}
|
||||
|
||||
func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
|
||||
func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) (*lego.Client, error) {
|
||||
client, err := lego.NewClient(newClientConfig(cmd, acc, keyType))
|
||||
if err != nil {
|
||||
log.Fatal("Could not create client.", log.ErrorAttr(err))
|
||||
return nil, fmt.Errorf("new client: %w", err)
|
||||
}
|
||||
|
||||
if client.GetExternalAccountRequired() && !cmd.IsSet(flgEAB) { // TODO(ldez): handle this flag.
|
||||
log.Fatal(fmt.Sprintf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC))
|
||||
return nil, errors.New("server requires External Account Binding (EAB)")
|
||||
}
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func newClientConfig(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Config {
|
||||
|
|
@ -96,26 +102,25 @@ func newClientConfig(cmd *cli.Command, acc registration.User, keyType certcrypto
|
|||
}
|
||||
|
||||
// getKeyType the type from which private keys should be generated.
|
||||
func getKeyType(cmd *cli.Command) certcrypto.KeyType {
|
||||
keyType := cmd.String(flgKeyType)
|
||||
func getKeyType(keyType string) (certcrypto.KeyType, error) {
|
||||
switch strings.ToUpper(keyType) {
|
||||
case "RSA2048":
|
||||
return certcrypto.RSA2048
|
||||
return certcrypto.RSA2048, nil
|
||||
case "RSA3072":
|
||||
return certcrypto.RSA3072
|
||||
return certcrypto.RSA3072, nil
|
||||
case "RSA4096":
|
||||
return certcrypto.RSA4096
|
||||
return certcrypto.RSA4096, nil
|
||||
case "RSA8192":
|
||||
return certcrypto.RSA8192
|
||||
return certcrypto.RSA8192, nil
|
||||
case "EC256":
|
||||
return certcrypto.EC256
|
||||
return certcrypto.EC256, nil
|
||||
case "EC384":
|
||||
return certcrypto.EC384
|
||||
return certcrypto.EC384, nil
|
||||
}
|
||||
|
||||
log.Fatal("Unsupported KeyType.", slog.String("keyType", keyType))
|
||||
// TODO(ldez): log + fallback?
|
||||
|
||||
return ""
|
||||
return "", fmt.Errorf("unsupported key type: %s", keyType)
|
||||
}
|
||||
|
||||
func getUserAgent(cmd *cli.Command) string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue