feat: Docker Compose files in repos
CI / Checks (pull_request) Successful in 40s
PR title / Conventional title (pull_request) Successful in 1s

compose.yaml, docker-compose.yml and their variants tracked by git are
listed under Repos, their services' environment variables as secrets,
named service.VAR.

- environment as a map or a list, and <<: *anchors shared between
  services; the secret patterns pick the keys, the rest hidden, with
  compose.secretPatterns for compose files' own.
- ${VAR}, $VAR, ${VAR:?…} and variables without a value are marked as
  references; docker-compose.example.yml and the like are hidden.
- Jasypt values decrypt with the service's JASYPT_ENCRYPTOR_PASSWORD.
- A blue Docker icon, themeable with composeColor.
This commit is contained in:
keb committed 2026-10-15 14:19:30 +02:00
1 parent 580c7ef985
commit de38b8b281
20 files changed
+577 -202

No files matched your search

+20 -28
View File
@@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"git.kebcloud.no/keb/lazysecrets/internal/jasypt"
"git.kebcloud.no/keb/lazysecrets/internal/provider"
@@ -17,12 +16,12 @@ const jasyptEnv = "JASYPT_ENCRYPTOR_PASSWORD"
// jasyptValue decrypts v when it's a Jasypt value, ENC(...), from a plain
// file; other values are as they are.
func jasyptValue(file string, v provider.SecretValue) (provider.SecretValue, error) {
func jasyptValue(file, name string, v provider.SecretValue) (provider.SecretValue, error) {
s := v.Reveal()
if !jasypt.Is(s) {
return v, nil
}
password, from, err := jasyptPassword(file)
password, from, err := jasyptPassword(file, name)
if err != nil {
return provider.SecretValue{}, err
}
@@ -36,39 +35,32 @@ func jasyptValue(file string, v provider.SecretValue) (provider.SecretValue, err
return provider.NewSecretValue([]byte(plain)), nil
}
// jasyptPassword is the password for a file's Jasypt values, and where it's
// from: $JASYPT_ENCRYPTOR_PASSWORD, else the file's own setting
// (jasypt.encryptor.password in a Spring file, JASYPT_ENCRYPTOR_PASSWORD
// in an env file), with a ${VAR} in it read from the environment.
func jasyptPassword(file string) (password, from string, err error) {
// jasyptPassword is the password for the Jasypt value of the key named
// name, and where it's from: $JASYPT_ENCRYPTOR_PASSWORD, else the file's
// own setting (jasypt.encryptor.password in a Spring file; the service's
// JASYPT_ENCRYPTOR_PASSWORD in a compose file, the file's in an env file),
// with a ${VAR} in it read from the environment.
func jasyptPassword(file, name string) (password, from string, err error) {
if v := os.Getenv(jasyptEnv); v != "" {
return v, "$" + jasyptEnv, nil
}
setting, read := jasyptEnv, plainValue
if IsSpring(file) {
setting, read = "jasypt.encryptor.password", springValue
setting, read, placeholder := jasyptEnv, plainValue, composePlaceholder // env files interpolate like compose
if t, ok := configTypeOf(file); ok {
setting, read, placeholder = t.jasyptSetting(name), t.value, t.placeholder
}
v, err := read(file, setting)
if err != nil {
return "", "", fmt.Errorf("jasypt: no password: set $%s, or %s in %s", jasyptEnv, setting, filepath.Base(file))
}
return resolvePlaceholder(v.Reveal(), setting+" in "+filepath.Base(file))
}
// placeholder is Spring's ${VAR} or ${VAR:default}.
var placeholder = regexp.MustCompile(`^\$\{([^:}]+)(:[^}]*)?\}$`)
// resolvePlaceholder reads a ${VAR} value from the environment, falling
// back to its default; other values are as they are.
func resolvePlaceholder(v, from string) (string, string, error) {
m := placeholder.FindStringSubmatch(v)
from = setting + " in " + filepath.Base(file)
env, def, hasDef, ok := placeholder(v.Reveal())
switch {
case m == nil:
return v, from, nil
case os.Getenv(m[1]) != "":
return os.Getenv(m[1]), "$" + m[1] + " (" + from + ")", nil
case m[2] != "":
return m[2][1:], "the default in " + from, nil
case !ok:
return v.Reveal(), from, nil
case os.Getenv(env) != "":
return os.Getenv(env), "$" + env + " (" + from + ")", nil
case hasDef:
return def, "the default in " + from, nil
}
return "", "", fmt.Errorf("jasypt: no password: %s is %s, and $%s isn't set", from, v, m[1])
return "", "", fmt.Errorf("jasypt: no password: %s is %s, and $%s isn't set", from, v.Reveal(), env)
}