본문 바로가기

nftables

nftables examples

source: https://wiki.nftables.org/wiki-nftables/index.php/Main_Page 의 Examples

이전 글: nftables 이란 / netfilter.org nftables project

다음 글: Port Knocking 활용



목차

  • Simple ruleset for a workstation
  • Simple ruleset for a server
  • Simple Ruleset for a Router
    • Basic routing firewall
    • Basic NAT
    • Typical workstation (separate IPv4 and IPv6)
    • Stateful router example
  • Bridge Filtering
  • Multiple NATs using nftables maps
  • Classic perimetral firewall example
  • Port knocking example
  • Classification to tc structure example
  • Using configuration management systems 
  • GeoIP matching

 

Simple ruleset for a workstation

fw.basic

hook input에 적용하므로 목적지가 시스템인 패켓에 대해 적용하는 룰셋입니다. 

# family ip (ipv4)에 대한 ruleset 입니다. 

table ip filter {
     chain input {
          # hook input에 적용하므로 system bound traffic 을 처리합니다. 
          type filter hook input priority 0;

          # 시스템에서 생성한 연결 트래픽은 허용합니다. 
          ct state established,related accept

          # 로컬 interface에서 발생한 트래픽을 허용합니다. iif는 input interface를 말합니다. 
          iif lo accept

          # 나머지 트래픽은 세고 차단합니다. 
          counter drop
     }
}

fw6.basic

hook input에 적용하므로 목적지가 시스템인 패켓에 대해 적용하는 룰셋입니다. 

# family ip6 (ipv6)에 대해 적용하는 ruleset입니다. 

table ip6 filter {
        chain input {
                 # hook input에 적용하므로 system bound 패켓에 적용합니다. 
                 type filter hook input priority 0;

                 # local interface에서 발생한 트래픽은 허용합니다. 
                 iif lo accept

                 # connection tracking을 한 결과 시스템에 시작한 연결은 허용합니다. 
                 ct state established,related accept

                 # ipv6 에서 이용하는 neighbor discovery 패켓을 허용합니다. 
                 icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept

                 # 나머지 패켓은 세고 차단합니다. 
                 counter drop
        }
}

Simple ruleset for a server

nftables.conf

#!/usr/sbin/nft -f

flush ruleset

# 집합 변수 SAFE_TRAFFIC_IPS를 정의합니다. 안전한 source IP address를 등록합니다. 
define SAFE_TRAFFIC_IPS = {
    x.x.x.x/xx,
    x.x.x.x/xx,
    x.x.x.x,
    x.x.x.x
}
define MGMT_IPS = y.y.y.y

table inet firewall {

    chain inbound {

        # 룰셋을 hook input에 적용합니다. 이하 룰에 맞지 않으면 모두 차단합니다. 
        type filter hook input priority 0; policy drop;

        # 이미 연결된 세션 관련 패켓은 모두 허용합니다 
        ct state established,related accept

        # 연결 상태가 정상이 아닌 것은 차단합니다. 
        ct state invalid drop

        # 로컬에서 발생한 트래픽은 모두 허용합니다. 
        iifname lo accept

        # 연결상태 점검을 위해 icmpv4, icmpv6, multicast igmpv4를 허용합니다. 
        # 다만 초당 4개씩만 허용합니다. 
        ip protocol icmp limit rate 4/second accept
        ip6 nexthdr ipv6-icmp limit rate 4/second accept
        ip protocol igmp limit rate 4/second accept

        # managment system IP address에서 이 시스템으로 향한 ssh 연결은 허용합니다. 
        ip saddr $MGMT_IPS tcp dport 22 accept

        # http, https 트래픽은 허용합니다. 
        tcp dport { http, https } accept
        udp dport { http, https } accept
        
        # 안전하다고 인정한 source IP address 오는 http, http 트래픽은 허용합니다. 
        # 아래 rule 2개는 위 2개 rule에 의해 의미 없습니다. 
        # tcp dport { http, https } ip saddr $SAFE_TRAFFIC_IPS accept
        # udp dport { http, https } ip saddr $SAFE_TRAFFIC_IPS accept

        # 필요하다면 이 시스템으로 향하는 트래픽의 목적지 포트를 알아내 등록하고 허용해 주면 됩니다. 
        # tcp dport 4000 accept
        # tcp dport 1313 accept

        # 필요하다면 차단된 나머지 트래픽 로그를 남기고 셉니다. 
        # log prefix "[nftables] Inbound Denied: " flags all counter drop

    }

    chain forward {

        # 이 시스템을 라우터가 아니므로 hook forward에서는 모든 트래픽을 차단합니다. 
        type filter hook forward priority 0; policy drop;

        # 필요하다면 차단된 나머지 트래픽 로그를 남기고 셉니다. 
        # log prefix "[nftables] Forward Denied: " flags all counter drop

    }

    chain outbound {

        # hook output에 적용. 즉 시스템에서 발생한 트래픽은 모두 외부로 전달합니다. 
        type filter hook output priority 0; policy accept;

    }

}

Simple Ruleset for a Router

Basic routing firewall

source: https://wiki.gentoo.org/wiki/Nftables/Examples

system 명령어로 ip forwarding을 먼저 활성화해 줍니다. (root # sysctl -w net.ipv4.ip_forward=1)

#!/sbin/nft -f

flush ruleset

table ip filter {
	# 시스템에서 발생하여 외부로 내보내는 트래픽은 모두 허용합니다. 
	chain output {
		type filter hook output priority 100; policy accept;
	}

	# local LAN interface에서서 시스템으로 향하는 트래픽은 허용합니다. 
    # WAN interface에서 시스템으로 향하는 트래픽은 차단합니다. 
	chain input {
		type filter hook input priority 0; policy accept;
		iifname "lan0" accept
		iifname "wan0" drop
	}

      # 입력이 lan0, 출력이 wan0인 트래픽은 허용합니다. 
      # 입력이 wan0, 출력이 lan0라도 여미 연결된 세션 트래픽은 허용합니다. 
      # 나머지는 모두 차단합니다. 
      # 즉 내부에서 외부에 접근하게만 하는 라우터/방화벽 설정입니다. 
	chain forward {
		type filter hook forward priority 0; policy drop;
		iifname "lan0" oifname "wan0" accept
		iifname "wan0" oifname "lan0" ct state related,established accept
	}
}

 

Basic NAT

source: https://wiki.gentoo.org/wiki/Nftables/Examples

외부로 연결되는 WAN interface의 IP address로 변환하여 나가는 예입니다. type nat 을 이용하며, 나가는 트래픽에 NAT을 적용하므로 hook postrouting에 적용합니다. 

#!/sbin/nft -f

flush ruleset

table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
	}

	# wan0로 나가는 모든 패켓의 source address를 wan0에 있는 IP address로 변환하여 내보냄
	# oifname: output interface name
    # masquerade: 가장, 꾸밈
    #
    chain postrouting {
		type nat hook postrouting priority 100; policy accept;
		oifname "wan0" masquerade
	}
}

만약 NAT address 용도로 public IP address를 확보한 것이 있다면 SNAT을 이용하여 그 address로 변환해서 내보내도 됩니다. 

#!/sbin/nft -f

flush ruleset

table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
	}

	# wan0로 나가는 모든 패켓의 source address를 wan0에 있는 IP address로 변환하여 내보냄
	# oifname: output interface name
    # masquerade: 가장, 꾸밈
    #
    chain postrouting {
		type nat hook postrouting priority 100; policy accept;
		oifname "wan0" snat to 1.2.3.4
	}
}

 

Typical workstation (separate IPv4 and IPv6)

source: https://wiki.gentoo.org/wiki/Nftables/Examples

일반 사용자가 사용하는 시스템에 적용할만한 설정입니다. IPv4, IPv6 를 분리하여 설정하였습니다. 필자는 IPv6에 대해서는 모두 drop 처리하였습니다. IPv4 룰셋은 1. 연결관련 이상한 것은 차단 2. 로컬에서 요청한 연결에 대한 응답은 허용 3. 로컬시스템에서 로컬시스템으로 향하는 것은 허용 4. 목적지 주소가 128.0.0.1/8이라도 iif가 로컬이 아니면 차단 5. ICMP는 허용 6. SSH 허용하고 나머지는 모두 차단합니다. 

#!/sbin/nft -f

flush ruleset

# ----- IPv4 -----
table ip filter {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state invalid counter drop comment "early drop of invalid packets"
		ct state {established, related} counter accept comment "accept all connections related to connections made by us"
		iif lo accept comment "accept loopback"
		iif != lo ip daddr 127.0.0.1/8 counter drop comment "drop connections to loopback not coming from loopback"
		ip protocol icmp counter accept comment "accept all ICMP types"
		tcp dport 22 counter accept comment "accept SSH"
		counter comment "count dropped packets"
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}
}

# ----- IPv6 -----
table ip6 filter {
	chain input {
		type filter hook input priority 0; policy drop;
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy drop;
	}
}

source: https://wiki.gentoo.org/wiki/Nftables/Examples

Stateful router example

source: https://wiki.gentoo.org/wiki/Nftables/Examples

 

필자가 만든 파일 2개입니다. 

salsal@r3:~$ cat /etc/nftables/ipv4-filter
table ip filter {
    chain input {
          type filter hook input priority 0; policy accept;
    }
    chain forward {
          type filter hook forward priority 0; policy accept;
    }
    chain output {
          type filter hook output priority 0; policy accept;
    }
}
salsal@r3:~$ cat /etc/nftables/ipv4-nat
table ip nat {
    chain prerouting {
          type nat hook prerouting priority 0; policy accept;
    }
    chain postrouting {
          type nat hook postrouting priority 100; policy accept;
    }
}

 

파일 nft.sh 내용입니다. 

#!/bin/bash

nft="/usr/sbin/nft";

${nft} flush ruleset;

export LAN_IN=enp0s8
export LAN_ML=enp0s9
export WAN=enp0s10
LAN_INLOCALNET=192.168.3.0/24
LAN_MLNET=10.52.0.0/14
MLIP=10.54.1.101
TORRENT_PORT_WAN=55414
TRACKER_TORRENT_PORT_WAN=4949
TORRENT_PORT_LAN=55413

MAC[2]=00:23:45:67:89:02
MAC[3]=00:23:45:67:89:03
MAC[4]=00:23:45:67:89:04
MAC[5]=00:23:45:67:89:05
MAC[6]=00:23:45:67:89:06
MAC[7]=00:23:45:67:89:07
MAC[8]=00:23:45:67:89:08
MAC[9]=00:23:45:67:89:09
MAC[10]=00:fe:dc:ba:98:10

${nft} -f /etc/nftables/ipv4-filter;
${nft} -f /etc/nftables/ipv4-nat;

# BANNED
${nft} add rule filter input meta iifname ${WAN} ip saddr 121.12.242.43 drop;

# Drop locals from internet
${nft} add rule filter input meta iifname ${WAN} ip saddr \
        { 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12 } drop;

# Drop invalid
${nft} add rule filter input ct state invalid drop;

${nft} add rule filter input meta iif lo ct state new accept;

${nft} add rule filter input meta iif ${LAN_ML} ip saddr ${LAN_MLNET} ct state new accept;
${nft} add rule filter input meta iif ${LAN_IN} ip saddr ${LAN_INLOCALNET} ct state new accept;

${nft} add rule filter input ip protocol tcp tcp dport \
        { ${TORRENT_PORT_LAN}, ${TORRENT_PORT_WAN}, \
                ${TRACKER_TORRENT_PORT_WAN} } ct state new accept;
${nft} add rule filter input ip protocol udp udp dport \
        { ${TORRENT_PORT_LAN}, ${TORRENT_PORT_WAN}, \
                ${TRACKER_TORRENT_PORT_WAN} } ct state new accept;

${nft} add rule filter input meta iifname ${WAN} ip protocol tcp ct state new tcp dport 80 accept;

# torrent port forwarding example
${nft} add rule nat prerouting meta iifname ${WAN} tcp dport ${TORRENT_PORT_LAN} \
        dnat 192.168.3.10:${TORRENT_PORT_LAN}

${nft} add rule filter forward meta iifname ${WAN} meta oif ${LAN_IN} ip daddr 192.168.3.10 \
        tcp dport ${TORRENT_PORT_LAN} ct state new accept;

${nft} add rule filter input ip saddr != ${LAN_INLOCALNET} ct state new drop;
${nft} add rule filter forward meta iif ${LAN_ML} ct state new drop;
${nft} add rule filter forward meta iifname ${WAN} ct state new drop;


${nft} add rule filter input ct state established,related accept;

${nft} add rule nat postrouting oif ${LAN_ML} ip saddr ${LAN_INLOCALNET} snat ${MLIP};
${nft} add rule nat postrouting oifname ${WAN} ip saddr ${LAN_INLOCALNET} masquerade;

${nft} add rule filter forward ct state established,related accept;

# Give internet access to internal LAN addresses
for i in {2..10}
do
if true > /dev/null;
then
        ${nft} add rule filter forward ether saddr ${MAC[$i]} ip saddr 192.168.3.$i \
                ct state new accept;
        echo ACCEPT 192.168.3.$i ALL;
else
        ${nft} add rule filter forward ether saddr ${MAC[$i]} ip saddr 192.168.3.$i \
                meta oif ${LAN_ML} ct state new accept;
        echo ACCEPT 192.168.3.$i ${LAN_ML};
fi
done



# Policies
${nft} add rule filter input drop;
${nft} add rule filter forward drop;
${nft} add rule filter output accept;

bash nft.sh를 수행하여 룰셋을 적용하고 난 결과입니다. 

salsal@r3:~$ sudo bash nft.sh
ACCEPT 192.168.3.2 ALL
ACCEPT 192.168.3.3 ALL
ACCEPT 192.168.3.4 ALL
ACCEPT 192.168.3.5 ALL
ACCEPT 192.168.3.6 ALL
ACCEPT 192.168.3.7 ALL
ACCEPT 192.168.3.8 ALL
ACCEPT 192.168.3.9 ALL
ACCEPT 192.168.3.10 ALL
salsal@r3:~$ sudo nft list ruleset
table ip filter {
	chain input {
		type filter hook input priority 0; policy accept;
		iifname "enp0s10" ip saddr 121.12.242.43 drop
		iifname "enp0s10" ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } drop
		ct state invalid drop
		iif "lo" ct state new accept
		iif "enp0s9" ip saddr 10.52.0.0/14 ct state new accept
		iif "enp0s8" ip saddr 192.168.3.0/24 ct state new accept
		tcp dport { munin, 55413, 55414 } ct state new accept
		udp dport { munin, 55413, 55414 } ct state new accept
		iifname "enp0s10" ct state new tcp dport http accept
		ip saddr != 192.168.3.0/24 ct state new drop
		ct state established,related accept
		drop
	}

	chain forward {
		type filter hook forward priority 0; policy accept;
		iifname "enp0s10" oif "enp0s8" ip daddr 192.168.3.10 tcp dport 55413 ct state new accept
		iif "enp0s9" ct state new drop
		iifname "enp0s10" ct state new drop
		ct state established,related accept
		ether saddr 00:23:45:67:89:02 ip saddr 192.168.3.2 ct state new accept
		ether saddr 00:23:45:67:89:03 ip saddr 192.168.3.3 ct state new accept
		ether saddr 00:23:45:67:89:04 ip saddr 192.168.3.4 ct state new accept
		ether saddr 00:23:45:67:89:05 ip saddr 192.168.3.5 ct state new accept
		ether saddr 00:23:45:67:89:06 ip saddr 192.168.3.6 ct state new accept
		ether saddr 00:23:45:67:89:07 ip saddr 192.168.3.7 ct state new accept
		ether saddr 00:23:45:67:89:08 ip saddr 192.168.3.8 ct state new accept
		ether saddr 00:23:45:67:89:09 ip saddr 192.168.3.9 ct state new accept
		ether saddr 00:fe:dc:ba:98:10 ip saddr 192.168.3.10 ct state new accept
		drop
	}

	chain output {
		type filter hook output priority 0; policy accept;
		accept
	}
}
table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		iifname "enp0s10" tcp dport 55413 dnat to 192.168.3.10:55413
	}

	chain postrouting {
		type nat hook postrouting priority 100; policy accept;
		oif "enp0s9" ip saddr 192.168.3.0/24 snat to 10.54.1.101
		oifname "enp0s10" ip saddr 192.168.3.0/24 masquerade
	}
}

위 룰셋을 리부팅 뒤에도 유지하려면 다음과 같이 하면 됩니다. 리부팅하여 제대로 읽어 들이는지 확인합니다. 

  • 현재의 룰셋을 저장합니다. 
  • /etc/nftables.conf 에 저장한 파일을 읽어 로드하라고 합니다. 파일 마지막에 (include "/etc/nftables.rules")를 추가하면 됩니다. 
root@r3:~# nft list ruleset > /etc/nftables.rules
root@r3:~# cat /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0;
	}
	chain forward {
		type filter hook forward priority 0;
	}
	chain output {
		type filter hook output priority 0;
	}
}

include "/etc/nftables.rules"

Bridge Filtering

source: https://wiki.nftables.org/wiki-nftables/index.php/Bridge_filtering

Bridge chain type and hooks

family bridge에서 이용할 수 있는 chain type은 filter 1개이며, 이용할 수 있는 hook은 5개 입니다. 

  • prerouting: forward database에 도달하기 전 hook입니다. 
  • input: bridge interface에 IP address가 있고, 그 곳으로 향하는 packet이 있을 경우 이 hook을 지납니다. 
  • forward: bridge 포트에서 다른 bridge 포트로 가는 패켓에 대해 적용하는 hook입니다. 
  • output: 시스템에서 발생한 bridge 패켓이 지나는 hook 입니다. 
  • postrouting: hook forward나 hook output을 지나서 나가는 패켓에 적용하는 hook 입니다. 

Stateless filtering

다음은 tcp/ssh 패켓을 허용하는 bridge 룰입니다. 

%  nft add rule bridge filter forward ether type ip tcp dport 22 accept

다음은 arp 패켓을 허용하는 bridge 룰입니다. 

% nft add rule bridge filter forward ether type arp accept

 2개 룰을 적용한 룰셋입니다. 

salsal@r3:~$ sudo nft list ruleset
table bridge filter {
	chain forward {
		type filter hook forward priority 0; policy accept;
		tcp dport ssh accept
		ether type arp accept
	}
}
salsal@r3:~$

Stateful filtering

family bridge에 대해 connection tracking은 Linux kernel 5.3부터 지원됩니다. Statefule filtering을 하려면 룰셋에서 connection state 만 추가로 확인하면 됩니다. 

아래 예는 다음과 같은 상황을 가정한 stateful bridge filtering 입니다. 

  • enp0s8, enp0s9은 내부를 향하는 포트입니다. 
  • enp0s8에 desktop PC가 있습니다
  • enp0s9에 web server가 있습니다. 
  • 외부로 향하는 uplink는 enp0s10 입니다. 
  • desktop을 향해 어떤 연결 요청도 허용하지 않습니다. 
  • web server를 행해서는 http, ssh 연결만 허용합니다. 
  • 단 desktop, web server는 외부로 어떤 연결이든 요청할 수 있습니다. 
salsal@r3:~$ sudo nft add table bridge filter
salsal@r3:~$ sudo nft add chain bridge filter forward '{type filter hook forward priority 0; policy drop;}'
salsal@r3:~$ sudo nft add rule bridge filter forward ct state established,related accept
salsal@r3:~$ sudo nft add rule bridge filter forward iif {enp0s8, enp0s9} oif enp0s10 ct state new accept
salsal@r3:~$ sudo nft 'add rule bridge filter forward iif enp0s10 oif enp0s9 ct state new tcp dport {22,80,443} accept'
salsal@r3:~$ sudo nft list ruleset
table bridge filter {
	chain forward {
		type filter hook forward priority 0; policy drop;
		ct state established,related accept
		iif { "enp0s8", "enp0s9" } oif "enp0s10" ct state new accept
		iif "enp0s10" oif "enp0s8" ct state new tcp dport { ssh, http, https } accept
	}
}

Multiple NATs using nftables maps

source: https://wiki.nftables.org/wiki-nftables/index.php/Multiple_NATs_using_nftables_maps

 

아래 iptables 설정이 있다고 가정합니다. 

% iptables -t nat -A PREROUTING -p tcp --dport 1000 -j DNAT --to-destination 1.1.1.1:1234
% iptables -t nat -A PREROUTING -p udp --dport 2000 -j DNAT --to-destination 2.2.2.2:2345
% iptables -t nat -A PREROUTING -p tcp --dport 3000 -j DNAT --to-destination 3.3.3.3:3456

이것을 nftables maps을 이용해 설정해 봅니다. 

% nft add rule nat prerouting dnat to \
      tcp dport map { 1000 : 1.1.1.1, 2000 : 2.2.2.2, 3000 : 3.3.3.3} \
      : tcp dport map { 1000 : 1234, 2000 : 2345, 3000 : 3456 }

 

아래 iptables 설정이 있다고 가정합니다. 

% iptables -t nat -A POSTROUTING -s 192.168.1.1 -j SNAT --to-source 1.1.1.1
% iptables -t nat -A POSTROUTING -s 192.168.2.2 -j SNAT --to-source 2.2.2.2
% iptables -t nat -A POSTROUTING -s 192.168.3.3 -j SNAT --to-source 3.3.3.3

이것을 nftables maps을 이용해 설정해 봅니다. 

% nft add rule nat postrouting snat to \
      ip saddr map { 192.168.1.1 : 1.1.1.1, 192.168.2.2 : 2.2.2.2, 192.168.3.3 : 3.3.3.3 }

salsal@r3:~$ sudo nft list ruleset
table ip nat {
	chain postrouting {
		snat to ip saddr map { 192.168.1.1 : 1.1.1.1, 192.168.2.2 : 2.2.2.2, 192.168.3.3 : 3.3.3.3 }
	}
}

 

Multiple NAT mapping with address and port

source: https://wiki.nftables.org/wiki-nftables/index.php/Multiple_NATs_using_nftables_maps

 

named map을 이용하는 방법입니다.  source에 있는 내용에 따라 아래를 시도하면 계속 에러 메시지를 받아 뭐가 문제인지 알 수 없습니다.

% nft add map nat foo { type inet_service : ipv4_addr . inet_service ; }
% nft add element nat foo { \
    1100 : 192.168.1.2 . 5061, \
    1101 : 192.168.1.3 . 5061, \
    1400 : 192.168.1.4 . 5061 \
}
% nft add rule nat pre ip protocol udp dnat ip addr . port to udp dport map @foo


salsal@r3:~$ sudo nft add map nat foo '{ type inet_service : ipv4_addr . inet_service ; }'
Error: unqualified mapping data type specified in map definition
add map nat foo { type inet_service : ipv4_addr . inet_service ; }
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

salsal@r3:~$ sudo nft add map nat foo '{ type inet_service : ipv4_addr . inet_service ; }'
Error: unqualified mapping data type specified in map definition
add map nat foo { type inet_service : ipv4_addr . inet_service ; }
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
salsal@r3:~$ sudo nft add element nat foo '{     1100 : 192.168.1.2 . 5061,     1101 : 192.168.1.3 . 5061,     1400 : 192.168.1.4 . 5061 }'
Error: Can't parse symbolic invalid expressions
add element nat foo {     1100 : 192.168.1.2 . 5061,     1101 : 192.168.1.3 . 5061,     1400 : 192.168.1.4 . 5061 }
                                 ^^^^^^^^^^^

anonymous map을 이용하는 방법입니다. source에 있는 내용에 따라 아래를 시도하면 계속 에러 메시지를 받아 뭐가 문제인지 알 수 없습니다.

% nft add rule nat pre ip protocol udp dnat ip addr . port to udp dport map { \
    1100 : 192.168.1.2 . 5061, \
    1101 : 192.168.1.3 . 5061, \
    1400 : 192.168.1.4 . 5061 \
}

salsal@r3:~$ sudo nft 'add rule nat prerouting ip protocol udp dnat \
    ip ipv4_addr . inet_service to udp dport map \
    { 1100:192.168.1.2 . 5061, 1101:192.168.1.3 . 5061}'
Error: syntax error, unexpected to
add rule nat prerouting ip protocol udp dnat \
    ip ipv4_addr . inet_service to udp dport \
    map { 1100:192.168.1.2 . 5061, 1101:192.168.1.3 . 5061}

참고로 아래 map은 성공하였습니다만 ...

table ip nat {
	map foo {
		type inet_service : ipv4_addr
	}
}

Classic perimetral firewall example

source: https://wiki.nftables.org/wiki-nftables/index.php/Classic_perimetral_firewall_example

Internet, DMZ, LAN에 연결되어 있는 라우터를 가정한 설정예입니다. 특별한 사항은

  • 룰을 그룹(local, forward)으로 분리해서 다른 파일에 저장해 관리합니다. 
  • address를 set을 이용해 관리합니다. 
  • address를 숫자로 기억하기 어려우니 definition을 이용하여 관리합니다. 
  • jump를 이용하여 공통 룰을 적용합니다. 

나머지는 원문 source를 참조하면 됩니다. 

Port knocking example

source: https://wiki.nftables.org/wiki-nftables/index.php/Port_knocking_example

포트 노킹은 비밀 노크를 말합니다. 특정한 port sequence를 입력하면 출입을 허용하거나 닫는 것을 말합니다. 그런데 port sequence는 어떤 이유로든 노출 가능성이 있으므로, 사용하지 않는 것을 권장합니다. 그러나 특수한 상황에서는 임시로 또는 보완적인 수단으로 이용할 수는 있습니다. 

 

port knocking을 이용하는 방법은 Port Knocking 활용을 참고하시기 바랍니다. 

Classification to tc structure example

source: https://wiki.nftables.org/wiki-nftables/index.php/Classification_to_tc_structure_example

 

tc: traffic control을 말합니다. Linux 시스템에서 트래픽을 제어하는 도구이며, shaping, policing, scheduling, dropping 기능을 제공합니다. nftables은 tc filter 룰을 대신할 수 있습니다. 필자도 tc syntax와 nftables syntax에 익숙하지 않아 source에 있는 내용을 다 이해할 수 없습니다만, 얼핏 보기에 nftables syntax가 훨씬 이해하기 편해 보입니다. 트래픽을 분류하고, 제어할 때 활용할 수 있는 수단입니다. 

참고: https://skylit.tistory.com/217

Using configuration management systems

source: https://wiki.nftables.org/wiki-nftables/index.php/Using_configuration_management_systems

 

configuration management software인 puppet을 이용해 nftables을 관리하는 예입니다. 즉 nftables 룰을 puppet과 같은 시스템에 저장해 두고, 그것을 시스템에 적용하는 것입니다. 현재 필자가 관심있는 주제는 아니므로 skip 합니다. 

 

GeoIP matching

source: https://wiki.nftables.org/wiki-nftables/index.php/GeoIP_matching

또 다른 참고글: [IPTables] 12. IPTables에 GeoIP를 설치해보자. [GeoIP ][CentOS 6/CentOS 7]

 

git을 먼저 설치합니다. 그리고 나서 nftables-geoip repo를 git clone 합니다. 폴더 nftables-geoip 아래 여러 파일을 볼 수 있습니다. 

salsal@r3:~$ sudo apt install git

salsal@r3:~$ git clone https://github.com/pvxe/nftables-geoip
Cloning into 'nftables-geoip'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 42 (delta 22), reused 34 (delta 14), pack-reused 0
Unpacking objects: 100% (42/42), done.

salsal@r3:~$ ls -al nftables-geoip
total 76
drwxrwxr-x  4 salsal salsal  4096  8월 11 12:17 .
drwxr-xr-x 16 salsal salsal  4096  8월 11 12:17 ..
drwxrwxr-x  8 salsal salsal  4096  8월 11 12:17 .git
-rw-rw-r--  1 salsal salsal 18092  8월 11 12:17 LICENSE
-rw-rw-r--  1 salsal salsal  3988  8월 11 12:17 README.md
drwxrwxr-x  2 salsal salsal  4096  8월 11 12:17 examples
-rw-rw-r--  1 salsal salsal 20562  8월 11 12:17 location.csv
-rwxrwxr-x  1 salsal salsal 11989  8월 11 12:17 nft_geoip.py

 

폴더 nftables-geoip로 이동하여 ./nft_geoip.py --file-location location.csv --download 를 수행합니다. 그리고 파일을 살펴보면 dbip.csv와 다른 많은 파일을 볼 수 있습니다. 

salsal@r3:~/nftables-geoip$ ./nft_geoip.py --file-location location.csv --download
Downloading db-ip.com geoip csv file...
Writing country definition files...
Writing nftables maps (geoip-ipv{4,6}.nft)...
Done!
salsal@r3:~/nftables-geoip$ ls -al
total 40072
drwxrwxr-x  4 salsal salsal     4096  8월 11 12:22 .
drwxr-xr-x 16 salsal salsal     4096  8월 11 12:17 ..
drwxrwxr-x  8 salsal salsal     4096  8월 11 12:17 .git
-rw-rw-r--  1 salsal salsal    18092  8월 11 12:17 LICENSE
-rw-rw-r--  1 salsal salsal     3988  8월 11 12:17 README.md
-rw-rw-r--  1 salsal salsal 19083517  8월 11 12:22 dbip.csv
drwxrwxr-x  2 salsal salsal     4096  8월 11 12:17 examples
-rw-rw-r--  1 salsal salsal      956  8월 11 12:22 geoip-def-africa.nft
-rw-rw-r--  1 salsal salsal     8419  8월 11 12:22 geoip-def-all.nft
-rw-rw-r--  1 salsal salsal      902  8월 11 12:22 geoip-def-americas.nft
-rw-rw-r--  1 salsal salsal       15  8월 11 12:22 geoip-def-antarctica.nft
-rw-rw-r--  1 salsal salsal      808  8월 11 12:22 geoip-def-asia.nft
-rw-rw-r--  1 salsal salsal      810  8월 11 12:22 geoip-def-europe.nft
-rw-rw-r--  1 salsal salsal      461  8월 11 12:22 geoip-def-oceania.nft
-rw-rw-r--  1 salsal salsal  9800241  8월 11 12:22 geoip-ipv4.nft
-rw-rw-r--  1 salsal salsal 12029927  8월 11 12:22 geoip-ipv6.nft
-rw-rw-r--  1 salsal salsal    20562  8월 11 12:17 location.csv
-rwxrwxr-x  1 salsal salsal    11989  8월 11 12:17 nft_geoip.py

nft_geoip.py가 수행하는 일은 다음과 같습니다. 

  • download.db-ip.com 에 접근하여 IP address별 국가를 알려주는 파일을 다운로드하여 그 결과를 dbip.csv에 저장합니다. 
  • locations.csv 파일을 읽어 나라이름, 대륙, 나라이름에 대응하는 2자리 이름 사전을 만듭니다. 
  • geoip-def-all.nft에는 2자리 나라이름별 code, 그리고 대륙 전체 정보를 저장합니다. geoip-def-* 나머지는 대륙별로 동일한 정보를 저장합니다. 
  • geoip-ipv4.nft에는 IPv4 address별 2자리 나라 이름을 저장하고 있습니다. 
  • geoip-ipv6.nft에는 IPv6 address별 2자리 나라 이름을 저장하고 있습니다. 

nftables에서 geoip를 이용한 예는 examples\accounting.nft를 살펴보면 됩니다. 

salsal@r3:~/nftables-geoip$ more examples/accounting.nft 
#!/usr/sbin/nft -f

# Accounting of incoming Spanish traffic, ipv4 and ipv6

flush ruleset

table inet filter {

	include "./geoip-def-all.nft"
	include "./geoip-ipv4.nft"
	include "./geoip-ipv6.nft"

	chain geoip-mark-input {
		type filter hook input priority -1; policy accept;

		meta mark set ip saddr map @geoip4
		meta mark set ip6 saddr map @geoip6
	}

	chain input {
		type filter hook input priority 0; policy accept;

		mark $ES counter comment "incoming-ES"
	} 
}

내용을 살펴보면 

  • 패켓이 들어오면 source ip address를 확인하고 geoip mapping 정보에 따라 국가를 마킹합니다. 
  • 마킹한 결과가 ES(에스파냐, 스페인)이면 counting을 합니다. 

만약 해당 국가 IP address를 차단하고 싶으면 drop을 선언하면 됩니다. 

 

필자는 sudo nft -f accounting.nft를 맥북 Virtualbox VM instance에서 실행했습니다 (Memory 4GB). 그런데 많은 양의 geoip를 load하다 보니 끝이나지 않네요. 그래서 VM instance의 메모리를 8GB로 변경하고 실행했습니다. 그 결과 메모리에 올라온 ruleset 을 다음과 같이 확인할 수 있습니다. 

 

salsal@r3:~/nftables-geoip/examples$ sudo nft list ruleset inet | head -100
table inet filter {
	map continent_code {
		type mark : mark
		flags interval
		elements = { 0x00000004 : 0x00000002, 0x00000008 : 0x00000003, 0x0000000a : 0x00000006, 0x0000000c : 0x00000001, 0x00000010 : 0x00000005,
			     0x00000014 : 0x00000003, 0x00000018 : 0x00000001, 0x0000001c : 0x00000004, 0x0000001f : 0x00000002, 0x00000020 : 0x00000004,
			     0x00000024 : 0x00000005, 0x00000028 : 0x00000003, 0x0000002c : 0x00000004, 0x00000030 : 0x00000002, 0x00000032 : 0x00000002,
			     0x00000033 : 0x00000002, 0x00000034 : 0x00000004, 0x00000038 : 0x00000003, 0x0000003c : 0x00000004, 0x00000040 : 0x00000002,
			     0x00000044 : 0x00000004, 0x00000046 : 0x00000003, 0x00000048 : 0x00000001, 0x0000004a : 0x00000004, 0x0000004c : 0x00000004,
			     0x00000054 : 0x00000004, 0x00000056 : 0x00000001, 0x0000005a : 0x00000005, 0x0000005c : 0x00000004, 0x00000060 : 0x00000002,
                 .....  삭제 .... 
			     0x00000314 : 0x00000001, 0x00000318 : 0x00000002, 0x0000031b : 0x00000002, 0x0000031c : 0x00000004, 0x0000031e : 0x00000005,
			     0x00000320 : 0x00000001, 0x00000324 : 0x00000003, 0x00000327 : 0x00000003, 0x00000332 : 0x00000001, 0x0000033a : 0x00000003,
			     0x0000033f : 0x00000003, 0x00000340 : 0x00000003, 0x00000341 : 0x00000003, 0x00000342 : 0x00000001, 0x00000348 : 0x00000004,
			     0x00000352 : 0x00000004, 0x00000356 : 0x00000001, 0x0000035a : 0x00000004, 0x0000035c : 0x00000002, 0x0000035e : 0x00000004,
			     0x0000036c : 0x00000005, 0x00000372 : 0x00000005, 0x00000377 : 0x00000002, 0x0000037e : 0x00000001 }
	}

	map geoip4 {
		type ipv4_addr : mark
		flags interval
		elements = { 1.0.0.0/24 : 0x00000024, 1.0.1.0-1.0.3.255 : 0x0000009c,
			     1.0.4.0/22 : 0x00000024, 1.0.8.0/21 : 0x0000009c,
			     1.0.16.0/20 : 0x00000188, 1.0.32.0/19 : 0x0000009c,
			     1.0.64.0/18 : 0x00000188, 1.0.128.0/17 : 0x000002fc,
			     1.1.0.0/24 : 0x0000009c, 1.1.1.0/24 : 0x00000024,
			     1.1.2.0-1.1.63.255 : 0x0000009c, 1.1.64.0/18 : 0x00000188,
			     1.1.128.0/17 : 0x000002fc, 1.2.0.0-1.2.2.255 : 0x0000009c,
			     1.2.3.0/24 : 0x00000024, 1.2.4.0-1.2.127.255 : 0x0000009c,
			     1.2.128.0/17 : 0x000002fc, 1.3.0.0/16 : 0x0000009c,
			     1.4.0.0/24 : 0x00000024, 1.4.1.0-1.4.127.255 : 0x0000009c,
			     1.4.128.0/17 : 0x000002fc, 1.5.0.0/16 : 0x00000188,
			     1.6.0.0/15 : 0x00000164, 1.8.0.0/16 : 0x0000009c,
			     1.9.0.0/16 : 0x000001ca, 1.10.0.0-1.10.9.255 : 0x0000009c,
			     1.10.10.0/24 : 0x00000024, 1.10.11.0-1.10.127.255 : 0x0000009c,
			     1.10.128.0/17 : 0x000002fc, 1.11.0.0/16 : 0x0000019a,
			     1.12.0.0/14 : 0x0000009c, 1.16.0.0/18 : 0x0000019a,
			     1.16.64.0-1.18.115.255 : 0x00000024, 1.18.116.0-1.19.255.255 : 0x0000019a,
			     1.20.0.0/16 : 0x000002fc, 1.21.0.0/16 : 0x00000188,
			     1.22.0.0/15 : 0x00000164, 1.24.0.0/13 : 0x0000009c,
			     1.32.0.0/17 : 0x000001ca, 1.32.128.0-1.32.194.255 : 0x000002be,
			     1.32.195.0-1.32.197.255 : 0x00000158, 1.32.198.0/24 : 0x000002be,
			     1.32.199.0/24 : 0x00000158, 1.32.200.0/23 : 0x000002be,
			     1.32.202.0/24 : 0x00000348, 1.32.203.0-1.32.204.128 : 0x000002be,
			     1.32.204.129-1.32.205.255 : 0x00000158, 1.32.206.0/24 : 0x00000074,
			     1.32.207.0/24 : 0x000002be, 1.32.208.0/23 : 0x0000009e,
			     1.32.210.0/24 : 0x00000158, 1.32.211.0/24 : 0x0000009e,
			     1.32.212.0/24 : 0x00000158, 1.32.213.0/24 : 0x0000009e,
			     1.32.214.0/23 : 0x00000158, 1.32.216.0/23 : 0x0000019a,
			     1.32.218.0/24 : 0x000002be, 1.32.219.0/24 : 0x00000158,

8GB 메모리를 가진 시스템에서 위 ruleset을 로딩한 뒤에 메모리 사용량을 살펴봤습니다. 로딩하는데 시간이 오래 걸리는 것이지 메모리를 많이 차지하는 것은 아니다라는 것을 알았습니다. 

 

 

 

이전 글: nftables 이란 / netfilter.org nftables project

다음 글: Port Knocking 활용

'nftables' 카테고리의 다른 글

Port Knocking 활용  (0) 2021.08.10
Output text modifier  (0) 2021.08.06
룰셋 Ruleset debug/tracing  (0) 2021.08.06
Scripting  (0) 2021.08.06
룰셋 갱신 지켜보기 / Monitoring ruleset updates  (0) 2021.08.06