// checkTLSConnection checks that we can correctly perform a TLS // handshake using the given credentials. func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) { clientCertPool := x509.NewCertPool() clientCertPool.AddCert(caCert) var outBytes bytes.Buffer const msg = "hello to the server" p0, p1 := net.Pipe() p0 = &recordingConn{ Conn: p0, Writer: io.MultiWriter(p0, &outBytes), } var clientState tls.ConnectionState done := make(chan error) go func() { config := utils.SecureTLSConfig() config.Certificates = []tls.Certificate{{ Certificate: [][]byte{srvCert.Raw}, PrivateKey: srvKey, }} conn := tls.Server(p1, config) defer conn.Close() data, err := ioutil.ReadAll(conn) c.Assert(err, jc.ErrorIsNil) c.Assert(string(data), gc.Equals, msg) close(done) }() tlsConfig := utils.SecureTLSConfig() tlsConfig.ServerName = "anyServer" tlsConfig.RootCAs = clientCertPool clientConn := tls.Client(p0, tlsConfig) defer clientConn.Close() _, err := clientConn.Write([]byte(msg)) c.Assert(err, jc.ErrorIsNil) clientState = clientConn.ConnectionState() clientConn.Close() // wait for server to exit <-done outData := outBytes.String() c.Assert(outData, gc.Not(gc.HasLen), 0) if strings.Index(outData, msg) != -1 { c.Fatalf("TLS connection not encrypted") } c.Assert(clientState.VerifiedChains, gc.HasLen, 1) c.Assert(clientState.VerifiedChains[0], gc.HasLen, 2) return clientState.VerifiedChains[0][1].Subject.CommonName }
func (cl *changeCertListener) tlsConfig() *tls.Config { cl.m.Lock() defer cl.m.Unlock() tlsConfig := utils.SecureTLSConfig() tlsConfig.Certificates = []tls.Certificate{cl.cert} return tlsConfig }
// connectWebsocket establishes a websocket connection to the RPC // API websocket on the API server using Info. If multiple API addresses // are provided in Info they will be tried concurrently - the first successful // connection wins. // // It also returns the TLS configuration that it has derived from the Info. func connectWebsocket(info *Info, opts DialOpts) (*websocket.Conn, *tls.Config, error) { if len(info.Addrs) == 0 { return nil, nil, errors.New("no API addresses to connect to") } tlsConfig := utils.SecureTLSConfig() // We want to be specific here (rather than just using "anything". // See commit 7fc118f015d8480dfad7831788e4b8c0432205e8 (PR 899). tlsConfig.ServerName = "juju-apiserver" tlsConfig.InsecureSkipVerify = opts.InsecureSkipVerify if !tlsConfig.InsecureSkipVerify { certPool, err := CreateCertPool(info.CACert) if err != nil { return nil, nil, errors.Annotate(err, "cert pool creation failed") } tlsConfig.RootCAs = certPool } path := "/" if info.ModelTag.Id() != "" { path = apiPath(info.ModelTag, "/api") } conn, err := dialWebSocket(info.Addrs, path, tlsConfig, opts) if err != nil { return nil, nil, errors.Trace(err) } logger.Infof("connection established to %q", conn.RemoteAddr()) return conn, tlsConfig, nil }
func newServer(s *state.State, lis *net.TCPListener, cfg ServerConfig) (_ *Server, err error) { tlsCert, err := tls.X509KeyPair(cfg.Cert, cfg.Key) if err != nil { return nil, err } // TODO(rog) check that *srvRoot is a valid type for using // as an RPC server. tlsConfig := utils.SecureTLSConfig() tlsConfig.Certificates = []tls.Certificate{tlsCert} stPool := cfg.StatePool if stPool == nil { stPool = state.NewStatePool(s) } srv := &Server{ state: s, statePool: stPool, lis: newChangeCertListener(lis, cfg.CertChanged, tlsConfig), tag: cfg.Tag, dataDir: cfg.DataDir, logDir: cfg.LogDir, limiter: utils.NewLimiter(loginRateLimit), validator: cfg.Validator, adminApiFactories: map[int]adminApiFactory{ 3: newAdminApiV3, }, } srv.authCtxt, err = newAuthContext(s) if err != nil { return nil, errors.Trace(err) } go srv.run() return srv, nil }
func (s *authHttpSuite) makeWebsocketConfigFromURL(c *gc.C, server string, header http.Header) *websocket.Config { config, err := websocket.NewConfig(server, "http://localhost/") c.Assert(err, jc.ErrorIsNil) config.Header = header caCerts := x509.NewCertPool() c.Assert(caCerts.AppendCertsFromPEM([]byte(testing.CACert)), jc.IsTrue) config.TlsConfig = utils.SecureTLSConfig() config.TlsConfig.RootCAs = caCerts config.TlsConfig.ServerName = "anything" return config }
// DialInfo returns information on how to dial // the state's mongo server with the given info // and dial options. func DialInfo(info Info, opts DialOpts) (*mgo.DialInfo, error) { if len(info.Addrs) == 0 { return nil, stderrors.New("no mongo addresses") } if len(info.CACert) == 0 { return nil, stderrors.New("missing CA certificate") } xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, fmt.Errorf("cannot parse CA certificate: %v", err) } pool := x509.NewCertPool() pool.AddCert(xcert) tlsConfig := utils.SecureTLSConfig() // TODO(natefinch): revisit this when are full-time on mongo 3. // We have to add non-ECDHE suites because mongo doesn't support ECDHE. moreSuites := []uint16{ tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, } tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, moreSuites...) tlsConfig.RootCAs = pool tlsConfig.ServerName = "juju-mongodb" dial := func(server *mgo.ServerAddr) (net.Conn, error) { addr := server.TCPAddr().String() c, err := net.DialTimeout("tcp", addr, opts.Timeout) if err != nil { logger.Warningf("mongodb connection failed, will retry: %v", err) return nil, err } cc := tls.Client(c, tlsConfig) if err := cc.Handshake(); err != nil { logger.Warningf("TLS handshake failed: %v", err) return nil, err } logger.Debugf("dialled mongodb server at %q", addr) return cc, nil } return &mgo.DialInfo{ Addrs: info.Addrs, Timeout: opts.Timeout, DialServer: dial, Direct: opts.Direct, }, nil }
func dialWebsocket(c *gc.C, addr, path string, tlsVersion uint16) (*websocket.Conn, error) { origin := "http://localhost/" url := fmt.Sprintf("wss://%s%s", addr, path) config, err := websocket.NewConfig(url, origin) c.Assert(err, jc.ErrorIsNil) pool := x509.NewCertPool() xcert, err := cert.ParseCert(coretesting.CACert) c.Assert(err, jc.ErrorIsNil) pool.AddCert(xcert) config.TlsConfig = utils.SecureTLSConfig() if tlsVersion > 0 { // This is for testing only. Please don't muck with the maxtlsversion in // production. config.TlsConfig.MaxVersion = tlsVersion } config.TlsConfig.RootCAs = pool return websocket.DialConfig(config) }
// bakeryDo provides a function suitable for using in httpRequestParams.Do // that will use the given http client (or utils.GetNonValidatingHTTPClient() // if client is nil) and use the given getBakeryError function // to translate errors in responses. func bakeryDo(client *http.Client, getBakeryError func(*http.Response) error) func(*http.Request) (*http.Response, error) { bclient := httpbakery.NewClient() if client != nil { bclient.Client = client } else { // Configure the default client to skip verification/ tlsConfig := utils.SecureTLSConfig() tlsConfig.InsecureSkipVerify = true bclient.Client.Transport = utils.NewHttpTLSTransport(tlsConfig) } return func(req *http.Request) (*http.Response, error) { var body io.ReadSeeker if req.Body != nil { body = req.Body.(io.ReadSeeker) req.Body = nil } return bclient.DoWithBodyAndCustomError(req, body, getBakeryError) } }
// dialAPI establishes a websocket connection to the RPC // API websocket on the API server using Info. If multiple API addresses // are provided in Info they will be tried concurrently - the first successful // connection wins. // // It also returns the TLS configuration that it has derived from the Info. func dialAPI(info *Info, opts DialOpts) (*websocket.Conn, *tls.Config, error) { // Set opts.DialWebsocket here rather than in open because // some tests call dialAPI directly. if opts.DialWebsocket == nil { opts.DialWebsocket = websocket.DialConfig } if len(info.Addrs) == 0 { return nil, nil, errors.New("no API addresses to connect to") } tlsConfig := utils.SecureTLSConfig() tlsConfig.InsecureSkipVerify = opts.InsecureSkipVerify if info.CACert != "" { // We want to be specific here (rather than just using "anything". // See commit 7fc118f015d8480dfad7831788e4b8c0432205e8 (PR 899). tlsConfig.ServerName = "juju-apiserver" certPool, err := CreateCertPool(info.CACert) if err != nil { return nil, nil, errors.Annotate(err, "cert pool creation failed") } tlsConfig.RootCAs = certPool } else { // No CA certificate so use the SNI host name for all // connections (if SNIHostName is empty, the host // name in the address will be used as usual). tlsConfig.ServerName = info.SNIHostName } path, err := apiPath(info.ModelTag, "/api") if err != nil { return nil, nil, errors.Trace(err) } conn, err := dialWebsocketMulti(info.Addrs, path, tlsConfig, opts) if err != nil { return nil, nil, errors.Trace(err) } logger.Infof("connection established to %q", conn.RemoteAddr()) return conn, tlsConfig, nil }
func (srv *Server) newTLSConfig(cfg ServerConfig) *tls.Config { tlsConfig := utils.SecureTLSConfig() if cfg.AutocertDNSName == "" { // No official DNS name, no certificate. tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { cert, _ := srv.localCertificate(clientHello.ServerName) return cert, nil } return tlsConfig } m := autocert.Manager{ Prompt: autocert.AcceptTOS, Cache: srv.state.AutocertCache(), HostPolicy: autocert.HostWhitelist(cfg.AutocertDNSName), } if cfg.AutocertURL != "" { m.Client = &acme.Client{ DirectoryURL: cfg.AutocertURL, } } tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { logger.Infof("getting certificate for server name %q", clientHello.ServerName) // Get the locally created certificate and whether it's appropriate // for the SNI name. If not, we'll try to get an acme cert and // fall back to the local certificate if that fails. cert, shouldUse := srv.localCertificate(clientHello.ServerName) if shouldUse { return cert, nil } acmeCert, err := m.GetCertificate(clientHello) if err == nil { return acmeCert, nil } logger.Errorf("cannot get autocert certificate for %q: %v", clientHello.ServerName, err) return cert, nil } return tlsConfig }
// NewAPIServer serves RPC methods on a localhost HTTP server. // When a connection is made to the API, the newRoot function // is called with the requested model UUID and the returned // value defines the API (see the juju/rpc package). // // Note that the root value accepts any facade version number - it // is not currently possible to use this to serve several different // facade versions. // // The server uses testing.ServerCert and testing.ServerKey // to host the server. // // The returned server must be closed after use. func NewAPIServer(newRoot func(modelUUID string) interface{}) *Server { tlsCert, err := tls.X509KeyPair([]byte(testing.ServerCert), []byte(testing.ServerKey)) if err != nil { panic("bad key pair") } srv := &Server{ newRoot: newRoot, } pmux := pat.New() pmux.Get("/model/:modeluuid/api", http.HandlerFunc(srv.serveAPI)) srv.Server = httptest.NewUnstartedServer(pmux) tlsConfig := utils.SecureTLSConfig() tlsConfig.Certificates = []tls.Certificate{tlsCert} srv.Server.TLS = tlsConfig srv.StartTLS() u, _ := url.Parse(srv.URL) srv.Addrs = []string{u.Host} return srv }