Merge pull request #104 from sheerun/verify

Allow for pinning certificate of the http2 server
This commit is contained in:
ginuerzh 2017-03-24 11:42:57 +02:00 committed by GitHub
commit cf09f68827
2 changed files with 96 additions and 6 deletions

View File

@ -3,13 +3,11 @@ package gost
import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"github.com/ginuerzh/pht"
"github.com/golang/glog"
"github.com/lucas-clemente/quic-go/h2quic"
"golang.org/x/net/http2"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
@ -18,6 +16,11 @@ import (
"strings"
"sync"
"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
@ -93,6 +96,22 @@ func (c *ProxyChain) Init() {
InsecureSkipVerify: node.insecureSkipVerify(),
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.initHttp2Client(cfg, c.nodes[:i]...)
break // shortest chain for HTTP2
@ -152,6 +171,68 @@ func (c *ProxyChain) Http2Enabled() bool {
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) {
if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) {
return
@ -166,7 +247,11 @@ func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
if err != nil {
return conn, err
}
conn = tls.Client(conn, cfg)
conn, err = wrapTLSClient(conn, cfg)
if err != nil {
return conn, err
}
// enable HTTP2 ping-pong
pingIntvl, _ := strconv.Atoi(http2Node.Get("ping"))

View File

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