package secret import ( "errors" "fmt" "os/exec" "regexp" "strconv" "strings" "coopcloud.tech/abra/client" "coopcloud.tech/abra/config" "github.com/schultz-is/passgen" ) type SecretValue struct { Version string Length int } func GeneratePasswords(count, length uint) ([]string, error) { passwords, err := passgen.GeneratePasswords( count, length, passgen.AlphabetDefault, ) if err != nil { return nil, err } return passwords, nil } func GeneratePassphrases(count uint) ([]string, error) { passphrases, err := passgen.GeneratePassphrases( count, passgen.PassphraseWordCountDefault, rune('-'), passgen.PassphraseCasingDefault, passgen.WordListDefault, ) if err != nil { return nil, err } return passphrases, nil } func ReadSecretEnvVars(appEnv config.AppEnv) map[string]string { secretEnvVars := make(map[string]string) for envVar := range appEnv { regex := regexp.MustCompile(`^SECRET.*VERSION.*`) if string(regex.Find([]byte(envVar))) != "" { secretEnvVars[envVar] = appEnv[envVar] } } return secretEnvVars } func ParseSecretEnvVarName(secretEnvVar string) string { withoutPrefix := strings.TrimPrefix(secretEnvVar, "SECRET_") withoutSuffix := strings.TrimSuffix(withoutPrefix, "_VERSION") return strings.ToLower(withoutSuffix) } func ParseSecretEnvVarValue(secretValue string) (SecretValue, error) { values := strings.Split(secretValue, "#") if len(values) == 0 { return SecretValue{}, fmt.Errorf("Unable to parse '%s'", secretValue) } if len(values) == 1 { return SecretValue{Version: values[0], Length: 0}, nil } else { split := strings.Split(values[1], "=") parsed := split[len(split)-1] stripped := strings.ReplaceAll(parsed, " ", "") length, err := strconv.Atoi(stripped) if err != nil { return SecretValue{}, err } return SecretValue{Version: values[0], Length: length}, nil } } func GenerateSecrets(secretEnvVars map[string]string, server string) (map[string]string, error) { secrets := make(map[string]string) for secretEnvVar := range secretEnvVars { secretName := ParseSecretEnvVarName(secretEnvVar) secretValue, err := ParseSecretEnvVarValue(secretEnvVars[secretEnvVar]) if err != nil { return nil, err } if secretValue.Length > 0 { passwords, err := GeneratePasswords(1, uint(secretValue.Length)) if err != nil { return nil, err } secrets[secretName] = passwords[0] if err := client.StoreSecret(secretName, passwords[0], server); err != nil { return nil, err } } else { passphrases, err := GeneratePassphrases(1) if err != nil { return nil, err } secrets[secretName] = passphrases[0] if err := client.StoreSecret(secretName, passphrases[0], server); err != nil { return nil, err } } } return secrets, nil } func PassInsertSecret(secretValue, secretName, appName, server string) error { _, err := exec.LookPath("pass") if err != nil { return errors.New("Pass cannot be found on your $PATH, is it installed?") } cmd := fmt.Sprintf( "echo %s | pass insert hosts/%s/%s/%s -m", secretValue, server, appName, secretName, ) if err := exec.Command("bash", "-c", cmd).Run(); err != nil { return err } return nil }