The filter constrains what arrives from outside, and names no network
The forward chain blocked everything passing through the machine and then allowed the machine's own containers back by naming their address ranges: 172.16.0.0/12 and 192.168.128.0/17 fixed here, the rest recorded per machine by 0043. Every way of keeping that list correct fails — a constant describes one machine, a recorded range goes stale in silence and cannot tell a network the mesh made from one a predecessor left behind, and generating it from the modules would put half the rule set on the machine. The mesh has no position on a container reaching outward: that is not a port opened to anybody. So both chains are written around the links traffic arrives on. What did not arrive from outside is accepted in one line; what did meets the declared rules. The tunnel is named beside the outward links rather than treated as inside, or a port nothing declares would be reachable from every machine in the mesh. A machine that has not reported an outward link is sent no filter and keeps the one it has, refused where a person reads it rather than as a rule set that will not load. Removes the two constants, `node networks`, and the column behind it. novox/hq ADR 0140, superseding 0137 and 0139.
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -310,7 +309,7 @@ func converge(ctx context.Context, open *stores, node string, yes bool, digest s
|
||||
return "", err
|
||||
}
|
||||
derived := derivedFilter{rules: rules, foundation: with.Foundation, mesh: with.Mesh,
|
||||
outward: plan.PublicDomain != "", routed: with.Routed}
|
||||
outward: plan.PublicDomain != "", outwardLinks: with.OutwardLinks}
|
||||
preview, saw := previewOf(node, reported, derived, plan, taken, filter, runs[filter])
|
||||
preview += "\n\n preview " + saw
|
||||
if !yes {
|
||||
@@ -415,16 +414,17 @@ func previewOf(node string, reported inventory.Adoption, derived derivedFilter,
|
||||
b.WriteString(" not previewed: traffic the machine routes that is not a published port " +
|
||||
"(a tunnel, NAT in the found firewall) — the derived filter drops it unless a module " +
|
||||
"declares it\n")
|
||||
// What it routes, said rather than left to the sentence above (novox/hq ADR 0137). The filter
|
||||
// forwards the container runtime's own default pools without being told; anything else is this
|
||||
// list, and a machine whose guests live outside those pools and names none of them loses their
|
||||
// egress at the flip, silently, which is how this was found.
|
||||
if len(derived.routed) > 0 {
|
||||
b.WriteString(fmt.Sprintf(" it routes: %s — kept forwarded, and their guests keep "+
|
||||
"address and name service\n", strings.Join(derived.routed, ", ")))
|
||||
// Which links the filter constrains, said rather than left to the sentence above (novox/hq ADR
|
||||
// 0140). Everything arriving anywhere else is this machine's own guest and keeps working — which
|
||||
// is what a reader most wants to know, because the previous shape of this filter cut a machine's
|
||||
// guests off at the flip without saying so, and that is how this was found.
|
||||
if len(derived.outwardLinks) > 0 {
|
||||
b.WriteString(fmt.Sprintf(" it filters what arrives on: %s, and on the private network "+
|
||||
"— everything its own guests send keeps working\n",
|
||||
strings.Join(derived.outwardLinks, ", ")))
|
||||
} else {
|
||||
b.WriteString(" it routes: nothing said, so only the container runtime's own default " +
|
||||
"pools are forwarded — `node networks <node> <cidr>...` if its guests live elsewhere\n")
|
||||
b.WriteString(" it has reported no link facing outside, so no filter can be composed " +
|
||||
"for it — the flip is refused until it reports one\n")
|
||||
}
|
||||
|
||||
isTaken := map[string]bool{}
|
||||
@@ -485,9 +485,10 @@ type derivedFilter struct {
|
||||
// mesh is every address on the private network; outward says the machine faces outside.
|
||||
mesh []string
|
||||
outward bool
|
||||
// routed is the networks this machine says it routes for what it hosts (novox/hq ADR 0137):
|
||||
// their guests keep address and name service, and what they send onward keeps being forwarded.
|
||||
routed []string
|
||||
// outwardLinks is the links this machine reported as facing outside it (novox/hq ADR 0140).
|
||||
// The filter constrains what arrives on them; everything arriving elsewhere is this machine's
|
||||
// own guest and is not filtered.
|
||||
outwardLinks []string
|
||||
}
|
||||
|
||||
// closesOutside is what a narrowing from everywhere to the private network is called: it closes.
|
||||
@@ -513,12 +514,11 @@ func (d derivedFilter) fate(r inventory.Reach) string {
|
||||
return "stays open — the mesh's own, from anywhere"
|
||||
}
|
||||
}
|
||||
// A guest on a network this machine routes asks it for an address and for names, and those two
|
||||
// arrive here (novox/hq ADR 0137). Matched on the listener's own address: a resolver bound to a
|
||||
// bridge in one of those networks is the one its guests ask.
|
||||
if within(r.Address, d.routed) &&
|
||||
((r.Protocol == "udp" && (r.Port == 53 || r.Port == 67)) || (r.Protocol == "tcp" && r.Port == 53)) {
|
||||
return "stays open — address and name service for a network this machine routes"
|
||||
// This machine's own guests ask it for an address and for names, and those two arrive here
|
||||
// (novox/hq ADR 0140). Admitted by the link they arrive on, so a listener bound anywhere but an
|
||||
// outward link keeps answering them.
|
||||
if (r.Protocol == "udp" && (r.Port == 53 || r.Port == 67)) || (r.Protocol == "tcp" && r.Port == 53) {
|
||||
return "stays open — this machine's own guests asking it for an address and for names"
|
||||
}
|
||||
for _, rule := range d.rules {
|
||||
if rule.Port != r.Port || rule.Protocol != r.Protocol {
|
||||
@@ -542,24 +542,6 @@ func (d derivedFilter) fate(r inventory.Reach) string {
|
||||
return "WILL CLOSE — no module assigned here declares it"
|
||||
}
|
||||
|
||||
// within says whether an address the machine reported sits inside one of the networks it routes.
|
||||
func within(address string, networks []string) bool {
|
||||
ip := net.ParseIP(strings.Trim(address, "[]"))
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
for _, n := range networks {
|
||||
_, block, err := net.ParseCIDR(n)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if block.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// countReachable is how much of a node's account of itself names something off the machine.
|
||||
// Loopback is left out for the same reason the preview leaves it out: nothing outside reaches it,
|
||||
// so a report of loopback alone says nothing about what the filter would close.
|
||||
|
||||
@@ -21,8 +21,7 @@ import (
|
||||
|
||||
func nodeCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("node add <name>, node list, node show <name>, " + publicDomainUsage +
|
||||
", or " + networksUsage)
|
||||
return errors.New("node add <name>, node list, node show <name>, or " + publicDomainUsage)
|
||||
}
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
@@ -68,10 +67,16 @@ func nodeCommand(ctx context.Context, args []string) error {
|
||||
return publicDomain(ctx, inv, args[1:])
|
||||
|
||||
case "networks":
|
||||
// The networks this machine routes for what it hosts (novox/hq ADR 0137): what the derived
|
||||
// filter must keep forwarding, beyond the container runtime's own default pools which it
|
||||
// allows without being told. Reports with no argument, for the same reason the domain does.
|
||||
return nodeNetworks(ctx, inv, args[1:])
|
||||
// Removed by novox/hq ADR 0140, which superseded the record that added it. The filter no
|
||||
// longer names any network: it constrains what arrives from outside the machine and says
|
||||
// nothing about what did not, so there is no list to keep. Answered rather than met with
|
||||
// "unknown command", because this was the documented way to stop a flip cutting a machine's
|
||||
// containers off and somebody will reasonably still type it.
|
||||
return errors.New("`node networks` is gone (novox/hq ADR 0140). The filter constrains what " +
|
||||
"arrives from outside this machine and says nothing about traffic that did not, so no " +
|
||||
"network is named anywhere and nothing needs to be said to keep a machine's own " +
|
||||
"containers reaching outward. The machine reports which of its links face outside; see " +
|
||||
"`node show <name>`")
|
||||
|
||||
case "account":
|
||||
// The operator's login on this machine (novox/hq to-be 29): what a home-scoped file is
|
||||
@@ -151,67 +156,6 @@ func nodeAccount(ctx context.Context, inv *inventory.Inventory, positionals []st
|
||||
return nil
|
||||
}
|
||||
|
||||
const networksUsage = "node networks <name> — what it routes now; " +
|
||||
"<name> <cidr>... to set them; <name> --clear to route only the container runtime's own"
|
||||
|
||||
// nodeNetworks reads, sets or clears the networks a machine routes for what it hosts.
|
||||
//
|
||||
// The same three forms as the domain above, and the read-shaped one reports rather than clearing,
|
||||
// for the same reason: this list is what keeps a machine's guests reaching anything, and losing it
|
||||
// by asking a question is not a mistake anybody can see afterwards.
|
||||
func nodeNetworks(ctx context.Context, inv *inventory.Inventory, args []string) error {
|
||||
set := flag.NewFlagSet("node networks", flag.ContinueOnError)
|
||||
clear := set.Bool("clear", false,
|
||||
"route only the container runtime's own default pools, as a machine that has said nothing does")
|
||||
positionals, err := parseAround(set, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(positionals) == 0 {
|
||||
return errors.New(networksUsage)
|
||||
}
|
||||
node := positionals[0]
|
||||
|
||||
switch {
|
||||
case *clear && len(positionals) > 1:
|
||||
return fmt.Errorf("give %s networks or --clear, not both: %q and --clear say opposite "+
|
||||
"things and the mesh will not choose between them", node, strings.Join(positionals[1:], " "))
|
||||
|
||||
case *clear:
|
||||
if err := inv.SetRoutedNetworks(ctx, node, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s routes only the container runtime's own default pools\n", node)
|
||||
fmt.Printf(" run `push %s` to send its filter\n", node)
|
||||
return nil
|
||||
|
||||
case len(positionals) > 1:
|
||||
if err := inv.SetRoutedNetworks(ctx, node, positionals[1:]); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s routes %s\n", node, strings.Join(positionals[1:], ", "))
|
||||
fmt.Printf(" its filter forwards them, and their guests keep address and name service\n")
|
||||
fmt.Printf(" run `push %s` to send it\n", node)
|
||||
return nil
|
||||
|
||||
default:
|
||||
if _, err := inv.NodeByName(ctx, node); err != nil {
|
||||
return err
|
||||
}
|
||||
networks, err := inv.RoutedNetworksOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(networks) == 0 {
|
||||
fmt.Printf("%s routes only the container runtime's own default pools\n", node)
|
||||
fmt.Printf(" `node networks %s <cidr>...` if its guests live elsewhere\n", node)
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("%s routes %s\n", node, strings.Join(networks, ", "))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const publicDomainUsage = "node public-domain <name> — what it is now; " +
|
||||
"<name> <domain> to set it; <name> --clear to take it away"
|
||||
|
||||
|
||||
@@ -640,9 +640,9 @@ func renderingFor(ctx context.Context, open *stores, node string,
|
||||
if err != nil {
|
||||
return catalogue.Rendering{}, inventory.Node{}, err
|
||||
}
|
||||
// The networks this machine routes for what it hosts, which the derived filter must forward for
|
||||
// (novox/hq ADR 0137).
|
||||
routed, err := inv.RoutedNetworksOf(ctx, node)
|
||||
// Which of this machine's links face outside, which is what the derived filter is written
|
||||
// around (novox/hq ADR 0140). Reported by the machine, never set.
|
||||
outwardLinks, err := inv.OutwardLinksOf(ctx, node)
|
||||
if err != nil {
|
||||
return catalogue.Rendering{}, inventory.Node{}, err
|
||||
}
|
||||
@@ -651,8 +651,8 @@ func renderingFor(ctx context.Context, open *stores, node string,
|
||||
Settings: settings, Generators: gens, Grants: grants, Needed: needed, Ports: ports,
|
||||
Certificate: certificate, Authority: authority, Mesh: private, Names: names,
|
||||
Machines: machines,
|
||||
Suffix: overlay.Suffix(), MeshRange: meshRange, Accounts: accounts, Foundation: foundation,
|
||||
Kept: kept, Adopted: record.Adopted, Routed: routed,
|
||||
Suffix: overlay.Suffix(), MeshRange: meshRange, TunnelInterface: overlay.Interface, Accounts: accounts, Foundation: foundation,
|
||||
Kept: kept, Adopted: record.Adopted, OutwardLinks: outwardLinks,
|
||||
Given: given, Taken: taken, Seats: seats, ArtifactStore: artifactStore, Built: built,
|
||||
BusUsers: busUsers,
|
||||
}, record, nil
|
||||
|
||||
Reference in New Issue
Block a user