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 }
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) } }
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 }