Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos-firewall-tool: add nftables support #324615

Merged
merged 3 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions maintainers/maintainer-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7608,6 +7608,12 @@
githubId = 91987;
name = "Jim Garrison";
};
garyguo = {
email = "[email protected]";
github = "nbdd0121";
githubId = 4065244;
name = "Gary Guo";
};
gavin = {
email = "[email protected]";
github = "gavinrogers";
Expand Down Expand Up @@ -18709,6 +18715,12 @@
githubId = 141248;
name = "Ramses";
};
rvfg = {
email = "[email protected]";
github = "duament";
githubId = 30264485;
name = "Rvfg";
};
rvl = {
email = "[email protected]";
github = "rvl";
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,8 @@
This saves UPS battery and ensures that host(s) get back up again when power comes back, even in the scenario when the UPS would have had enough capacity to keep power on during the whole power outage.
If you like the old behaviour of keeping the UPSs on (and emptying the battery) after the host(s) have shut down, and risk not getting a power cycle event to get the host(s) back up, set `power.ups.upsmon.settings.POWERDOWNFLAG = null;`.

- `nixos-firewall-tool` now supports nftables in addition to iptables and is installed by default when NixOS firewall is enabled.

- Support for *runner registration tokens* has been [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872)
in `gitlab-runner` 15.6 and is expected to be removed in `gitlab-runner` 18.0. Configuration of existing runners
should be changed to using *runner authentication tokens* by configuring
Expand Down
1 change: 0 additions & 1 deletion nixos/modules/services/networking/firewall-iptables.nix
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ in
}
];

environment.systemPackages = [ pkgs.nixos-firewall-tool ];
networking.firewall.checkReversePath = lib.mkIf (!kernelHasRPFilter) (lib.mkDefault false);

systemd.services.firewall = {
Expand Down
9 changes: 9 additions & 0 deletions nixos/modules/services/networking/firewall-nftables.nix
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ in

networking.nftables.tables."nixos-fw".family = "inet";
networking.nftables.tables."nixos-fw".content = ''
set temp-ports {
comment "Temporarily opened ports"
type inet_proto . inet_service
flags interval
auto-merge
}

${lib.optionalString (cfg.checkReversePath != false) ''
chain rpfilter {
type filter hook prerouting priority mangle + 10; policy drop;
Expand Down Expand Up @@ -147,6 +154,8 @@ in
''
) cfg.allInterfaces)}

meta l4proto . th dport @temp-ports accept

${lib.optionalString cfg.allowPing ''
icmp type echo-request ${lib.optionalString (cfg.pingLimit != null) "limit rate ${cfg.pingLimit}"} accept comment "allow ping"
''}
Expand Down
5 changes: 4 additions & 1 deletion nixos/modules/services/networking/firewall.nix
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,10 @@ in

networking.firewall.trustedInterfaces = [ "lo" ];

environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
environment.systemPackages = [
cfg.package
pkgs.nixos-firewall-tool
] ++ cfg.extraPackages;

boot.kernelModules = (lib.optional cfg.autoLoadConntrackHelpers "nf_conntrack")
++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
Expand Down
31 changes: 28 additions & 3 deletions nixos/tests/firewall.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,31 @@
import ./make-test-python.nix ( { pkgs, nftables, ... } : {
name = "firewall" + pkgs.lib.optionalString nftables "-nftables";
meta = with pkgs.lib.maintainers; {
maintainers = [ ];
maintainers = [ rvfg garyguo ];
};

nodes =
{ walled =
{ ... }:
{ networking.firewall.enable = true;
networking.firewall.logRefusedPackets = true;
{ networking.firewall = {
enable = true;
logRefusedPackets = true;
# Syntax smoke test, not actually verified otherwise
allowedTCPPorts = [ 25 993 8005 ];
allowedTCPPortRanges = [
{ from = 980; to = 1000; }
{ from = 990; to = 1010; }
{ from = 8000; to = 8010; }
];
interfaces.eth0 = {
allowedTCPPorts = [ 10003 ];
allowedTCPPortRanges = [ { from = 10000; to = 10005; } ];
};
interfaces.eth3 = {
allowedUDPPorts = [ 10003 ];
allowedUDPPortRanges = [ { from = 10000; to = 10005; } ];
};
};
networking.nftables.enable = nftables;
services.httpd.enable = true;
services.httpd.adminAddr = "[email protected]";
Expand Down Expand Up @@ -48,6 +65,14 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
walled.succeed("curl -v http://attacker/ >&2")
walled.succeed("ping -c 1 attacker >&2")

# Open tcp port 80 at runtime
walled.succeed("nixos-firewall-tool open tcp 80")
attacker.succeed("curl -v http://walled/ >&2")

# Reset the firewall
walled.succeed("nixos-firewall-tool reset")
attacker.fail("curl --fail --connect-timeout 2 http://walled/ >&2")

# If we stop the firewall, then connections should succeed.
walled.stop_job("${unit}")
attacker.succeed("curl -v http://walled/ >&2")
Expand Down
38 changes: 34 additions & 4 deletions pkgs/by-name/ni/nixos-firewall-tool/nixos-firewall-tool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

set -euo pipefail

# Detect if iptables or nftables-based firewall is used.
if [[ -e /etc/systemd/system/firewall.service ]]; then
BACKEND=iptables
elif [[ -e /etc/systemd/system/nftables.service ]]; then
BACKEND=nftables
else
echo "nixos-firewall-tool: cannot detect firewall backend" >&2
exit 1
fi

ip46tables() {
iptables -w "$@"
ip6tables -w "$@"

}

show_help() {
Expand Down Expand Up @@ -36,13 +45,34 @@ case $1 in
protocol="$2"
port="$3"

ip46tables -I nixos-fw -p "$protocol" --dport "$port" -j nixos-fw-accept
case $BACKEND in
iptables)
ip46tables -I nixos-fw -p "$protocol" --dport "$port" -j nixos-fw-accept
;;
nftables)
nft add element inet nixos-fw "temp-ports" "{ $protocol . $port }"
;;
esac
;;
"show")
ip46tables --numeric --list nixos-fw
case $BACKEND in
iptables)
ip46tables --numeric --list nixos-fw
;;
nftables)
nft list table inet nixos-fw
;;
esac
;;
"reset")
systemctl restart firewall.service
case $BACKEND in
iptables)
systemctl restart firewall.service
;;
nftables)
nft flush set inet nixos-fw "temp-ports"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This implementation differs from the one with iptables. With iptables, all rules are being reset to the definitions done with nix. With nftables only temporarily opened ports are flushed.

I appreciate the nftables implementation more, as it respects dynamic rules done by other tools like Docker, but maybe this should be highlighted, or the subcommands for nftables should be renamed, and a notice added that reset only works with iptables and for nftables the unit should be restarted manually.
Alternatively, reset could be implemented for nftables with the same functionality, and the current implementation gets moved into another separate subcommand.

Although I am also fine with the implementation as it is right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's intentional, since otherwise it's very disruptive. It's especially so when you uses nft sets, everything added there will also be lost.

Arguably the same should be done for iptables by setting up a separate chain, but I don't have bandwidth for it now, I just want to get nftables functionality to work on par with iptables so more people can switch over.

;;
esac
;;
-h|--help|help)
show_help
Expand Down
8 changes: 3 additions & 5 deletions pkgs/by-name/ni/nixos-firewall-tool/package.nix
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
{ writeShellApplication, iptables, lib }:
{ writeShellApplication, lib }:

writeShellApplication {
name = "nixos-firewall-tool";

text = builtins.readFile ./nixos-firewall-tool.sh;
runtimeInputs = [
iptables
];

meta = with lib; {
description = "Temporarily manipulate the NixOS firewall";
license = licenses.mit;
maintainers = with maintainers; [ clerie ];
maintainers = with maintainers; [ clerie rvfg garyguo ];
};
}