Commit 34cbc9d6 authored by Branko Mikić's avatar Branko Mikić
Browse files

BugFixes and various changes

~ BugFix! getLinkMAC() accidentally checked the hardwired 'eth2' interface regardless of the argument. This has been fixed by using the argument instead.
~ obtainLinkOfIP() and is_local_IP() functions added.
~ BugFix! On RESET the raw table rulesets were never emptied and flushed. This has been fixed.
~ DHCP negotiation can now differentiate between initial broadcast requests and follow up requests to extend the lease.
For everyone wondering why a DHCP request via broadcast like 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP ... is never seen by netfilter on any outgoing chain on any table that's because most dhcp daemons are using PF_PACKET sockets for the _first_, _initial_ request which bypasses the whole netfilter.
To keep things more confusing this doesn't apply for any 'ordinary' follow up dhcp packet (only the first one!)
See https://unix.stackexchange.com/questions/447440/ufw-iptables-not-blocking-dhcp-udp-port-67 for details.
~ BugFix! In DHCP chain any returning follow-up reply packet was filtered by source port set to DHCP server port (68) which is obviously wrong here. This has been fixed by accepting reply packets with any source port number.
Attention!
For such DHCP follow-up packets additionally the destination address type now filters to LOCAL and limits it's IP address only to be received on the configured, corresponding interface.
~ BugFix! Limiter is too aggressive. This has been fixed by mitigating the limiter's filter for common http(s) and email traffic.
Additionally the hashlimit has been set to a tighter setting of >8/day for every other traffic which is explicitly configured to use the LIMITER chain.
~ Attention! BLOCK chain has been renamed to QUASH. All packets which were send to BLOCK are now sent to QUASH (!)
Alot of iptables log messages in the format "[BLOCK] (...)" have been chaged for the QUASH, ANTI-FLOOD, LIMITER, ... chains to avoid confusion wether blocking is intended or just notifying a packet loss. Some are now in the format: "[CHAIN_NAME] ACTION" eg: "[LIMITER] drop" or "[DHCP] packet loss".
~ ALLOW_STATEFUL_PACKETS now checks IP address arguments to be an IP address on a local interface of the host.
~ BugFix! The USER_OUT chain of ALLOW_STATEFUL_PACKETS now allows NEW, RELATED and ESTABLISHED packets to leave for interfaces with a local IP address only (!)
Attention!
This doesn't apply when using ALLOW_STATEFUL_PACKETS with an interface arg (!)
~ LIST now shows iptables dumps for both protocols IPv(4|6)
parent 19a91a97
......@@ -170,7 +170,7 @@ getLinkMAC()
printf "%s" $(cat /sys/class/net/$1/address)
# tricky! regardless wether it's true or false >> just return the return code
[ $(cat /sys/class/net/eth2/addr_len) -gt 0 ] ; return $?
[ $(cat /sys/class/net/$1/addr_len) -gt 0 ] ; return $?
}
###
......@@ -194,6 +194,22 @@ obtainIPs()
done
}
###
### $1; IP address (either IPv4 or IPv6)
###
### Returns a string with the name of a link the given
### IP is configured on otherwise an empty string.
###
obtainLinkOfIP()
{
echo $(ip -br addr show | grep $1 | grep -oP "^\w+(?=@)*");
}
is_local_IP()
{
[ -f /sys/class/net/$(obtainLinkOfIP $1)/address ] && return 0 || return 1;
}
###
### $1: Some IP of a local net
###
......@@ -579,6 +595,9 @@ formatAsHexID() {
$IPTABLES -t nat -nvL 1>/dev/nul 2>/dev/nul
(( $? == 0 )) && $IPTABLES -t nat -F
$IPTABLES -t raw -nvL 1>/dev/nul 2>/dev/nul
(( $? == 0 )) && $IPTABLES -t raw -F
$IPTABLES -t mangle -nvL 1>/dev/nul 2>/dev/nul
(( $? == 0 )) && $IPTABLES -t mangle -F
......@@ -590,6 +609,7 @@ formatAsHexID() {
# delete all user-specified chains
$IPTABLES -X
$IPTABLES -t nat -X
$IPTABLES -t raw -X
$IPTABLES -t mangle -X
# reset all counters
$IPTABLES -Z
......@@ -616,10 +636,31 @@ formatAsHexID() {
-j MARK --set-mark 0xff \
-m comment --comment "sending request from local interface";
$IPTABLES -t mangle -A DHCP -p udp \
-m udp --sport $dhcp_server --dport $dhcp_client \
-m addrtype --dst-type LOCAL --limit-iface-in \
-m udp --dport $dhcp_client \
-m mark ! --mark 0/$DHCP_BOTH_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "request/discover/reply from server";
-m comment --comment "reply from server to local interface";
# for everyone wondering why a DHCP request via broadcast like
# 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP ... is never seen by
# netfilter on any outgoing chain on any table that's because most
# dhcp daemons are using PF_PACKET sockets for the _first_, _initial_
# request which bypasses the whole netfilter.
# To keep things more confusing this doesn't apply for any 'ordinary'
# follow up dhcp packet (only the first one!)
# https://unix.stackexchange.com/questions/447440/ufw-iptables-not-blocking-dhcp-udp-port-67
# receive requests on server different for IPv(4|6)
[ $ENVID == "4" ] && {
$IPTABLES -t mangle -A DHCP -p udp \
-s 0.0.0.0/32 \
-m addrtype --dst-type BROADCAST \
-m udp --sport $dhcp_client --dport $dhcp_server \
-m mark --mark $DHCP_SERVER_MARK/$DHCP_SERVER_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "receive initial requests via broadcast (IPv4 only)";
};
[ $ENVID == "6" ] && {
$IPTABLES -t mangle -A DHCP -p udp \
-s $LLsubnet \
......@@ -627,44 +668,55 @@ formatAsHexID() {
-m udp --sport $dhcp_client --dport $dhcp_server \
-m mark --mark $DHCP_SERVER_MARK/$DHCP_SERVER_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "receiving requests via link-local (IPv6 only)";
}
-m comment --comment "receive requests via link-local (IPv6 only)";
};
$IPTABLES -t mangle -A DHCP -m mark --mark 0xff -j RETURN;
$IPTABLES -t mangle -A DHCP $TO_LOG "[BLOCK] (DHCP) ";
$IPTABLES -t mangle -A DHCP $TO_LOG "[DHCP] packet loss ";
$IPTABLES -t mangle -A DHCP -j DROP;
# mark incoming dhcp packets very early
$IPTABLES -t mangle -I PREROUTING -p udp -m multiport --port ${dhcp_client},${dhcp_server} \
-j DHCP -m comment --comment "dhcp in"
-j DHCP -m comment --comment "dhcp in";
# mark follow up dhcp packets leaving
$IPTABLES -t mangle -I OUTPUT -p udp -m multiport --port ${dhcp_client},${dhcp_server} \
-j DHCP -m comment --comment "dhcp out"
# for everyone wondering why a DHCP request via broadcast like
# 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP ... is never seen by
# netfilter on any outgoing chain on any table that's because most
# dhcp daemons are using PF_PACKET sockets for the _first_, _initial_
# request which bypasses the whole netfilter.
# To keep things more confusing this doesn't apply for any 'ordinary'
# follow up dhcp packet (only the first one!)
# https://unix.stackexchange.com/questions/447440/ufw-iptables-not-blocking-dhcp-udp-port-67
-j DHCP -m comment --comment "dhcp out";
# quash INVALID traffic but return exceptions to the rule
$IPTABLES -N QUASH
$IPTABLES -A QUASH -m state ! --state INVALID -j RETURN \
-m comment --comment "for valid packets accidentally stranded here";
$IPTABLES -A QUASH -p tcp --tcp-flags SYN,ACK,FIN,RST RST \
-m multiport --sports 80,443 \
-m addrtype ! --src-type LOCAL --dst-type LOCAL \
-m comment --comment "allow http(s) RST packets from outside even when invalid" \
-j ACCEPT;
$IPTABLES -A QUASH $TO_LOG "[QUASH] drop invalid ";
$IPTABLES -A QUASH -j DROP;
# create an ANTI-FLOOD protection chain. Maximises the rate of incoming connections.
# Used for protection against SYN or PING_OF_DEATH flooding
$IPTABLES -N ANTI-FLOOD
$IPTABLES -A ANTI-FLOOD -m limit --limit 3/s -j RETURN
$IPTABLES -A ANTI-FLOOD $TO_LOG "[BLOCK] (ANTI-FLOOD) "
$IPTABLES -A ANTI-FLOOD -j DROP
$IPTABLES -N ANTI-FLOOD;
$IPTABLES -A ANTI-FLOOD -m limit --limit 3/s -j RETURN;
$IPTABLES -A ANTI-FLOOD -p tcp -m multiport --ports 80,443 \
-m limit --limit 32/s \
-m comment --comment "relax limit for http(s) traffic" \
-j RETURN;
$IPTABLES -A ANTI-FLOOD -p tcp -m multiport --ports 993 \
-m limit --limit 32/s \
-m comment --comment "relax limit for email traffic" \
-j RETURN;
$IPTABLES -A ANTI-FLOOD $TO_LOG "[ANTI-FLOOD] drop ";
$IPTABLES -A ANTI-FLOOD -j DROP;
# limits the connection attempts
$IPTABLES -N LIMITER
$IPTABLES -A LIMITER -m state ! --state NEW -j RETURN
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name DROP_SILENTLY \
--hashlimit-mode srcip --hashlimit-srcmask 16 \
--hashlimit-above 8/day -j DROP
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name LIMITER \
--hashlimit-mode srcip --hashlimit-upto 3/hour --hashlimit-burst 3 \
--hashlimit-htable-size 256 --hashlimit-htable-max 1024 -j RETURN
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name DROP_SILENTLY \
--hashlimit-mode srcip --hashlimit-srcmask 16 \
--hashlimit-above 16/day -j DROP
$IPTABLES -A LIMITER $TO_LOG "[BLOCK] (LIMITER) "
$IPTABLES -A LIMITER $TO_LOG "[LIMITER] drop "
$IPTABLES -A LIMITER -j DROP
# Will be used in INPUT & OUTPUT to add allowed subnets on local links
......@@ -708,7 +760,7 @@ formatAsHexID() {
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IPv6
if [ $ENVID == "6" ]; then
#TODO: Hints what ICMP subtypes to filter can be found in https://tools.ietf.org/html/rfc4890
# filter all packets that have RH0 headers:
#$IPTABLES -A INPUT -m rt --rt-type 0 --rt-segsleft 0 -j DROP
#$IPTABLES -A OUTPUT -m rt --rt-type 0 --rt-segsleft 0 -j DROP
......@@ -750,8 +802,8 @@ formatAsHexID() {
# only routable subnets allowed
$IPTABLES -A OUTPUT -j LOCAL
$IPTABLES -A INPUT -m state --state INVALID $TO_LOG "[BLOCK] (INVALID) "
$IPTABLES -A INPUT -m state --state INVALID -j DROP
# check invalid packets
$IPTABLES -A INPUT -m state --state INVALID -j QUASH
# syn-flood protection
$IPTABLES -A INPUT -p tcp --syn -j ANTI-FLOOD
......@@ -1008,7 +1060,7 @@ formatAsHexID() {
;;
ALLOW_STATEFUL_PACKETS)
unset sz
unset sz; unset by;
probeChains USER-IN USER-OUT
(( $? != 0 )) && error 42 "ALLOW_STATEFUL_PACKETS needs 'USER-*' chains. Setup a new base firewall with BASE_RULE_SET first."
......@@ -1025,11 +1077,17 @@ formatAsHexID() {
printf "# allowing stateful packets on '%s'.\n" $1
sz=$(getLinkID $1);
else
[ -z "$(obtainNetPrefix $1)" ] && \
printf >&2 "WARNING! no route to '$1' or argument has malformed IP format"
# [ -z "$(obtainNetPrefix $1)" ] && \
# printf >&2 "WARNING! no route to '$1' or argument has malformed IP format"
# not working for PTP addresses with a /32 netmask
is_local_IP $1 || {
printf >&2 "ERROR! ALLOW_STATEFUL_PACKETS expects a locally configured IP address. '$1' isn't configured on any local net interface of this host.";
exit 25;
}
by[0]="-d $1 -m addrtype ! --src-type LOCAL";
by[1]="-s $1";
by[2]=",RELATED,ESTABLISHED";
printf "# allowing stateful packets on any interface when sending from '%s'.\n" $1
sz=$(formatAsHexID $1);
fi
......@@ -1046,7 +1104,7 @@ formatAsHexID() {
# stateful packets allowing only new packets out
$IPTABLES -A USER-OUT ${by[1]} \
-m state --state NEW \
-m state --state NEW${by[2]} \
-j ACCEPT -m comment --comment "$ID";
shift;
;;
......@@ -1237,8 +1295,8 @@ formatAsHexID() {
# for IPv6 subnets the max chain name length can
# be easily exceeded so we need a shorter name
#CHAIN=$(printf "%s-%s-%s" $(formatAsHexID "$1") $2 $DEV)
CHAIN=$(printf "%X-IN" $(cksum <<<"$ID" | grep -oP "^\d*"))
allocChain $CHAIN
CHAIN=$(printf "%X-IN" $(cksum <<<"$ID" | grep -oP "^\d*"));
allocChain $CHAIN;
# get index position for placing subchains in FORWARD chain
returnRuleCount FORWARD
......@@ -1249,36 +1307,35 @@ formatAsHexID() {
[ $ENVID -eq 6 ] && sz="ipv6-icmp"
$IPTABLES -A $CHAIN -p $sz -j ICMP
# drop invalid packets
$IPTABLES -A $CHAIN -m state --state INVALID $TO_LOG "[BLOCK] (INVALID) "
$IPTABLES -A $CHAIN -m state --state INVALID -j DROP
# check invalid packets
$IPTABLES -A $CHAIN -m state --state INVALID -j QUASH;
# syn-flood protection
$IPTABLES -A $CHAIN -p tcp -d $1 --syn -j ANTI-FLOOD
$IPTABLES -A $CHAIN -p tcp -d $1 --syn -j ANTI-FLOOD;
# furtive port scanner
$IPTABLES -A $CHAIN -p tcp -d $1 --tcp-flags SYN,ACK,FIN,RST RST -j ANTI-FLOOD
$IPTABLES -A $CHAIN -p tcp -d $1 --tcp-flags SYN,ACK,FIN,RST RST -j ANTI-FLOOD;
# allow safe ports for inbound traffic
$IPTABLES -A $CHAIN -p tcp -m tcp -m multiport --sports 80,443 -j ACCEPT
$IPTABLES -A $CHAIN -p tcp -m tcp -m multiport --sports 80,443 -j ACCEPT;
# forward to the inside for related or established traffic only!
$IPTABLES -A $CHAIN -m state --state RELATED,ESTABLISHED -j ACCEPT
$IPTABLES -A $CHAIN -m state --state RELATED,ESTABLISHED -j ACCEPT;
$IPTABLES -I FORWARD $n -i $2 -o $DEV -d $1 -j $CHAIN -m comment --comment "$ID"
$IPTABLES -I FORWARD $n -i $2 -o $DEV -d $1 -j $CHAIN -m comment --comment "$ID";
CHAIN=$(printf "%X-OUT" $(cksum <<<"$ID" | grep -oP "^\d*"))
allocChain $CHAIN
CHAIN=$(printf "%X-OUT" $(cksum <<<"$ID" | grep -oP "^\d*"));
allocChain $CHAIN;
# forward new, related & established traffic outside
$IPTABLES -A $CHAIN -m state --state RELATED,ESTABLISHED -j ACCEPT
$IPTABLES -A $CHAIN -m state --state NEW -j ACCEPT
$IPTABLES -A $CHAIN -m state --state RELATED,ESTABLISHED -j ACCEPT;
$IPTABLES -A $CHAIN -m state --state NEW -j ACCEPT;
# allow safe ports for outbound traffic
$IPTABLES -A $CHAIN -p tcp -m tcp -m multiport --dports 80,443 -j ACCEPT
$IPTABLES -A $CHAIN -p tcp -m tcp -m multiport --dports 80,443 -j ACCEPT;
$IPTABLES -I FORWARD $n -i $DEV -o $2 -s $1 -j $CHAIN -m comment --comment "$ID"
$IPTABLES -I FORWARD $n -i $DEV -o $2 -s $1 -j $CHAIN -m comment --comment "$ID";
unset sz; unset DEV; unset CHAIN; unset n
unset sz; unset DEV; unset CHAIN; unset n;
shift; shift
;;
......@@ -1420,7 +1477,10 @@ formatAsHexID() {
;;
LIST)
$IPTABLES --line-numbers -nvL $1
printf "\n#\n# ~~~ IPv4\n#\n\n";
iptables --line-numbers -nvL $1
printf "\n#\n# ~~~ IPv6\n#\n\n";
ip6tables --line-numbers -nvL $1
shift;
;;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment