Skip to content

Instantly share code, notes, and snippets.

@vodik
Created September 28, 2012 01:12
Show Gist options
  • Save vodik/3797424 to your computer and use it in GitHub Desktop.
Save vodik/3797424 to your computer and use it in GitHub Desktop.
My old firewall script
#! vim: ft=sh
EXTERNAL=eth0
INTERNAL=br0
NAT=static
EXTERNAL_IP='x.x.x.x'
NETWORK='10.0.0.0/22'
SERVICES_TCP=(http https)
SERVICES_UDP=(openvpn)
# NOT REAL SECURITY
STEALTH=no
# Uplink and downlink speeds
# Normally use a bit lower values than your real speed, but you should experiment a bit
DOWNLINK=10000
UPLINK=512
QOS_DEFAULT=5
QOS=(interactive voip browsing default low)
# Interactive traffic: guarantee realtime full uplink for 50ms, then 5/10 of the uplink
function QOS_interactive()
{
ARGS="rc m1 ${UPLINK}kbit d 50ms m2 $((5*${UPLINK}/10))kbit ls m1 ${UPLINK}kbit d 50ms m2 $((7*${UPLINK}/10))kbit"
TCP=(ssh)
UDP=(domain)
}
# VoIP: guarantee full uplink for 200ms, then 3/10
function QOS_voip()
{
ARGS="sc m1 ${UPLINK}kbit d 200ms m2 $((3*$UPLINK/10))kbit"
TCP=(23399)
UDP=(23399)
}
# Browsing: guarantee 3/10 uplink for 200ms, then guarantee 1/10
function QOS_browsing()
{
ARGS="sc m1 $((3*$UPLINK/10))kbit d 200ms m2 $((1*$UPLINK/10))kbit"
TCP=(http https)
}
# Default traffic: don't guarantee anything for the first second, then guarantee 1/10
function QOS_default()
{
ARGS="sc m1 0 d 1s m2 $((1*$UPLINK/10))kbit"
}
# Lowest taffic: don't guarantee anything for the first 10 seconds, then guarantee 1/20
function QOS_low()
{
ARGS="sc m1 0 d 10s m2 $((1*$UPLINK/30))kbit"
TCP=(49164)
UDP=(6881)
}
#!/bin/sh
# This script must be run as root. If sudo exists, auto-elevate
test "$(id -u)" != 0 && exec sudo -E $0
iptables() {
echo "iptables $*"
\iptables $* || exit 1
}
tc() {
echo "tc $*"
\tc $* || exit 1
}
function ipof()
{
host "$@" 127.0.0.1 | awk '{print $4}'
#case $1 in
#sheep) echo "10.0.0.6" ;;
#esac
}
########################################################################################
# Reset iptables
_reset_tables() {
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
iptables -X
for TABLE in nat mangle
do
iptables -t $TABLE -F
iptables -t $TABLE -X
iptables -t $TABLE -Z
done
}
# Reset tc
_reset_qos() {
\tc qdisc del dev $EXTERNAL root
\tc qdisc del dev $EXTERNAL ingress
}
_basic_FORWARD() {
iptables -P FORWARD DROP
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m state --state INVALID -j DROP
iptables -A FORWARD -m state --state NEW -i $INTERNAL -j ACCEPT
}
_basic_OUTPUT() {
:
}
_basic_INPUT() {
iptables -P INPUT DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -m state --state NEW ! -i $EXTERNAL -j ACCEPT
}
_INPUT_allow_pings() {
iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW -j ACCEPT
}
_INPUT_block_local() {
iptables -I INPUT -i $EXTERNAL -s $NETWORK -j DROP
}
# Normally:
# - Reject TCP connections with TCP RST packages
# - Reject UDP streams with ICMP port unreachable messages
# stealth mode drops all communication
_INPUT_stealth_mode() {
iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreach
iptables -A INPUT -p tcp -j REJECT --reject-with tcp-rst
}
########################################################################################
. "$(dirname $0)/config"
if [[ -z "$EXTERNAL" ]]; then
echo "$0: requires an external devices"
exit -1
fi
if [[ -z "$INTERNAL" ]]; then
echo "$0: requires an internal devices"
exit -1
fi
if [[ -z "$DOWNLINK" ]]; then
echo "$0: start requires a downlink speed, aborting."
exit -1
fi
if [[ -z "$UPLINK" ]]; then
echo "$0: start requires an uplink speed, aborting."
exit -1
fi
_reset_tables
_reset_qos
_basic_FORWARD
_basic_OUTPUT
_basic_INPUT
_INPUT_allow_pings
_INPUT_block_local
# If STEALTH mode isn't set,
if [[ "$STEALTH" != "yes" ]]
then
_INPUT_stealth_mode # wrong, it does the opposite
fi
# POSTROUTING
# ------------
function add_nat()
{
case "$1" in
dynamic)
iptables -t nat -A POSTROUTING -o $2 \
-s $3 -j MASQUERADE
break;
;;
static)
[[ -n "$4" ]] && iptables -t nat -A POSTROUTING -o $2 \
-s $3 -j SNAT \
--to $4
;;
esac
}
add_nat $NAT $EXTERNAL $NETWORK $EXTERNAL_IP
test "$BASIC" == "yes" && exit 0
# OPENING SERVICES
# -----------------
if [[ -n "${SERVICES_TCP[@]}" ]]; then
iptables -N TCP
iptables -A INPUT -p tcp --syn -m state --state NEW -j TCP
for SERVICE in ${SERVICES_TCP[@]}; do
iptables -A TCP -p tcp --dport ${SERVICE} -j ACCEPT
done
fi
if [[ -n "${SERVICES_UDP[@]}" ]]; then
iptables -N UDP
iptables -A INPUT -p udp -m state --state NEW -j UDP
for SERVICE in ${SERVICES_UDP[@]}
do
iptables -A UDP -p udp --dport ${SERVICE} -j ACCEPT
done
fi
# QOS
# ----
[[ -z "${QOS[@]}" ]] && exit 0
# add HFSC root qdisc
tc qdisc add dev ${EXTERNAL} root handle 1: hfsc default ${QOS_DEFAULT}
# add main rate limit class
tc class add dev ${EXTERNAL} parent 1: classid 1:1 hfsc \
sc rate ${UPLINK}kbit ul rate ${UPLINK}kbit
iptables -t mangle -N QOS
iptables -t mangle -A POSTROUTING -o ${EXTERNAL} -j QOS
CLASSID=2
for CLASS in "${QOS[@]}"; do
(
echo "CLASS: ${CLASS}"
QOS_${CLASS}
tc class add dev ${EXTERNAL} parent 1:1 classid 1:${CLASSID} hfsc ${ARGS} ul rate ${UPLINK}kbit
for SERVICE in "${TCP[@]}"; do
#iptables -t mangle -A QOS -p tcp --sport ${SERVICE} -j CLASSIFY --set-class 1:${CLASSID}
iptables -t mangle -A QOS -p tcp --dport ${SERVICE} -j CLASSIFY --set-class 1:${CLASSID}
done
for SERVICE in "${UDP[@]}"; do
#iptables -t mangle -A QOS -p udp --sport ${SERVICE} -j CLASSIFY --set-class 1:${CLASSID}
iptables -t mangle -A QOS -p udp --dport ${SERVICE} -j CLASSIFY --set-class 1:${CLASSID}
done
)
export CLASS_${CLASS}_ID=${CLASSID}
CLASSID=$((CLASSID + 1))
done
# To speed up downloads while an upload is going on, put short ACK
# packets in the interactive class:
iptables -t mangle -A QOS \
-p tcp \
-m tcp --tcp-flags FIN,SYN,RST,ACK ACK \
-m length --length :64 \
-j CLASSIFY --set-class 1:${CLASS_interactive_ID}
# put large (512+) icmp packets in browsing category
iptables -t mangle -A QOS \
-p icmp \
-m length --length 512: \
-j CLASSIFY --set-class 1:${CLASS_browsing_ID}
# ICMP (ip protocol 1) in the interactive class
iptables -t mangle -A QOS \
-p icmp \
-m length --length :512 \
-j CLASSIFY --set-class 1:${CLASS_interactive_ID}
# Try to control the incoming traffic as well.
# Set up ingress qdisc
tc qdisc add dev ${EXTERNAL} handle ffff: ingress
# Filter everything that is coming in too fast
# It's mostly HTTP downloads that keep jamming the downlink, so try to restrict
# them to 95/100 of the downlink.
tc filter add dev ${EXTERNAL} parent ffff: protocol ip prio 50 \
u32 match ip src 0.0.0.0/0 \
match ip protocol 6 0xff \
match ip sport 80 0xffff \
police rate $((95* DOWNLINK / 100))kbit \
burst 10k drop flowid :1
tc filter add dev ${EXTERNAL} parent ffff: protocol ip prio 50 \
u32 match ip src 0.0.0.0/0 \
match ip protocol 6 0xff \
match ip dport 80 0xffff \
police rate $((95 * DOWNLINK / 100))kbit \
burst 10k drop flowid :1
# EXTRAS (firewall.d/)
# ---------------------
if [[ -n "${EXTRA[@]}" ]]; then
for SERVICE in ${EXTRA[@]}; do
( . "firewall.d/${SERVICE}" )
done
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment