feat(membership): forbid cleartext rooms on public deployments (H4 min defense)
Audit H4 (Alto). The embedded NATS has a single account with no per-subject permissions, so any registered peer can subscribe to any subject — a cleartext (ModeNATS) room's payload is readable by anyone who knows the subject. A complete per-subject ACL derived from membership does not fit here: NATS evaluates a connection's permissions once at connect time and never re-evaluates them, but unibus clients connect-then-create/join-then-publish on one connection (TestSecureBusEndToEnd). Static permissions would forbid the owner from publishing to a room it just created; the dynamic reconnection model belongs to the 0003 decentralization redesign. See dev/0004d-dataplane-acl.md. Minimum defense implemented: Server.RequireEncryptedRooms (set by membershipd on any non-loopback bind) refuses to create cleartext rooms, so every room on a public deployment is end-to-end encrypted. Message CONTENT stays confidential even with no subject isolation; residual traffic-metadata exposure is documented and tracked for 0003. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,14 @@ func main() {
|
||||
}
|
||||
|
||||
srv := membership.NewServer(store, blobs, authMode)
|
||||
// On a public (non-loopback) bind, disable cleartext rooms: the embedded NATS
|
||||
// has no per-subject ACL, so cleartext content would be readable by any
|
||||
// registered peer. Forcing E2E keeps message content confidential regardless
|
||||
// (audit H4 minimum defense; see dev/0004d-dataplane-acl.md).
|
||||
if !isLoopbackBind(*bind) {
|
||||
srv.RequireEncryptedRooms = true
|
||||
log.Printf("cleartext rooms: DISABLED (public bind requires end-to-end encryption)")
|
||||
}
|
||||
log.Printf("control-plane auth: %s", authMode)
|
||||
addr := *bind + ":" + *httpPort
|
||||
httpSrv := &http.Server{
|
||||
|
||||
Reference in New Issue
Block a user