Beispiel #1
0
func (certSuite) TestParseCertificate(c *gc.C) {
	xcert, err := cert.ParseCert(caCertPEM)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing")

	xcert, err = cert.ParseCert(caKeyPEM)
	c.Check(xcert, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, "no certificates found")

	xcert, err = cert.ParseCert("hello")
	c.Check(xcert, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, "no certificates found")
}
Beispiel #2
0
func (certSuite) TestWithNonUTCExpiry(c *gc.C) {
	expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET")
	c.Assert(err, jc.ErrorIsNil)
	certPEM, keyPEM, err := cert.NewCA("foo", expiry)
	xcert, err := cert.ParseCert(certPEM)
	c.Assert(err, jc.ErrorIsNil)
	checkNotAfter(c, xcert, expiry)

	var noHostnames []string
	certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames)
	xcert, err = cert.ParseCert(certPEM)
	c.Assert(err, jc.ErrorIsNil)
	checkNotAfter(c, xcert, expiry)
}
Beispiel #3
0
func mustParseCert(pemData string) *x509.Certificate {
	cert, err := cert.ParseCert(pemData)
	if err != nil {
		panic(err)
	}
	return cert
}
Beispiel #4
0
// Validate ensures that config is a valid configuration.
func Validate(c Config) error {
	if v, ok := c[IdentityURL].(string); ok {
		u, err := url.Parse(v)
		if err != nil {
			return errors.Annotate(err, "invalid identity URL")
		}
		if u.Scheme != "https" {
			return errors.Errorf("URL needs to be https")
		}

	}

	if v, ok := c[IdentityPublicKey].(string); ok {
		var key bakery.PublicKey
		if err := key.UnmarshalText([]byte(v)); err != nil {
			return errors.Annotate(err, "invalid identity public key")
		}
	}

	caCert, caCertOK := c.CACert()
	if !caCertOK {
		return errors.Errorf("missing CA certificate")
	}
	if _, err := cert.ParseCert(caCert); err != nil {
		return errors.Annotate(err, "bad CA certificate in configuration")
	}

	if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) {
		return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid)
	}

	return nil
}
Beispiel #5
0
func (s *RsyslogSuite) TestRsyslogCert(c *gc.C) {
	st, m := s.st, s.machine
	err := s.machine.SetProviderAddresses(network.NewAddress("example.com"))
	c.Assert(err, jc.ErrorIsNil)

	worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"})
	c.Assert(err, jc.ErrorIsNil)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()
	waitForFile(c, filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem"))

	rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem"))
	c.Assert(err, jc.ErrorIsNil)

	cert, err := cert.ParseCert(string(rsyslogCertPEM))
	c.Assert(err, jc.ErrorIsNil)

	c.Assert(cert.DNSNames, gc.DeepEquals, []string{"example.com", "*"})

	subject := cert.Subject
	c.Assert(subject.CommonName, gc.Equals, "*")
	c.Assert(subject.Organization, gc.DeepEquals, []string{"juju"})

	issuer := cert.Issuer
	c.Assert(issuer.CommonName, gc.Equals, "juju-generated CA for environment \"rsyslog\"")
	c.Assert(issuer.Organization, gc.DeepEquals, []string{"juju"})
}
func (s *CertUpdaterSuite) TestStartStop(c *gc.C) {
	var initialAddresses []string
	setter := func(info params.StateServingInfo, dying <-chan struct{}) error {
		// Only care about first time called.
		if len(initialAddresses) > 0 {
			return nil
		}
		srvCert, err := cert.ParseCert(info.Cert)
		c.Assert(err, jc.ErrorIsNil)
		initialAddresses = make([]string, len(srvCert.IPAddresses))
		for i, ip := range srvCert.IPAddresses {
			initialAddresses[i] = ip.String()
		}
		return nil
	}
	changes := make(chan struct{})
	certChangedChan := make(chan params.StateServingInfo)
	worker := certupdater.NewCertificateUpdater(
		&mockMachine{changes}, s, &mockConfigGetter{}, &mockAPIHostGetter{}, setter, certChangedChan,
	)
	worker.Kill()
	c.Assert(worker.Wait(), gc.IsNil)
	// Initial cert addresses initialised to cloud local ones.
	c.Assert(initialAddresses, jc.DeepEquals, []string{"192.168.1.1"})
}
Beispiel #7
0
func (s *MachineSuite) TestCertificateUpdateWorkerUpdatesCertificate(c *gc.C) {
	// Set up the machine agent.
	m, _, _ := s.primeAgent(c, state.JobManageModel)
	a := s.newAgent(c, m)
	a.ReadConfig(names.NewMachineTag(m.Id()).String())

	// Set up check that certificate has been updated.
	updated := make(chan struct{})
	go func() {
		for {
			stateInfo, _ := a.CurrentConfig().StateServingInfo()
			srvCert, err := cert.ParseCert(stateInfo.Cert)
			if !c.Check(err, jc.ErrorIsNil) {
				break
			}
			sanIPs := make([]string, len(srvCert.IPAddresses))
			for i, ip := range srvCert.IPAddresses {
				sanIPs[i] = ip.String()
			}
			if len(sanIPs) == 1 && sanIPs[0] == "0.1.2.3" {
				close(updated)
				break
			}
			time.Sleep(100 * time.Millisecond)
		}
	}()

	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
	s.assertChannelActive(c, updated, "certificate to be updated")
}
Beispiel #8
0
func (s *syslogSuite) createSyslogServer(c *gc.C, received chan rfc5424test.Message, done chan struct{}) string {
	server := rfc5424test.NewServer(rfc5424test.HandlerFunc(func(msg rfc5424test.Message) {
		select {
		case received <- msg:
		case <-done:
		}
	}))
	s.AddCleanup(func(*gc.C) { server.Close() })
	s.AddCleanup(func(*gc.C) { close(done) })

	serverCert, err := tls.X509KeyPair(
		[]byte(coretesting.ServerCert),
		[]byte(coretesting.ServerKey),
	)
	c.Assert(err, jc.ErrorIsNil)
	caCert, err := cert.ParseCert(coretesting.CACert)
	c.Assert(err, jc.ErrorIsNil)
	clientCAs := x509.NewCertPool()
	clientCAs.AddCert(caCert)
	server.TLS = &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientCAs:    clientCAs,
	}
	server.StartTLS()

	// We must use "localhost", as the certificate does not
	// have any IP SANs.
	port := server.Listener.Addr().(*net.TCPAddr).Port
	addr := net.JoinHostPort("localhost", fmt.Sprint(port))
	return addr
}
Beispiel #9
0
// verifyKeyPair verifies that the certificate and key parse correctly.
// The key is optional - if it is provided, we also check that the key
// matches the certificate.
func verifyKeyPair(certb, key string) error {
	if key != "" {
		_, err := tls.X509KeyPair([]byte(certb), []byte(key))
		return err
	}
	_, err := cert.ParseCert(certb)
	return err
}
Beispiel #10
0
func dialWebsocket(c *gc.C, addr, path string) (*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 = &tls.Config{RootCAs: pool}
	return websocket.DialConfig(config)
}
Beispiel #11
0
Datei: open.go Projekt: bac/juju
// 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
}
Beispiel #12
0
// SetRsyslogCert sets the rsyslog CACert.
func (api *RsyslogAPI) SetRsyslogCert(args params.SetRsyslogCertParams) (params.ErrorResult, error) {
	var result params.ErrorResult
	if !api.canModify {
		result.Error = common.ServerError(common.ErrBadCreds)
		return result, nil
	}
	if _, err := cert.ParseCert(string(args.CACert)); err != nil {
		result.Error = common.ServerError(err)
		return result, nil
	}
	attrs := map[string]interface{}{"rsyslog-ca-cert": string(args.CACert)}
	if err := api.st.UpdateEnvironConfig(attrs, nil, nil); err != nil {
		result.Error = common.ServerError(err)
	}
	return result, nil
}
Beispiel #13
0
// updateRequired returns true and a list of merged addresses if any of the
// new addresses are not yet contained in the server cert SAN list.
func updateRequired(serverCert string, newAddrs []string) ([]string, bool, error) {
	x509Cert, err := cert.ParseCert(serverCert)
	if err != nil {
		return nil, false, errors.Annotate(err, "cannot parse existing TLS certificate")
	}
	existingAddr := set.NewStrings()
	for _, ip := range x509Cert.IPAddresses {
		existingAddr.Add(ip.String())
	}
	logger.Debugf("existing cert addresses %v", existingAddr)
	logger.Debugf("new addresses %v", newAddrs)
	// Does newAddr contain any that are not already in existingAddr?
	newAddrSet := set.NewStrings(newAddrs...)
	update := newAddrSet.Difference(existingAddr).Size() > 0
	newAddrSet = newAddrSet.Union(existingAddr)
	return newAddrSet.SortedValues(), update, nil
}
Beispiel #14
0
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)
}
Beispiel #15
0
func (cfg RawConfig) tlsConfig() (*tls.Config, error) {
	clientCert, err := tls.X509KeyPair([]byte(cfg.ClientCert), []byte(cfg.ClientKey))
	if err != nil {
		return nil, errors.Annotate(err, "parsing client key pair")
	}

	caCert, err := cert.ParseCert(cfg.CACert)
	if err != nil {
		return nil, errors.Annotate(err, "parsing CA certificate")
	}
	rootCAs := x509.NewCertPool()
	rootCAs.AddCert(caCert)

	return &tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCAs,
	}, nil
}
Beispiel #16
0
// CreateCertPool creates a new x509.CertPool and adds in the caCert passed
// in.  All certs from the cert directory (/etc/juju/cert.d on ubuntu) are
// also added.
func CreateCertPool(caCert string) (*x509.CertPool, error) {

	pool := x509.NewCertPool()
	if caCert != "" {
		xcert, err := cert.ParseCert(caCert)
		if err != nil {
			return nil, errors.Trace(err)
		}
		pool.AddCert(xcert)
	}

	count := processCertDir(pool)
	if count >= 0 {
		logger.Debugf("added %d certs to the pool from %s", count, certDir)
	}

	return pool, nil
}
Beispiel #17
0
func addDownloadToolsCmds(ser string, certificate string, toolsList tools.List) ([]string, error) {
	var cmds []string
	var getDownloadFileCmd func(url string) string
	if series.IsWindowsNano(ser) {
		parsedCert, err := cert.ParseCert(certificate)
		if err != nil {
			return nil, err
		}
		caCert := base64.URLEncoding.EncodeToString(parsedCert.Raw)
		cmds = []string{fmt.Sprintf(`$cacert = "%s"`, caCert),
			`$cert_bytes = $cacert | %{ ,[System.Text.Encoding]::UTF8.GetBytes($_) }`,
			`$cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2(,$cert_bytes)`,
			`$store = Get-Item Cert:\LocalMachine\AuthRoot`,
			`$store.Open("ReadWrite")`,
			`$store.Add($cert)`,
		}
		getDownloadFileCmd = func(url string) string {
			return fmt.Sprintf(`Invoke-FastWebRequest -URI '%s' -OutFile "$binDir\tools.tar.gz"`, url)
		}
	} else {
		cmds = []string{
			`$WebClient = New-Object System.Net.WebClient`,
			`[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}`,
			`[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12`,
		}
		getDownloadFileCmd = func(url string) string {
			return fmt.Sprintf(`$WebClient.DownloadFile('%s', "$binDir\tools.tar.gz");`, url)
		}
	}

	// Attempt all of the URLs, one after the other, until one succeeds.
	// If all of the URLs fail, we retry the whole lot. We retry in this
	// way, rather than retrying individually, to avoid one permanently
	// bad URL from holding up the download.
	downloadCmds := make([]string, len(toolsList))
	for i, tools := range toolsList {
		downloadCmds[i] = fmt.Sprintf("{ %s }", getDownloadFileCmd(tools.URL))
	}
	downloadCmd := fmt.Sprintf("ExecRetry { TryExecAll @(%s) }", strings.Join(downloadCmds, ", "))
	cmds = append(cmds, downloadCmd)

	return cmds, nil
}
Beispiel #18
0
// Connect establishes a websocket connection to the API server using
// the Info, API path tail and (optional) request headers provided. If
// multiple API addresses are provided in Info they will be tried
// concurrently - the first successful connection wins.
//
// The path tail may be blank, in which case the default value will be
// used. Otherwise, it must start with a "/".
func Connect(info *Info, pathTail string, header http.Header, opts DialOpts) (*websocket.Conn, error) {
	if len(info.Addrs) == 0 {
		return nil, errors.New("no API addresses to connect to")
	}
	if pathTail != "" && !strings.HasPrefix(pathTail, "/") {
		return nil, errors.New(`path tail must start with "/"`)
	}

	pool := x509.NewCertPool()
	xcert, err := cert.ParseCert(info.CACert)
	if err != nil {
		return nil, errors.Annotate(err, "cert pool creation failed")
	}
	pool.AddCert(xcert)

	path := makeAPIPath(info.EnvironTag.Id(), pathTail)

	// Dial all addresses at reasonable intervals.
	try := parallel.NewTry(0, nil)
	defer try.Kill()
	for _, addr := range info.Addrs {
		err := dialWebsocket(addr, path, header, opts, pool, try)
		if err == parallel.ErrStopped {
			break
		}
		if err != nil {
			return nil, errors.Trace(err)
		}
		select {
		case <-time.After(opts.DialAddressInterval):
		case <-try.Dead():
		}
	}
	try.Close()
	result, err := try.Result()
	if err != nil {
		return nil, errors.Trace(err)
	}
	conn := result.(*websocket.Conn)
	logger.Infof("connection established to %q", conn.RemoteAddr())
	return conn, nil
}
Beispiel #19
0
func (s *CertUpdaterSuite) TestAddressChange(c *gc.C) {
	var srvCert *x509.Certificate
	updated := make(chan struct{})
	setter := func(info params.StateServingInfo, dying <-chan struct{}) error {
		s.stateServingInfo = info
		var err error
		srvCert, err = cert.ParseCert(info.Cert)
		c.Assert(err, jc.ErrorIsNil)
		sanIPs := make([]string, len(srvCert.IPAddresses))
		for i, ip := range srvCert.IPAddresses {
			sanIPs[i] = ip.String()
		}
		sanIPsSet := set.NewStrings(sanIPs...)
		if sanIPsSet.Size() == 2 && sanIPsSet.Contains("0.1.2.3") && sanIPsSet.Contains("192.168.1.1") {
			close(updated)
		}
		return nil
	}
	changes := make(chan struct{})
	certChangedChan := make(chan params.StateServingInfo)
	worker := certupdater.NewCertificateUpdater(
		&mockMachine{changes}, s, &mockConfigGetter{}, &mockAPIHostGetter{}, setter, certChangedChan,
	)
	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
	defer worker.Kill()

	changes <- struct{}{}
	// Certificate should be updated with the address value.
	select {
	case <-updated:
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for certificate to be updated")
	}

	// The server certificates must report "juju-apiserver" as a DNS
	// name for backwards-compatibility with API clients. They must
	// also report "juju-mongodb" because these certicates are also
	// used for serving MongoDB connections.
	c.Assert(srvCert.DNSNames, jc.SameContents,
		[]string{"localhost", "juju-apiserver", "juju-mongodb", "anything"})
}
Beispiel #20
0
func (s *MachineSuite) TestCertificateDNSUpdated(c *gc.C) {
	// Disable the certificate work so it doesn't update the certificate.
	newUpdater := func(certupdater.AddressWatcher, certupdater.StateServingInfoGetter, certupdater.ControllerConfigGetter,
		certupdater.APIHostPortsGetter, certupdater.StateServingInfoSetter,
	) worker.Worker {
		return worker.NewNoOpWorker()
	}
	s.PatchValue(&newCertificateUpdater, newUpdater)

	// Set up the machine agent.
	m, _, _ := s.primeAgent(c, state.JobManageModel)
	a := s.newAgent(c, m)

	// Set up check that certificate has been updated when the agent starts.
	updated := make(chan struct{})
	expectedDnsNames := set.NewStrings("local", "juju-apiserver", "juju-mongodb")
	go func() {
		for {
			stateInfo, _ := a.CurrentConfig().StateServingInfo()
			srvCert, err := cert.ParseCert(stateInfo.Cert)
			c.Assert(err, jc.ErrorIsNil)
			certDnsNames := set.NewStrings(srvCert.DNSNames...)
			if !expectedDnsNames.Difference(certDnsNames).IsEmpty() {
				continue
			}
			pemContent, err := ioutil.ReadFile(filepath.Join(s.DataDir(), "server.pem"))
			c.Assert(err, jc.ErrorIsNil)
			if string(pemContent) == stateInfo.Cert+"\n"+stateInfo.PrivateKey {
				close(updated)
				break
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()

	go func() { c.Check(a.Run(nil), jc.ErrorIsNil) }()
	defer func() { c.Check(a.Stop(), jc.ErrorIsNil) }()
	s.assertChannelActive(c, updated, "certificate to be updated")
}
Beispiel #21
0
// 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 := &tls.Config{
		RootCAs:    pool,
		ServerName: "juju-mongodb",
	}
	dial := func(addr net.Addr) (net.Conn, error) {
		c, err := net.Dial("tcp", addr.String())
		if err != nil {
			logger.Debugf("connection failed, will retry: %v", err)
			return nil, err
		}
		cc := tls.Client(c, tlsConfig)
		if err := cc.Handshake(); err != nil {
			logger.Debugf("TLS handshake failed: %v", err)
			return nil, err
		}
		logger.Infof("dialled mongo successfully on address %q", addr)
		return cc, nil
	}

	return &mgo.DialInfo{
		Addrs:   info.Addrs,
		Timeout: opts.Timeout,
		Dial:    dial,
		Direct:  opts.Direct,
	}, nil
}
Beispiel #22
0
// upgradeCertificateDNSNames ensure that the controller certificate
// recorded in the agent config and also mongo server.pem contains the
// DNSNames entires required by Juju/
func (a *MachineAgent) upgradeCertificateDNSNames() error {
	agentConfig := a.CurrentConfig()
	si, ok := agentConfig.StateServingInfo()
	if !ok || si.CAPrivateKey == "" {
		// No certificate information exists yet, nothing to do.
		return nil
	}
	// Parse the current certificate to get the current dns names.
	serverCert, err := cert.ParseCert(si.Cert)
	if err != nil {
		return err
	}
	update := false
	dnsNames := set.NewStrings(serverCert.DNSNames...)
	requiredDNSNames := []string{"local", "juju-apiserver", "juju-mongodb"}
	for _, dnsName := range requiredDNSNames {
		if dnsNames.Contains(dnsName) {
			continue
		}
		dnsNames.Add(dnsName)
		update = true
	}
	if !update {
		return nil
	}
	// Write a new certificate to the mongo pem and agent config files.
	si.Cert, si.PrivateKey, err = cert.NewDefaultServer(agentConfig.CACert(), si.CAPrivateKey, dnsNames.Values())
	if err != nil {
		return err
	}
	if err := mongo.UpdateSSLKey(agentConfig.DataDir(), si.Cert, si.PrivateKey); err != nil {
		return err
	}
	return a.AgentConfigWriter.ChangeConfig(func(config agent.ConfigSetter) error {
		config.SetStateServingInfo(si)
		return nil
	})
}
Beispiel #23
0
// processCertDir iterates through the certDir looking for *.pem files.
// Each pem file is read in turn and added to the pool.  A count of the number
// of successful certificates processed is returned.
func processCertDir(pool *x509.CertPool) (count int) {
	fileInfo, err := os.Stat(certDir)
	if os.IsNotExist(err) {
		logger.Tracef("cert dir %q does not exist", certDir)
		return -1
	}
	if err != nil {
		logger.Infof("unexpected error reading cert dir: %s", err)
		return -1
	}
	if !fileInfo.IsDir() {
		logger.Infof("cert dir %q is not a directory", certDir)
		return -1
	}

	matches, err := filepath.Glob(filepath.Join(certDir, "*.pem"))
	if err != nil {
		logger.Infof("globbing files failed: %s", err)
		return -1
	}

	for _, match := range matches {
		data, err := ioutil.ReadFile(match)
		if err != nil {
			logger.Infof("error reading %q: %v", match, err)
			continue
		}
		certificate, err := cert.ParseCert(string(data))
		if err != nil {
			logger.Infof("error parsing cert %q: %v", match, err)
			continue
		}
		pool.AddCert(certificate)
		count++
	}
	return count
}
Beispiel #24
0
func (s *ConfigSuite) TestGenerateControllerCertAndKey(c *gc.C) {
	// Add a cert.
	s.FakeHomeSuite.Home.AddFiles(c, gitjujutesting.TestFile{".ssh/id_rsa.pub", "rsa\n"})

	for _, test := range []struct {
		caCert    string
		caKey     string
		sanValues []string
	}{{
		caCert: testing.CACert,
		caKey:  testing.CAKey,
	}, {
		caCert:    testing.CACert,
		caKey:     testing.CAKey,
		sanValues: []string{"10.0.0.1", "192.168.1.1"},
	}} {
		certPEM, keyPEM, err := controller.GenerateControllerCertAndKey(test.caCert, test.caKey, test.sanValues)
		c.Assert(err, jc.ErrorIsNil)

		_, _, err = cert.ParseCertAndKey(certPEM, keyPEM)
		c.Check(err, jc.ErrorIsNil)

		err = cert.Verify(certPEM, testing.CACert, time.Now())
		c.Assert(err, jc.ErrorIsNil)
		err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(9, 0, 0))
		c.Assert(err, jc.ErrorIsNil)
		err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(10, 0, 1))
		c.Assert(err, gc.NotNil)
		srvCert, err := cert.ParseCert(certPEM)
		c.Assert(err, jc.ErrorIsNil)
		sanIPs := make([]string, len(srvCert.IPAddresses))
		for i, ip := range srvCert.IPAddresses {
			sanIPs[i] = ip.String()
		}
		c.Assert(sanIPs, jc.SameContents, test.sanValues)
	}
}
Beispiel #25
0
// Validate ensures that config is a valid configuration.
func Validate(c Config) error {
	if v, ok := c[IdentityPublicKey].(string); ok {
		var key bakery.PublicKey
		if err := key.UnmarshalText([]byte(v)); err != nil {
			return errors.Annotate(err, "invalid identity public key")
		}
	}

	if v, ok := c[IdentityURL].(string); ok {
		u, err := url.Parse(v)
		if err != nil {
			return errors.Annotate(err, "invalid identity URL")
		}
		// If we've got an identity public key, we allow an HTTP
		// scheme for the identity server because we won't need
		// to rely on insecure transport to obtain the public
		// key.
		if _, ok := c[IdentityPublicKey]; !ok && u.Scheme != "https" {
			return errors.Errorf("URL needs to be https when %s not provided", IdentityPublicKey)
		}
	}

	caCert, caCertOK := c.CACert()
	if !caCertOK {
		return errors.Errorf("missing CA certificate")
	}
	if _, err := cert.ParseCert(caCert); err != nil {
		return errors.Annotate(err, "bad CA certificate in configuration")
	}

	if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) {
		return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid)
	}

	return nil
}
Beispiel #26
0
func Open(info *Info, opts DialOpts) (*State, error) {
	if len(info.Addrs) == 0 {
		return nil, fmt.Errorf("no API addresses to connect to")
	}
	pool := x509.NewCertPool()
	xcert, err := cert.ParseCert(info.CACert)
	if err != nil {
		return nil, err
	}
	pool.AddCert(xcert)

	var environUUID string
	if info.EnvironTag != nil {
		environUUID = info.EnvironTag.Id()
	}
	// Dial all addresses at reasonable intervals.
	try := parallel.NewTry(0, nil)
	defer try.Kill()
	var addrs []string
	for _, addr := range info.Addrs {
		if strings.HasPrefix(addr, "localhost:") {
			addrs = append(addrs, addr)
			break
		}
	}
	if len(addrs) == 0 {
		addrs = info.Addrs
	}
	for _, addr := range addrs {
		err := dialWebsocket(addr, environUUID, opts, pool, try)
		if err == parallel.ErrStopped {
			break
		}
		if err != nil {
			return nil, err
		}
		select {
		case <-time.After(opts.DialAddressInterval):
		case <-try.Dead():
		}
	}
	try.Close()
	result, err := try.Result()
	if err != nil {
		return nil, err
	}
	conn := result.(*websocket.Conn)
	logger.Infof("connection established to %q", conn.RemoteAddr())

	client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil)
	client.Start()
	st := &State{
		client:     client,
		conn:       conn,
		addr:       conn.Config().Location.Host,
		serverRoot: "https://" + conn.Config().Location.Host,
		// why are the contents of the tag (username and password) written into the
		// state structure BEFORE login ?!?
		tag:      toString(info.Tag),
		password: info.Password,
		certPool: pool,
	}
	if info.Tag != nil || info.Password != "" {
		if err := st.Login(info.Tag.String(), info.Password, info.Nonce); err != nil {
			conn.Close()
			return nil, err
		}
	}
	st.broken = make(chan struct{})
	st.closed = make(chan struct{})
	go st.heartbeatMonitor()
	return st, nil
}