v0.328.0: the catalog allow-list — the box refuses container rights a template asks for and the box does not give (R-971, D2 = A)
gates / gates (push) Successful in 45s
gates / gates (push) Successful in 45s
internal/privpolicy checks the RENDERED compose (privileged, cap_add, devices, host namespaces, security_opt,
sysctls, volumes_from, build/include/extends, env_file/config files outside the stack dir, aliasing named volumes,
binds outside the box's data roots, the Docker socket, propagation, ${VAR} in a privileged key) against an
allow-list compiled into the controller (one entry: homepage's read-only socket). Asked before every compose
write: the 15-minute sync, the guarded update/ladder step, the install, both settings changes, both restores
(before the stop). A refusal writes nothing, keeps the app on its last good compose, raises the operator-only
event app_definition_refused once per definition and puts one line on the app page. The sync never copies a
template named like a controller infra stack. Structural guard TestAllowList_IsWiredIntoEveryComposeWriter.
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -418,6 +418,7 @@ func main() {
|
||||
// the pre-v0.235.0 product; the freeze is INERT unless it is wired here, which is why a test
|
||||
// walks this file's AST for the call rather than trusting the package to be correct alone.
|
||||
syncer.SetRenderPlanFn(stackMgr.RenderPlanFor)
|
||||
syncer.SetAdmitFn(stackMgr.AdmitSyncedCompose) // R-971: the catalog allow-list, before any compose is written
|
||||
// Start() is deliberately NOT called here — see the comment beside the call further down, after
|
||||
// the pin adoption pass.
|
||||
defer syncer.Stop()
|
||||
@@ -2001,6 +2002,12 @@ func main() {
|
||||
// `app_deploy_started`, emitted by the API router beside its 202.
|
||||
// v0.279.0: the hook is set on EVERY box — after_install (decision 45) must run on a box with no hub too;
|
||||
// only the events need the notifier.
|
||||
// R-971: a definition the catalog allow-list refused raises the operator-only event (once per definition).
|
||||
stackMgr.SetDefinitionRefusedHook(func(name, detail string) {
|
||||
if notifier != nil {
|
||||
notifier.NotifyAppDefinitionRefused(name, detail)
|
||||
}
|
||||
})
|
||||
stackMgr.SetDeployDoneHook(func(name string, ok bool, detail string) {
|
||||
display := name
|
||||
if s, found := stackMgr.GetStack(name); found && s.Meta.DisplayName != "" {
|
||||
@@ -3228,6 +3235,10 @@ func (a *stackAdapter) RecreateStackDefinitionFromUnit(name, composeSrcDir strin
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
stackDir := filepath.Dir(s.ComposePath)
|
||||
// R-971: the writer asks too (the restores asked before they stopped the app; this is the last word).
|
||||
if err := a.AdmitDefinitionFromUnit(name, composeSrcDir, fullEnv); err != nil {
|
||||
return err
|
||||
}
|
||||
// Recover the app definition from the unit (compose + .felhom.yml) into the stack dir.
|
||||
for _, fname := range []string{"docker-compose.yml", ".felhom.yml"} {
|
||||
data, err := os.ReadFile(filepath.Join(composeSrcDir, fname))
|
||||
@@ -3265,6 +3276,16 @@ func (a *stackAdapter) RecreateStackDefinitionFromUnit(name, composeSrcDir strin
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdmitDefinitionFromUnit (R-971) checks a unit's compose, rendered with the restore's env, against the box's catalog
|
||||
// allow-list. No compose in the unit → nothing would be written → nil.
|
||||
func (a *stackAdapter) AdmitDefinitionFromUnit(name, composeSrcDir string, fullEnv map[string]string) error {
|
||||
data, err := os.ReadFile(filepath.Join(composeSrcDir, "docker-compose.yml"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return a.mgr.AdmitCompose(name, data, fullEnv)
|
||||
}
|
||||
|
||||
// SetStackSecretValues writes the named values into the app's app.yaml (R-1106: the off-site restore's database
|
||||
// password). The optional backup.secretValueSetter.
|
||||
func (a *stackAdapter) SetStackSecretValues(name string, values map[string]string) error {
|
||||
|
||||
Reference in New Issue
Block a user