blueprint: add support for unencrypted passwords in user customization

This commit is contained in:
Ondřej Budai 2019-10-29 16:56:13 +01:00 committed by Tom Gundersen
parent 189b2b4547
commit 705338c5b3
3 changed files with 103 additions and 2 deletions

View file

@ -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
View 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
}

View 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)
}
})
}
}