wgtunnel: S3 Part 1 — pure-Go keygen + hub wire (WireWireguard, report stanza, RegisterWG)
key.go: create-once 0600/0700, corrupt-refusal (never overwrite — may be escrowed identity), clamp for CANONICAL STORED form (x/crypto X25519 clamps derivation internally — discovered during red-proof (c); the stored-clamped test is the real anchor). Fixed vectors generated with real wg pubkey (provenance in test). hub: WireDesiredState.Wireguard + WireguardStatus report stanza + RegisterWG client (typed errors, token-free). S2 golden copied BYTE-IDENTICAL + field-exact decode test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package hub
|
||||
|
||||
// S3 Group C — RegisterWG client (POST /hosts/{id}/wg, per-host key). Typed-error mapping and
|
||||
// the no-token-in-errors invariant, same scaffolding as the desired-state client tests.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegisterWG_PathAuthBodyAndDecode(t *testing.T) {
|
||||
var gotPath, gotAuth, gotMethod, gotBody string
|
||||
c := testClient(func(r *http.Request) (*http.Response, error) {
|
||||
gotPath = r.URL.Path
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
gotMethod = r.Method
|
||||
b, _ := io.ReadAll(r.Body)
|
||||
gotBody = string(b)
|
||||
return httpResp(200, `{"pubkey":"PK","assigned_ip":"10.77.0.2/32","existed":false,"generation":3,"sync":"ok"}`), nil
|
||||
})
|
||||
|
||||
resp, err := c.RegisterWG(context.Background(), "PK")
|
||||
if err != nil {
|
||||
t.Fatalf("RegisterWG: %v", err)
|
||||
}
|
||||
if gotMethod != http.MethodPost || gotPath != "/api/v1/hosts/demo-host-01/wg" {
|
||||
t.Errorf("request = %s %s, want POST /api/v1/hosts/demo-host-01/wg", gotMethod, gotPath)
|
||||
}
|
||||
if gotAuth != "Bearer super-secret-bearer-key" {
|
||||
t.Errorf("auth = %q", gotAuth)
|
||||
}
|
||||
if gotBody != `{"pubkey":"PK"}` {
|
||||
t.Errorf("body = %s", gotBody)
|
||||
}
|
||||
if resp.AssignedIP != "10.77.0.2/32" || resp.Existed || resp.Generation != 3 || resp.Sync != "ok" {
|
||||
t.Errorf("resp = %+v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterWG_TypedErrors(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
status int
|
||||
body string
|
||||
}{
|
||||
{403, "Forbidden: host_id mismatch"},
|
||||
{404, "Unknown host_id"},
|
||||
{409, "wg endpoint not configured"},
|
||||
{409, "pubkey already registered elsewhere"},
|
||||
} {
|
||||
c := testClient(func(r *http.Request) (*http.Response, error) {
|
||||
return httpResp(tc.status, tc.body), nil
|
||||
})
|
||||
_, err := c.RegisterWG(context.Background(), "PK")
|
||||
var he *HTTPError
|
||||
if !errors.As(err, &he) || he.StatusCode != tc.status {
|
||||
t.Errorf("status %d: err = %v, want HTTPError %d", tc.status, err, tc.status)
|
||||
}
|
||||
if strings.Contains(err.Error(), "super-secret-bearer-key") {
|
||||
t.Fatalf("bearer token leaked into error: %v", err)
|
||||
}
|
||||
}
|
||||
// Transport failure → TransportError, token-free.
|
||||
c := testClient(func(r *http.Request) (*http.Response, error) {
|
||||
return nil, errors.New("dial tcp: connection refused")
|
||||
})
|
||||
_, err := c.RegisterWG(context.Background(), "PK")
|
||||
var te *TransportError
|
||||
if !errors.As(err, &te) {
|
||||
t.Errorf("transport err = %v, want TransportError", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user