letsencrypt: Re-prompt user if obtaining certs fails due to updated SA

This commit is contained in:
Matthew Holt 2015-11-02 11:06:42 -07:00
parent 2712dcd1f5
commit be0fb0053d
3 changed files with 45 additions and 10 deletions

View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
@ -84,11 +83,35 @@ func Activate(configs []server.Config) ([]server.Config, error) {
}
// client is ready, so let's get free, trusted SSL certificates! yeah!
Obtain:
certificates, failures := obtainCertificates(client, serverConfigs)
if len(failures) > 0 {
for k, v := range failures {
log.Printf("[%s] Failed to get a certificate: %s", k, v)
// Build an error string to return, using all the failures in the list.
var errMsg string
// An agreement error means we need to prompt the user (once) with updated terms
// while they're still here.
var promptedUpdatedTerms bool
for domain, obtainErr := range failures {
// If the failure was simply because the terms have changed, re-prompt and re-try
if tosErr, ok := obtainErr.(acme.TOSError); ok && !promptedUpdatedTerms {
Agreed = promptUserAgreement(tosErr.Detail, true) // TODO: Use latest URL
promptedUpdatedTerms = true
if Agreed {
err := client.AgreeToTOS()
if err != nil {
return configs, errors.New("error agreeing to updated terms: " + err.Error())
}
goto Obtain
}
}
// If user did not agree or it was any other kind of error, just append to the list of errors
errMsg += "[" + domain + "] failed to get certificate: " + obtainErr.Error() + "\n"
}
return configs, errors.New(errMsg)
}
// ... that's it. save the certs, keys, and metadata files to disk
@ -213,7 +236,7 @@ func newClient(leEmail string) (*acme.Client, error) {
leUser.Registration = reg
if !Agreed && reg.TosURL == "" {
Agreed = promptUserAgreement("<TODO>", false) // TODO
Agreed = promptUserAgreement(saURL, false) // TODO - latest URL
}
if !Agreed && reg.TosURL == "" {
return nil, errors.New("user must agree to terms")

View File

@ -97,8 +97,8 @@ func renewCertificates(configs []server.Config) (int, []error) {
// Directly convert it to days for the following checks.
daysLeft := int(expTime.Sub(time.Now().UTC()).Hours() / 24)
// Renew with a week or less remaining.
if daysLeft <= 7 {
// Renew with two weeks or less remaining.
if daysLeft <= 14 {
log.Printf("[INFO] There are %d days left on the certificate of %s. Trying to renew now.", daysLeft, cfg.Host)
client, err := newClient("") // email not used for renewal
if err != nil {
@ -127,8 +127,17 @@ func renewCertificates(configs []server.Config) (int, []error) {
// Renew certificate.
// TODO: revokeOld should be an option in the caddyfile
// TODO: bundle should be an option in the caddyfile as well :)
Renew:
newCertMeta, err := client.RenewCertificate(certMeta, true, true)
if err != nil {
if _, ok := err.(acme.TOSError); ok {
err := client.AgreeToTOS()
if err != nil {
errs = append(errs, err)
}
goto Renew
}
time.Sleep(10 * time.Second)
newCertMeta, err = client.RenewCertificate(certMeta, true, true)
if err != nil {

View File

@ -146,9 +146,9 @@ func getEmail(cfg server.Config) string {
reader := bufio.NewReader(stdin)
fmt.Println("Your sites will be served over HTTPS automatically using Let's Encrypt.")
fmt.Println("By continuing, you agree to the Let's Encrypt Subscriber Agreement at:")
fmt.Println(" https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf") // TODO: Show current SA link
fmt.Println(" " + saURL) // TODO: Show current SA link
fmt.Println("Please enter your email address so you can recover your account if needed.")
fmt.Println("You can leave it blank, but you lose the ability to recover your account.")
fmt.Println("You can leave it blank, but you'll lose the ability to recover your account.")
fmt.Print("Email address: ")
var err error
leEmail, err = reader.ReadString('\n')
@ -167,10 +167,10 @@ func getEmail(cfg server.Config) string {
// agreeing, pass false. It returns whether the user agreed or not.
func promptUserAgreement(agreementURL string, changed bool) bool {
if changed {
fmt.Printf("The Let's Encrypt Subscriber Agreement has changed:\n%s\n", agreementURL)
fmt.Printf("The Let's Encrypt Subscriber Agreement has changed:\n %s\n", agreementURL)
fmt.Print("Do you agree to the new terms? (y/n): ")
} else {
fmt.Printf("To continue, you must agree to the Let's Encrypt Subscriber Agreement:\n%s\n", agreementURL)
fmt.Printf("To continue, you must agree to the Let's Encrypt Subscriber Agreement:\n %s\n", agreementURL)
fmt.Print("Do you agree to the terms? (y/n): ")
}
@ -191,3 +191,6 @@ var stdin = io.ReadWriter(os.Stdin)
// The name of the folder for accounts where the email
// address was not provided; default 'username' if you will.
const emptyEmail = "default"
// TODO: Use latest
const saURL = "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"