80 lines
4.4 KiB
Go
80 lines
4.4 KiB
Go
package overlay
|
|
|
|
// The private network as a module rather than as code beside the module system.
|
|
//
|
|
// A machine's peer list is derived from every other machine, so it differs on each one and
|
|
// changes when any of them changes — there is nothing that could be written in a manifest. So the
|
|
// module says its resources are computed, and this computes them.
|
|
//
|
|
// What that buys, beyond one mechanism instead of two: **a machine is on the private network
|
|
// because it was assigned the module.** Before this, every machine with a key and an address was
|
|
// on it, and there was no way to say a machine should stay off.
|
|
//
|
|
// It is worth saying what is *not* here, because networking looks like it should be foundational.
|
|
// The host needs none of this. It has an address and a route to the broker before the mesh exists
|
|
// — that is the machine's own networking — and the broker's address is carried in the enrolment
|
|
// token rather than resolved. So the private network is something the mesh installs on top, like
|
|
// anything else, and a node without it is an ordinary node that nothing reaches directly.
|
|
|
|
// What a module asks for when it needs machines to reach each other. **This is the name that
|
|
// matters** — WireGuard is one way to answer it, and naming the requirement after the answer is
|
|
// how a mesh ends up unable to have a second one.
|
|
const Requirement = "private-network"
|
|
|
|
// Name is the module that answers it with WireGuard.
|
|
//
|
|
// **It writes no names.** A machine's mesh names are answered by the mesh's one resolver (novox/hq
|
|
// ADR 0194), and /etc/hosts is the file of one module, the holder of `node-hostname` (ADR 0199,
|
|
// ADR 0223): the controller writes into no file another seat's holder owns. If the mesh ever needs a line there, it
|
|
// asks that holder to register it. This module asked for a `node-names` fact written into /etc/hosts
|
|
// until 2026-10-05; the host gives that region back at the first push without it.
|
|
const Name = "mesh-wireguard"
|
|
|
|
// Addressing is the mesh handing out addresses on the private network itself.
|
|
//
|
|
// The mesh's resolver answers names from it, which is why it requires this rather than a private
|
|
// network in general. A different VPN that hands out its own addresses would come with its own
|
|
// names — the mesh has nothing to answer about a machine whose address it did not choose.
|
|
const Addressing = "mesh-addressing"
|
|
|
|
// TheNetwork is what a machine can only have one of.
|
|
//
|
|
// Providing a private network is not the singular part — a machine could reasonably run two VPNs
|
|
// for two different purposes. Being **the** one the mesh runs over is singular, and without
|
|
// saying so a person who chose a different VPN can still end up with this one dragged back in by
|
|
// something that needed the mesh's own addresses. Which is exactly what happened, once.
|
|
const TheNetwork = "the-private-network"
|
|
|
|
// **A resolver's data went the same way as the names.** Two constants used to sit here — a
|
|
// `mesh-resolver` module that would write the machines as wildcards, and a `resolver-data`
|
|
// requirement a daemon would ask for. Nothing ever provided or consumed either: a module that
|
|
// answers names asks for the `node-zones` fact in its own manifest (catalogue.FactsInto) and
|
|
// requires Addressing, since the file is made of the mesh's addresses and means nothing off the
|
|
// network. A requirement nothing provides is refused at resolution, so leaving the names here
|
|
// would only have documented a mechanism that does not exist.
|
|
|
|
// Manifest is the module the mesh provides for itself.
|
|
//
|
|
// It ships with the control plane rather than coming from a repository, because the thing that
|
|
// computes it ships with the control plane. That is the only way it is unusual: it is assigned,
|
|
// unassigned, resolved and settled exactly like a module somebody wrote.
|
|
func Manifest() map[string]any {
|
|
return map[string]any{
|
|
"module": Name,
|
|
"version": "1",
|
|
"computed": Name,
|
|
"provides": []string{Requirement, Addressing},
|
|
"claims": []map[string]any{{"name": TheNetwork, "scope": "node"}},
|
|
}
|
|
}
|
|
|
|
// Interface is what the private network is called on a machine, and where the node's own key
|
|
// lives. A constant rather than a setting: two nodes disagreeing about the name would produce a
|
|
// mesh where each is configured correctly and nothing meets.
|
|
const (
|
|
Interface = "mesh0"
|
|
ConfigPath = "/etc/wireguard/" + Interface + ".conf"
|
|
Unit = "wg-quick@" + Interface
|
|
DefaultKeyPath = "/var/lib/mesh-host/overlay.key"
|
|
)
|