Adds renew hook (#845)

* chore: update golangci-lint.
* feat: support renew-hook.
This commit is contained in:
Ludovic Fernandez 2019-04-02 18:38:23 +02:00 committed by GitHub
parent 55572c2606
commit 5d557fdc6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 15 deletions

View file

@ -32,7 +32,7 @@ before_install:
- go get -u github.com/letsencrypt/pebble/... - go get -u github.com/letsencrypt/pebble/...
# Install linters and misspell # Install linters and misspell
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0 - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.16.0
- golangci-lint --version - golangci-lint --version
# Hugo - documentation # Hugo - documentation

View file

@ -6,7 +6,6 @@ import (
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"github.com/go-acme/lego/acme/api/internal/nonces" "github.com/go-acme/lego/acme/api/internal/nonces"
@ -118,9 +117,6 @@ func (j *JWS) GetKeyAuthorization(token string) (string, error) {
// Generate the Key Authorization for the challenge // Generate the Key Authorization for the challenge
jwk := &jose.JSONWebKey{Key: publicKey} jwk := &jose.JSONWebKey{Key: publicKey}
if jwk == nil {
return "", errors.New("could not generate JWK from key")
}
thumbBytes, err := jwk.Thumbprint(crypto.SHA256) thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
if err != nil { if err != nil {

View file

@ -1,8 +1,13 @@
package cmd package cmd
import ( import (
"context"
"crypto" "crypto"
"crypto/x509" "crypto/x509"
"errors"
"fmt"
"os/exec"
"strings"
"time" "time"
"github.com/go-acme/lego/certcrypto" "github.com/go-acme/lego/certcrypto"
@ -47,6 +52,10 @@ func createRenew() cli.Command {
Name: "must-staple", 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.", 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: "renew-hook",
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
},
}, },
} }
} }
@ -122,7 +131,7 @@ func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *Certif
certsStorage.SaveResource(certRes) certsStorage.SaveResource(certRes)
return nil return renewHook(ctx)
} }
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool) error { func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool) error {
@ -158,8 +167,7 @@ func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *Certificat
certsStorage.SaveResource(certRes) certsStorage.SaveResource(certRes)
return nil return renewHook(ctx)
} }
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool { func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
@ -194,3 +202,25 @@ func merge(prevDomains []string, nextDomains []string) []string {
} }
return prevDomains return prevDomains
} }
func renewHook(ctx *cli.Context) error {
hook := ctx.String("renew-hook")
if hook == "" {
return nil
}
ctxCmd, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
parts := strings.Fields(hook)
output, err := exec.CommandContext(ctxCmd, parts[0], parts[1:]...).CombinedOutput()
if len(output) > 0 {
fmt.Println(string(output))
}
if ctxCmd.Err() == context.DeadlineExceeded {
return errors.New("hook timed out")
}
return err
}

View file

@ -30,6 +30,14 @@ lego --email="foo@bar.com" --domains="example.com" --http renew
lego --email="foo@bar.com" --domains="example.com" --http renew --days 45 lego --email="foo@bar.com" --domains="example.com" --http renew --days 45
``` ```
### To renew the certificate (and hook)
The hook is executed only when the certificates are effectively renewed.
```bash
lego --email="foo@bar.com" --domains="example.com" --http renew --renew-hook="./myscript.sh"
```
### Obtain a certificate using the DNS challenge ### Obtain a certificate using the DNS challenge
```bash ```bash

View file

@ -132,5 +132,4 @@ func generateCLIHelp(models *Providers) error {
_, err = file.Write(source) _, err = file.Write(source)
return err return err
} }

View file

@ -47,13 +47,13 @@ func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl
return err return err
} }
message := &apiResponse{} message := apiResponse{}
err = d.do(req, message) err = d.do(req, &message)
if err != nil { if err != nil {
return fmt.Errorf("unable to create TXT record for domain %s and name %s: %v", domain, name, err) return fmt.Errorf("unable to create TXT record for domain %s and name %s: %v", domain, name, err)
} }
if message != nil && len(message.Message) > 0 { if len(message.Message) > 0 {
log.Infof("API response: %s", message.Message) log.Infof("API response: %s", message.Message)
} }
@ -87,13 +87,13 @@ func (d *DNSProvider) deleteTXTRecord(domain string, name string) error {
return err return err
} }
message := &apiResponse{} message := apiResponse{}
err = d.do(req, message) err = d.do(req, &message)
if err != nil { if err != nil {
return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %v", domain, name, err) return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %v", domain, name, err)
} }
if message != nil && len(message.Message) > 0 { if len(message.Message) > 0 {
log.Infof("API response: %s", message.Message) log.Infof("API response: %s", message.Message)
} }