feat(secrets): scoped shared secrets — backend + API (Phase 1)
Secrets defined once and applied to many workloads by scope (global or per-app), encrypted at rest and resolved into container env as a low-precedence default layer: global-shared < app-shared < image cfg.Env < workload_env. A workload with no applicable shared secrets is byte-identical to the prior workload_env-only behavior. - store: shared_secrets table + CRUD + ListApplicableSharedSecrets (enabled global + app, global-first), UNIQUE(scope,app_id,name). - plugin.ResolveSharedSecrets + integration into BuildWorkloadEnv (static/dockerfile) and image buildEnv; best-effort — a shared-secret store/decrypt error never fails a deploy, and values are never logged. - REST CRUD at /api/shared-secrets (reads authed, mutations AdminOnly); values encrypted at the boundary via crypto.Encrypt and never returned (only a has_value flag), mirroring workload_env. UNIQUE collisions 409. Compose is out of scope (YAML-defined env). Frontend rule UI is Phase 2. Reviewed: go + security APPROVE (0 CRITICAL/HIGH); two MEDIUMs fixed (translateSQLError -> 409, no driver-message leak). Deferred defense-in- depth: json:"-" on the model value + a description length cap.
This commit is contained in:
@@ -159,6 +159,29 @@ type WorkloadEnv struct {
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// SharedSecret is an env var shared across workloads by scope. Resolved
|
||||
// into a workload's container env as a low-precedence default (overridden
|
||||
// by image cfg.Env and workload_env).
|
||||
type SharedSecret struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"` // the env KEY
|
||||
Value string `json:"value"` // ciphertext when Encrypted; never returned decrypted by the API
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Scope string `json:"scope"` // global | app
|
||||
AppID string `json:"app_id"` // set when scope == app; "" for global
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// Shared-secret scope enum: a secret is either applied to every workload
|
||||
// (global) or only to workloads whose app_id matches (app).
|
||||
const (
|
||||
SharedSecretScopeGlobal = "global"
|
||||
SharedSecretScopeApp = "app"
|
||||
)
|
||||
|
||||
// VolumeScope defines the sharing scope for a volume mount.
|
||||
// Valid scopes: instance, stage, project, project_named, named, ephemeral.
|
||||
type VolumeScope string
|
||||
|
||||
Reference in New Issue
Block a user