hashes passwords support

This commit is contained in:
Mikaël Cluseau
2023-12-17 17:29:08 +01:00
parent c02f701c04
commit b616b710cb
8 changed files with 130 additions and 99 deletions

View File

@ -13,8 +13,8 @@ func clusterFuncs(clusterSpec *clustersconfig.Cluster) map[string]interface{} {
cluster := clusterSpec.Name
return map[string]interface{}{
"password": func(name string) (s string) {
return fmt.Sprintf("{{ password %q %q }}", cluster, name)
"password": func(name, hash string) (s string) {
return fmt.Sprintf("{{ password %q %q %q | quote }}", cluster, name, hash)
},
"token": func(name string) (s string) {

View File

@ -1,15 +1,20 @@
package main
import (
"crypto/rand"
"encoding/base32"
"encoding/json"
"fmt"
"log"
"path"
"strconv"
"strings"
cfsslconfig "github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
yaml "gopkg.in/yaml.v2"
"novit.tech/direktil/pkg/bootstrapconfig"
"novit.tech/direktil/pkg/config"
)
@ -28,16 +33,50 @@ func templateFuncs(sslCfg *cfsslconfig.Config) map[string]any {
return getUsableKeyCert(cluster, caName, name, profile, label, certReq, sslCfg)
}
hash := func(plain, seed []byte, hashAlg string) (hashed string, err error) {
switch hashAlg {
case "sha512crypt":
return sha512crypt(plain, seed)
case "bootstrap":
return bootstrapconfig.JoinSeedAndHash(seed, bootstrapconfig.PasswordHashFromSeed(seed, plain)), nil
default:
return "", fmt.Errorf("unknown hash alg: %q", hashAlg)
}
}
return map[string]any{
"password": func(cluster, name string) (password string, err error) {
password, _, err = clusterPasswords.Get(cluster + "/" + name)
"quote": strconv.Quote,
"password": func(cluster, name, hashAlg string) (password string, err error) {
key := cluster + "/" + name
seed, err := seeds.GetOrCreate(key, func() (seed []byte, err error) {
seed = make([]byte, 16)
_, err = rand.Read(seed)
return
})
if err != nil {
return "", fmt.Errorf("failed to get seed: %w", err)
}
password, err = clusterPasswords.GetOrCreate(key, func() (password string, err error) {
raw := make([]byte, 10)
_, err = rand.Read(raw)
if err != nil {
return "", fmt.Errorf("failed to generate password: %w", err)
}
password = strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(raw))
return
})
if err != nil {
return
}
if len(password) == 0 {
err = fmt.Errorf("password %q not defined for cluster %q", name, cluster)
}
return
return hash([]byte(password), seed, hashAlg)
},
"token": getOrCreateClusterToken,

View File

@ -291,6 +291,24 @@ func (s KVSecrets[T]) Put(key string, v T) (err error) {
return
}
func (s KVSecrets[T]) GetOrCreate(key string, create func() (T, error)) (v T, err error) {
v, found, err := s.Get(key)
if err != nil {
return
}
if !found {
v, err = create()
if err != nil {
return
}
err = s.Put(key, v)
}
return
}
func (s KVSecrets[T]) WsList(resp *restful.Response, prefix string) {
keys, err := s.Keys(prefix)
if err != nil {

View File

@ -0,0 +1,39 @@
package main
import (
"encoding/base64"
crypthash "github.com/sergeymakinen/go-crypt/hash"
"github.com/sergeymakinen/go-crypt/sha512"
)
// for some reason, no implementation of crypt's sha512 is clean enough :(
func sha512crypt(password, seed []byte) (string, error) {
// loose salt entropy because of character restriction in the salt
salt := []byte(base64.RawStdEncoding.EncodeToString(seed))[:sha512.MaxSaltLength]
// - base64 allows '+' where the salt accepts '.'
for i, c := range salt {
if c == '+' {
salt[i] = '.'
}
}
scheme := struct {
HashPrefix string
Rounds uint32 `hash:"param:rounds,omitempty"`
Salt []byte
Sum [86]byte
}{
HashPrefix: sha512.Prefix,
Rounds: sha512.DefaultRounds,
Salt: salt,
}
key, err := sha512.Key([]byte(password), scheme.Salt, scheme.Rounds)
if err != nil {
return "", err
}
crypthash.LittleEndianEncoding.Encode(scheme.Sum[:], key)
return crypthash.Marshal(scheme)
}

View File

@ -4,6 +4,8 @@ import (
restful "github.com/emicklei/go-restful"
)
var seeds = newClusterSecretKV[[]byte]("seeds")
var clusterPasswords = newClusterSecretKV[string]("passwords")
func wsClusterPasswords(req *restful.Request, resp *restful.Response) {