feat: add hook on the run command. (#1157)
This commit is contained in:
parent
2f1b384411
commit
e07bf641ab
5 changed files with 78 additions and 48 deletions
|
@ -1,14 +1,8 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v3/certcrypto"
|
||||
|
@ -145,7 +139,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
|
|||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
||||
return renewHook(ctx, meta)
|
||||
return launchHook(ctx.String("renew-hook"), meta)
|
||||
}
|
||||
|
||||
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
|
||||
|
@ -185,7 +179,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
|
|||
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
|
||||
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
|
||||
|
||||
return renewHook(ctx, meta)
|
||||
return launchHook(ctx.String("renew-hook"), meta)
|
||||
}
|
||||
|
||||
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
|
||||
|
@ -220,40 +214,3 @@ func merge(prevDomains []string, nextDomains []string) []string {
|
|||
}
|
||||
return prevDomains
|
||||
}
|
||||
|
||||
func renewHook(ctx *cli.Context, meta map[string]string) error {
|
||||
hook := ctx.String("renew-hook")
|
||||
if hook == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
|
||||
parts := strings.Fields(hook)
|
||||
|
||||
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
|
||||
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
|
||||
|
||||
output, err := cmdCtx.CombinedOutput()
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
|
||||
if ctxCmd.Err() == context.DeadlineExceeded {
|
||||
return errors.New("hook timed out")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func metaToEnv(meta map[string]string) []string {
|
||||
var envs []string
|
||||
|
||||
for k, v := range meta {
|
||||
envs = append(envs, k+"="+v)
|
||||
}
|
||||
|
||||
return envs
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ func createRun() cli.Command {
|
|||
Name: "must-staple",
|
||||
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "run-hook",
|
||||
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +90,14 @@ func run(ctx *cli.Context) error {
|
|||
|
||||
certsStorage.SaveResource(cert)
|
||||
|
||||
return nil
|
||||
meta := map[string]string{
|
||||
renewEnvAccountEmail: account.Email,
|
||||
renewEnvCertDomain: cert.Domain,
|
||||
renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"),
|
||||
renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"),
|
||||
}
|
||||
|
||||
return launchHook(ctx.String("run-hook"), meta)
|
||||
}
|
||||
|
||||
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
|
||||
|
|
47
cmd/hook.go
Normal file
47
cmd/hook.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func launchHook(hook string, meta map[string]string) error {
|
||||
if hook == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
|
||||
parts := strings.Fields(hook)
|
||||
|
||||
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
|
||||
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
|
||||
|
||||
output, err := cmdCtx.CombinedOutput()
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
|
||||
if ctxCmd.Err() == context.DeadlineExceeded {
|
||||
return errors.New("hook timed out")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func metaToEnv(meta map[string]string) []string {
|
||||
var envs []string
|
||||
|
||||
for k, v := range meta {
|
||||
envs = append(envs, k+"="+v)
|
||||
}
|
||||
|
||||
return envs
|
||||
}
|
|
@ -37,11 +37,11 @@ GLOBAL OPTIONS:
|
|||
--hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
|
||||
--key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "ec384")
|
||||
--filename value (deprecated) Filename of the generated certificate.
|
||||
--path value Directory to use for storing the data. (default: "./.lego")
|
||||
--path value Directory to use for storing the data. (default: "./.lego") [$LEGO_PATH]
|
||||
--http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80")
|
||||
--http.proxy-header value Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy. (default: "Host")
|
||||
--http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be served at /.well-known/acme-challenge
|
||||
--http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge
|
||||
--http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
|
||||
--tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.
|
||||
--tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443")
|
||||
|
|
|
@ -18,6 +18,21 @@ lego --email="foo@bar.com" --domains="example.com" --http run
|
|||
|
||||
(Find your certificate in the `.lego` folder of current working directory.)
|
||||
|
||||
### Obtain a certificate (and hook)
|
||||
|
||||
The hook is executed only when the certificates are effectively created.
|
||||
|
||||
```bash
|
||||
lego --email="foo@bar.com" --domains="example.com" --http run --run-hook="./myscript.sh"
|
||||
```
|
||||
|
||||
Some information are added to the environment variables when the hook is used:
|
||||
|
||||
- `LEGO_ACCOUNT_EMAIL`: the email of the account.
|
||||
- `LEGO_CERT_DOMAIN`: the main domain of the certificate.
|
||||
- `LEGO_CERT_PATH`: the path of the certificate.
|
||||
- `LEGO_CERT_KEY_PATH`: the path of the certificate key.
|
||||
|
||||
### To renew the certificate
|
||||
|
||||
```bash
|
||||
|
|
Loading…
Reference in a new issue