Skip to content

Commit

Permalink
Add flag --bupem to make a bottom-up PEM
Browse files Browse the repository at this point in the history
Outputs also a 'bottom-up' PEM (.bupem) file containing, in this order:

Private .key
Public .crt
Issuer .crt

Saves a manual `cat` step to build such a file. Useful for e.g.
Postfix (+ >=OpenSSL 1.1.1) with its `*_chain_files` options:

```
smtpd_tls_chain_files =
  /etc/postfix/mail.example.com.rsa4096.bupem,
  /etc/postfix/mail.example.com.ec256.bupem
smtp_tls_chain_files =
  /etc/postfix/mail.example.com.rsa4096.bupem,
  /etc/postfix/mail.example.com.ec256.bupem
```

The 'bottom-up' convention expects the private key first.
  • Loading branch information
systemcrash committed Mar 7, 2023
1 parent e638d79 commit 9e6f4a0
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
22 changes: 20 additions & 2 deletions cmd/certs_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const (
type CertificatesStorage struct {
rootPath string
archivePath string
bundle bool
bupem bool
pem bool
pfx bool
pfxPassword string
Expand All @@ -54,6 +56,8 @@ func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
return &CertificatesStorage{
rootPath: filepath.Join(ctx.String("path"), baseCertificatesFolderName),
archivePath: filepath.Join(ctx.String("path"), baseArchivesFolderName),
bundle: !ctx.Bool("no-bundle"),
bupem: ctx.Bool("bupem"),
pem: ctx.Bool("pem"),
pfx: ctx.Bool("pfx"),
pfxPassword: ctx.String("pfx.pass"),
Expand Down Expand Up @@ -102,8 +106,8 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) {
if err != nil {
log.Fatalf("Unable to save PrivateKey for domain %s\n\t%v", domain, err)
}
} else if s.pem || s.pfx {
// we don't have the private key; can't write the .pem or .pfx file
} else if s.bupem || s.pem || s.pfx {
// we don't have the private key; can't write the .bupem, .pem or .pfx file
log.Fatalf("Unable to save PEM or PFX without private key for domain %s. Are you using a CSR?", domain)
}

Expand Down Expand Up @@ -181,6 +185,20 @@ func (s *CertificatesStorage) WriteCertificateFiles(domain string, certRes *cert
return fmt.Errorf("unable to save key file: %w", err)
}

if s.bupem { // how we make the bupem depends on whether the bundle flag is true. bottom-up == private key first.
if s.bundle { // bundle is true by default. So certRes.Certificate already consists of OurX509+Int+CA
err = s.WriteFile(domain, ".bupem", bytes.Join([][]byte{certRes.PrivateKey, certRes.Certificate}, nil))
if err != nil {
return fmt.Errorf("unable to save bottom-up PEM file: %w", err)
}
} else { // when bundle is false, we want pKey+OurX509+Int+CA
err = s.WriteFile(domain, ".bupem", bytes.Join([][]byte{certRes.PrivateKey, certRes.Certificate, certRes.IssuerCertificate}, nil))
if err != nil {
return fmt.Errorf("unable to save bottom-up PEM file: %w", err)
}
}
}

if s.pem {
err = s.WriteFile(domain, ".pem", bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil))
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ func CreateFlags(defaultPath string) []cli.Flag {
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries.",
Value: 10,
},
&cli.BoolFlag{
Name: "bupem",
Usage: "Generate an additional 'bottom-up' .bupem (PEM, base64) file by concatenating the .key, .crt and issuer .crt together, in that order.",
},
&cli.BoolFlag{
Name: "pem",
Usage: "Generate an additional .pem (base64) file by concatenating the .key and .crt files together.",
Expand Down
1 change: 1 addition & 0 deletions docs/data/zz_cli_help.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ COMMANDS:
GLOBAL OPTIONS:
--accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service. (default: false)
--bupem Generate an additional 'bottom-up' .bupem (PEM, base64) file by concatenating the .key, .crt and issuer .crt together, in that order. (default: false)
--cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
--csr value, -c value Certificate signing request filename, if an external CSR is to be used.
--dns value Solve a DNS-01 challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
Expand Down

0 comments on commit 9e6f4a0

Please sign in to comment.