-
-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
nftables module: Add new module for nftables firewall settings #18842
Conversation
@Jookia, thanks for your PR! By analyzing the annotation information on this pull request, we identified @edolstra, @bjornfor and @offlinehacker to be potential reviewers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really unsure about this addition.
I haven't looked into nftables yet but maybe we could provide a better interface and use nftables for networking.firewall
eventually.
Just a few comments:
the logic for the service should be wants = [ "network-pre.target" ];
not the other way around.
Adding ruleset
and rulesetFile
options that are mutually exclusive (rulesetFile
overrides ruleset
) seems wrong to me. Either one should suffice.
If you have ideas for a better interface, I'd be happy to listen. I use rulesetFile, but it would be annoying to have to do pkgs.writeText if you're just writing inline rules. The wants change is a good idea. I use this personally, but if it's not useful for NixOS I'm happy to close the PR |
Actually the wants change isn't a good idea. This module does the same as firewall. |
Then the firewall module is broken too...
|
Yeah, I guess I'll fix it here then. The nftables module isn't intended to replace the firewall, but it is incompatible with the firewall. nftables itself is probably a little too early to go all out on, but it's nice to have for now as a module with some examples. |
Fixed? |
1 similar comment
Fixed? |
I think we need some |
Added wantedBy = multi-user.target, this is how another firewall package does it. |
My 2¢ as someone who manages many complex Linux firewalls, and in the process of switching some of them to NixOS. I would like to have an abstraction layer over tables, chains and rules, so I can split my firewall configuration over multiple Nix files, eg. have a An example of what I'm thinking about: networking.firewall = {
ipRules = {
filter.INPUT = [
{ inputInterface = "eth2"; chain = "dmz"; }
];
filter.dmz = [
{ proto = "tcp"; destinationPort = [ 80 443 ]; target = "ACCEPT"; } # expands to 2 iptables rules
{ allowedTCPPorts = [ 80 443 ]; } # shortcut for the rule above
];
nat.POSTROUTING = [
{ source = "192.168.0.1/24"; outputInterface = "eth1"; target = "SNAT"; targetArgs = "--to 192.168.7.1"; }
];
mangle.PREROUTING = [
{ source = "10.0.0.1/24"; inputInterface = "eth0"; mark = "0"; target = "MARK"; targetArgs = "--set-mark 0x20"; }
];
};
ip6Rules = {
# ...
};
}; The proposal in #12940 would be a step in the right direction for iptables IMO. However, nftables has a much more expressive language than iptables, and abstracting all its features with Nix would be way overkill. networking.firewall.iptablesRules = {
filter.INPUT = [
"-i eth2 -j dmz"
];
filter.dmz = [
"-p tcp --dport 80 -j ACCEPT"
"-p tcp --dport 443 -j ACCEPT"
];
}; Or, for nftables: networking.firewall.nftables = {
sets.private = ''
{ 192.168.0.0/16, 10.0.0.0/8 }
'';
ipRules.filter.input = ''
ip saddr @private log drop
iif eth2 jump dmz
'';
ipRules.filter.dmz = ''
tcp dport { 80, 443 } accept
'';
ip6Rules.filter.input = "...";
}; The existing Let me know what you think. |
Should this be renamed to something like nftables-raw then? |
Something I do with my nftables ruleset files is to put |
On Fri, Sep 23, 2016 at 06:15:14PM -0700, Aneesh Agrawal wrote:
Having it twice doesn't hurt, and having it in ExecReload allows it to ensure Would it be better to update documentation to include "flush ruleset" and also Perhaps rulesetFile could not include it. |
I can't imagine a scenario where you wouldn't want atomic flushing. IMO, always automatically adding If there is a good reason not to use atomic flushing, I would like to hear about it. |
On Fri, Sep 23, 2016 at 08:48:13PM -0700, Aneesh Agrawal wrote:
What's the difference between adding it at build-time versus runtime?
Load fast crash hard? |
RemainAfterExit = true; | ||
ExecStart = "${pkgs.nftables}/bin/nft --file ${cfg.rulesetFile}"; | ||
ExecStop = "${pkgs.nftables}/bin/nft flush ruleset"; | ||
ExecReload = pkgs.writeScript "nftables-atomic-reload" '' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this version as used to reload nftables in archlinux clever:
https://git.archlinux.org/svntogit/packages.git/tree/trunk/nftables-reload?h=packages/nftables
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the advantage of this over how it works now? It would require adding another intermediate file just for reloading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, you also generate a script using writeScript to do the same, this could be replaced by a direct call to nft. This way no temporary files are required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, that sounds good. But why? Are temporarily files a big deal? Or is it just more clever to do it that way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/tmp/
could be full for example and this version is also easier to read:
ExecReload = pkgs.writeScript "nftables-atomic-reload" ''
#! ${pkgs.nft}/bin/nft -f
flush ruleset
include "${cfg.rulesetFile}"
''
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, yeah. That looks MUCH better. I assumed I had a quick one-liner using shell pipes to fix this, but re-reading my code it looks awful. The github issue didn't show the rest of the code, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
"ip_tables" | ||
"iptable_nat" | ||
"iptable_mangle" | ||
"iptable_filter" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if these modules are already loaded? (because a user switches from iptables to nftables)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nftables' NAT rules won't have any effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script will now error if ip_tables is loaded. Fixed.
description = | ||
'' | ||
The ruleset file to be used with nftables. Should be in a format that | ||
can be loaded using "/bin/nft -f". The ruleset is updated atomically. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should change these path descriptions somehow, since /bin/nft
will not be a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just 'nft'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@aneeshusa even if |
@grahamc security |
Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally it looks good but I really don't like some design choices...
default = | ||
'' | ||
table inet filter { | ||
# Block all IPv4/IPv6 input traffic except SSH. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ct state {established, related} accept
iifname lo accept
Block all IPv4/IPv6 input traffic except SSH.
Doesn't block all traffic... (only new traffic)
I don't like that port 22 is hard-coded.
reject | ||
} | ||
|
||
# Allow anything in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the output
chain?
chain output { | ||
type filter hook output priority 0; | ||
ct state invalid reject | ||
ct state {established, related} accept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally one would use new
here as well (at least with iptables
).
ct state invalid reject | ||
ct state {established, related} accept | ||
oifname lo accept | ||
accept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We accept everything (that hasn't an invalid
conntrack state) anyway.
''; | ||
example = | ||
'' | ||
# Check out http://wiki.nftables.org/ for better documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I generally prefer HTTPS links.
} | ||
} | ||
|
||
# Block all IPv6 traffic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably very common at the moment but I really think we shouldn't include this in an example.
The IAB expects that the IETF will stop requiring IPv4 compatibility in new or extended protocols. Future IETF protocol work will then optimize for and depend on IPv6.
can be loaded using "/bin/nft -f". The ruleset is updated atomically. | ||
''; | ||
}; | ||
networking.nftables.rulesetFile = mkOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great 😄
@primeos I saw your rfc, but I thought this pull request orthogonal. A generic firewall can still apply its rules using this module. Also my guess was that it would also take some time, while this module is already useful. I address your concerns regarding the default rule set. |
How about leaving it |
Agreed, I just hope we don't have to make any changes later that could break many setups.
Would probably be a good idea. Generally that obviously wouldn't be desirable but it would hopefully prevent SSH breakage (e.g. with But we should probably at least keep "# Check out https://wiki.nftables.org/ for better documentation." and fix the "# Allow anything in." comment.
It probably is (I initially feared this would cause too many problems later but I guess I was wrong since the module is pretty minimal and we can probably easily extend it). |
Suggestion for the example:
By far not a perfect example (I mainly copied the one from https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_workstation) but it should work (requires testing, unfortunately I can't do that ATM) but it should e.g. work with IPv6. But note that this can potentially still cause a lot of problems (it probably doesn't accept any ICMP packets -> outgoing ping, traceroute, etc. won't work). |
A potential fix (from the Arch-Wiki) for the ICMP packets (but they might already be covered by the conntrack state
I'd probably add |
changed in 6c36d9f |
@Mic92 Great, thanks 😄 |
Motivation for this change
This adds a simple service for setting up firewall rules using nftables instead of iptables. It includes a few examples, and it allows you to specify files so they can be generated from scripts.
Things done
(nix.useSandbox on NixOS,
or option
build-use-sandbox
innix.conf
on non-NixOS)
nix-shell -p nox --run "nox-review wip"
./result/bin/
)