agent bridge: drop frames whose source MAC or address isn't the container's own (egress.antiSpoof)
This commit is contained in:
parent
c3f1ef6eb2
commit
c4ac01ef88
1 changed files with 636 additions and 25 deletions
|
|
@ -72,10 +72,13 @@ let
|
|||
|
||||
# The two settings of `egress.forward` the ordinary host (`log`) does not
|
||||
# render, for ../host-modules/hive-egress.nix: `accept` leaves the table
|
||||
# without a forward chain, `drop` is refused by an assertion.
|
||||
# without a forward chain, `drop` is refused by an assertion. The `accept`
|
||||
# host also sets `egress.antiSpoof = "log"`, the one setting the ordinary
|
||||
# host (`drop`) does not render.
|
||||
egressForwardAccept = hive {
|
||||
network.enable = true;
|
||||
network.egress.forward = "accept";
|
||||
network.egress.antiSpoof = "log";
|
||||
};
|
||||
egressForwardDrop = hive {
|
||||
network.enable = true;
|
||||
|
|
@ -1529,7 +1532,8 @@ let
|
|||
unit != null
|
||||
&& unit.serviceConfig.Type == "oneshot"
|
||||
&& lib.hasInfix "hive-egress.nft" unit.serviceConfig.ExecStart
|
||||
&& lib.hasInfix "delete table inet hive_egress" unit.serviceConfig.ExecStop
|
||||
&& lib.hasInfix "delete table inet hive_egress" (toString unit.serviceConfig.ExecStop)
|
||||
&& lib.hasInfix "delete table bridge hive_egress" (toString unit.serviceConfig.ExecStop)
|
||||
&& bare.networking.firewall.extraInputRules == ""
|
||||
&& !(centralToggleOff.systemd.services ? hive-egress);
|
||||
}
|
||||
|
|
@ -1563,6 +1567,83 @@ let
|
|||
&& !(lib.any (l: lib.hasSuffix " drop" l || lib.hasSuffix " reject" l) rules)
|
||||
&& lib.hasInfix "chain input" text;
|
||||
}
|
||||
{
|
||||
# The bridge table drops what a port sends from an address that is not
|
||||
# its own, and logs it. The ruleset is read as text: the spoof chain
|
||||
# ends every path in `drop`, and the accepts that remain are the ones that
|
||||
# let the leased source through, which is the presence arm.
|
||||
name = "the default egress table drops frames whose source is not the port's own";
|
||||
ok =
|
||||
let
|
||||
text = bare.services.hyperhive.network.egress.ruleset;
|
||||
bridgeText = lib.last (lib.splitString "table bridge hive_egress" text);
|
||||
section =
|
||||
name: lib.head (lib.splitString "chain" (lib.last (lib.splitString "chain ${name} {" bridgeText)));
|
||||
spoof = lib.filter (l: l != "" && l != "}") (map lib.trim (lib.splitString "\n" (section "spoof")));
|
||||
ip4 = section "l3_v4";
|
||||
in
|
||||
bare.services.hyperhive.network.egress.antiSpoof == "drop"
|
||||
&& lib.hasInfix "hook prerouting priority -300" bridgeText
|
||||
&& lib.hasInfix ''meta ibrname != "hive-br0" accept'' bridgeText
|
||||
&& lib.hasInfix "iifname . ether saddr != @port_mac goto spoof" bridgeText
|
||||
&& lib.hasInfix "ether type { 8021q, 8021ad } goto spoof" bridgeText
|
||||
&& lib.hasInfix "udp dport 67 iifname . @th,288,48 != @port_chaddr goto spoof" bridgeText
|
||||
&& lib.hasInfix "ether saddr . ip saddr @lease_v4 accept" bridgeText
|
||||
&& lib.hasInfix "ip6 saddr fe80::/10 accept" bridgeText
|
||||
&& lib.hasInfix "arp saddr ether . arp saddr ip @lease_v4 accept" bridgeText
|
||||
&& lib.hasInfix ''log prefix "hive-egress-spoof "'' bridgeText
|
||||
&& lib.hasInfix ''log prefix "hive-egress-spoof-unmetered "'' bridgeText
|
||||
&& lib.hasInfix "update @spoof_ports { iifname limit rate 1/second burst 20 packets }" bridgeText
|
||||
&& lib.length spoof == 3
|
||||
&& lib.all (l: lib.hasSuffix " drop" l) spoof
|
||||
&& lib.hasInfix "goto spoof" ip4;
|
||||
}
|
||||
{
|
||||
# `log` renders the same chains with `accept` where `drop` was: the lines
|
||||
# and counters stay, the frame goes through. The `accept` host is the
|
||||
# only fixture that sets it; `bare` is the control that the two differ.
|
||||
name = "egress.antiSpoof log renders the spoof chain with accept and no drop";
|
||||
ok =
|
||||
let
|
||||
textOf = m: m.services.hyperhive.network.egress.ruleset;
|
||||
spoofOf =
|
||||
m:
|
||||
let
|
||||
t = lib.last (lib.splitString "chain spoof {" (textOf m));
|
||||
in
|
||||
lib.filter (l: l != "" && l != "}") (
|
||||
map lib.trim (lib.splitString "\n" (lib.head (lib.splitString "chain" t)))
|
||||
);
|
||||
logged = spoofOf egressForwardAccept;
|
||||
in
|
||||
egressForwardAccept.services.hyperhive.network.egress.antiSpoof == "log"
|
||||
&& lib.length logged == 3
|
||||
&& lib.all (l: lib.hasSuffix " accept" l) logged
|
||||
&& lib.hasInfix ''log prefix "hive-egress-spoof "'' (textOf egressForwardAccept)
|
||||
&& !(lib.any (l: lib.hasSuffix " drop" l) logged)
|
||||
&& lib.all (l: lib.hasSuffix " drop" l) (spoofOf bare);
|
||||
}
|
||||
{
|
||||
# The sets are filled at run time, from three places: the unit at start
|
||||
# and reload, the udev rule for each new port, and dnsmasq for each
|
||||
# lease. Each is present on `bare`; the host without the bridge has none.
|
||||
name = "the bridge table's sets are filled by the unit, a udev rule per port and dnsmasq's lease script";
|
||||
ok =
|
||||
let
|
||||
unit = bare.systemd.services.hive-egress;
|
||||
pin = bare.systemd.services."hive-egress-pin@";
|
||||
in
|
||||
lib.hasInfix "egress-pin --seed" unit.serviceConfig.ExecStartPost
|
||||
&& lib.any (lib.hasInfix "egress-pin --seed") unit.serviceConfig.ExecReload
|
||||
&& unit.environment.HIVE_EGRESS_BRIDGE == "hive-br0"
|
||||
&& lib.hasInfix "egress-pin %i" pin.serviceConfig.ExecStart
|
||||
&& lib.hasInfix ''KERNEL=="vb-*"'' bare.services.udev.extraRules
|
||||
&& lib.hasInfix "hive-egress-pin@%k.service" bare.services.udev.extraRules
|
||||
&&
|
||||
bare.services.dnsmasq.settings.dhcp-script
|
||||
== toString bare.services.hyperhive.network.egress.leaseScript
|
||||
&& !(centralToggleOff.systemd.services ? "hive-egress-pin@");
|
||||
}
|
||||
{
|
||||
# `accept` renders the table without the forward chain and without the
|
||||
# log prefix; the input chain is the presence arm that the text it
|
||||
|
|
|
|||
Loading…
Reference in a new issue