Issue 339's guard (#63/#64/#65) now refuses the machine's own trees, another account's directory below a home, and uses a found directory as found; this branch's own copy of those rules goes. What stays is the one rule only an account that never becomes root needs: below its home it owns every parent and could swap a link in between the engine's check and an unpack or a write-into (hq ADR 0266 §7). One reader of the user database serves both.
77 lines
2.5 KiB
Go
77 lines
2.5 KiB
Go
package apply
|
|
|
|
// What is never placed below an agent's home (novox/hq ADR 0266 §7).
|
|
//
|
|
// An account that never becomes root owns every directory in its home, so it can swap a link into the path
|
|
// between the engine's check and the engine's write. The engine writes a whole file there through a
|
|
// descriptor that followed no link (home_links.go), but it unpacks an archive, and writes into an existing
|
|
// file, by path. So below the home of an account the declaration says never becomes root, neither is placed.
|
|
// The catalogue's own check (checks/agenthome) refuses either under `${machine:agent-home}`, so no module
|
|
// asks; this is the engine refusing it again where it applies. The machine's own trees, other accounts'
|
|
// homes and a directory's owner below a home are placement_guard.go's (novox/hq issue 339).
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
)
|
|
|
|
// AgentHomeRefusedError is an archive or a written-into file the engine will not place below an agent's home.
|
|
type AgentHomeRefusedError struct {
|
|
Path, What, Home string
|
|
}
|
|
|
|
func (e *AgentHomeRefusedError) Error() string {
|
|
return fmt.Sprintf("%s is not placed: %s below %s, the home of an account that never becomes root, which "+
|
|
"owns every parent there and could swap a link in between the check and the write (novox/hq ADR 0266); "+
|
|
"nothing was touched", e.Path, e.What, e.Home)
|
|
}
|
|
|
|
// refuseUnderAgentHome says why a resource is not placed below one of agentHomes; nil when it may be.
|
|
func refuseUnderAgentHome(r declaration.Resource, agentHomes []string) error {
|
|
what := ""
|
|
switch res := r.(type) {
|
|
case *declaration.Archive:
|
|
what = "an archive unpacked"
|
|
case *declaration.File:
|
|
if res.Into != "" {
|
|
what = "a file written into (" + res.Into + ")"
|
|
}
|
|
}
|
|
if what == "" {
|
|
return nil
|
|
}
|
|
path := filepath.Clean(r.Target())
|
|
for _, p := range []string{path, resolved(path)} {
|
|
for _, home := range agentHomes {
|
|
if atOrBelow(p, home) {
|
|
return &AgentHomeRefusedError{Path: path, What: what, Home: home}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rootNeverHomes is the home of every account the declaration says never becomes root, from the user
|
|
// database; an account not made yet has no home to protect.
|
|
func rootNeverHomes(d *declaration.Declaration) []string {
|
|
if d == nil {
|
|
return nil
|
|
}
|
|
homes := accountsOfHomes()
|
|
var out []string
|
|
for _, r := range d.Resources {
|
|
u, ok := r.(*declaration.User)
|
|
if !ok || u.Root != declaration.RootNever {
|
|
continue
|
|
}
|
|
for home, a := range homes {
|
|
if a.Name == u.Name {
|
|
out = append(out, home)
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|