137 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, pkgs, lib, ... }:
 | |
| with lib;
 | |
| let
 | |
|   cfg = config.networking.nftables;
 | |
| in
 | |
| {
 | |
|   ###### interface
 | |
| 
 | |
|   options = {
 | |
|     networking.nftables.enable = mkOption {
 | |
|       type = types.bool;
 | |
|       default = false;
 | |
|       description =
 | |
|         ''
 | |
|           Whether to enable nftables.  nftables is a Linux-based packet
 | |
|           filtering framework intended to replace frameworks like iptables.
 | |
| 
 | |
|           This conflicts with the standard networking firewall, so make sure to
 | |
|           disable it before using nftables.
 | |
| 
 | |
|           Note that if you have Docker enabled you will not be able to use
 | |
|           nftables without intervention. Docker uses iptables internally to
 | |
|           setup NAT for containers. This module disables the ip_tables kernel
 | |
|           module, however Docker automatically loads the module. Please see [1]
 | |
|           for more information.
 | |
| 
 | |
|           There are other programs that use iptables internally too, such as
 | |
|           libvirt.
 | |
| 
 | |
|           [1]: https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273
 | |
|         '';
 | |
|     };
 | |
|     networking.nftables.ruleset = mkOption {
 | |
|       type = types.lines;
 | |
|       example = ''
 | |
|         # Check out https://wiki.nftables.org/ for better documentation.
 | |
|         # Table for both IPv4 and IPv6.
 | |
|         table inet filter {
 | |
|           # Block all incomming connections traffic except SSH and "ping".
 | |
|           chain input {
 | |
|             type filter hook input priority 0;
 | |
| 
 | |
|             # accept any localhost traffic
 | |
|             iifname lo accept
 | |
| 
 | |
|             # accept traffic originated from us
 | |
|             ct state {established, related} accept
 | |
| 
 | |
|             # ICMP
 | |
|             # routers may also want: mld-listener-query, nd-router-solicit
 | |
|             ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
 | |
|             ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
 | |
| 
 | |
|             # allow "ping"
 | |
|             ip6 nexthdr icmp icmpv6 type echo-request accept
 | |
|             ip protocol icmp icmp type echo-request accept
 | |
| 
 | |
|             # accept SSH connections (required for a server)
 | |
|             tcp dport 22 accept
 | |
| 
 | |
|             # count and drop any other traffic
 | |
|             counter drop
 | |
|           }
 | |
| 
 | |
|           # Allow all outgoing connections.
 | |
|           chain output {
 | |
|             type filter hook output priority 0;
 | |
|             accept
 | |
|           }
 | |
| 
 | |
|           chain forward {
 | |
|             type filter hook forward priority 0;
 | |
|             accept
 | |
|           }
 | |
|         }
 | |
|       '';
 | |
|       description =
 | |
|         ''
 | |
|           The ruleset to be used with nftables.  Should be in a format that
 | |
|           can be loaded using "/bin/nft -f".  The ruleset is updated atomically.
 | |
|         '';
 | |
|     };
 | |
|     networking.nftables.rulesetFile = mkOption {
 | |
|       type = types.path;
 | |
|       default = pkgs.writeTextFile {
 | |
|         name = "nftables-rules";
 | |
|         text = cfg.ruleset;
 | |
|       };
 | |
|       description =
 | |
|         ''
 | |
|           The ruleset file to be used with nftables.  Should be in a format that
 | |
|           can be loaded using "nft -f".  The ruleset is updated atomically.
 | |
|         '';
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   ###### implementation
 | |
| 
 | |
|   config = mkIf cfg.enable {
 | |
|     assertions = [{
 | |
|       assertion = config.networking.firewall.enable == false;
 | |
|       message = "You can not use nftables with services.networking.firewall.";
 | |
|     }];
 | |
|     boot.blacklistedKernelModules = [ "ip_tables" ];
 | |
|     environment.systemPackages = [ pkgs.nftables ];
 | |
|     systemd.services.nftables = {
 | |
|       description = "nftables firewall";
 | |
|       before = [ "network-pre.target" ];
 | |
|       wants = [ "network-pre.target" ];
 | |
|       wantedBy = [ "multi-user.target" ];
 | |
|       reloadIfChanged = true;
 | |
|       serviceConfig = let
 | |
|         rulesScript = pkgs.writeScript "nftables-rules" ''
 | |
|           #! ${pkgs.nftables}/bin/nft -f
 | |
|           flush ruleset
 | |
|           include "${cfg.rulesetFile}"
 | |
|         '';
 | |
|         checkScript = pkgs.writeScript "nftables-check" ''
 | |
|           #! ${pkgs.runtimeShell} -e
 | |
|           if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
 | |
|             echo "Unload ip_tables before using nftables!" 1>&2
 | |
|             exit 1
 | |
|           else
 | |
|             ${rulesScript}
 | |
|           fi
 | |
|         '';
 | |
|       in {
 | |
|         Type = "oneshot";
 | |
|         RemainAfterExit = true;
 | |
|         ExecStart = checkScript;
 | |
|         ExecReload = checkScript;
 | |
|         ExecStop = "${pkgs.nftables}/bin/nft flush ruleset";
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| }
 | 
