Descriptionstate/unit/machine/user: change password hashes
This is a follow up to my previous PasswordHash changes, making it a
more complete step.
1) PasswordHash is split into 3 functions:
UserPasswordHash(password, salt) passwordhash
CompatPasswordHash(password) passwordhash {
return UserPasswordHash(password, oldDefaultSalt)
}
AgentPasswordHash(password) passwordHash, error
2) UserPasswordHash does the same 8192 rounds of pbkdf2 hashing to
ensure that it is hard to brute force a user's password. On top of
that, it requires a "salt" value.
state/User grows an added field PasswordSalt. Whenever we call
User.SetPassword we generate a salt and use that to compute the
password hash.
For compatibility, if User.PasswordSalt is the empty string, we use
the CompatPasswordHash which sets the salt to the old hard-coded salt.
I chose to use a different function to make it clear that we don't
really want to support empty salts.
3) CompatPasswordHash is just a thin wrapper over UserPasswordHash. It
should always generate the same hash value that utils.PasswordHash
used to generate. I changed the name just to make it clear. Though
Bootstrap still uses this directly as the way to hide the real
plaintext password.
4) User.PasswordValid(password) knows how to check both ways for
compatibility, and if it sees that the saved hash was not salted, it
calls SetPassword() immediately to set a salt and regenerate the
password hash.
5) For Machine and Unit agents, we change their hash function to just
be a simple SHA-512 of the password. We don't bother with a salt or
with iterated pbkdf2. This is because we know that agents have very
high entropy passwords (utils.RandomPassword uses 18 bytes of entropy
or 2^144). This is not really feasible to brute force, so there isn't
really a point to salt or iterate.
As part of this change, AgentPasswordHash(string) string, error can
return an error if it appears the password might not have enough
entropy (for now we just check the length of the password). That way
someone can't accidentally get rooted because they set the machine-0
agent password to "foo".
This probably had the widest effect as we had a lot of tests that set
a simple password just so we could login as that agent, but we just
change it so we use utils.RandomPassword in all of those places.
As mentioned before, this is a big performance win when scaling up to
thousands of agents. Timing shows UserPasswordHash takes about 80ms,
while AgentPasswordHash takes about 8us. PasswordHash was dominating
the time it took for the system to recover after restarting the API
machine.
6) Similar to User.PasswordValid, Machine.PasswordValid and
Unit.PasswordValid will do a fast check using AgentPasswordValid, and
if that fails, they will fall back to the slow CompatPasswordValid. If
they find the passwordhash is a compat value, they reset the value to
the fast-path value.
7) I looked at removing the FastInsecureHash flag (since we've split
the hashes we create). However, with it unset I found the 'state/...'
tests took >4min while with it set they still took more like 3min. So
it still shaves 25% of the test suite time.
https://code.launchpad.net/~jameinel/juju-core/faster-passwords/+merge/193667
(do not edit description out of merge proposal)
Patch Set 1 #
Total comments: 19
Patch Set 2 : state/unit/machine/user: change password hashes #
Total comments: 14
Patch Set 3 : state/unit/machine/user: change password hashes #Patch Set 4 : state/unit/machine/user: change password hashes #
MessagesTotal messages: 10
|