Netfilter

Linux Netfilter firewall is often configured via one of the following tools

  • iptables/ip6tables — administration tool for IPv4/IPv6 packet filtering and NAT

  • nft - Administration tool of the nftables framework for packet filtering and classification

  • firewall-cmd - firewalld command line client

Default policy

Allowing everything and denying a few ports you don’t need and who may be dangerous is not a good idea. For an effective network filtering, it’s far better to deny everything by default and allow what is needed. Each server and computer should have an active firewall to filter network traffic. This deny policy should be applied to incoming traffic and routed traffic but also to outgoing traffic.

Lateral movement refers to the techniques that cyber attackers, or threat actors, use to progressively move through a network as they search for the key data and assets that are ultimately the target of their attack campaigns. If each server and computer filter not only the incoming traffic but also the outgoing traffic, attackers will have less ways to move through your servers.

TCP MSS

The TCP protocol includes a mechanism for both ends of a connection to advertise the Maximum Segment Size (MSS) to be used by its peer when the connection is created. Each end uses the OPTIONS field in the TCP header to advertise a proposed MSS. If one endpoint does not provide its MSS, then 536 bytes is assumed for IPv4. Small MSS are bad for performance.

Technically, the MSS can be lower than 536. Some of the packets generated by nmap while doing OS fingerprinting are using such values.

osscan2.cc
84/* 8 options:
85 *  0~5: six options for SEQ/OPS/WIN/T1 probes.
86 *  6:   ECN probe.
87 *  7-12:   T2~T7 probes.
88 *
89 * option 0: WScale (10), Nop, MSS (1460), Timestamp, SackP
90 * option 1: MSS (1400), WScale (0), SackP, T(0xFFFFFFFF,0x0), EOL
91 * option 2: T(0xFFFFFFFF, 0x0), Nop, Nop, WScale (5), Nop, MSS (640)
92 * option 3: SackP, T(0xFFFFFFFF,0x0), WScale (10), EOL
93 * option 4: MSS (536), SackP, T(0xFFFFFFFF,0x0), WScale (10), EOL
94 * option 5: MSS (265), SackP, T(0xFFFFFFFF,0x0)
95 * option 6: WScale (10), Nop, MSS (1460), SackP, Nop, Nop
96 * option 7-11: WScale (10), Nop, MSS (265), T(0xFFFFFFFF,0x0), SackP
97 * option 12: WScale (15), Nop, MSS (265), T(0xFFFFFFFF,0x0), SackP

I have chosen to drop packets with a MSS lower than 536.

iptables

iptables and ip6tables are used to set up, maintain, and inspect the tables of IPv4 and IPv6 packet filter rules in the Linux kernel. Several different tables may be defined. Each table contains a number of built-in chains and may also contain user-defined chains.

On AlmaLinux 9, iptables-nft package brings nftables compatibility for iptables, arptables and ebtables.

A good policy is to deny everything by default.

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

Remember that filtering the OUTPUT traffic limits lateral movement.

https://www.frozentux.net/iptables-tutorial/images/tables_traverse.jpg

To filter anomalous traffic early, instead of doing it in INPUT and FORWARD from table filter, it’s possible to do it in PREROUTING from table mangle.

In this example, the external interface is eth7.

iptables -t mangle -A PREROUTING                -m conntrack --ctstate INVALID -j DROP
iptables -t mangle -A PREROUTING -p tcp         ! --tcp-flags ALL SYN -m conntrack --ctstate NEW,RELATED -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,RST SYN -m tcpmss ! --mss 536:65535 -j DROP
iptables -t mangle -A PREROUTING -p tcp --sport 0 -j DROP
iptables -t mangle -A PREROUTING -p udp --sport 0 -j DROP
iptables -t mangle -A PREROUTING -m set -i eth7 --set blocklist src             -j DROP

These rules are dropping

  • INVALID packets

  • NEW (or RELATED) tcp packets without SYN flag. AFAIK, there should be no SYN + URG and no SYN + PSH.

  • tcp packets containing a SYN and no RST with a MSS lower than 536.

  • tcp and udp packets with source port 0

  • packets from the blocklist ipset but only on the external interface.

# iptables -nvL PREROUTING -t mangle
Chain PREROUTING (policy ACCEPT 3655M packets, 9547G bytes)
 pkts bytes target     prot opt in     out     source               destination
 986K   48M DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           ctstate INVALID
48868 6843K DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           tcp flags:!0x3F/0x02 ctstate NEW
7108K  284M DROP       tcp  --  eth7   *       0.0.0.0/0            0.0.0.0/0           ctstate NEW,RELATED tcpmss match !536:65535
    4   192 DROP       udp  --  eth7   *       0.0.0.0/0            0.0.0.0/0           udp spt:0
    6   336 DROP       tcp  --  eth7   *       0.0.0.0/0            0.0.0.0/0           tcp spt:0
 2584 1285K DROP       all  --  eth7   *       0.0.0.0/0            0.0.0.0/0           match-set blocklist src

To prevent mapping of internal network topology, I drop packets with a Time-to-Live (TTL) lower than 5. Note that dropping INVALID packets, new TCP connexion without SYN and TCP SYN packet without the MSS option isn’t necessary if its done in the PREROUTING rules.

iptables -A FORWARD -m state --state INVALID                                                  -j DROP
iptables -A FORWARD -m state --state NEW,RELATED              -p tcp ! --tcp-flags ALL SYN    -j DROP
iptables -A FORWARD -m state --state NEW,RELATED              -p tcp ! --tcp-option 2         -j DROP
iptables -A FORWARD -i eth7 -m ttl --ttl-lt 5 -m limit --limit 4/s                            -j LOG --log-prefix "TTL too short "
iptables -A FORWARD -i eth7 -m ttl --ttl-lt 5 -j DROP
iptables -A FORWARD -m state --state ESTABLISHED,RELATED                                      -j ACCEPT
# iptables -nvL FORWARD
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
 ...
15871  991K LOG        all  --  eth7   *       0.0.0.0/0            0.0.0.0/0           TTL match TTL < 5 limit: avg 4/sec burst 5 LOG flags 0 level 4 prefix `TTL too short '
17856 1101K DROP       all  --  eth7   *       0.0.0.0/0            0.0.0.0/0           TTL match TTL < 5
3529M 9533G ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED

Similar rules can be created for INPUT.

iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp ! --tcp-flags ALL SYN -m state --state NEW -m limit --limit 4/s -j LOG --log-prefix "TCP INPUT without SYN "
iptables -A INPUT -p tcp ! --tcp-flags ALL SYN -m state --state NEW -j DROP
...
iptables -A INPUT -m limit --limit 4/s -j LOG --log-prefix "INPUT bad "
iptables -A INPUT -j DROP

It’s good practice to filter outgoing packets from your servers too.