egress anti-spoofing: keep established bindings, load tables with bindings and leases in one transaction
This commit is contained in:
parent
c4ac01ef88
commit
70179b6c1f
1 changed files with 400 additions and 134 deletions
|
|
@ -648,23 +648,31 @@ source check lives in that table and the host-side filtering stays in
|
|||
bound a moment after its veth appears, and the container's DHCP client
|
||||
retries until then.
|
||||
- **Where the bindings come from.** Addresses are dynamic, so the table's sets
|
||||
the commands below fill them at run time. `hive-priv egress-pin` binds a port to the MAC of the
|
||||
link at the other end of its veth pair, found through the container systemd
|
||||
registers for it. udev starts it for each new `vb-*` port
|
||||
(`hive-egress-pin@<port>.service`), and the `hive-egress` unit starts it for
|
||||
every running container on each start and reload. A binding stays with the
|
||||
port until the port goes: root in the container can't move the port to
|
||||
another MAC afterwards (state in `/run/hive-egress-pins`). If two ports have
|
||||
the same MAC, neither gets a binding. dnsmasq's `dhcp-script` (`hive-priv
|
||||
egress-lease`) adds each lease as a (MAC, address) pair that times out with
|
||||
the lease.
|
||||
fill at run time. `hive-priv egress-pin` binds a port to the MAC of the link at
|
||||
the other end of its veth pair, found through the container systemd registers
|
||||
for it. udev starts it for each new `vb-*` port
|
||||
(`hive-egress-pin@<port>.service`), and a timer (`hive-egress-pin.timer`, every
|
||||
30 seconds) runs it for the ports that still have no binding. The timer reads
|
||||
no container while every port has one. The `hive-egress` unit loads the tables
|
||||
with `hive-priv egress-load`: it looks up the bindings first, while the old
|
||||
tables still filter, and puts them and the leases from dnsmasq's lease file
|
||||
into the same nft transaction as the tables. A start or reload never leaves
|
||||
the bridge with empty sets. If loading the leases fails, the unit logs a
|
||||
warning and loads the tables with the bindings alone. A binding stays with the
|
||||
port until the port goes: root in the container can't move the port to another
|
||||
MAC afterwards (state in `/run/hive-egress-pins`). A port never takes a MAC
|
||||
that a binding already holds, so a newcomer can't evict an established
|
||||
container. If two new ports claim one MAC, neither gets a binding. dnsmasq's
|
||||
`dhcp-script` (`hive-priv egress-lease`) adds each lease as a (MAC, address)
|
||||
pair that times out with the lease.
|
||||
- **The window.** The binding takes the MAC the container's link has when the
|
||||
command first looks, normally within a second of the veth appearing. Until
|
||||
then root in the container could change its own MAC, but in that time only
|
||||
the container's early init runs, which is nix-built, and the agent's code
|
||||
starts later. A container that did change its MAC first would get its own
|
||||
lease for the new MAC, not another container's: a MAC that two ports have
|
||||
gets no binding, and a lease follows the `chaddr` the port may send.
|
||||
starts later. A container that did change its MAC first gets a binding for
|
||||
that MAC only if nobody holds it; a lease follows the `chaddr` the port may
|
||||
send. A port refused because it claimed a held MAC sends nothing, and the
|
||||
timer binds it once its link has a MAC of its own.
|
||||
- **The log.** A refused frame (all of them with `drop`, the same ones with
|
||||
`log`) gets a kernel log line with the prefix `hive-egress-spoof `, rate
|
||||
limited per port: a burst of 20, then one a second. A frame its port can't log
|
||||
|
|
|
|||
Loading…
Reference in a new issue