Files
felhom.eu/hub/internal/poke/notifier_test.go
T
admin eb227486d0 hub v0.59.0: Direction-2a agent-plane immediate-sync poke sender + ep0 felhom-poke surface
- internal/poke: pinned-host-key SSH poke sender (wgsync sibling) + fire-and-forget Notifier (PokeHost/PokeAllHosts). Poke refuses non-WG targets pre-dial; contentless via ep0 forced command to the box WG /32:51822.
- wiring: Server.SetPoke; applyPBSDR pokes the host after each descriptor gen-bump; handleSetArtifacts (MinAgent floor) pokes all hosts. main.go env POKE_SSH_KEY_FILE (reuses peersync endpoint/hostkey).
- scripts/felhom-poke.sh (non-root forced command) + offsite-endpoint.md §11; manifests/hub.yaml Secret/agent-poke + POKE_SSH_KEY_FILE (image tag bump follows the build).
2026-07-16 22:48:15 +02:00

137 lines
3.8 KiB
Go

package poke
import (
"context"
"crypto/ed25519"
"crypto/rand"
"encoding/pem"
"errors"
"io"
"log"
"strings"
"sync"
"testing"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
"golang.org/x/crypto/ssh"
)
func quietLogger() *log.Logger { return log.New(io.Discard, "", 0) }
type fakePeerStore struct {
byHost map[string]*store.WGPeer
all []store.WGPeer
err error
}
func (f *fakePeerStore) GetWGPeerForHost(hostID string) (*store.WGPeer, error) {
if f.err != nil {
return nil, f.err
}
return f.byHost[hostID], nil
}
func (f *fakePeerStore) ListWGPeers() ([]store.WGPeer, error) {
if f.err != nil {
return nil, f.err
}
return f.all, nil
}
type fakeSender struct {
mu sync.Mutex
targets []string
err error
}
func (f *fakeSender) Poke(_ context.Context, boxWGIP string) error {
f.mu.Lock()
f.targets = append(f.targets, boxWGIP)
f.mu.Unlock()
return f.err
}
func (f *fakeSender) got() []string {
f.mu.Lock()
defer f.mu.Unlock()
return append([]string(nil), f.targets...)
}
// pokeHost resolves the host's WG /32 and sends the poke there — the address is looked up, never
// caller-supplied.
func TestPokeHost_SendsToResolvedWGIP(t *testing.T) {
st := &fakePeerStore{byHost: map[string]*store.WGPeer{
"host-abc": {HostID: "host-abc", AssignedIP: "10.77.0.5"},
}}
snd := &fakeSender{}
n := NewNotifier(st, snd, quietLogger())
if err := n.pokeHost(context.Background(), "host-abc"); err != nil {
t.Fatalf("pokeHost: %v", err)
}
got := snd.got()
if len(got) != 1 || got[0] != "10.77.0.5" {
t.Fatalf("sender targets = %v, want [10.77.0.5]", got)
}
}
// A host with no WG peer is a no-op error the caller logs — never a send, never a save failure.
func TestPokeHost_NoPeerNoSend(t *testing.T) {
snd := &fakeSender{}
n := NewNotifier(&fakePeerStore{byHost: map[string]*store.WGPeer{}}, snd, quietLogger())
if err := n.pokeHost(context.Background(), "unknown-host"); err == nil {
t.Fatal("pokeHost for a peerless host returned nil, want an error")
}
if len(snd.got()) != 0 {
t.Fatalf("sender was called for a peerless host: %v", snd.got())
}
}
// A store error propagates as an error (caller logs, never fails the save) and sends nothing.
func TestPokeHost_StoreErrorNoSend(t *testing.T) {
snd := &fakeSender{}
n := NewNotifier(&fakePeerStore{err: errors.New("db down")}, snd, quietLogger())
if err := n.pokeHost(context.Background(), "host-abc"); err == nil {
t.Fatal("pokeHost on a store error returned nil")
}
if len(snd.got()) != 0 {
t.Fatalf("sender called despite store error: %v", snd.got())
}
}
// A nil *Notifier is a safe no-op (poke disabled) — the public entrypoints must not panic.
func TestNilNotifierIsNoOp(t *testing.T) {
var n *Notifier
n.PokeHost("h") // must not panic
n.PokeAllHosts()
}
// Client.Poke REFUSES any target outside the WG /24 BEFORE any network I/O — the confinement the
// forced command and the WG kernel also enforce, checked here at the source.
func TestClientPoke_RefusesNonWGTarget(t *testing.T) {
_, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("keygen: %v", err)
}
signer, err := ssh.NewSignerFromKey(priv)
if err != nil {
t.Fatalf("signer: %v", err)
}
pemBlock, err := ssh.MarshalPrivateKey(priv, "")
if err != nil {
t.Fatalf("marshal key: %v", err)
}
hostLine := string(ssh.MarshalAuthorizedKey(signer.PublicKey()))
c, err := New(Config{
Addr: "198.51.100.1:22", // TEST-NET-2 — must never actually be dialed
User: "felhom-peersync",
PrivateKey: pem.EncodeToMemory(pemBlock),
HostKeyLine: hostLine,
}, quietLogger())
if err != nil {
t.Fatalf("New: %v", err)
}
err = c.Poke(context.Background(), "8.8.8.8") // public IP — outside 10.77.0.0/24
if err == nil || !strings.Contains(err.Error(), "refusing non-WG target") {
t.Fatalf("Poke to a public IP: err = %v, want a pre-dial 'refusing non-WG target' refusal", err)
}
}