On the control node every agent ran as the operator's account, which has passwordless sudo, so an agent could become root without a person (hq ADR 0266). A node now names an agent account at the controller's terminal only; the agent's module declares it never to become root, the node-engine judges that, and the self-check (DA) raises agent-can-become-root while it does not hold, so ADR 0259's router can rest on it.
77 lines
2.6 KiB
Go
77 lines
2.6 KiB
Go
package inventory
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The agent account (novox/hq ADR 0266): recorded and read back with every node, its home derived when
|
|
// not stated, cleared by an empty name — and refused when it is root, the operator's own account, or no
|
|
// login at all, because each of those would say agents have an account of their own while they do not.
|
|
func TestAgentAccountIsRecordedAndRefusedWhereItWouldNotConfine(t *testing.T) {
|
|
inv := ForTest(t)
|
|
ctx := context.Background()
|
|
if _, err := inv.AddNode(ctx, "anchor"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.SetAccount(ctx, "anchor", "operator", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
n, err := inv.NodeByName(ctx, "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n.AgentAccount != "" || n.AgentHome() != "" {
|
|
t.Fatalf("a node that names none has agent account %q, home %q", n.AgentAccount, n.AgentHome())
|
|
}
|
|
|
|
if err := inv.SetAgentAccount(ctx, "anchor", "agent", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n, _ = inv.NodeByName(ctx, "anchor")
|
|
if n.AgentAccount != "agent" || n.AgentHome() != "/home/agent" {
|
|
t.Fatalf("agent account %q, home %q; want agent, /home/agent", n.AgentAccount, n.AgentHome())
|
|
}
|
|
all, err := inv.Nodes(ctx)
|
|
if err != nil || len(all) != 1 || all[0].AgentAccount != "agent" {
|
|
t.Fatalf("the listing does not carry the agent account: %+v %v", all, err)
|
|
}
|
|
|
|
if err := inv.SetAgentAccount(ctx, "anchor", "agent", "/srv/agent"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n, _ = inv.NodeByName(ctx, "anchor"); n.AgentHome() != "/srv/agent" {
|
|
t.Fatalf("the stated home is %q", n.AgentHome())
|
|
}
|
|
|
|
for _, c := range []struct{ account, home, says string }{
|
|
{"root", "", "may not run as root"},
|
|
{"operator", "", "operator account"},
|
|
{"Agent", "", "not a login name"},
|
|
{"9agent", "", "not a login name"},
|
|
{"agent", "relative", "absolute"},
|
|
{"", "/home/x", "without an agent account"},
|
|
} {
|
|
err := inv.SetAgentAccount(ctx, "anchor", c.account, c.home)
|
|
if err == nil || !strings.Contains(err.Error(), c.says) {
|
|
t.Errorf("%q %q: %v; want a refusal saying %q", c.account, c.home, err, c.says)
|
|
}
|
|
}
|
|
if n, _ = inv.NodeByName(ctx, "anchor"); n.AgentAccount != "agent" {
|
|
t.Fatalf("a refusal changed the record: %q", n.AgentAccount)
|
|
}
|
|
|
|
if err := inv.SetAgentAccount(ctx, "anchor", "", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n, _ = inv.NodeByName(ctx, "anchor"); n.AgentAccount != "" || n.AgentAccountHome != "" {
|
|
t.Fatalf("clearing left %q %q", n.AgentAccount, n.AgentAccountHome)
|
|
}
|
|
if err := inv.SetAgentAccount(ctx, "nowhere", "agent", ""); !errors.Is(err, ErrNoSuchNode) {
|
|
t.Fatalf("an unknown node: %v", err)
|
|
}
|
|
}
|