// tlsConfig builds a tls.Config for dialing the upstream host. Constructed // tls.Configs are cached on a per-masquerade basis to enable client session // caching and reduce the amount of PEM certificate parsing. func (serverInfo *ServerInfo) tlsConfig(masquerade *Masquerade) *tls.Config { serverInfo.tlsConfigsMutex.Lock() defer serverInfo.tlsConfigsMutex.Unlock() if serverInfo.tlsConfigs == nil { serverInfo.tlsConfigs = make(map[string]*tls.Config) } configKey := "" serverName := serverInfo.Host if masquerade != nil { configKey = masquerade.Domain + "|" + masquerade.RootCA serverName = masquerade.Domain } tlsConfig := serverInfo.tlsConfigs[configKey] if tlsConfig == nil { tlsConfig = &tls.Config{ ClientSessionCache: tls.NewLRUClientSessionCache(1000), InsecureSkipVerify: serverInfo.InsecureSkipVerify, ServerName: serverName, } if masquerade != nil && masquerade.RootCA != "" { caCert, err := keyman.LoadCertificateFromPEMBytes([]byte(masquerade.RootCA)) if err != nil { log.Fatalf("Unable to load root ca cert: %s", err) } tlsConfig.RootCAs = caCert.PoolContainingCert() } serverInfo.tlsConfigs[configKey] = tlsConfig } return tlsConfig }
// Dialer creates a *balancer.Dialer backed by a chained server. func (s *ChainedServerInfo) Dialer() (*balancer.Dialer, error) { netd := &net.Dialer{Timeout: chainedDialTimeout} var dial func() (net.Conn, error) if s.Cert == "" { log.Error("No Cert configured for chained server, will dial with plain tcp") dial = func() (net.Conn, error) { return netd.Dial("tcp", s.Addr) } } else { log.Trace("Cert configured for chained server, will dial with tls over tcp") cert, err := keyman.LoadCertificateFromPEMBytes([]byte(s.Cert)) if err != nil { return nil, fmt.Errorf("Unable to parse certificate: %s", err) } x509cert := cert.X509() sessionCache := tls.NewLRUClientSessionCache(1000) dial = func() (net.Conn, error) { conn, err := tlsdialer.DialWithDialer(netd, "tcp", s.Addr, false, &tls.Config{ ClientSessionCache: sessionCache, InsecureSkipVerify: true, }) if err != nil { return nil, err } if !conn.ConnectionState().PeerCertificates[0].Equal(x509cert) { conn.Close() return nil, fmt.Errorf("Server's certificate didn't match expected!") } return conn, err } } ccfg := chained.Config{ DialServer: dial, } if s.AuthToken != "" { ccfg.OnRequest = func(req *http.Request) { req.Header.Set("X-LANTERN-AUTH-TOKEN", s.AuthToken) } } d := chained.NewDialer(ccfg) // Is this a trusted proxy that we could use for HTTP traffic? var trusted string if s.Trusted { trusted = "(trusted) " } return &balancer.Dialer{ Label: fmt.Sprintf("%schained proxy at %s", trusted, s.Addr), Weight: s.Weight, QOS: s.QOS, Trusted: s.Trusted, Dial: func(network, addr string) (net.Conn, error) { return withStats(d.Dial(network, addr)) }, }, nil }
func TestNotOKWithBadRootCert(t *testing.T) { badCert, err := keyman.LoadCertificateFromPEMBytes([]byte(GoogleInternetAuthority)) if err != nil { t.Fatalf("Unable to load GoogleInternetAuthority cert: %s", err) } _, fdc, err := fdcount.Matching("TCP") if err != nil { t.Fatal(err) } conn, err := Dial("tcp", ADDR, true, &tls.Config{ RootCAs: badCert.PoolContainingCert(), }) assert.Error(t, err, "There should have been a problem dialing") if err != nil { assert.Contains(t, err.Error(), CERTIFICATE_ERROR, "Wrong error on dial") } <-receivedServerNames closeAndCountFDs(t, conn, err, fdc) }
// HTTPClient creates an http.Client. If rootCA is specified, the client will // validate the server's certificate on TLS connections against that RootCA. If // proxyAddr is specified, the client will proxy through the given http proxy. func HTTPClient(rootCA string, proxyAddr string) (*http.Client, error) { tr := &http.Transport{} if rootCA != "" { caCert, err := keyman.LoadCertificateFromPEMBytes([]byte(rootCA)) if err != nil { return nil, fmt.Errorf("Unable to decode rootCA: %s", err) } tr.TLSClientConfig = &tls.Config{ RootCAs: caCert.PoolContainingCert(), } } if proxyAddr != "" { tr.Proxy = func(req *http.Request) (*url.URL, error) { noHostSpecified := len(strings.Split(proxyAddr, ":")[0]) == 0 if noHostSpecified { // For addresses of the form ":8080", prepend the loopback IP proxyAddr = "127.0.0.1" + proxyAddr } return url.Parse("http://" + proxyAddr) } } return &http.Client{Transport: tr}, nil }
// httpClient creates an http.Client. If rootCA is specified, the client will // validate the server's certificate on TLS connections against that RootCA. If // proxyAddr is specified, the client will proxy through the given http proxy. func httpClient(rootCA string, proxyAddr string, persistent bool) (*http.Client, error) { log.Debugf("Creating new HTTPClient with proxy: %v", proxyAddr) tr := &http.Transport{ Dial: (&net.Dialer{ Timeout: 60 * time.Second, KeepAlive: 30 * time.Second, }).Dial, TLSHandshakeTimeout: 10 * time.Second, // This method is typically used for creating a one-off HTTP client // that we don't want to keep around for future calls, making // persistent connections a potential source of file descriptor // leaks. Note the name of this variable is misleading -- it would // be clearer to call it DisablePersistentConnections -- i.e. it has // nothing to do with TCP keep alives along the lines of the KeepAlive // variable in net.Dialer. DisableKeepAlives: !persistent, } if rootCA != "" { caCert, err := keyman.LoadCertificateFromPEMBytes([]byte(rootCA)) if err != nil { return nil, fmt.Errorf("Unable to decode rootCA: %s", err) } tr.TLSClientConfig = &tls.Config{ RootCAs: caCert.PoolContainingCert(), } } if proxyAddr != "" { host, _, err := net.SplitHostPort(proxyAddr) if err != nil { return nil, fmt.Errorf("Unable to split host and port for %v: %v", proxyAddr, err) } noHostSpecified := host == "" if noHostSpecified { // For addresses of the form ":8080", prepend the loopback IP host = "127.0.0.1" proxyAddr = host + proxyAddr } if isLoopback(host) { log.Debugf("Waiting for loopback proxy server to came online...") // Waiting for proxy server to came online. err := waitforserver.WaitForServer("tcp", proxyAddr, 60*time.Second) if err != nil { // Instead of finishing here we just log the error and continue, the client // we are going to create will surely fail when used and return errors, // those errors should be handled by the code that depends on such client. log.Errorf("Proxy never came online at %v: %q", proxyAddr, err) } log.Debugf("Connected to proxy on localhost") } else { log.Errorf("Attempting to proxy through server other than loopback %v", host) } tr.Proxy = func(req *http.Request) (*url.URL, error) { return url.Parse("http://" + proxyAddr) } } else { log.Errorf("Using direct http client with no proxyAddr") } return &http.Client{Transport: tr}, nil }
// Dialer creates a *balancer.Dialer backed by a chained server. func (s *ChainedServerInfo) Dialer(deviceID string) (*balancer.Dialer, error) { netd := &net.Dialer{Timeout: chainedDialTimeout} forceProxy := ForceChainedProxyAddr != "" addr := s.Addr if forceProxy { log.Errorf("Forcing proxying to server at %v instead of configured server at %v", ForceChainedProxyAddr, s.Addr) addr = ForceChainedProxyAddr } var dial func() (net.Conn, error) if s.Cert == "" && !forceProxy { log.Error("No Cert configured for chained server, will dial with plain tcp") dial = func() (net.Conn, error) { return netd.Dial("tcp", addr) } } else { log.Trace("Cert configured for chained server, will dial with tls over tcp") cert, err := keyman.LoadCertificateFromPEMBytes([]byte(s.Cert)) if err != nil { return nil, fmt.Errorf("Unable to parse certificate: %s", err) } x509cert := cert.X509() sessionCache := tls.NewLRUClientSessionCache(1000) dial = func() (net.Conn, error) { conn, err := tlsdialer.DialWithDialer(netd, "tcp", addr, false, &tls.Config{ ClientSessionCache: sessionCache, InsecureSkipVerify: true, }) if err != nil { return nil, err } if !forceProxy && !conn.ConnectionState().PeerCertificates[0].Equal(x509cert) { if err := conn.Close(); err != nil { log.Debugf("Error closing chained server connection: %s", err) } return nil, fmt.Errorf("Server's certificate didn't match expected!") } return conn, err } } // Is this a trusted proxy that we could use for HTTP traffic? var trusted string if s.Trusted { trusted = "(trusted) " } label := fmt.Sprintf("%schained proxy at %s", trusted, addr) ccfg := chained.Config{ DialServer: dial, Label: label, } authToken := s.AuthToken if ForceAuthToken != "" { authToken = ForceAuthToken } ccfg.OnRequest = func(req *http.Request) { if authToken != "" { req.Header.Set("X-LANTERN-AUTH-TOKEN", authToken) } req.Header.Set("X-LANTERN-DEVICE-ID", deviceID) } d := chained.NewDialer(ccfg) return &balancer.Dialer{ Label: label, Weight: s.Weight, QOS: s.QOS, Trusted: s.Trusted, Dial: func(network, addr string) (net.Conn, error) { conn, err := d.Dial(network, addr) if err != nil { return conn, err } conn = idletiming.Conn(conn, idleTimeout, func() { log.Debugf("Proxy connection to %s via %s idle for %v, closing", addr, conn.RemoteAddr(), idleTimeout) if err := conn.Close(); err != nil { log.Debugf("Unable to close connection: %v", err) } }) return conn, nil }, AuthToken: authToken, }, nil }