hub v0.38.0: offsite SLICE 2 — capture the box host-key fingerprint
Descriptor.HostFingerprint (SHA256, non-secret), captured at provision via an x/crypto/ssh keyscan (SSHHostKeyScanner — dials :23, grabs the host key from the handshake, no ssh binary). Fail-closed: nil scanner or scan failure → error (don't serve a descriptor the controller can't verify). Pairs with controller v0.106.0 which re-scans + refuses on mismatch (no blind TOFU). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -23,7 +23,17 @@ func newTestProvisioner(t *testing.T) (*Provisioner, *hetznerapi.Fake, *store.St
|
||||
}
|
||||
t.Cleanup(func() { st.Close() })
|
||||
fake := hetznerapi.NewFake()
|
||||
return &Provisioner{API: fake, Store: st, PoolBoxID: 611421, Location: "fsn1", Logger: log.New(io.Discard, "", 0)}, fake, st
|
||||
return &Provisioner{API: fake, Store: st, Scanner: &fakeScanner{fp: "SHA256:testfp"}, PoolBoxID: 611421, Location: "fsn1", Logger: log.New(io.Discard, "", 0)}, fake, st
|
||||
}
|
||||
|
||||
// fakeScanner returns a fixed fingerprint (or an error) — no live SSH in tests.
|
||||
type fakeScanner struct {
|
||||
fp string
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeScanner) Fingerprint(_ context.Context, _ string, _ int) (string, error) {
|
||||
return f.fp, f.err
|
||||
}
|
||||
|
||||
// Scenario A — enable shared → sub-account provisioned, descriptor built, one-time password stored (NOT in
|
||||
@@ -62,6 +72,30 @@ func TestProvision_Shared(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Part 0 — the descriptor carries the box host-key fingerprint (captured at provision).
|
||||
func TestProvision_HostFingerprint(t *testing.T) {
|
||||
p, _, _ := newTestProvisioner(t)
|
||||
d, err := p.ProvisionOffsite(context.Background(), "cust-fp", Input{Enabled: true, Type: "shared", QuotaGB: 10})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if d.HostFingerprint != "SHA256:testfp" {
|
||||
t.Fatalf("descriptor must carry the host fingerprint, got %q", d.HostFingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
// Part 0 — a host-key scan failure is fail-closed (no descriptor served).
|
||||
func TestProvision_ScanFailClosed(t *testing.T) {
|
||||
p, _, st := newTestProvisioner(t)
|
||||
p.Scanner = &fakeScanner{err: errors.New("keyscan timeout")}
|
||||
d, err := p.ProvisionOffsite(context.Background(), "cust-sf", Input{Enabled: true, Type: "shared", QuotaGB: 10})
|
||||
if err == nil || d != nil {
|
||||
t.Fatalf("a keyscan failure must fail-closed, got d=%+v err=%v", d, err)
|
||||
}
|
||||
// the resource may have been created + password stored, but no verifiable descriptor is served
|
||||
_ = st
|
||||
}
|
||||
|
||||
// Scenario B — enable dedicated → box provisioned.
|
||||
func TestProvision_Dedicated(t *testing.T) {
|
||||
p, fake, st := newTestProvisioner(t)
|
||||
|
||||
Reference in New Issue
Block a user