2019-03-11 16:56:48 +00:00
|
|
|
package cmd
|
2018-12-06 21:50:17 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2020-09-02 01:20:01 +00:00
|
|
|
"github.com/go-acme/lego/v4/certcrypto"
|
|
|
|
"github.com/go-acme/lego/v4/lego"
|
|
|
|
"github.com/go-acme/lego/v4/log"
|
|
|
|
"github.com/go-acme/lego/v4/registration"
|
2022-02-13 11:28:51 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
2018-12-06 21:50:17 +00:00
|
|
|
)
|
|
|
|
|
2020-07-09 23:48:18 +00:00
|
|
|
const filePerm os.FileMode = 0o600
|
2018-12-06 21:50:17 +00:00
|
|
|
|
|
|
|
func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
|
2019-02-08 18:29:28 +00:00
|
|
|
keyType := getKeyType(ctx)
|
|
|
|
privateKey := accountsStorage.GetPrivateKey(keyType)
|
2018-12-06 21:50:17 +00:00
|
|
|
|
|
|
|
var account *Account
|
|
|
|
if accountsStorage.ExistsAccountFilePath() {
|
|
|
|
account = accountsStorage.LoadAccount(privateKey)
|
|
|
|
} else {
|
|
|
|
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
|
|
|
|
}
|
|
|
|
|
2019-02-08 18:29:28 +00:00
|
|
|
client := newClient(ctx, account, keyType)
|
2018-12-06 21:50:17 +00:00
|
|
|
|
|
|
|
return account, client
|
|
|
|
}
|
|
|
|
|
2019-02-08 18:29:28 +00:00
|
|
|
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
|
2018-12-06 21:50:17 +00:00
|
|
|
config := lego.NewConfig(acc)
|
2024-09-20 17:47:50 +00:00
|
|
|
config.CADirURL = ctx.String(flgServer)
|
2019-01-09 07:29:17 +00:00
|
|
|
|
|
|
|
config.Certificate = lego.CertificateConfig{
|
2024-06-13 20:48:04 +00:00
|
|
|
KeyType: keyType,
|
2024-09-20 17:47:50 +00:00
|
|
|
Timeout: time.Duration(ctx.Int(flgCertTimeout)) * time.Second,
|
|
|
|
OverallRequestLimit: ctx.Int(flgOverallRequestLimit),
|
2019-01-09 07:29:17 +00:00
|
|
|
}
|
2022-06-29 18:56:56 +00:00
|
|
|
config.UserAgent = getUserAgent(ctx)
|
2018-12-06 21:50:17 +00:00
|
|
|
|
2024-09-20 17:47:50 +00:00
|
|
|
if ctx.IsSet(flgHTTPTimeout) {
|
|
|
|
config.HTTPClient.Timeout = time.Duration(ctx.Int(flgHTTPTimeout)) * time.Second
|
2018-12-06 21:50:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client, err := lego.NewClient(config)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Could not create client: %v", err)
|
|
|
|
}
|
|
|
|
|
2024-09-20 17:47:50 +00:00
|
|
|
if client.GetExternalAccountRequired() && !ctx.IsSet(flgEAB) {
|
|
|
|
log.Fatalf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC)
|
2018-12-06 21:50:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return client
|
|
|
|
}
|
|
|
|
|
2020-05-08 17:35:25 +00:00
|
|
|
// getKeyType the type from which private keys should be generated.
|
2018-12-06 21:50:17 +00:00
|
|
|
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
|
2024-09-20 17:47:50 +00:00
|
|
|
keyType := ctx.String(flgKeyType)
|
2018-12-06 21:50:17 +00:00
|
|
|
switch strings.ToUpper(keyType) {
|
|
|
|
case "RSA2048":
|
|
|
|
return certcrypto.RSA2048
|
2023-03-01 15:27:20 +00:00
|
|
|
case "RSA3072":
|
|
|
|
return certcrypto.RSA3072
|
2018-12-06 21:50:17 +00:00
|
|
|
case "RSA4096":
|
|
|
|
return certcrypto.RSA4096
|
|
|
|
case "RSA8192":
|
|
|
|
return certcrypto.RSA8192
|
|
|
|
case "EC256":
|
|
|
|
return certcrypto.EC256
|
|
|
|
case "EC384":
|
|
|
|
return certcrypto.EC384
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Fatalf("Unsupported KeyType: %s", keyType)
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func getEmail(ctx *cli.Context) string {
|
2024-09-20 17:47:50 +00:00
|
|
|
email := ctx.String(flgEmail)
|
2021-03-04 19:16:59 +00:00
|
|
|
if email == "" {
|
2024-09-20 17:47:50 +00:00
|
|
|
log.Fatalf("You have to pass an account (email address) to the program using --%s or -m", flgEmail)
|
2018-12-06 21:50:17 +00:00
|
|
|
}
|
|
|
|
return email
|
|
|
|
}
|
|
|
|
|
2022-06-29 18:56:56 +00:00
|
|
|
func getUserAgent(ctx *cli.Context) string {
|
2024-09-20 17:47:50 +00:00
|
|
|
return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", ctx.String(flgUserAgent), ctx.App.Version))
|
2022-06-29 18:56:56 +00:00
|
|
|
}
|
|
|
|
|
2018-12-06 21:50:17 +00:00
|
|
|
func createNonExistingFolder(path string) error {
|
|
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
2020-07-09 23:48:18 +00:00
|
|
|
return os.MkdirAll(path, 0o700)
|
2018-12-06 21:50:17 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func readCSRFile(filename string) (*x509.CertificateRequest, error) {
|
2021-08-25 09:44:11 +00:00
|
|
|
bytes, err := os.ReadFile(filename)
|
2018-12-06 21:50:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
raw := bytes
|
|
|
|
|
|
|
|
// see if we can find a PEM-encoded CSR
|
|
|
|
var p *pem.Block
|
|
|
|
rest := bytes
|
|
|
|
for {
|
|
|
|
// decode a PEM block
|
|
|
|
p, rest = pem.Decode(rest)
|
|
|
|
|
|
|
|
// did we fail?
|
|
|
|
if p == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// did we get a CSR?
|
2021-06-05 11:47:39 +00:00
|
|
|
if p.Type == "CERTIFICATE REQUEST" || p.Type == "NEW CERTIFICATE REQUEST" {
|
2018-12-06 21:50:17 +00:00
|
|
|
raw = p.Bytes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no PEM-encoded CSR
|
|
|
|
// assume we were given a DER-encoded ASN.1 CSR
|
|
|
|
// (if this assumption is wrong, parsing these bytes will fail)
|
|
|
|
return x509.ParseCertificateRequest(raw)
|
|
|
|
}
|