Skip to content

net/http: something about ACME letsencrypt #11651

@coolaj86

Description

@coolaj86

Solution (Note to Les Googlers)

My question was, as I misinterpreted the documentation.

  • GetCertificate == SNICallback
  • You cannot cast net.Conn to http.Conn, you can only cast net.Listener to http.Listener
    • i.e. server := &http.Serve{}; server.Server(tlsListener)

See examples at

The crux of the issue In 68 lines:

package main

import (
    "crypto/tls"
    "fmt"
    "net"
    "net/http"
    "os"
    "path/filepath"
    "strconv"
    "strings"
)

type myHandler struct{}

func (m *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.Host)
    fmt.Println(r.Method)
    fmt.Println(r.RequestURI)
    fmt.Println(r.URL) // also has many keys, such as Query
    for k, v := range r.Header {
        fmt.Println(k, v)
    }
    fmt.Println(r.Body)

    // End the request
    fmt.Fprintf(w, "Hi there, %s %q? Wow!\n\nWith Love,\n\t%s", r.Method, r.URL.Path[1:], r.Host)
}

func main() {
    port := uint(8443)
    certsPath := "/etc/letsencrypt/live"
    defaultHost := "localhost.daplie.com"

    fmt.Printf("Loading Certificates %s/%s/{privkey.pem,fullchain.pem}\n", certsPath, defaultHost)

    privkeyPath := filepath.Join(certsPath, defaultHost, "privkey.pem")
    certPath := filepath.Join(certsPath, defaultHost, "fullchain.pem")
    cert, err := tls.LoadX509KeyPair(certPath, privkeyPath)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Couldn't load default certificates: %s\n", err)
        os.Exit(1)
    }

    addr := ":" + strconv.Itoa(int(port))

    conn, err := net.Listen("tcp", addr)
    if nil != err {
        fmt.Fprintf(os.Stderr, "Couldn't bind to TCP socket %q: %s\n", addr, err)
        os.Exit(1)
    }

    tlsConfig := new(tls.Config)
    tlsConfig.Certificates = []tls.Certificate{cert}
    tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
        return &cert, nil
    }
    tlsListener := tls.NewListener(conn, tlsConfig)

    server := &http.Server{
        Addr:    addr,
        Handler: &myHandler{},
    }

    host := strings.ToLower(defaultHost)
    fmt.Printf("Listening on https://%s:%d\n", host, port)
    server.Serve(tlsListener)
}

Original Question

https://LetsEncrypt.org's release is just around the corner, but it's currently not possible to dynamically retrieve and renew certificates in go with an active http server.

To do so requires fixing a tiny regression introduced sometime between the move from httputils and now. See https://golang.org/pkg/net/http/httputil/#ServerConn.

We need to re-expose http.NewConn and http.conn.Serve.
See the diff: coolaj86@1a30898

I've got a working demo of dynamically loading certificates with this change here:
https://gist.github.com/coolaj86/16ed8fd810e19dec71be

I've already signed the CLA and with a little coaching I'm sure I could turn my example into an appropriate test case, but I'm way out of my league with the 37-page explanation of how to make a pull request...

See also #11649

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions