forked from toolshed/abra
fix: secrets from config, --offline/chaos handling, typos
See coop-cloud/organising#464
This commit is contained in:
@ -5,13 +5,14 @@ package secret
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||
loader "coopcloud.tech/abra/pkg/upstream/stack"
|
||||
"github.com/decentral1se/passgen"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -60,40 +61,54 @@ func GeneratePassphrases(count uint) ([]string, error) {
|
||||
return passphrases, nil
|
||||
}
|
||||
|
||||
// ReadSecretEnvVars reads secret env vars from an app env var config.
|
||||
func ReadSecretEnvVars(appEnv config.AppEnv) map[string]string {
|
||||
secretEnvVars := make(map[string]string)
|
||||
// ReadSecretsConfig reads secret names/versions from the recipe config. The
|
||||
// function generalises appEnv/composeFiles because some times you have an app
|
||||
// and some times you don't (as the caller). We need to be able to handle the
|
||||
// "app new" case where we pass in the .env.sample and the "secret generate"
|
||||
// case where the app is created.
|
||||
func ReadSecretsConfig(appEnv map[string]string, composeFiles []string, recipeName string) (map[string]string, error) {
|
||||
secretConfigs := make(map[string]string)
|
||||
|
||||
for envVar := range appEnv {
|
||||
regex := regexp.MustCompile(`^SECRET.*VERSION.*`)
|
||||
if string(regex.Find([]byte(envVar))) != "" {
|
||||
secretEnvVars[envVar] = appEnv[envVar]
|
||||
opts := stack.Deploy{Composefiles: composeFiles}
|
||||
config, err := loader.LoadComposefile(opts, appEnv)
|
||||
if err != nil {
|
||||
return secretConfigs, err
|
||||
}
|
||||
|
||||
var enabledSecrets []string
|
||||
for _, service := range config.Services {
|
||||
for _, secret := range service.Secrets {
|
||||
enabledSecrets = append(enabledSecrets, secret.Source)
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Debugf("read %s as secrets from %s", secretEnvVars, appEnv)
|
||||
if len(enabledSecrets) == 0 {
|
||||
logrus.Debugf("not generating app secrets, none enabled in recipe config")
|
||||
return secretConfigs, nil
|
||||
}
|
||||
|
||||
return secretEnvVars
|
||||
for _, secret := range config.Secrets {
|
||||
firstIdx := strings.Index(secret.Name, "_")
|
||||
lastIdx := strings.LastIndex(secret.Name, "_")
|
||||
secretName := secret.Name[firstIdx+1 : lastIdx]
|
||||
|
||||
if secret.Name != "" && string(secret.Name[len(secret.Name)-1]) == "_" {
|
||||
return secretConfigs, fmt.Errorf("missing version for secret? (%s)", secretName)
|
||||
}
|
||||
|
||||
if !(slices.Contains(enabledSecrets, secretName)) {
|
||||
logrus.Debugf("%s not enabled in recipe config, not generating", secretName)
|
||||
continue
|
||||
}
|
||||
|
||||
secretVersion := secret.Name[lastIdx+1:]
|
||||
secretConfigs[secretName] = secretVersion
|
||||
}
|
||||
|
||||
return secretConfigs, nil
|
||||
}
|
||||
|
||||
func ParseSecretEnvVarName(secretEnvVar string) string {
|
||||
withoutPrefix := strings.TrimPrefix(secretEnvVar, "SECRET_")
|
||||
withoutSuffix := strings.TrimSuffix(withoutPrefix, "_VERSION")
|
||||
name := strings.ToLower(withoutSuffix)
|
||||
logrus.Debugf("parsed %s as name from %s", name, secretEnvVar)
|
||||
return name
|
||||
}
|
||||
|
||||
func ParseGeneratedSecretName(secret string, appEnv config.App) string {
|
||||
name := fmt.Sprintf("%s_", appEnv.StackName())
|
||||
withoutAppName := strings.TrimPrefix(secret, name)
|
||||
idx := strings.LastIndex(withoutAppName, "_")
|
||||
parsed := withoutAppName[:idx]
|
||||
logrus.Debugf("parsed %s as name from %s", parsed, secret)
|
||||
return parsed
|
||||
}
|
||||
|
||||
func ParseSecretEnvVarValue(secret string) (secretValue, error) {
|
||||
func ParseSecretValue(secret string) (secretValue, error) {
|
||||
values := strings.Split(secret, "#")
|
||||
if len(values) == 0 {
|
||||
return secretValue{}, fmt.Errorf("unable to parse %s", secret)
|
||||
@ -118,30 +133,29 @@ func ParseSecretEnvVarValue(secret string) (secretValue, error) {
|
||||
}
|
||||
|
||||
// GenerateSecrets generates secrets locally and sends them to a remote server for storage.
|
||||
func GenerateSecrets(cl *dockerClient.Client, secretEnvVars map[string]string, appName, server string) (map[string]string, error) {
|
||||
func GenerateSecrets(cl *dockerClient.Client, secretsFromConfig map[string]string, appName, server string) (map[string]string, error) {
|
||||
secrets := make(map[string]string)
|
||||
|
||||
var mutex sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan error, len(secretEnvVars))
|
||||
for secretEnvVar := range secretEnvVars {
|
||||
ch := make(chan error, len(secretsFromConfig))
|
||||
for n, v := range secretsFromConfig {
|
||||
wg.Add(1)
|
||||
|
||||
go func(s string) {
|
||||
go func(secretName, secretValue string) {
|
||||
defer wg.Done()
|
||||
|
||||
secretName := ParseSecretEnvVarName(s)
|
||||
secretValue, err := ParseSecretEnvVarValue(secretEnvVars[s])
|
||||
parsedSecretValue, err := ParseSecretValue(secretValue)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
|
||||
secretRemoteName := fmt.Sprintf("%s_%s_%s", appName, secretName, secretValue.Version)
|
||||
secretRemoteName := fmt.Sprintf("%s_%s_%s", appName, secretName, parsedSecretValue.Version)
|
||||
logrus.Debugf("attempting to generate and store %s on %s", secretRemoteName, server)
|
||||
|
||||
if secretValue.Length > 0 {
|
||||
passwords, err := GeneratePasswords(1, uint(secretValue.Length))
|
||||
if parsedSecretValue.Length > 0 {
|
||||
passwords, err := GeneratePasswords(1, uint(parsedSecretValue.Length))
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
@ -182,12 +196,12 @@ func GenerateSecrets(cl *dockerClient.Client, secretEnvVars map[string]string, a
|
||||
secrets[secretName] = passphrases[0]
|
||||
}
|
||||
ch <- nil
|
||||
}(secretEnvVar)
|
||||
}(n, v)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for range secretEnvVars {
|
||||
for range secretsFromConfig {
|
||||
err := <-ch
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
Reference in New Issue
Block a user