IfState: Difference between revisions
m ifstate: remove "advertisement" |
m dhcpv4: replace custom script with packaged udhcpc/default.script |
||
| (13 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
[https://ifstate.net/2.0/ IfState] is a python 3 utility designed for declarative management of Linux network interfaces. It acts as a frontend to the kernel's Netlink interface, using the <code>pyroute2</code> library to configure network settings such as IP addresses, bridges, traffic control, and WireGuard in an idempotent manner—much like an <code>iproute2</code>/<code>ethtool</code>/<code>tc</code>/<code>wg</code> wrapper. | [https://ifstate.net/2.0/ IfState] is a python 3 utility designed for declarative management of Linux network interfaces. It acts as a frontend to the kernel's Netlink interface, using the <code>pyroute2</code> library to configure network settings such as IP addresses, bridges, traffic control, and WireGuard in an idempotent manner—much like an <code>iproute2</code>/<code>ethtool</code>/<code>tc</code>/<code>wg</code> wrapper. | ||
It | It is available since NixOS 25.11 (see https://github.com/NixOS/nixpkgs/pull/431047). | ||
=== Examples === | === Examples === | ||
You can find several examples on the [https://ifstate.net/2.0/examples/ IfState website]. | You can find several examples on the [https://ifstate.net/2.0/examples/ IfState website]. Some include NixOS configuration instructions, while the more complex examples are covered in detail here. | ||
=== Network Namespaces (netns) === | ==== Network Namespaces (netns) ==== | ||
Network namespaces are a powerful feature in Linux that allow you to create isolated network environments. Each namespace has its own network interfaces, IP addresses, routing tables, and firewall rules. This isolation is particularly useful for scenarios like running systemd services or NixOS containers in separate network environments, enabling better control and security. | Network namespaces are a powerful feature in Linux that allow you to create isolated network environments. Each namespace has its own network interfaces, IP addresses, routing tables, and firewall rules. This isolation is particularly useful for scenarios like running systemd services or NixOS containers in separate network environments, enabling better control and security. | ||
==== to isolate services / nixos-containers ==== | ===== to isolate services / nixos-containers ===== | ||
You can bind specific systemd services to a network namespace, ensuring they operate in a controlled network environment without affecting the host or other services.<syntaxhighlight lang="nixos"> | You can bind specific systemd services to a network namespace, ensuring they operate in a controlled network environment without affecting the host or other services.<syntaxhighlight lang="nixos"> | ||
{ | { | ||
| Line 85: | Line 59: | ||
}</syntaxhighlight> | }</syntaxhighlight> | ||
==== to separate provider network from | ===== to separate provider network from GRT ===== | ||
Another practical application could involve setting up a VPN gateway on a virtual server hosted by a provider. Imagine you’re using a WireGuard tunnel (<code>wg0</code>) to connect to a network that provides internet access, alongside a client peer WireGuard endpoint (<code>wg1</code>) that allows your personal devices to connect. | Another practical application could involve setting up a VPN gateway on a virtual server hosted by a provider. Imagine you’re using a WireGuard tunnel (<code>wg0</code>) to connect to a network that provides internet access, alongside a client peer WireGuard endpoint (<code>wg1</code>) that allows your personal devices to connect. | ||
| Line 91: | Line 65: | ||
To achieve this, you might want to isolate the provider network from your Global Routing Table (GRT) and bind the WireGuard endpoints. The <code>IfState</code> tool offers a link configuration option called <code>bind_netns</code>, which can be used with tunnel links (such as WireGuard, GRE, SIT, etc.) to implement this separation. | To achieve this, you might want to isolate the provider network from your Global Routing Table (GRT) and bind the WireGuard endpoints. The <code>IfState</code> tool offers a link configuration option called <code>bind_netns</code>, which can be used with tunnel links (such as WireGuard, GRE, SIT, etc.) to implement this separation. | ||
[[File:Ifstate-vpn-gw.png|center|frameless]] | |||
'''Important Note:''' If <code>enp0s3</code> is your provider interface, this configuration will move it into an external network namespace that contains nothing except the bound WireGuard endpoint. As a result, you won’t be able to access systemd services like your SSH server without an active WireGuard connection. Plan accordingly to avoid losing access to critical services.<syntaxhighlight lang="nixos"> | '''Important Note:''' If <code>enp0s3</code> is your provider interface, this configuration will move it into an external network namespace that contains nothing except the bound WireGuard endpoint. As a result, you won’t be able to access systemd services like your SSH server without an active WireGuard connection. Plan accordingly to avoid losing access to critical services.<syntaxhighlight lang="nixos"> | ||
| Line 101: | Line 76: | ||
# public ip addresses from your upstream provider | # public ip addresses from your upstream provider | ||
addresses = [ | addresses = [ | ||
" | "198.51.100.10/26" | ||
"2001:db8::10/64" | "2001:db8::10/64" | ||
]; | ]; | ||
| Line 113: | Line 88: | ||
{ | { | ||
to = "0.0.0.0/0"; | to = "0.0.0.0/0"; | ||
via = " | via = "198.51.100.62"; | ||
} | } | ||
{ | { | ||
to = "::/0"; | to = "::/0"; | ||
dev = "enp3s0"; | |||
via = "fe80::1"; | via = "fe80::1"; | ||
} | } | ||
| Line 133: | Line 109: | ||
# the tunnel addresses for your upstream wireguard | # the tunnel addresses for your upstream wireguard | ||
addresses = [ | addresses = [ | ||
" | "203.0.113.100/25" | ||
"2001:db8:bad:c0de::100/128" | "2001:db8:bad:c0de::100/128" | ||
]; | ]; | ||
| Line 139: | Line 115: | ||
listen_port = 40608; | listen_port = 40608; | ||
private_key = "!include /keys/wg0.priv"; | private_key = "!include /keys/wg0.priv"; | ||
peers." | peers."4PG5bt3cXacnjKolLFYHDon5NIPmaBL/CNFUjOUUEGQ=" = { | ||
allowedips = [ | allowedips = [ | ||
"0.0.0.0/0" | "0.0.0.0/0" | ||
| Line 156: | Line 132: | ||
bind_netns = "outside"; | bind_netns = "outside"; | ||
}; | }; | ||
# the tunnel addresses for your | # the tunnel addresses for your own clients | ||
addresses = [ | addresses = [ | ||
" | "203.0.113.129/25" | ||
"2001:db8: | "2001:db8:dead:beef::1/128" | ||
]; | ]; | ||
wireguard = { | wireguard = { | ||
listen_port = 20406; | listen_port = 20406; | ||
private_key = "!include /keys/wg1.priv"; | private_key = "!include /keys/wg1.priv"; | ||
peers." | peers."GGavg2J9HdumqCgfpFXD85GYb6T0vWmtXBVQmlj9d0w=" = { | ||
allowedips = [ | allowedips = [ | ||
" | "203.0.113.130/32" | ||
"2001:db8:dead:beef::2/128" | "2001:db8:dead:beef::2/128" | ||
]; | ]; | ||
}; | }; | ||
}; | |||
}; | |||
}; | |||
routing.routes = [ | |||
{ | |||
to = "0.0.0.0/0"; | |||
via = "203.0.113.1"; | |||
} | |||
{ | |||
to = "::/0"; | |||
via = "2001:db8:bad:c0de::1"; | |||
} | |||
]; | |||
}; | |||
} | |||
</syntaxhighlight> | |||
==== DHCPv4 ==== | |||
<syntaxhighlight lang="nixos"> | |||
{ lib, pkgs, ... }: | |||
{ | |||
networking.ifstate = { | |||
enable = true; | |||
settings = { | |||
parameters.hooks.dhcp.script = pkgs.writeScript "ifstate-udhcp-wrapper-script.sh" '' | |||
${lib.getExe' pkgs.busybox "udhcpc"} --quit --now -i $IFS_IFNAME -b --script ${pkgs.busybox}/default.script | |||
''; | |||
interfaces.eth1 = { | |||
addresses = [ ]; | |||
hooks = [ | |||
{ name = "dhcp"; } | |||
]; | |||
link = { | |||
state = "up"; | |||
kind = "physical"; | |||
}; | }; | ||
}; | }; | ||
| Line 175: | Line 187: | ||
}; | }; | ||
} | } | ||
</syntaxhighlight> | |||
=== Known Issues === | |||
==== Firewall for netns ==== | |||
Currently the nixos modules for firewall configuration are not capable of configuring a firewall for a network namespace (see [https://github.com/NixOS/nixpkgs/issues/372414 github:nixpkgs/nixos#372414]). | |||
It's possible to apply the nftables firewall ruleset in all network namespaces by adding the following nix configuration, but this comes with the limitation, that interface names have to be unique across all network namespaces.<syntaxhighlight lang="nixos"> | |||
# stolen from https://github.com/secshellnet/nixos/blob/main/modules/firewall.nix | |||
{ | |||
lib, | |||
pkgs, | |||
config, | |||
... | |||
}: | |||
let | |||
netns = [ ]; # TODO add the network namespaces to apply the firewall ruleset to here | |||
in | |||
{ | |||
systemd.services = builtins.listToAttrs ( | |||
map (key: { | |||
name = "nftables@${key}"; | |||
value = | |||
let | |||
cfg = config.systemd.services.nftables; | |||
map' = f: x: if lib.isList x then map f x else f x; | |||
mapFunc = file: "${lib.getExe' pkgs.iproute2 "ip"} netns exec %i ${file}"; | |||
in | |||
{ | |||
inherit (cfg) | |||
conflicts | |||
wants | |||
wantedBy | |||
reloadIfChanged | |||
; | |||
description = "nftables firewall for network namespace %i"; | |||
before = [ "network.target" ]; | |||
after = [ | |||
"network-setup.service" | |||
"network-pre.target" | |||
# netns must exist, before firewall rules can be applied | |||
"ifstate.service" | |||
]; | |||
serviceConfig = { | |||
inherit (cfg.serviceConfig) Type RemainAfterExit StateDirectory; | |||
} | |||
// builtins.listToAttrs ( | |||
map | |||
(key: { | |||
name = key; | |||
value = map' mapFunc cfg.serviceConfig.${key}; | |||
}) | |||
[ | |||
"ExecStart" | |||
"ExecStartPost" | |||
"ExecStop" | |||
"ExecReload" | |||
] | |||
); | |||
unitConfig.DefaultDependencies = false; | |||
}; | |||
}) netns | |||
); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Category:Networking]] | |||
[[Category:Python]] | |||