blueprint: add support for unencrypted passwords in user customization
This commit is contained in:
parent
189b2b4547
commit
705338c5b3
3 changed files with 103 additions and 2 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/crypt"
|
||||
"github.com/osbuild/osbuild-composer/internal/pipeline"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -161,8 +162,16 @@ func (c *Customizations) customizeUserAndSSHKey(p *pipeline.Pipeline) error {
|
|||
|
||||
users := make(map[string]pipeline.UsersStageOptionsUser)
|
||||
for _, user := range c.User {
|
||||
// TODO: only hashed password are currently supported as an input
|
||||
// plain-text passwords should be also supported due to parity with lorax-composer
|
||||
|
||||
if user.Password != nil && !crypt.PasswordIsCrypted(*user.Password) {
|
||||
cryptedPassword, err := crypt.CryptSHA512(*user.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Password = &cryptedPassword
|
||||
}
|
||||
|
||||
userData := pipeline.UsersStageOptionsUser{
|
||||
Groups: user.Groups,
|
||||
Description: user.Description,
|
||||
|
|
|
|||
49
internal/crypt/crypt.go
Normal file
49
internal/crypt/crypt.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CryptSHA512(phrase string) (string, error) {
|
||||
const SHA512SaltLength = 16
|
||||
|
||||
salt, err := genSalt(SHA512SaltLength)
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
hashSettings := "$6$" + salt
|
||||
return crypt(phrase, hashSettings)
|
||||
}
|
||||
|
||||
func genSalt(length int) (string, error) {
|
||||
saltChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
|
||||
|
||||
b := make([]byte, length)
|
||||
|
||||
for i := range b {
|
||||
runeIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(saltChars))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b[i] = saltChars[runeIndex.Int64()]
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func PasswordIsCrypted(s string) bool {
|
||||
// taken from lorax src: src/pylorax/api/compose.py:533
|
||||
prefixes := [...]string{"$2b$", "$6$", "$5$"}
|
||||
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
43
internal/crypt/crypt_test.go
Normal file
43
internal/crypt/crypt_test.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_crypt_PasswordIsCrypted(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "bcrypt",
|
||||
password: "$2b$04$123465789012345678901uac5A8egfBuZVHMrDZsQzR96IqNBivCy",
|
||||
want: true,
|
||||
}, {
|
||||
name: "sha256",
|
||||
password: "$5$1234567890123456$v.2bOKKLlpmUSKn0rxJmgnh.e3wOKivAVNZmNrOsoA3",
|
||||
want: true,
|
||||
}, {
|
||||
name: "sha512",
|
||||
password: "$6$1234567890123456$d.pgKQFaiD8bRiExg5NesbGR/3u51YvxeYaQXPzx4C6oSYREw8VoReiuYZjx0V9OhGVTZFqhc6emAxT1RC5BV.",
|
||||
want: true,
|
||||
}, {
|
||||
name: "scrypt",
|
||||
password: "$7$123456789012345", //not actual hash output from scrypt
|
||||
want: false,
|
||||
}, {
|
||||
name: "plain",
|
||||
password: "password",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := PasswordIsCrypted(test.password); got != test.want {
|
||||
t.Errorf("PasswordIsCrypted() =%v, want %v", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue