The firewall opens the port the mesh itself runs on
Rules are derived from what modules declare they listen on, and the substrate is not a module. So the broker's port — the one every machine dials to enrol and to receive every declaration it is ever sent — appeared in no ruleset the mesh has ever generated. Nothing caught it because a mesh of one never dials its own broker across the network: the ruleset looks complete right up until a second machine tries to join a firewalled anchor and is refused by the packet filter, during enrolment, before the mesh can report anything about it. Assigning the firewall before joining machines is both the natural order and the one that breaks. It is a floor for the same reason ssh is. A machine nobody can reach cannot be repaired; a machine the mesh cannot reach cannot be managed. Neither is a thing any module asks for and neither may be derived away. From anywhere rather than from the private network, deliberately: a node enrols BEFORE it has an address on that network, so narrowing the rule to it would close the door being knocked on. The port is read from the broker this control plane was told about, so the address handed out in a token and the port a machine must accept on stay one fact. A mesh never told about a broker gets no such rule, rather than a broken one — and cannot issue tokens either, which is where that surfaces. Closes novox/hq 04-ISSUES/052. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -89,6 +89,12 @@ type Rendering struct {
|
||||
// a fact about the mesh, and resolution answers questions about one machine.
|
||||
Mesh []string
|
||||
|
||||
// Substrate is the ports the mesh itself needs reachable on every machine, which no module
|
||||
// declares because the substrate is not a module (novox/hq 04-ISSUES/051 and 052). The broker
|
||||
// is the one that matters: a machine dials it to enrol, and a firewall derived only from
|
||||
// modules closes it.
|
||||
Substrate []int
|
||||
|
||||
// Names is every machine's internal name and its address, for containers to be given.
|
||||
//
|
||||
// **A container does not inherit the machine's names**, so every internal name the mesh wrote
|
||||
@@ -207,7 +213,7 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtering := AsNftables(rules, with.Mesh, r.PublicDomain != "")
|
||||
filtering := AsNftables(rules, with.Mesh, r.PublicDomain != "", with.Substrate)
|
||||
|
||||
var out []map[string]any
|
||||
for _, m := range r.Modules {
|
||||
|
||||
Reference in New Issue
Block a user