go – crypto/autocert package stopped working, hangs forever waiting for acme challenge-ThrowExceptions

Exception or error:

I’ve been relying on this lib for sometime now without any issues but suddenly it is completely ignoring the cached cert, attempts to get a new cert, but then it just waits forever for the acme challenge which apparently never comes. As far as I can tell my usage is completely correct (and this has worked for the past several weeks without issue):

certManager := autocert.Manager{
    Prompt:     autocert.AcceptTOS,
    HostPolicy: autocert.HostWhitelist("example.com"),
    Cache:      autocert.DirCache("certs"),
}

c1 := make(chan error)

go func() {
    err := http.ListenAndServe(":80", certManager.HTTPHandler(nil))
    if err != nil {
        c1 <- err
    }
}()

err := <-c1
if err != nil {
    os.Stderr.WriteString(
        fmt.Sprintf(
            "failed to serve certification manager for acme verification - %v\n",
            err,
        )
    )
}
if err := s.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
    os.Stderr.WriteString(fmt.Sprintf("Failed to start: %v\n", err))
    return err
}

I’m running this on AWS, I’m 100% certain that route53 is properly configured (it works over http) and the security group has all ports open for all traffic so I know that isn’t the issue either.

In the past if the challenge failed or if the certs couldn’t be loaded I received some sort of useful debug information, but without any apparent change to this particular code and any meaningful error information I’m completely stuck. How can I debug this and get some meaningful explanation as to why this working code is suddenly failing? Is there a way I can know if letsencrypt is throttling me or something?

How to solve:

I was able to resolve this by changing my server config from this:

s := &http.Server{
    Addr:    ":https",
    Handler: allowCORS(mux),
    TLSConfig: &tls.Config{
        GetCertificate: certManager.GetCertificate,
    },
}

to this:

s := &http.Server{
    Addr:    ":https",
    Handler: allowCORS(mux),
    TLSConfig: certManager.TLSConfig(),
}

which made explicitly serving over HTTP redundant, so I also removed this code and its error handling:

go func() {
    err := http.ListenAndServe(":http", certManager.HTTPHandler(nil))
    if err != nil {
        c1 <- err
    }
}()

Ultimately my config looks something like this:

certManager := autocert.Manager{
    Prompt:     autocert.AcceptTOS,
    HostPolicy: autocert.HostWhitelist("example.com"),
    Cache:      autocert.DirCache("certs"),
}

s := &http.Server{
    Addr:      ":https",
    Handler:   allowCORS(mux),
    TLSConfig: certManager.TLSConfig(),
}

log.Fatal(s.ListenAndServeTLS("", ""))

And my TLS certificate works as expected

If someone wants to explain why this worked I will happily upvote and mark it as the correct answer!

Leave a Reply

Your email address will not be published. Required fields are marked *