test(client): secure bus end-to-end (auth + TLS + E2E together)
TestSecureBusEndToEnd boots the server with control-plane enforce, NATS nkey auth, and TLS all on; two registered peers connect with nkey+TLS, A creates a Matrix room, invites B, publishes, and B decrypts — proving the three layers compose. This is the headline golden of issue 0001. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,16 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/enmanuel/unibus/pkg/client"
|
||||
"github.com/enmanuel/unibus/pkg/frame"
|
||||
"github.com/enmanuel/unibus/pkg/membership"
|
||||
"github.com/enmanuel/unibus/pkg/room"
|
||||
)
|
||||
@@ -111,3 +114,72 @@ func TestNatsTLS(t *testing.T) {
|
||||
t.Fatalf("client without the CA must fail the TLS handshake")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSecureBusEndToEnd is the headline golden of issue 0001: with ALL three
|
||||
// layers active at once — control-plane request signing (enforce), NATS nkey
|
||||
// auth, and TLS — two registered peers run an encrypted room end to end. A
|
||||
// creates a Matrix-policy room, invites B, A publishes and B decrypts. This
|
||||
// proves the layers compose: signed HTTP control plane + authenticated,
|
||||
// encrypted data plane + E2E room content.
|
||||
func TestSecureBusEndToEnd(t *testing.T) {
|
||||
serverTLS, caPool := genTestCA(t)
|
||||
h := bootHarness(t, membership.AuthEnforce, true, serverTLS)
|
||||
waitHealth(t, h.ctrlURL)
|
||||
|
||||
clientTLS := &tls.Config{RootCAs: caPool, MinVersion: tls.VersionTLS12}
|
||||
secure := func(t *testing.T, handle string) (*client.Client, membership.AuthMode) {
|
||||
id := mustIdentity(t)
|
||||
if err := h.store.AddUser(hex.EncodeToString(id.SignPub), handle, membership.RoleMember); err != nil {
|
||||
t.Fatalf("register %s: %v", handle, err)
|
||||
}
|
||||
c, err := client.NewWithOptions(h.natsURL, h.ctrlURL, id, client.Options{UseNkey: true, TLS: clientTLS})
|
||||
if err != nil {
|
||||
t.Fatalf("connect %s securely: %v", handle, err)
|
||||
}
|
||||
return c, 0
|
||||
}
|
||||
|
||||
a, _ := secure(t, "alice")
|
||||
defer a.Close()
|
||||
b, _ := secure(t, "bob")
|
||||
defer b.Close()
|
||||
|
||||
roomID, err := a.CreateRoom("room.secure", room.ModeMatrix)
|
||||
if err != nil {
|
||||
t.Fatalf("A create encrypted room over secure bus: %v", err)
|
||||
}
|
||||
if err := a.Invite(roomID, b.Endpoint()); err != nil {
|
||||
t.Fatalf("A invite B: %v", err)
|
||||
}
|
||||
if err := b.Join(roomID); err != nil {
|
||||
t.Fatalf("B join: %v", err)
|
||||
}
|
||||
|
||||
var mu sync.Mutex
|
||||
var got []string
|
||||
sub, err := b.Subscribe(roomID, func(_ frame.Frame, plaintext []byte) {
|
||||
mu.Lock()
|
||||
got = append(got, string(plaintext))
|
||||
mu.Unlock()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("B subscribe: %v", err)
|
||||
}
|
||||
defer sub.Unsubscribe()
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
const msg = "mensaje sobre bus seguro (auth+TLS+E2E)"
|
||||
if err := a.Publish(roomID, []byte(msg)); err != nil {
|
||||
t.Fatalf("A publish: %v", err)
|
||||
}
|
||||
if !waitFor(&mu, &got, func(rs []string) bool {
|
||||
for _, r := range rs {
|
||||
if r == msg {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, 2*time.Second) {
|
||||
t.Fatalf("B did not receive/decrypt the message over the secured bus; got %v", snapshot(&mu, &got))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user