From a0a4b91fafa7362bbdd3aac2e673722a75d98aa9 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Wed, 28 Mar 2018 23:32:21 -0500 Subject: [PATCH] roles/dch-gw: Configure the filter table The *filter* table is responsible for deciding which packets will be accepted and which will be rejected. It has three chains, which classify packets according to whether they are destined for the local machine (input), passing through this machine (forward) or originating from the local machine (output). The *dch-gw* role now configures all three chains in this table. For now, it defines basic rules, mostly based on TCP/UDP destination port: * Traffic destined for a service hosted by the local machine (DNS, DHCP, SSH), is allowed if it does not come from the Internet * Traffic passing through the machine is allowed if: * It is passing between internal networks * It is destined for a host on the FireMon network (VPN) * It was NATed to in internal host (marked 323) * It is destined for the Internet * Only DHCP, HTTP, and DNS are allowed to originate from the local machine This configuration requires an `internet_iface` variable, which indicates the name of the network interface connected to the Internet directly. --- group_vars/dch-gw/dch-network.yml | 35 +++++++++++++++++++++ host_vars/gw0/network.yml | 6 ++-- roles/dch-gw/defaults/main.yml | 2 ++ roles/dch-gw/files/inet-filter.nft | 7 +++++ roles/dch-gw/tasks/main.yml | 27 ++++++++++++++++ roles/dch-gw/templates/forward.nft.j2 | 23 ++++++++++++++ roles/dch-gw/templates/incoming.nft.j2 | 34 ++++++++++++++++++++ roles/dch-gw/templates/outgoing.nft.j2 | 31 ++++++++++++++++++ roles/dch-gw/templates/port-forwards.nft.j2 | 6 ++-- roles/dch-gw/templates/reject.nft.j2 | 6 ++++ 10 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 roles/dch-gw/files/inet-filter.nft create mode 100644 roles/dch-gw/templates/forward.nft.j2 create mode 100644 roles/dch-gw/templates/incoming.nft.j2 create mode 100644 roles/dch-gw/templates/outgoing.nft.j2 create mode 100644 roles/dch-gw/templates/reject.nft.j2 diff --git a/group_vars/dch-gw/dch-network.yml b/group_vars/dch-gw/dch-network.yml index a5cab49..8e68f32 100644 --- a/group_vars/dch-gw/dch-network.yml +++ b/group_vars/dch-gw/dch-network.yml @@ -40,6 +40,11 @@ dch_networks: router_iface: vlan254 +firemon_networks: +- 192.168.0.0/16 +- 172.28.33.0/24 + + nat_port_forwards: - protocol: tcp port: http @@ -62,3 +67,33 @@ nat_port_forwards: - protocol: udp port: ipsec-nat-t destination: 172.31.0.2 + + +allow_incoming: +- protocol: udp + port: domain +- protocol: tcp + port: domain +- protocol: udp + port: bootps +- protocol: tcp + port: ssh + + +allow_outgoing: +- protocol: udp + port: ntp +- protocol: udp + port: dhcpv6-server +- protocol: udp + port: bootps +- protocol: tcp + port: https +- protocol: tcp + port: http +- protocol: udp + port: domain +- protocol: tcp + port: domain + +trace_dropped: true diff --git a/host_vars/gw0/network.yml b/host_vars/gw0/network.yml index d284d55..5905020 100644 --- a/host_vars/gw0/network.yml +++ b/host_vars/gw0/network.yml @@ -1,6 +1,8 @@ +internet_iface: enp4s0 + network: ignore_interfaces: - - enp4s0 + - '{{ internet_iface }}' interfaces: - ifname: enp1s0 enabled: true @@ -66,7 +68,7 @@ dhcpcd_ntp_servers: false dhcpcd_noipv4: true dhcpcd_noipv6rs: true dhcpcd_interfaces: -- name: enp4s0 +- name: '{{ internet_iface }}' description: Internet config: - ipv4 diff --git a/roles/dch-gw/defaults/main.yml b/roles/dch-gw/defaults/main.yml index af62751..9091f79 100644 --- a/roles/dch-gw/defaults/main.yml +++ b/roles/dch-gw/defaults/main.yml @@ -1 +1,3 @@ nat_port_forwards: [] +allow_incoming: [] +trace_dropped: false diff --git a/roles/dch-gw/files/inet-filter.nft b/roles/dch-gw/files/inet-filter.nft new file mode 100644 index 0000000..cb92eff --- /dev/null +++ b/roles/dch-gw/files/inet-filter.nft @@ -0,0 +1,7 @@ +#! /usr/sbin/nft -f + +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; } +} diff --git a/roles/dch-gw/tasks/main.yml b/roles/dch-gw/tasks/main.yml index 4a7cc22..aa8a0e5 100644 --- a/roles/dch-gw/tasks/main.yml +++ b/roles/dch-gw/tasks/main.yml @@ -31,6 +31,33 @@ sysctl_file=/etc/sysctl.d/ip-forwarding.conf state=present +- name: ensure inet filter rules are configured + copy: + src=inet-filter.nft + dest=/etc/nftables/ruleset.d/10_inet-filter.nft + mode=0644 + notify: reload nftables +- name: ensure basic rules are defined + template: + src={{ item }}.nft.j2 + dest=/etc/nftables/ruleset.d/20_{{ item }}.nft + mode=0644 + with_items: + - incoming + - forward + - outgoing + notify: reload nftables +- name: ensure final reject rules are defined + template: + src=reject.nft.j2 + dest=/etc/nftables/ruleset.d/90_{{ item }}-reject.nft + mode=0644 + with_items: + - input + - forward + - output + notify: reload nftables + - name: ensure ipv4 nat rules are configured copy: src=ipv4-nat.nft diff --git a/roles/dch-gw/templates/forward.nft.j2 b/roles/dch-gw/templates/forward.nft.j2 new file mode 100644 index 0000000..792864e --- /dev/null +++ b/roles/dch-gw/templates/forward.nft.j2 @@ -0,0 +1,23 @@ +{#- vim: set sw=4 ts=4 sts=4 et : #} +table inet filter { + set firemon { + type ipv4_addr + flags interval + elements = { +{% for prefix in firemon_networks %} + {{ prefix }}, +{% endfor %} + } + } + + chain forward { + ct state established,related accept + iifname {{ dch_networks.guest.router_iface }} oif != {{ internet_iface }} drop + iif != {{ internet_iface }} oifname {{ dch_networks.guest.router_iface }} drop + iif != {{ internet_iface }} oif != {{ internet_iface }} counter accept + ip daddr @firemon counter accept + mark 323 counter accept + tcp dport smtp counter reject with icmpx type host-unreachable + oif {{ internet_iface }} accept + } +} diff --git a/roles/dch-gw/templates/incoming.nft.j2 b/roles/dch-gw/templates/incoming.nft.j2 new file mode 100644 index 0000000..e175bdc --- /dev/null +++ b/roles/dch-gw/templates/incoming.nft.j2 @@ -0,0 +1,34 @@ +{#- vim: set sw=4 ts=4 sts=4 et : #} +table inet filter { + set allow_tcp_in { + type inet_service + flags interval + elements = { +{% for item in allow_incoming if item.protocol|d('tcp') == 'tcp' %} + {{ item.port }}, +{% endfor %} + } + } + + set allow_udp_in { + type inet_service + flags interval + elements = { +{% for item in allow_incoming if item.protocol|d('tcp') == 'udp' %} + {{ item.port }}, +{% endfor %} + } + } + + chain input { + ct state established,related accept + iif lo accept + ip6 nexthdr ipv6-icmp accept + ip protocol icmp accept + udp sport dhcpv6-server counter accept + iif != {{ internet_iface }} tcp dport @allow_tcp_in ct state new counter accept + iif != {{ internet_iface }} udp dport @allow_udp_in ct state new counter accept + iif {{ internet_iface }} drop + pkttype != host drop + } +} diff --git a/roles/dch-gw/templates/outgoing.nft.j2 b/roles/dch-gw/templates/outgoing.nft.j2 new file mode 100644 index 0000000..67c14fe --- /dev/null +++ b/roles/dch-gw/templates/outgoing.nft.j2 @@ -0,0 +1,31 @@ +{#- vim: set sw=4 ts=4 sts=4 et : #} +table inet filter { + set allow_tcp_out { + type inet_service + flags interval + elements = { +{% for item in allow_outgoing if item.protocol|d('tcp') == 'tcp' %} + {{ item.port }}, +{% endfor %} + } + } + + set allow_udp_out { + type inet_service + flags interval + elements = { +{% for item in allow_outgoing if item.protocol|d('tcp') == 'udp' %} + {{ item.port }}, +{% endfor %} + } + } + + chain output { + ct state established,related accept + oif lo accept + ip6 nexthdr ipv6-icmp accept + ip protocol icmp accept + tcp dport @allow_tcp_out ct state new counter accept + udp dport @allow_udp_out ct state new counter accept + } +} diff --git a/roles/dch-gw/templates/port-forwards.nft.j2 b/roles/dch-gw/templates/port-forwards.nft.j2 index 567a039..afc0ff3 100644 --- a/roles/dch-gw/templates/port-forwards.nft.j2 +++ b/roles/dch-gw/templates/port-forwards.nft.j2 @@ -33,13 +33,13 @@ table ip nat { } chain prerouting { - ip daddr $outside_address dnat tcp dport map @tcp_forward - ip daddr $outside_address dnat udp dport map @udp_forward + ip daddr $outside_address meta mark set 323 dnat tcp dport map @tcp_forward + ip daddr $outside_address meta mark set 323 dnat udp dport map @udp_forward } chain postrouting { {% for item in nat_port_forwards %} - ip saddr @inside_networks ip daddr {{ item.destination }} {{ item.protocol|d('tcp') }} dport {{ item.port }} masquerade + ip saddr @inside_networks ip daddr {{ item.destination }} {{ item.protocol|d('tcp') }} dport {{ item.port }} meta mark set 323 masquerade {% endfor %} } } diff --git a/roles/dch-gw/templates/reject.nft.j2 b/roles/dch-gw/templates/reject.nft.j2 new file mode 100644 index 0000000..dc730b8 --- /dev/null +++ b/roles/dch-gw/templates/reject.nft.j2 @@ -0,0 +1,6 @@ +{#- vim: set sw=4 ts=4 sts=4 et : #} +table inet filter { + chain {{ item }} { + {% if trace_dropped %}nftrace set 1 {% endif %}counter reject with icmpx type host-unreachable + } +}