Example #1
0
func (c *CertificateUpdater) updateCertificate(addresses []network.Address, done <-chan struct{}) error {
	logger.Debugf("new machine addresses: %#v", addresses)
	c.addresses = addresses

	// Older Juju deployments will not have the CA cert private key
	// available.
	stateInfo, ok := c.getter.StateServingInfo()
	if !ok {
		return errors.New("no state serving info, cannot regenerate server certificate")
	}
	caPrivateKey := stateInfo.CAPrivateKey
	if caPrivateKey == "" {
		logger.Errorf("no CA cert private key, cannot regenerate server certificate")
		return nil
	}

	cfg, err := c.configGetter.ControllerConfig()
	if err != nil {
		return errors.Annotate(err, "cannot read controller config")
	}

	// For backwards compatibility, we must include "anything", "juju-apiserver"
	// and "juju-mongodb" as hostnames as that is what clients specify
	// as the hostname for verification (this certicate is used both
	// for serving MongoDB and API server connections).  We also
	// explicitly include localhost.
	serverAddrs := []string{"localhost", "juju-apiserver", "juju-mongodb", "anything"}
	for _, addr := range addresses {
		if addr.Value == "localhost" {
			continue
		}
		serverAddrs = append(serverAddrs, addr.Value)
	}
	newServerAddrs, update, err := updateRequired(stateInfo.Cert, serverAddrs)
	if err != nil {
		return errors.Annotate(err, "cannot determine if cert update needed")
	}
	if !update {
		logger.Debugf("no certificate update required")
		return nil
	}

	// Generate a new controller certificate with the machine addresses in the SAN value.
	caCert, hasCACert := cfg.CACert()
	if !hasCACert {
		return errors.New("configuration has no ca-cert")
	}
	newCert, newKey, err := controller.GenerateControllerCertAndKey(caCert, caPrivateKey, newServerAddrs)
	if err != nil {
		return errors.Annotate(err, "cannot generate controller certificate")
	}
	stateInfo.Cert = string(newCert)
	stateInfo.PrivateKey = string(newKey)
	err = c.setter(stateInfo, done)
	if err != nil {
		return errors.Annotate(err, "cannot write agent config")
	}
	logger.Infof("controller cerificate addresses updated to %q", newServerAddrs)
	return nil
}
Example #2
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)
	}
}
Example #3
0
func finalizeInstanceBootstrapConfig(
	ctx environs.BootstrapContext,
	icfg *instancecfg.InstanceConfig,
	args BootstrapParams,
	cfg *config.Config,
	customImageMetadata []*imagemetadata.ImageMetadata,
) error {
	if icfg.APIInfo != nil || icfg.Controller.MongoInfo != nil {
		return errors.New("machine configuration already has api/state info")
	}
	controllerCfg := icfg.Controller.Config
	caCert, hasCACert := controllerCfg.CACert()
	if !hasCACert {
		return errors.New("controller configuration has no ca-cert")
	}
	icfg.APIInfo = &api.Info{
		Password: args.AdminSecret,
		CACert:   caCert,
		ModelTag: names.NewModelTag(cfg.UUID()),
	}
	icfg.Controller.MongoInfo = &mongo.MongoInfo{
		Password: args.AdminSecret,
		Info:     mongo.Info{CACert: caCert},
	}

	// These really are directly relevant to running a controller.
	// Initially, generate a controller certificate with no host IP
	// addresses in the SAN field. Once the controller is up and the
	// NIC addresses become known, the certificate can be regenerated.
	cert, key, err := controller.GenerateControllerCertAndKey(caCert, args.CAPrivateKey, nil)
	if err != nil {
		return errors.Annotate(err, "cannot generate controller certificate")
	}
	icfg.Bootstrap.StateServingInfo = params.StateServingInfo{
		StatePort:    controllerCfg.StatePort(),
		APIPort:      controllerCfg.APIPort(),
		Cert:         string(cert),
		PrivateKey:   string(key),
		CAPrivateKey: args.CAPrivateKey,
	}
	if _, ok := cfg.AgentVersion(); !ok {
		return errors.New("controller model configuration has no agent-version")
	}

	icfg.Bootstrap.ControllerModelConfig = cfg
	icfg.Bootstrap.CustomImageMetadata = customImageMetadata
	icfg.Bootstrap.ControllerCloudName = args.CloudName
	icfg.Bootstrap.ControllerCloud = args.Cloud
	icfg.Bootstrap.ControllerCloudRegion = args.CloudRegion
	icfg.Bootstrap.ControllerCloudCredential = args.CloudCredential
	icfg.Bootstrap.ControllerCloudCredentialName = args.CloudCredentialName
	icfg.Bootstrap.ControllerConfig = args.ControllerConfig
	icfg.Bootstrap.ControllerInheritedConfig = args.ControllerInheritedConfig
	icfg.Bootstrap.RegionInheritedConfig = args.Cloud.RegionConfig
	icfg.Bootstrap.HostedModelConfig = args.HostedModelConfig
	icfg.Bootstrap.Timeout = args.DialOpts.Timeout
	icfg.Bootstrap.GUI = guiArchive(args.GUIDataSourceBaseURL, func(msg string) {
		ctx.Infof(msg)
	})
	return nil
}