mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
feat: new list sub-commands
This commit is contained in:
parent
112e1dfd99
commit
1164cd4052
4 changed files with 272 additions and 146 deletions
133
cmd/cmd_list.go
133
cmd/cmd_list.go
|
|
@ -1,137 +1,16 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/certcrypto"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func createList() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "Display certificates and accounts information.",
|
||||
Action: list,
|
||||
Flags: createListFlags(),
|
||||
Name: "list",
|
||||
Usage: "Display certificates and accounts information.",
|
||||
Commands: []*cli.Command{
|
||||
createListCertificates(),
|
||||
createListAccounts(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func list(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.Bool(flgAccounts) && !cmd.Bool(flgNames) {
|
||||
if err := listAccount(ctx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return listCertificates(ctx, cmd)
|
||||
}
|
||||
|
||||
func listCertificates(_ context.Context, cmd *cli.Command) error {
|
||||
certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath))
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := cmd.Bool(flgNames)
|
||||
|
||||
if len(matches) == 0 {
|
||||
if !names {
|
||||
fmt.Println("No certificates found.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if !names {
|
||||
fmt.Println("Found the following certs:")
|
||||
}
|
||||
|
||||
for _, filename := range matches {
|
||||
if strings.HasSuffix(filename, storage.ExtIssuer) {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pCert, err := certcrypto.ParsePEMCertificate(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name, err := certcrypto.GetCertificateMainDomain(pCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if names {
|
||||
fmt.Println(name)
|
||||
} else {
|
||||
fmt.Println(" Certificate Name:", name)
|
||||
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
|
||||
fmt.Println(" Expiry Date:", pCert.NotAfter)
|
||||
fmt.Println(" Certificate Path:", filename)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listAccount(_ context.Context, cmd *cli.Command) error {
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
fmt.Println("No accounts were found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Found the following accounts:")
|
||||
|
||||
for _, filename := range matches {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var account storage.Account
|
||||
|
||||
err = json.Unmarshal(data, &account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := url.Parse(account.Registration.URI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(" ID:", account.GetID())
|
||||
fmt.Println(" Email:", account.Email)
|
||||
fmt.Println(" Server:", uri.Host)
|
||||
fmt.Println(" Path:", filepath.Dir(filename))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
116
cmd/cmd_list_accounts.go
Normal file
116
cmd/cmd_list_accounts.go
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
type ListAccount struct {
|
||||
storage.Account
|
||||
|
||||
Server string `json:"server,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func createListAccounts() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "accounts",
|
||||
Usage: "Display information about accounts.",
|
||||
Action: listAccounts,
|
||||
Flags: createListFlags(),
|
||||
}
|
||||
}
|
||||
|
||||
func listAccounts(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.Bool(flgFormatJSON) {
|
||||
return listAccountsJSON(ctx, cmd)
|
||||
}
|
||||
|
||||
return listAccountsText(ctx, cmd)
|
||||
}
|
||||
|
||||
func listAccountsText(_ context.Context, cmd *cli.Command) error {
|
||||
accounts, err := readAccounts(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(accounts) == 0 {
|
||||
fmt.Println("No accounts were found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Found the following accounts:")
|
||||
|
||||
for _, account := range accounts {
|
||||
fmt.Println(account.GetID())
|
||||
fmt.Println("├── Email:", account.Email)
|
||||
fmt.Println("├── Server:", account.Server)
|
||||
fmt.Println("└── Path:", account.Path)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listAccountsJSON(_ context.Context, cmd *cli.Command) error {
|
||||
accounts, err := readAccounts(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.NewEncoder(os.Stdout).Encode(accounts)
|
||||
}
|
||||
|
||||
func readAccounts(cmd *cli.Command) ([]ListAccount, error) {
|
||||
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var accounts []ListAccount
|
||||
|
||||
for _, filename := range matches {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var account storage.Account
|
||||
|
||||
err = json.Unmarshal(data, &account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var server string
|
||||
|
||||
uri, err := url.Parse(account.Registration.URI)
|
||||
if err != nil {
|
||||
log.Error("Parsing account registration URI.", log.ErrorAttr(err))
|
||||
} else {
|
||||
server = fmt.Sprintf("%s://%s", uri.Scheme, uri.Host)
|
||||
}
|
||||
|
||||
accounts = append(accounts, ListAccount{
|
||||
Account: account,
|
||||
Server: server,
|
||||
Path: filename,
|
||||
})
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
138
cmd/cmd_list_certificates.go
Normal file
138
cmd/cmd_list_certificates.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v5/certcrypto"
|
||||
"github.com/go-acme/lego/v5/cmd/internal/storage"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
type ListCertificate struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Domains []string `json:"domains,omitempty"`
|
||||
IPs []string `json:"ips,omitempty"`
|
||||
ExpirationDate string `json:"expirationDate,omitempty"`
|
||||
Expired bool `json:"expired"`
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func createListCertificates() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "certificates",
|
||||
Usage: "Display information about certificates.",
|
||||
Action: listCertificates,
|
||||
Flags: createListFlags(),
|
||||
}
|
||||
}
|
||||
|
||||
func listCertificates(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.Bool(flgFormatJSON) {
|
||||
return listCertificatesJSON(ctx, cmd)
|
||||
}
|
||||
|
||||
return listCertificatesText(ctx, cmd)
|
||||
}
|
||||
|
||||
func listCertificatesText(_ context.Context, cmd *cli.Command) error {
|
||||
certs, err := readCertificates(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
fmt.Println("No certificates were found.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Found the following certificates:")
|
||||
|
||||
for _, info := range certs {
|
||||
fmt.Println(info.Name)
|
||||
|
||||
if info.Expired {
|
||||
fmt.Println("├── Status: this certificate is expired.")
|
||||
}
|
||||
|
||||
if len(info.Domains) > 0 {
|
||||
fmt.Println("├── Domains:", strings.Join(info.Domains, ", "))
|
||||
}
|
||||
|
||||
if len(info.IPs) > 0 {
|
||||
fmt.Println("├── IPs:", strings.Join(info.IPs, ","))
|
||||
}
|
||||
|
||||
fmt.Println("├── Expiration Date:", info.ExpirationDate)
|
||||
fmt.Println("├── Issuer:", info.Issuer)
|
||||
fmt.Println("└── Certificate Path:", info.Path)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listCertificatesJSON(_ context.Context, cmd *cli.Command) error {
|
||||
certs, err := readCertificates(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.NewEncoder(os.Stdout).Encode(certs)
|
||||
}
|
||||
|
||||
func readCertificates(cmd *cli.Command) ([]ListCertificate, error) {
|
||||
certsStorage := storage.NewCertificatesStorage(cmd.String(flgPath))
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certificates []ListCertificate
|
||||
|
||||
for _, filename := range matches {
|
||||
certFilename := strings.TrimSuffix(filename, storage.ExtResource) + storage.ExtCert
|
||||
|
||||
data, err := os.ReadFile(certFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pCert, err := certcrypto.ParsePEMCertificate(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := strings.TrimSuffix(filepath.Base(certFilename), storage.ExtCert)
|
||||
|
||||
certificates = append(certificates, ListCertificate{
|
||||
Name: name,
|
||||
Domains: pCert.DNSNames,
|
||||
IPs: toStringSlice(pCert.IPAddresses),
|
||||
ExpirationDate: pCert.NotAfter.String(),
|
||||
Expired: pCert.NotAfter.Before(time.Now()),
|
||||
Issuer: pCert.Issuer.String(),
|
||||
Path: certFilename,
|
||||
})
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
func toStringSlice[T fmt.Stringer](values []T) []string {
|
||||
var s []string
|
||||
|
||||
for _, value := range values {
|
||||
s = append(s, value.String())
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
31
cmd/flags.go
31
cmd/flags.go
|
|
@ -149,10 +149,9 @@ const (
|
|||
flgReason = "reason"
|
||||
)
|
||||
|
||||
// Flag names related to the list command.
|
||||
// Flag names related to the list commands.
|
||||
const (
|
||||
flgAccounts = "accounts"
|
||||
flgNames = "names"
|
||||
flgFormatJSON = "json"
|
||||
)
|
||||
|
||||
func toEnvName(flg string) string {
|
||||
|
|
@ -671,22 +670,6 @@ func createRevokeFlags() []cli.Flag {
|
|||
return flags
|
||||
}
|
||||
|
||||
func createListFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: flgAccounts,
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Display accounts.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flgNames,
|
||||
Aliases: []string{"n"},
|
||||
Usage: "Display certificate names only.",
|
||||
},
|
||||
createPathFlag(false),
|
||||
}
|
||||
}
|
||||
|
||||
func createRegisterFlags() []cli.Flag {
|
||||
flags := []cli.Flag{
|
||||
createPathFlag(true),
|
||||
|
|
@ -699,6 +682,16 @@ func createRegisterFlags() []cli.Flag {
|
|||
return flags
|
||||
}
|
||||
|
||||
func createListFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
createPathFlag(false),
|
||||
&cli.BoolFlag{
|
||||
Name: flgFormatJSON,
|
||||
Usage: "Format the output as JSON.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createAcceptFlag() cli.Flag {
|
||||
return &cli.BoolFlag{
|
||||
Name: flgAcceptTOS,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue