diff --git a/pkg/authenticator/passwordfile/passwordfile.go b/pkg/authenticator/passwordfile/passwordfile.go index af64f6e1c5..0abd94d9de 100644 --- a/pkg/authenticator/passwordfile/passwordfile.go +++ b/pkg/authenticator/passwordfile/passwordfile.go @@ -18,13 +18,13 @@ package passwordfile import ( "context" - "crypto/subtle" "encoding/csv" "fmt" "io" "os" "strings" + "github.com/k3s-io/k3s/pkg/nodepassword" "k8s.io/klog/v2" "k8s.io/apiserver/pkg/authentication/authenticator" @@ -37,8 +37,8 @@ type PasswordAuthenticator struct { } type userPasswordInfo struct { - info *user.DefaultInfo - password string + info *user.DefaultInfo + hash string } // NewCSV returns a PasswordAuthenticator, populated from a CSV file. @@ -65,9 +65,13 @@ func NewCSV(path string) (*PasswordAuthenticator, error) { if len(record) < 3 { return nil, fmt.Errorf("password file '%s' must have at least 3 columns (password, user name, user uid), found %d", path, len(record)) } + hash, err := nodepassword.Hasher.CreateHash(record[0]) + if err != nil { + return nil, fmt.Errorf("failed to hash password for username '%s' in password file '%s': %v", record[1], path, err) + } obj := &userPasswordInfo{ - info: &user.DefaultInfo{Name: record[1], UID: record[2]}, - password: record[0], + info: &user.DefaultInfo{Name: record[1], UID: record[2]}, + hash: hash, } if len(record) >= 4 { obj.info.Groups = strings.Split(record[3], ",") @@ -88,7 +92,7 @@ func (a *PasswordAuthenticator) AuthenticatePassword(ctx context.Context, userna if !ok { return nil, false, nil } - if subtle.ConstantTimeCompare([]byte(user.password), []byte(password)) == 0 { + if err := nodepassword.Hasher.VerifyHash(user.hash, password); err != nil { return nil, false, nil } return &authenticator.Response{User: user.info}, true, nil diff --git a/pkg/nodepassword/nodepassword.go b/pkg/nodepassword/nodepassword.go index a2f3abf156..b52315c1e7 100644 --- a/pkg/nodepassword/nodepassword.go +++ b/pkg/nodepassword/nodepassword.go @@ -18,8 +18,8 @@ import ( ) var ( - // hasher provides the algorithm for generating and verifying hashes - hasher = hash.NewSCrypt() + // Hasher provides the algorithm for generating and verifying hashes + Hasher = hash.NewSCrypt() ) func getSecretName(nodeName string) string { @@ -33,7 +33,7 @@ func verifyHash(secretClient coreclient.SecretClient, nodeName, pass string) err return err } if hash, ok := secret.Data["hash"]; ok { - if err := hasher.VerifyHash(string(hash), pass); err != nil { + if err := Hasher.VerifyHash(string(hash), pass); err != nil { return errors.Wrapf(err, "unable to verify hash for node '%s'", nodeName) } return nil @@ -47,7 +47,7 @@ func Ensure(secretClient coreclient.SecretClient, nodeName, pass string) error { return err } - hash, err := hasher.CreateHash(pass) + hash, err := Hasher.CreateHash(pass) if err != nil { return errors.Wrapf(err, "unable to create hash for node '%s'", nodeName) } diff --git a/pkg/passwd/passwd.go b/pkg/passwd/passwd.go index 62a0445560..fe83c9c251 100644 --- a/pkg/passwd/passwd.go +++ b/pkg/passwd/passwd.go @@ -60,14 +60,6 @@ func Read(file string) (*Passwd, error) { return result, nil } -func (p *Passwd) Check(name, pass string) (matches bool, exists bool) { - e, ok := p.names[name] - if !ok { - return false, false - } - return e.pass == pass, true -} - func (p *Passwd) EnsureUser(name, role, passwd string) error { tokenPrefix := "::" + name + ":" idx := strings.Index(passwd, tokenPrefix) diff --git a/pkg/server/router.go b/pkg/server/router.go index 435e6b7fdf..b3473c9383 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -483,8 +483,12 @@ func verifyLocalPassword(ctx context.Context, config *Config, mu *sync.Mutex, de return "", http.StatusInternalServerError, errors.Wrap(err, "unable to read node password file") } - password := strings.TrimSpace(string(passBytes)) - if password != node.Password { + passHash, err := nodepassword.Hasher.CreateHash(strings.TrimSpace(string(passBytes))) + if err != nil { + return "", http.StatusInternalServerError, errors.Wrap(err, "unable to hash node password file") + } + + if err := nodepassword.Hasher.VerifyHash(passHash, node.Password); err != nil { return "", http.StatusForbidden, errors.Wrapf(err, "unable to verify local password for node '%s'", node.Name) }