Remove a machine's mesh keys and entry when its key is no longer reported (issue 955 F)

This commit is contained in:
2026-10-16 22:53:22 +02:00
parent 896f1d2854
commit 6fdf3629d3
2 changed files with 33 additions and 1 deletions
@@ -66,6 +66,8 @@ type Service struct {
keys map[string]machineKey
known string // the forge's host key line, as the machines write it into known_hosts
bound provider.Bus // the bus the entries are written to (set by Provider)
making sync.Mutex // one Make at a time: they register keys, and two at once would race each other
}
@@ -85,10 +87,12 @@ func NewService(cfg Config) *Service {
// Provider is the provider helper answering the consumers over bus. Every consumer is made again every ten minutes,
// which puts back an account, its team or a key changed by hand.
func (s *Service) Provider(bus provider.Bus) *provider.Provider {
vb := &valuesBus{Bus: bus, s: s, written: map[string][]byte{}}
s.bound = vb
return provider.New(provider.Options{
Node: s.cfg.Node, Module: Module,
Offers: map[string]provider.Offer{ProvisionName: {Credential: provider.NoCredential}},
Bus: &valuesBus{Bus: bus, s: s, written: map[string][]byte{}},
Bus: vb,
Make: s.Make, Gone: s.Gone,
Every: 10 * time.Minute,
Logf: s.cfg.Log,
@@ -115,6 +119,16 @@ func (s *Service) Make(ctx context.Context, c provider.Consumer) error {
}
have := s.keysOf(node)
if len(have) == 0 {
// A key the machine no longer reports is no longer wanted (ADR 0400 §2): the mesh's keys go, the operator's
// stay, and the entry that served the machine goes with them.
if err := s.accounts.RemoveKeysTitled(ctx, user, forgeaccount.TitlePrefix+node+"."); err != nil {
return err
}
if s.bound != nil {
if err := s.bound.Delete(ctx, provider.BucketOf(Module), c.Entry.Key()); err != nil {
return err
}
}
return errNoKey{node}
}
if _, err := s.hostName(); err != nil {
@@ -415,3 +415,21 @@ func (f *fakeKeys) Watch(handle func(key, op string, value []byte, current bool)
return nil
}
func (f *fakeKeys) send(key, op string, v []byte, current bool) { f.handle(key, op, v, current) }
func TestADeletedKeyEntryRemovesTheMeshKeyAndTheBoundEntryButNotTheOperatorsKey(t *testing.T) {
r := newRig(t)
r.bus.bindClient("g14")
r.key("g14", "nox", pubA+" nox@g14 (mesh)")
r.pass()
r.forge.mu.Lock()
r.forge.held["agent-g14"] = append(r.forge.held["agent-g14"], map[string]any{"id": int64(99), "title": "my own", "key": pubB})
r.forge.mu.Unlock()
r.svc.KeyGone("g14.nox")
r.pass()
if got := r.forge.titles("agent-g14"); len(got) != 1 || got[0] != "my own" {
t.Fatalf("keys = %v", got)
}
if r.bus.has("g14.ssh-client.forge-account") {
t.Fatal("the entry still serves a machine whose key is gone")
}
}