Skip to content

net/http: decide how users should use SetSessionTicketKeys with ServeTLS, etc #22018

@costela

Description

@costela

Short version:

Using tls.Config.SetSessionTicketKeys does not work with http.Server.ServeTLS (and all methods that build on it, like ListenAndServeTLS).
Problem arises because tls.Config gets cloned inside ServeTLS and there seems to be no way to reference the new one from "outside". If this behavior can't be changed, at least a mention in the docs might be nice! 😉

Long version:

What version of Go are you using (go version)?

1.9 (but problem seems to be present starting with 1.5)

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/costela/go"
GORACE=""
GOROOT="/usr/lib/go-1.9"
GOTOOLDIR="/usr/lib/go-1.9/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build167108121=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

Attempt to use tls.Config.SetSessionTicketKeys on a running server, which is explicitly mentioned as possible in the docs:

httpsServer := &http.Server{
    Addr:      "localhost:https",
    TLSConfig: &tls.Config{
        SessionTicketKey: [32]byte{...some hard-coded key...}
    },
}

go func() {
    keys := make([][32]byte, 1)
    for t := range time.Tick(10 * time.Second) {
        bytes, _ := t.MarshalBinary()
        copy(keys[0][:], bytes)
        tlsConfig.SetSessionTicketKeys(keys) // yes, I'm aware this is highly insecure ;)
	}
}()

log.Fatal(httpsServer.ListenAndServeTLS("some.crt", "some.key"))

And then checked to see if session-resumption works with:

$ openssl s_client -connect localhost:443 -sess_out session.tmp -quiet
$ openssl s_client -connect localhost:443 -sess_in session.tmp | grep Reused

What did you expect to see?

  • session being reused in the first 10 seconds (using the hard-coded initial key)
  • session being reused "inside" a 10 second tick (using the same time-based key)
  • session not being reused "between" two 10 second ticks (using different time-based keys)

What did you see instead?

Session was reused in all scenarios, consistent with what would be expected because of the tls.Config cloning mentioned in the "short" explanation above.

Why is this a problem?

Because there is no mention in the docs (or did I miss something?) that using ServeTLS and friends will cause your reference to tls.Config to become "useless", in the sense that it doesn't reflect the running server's state. Workaround would be to create your own net.Listener, but that means a lot of boilerplate code, e.g. replicating tcpKeepAliveListener, etc.

I think I understand why the clone might be necessary (I imagine it has to do with not changing it because it might be reused), but I can't think of a reason to not update the pointer in the http.Server struct to point to the new clone. Am I missing something obvious?
But even if I am, it would be nice to mention this "gotcha" in the docs (I'd be happy to prepare a small PR for that if there are no objections)

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.early-in-cycleA change that should be done early in the 3 month dev cycle.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions