From f7c4820a84df8d39a0f8fcb8c9e9131b47aeecba Mon Sep 17 00:00:00 2001 From: stevef Date: Mon, 13 Apr 2026 19:55:58 +0200 Subject: [PATCH] feat(k3s): support env:// credentials and improve stale config cleanup on security_shield_v2 --- k3s/configmap.yaml | 4 ++-- k3s/deployment.yaml | 12 ++++++++++++ pkg/config/config_struct.go | 14 ++++++++++---- pkg/credential/credential.go | 10 ++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/k3s/configmap.yaml b/k3s/configmap.yaml index 527515c64..e0145c2d1 100644 --- a/k3s/configmap.yaml +++ b/k3s/configmap.yaml @@ -46,7 +46,7 @@ data: }, "telegram": { "enabled": true, - "token": "file://secrets/telegram-token", + "token": "env://PICOCLAW_TELEGRAM_TOKEN", "base_url": "", "proxy": "", "allow_from": [ @@ -258,7 +258,7 @@ data: "model_name": "gemini-flash", "model": "openai/gemini-3-flash-preview", "api_base": "https://generativelanguage.googleapis.com/v1beta/openai/", - "api_key": "env://GOOGLE_API_KEY", + "api_key": "env://PICOCLAW_GOOGLE_API_KEY", "request_timeout": 300 }, { diff --git a/k3s/deployment.yaml b/k3s/deployment.yaml index aaa1a8ef7..18cc3f5e6 100644 --- a/k3s/deployment.yaml +++ b/k3s/deployment.yaml @@ -24,7 +24,9 @@ spec: - | mkdir -p /home/picoclaw/.picoclaw echo "Syncing config.json from ConfigMap..." + grep "GOOGLE" /config-source/config.json cp /config-source/config.json /home/picoclaw/.picoclaw/config.json + rm -f /home/picoclaw/.picoclaw/secure.yaml /home/picoclaw/.picoclaw/.security.yml # Ensure the agent has write permissions to its home volume chown -R 1000:1000 /home/picoclaw/.picoclaw volumeMounts: @@ -43,6 +45,16 @@ spec: value: /home/picoclaw/.picoclaw - name: PICOCLAW_GATEWAY_HOST value: "0.0.0.0" + - name: PICOCLAW_GOOGLE_API_KEY + valueFrom: + secretKeyRef: + name: picoclaw-secrets + key: GOOGLE_API_KEY + - name: PICOCLAW_TELEGRAM_TOKEN + valueFrom: + secretKeyRef: + name: picoclaw-secrets + key: telegram-token volumeMounts: - name: picoclaw-data mountPath: /home/picoclaw/.picoclaw diff --git a/pkg/config/config_struct.go b/pkg/config/config_struct.go index ac2632000..37d91add2 100644 --- a/pkg/config/config_struct.go +++ b/pkg/config/config_struct.go @@ -225,12 +225,16 @@ func (s *SecureString) UnmarshalJSON(value []byte) error { } func (s SecureString) MarshalYAML() (any, error) { - // Preserve raw value if it is already a reference (enc:// or file://) - if strings.HasPrefix(s.raw, credential.EncScheme) || strings.HasPrefix(s.raw, credential.FileScheme) { + // Preserve raw value if it is already a reference (enc://, file://, or env://) + if strings.HasPrefix(s.raw, credential.EncScheme) || + strings.HasPrefix(s.raw, credential.FileScheme) || + strings.HasPrefix(s.raw, credential.EnvScheme) { return s.raw, nil } // If resolved is a reference format (e.g. set via Set), copy back to raw - if strings.HasPrefix(s.resolved, credential.EncScheme) || strings.HasPrefix(s.resolved, credential.FileScheme) { + if strings.HasPrefix(s.resolved, credential.EncScheme) || + strings.HasPrefix(s.resolved, credential.FileScheme) || + strings.HasPrefix(s.resolved, credential.EnvScheme) { s.raw = s.resolved return s.raw, nil } @@ -280,7 +284,9 @@ func resolveKey(v string) (string, error) { if resolver == nil { resolver = credential.NewResolver("") } - if strings.HasPrefix(v, "enc://") || strings.HasPrefix(v, "file://") { + if strings.HasPrefix(v, credential.EncScheme) || + strings.HasPrefix(v, credential.FileScheme) || + strings.HasPrefix(v, credential.EnvScheme) { decrypted, err := resolver.Resolve(v) if err != nil { logger.Errorf("Resolve error: %v", err) diff --git a/pkg/credential/credential.go b/pkg/credential/credential.go index 8ecd6783b..0db2ef095 100644 --- a/pkg/credential/credential.go +++ b/pkg/credential/credential.go @@ -77,6 +77,7 @@ const picoclawHome = "PICOCLAW_HOME" const ( FileScheme = "file://" EncScheme = "enc://" + EnvScheme = "env://" hkdfInfo = "picoclaw-credential-v1" saltLen = 16 @@ -149,6 +150,15 @@ func (r *Resolver) Resolve(raw string) (string, error) { return resolveEncrypted(raw) } + if strings.HasPrefix(raw, EnvScheme) { + envVar := strings.TrimPrefix(raw, EnvScheme) + val := os.Getenv(envVar) + if val == "" { + return "", fmt.Errorf("credential: environment variable %q not set", envVar) + } + return strings.TrimSpace(val), nil + } + // Plaintext credential — return unchanged. return raw, nil }