v0.324.0: the database password comes back with the data (R-1106, R-127b, R-926); restic 0.19.1 (R-1107); R-1108, R-1065, R-1017; dashboard hardening (R-983, R-976)
gates / gates (push) Successful in 44s
gates / gates (push) Successful in 44s
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -1590,3 +1591,51 @@ func restoredLoginFields(app string, meta Metadata, prior *AppConfig, env map[st
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SetStackSecretValues (R-1106) writes the named values into an installed app's app.yaml and NOTHING else: load the
|
||||
// file as it is (records, pin, gates, locked fields all kept — the R-697 lesson: a non-install writer loads, then
|
||||
// saves), replace the named env values, save with the sensitive fields encrypted. Used by the off-site restore to put
|
||||
// back the database password a restored data directory was created with. Values are never logged. The in-memory app
|
||||
// config is refreshed so the next start renders the new value.
|
||||
func (m *Manager) SetStackSecretValues(name string, values map[string]string) error {
|
||||
stack, ok := m.GetStack(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
stackDir := filepath.Dir(stack.ComposePath)
|
||||
meta := LoadMetadata(stackDir)
|
||||
cfg := LoadAppConfig(stackDir)
|
||||
if cfg == nil {
|
||||
return fmt.Errorf("app.yaml of %s not readable", name)
|
||||
}
|
||||
if cfg.Env == nil {
|
||||
cfg.Env = map[string]string{}
|
||||
}
|
||||
names := make([]string, 0, len(values))
|
||||
for k, v := range values {
|
||||
cfg.Env[k] = v // plaintext here; SaveAppConfig encrypts every sensitive field
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
// R-926 / R-694 on this path: the only caller writes these values because the install is NOT the one the restored
|
||||
// data belongs to (a reinstall). Its `type: password` admin logins were generated for the fresh, empty app; the
|
||||
// restored data carries the household's own login. So they are recorded as restored-login fields — the page then
|
||||
// never shows them as the app's login and says to use the old password, exactly as after a unit restore that had
|
||||
// to generate one. A login the app applies at every start (loginAppliedEveryStart) stays a real login.
|
||||
for _, f := range meta.DeployFields {
|
||||
if f.Type != "password" || cfg.Env[f.EnvVar] == "" || loginAppliedEveryStart[name][f.EnvVar] || containsStr(cfg.RestoredLogins, f.EnvVar) {
|
||||
continue
|
||||
}
|
||||
cfg.RestoredLogins = append(cfg.RestoredLogins, f.EnvVar)
|
||||
}
|
||||
if err := SaveAppConfig(stackDir, cfg, m.encKey, SensitiveEnvVars(&meta)); err != nil {
|
||||
return fmt.Errorf("saving app config: %w", err)
|
||||
}
|
||||
m.mu.Lock()
|
||||
if s, ok := m.stacks[name]; ok {
|
||||
s.AppConfig = LoadAppConfig(stackDir)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.logger.Printf("[INFO] [stacks] %s: secret value(s) %v replaced in app.yaml (values not logged)", name, names)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user