Skip to content

Commit

Permalink
Added tls helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
Michal Witkowski committed Nov 5, 2016
1 parent 2a4ff69 commit c10cc00
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 56 deletions.
62 changes: 62 additions & 0 deletions connhelpers/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.

package connhelpers

import (
"crypto/tls"
"fmt"
)



// TlsConfigForCerts is a returns a simple `tls.Config` with the given server cert loaded.
// This is useful if you can't use `http.ListenAndServerTLS` when using a custom `net.Listener`.
func TlsConfigForServerCerts(certFile string, keyFile string) (*tls.Config, error) {
var err error
config := new(tls.Config)
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return config, nil
}

// TlsConfigWithHttp2Enabled makes it easy to configure the given `tls.Config` to prefer H2 connections.
// This is useful if you can't use `http.ListenAndServerTLS` when using a custom `net.Listener`.
func TlsConfigWithHttp2Enabled(config *tls.Config) (*tls.Config, error) {
// mostly based on http2 code in the standards library.
if config.CipherSuites != nil {
// If they already provided a CipherSuite list, return
// an error if it has a bad order or is missing
// ECDHE_RSA_WITH_AES_128_GCM_SHA256.
const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
haveRequired := false
for _, cs := range config.CipherSuites {
if cs == requiredCipher {
haveRequired = true
}
}
if !haveRequired {
return nil, fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
}
}

config.PreferServerCipherSuites = true

haveNPN := false
for _, p := range config.NextProtos {
if p == "h2" {
haveNPN = true
break
}
}
if !haveNPN {
config.NextProtos = append(config.NextProtos, "h2")
}
config.NextProtos = append(config.NextProtos, "h2-14")
// make sure http 1.1 is *after* all of the other ones.
config.NextProtos = append(config.NextProtos, "http/1.1")
return config, nil
}
62 changes: 6 additions & 56 deletions example/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"golang.org/x/net/context/ctxhttp"
_ "golang.org/x/net/trace"
"github.com/mwitkow/go-conntrack"
"github.com/mwitkow/go-conntrack/connhelpers"
)

var (
Expand Down Expand Up @@ -66,72 +67,21 @@ func main() {
if !*useTls {
httpListener = listener
} else {
tlsConfig, err := tlsConfigForCert(*tlsCertFilePath, *tlsKeyFilePath)
tlsConfig, err := connhelpers.TlsConfigForServerCerts(*tlsCertFilePath, *tlsKeyFilePath)
if err != nil {
log.Fatalf("Failed configuring TLS: %v", err)
}
tlsConfig, err = connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
if err != nil {
log.Fatalf("Failed configuring TLS: %v", err)
}
log.Printf("Listening with TLS")
//httpServer.TLSConfig = tlsConfig
tlsListener := tls.NewListener(listener, tlsConfig)
httpListener = tlsListener

}
//httpListener.Addr()
log.Printf("Listening on: %s", listener.Addr().String())
if err := httpServer.Serve(httpListener); err != nil {
log.Fatalf("Failed listning: %v", err)
}
}

// tlsConfigForCert is needed as it duplicates ListenAndServeTLS, but for a listener.
func tlsConfigForCert(certFile string, keyFile string) (*tls.Config, error) {
var err error
config := new(tls.Config)
config, err = tlsEnableHttp2(config)
if err != nil {
return nil, err
}
// Make sure http1.1 is *after* h2.
config.NextProtos = append(config.NextProtos, "http/1.1")

config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return tlsEnableHttp2(config)
}

//tlsEnableHttp2 performs what http2ConfigureServer does privately.
func tlsEnableHttp2(config *tls.Config) (*tls.Config, error) {
if config.CipherSuites != nil {
// If they already provided a CipherSuite list, return
// an error if it has a bad order or is missing
// ECDHE_RSA_WITH_AES_128_GCM_SHA256.
const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
haveRequired := false
for _, cs := range config.CipherSuites {
if cs == requiredCipher {
haveRequired = true
}
}
if !haveRequired {
return nil, fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
}
}

config.PreferServerCipherSuites = true

haveNPN := false
for _, p := range config.NextProtos {
if p == "h2" {
haveNPN = true
break
}
}
if !haveNPN {
config.NextProtos = append(config.NextProtos, "h2")
}
config.NextProtos = append(config.NextProtos, "h2-14")
return config, nil
}
16 changes: 16 additions & 0 deletions listener_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sync"

"golang.org/x/net/trace"
"time"
)

const (
Expand All @@ -20,6 +21,7 @@ type listenerOpts struct {
name string
monitoring bool
tracing bool
tcpKeepAlive time.Duration
}

type listenerOpt func(*listenerOpts)
Expand All @@ -45,6 +47,16 @@ func TrackWithTracing() listenerOpt {
}
}

// TrackWithTcpKeepAlive makes sure that any `net.TCPConn` that get accepted have a keep-alive.
// This is useful for HTTP servers in order for, for example laptops, to not use up resources on the
// server while they don't utilise their connection.
// A value of 0 disables it.
func TrackWithTcpKeepAlive(keepalive time.Duration) listenerOpt {
return func(opts *listenerOpts) {
opts.tcpKeepAlive = keepalive
}
}

type connTrackListener struct {
net.Listener
opts *listenerOpts
Expand Down Expand Up @@ -75,6 +87,10 @@ func (ct *connTrackListener) Accept() (net.Conn, error) {
if err != nil {
return nil, err
}
if tcpConn, ok := conn.(net.TCPConn); ok && ct.opts.tcpKeepAlive > 0 {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(ct.opts.tcpKeepAlive)
}
return newServerConnTracker(conn, ct.opts), nil
}

Expand Down

0 comments on commit c10cc00

Please sign in to comment.