package config import ( "crypto/rand" "encoding/base64" "errors" "io/fs" "os" "path/filepath" ) func EnsureSessionKey(secretsPath string) (bool, error) { dir := filepath.Dir(secretsPath) if err := os.Mkdir(dir, 0o700); err != nil && !errors.Is(err, fs.ErrExist) { return false, err } var data []byte info, err := os.Lstat(secretsPath) fresh := errors.Is(err, fs.ErrNotExist) if !fresh { if err != nil || !info.Mode().IsRegular() { return false, nil } var reason string data, reason = readRegular(secretsPath, maxSecretsSize, reasonSecretsTooLarge, secretsModeReason) if reason != "" { return false, nil } vars, problems := parseSecrets(secretsPath, data) if _, ok := vars[nameSessionKey]; ok || len(problems) > 0 { return false, nil } if len(data) > 0 && data[len(data)-1] != '\n' { data = append(data, '\n') } } key := make([]byte, sessionKeyLength) if _, err := rand.Read(key); err != nil { return false, err } data = append(data, nameSessionKey+"="+base64.StdEncoding.EncodeToString(key)+"\n"...) tmp, err := os.CreateTemp(dir, ".secrets.env-*") if err != nil { return false, err } defer os.Remove(tmp.Name()) _, err = tmp.Write(data) if err == nil { err = tmp.Sync() } if cerr := tmp.Close(); err == nil { err = cerr } if err != nil { return false, err } if fresh { err = os.Link(tmp.Name(), secretsPath) if errors.Is(err, fs.ErrExist) { return false, nil } if err == nil { err = os.Remove(tmp.Name()) } } else { err = os.Rename(tmp.Name(), secretsPath) } if err != nil { return false, err } d, err := os.Open(dir) if err != nil { return false, err } err = d.Sync() if cerr := d.Close(); err == nil { err = cerr } return err == nil, err }