Use scram-sha-256 hash if postgresql parameter password_encryption set to do so. (#995)

* Use scram-sha-256 hash if postgresql parameter password_encryption set to do so.

* test fixed

* Refactoring

* code style
This commit is contained in:
Igor Yanchenko
2020-07-16 14:43:57 +02:00
committed by GitHub
parent ec932f88d8
commit 002b47ec32
5 changed files with 90 additions and 17 deletions
+57 -4
View File
@@ -1,8 +1,11 @@
package util
import (
"crypto/hmac"
"crypto/md5" // #nosec we need it to for PostgreSQL md5 passwords
cryptoRand "crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
@@ -16,10 +19,14 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/zalando/postgres-operator/pkg/spec"
"golang.org/x/crypto/pbkdf2"
)
const (
md5prefix = "md5"
md5prefix = "md5"
scramsha256prefix = "SCRAM-SHA-256"
saltlength = 16
iterations = 4096
)
var passwordChars = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
@@ -61,16 +68,62 @@ func NameFromMeta(meta metav1.ObjectMeta) spec.NamespacedName {
}
}
// PGUserPassword is used to generate md5 password hash for a given user. It does nothing for already hashed passwords.
func PGUserPassword(user spec.PgUser) string {
if (len(user.Password) == md5.Size*2+len(md5prefix) && user.Password[:3] == md5prefix) || user.Password == "" {
type Hasher func(user spec.PgUser) string
type Random func(n int) string
type Encryptor struct {
encrypt Hasher
random Random
}
func NewEncryptor(encryption string) *Encryptor {
e := Encryptor{random: RandomPassword}
m := map[string]Hasher{
"md5": e.PGUserPasswordMD5,
"scram-sha-256": e.PGUserPasswordScramSHA256,
}
hasher, ok := m[encryption]
if !ok {
hasher = e.PGUserPasswordMD5
}
e.encrypt = hasher
return &e
}
func (e *Encryptor) PGUserPassword(user spec.PgUser) string {
if (len(user.Password) == md5.Size*2+len(md5prefix) && user.Password[:3] == md5prefix) ||
(len(user.Password) > len(scramsha256prefix) && user.Password[:len(scramsha256prefix)] == scramsha256prefix) || user.Password == "" {
// Avoid processing already encrypted or empty passwords
return user.Password
}
return e.encrypt(user)
}
func (e *Encryptor) PGUserPasswordMD5(user spec.PgUser) string {
s := md5.Sum([]byte(user.Password + user.Name)) // #nosec, using md5 since PostgreSQL uses it for hashing passwords.
return md5prefix + hex.EncodeToString(s[:])
}
func (e *Encryptor) PGUserPasswordScramSHA256(user spec.PgUser) string {
salt := []byte(e.random(saltlength))
key := pbkdf2.Key([]byte(user.Password), salt, iterations, 32, sha256.New)
mac := hmac.New(sha256.New, key)
mac.Write([]byte("Server Key"))
serverKey := mac.Sum(nil)
mac = hmac.New(sha256.New, key)
mac.Write([]byte("Client Key"))
clientKey := mac.Sum(nil)
storedKey := sha256.Sum256(clientKey)
pass := fmt.Sprintf("%s$%v:%s$%s:%s",
scramsha256prefix,
iterations,
base64.StdEncoding.EncodeToString(salt),
base64.StdEncoding.EncodeToString(storedKey[:]),
base64.StdEncoding.EncodeToString(serverKey),
)
return pass
}
// Diff returns diffs between 2 objects
func Diff(a, b interface{}) []string {
return pretty.Diff(a, b)