A machine says which networks it routes, and its filter forwards them
The derived filter denies forwarding by default and then allows the container runtime's two default pools, named in this code with a comment saying a machine configured otherwise needs to say so -- and no way to say it. So the filter was right on a machine using the defaults and silently wrong on any other. Measured today: flipping a workstation to the derived filter cut egress for five of its container networks and for every network its test beds create, because those come from ranges the defaults do not cover. Nothing reported a fault; the guests just could not reach anything, while the machine reported it had applied what it was told. A node-level fact beside the public domain, because the machine routes them and the module that loads the filter may be replaced. Added to the defaults, never replacing them. Their guests also keep address and name service, without which a network does not work at all, and the converge preview now says what a machine routes instead of leaving it to a sentence about what it cannot preview.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -518,6 +519,69 @@ func (i *Inventory) PublicDomainOf(ctx context.Context, name string) (string, er
|
||||
return *domain, nil
|
||||
}
|
||||
|
||||
// SetRoutedNetworks records the networks this machine routes for what it hosts, beyond the
|
||||
// container runtime's own default pools.
|
||||
//
|
||||
// A node-level fact (novox/hq ADR 0137), beside the node's public domain: the machine routes them,
|
||||
// not whichever module loads the filter, so swapping that module must not lose them. Added to the
|
||||
// runtime's defaults rather than replacing them, so a machine that says one range does not lose the
|
||||
// ranges its containers were already using. An empty list clears it.
|
||||
//
|
||||
// Each entry is checked as a CIDR here rather than at render time: an address that does not parse
|
||||
// becomes a line nftables refuses, and a refused ruleset is a machine that filters nothing while
|
||||
// its service reports a configuration fault.
|
||||
func (i *Inventory) SetRoutedNetworks(ctx context.Context, name string, networks []string) error {
|
||||
node, err := i.NodeByName(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var kept []string
|
||||
for _, n := range networks {
|
||||
n = strings.TrimSpace(n)
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
if _, _, err := net.ParseCIDR(n); err != nil {
|
||||
return fmt.Errorf("%q is not a network in CIDR form (10.0.0.0/8, 192.168.0.0/16): %w",
|
||||
n, err)
|
||||
}
|
||||
kept = append(kept, n)
|
||||
}
|
||||
if len(kept) == 0 {
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`update node set routed_networks = null where id = $1`, node.ID)
|
||||
return err
|
||||
}
|
||||
body, err := json.Marshal(kept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`update node set routed_networks = $2 where id = $1`, node.ID, string(body))
|
||||
return err
|
||||
}
|
||||
|
||||
// RoutedNetworksOf is the networks a machine routes for what it hosts, empty when it has named none.
|
||||
func (i *Inventory) RoutedNetworksOf(ctx context.Context, name string) ([]string, error) {
|
||||
var body []byte
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select routed_networks from node where name = $1`, name).Scan(&body)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, fmt.Errorf("%w: %s", ErrNoSuchNode, name)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var networks []string
|
||||
if err := json.Unmarshal(body, &networks); err != nil {
|
||||
return nil, fmt.Errorf("the networks recorded for %s are not a list: %w", name, err)
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// RecordOverlayKey keeps the public half a node generated.
|
||||
func (i *Inventory) RecordOverlayKey(ctx context.Context, node, key string) error {
|
||||
if strings.TrimSpace(key) == "" {
|
||||
|
||||
Reference in New Issue
Block a user