1
1
package util
2
2
3
3
import (
4
+ "crypto/hmac"
4
5
"crypto/md5" // #nosec we need it to for PostgreSQL md5 passwords
5
6
cryptoRand "crypto/rand"
7
+ "crypto/sha256"
8
+ "encoding/base64"
6
9
"encoding/hex"
7
10
"fmt"
8
11
"math/big"
@@ -16,10 +19,14 @@ import (
16
19
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
20
18
21
"github.com/zalando/postgres-operator/pkg/spec"
22
+ "golang.org/x/crypto/pbkdf2"
19
23
)
20
24
21
25
const (
22
- md5prefix = "md5"
26
+ md5prefix = "md5"
27
+ scramsha256prefix = "SCRAM-SHA-256"
28
+ saltlength = 16
29
+ iterations = 4096
23
30
)
24
31
25
32
var passwordChars = []byte ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" )
@@ -61,16 +68,62 @@ func NameFromMeta(meta metav1.ObjectMeta) spec.NamespacedName {
61
68
}
62
69
}
63
70
64
- // PGUserPassword is used to generate md5 password hash for a given user. It does nothing for already hashed passwords.
65
- func PGUserPassword (user spec.PgUser ) string {
66
- if (len (user .Password ) == md5 .Size * 2 + len (md5prefix ) && user .Password [:3 ] == md5prefix ) || user .Password == "" {
71
+ type Hasher func (user spec.PgUser ) string
72
+ type Random func (n int ) string
73
+
74
+ type Encryptor struct {
75
+ encrypt Hasher
76
+ random Random
77
+ }
78
+
79
+ func NewEncryptor (encryption string ) * Encryptor {
80
+ e := Encryptor {random : RandomPassword }
81
+ m := map [string ]Hasher {
82
+ "md5" : e .PGUserPasswordMD5 ,
83
+ "scram-sha-256" : e .PGUserPasswordScramSHA256 ,
84
+ }
85
+ hasher , ok := m [encryption ]
86
+ if ! ok {
87
+ hasher = e .PGUserPasswordMD5
88
+ }
89
+ e .encrypt = hasher
90
+ return & e
91
+ }
92
+
93
+ func (e * Encryptor ) PGUserPassword (user spec.PgUser ) string {
94
+ if (len (user .Password ) == md5 .Size * 2 + len (md5prefix ) && user .Password [:3 ] == md5prefix ) ||
95
+ (len (user .Password ) > len (scramsha256prefix ) && user .Password [:len (scramsha256prefix )] == scramsha256prefix ) || user .Password == "" {
67
96
// Avoid processing already encrypted or empty passwords
68
97
return user .Password
69
98
}
99
+ return e .encrypt (user )
100
+ }
101
+
102
+ func (e * Encryptor ) PGUserPasswordMD5 (user spec.PgUser ) string {
70
103
s := md5 .Sum ([]byte (user .Password + user .Name )) // #nosec, using md5 since PostgreSQL uses it for hashing passwords.
71
104
return md5prefix + hex .EncodeToString (s [:])
72
105
}
73
106
107
+ func (e * Encryptor ) PGUserPasswordScramSHA256 (user spec.PgUser ) string {
108
+ salt := []byte (e .random (saltlength ))
109
+ key := pbkdf2 .Key ([]byte (user .Password ), salt , iterations , 32 , sha256 .New )
110
+ mac := hmac .New (sha256 .New , key )
111
+ mac .Write ([]byte ("Server Key" ))
112
+ serverKey := mac .Sum (nil )
113
+ mac = hmac .New (sha256 .New , key )
114
+ mac .Write ([]byte ("Client Key" ))
115
+ clientKey := mac .Sum (nil )
116
+ storedKey := sha256 .Sum256 (clientKey )
117
+ pass := fmt .Sprintf ("%s$%v:%s$%s:%s" ,
118
+ scramsha256prefix ,
119
+ iterations ,
120
+ base64 .StdEncoding .EncodeToString (salt ),
121
+ base64 .StdEncoding .EncodeToString (storedKey [:]),
122
+ base64 .StdEncoding .EncodeToString (serverKey ),
123
+ )
124
+ return pass
125
+ }
126
+
74
127
// Diff returns diffs between 2 objects
75
128
func Diff (a , b interface {}) []string {
76
129
return pretty .Diff (a , b )
0 commit comments