func (s *UserSuite) TestPasswordValidUpdatesSalt(c *gc.C) { user := s.Factory.MakeUser(c, nil) compatHash := utils.UserPasswordHash("foo", utils.CompatSalt) err := user.SetPasswordHash(compatHash, "") c.Assert(err, jc.ErrorIsNil) beforeSalt, beforeHash := state.GetUserPasswordSaltAndHash(user) c.Assert(beforeSalt, gc.Equals, "") c.Assert(beforeHash, gc.Equals, compatHash) c.Assert(user.PasswordValid("bar"), jc.IsFalse) // A bad password doesn't trigger a rewrite afterBadSalt, afterBadHash := state.GetUserPasswordSaltAndHash(user) c.Assert(afterBadSalt, gc.Equals, "") c.Assert(afterBadHash, gc.Equals, compatHash) // When we get a valid check, we then add a salt and rewrite the hash c.Assert(user.PasswordValid("foo"), jc.IsTrue) afterSalt, afterHash := state.GetUserPasswordSaltAndHash(user) c.Assert(afterSalt, gc.Not(gc.Equals), "") c.Assert(afterHash, gc.Not(gc.Equals), compatHash) c.Assert(afterHash, gc.Equals, utils.UserPasswordHash("foo", afterSalt)) // running PasswordValid again doesn't trigger another rewrite c.Assert(user.PasswordValid("foo"), jc.IsTrue) lastSalt, lastHash := state.GetUserPasswordSaltAndHash(user) c.Assert(lastSalt, gc.Equals, afterSalt) c.Assert(lastHash, gc.Equals, afterHash) }
// PasswordValid returns whether the given password // is valid for the user. func (u *User) PasswordValid(password string) bool { // If the user is deactivated, no point in carrying on if u.IsDeactivated() { return false } // Since these are potentially set by a User, we intentionally use the // slower pbkdf2 style hashing. Also, we don't expect to have thousands // of Users trying to log in at the same time (which we *do* expect of // Unit and Machine agents.) if u.doc.PasswordSalt != "" { return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash } // In Juju 1.16 and older, we did not set a Salt for the user password, // so check if the password hash matches using CompatSalt. if it // does, then set the password again so that we get a proper salt if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { // This will set a new Salt for the password. We ignore if it // fails because we will try again at the next request logger.Debugf("User %s logged in with CompatSalt resetting password for new salt", u.Name()) err := u.SetPassword(password) if err != nil { logger.Errorf("Cannot set resalted password for user %q", u.Name()) } return true } return false }
// PasswordValid returns whether the given password is valid for the User. func (u *User) PasswordValid(password string) bool { // If the User is deactivated, no point in carrying on. Since any // authentication checks are done very soon after the user is read // from the database, there is a very small timeframe where an user // could be disabled after it has been read but prior to being checked, // but in practice, this isn't a problem. if u.IsDisabled() { return false } if u.doc.PasswordSalt != "" { return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash } // In Juju 1.16 and older, we did not set a Salt for the user password, // so check if the password hash matches using CompatSalt. if it // does, then set the password again so that we get a proper salt if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { // This will set a new Salt for the password. We ignore if it // fails because we will try again at the next request logger.Debugf("User %s logged in with CompatSalt resetting password for new salt", u.Name()) err := u.SetPassword(password) if err != nil { logger.Errorf("Cannot set resalted password for user %q", u.Name()) } return true } return false }
func (s *UserSuite) TestSetPasswordHashWithSalt(c *gc.C) { user := s.Factory.MakeUser(c, nil) err := user.SetPasswordHash(utils.UserPasswordHash("foo", "salted"), "salted") c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid("foo"), jc.IsTrue) salt, hash := state.GetUserPasswordSaltAndHash(user) c.Assert(salt, gc.Equals, "salted") c.Assert(hash, gc.Not(gc.Equals), utils.UserPasswordHash("foo", utils.CompatSalt)) }
// SetPassword sets the password associated with the user. func (u *User) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) }
// SetPassword sets the password associated with the Identity. func (i *Identity) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return i.setPasswordHash(utils.UserPasswordHash(password, salt), salt) }
// AddUser adds a user to the state. func (st *State) AddUser(username, displayName, password, creator string) (*User, error) { if !names.IsValidUser(username) { return nil, errors.Errorf("invalid user name %q", username) } salt, err := utils.RandomSalt() if err != nil { return nil, err } timestamp := time.Now().Round(time.Second).UTC() u := &User{ st: st, doc: userDoc{ Name: username, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: timestamp, }, } ops := []txn.Op{{ C: usersC, Id: username, Assert: txn.DocMissing, Insert: &u.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("user already exists") } if err != nil { return nil, errors.Trace(err) } return u, nil }
// resetAdminPasswordAndFetchDBNames logs into the database with a // plausible password and returns all the database's db names. We need // to try several passwords because we don't know what state the mongo // server is in when Reset is called. If the test has set a custom // password, we're out of luck, but if they are using // DefaultStatePassword, we can succeed. func resetAdminPasswordAndFetchDBNames(session *mgo.Session) ([]string, bool, error) { // First try with no password dbnames, err := session.DatabaseNames() if err == nil { return dbnames, true, nil } if !isUnauthorized(err) { return nil, false, errors.Trace(err) } // Then try the two most likely passwords in turn. for _, password := range []string{ DefaultMongoPassword, utils.UserPasswordHash(DefaultMongoPassword, utils.CompatSalt), } { admin := session.DB("admin") if err := admin.Login("admin", password); err != nil { logger.Errorf("failed to log in with password %q", password) continue } dbnames, err := session.DatabaseNames() if err == nil { if err := admin.RemoveUser("admin"); err != nil { return nil, false, errors.Trace(err) } return dbnames, true, nil } if !isUnauthorized(err) { return nil, false, errors.Trace(err) } logger.Infof("unauthorized access when getting database names; password %q", password) } return nil, false, errors.Trace(err) }
func createInitialUserOps(controllerUUID string, user names.UserTag, password, salt string, dateCreated time.Time) []txn.Op { nameToLower := strings.ToLower(user.Name()) doc := userDoc{ DocID: nameToLower, Name: user.Name(), DisplayName: user.Name(), PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: user.Name(), DateCreated: dateCreated, } ops := []txn.Op{{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &doc, }} controllerUserOps := createControllerUserOps(controllerUUID, names.NewUserTag(user.Name()), names.NewUserTag(user.Name()), user.Name(), dateCreated, // first user is controller admin. permission.SuperuserAccess) ops = append(ops, controllerUserOps...) return ops }
func (s *UserSuite) TestAddUserSetsSalt(c *gc.C) { user := s.Factory.MakeUser(c, &factory.UserParams{Password: "******"}) salt, hash := state.GetUserPasswordSaltAndHash(user) c.Assert(hash, gc.Not(gc.Equals), "") c.Assert(salt, gc.Not(gc.Equals), "") c.Assert(utils.UserPasswordHash("a-password", salt), gc.Equals, hash) c.Assert(user.PasswordValid("a-password"), jc.IsTrue) }
// FinishMachineConfig sets fields on a MachineConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) { defer errors.Maskf(&err, "cannot complete machine configuration") if err := PopulateMachineConfig( mcfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.PreferIPv6(), ); err != nil { return err } // The following settings are only appropriate at bootstrap time. At the // moment, the only state server is the bootstrap node, but this // will probably change. if !mcfg.Bootstrap { return nil } if mcfg.APIInfo != nil || mcfg.MongoInfo != nil { return fmt.Errorf("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return fmt.Errorf("environment configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return fmt.Errorf("environment configuration has no admin-secret") } passwordHash := utils.UserPasswordHash(password, utils.CompatSalt) mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert} mcfg.MongoInfo = &authentication.MongoInfo{Password: passwordHash, Info: mongo.Info{CACert: caCert}} // These really are directly relevant to running a state server. cert, key, err := cfg.GenerateStateServerCertAndKey() if err != nil { return errors.Annotate(err, "cannot generate state server certificate") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), SystemIdentity: mcfg.SystemPrivateSSHKey, } mcfg.StateServingInfo = &srvInfo mcfg.Constraints = cons if mcfg.Config, err = BootstrapConfig(cfg); err != nil { return err } return nil }
func (*passwordSuite) TestUserPasswordHash(c *gc.C) { seenHashes := make(map[string]bool) for i, password := range testPasswords { for j, salt := range testSalts { c.Logf("test %d, %d %s %s", i, j, password, salt) hashed := utils.UserPasswordHash(password, salt) c.Logf("hash %q", hashed) c.Assert(len(hashed), gc.Equals, 24) c.Assert(seenHashes[hashed], gc.Equals, false) // check we're not adding base64 padding. c.Assert(hashed, gc.Matches, base64Chars) seenHashes[hashed] = true // check it's deterministic altHashed := utils.UserPasswordHash(password, salt) c.Assert(altHashed, gc.Equals, hashed) } } }
// SetPassword sets the password associated with the User. func (u *User) SetPassword(password string) error { if err := u.ensureNotDeleted(); err != nil { return errors.Annotate(err, "cannot set password") } salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) }
// NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, _, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } err = environs.CheckEnvironment(environ) if err != nil { return nil, err } info.Password = password opts := mongo.DefaultDialOpts() st, err := state.Open(info, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(err) { logger.Infof("authorization error while connecting to state server; retrying") // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil }
// PasswordValid returns whether the given password is valid for the Identity. func (i *Identity) PasswordValid(password string) bool { // If the Identity is deactivated, no point in carrying on. Since any // authentication checks are done very soon after the identity is read // from the database, there is a very small timeframe where an identity // could be disabled after it has been read but prior to being checked, // but in practice, this isn't a problem. if i.IsDeactivated() { return false } pwHash := utils.UserPasswordHash(password, i.doc.PasswordSalt) return pwHash == i.doc.PasswordHash }
func (st *State) addUser(name, displayName, password, creator string, secretKey []byte) (*User, error) { if !names.IsValidUserName(name) { return nil, errors.Errorf("invalid user name %q", name) } nameToLower := strings.ToLower(name) dateCreated := st.NowToTheSecond() user := &User{ st: st, doc: userDoc{ DocID: nameToLower, Name: name, DisplayName: displayName, SecretKey: secretKey, CreatedBy: creator, DateCreated: dateCreated, }, } if password != "" { salt, err := utils.RandomSalt() if err != nil { return nil, err } user.doc.PasswordHash = utils.UserPasswordHash(password, salt) user.doc.PasswordSalt = salt } ops := []txn.Op{{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &user.doc, }} controllerUserOps := createControllerUserOps(st.ControllerUUID(), names.NewUserTag(name), names.NewUserTag(creator), displayName, dateCreated, defaultControllerPermission) ops = append(ops, controllerUserOps...) err := st.runTransaction(ops) if err == txn.ErrAborted { err = errors.AlreadyExistsf("user") } if err != nil { return nil, errors.Trace(err) } return user, nil }
// PasswordValid returns whether the given password is valid for the User. The // caller should call user.Refresh before calling this. func (u *User) PasswordValid(password string) bool { // If the User is deactivated or deleted, there is no point in carrying on. // Since any authentication checks are done very soon after the user is // read from the database, there is a very small timeframe where an user // could be disabled after it has been read but prior to being checked, but // in practice, this isn't a problem. if u.IsDisabled() || u.IsDeleted() { return false } if u.doc.PasswordSalt != "" { return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash } return false }
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ DataDir: dataDir, Tag: "machine-0", UpgradedToVersion: version.Current.Number, StateAddresses: []string{gitjujutesting.MgoServer.Addr()}, CACert: testing.CACert, Password: pwHash, } cfg, err := agent.NewAgentConfig(configParams) c.Assert(err, gc.IsNil) cfg.SetStateServingInfo(params.StateServingInfo{ APIPort: 5555, StatePort: gitjujutesting.MgoServer.Port(), Cert: "foo", PrivateKey: "bar", SharedSecret: "baz", SystemIdentity: "qux", }) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) st, _, err := agent.InitializeState(cfg, envCfg, mcfg, mongo.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) err = st.SetAdminMongoPassword("") c.Check(err, gc.IsNil) st.Close() st, _, err = agent.InitializeState(cfg, envCfg, mcfg, mongo.DialOpts{}, environs.NewStatePolicy()) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "failed to initialize state: cannot create log collection: unauthorized mongo access: unauthorized") }
func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) configParams := agent.AgentConfigParams{ Paths: agent.Paths{DataDir: dataDir}, Tag: names.NewMachineTag("0"), UpgradedToVersion: version.Current.Number, StateAddresses: []string{s.mgoInst.Addr()}, CACert: testing.CACert, Password: pwHash, Environment: testing.EnvironmentTag, } cfg, err := agent.NewAgentConfig(configParams) c.Assert(err, jc.ErrorIsNil) cfg.SetStateServingInfo(params.StateServingInfo{ APIPort: 5555, StatePort: s.mgoInst.Port(), Cert: "foo", PrivateKey: "bar", SharedSecret: "baz", SystemIdentity: "qux", }) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []multiwatcher.MachineJob{multiwatcher.JobManageEnviron}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), "state-id": "1", // needed so policy can Open config }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, jc.ErrorIsNil) adminUser := names.NewLocalUserTag("agent-admin") st, _, err := agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, jc.ErrorIsNil) st.Close() st, _, err = agent.InitializeState(adminUser, cfg, envCfg, mcfg, mongo.DefaultDialOpts(), environs.NewStatePolicy()) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*") }
func (s *UserSuite) TestSetPasswordHash(c *gc.C) { user := s.Factory.MakeUser(c, nil) err := user.SetPasswordHash(utils.UserPasswordHash("foo", utils.CompatSalt), utils.CompatSalt) c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid("foo"), jc.IsTrue) c.Assert(user.PasswordValid("bar"), jc.IsFalse) // User passwords should *not* use the fast PasswordHash function hash := utils.AgentPasswordHash("foo-12345678901234567890") c.Assert(err, jc.ErrorIsNil) err = user.SetPasswordHash(hash, "") c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid("foo-12345678901234567890"), jc.IsFalse) }
func createInitialUserOp(st *State, user names.UserTag, password, salt string) txn.Op { nameToLower := strings.ToLower(user.Name()) doc := userDoc{ DocID: nameToLower, Name: user.Name(), DisplayName: user.Name(), PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: user.Name(), DateCreated: nowToTheSecond(), } return txn.Op{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &doc, } }
// PasswordValid returns whether the given password is valid // for the given machine. func (m *Machine) PasswordValid(password string) bool { agentHash := utils.AgentPasswordHash(password) if agentHash == m.doc.PasswordHash { return true } // In Juju 1.16 and older we used the slower password hash for unit // agents. So check to see if the supplied password matches the old // path, and if so, update it to the new mechanism. // We ignore any error in setting the password, as we'll just try again // next time if utils.UserPasswordHash(password, utils.CompatSalt) == m.doc.PasswordHash { logger.Debugf("%s logged in with old password hash, changing to AgentPasswordHash", m.Tag()) m.setPasswordHash(agentHash) return true } return false }
func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "admin-secret": "lisboan-pork", "agent-version": "1.2.3", "controller": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, jc.ErrorIsNil) oldAttrs := cfg.AllAttrs() icfg := &instancecfg.InstanceConfig{ Bootstrap: true, } err = instancecfg.FinishInstanceConfig(icfg, cfg) c.Assert(err, jc.ErrorIsNil) c.Check(icfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") c.Check(icfg.DisableSSLHostnameVerification, jc.IsFalse) password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) c.Check(icfg.APIInfo, gc.DeepEquals, &api.Info{ Password: password, CACert: testing.CACert, ModelTag: testing.ModelTag, }) c.Check(icfg.MongoInfo, gc.DeepEquals, &mongo.MongoInfo{ Password: password, Info: mongo.Info{CACert: testing.CACert}, }) c.Check(icfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) c.Check(icfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) c.Check(icfg.StateServingInfo.CAPrivateKey, gc.Equals, oldAttrs["ca-private-key"]) oldAttrs["ca-private-key"] = "" oldAttrs["admin-secret"] = "" c.Check(icfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) srvCertPEM := icfg.StateServingInfo.Cert srvKeyPEM := icfg.StateServingInfo.PrivateKey _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Check(err, jc.ErrorIsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) c.Assert(err, jc.ErrorIsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) c.Assert(err, jc.ErrorIsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) }
func (*NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) { attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "admin-secret": "side-effect secret", "secret": "pork", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, environs.BootstrapParams{}) c.Assert(err, gc.IsNil) info, _, err := env.StateInfo() c.Assert(err, gc.IsNil) info.Password = utils.UserPasswordHash("side-effect secret", utils.CompatSalt) // Use a state without a nil policy, which will allow us to set an invalid config. st, err := state.Open(info, mongo.DefaultDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer assertClose(c, st) // Verify we have secrets in the environ config already. statecfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") // Remove the secret from state, and then make sure it gets // pushed back again. err = st.UpdateEnvironConfig(map[string]interface{}{}, []string{"secret"}, nil) c.Assert(err, gc.IsNil) // Make a new Conn, which will push the secrets. conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) defer assertClose(c, conn) statecfg, err = conn.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") // Reset the admin password so the state db can be reused. err = conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) }
func (*NewConnSuite) TestConnWithPassword(c *gc.C) { attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "admin-secret": "nutkin", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, environs.BootstrapParams{}) c.Assert(err, gc.IsNil) // Check that Bootstrap has correctly used a hash // of the admin password. info, _, err := env.StateInfo() c.Assert(err, gc.IsNil) info.Password = utils.UserPasswordHash("nutkin", utils.CompatSalt) st, err := state.Open(info, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) assertClose(c, st) // Check that we can connect with the original environment. conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) assertClose(c, conn) // Check that the password has now been changed to the original // admin password. info.Password = "******" st1, err := state.Open(info, mongo.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) assertClose(c, st1) // Check that we can still connect with the original // environment. conn, err = juju.NewConn(env) c.Assert(err, gc.IsNil) defer assertClose(c, conn) // Reset the admin password so the state db can be reused. err = conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) }
func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "admin-secret": "lisboan-pork", "agent-version": "1.2.3", "state-server": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) oldAttrs := cfg.AllAttrs() mcfg := &cloudinit.MachineConfig{ Bootstrap: true, } cons := constraints.MustParse("mem=1T cpu-power=999999999") err = environs.FinishMachineConfig(mcfg, cfg, cons) c.Assert(err, gc.IsNil) c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse) password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{ Password: password, CACert: testing.CACert, }) c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{ Password: password, Info: mongo.Info{CACert: testing.CACert}, }) c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) c.Check(mcfg.Constraints, gc.DeepEquals, cons) oldAttrs["ca-private-key"] = "" oldAttrs["admin-secret"] = "" c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) srvCertPEM := mcfg.StateServingInfo.Cert srvKeyPEM := mcfg.StateServingInfo.PrivateKey _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Check(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) }
// newState returns a new State that uses the given environment. // The environment must have already been bootstrapped. func newState(environ environs.Environ, mongoInfo *authentication.MongoInfo) (*state.State, error) { password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } if err := environs.CheckEnvironment(environ); err != nil { return nil, err } mongoInfo.Password = password opts := mongo.DefaultDialOpts() st, err := state.Open(mongoInfo, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(err) { // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. mongoInfo.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(mongoInfo, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } if err := updateSecrets(environ, st); err != nil { st.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return st, nil }
// AddIdentity adds an identity to the database. func (st *State) AddIdentity(name, displayName, password, creator string) (*Identity, error) { // The name of the identity happens to match the regex we use to confirm user names. // Identities do not have tags, so there is no special function for identities. Given // the relationships between users and identities it seems reasonable to use the same // validation check. if !names.IsValidUser(name) { return nil, errors.Errorf("invalid identity name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } identity := &Identity{ st: st, doc: identityDoc{ Name: name, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: nowToTheSecond(), }, } ops := []txn.Op{{ C: identityCollectionName, Id: name, Assert: txn.DocMissing, Insert: &identity.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("identity already exists") } if err != nil { return nil, errors.Trace(err) } return identity, nil }
// AddUser adds a user to the database. func (st *State) AddUser(name, displayName, password, creator string) (*User, error) { if !names.IsValidUserName(name) { return nil, errors.Errorf("invalid user name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } nameToLower := strings.ToLower(name) user := &User{ st: st, doc: userDoc{ DocID: nameToLower, Name: name, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: nowToTheSecond(), }, } ops := []txn.Op{{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &user.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.AlreadyExistsf("user") } if err != nil { return nil, errors.Trace(err) } return user, nil }
func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) error { selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), args.Constraints.Arch) if err != nil { return err } series := selectedTools.OneSeries() defer delay() if err := e.checkBroken("Bootstrap"); err != nil { return err } network.InitializeFromConfig(e.Config()) password := e.Config().AdminSecret() if password == "" { return fmt.Errorf("admin-secret is required for bootstrap") } if _, ok := e.Config().CACert(); !ok { return fmt.Errorf("no CA certificate in environment configuration") } logger.Infof("would pick tools from %s", selectedTools) cfg, err := environs.BootstrapConfig(e.Config()) if err != nil { return fmt.Errorf("cannot make bootstrap config: %v", err) } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() if estate.bootstrapped { return fmt.Errorf("environment is already bootstrapped") } estate.preferIPv6 = e.Config().PreferIPv6() // Write the bootstrap file just like a normal provider. However // we need to release the mutex for the save state to work, so regain // it after the call. estate.mu.Unlock() instIds := []instance.Id{BootstrapInstanceId} if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: instIds}); err != nil { logger.Errorf("failed to save state instances: %v", err) estate.mu.Lock() // otherwise defered unlock will fail return err } estate.mu.Lock() // back at it // Create an instance for the bootstrap node. logger.Infof("creating bootstrap instance") i := &dummyInstance{ id: BootstrapInstanceId, addresses: network.NewAddresses("localhost"), ports: make(map[network.Port]bool), machineId: agent.BootstrapMachineId, series: series, firewallMode: e.Config().FirewallMode(), state: estate, stateServer: true, } estate.insts[i.id] = i if e.ecfg().stateServer() { // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go // so that we can call it here. info := stateInfo(estate.preferIPv6) st, err := state.Initialize(info, cfg, mongo.DefaultDialOpts(), estate.statePolicy) if err != nil { panic(err) } if err := st.SetEnvironConstraints(args.Constraints); err != nil { panic(err) } if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil { panic(err) } _, err = st.AddAdminUser(password) if err != nil { panic(err) } estate.apiServer, err = apiserver.NewServer(st, estate.apiListener, apiserver.ServerConfig{ Cert: []byte(testing.ServerCert), Key: []byte(testing.ServerKey), DataDir: DataDir, LogDir: LogDir, }) if err != nil { panic(err) } estate.apiState = st } estate.bootstrapped = true estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} return nil }