From 611d765b76d7e97f2618cb04a5d1c1604496cac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac-Jacqu=C3=A9?= Date: Sat, 29 Feb 2020 18:17:27 +0100 Subject: [PATCH 1/2] nixos/networkd: Add the RoutingPolicyRule-related options --- nixos/modules/system/boot/networkd.nix | 46 +++++++++++++++++++++++ nixos/modules/system/boot/systemd-lib.nix | 5 +++ 2 files changed, 51 insertions(+) diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index a77dbc609f4..44550b2f09c 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -235,6 +235,21 @@ let (assertValueOneOf "AutoJoin" boolValues) ]; + checkRoutingPolicyRule = checkUnitConfig "RoutingPolicyRule" [ + (assertOnlyFields [ + "TypeOfService" "From" "To" "FirewallMark" "Table" "Priority" + "IncomingInterface" "OutgoingInterface" "SourcePort" "DestinationPort" + "IPProtocol" "InvertRule" "Family" + ]) + (assertRange "TypeOfService" 0 255) + (assertRange "FirewallMark" 1 4294967295) + (assertInt "Priority") + (assertPort "SourcePort") + (assertPort "DestinationPort") + (assertValueOneOf "InvertRule" boolValues) + (assertValueOneOf "Family" ["ipv4" "ipv6" "both"]) + ]; + checkRoute = checkUnitConfig "Route" [ (assertOnlyFields [ "Gateway" "GatewayOnLink" "Destination" "Source" "Metric" @@ -535,6 +550,22 @@ let }; }; + routingPolicyRulesOptions = { + options = { + routingPolicyRuleConfig = mkOption { + default = { }; + example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; } ;}; + type = types.addCheck (types.attrsOf unitOption) checkRoutingPolicyRule; + description = '' + Each attribute in this set specifies an option in the + [RoutingPolicyRule] section of the unit. See + systemd.network + 5 for details. + ''; + }; + }; + }; + routeOptions = { options = { routeConfig = mkOption { @@ -772,6 +803,16 @@ let ''; }; + routingPolicyRules = mkOption { + default = [ ]; + type = with types; listOf (submodule routingPolicyRulesOptions); + description = '' + A list of routing policy rules sections to be added to the unit. See + systemd.network + 5 for details. + ''; + }; + routes = mkOption { default = [ ]; type = with types; listOf (submodule routeOptions); @@ -928,6 +969,11 @@ let [Route] ${attrsToSection x.routeConfig} + '')} + ${flip concatMapStrings def.routingPolicyRules (x: '' + [RoutingPolicyRule] + ${attrsToSection x.routingPolicyRuleConfig} + '')} ${def.extraConfig} ''; diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix index fd1a5b9f62c..a3360291586 100644 --- a/nixos/modules/system/boot/systemd-lib.nix +++ b/nixos/modules/system/boot/systemd-lib.nix @@ -59,6 +59,11 @@ in rec { optional (attr ? ${name} && ! isMacAddress attr.${name}) "Systemd ${group} field `${name}' must be a valid mac address."; + isPort = i: i >= 0 && i <= 65535; + + assertPort = name: group: attr: + optional (attr ? ${name} && ! isPort attr.${name}) + "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number."; assertValueOneOf = name: values: group: attr: optional (attr ? ${name} && !elem attr.${name} values) From 9897d83f5863cbf19ac9eab621b3997ae463d756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac-Jacqu=C3=A9?= Date: Sat, 29 Feb 2020 19:34:48 +0100 Subject: [PATCH 2/2] nixos/networkd: test routingPolicyRules with a nixos vm test --- nixos/modules/system/boot/networkd.nix | 23 +++++++++--- nixos/tests/all-tests.nix | 2 +- ...rkd-wireguard.nix => systemd-networkd.nix} | 35 +++++++++++++++++-- 3 files changed, 52 insertions(+), 8 deletions(-) rename nixos/tests/{systemd-networkd-wireguard.nix => systemd-networkd.nix} (65%) diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 44550b2f09c..6dfbe66fc64 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -67,7 +67,12 @@ let (assertOnlyFields [ "PrivateKeyFile" "ListenPort" "FwMark" ]) - (assertRange "FwMark" 1 4294967295) + # The following check won't work on nix <= 2.2 + # see https://github.com/NixOS/nix/pull/2378 + # + # Add this again when we'll have drop the + # nix < 2.2 support. + # (assertRange "FwMark" 1 4294967295) ]; # NOTE The PresharedKey directive is missing on purpose here, please @@ -181,7 +186,12 @@ let (assertOnlyFields [ "InterfaceId" "Independent" ]) - (assertRange "InterfaceId" 1 4294967295) + # The following check won't work on nix <= 2.2 + # see https://github.com/NixOS/nix/pull/2378 + # + # Add this again when we'll have drop the + # nix < 2.2 support. + # (assertRange "InterfaceId" 1 4294967295) (assertValueOneOf "Independent" boolValues) ]; @@ -242,7 +252,12 @@ let "IPProtocol" "InvertRule" "Family" ]) (assertRange "TypeOfService" 0 255) - (assertRange "FirewallMark" 1 4294967295) + # The following check won't work on nix <= 2.2 + # see https://github.com/NixOS/nix/pull/2378 + # + # Add this again when we'll have drop the + # nix < 2.2 support. + # (assertRange "FirewallMark" 1 4294967295) (assertInt "Priority") (assertPort "SourcePort") (assertPort "DestinationPort") @@ -554,7 +569,7 @@ let options = { routingPolicyRuleConfig = mkOption { default = { }; - example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; } ;}; + example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ;}; type = types.addCheck (types.attrsOf unitOption) checkRoutingPolicyRule; description = '' Each attribute in this set specifies an option in the diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 893c2c423ef..b57a2e0b276 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -283,7 +283,7 @@ in systemd-confinement = handleTest ./systemd-confinement.nix {}; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {}; - systemd-networkd-wireguard = handleTest ./systemd-networkd-wireguard.nix {}; + systemd-networkd = handleTest ./systemd-networkd.nix {}; systemd-nspawn = handleTest ./systemd-nspawn.nix {}; pdns-recursor = handleTest ./pdns-recursor.nix {}; taskserver = handleTest ./taskserver.nix {}; diff --git a/nixos/tests/systemd-networkd-wireguard.nix b/nixos/tests/systemd-networkd.nix similarity index 65% rename from nixos/tests/systemd-networkd-wireguard.nix rename to nixos/tests/systemd-networkd.nix index be5c0da981d..319e5e94ece 100644 --- a/nixos/tests/systemd-networkd-wireguard.nix +++ b/nixos/tests/systemd-networkd.nix @@ -41,15 +41,25 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: { { routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }; } ]; }; - "90-eth1" = { + "30-eth1" = { matchConfig = { Name = "eth1"; }; - address = [ "192.168.1.${nodeId}/24" ]; + address = [ + "192.168.1.${nodeId}/24" + "fe80::${nodeId}/64" + ]; + routingPolicyRules = [ + { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; };} + { routingPolicyRuleConfig = { Table = 20; OutgoingInterface = "eth1"; };} + { routingPolicyRuleConfig = { Table = 30; From = "192.168.1.1"; To = "192.168.1.2"; SourcePort = 666 ; DestinationPort = 667; };} + { routingPolicyRuleConfig = { Table = 40; IPProtocol = "tcp"; InvertRule = true; };} + { routingPolicyRuleConfig = { Table = 50; IncomingInterface = "eth1"; Family = "ipv4"; };} + ]; }; }; }; }; in import ./make-test-python.nix ({pkgs, ... }: { - name = "networkd-wireguard"; + name = "networkd"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ ninjatrappeur ]; }; @@ -76,9 +86,28 @@ testScript = '' start_all() node1.wait_for_unit("systemd-networkd-wait-online.service") node2.wait_for_unit("systemd-networkd-wait-online.service") + + # ================================ + # Wireguard + # ================================ node1.succeed("ping -c 5 10.0.0.2") node2.succeed("ping -c 5 10.0.0.1") # Is the fwmark set? node2.succeed("wg | grep -q 42") + + # ================================ + # Routing Policies + # ================================ + # Testing all the routingPolicyRuleConfig members: + # Table + IncomingInterface + node1.succeed("sudo ip rule | grep 'from all iif eth1 lookup 10'") + # OutgoingInterface + node1.succeed("sudo ip rule | grep 'from all oif eth1 lookup 20'") + # From + To + SourcePort + DestinationPort + node1.succeed( + "sudo ip rule | grep 'from 192.168.1.1 to 192.168.1.2 sport 666 dport 667 lookup 30'" + ) + # IPProtocol + InvertRule + node1.succeed("sudo ip rule | grep 'not from all ipproto tcp lookup 40'") ''; })