Allow for verifying CA of http2 server

This commit is contained in:
Adam Stankiewicz 2017-03-09 23:53:01 +01:00
parent 42a9d102dd
commit 8901a92289
No known key found for this signature in database
GPG Key ID: A62480DCEAC884DF
2 changed files with 96 additions and 6 deletions

View File

@ -3,13 +3,11 @@ package gost
import ( import (
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/ginuerzh/pht"
"github.com/golang/glog"
"github.com/lucas-clemente/quic-go/h2quic"
"golang.org/x/net/http2"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -18,6 +16,11 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/ginuerzh/pht"
"github.com/golang/glog"
"github.com/lucas-clemente/quic-go/h2quic"
"golang.org/x/net/http2"
) )
// Proxy chain holds a list of proxy nodes // Proxy chain holds a list of proxy nodes
@ -93,6 +96,22 @@ func (c *ProxyChain) Init() {
InsecureSkipVerify: node.insecureSkipVerify(), InsecureSkipVerify: node.insecureSkipVerify(),
ServerName: node.serverName, ServerName: node.serverName,
} }
caFile := node.caFile()
if caFile != "" {
cfg.RootCAs = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile)
if err != nil {
glog.Fatal(err)
}
if !cfg.RootCAs.AppendCertsFromPEM(data) {
glog.Fatal(err)
}
}
c.http2NodeIndex = i c.http2NodeIndex = i
c.initHttp2Client(cfg, c.nodes[:i]...) c.initHttp2Client(cfg, c.nodes[:i]...)
break // shortest chain for HTTP2 break // shortest chain for HTTP2
@ -152,6 +171,68 @@ func (c *ProxyChain) Http2Enabled() bool {
return c.http2Enabled return c.http2Enabled
} }
// Wrap a net.Conn into a client tls connection, performing any
// additional verification as needed.
//
// As of go 1.3, crypto/tls only supports either doing no certificate
// verification, or doing full verification including of the peer's
// DNS name. For consul, we want to validate that the certificate is
// signed by a known CA, but because consul doesn't use DNS names for
// node names, we don't verify the certificate DNS names. Since go 1.3
// no longer supports this mode of operation, we have to do it
// manually.
//
// This code is taken from consul:
// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go
func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
var err error
var tlsConn *tls.Conn
tlsConn = tls.Client(conn, tlsConfig)
// If crypto/tls is doing verification, there's no need to do our own.
if tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil
}
// Similarly if we use host's CA, we can do full handshake
if tlsConfig.RootCAs == nil {
return tlsConn, nil
}
// Otherwise perform handshake, but don't verify the domain
//
// The following is lightly-modified from the doFullHandshake
// method in https://golang.org/src/crypto/tls/handshake_client.go
if err = tlsConn.Handshake(); err != nil {
tlsConn.Close()
return nil, err
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
return tlsConn, err
}
func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) { func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) { if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) {
return return
@ -166,7 +247,11 @@ func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
if err != nil { if err != nil {
return conn, err return conn, err
} }
conn = tls.Client(conn, cfg)
conn, err = wrapTLSClient(conn, cfg)
if err != nil {
return conn, err
}
// enable HTTP2 ping-pong // enable HTTP2 ping-pong
pingIntvl, _ := strconv.Atoi(http2Node.Get("ping")) pingIntvl, _ := strconv.Atoi(http2Node.Get("ping"))

View File

@ -3,12 +3,13 @@ package gost
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"github.com/golang/glog"
"net" "net"
"net/url" "net/url"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/golang/glog"
) )
// Proxy node represent a proxy // Proxy node represent a proxy
@ -142,6 +143,10 @@ func (node *ProxyNode) insecureSkipVerify() bool {
return !node.getBool("secure") return !node.getBool("secure")
} }
func (node *ProxyNode) caFile() string {
return node.Get("ca")
}
func (node *ProxyNode) certFile() string { func (node *ProxyNode) certFile() string {
if cert := node.Get("cert"); cert != "" { if cert := node.Get("cert"); cert != "" {
return cert return cert