Tűzfalak

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
(Naiv módszer: update 2021)
(Tűzfalscriptek írása: update 2021)
266. sor: 266. sor:
 
** Dinamikus szabályok készítése nehézkes (pl. dinamikus hostnevek IP-címeinek követése)
 
** Dinamikus szabályok készítése nehézkes (pl. dinamikus hostnevek IP-címeinek követése)
 
*** Bár <tt>ipset</tt>-tel megoldható
 
*** Bár <tt>ipset</tt>-tel megoldható
  +
** Kommentálni csak az egyes szabályok szintjén lehet; hosszabb magyarázatot nem tudunk elhelyezni közben
 
** Könnyen átláthatatlanná válik
 
** Könnyen átláthatatlanná válik
 
** Nem választja el a "kódot" a konfigurációtól
 
** Nem választja el a "kódot" a konfigurációtól
286. sor: 287. sor:
 
* INPUT-szűrés sincs benne
 
* INPUT-szűrés sincs benne
 
* Ez kb. egy minimális ADSL-megosztási konfiguráció
 
* Ez kb. egy minimális ADSL-megosztási konfiguráció
  +
* De legalább kommentezve van!
   
 
=== Spagettiscript ===
 
=== Spagettiscript ===
329. sor: 331. sor:
   
 
# Allow unlimited traffic on the loopback interface
 
# Allow unlimited traffic on the loopback interface
iptables -A INPUT -i lo -j ACCEPT
+
iptables -A INPUT -j ACCEPT -i lo
iptables -A OUTPUT -o lo -j ACCEPT
+
iptables -A OUTPUT -j ACCEPT -o lo
   
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+
iptables -A INPUT -j ACCEPT -m state --state ESTABLISHED,RELATED
   
iptables -A INPUT -s 1.2.3.4 -p tcp --dport ssh -j ACCEPT
+
iptables -A INPUT -j ACCEPT -p tcp --dport ssh -s 1.2.3.4
   
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
+
iptables -A FORWARD -j ACCEPT -m state --state ESTABLISHED,RELATED
   
iptables -A FORWARD -d 2.3.4.5 -p tcp --dport www -j ACCEPT
+
iptables -A FORWARD -j ACCEPT -p tcp --dport www -d 2.3.4.5
iptables -A FORWARD -d 2.3.4.5 -p tcp --dport https -j ACCEPT
+
iptables -A FORWARD -j ACCEPT -p tcp --dport https -d 2.3.4.5
   
iptables -A FORWARD -d 2.3.4.6 -p tcp --dport smtp -j ACCEPT
+
iptables -A FORWARD -j ACCEPT -p tcp --dport smtp -d 2.3.4.6
   
iptables -A FORWARD -s 2.3.4.7 -p tcp --dport www -j ACCEPT # http proxy can talk to net
+
iptables -A FORWARD -j ACCEPT -p tcp --dport www -s 2.3.4.7 # http proxy can talk to net
iptables -A FORWARD -s 2.3.4.7 -p tcp --dport https -j ACCEPT # http proxy can talk to net
+
iptables -A FORWARD -j ACCEPT -p tcp --dport https -s 2.3.4.7 # http proxy can talk to net
iptables -A FORWARD -s 2.3.4.6 -p tcp --dport smtp -j ACCEPT # mailserver too
+
iptables -A FORWARD -j ACCEPT -p tcp --dport smtp -s 2.3.4.6 # mailserver too
   
 
# intranet clients can access DMZ
 
# intranet clients can access DMZ
iptables -A FORWARD -i eth2 -o eth1 -m state --state NEW -j ACCEPT
+
iptables -A FORWARD -j ACCEPT -i eth2 -o eth1 -m state --state NEW
   
iptables -A OUTPUT -m ESTABLISHED,RELATED -j ACCEPT
+
iptables -A OUTPUT -j ACCEPT -m ESTABLISHED,RELATED
iptables -A OUTPUT -d 1.2.3.4 -j ACCEPT
+
iptables -A OUTPUT -j ACCEPT -d 1.2.3.4
iptables -A OUTPUT -o eth1 -j ACCEPT # firewall can access DMZ
+
iptables -A OUTPUT -j ACCEPT -o eth1 # firewall can access DMZ
 
</pre>
 
</pre>
   
 
* Ez sem igazán jó példa, inkább állatorvosi ló
 
* Ez sem igazán jó példa, inkább állatorvosi ló
  +
* Ami követendő lehet belőle: előre írtam a rendelkező részt, és "táblázatszerűen" tagoltam a scriptet, így jobban olvasható, mint ömlesztve
   
 
=== Strukturált script ===
 
=== Strukturált script ===
   
 
* "Shell-tömbök" adják a konfiguráció ACL-jeit (pl. WEBSERVERS="192.168.1.2 192.168.1.3 192.168.1.4")
 
* "Shell-tömbök" adják a konfiguráció ACL-jeit (pl. WEBSERVERS="192.168.1.2 192.168.1.3 192.168.1.4")
  +
** Főként zsh-ban érdemes valódi tömböket használni: <tt>WEBSERVERS=(192.168.1.2 192.168.1.3 192.168.1.4)</tt>
 
* Shell-függvények felelősek bizonyos láncok összeállításáért (pl. ACCEPT() proto tcp from 0/0 to "$WEBSERVERS" srcport any dstport 80 parent INPUT)
 
* Shell-függvények felelősek bizonyos láncok összeállításáért (pl. ACCEPT() proto tcp from 0/0 to "$WEBSERVERS" srcport any dstport 80 parent INPUT)
 
* Vagy ha nem is függvények, legalább ciklusok
 
* Vagy ha nem is függvények, legalább ciklusok
375. sor: 379. sor:
 
** Jogok részleges delegálása lehetetlen
 
** Jogok részleges delegálása lehetetlen
 
** Megnehezíti az implementációt, ha olyan DNS-feloldásokat is cache-elni akarunk, amelyek egynél több címet adnak vissza
 
** Megnehezíti az implementációt, ha olyan DNS-feloldásokat is cache-elni akarunk, amelyek egynél több címet adnak vissza
  +
** Az <tt>ipset</tt> igazából sokkal elegánsabb megoldás, mint a shell-tömb, és 2021-ben már teljesen használható is
   
 
Példa (figyelem! nem jó!):
 
Példa (figyelem! nem jó!):
382. sor: 387. sor:
   
 
LOCK=/var/run/firewall.lock
 
LOCK=/var/run/firewall.lock
[ -f $LOCK ] && exit 0 # Pozor! Ez így nem jó!
+
[ -f $LOCK ] && exit 0 # Pozor! Ez így nem jó! Versenyhelyzet! Így nem lehet shellben kölcsönös kizárást csinálni!
 
touch $LOCK
 
touch $LOCK
   
389. sor: 394. sor:
   
 
function iptables() {
 
function iptables() {
/sbin/iptables "$@" || echo failed: iptables $@
+
/sbin/iptables "$@" || echo "failed: iptables $@"
 
}
 
}
   
 
for i in ip_tables iptable_filter ipt_limit ipt_mac ipt_owner ip_nat_ftp ip_nat_irc ipt_unclean ipt_REJECT ipt_REDIRECT; do
 
for i in ip_tables iptable_filter ipt_limit ipt_mac ipt_owner ip_nat_ftp ip_nat_irc ipt_unclean ipt_REJECT ipt_REDIRECT; do
modprobe $i 2>/dev/null >/dev/null;
+
modprobe $i 2>/dev/null >/dev/null
 
done
 
done
   
519. sor: 524. sor:
 
** Ne az iptables-t hívogassuk, hanem állítsunk elő iptables-restore formátumú szövegfile-t, majd a végén töltsük be azt; így atomivá válik a szabályok betöltése
 
** Ne az iptables-t hívogassuk, hanem állítsunk elő iptables-restore formátumú szövegfile-t, majd a végén töltsük be azt; így atomivá válik a szabályok betöltése
   
Példa (ilyet használok ma, ez az "iptablez"):
+
Példa (ilyet használtam 2012 körül; ez az "iptablez"):
   
 
<pre>
 
<pre>
 
#!/bin/zsh
 
#!/bin/zsh
# file: /etc/firewall/firewall
 
   
if ! sv start dnscache >/dev/null 2>/dev/null; then
+
#exec 2>/tmp/fw2.log
(sleep 30; setlock -n /var/lock/firewall.lock /etc/firewall/firewall) &
+
#exec >/tmp/fw-out.log
disown %1
 
exit 0
 
fi
 
   
 
BASEDIR=/etc/firewall
 
BASEDIR=/etc/firewall
539. sor: 543. sor:
 
CONFDIR=${CONFDIR:-$BASEDIR/conf.d}
 
CONFDIR=${CONFDIR:-$BASEDIR/conf.d}
 
SCRIPTDIR=${SCRIPTDIR:-$BASEDIR/script.d}
 
SCRIPTDIR=${SCRIPTDIR:-$BASEDIR/script.d}
  +
PREDIR=${PREDIR:-$BASEDIR/pre.d}
  +
POSTDIR=${POSTDIR:-$BASEDIR/post.d}
 
ACLDIR=${ACLDIR:-$BASEDIR/acl.d}
 
ACLDIR=${ACLDIR:-$BASEDIR/acl.d}
 
RESTOREDIR=$BASEDIR/restore
 
RESTOREDIR=$BASEDIR/restore
547. sor: 553. sor:
   
 
function debug() {
 
function debug() {
if [[ "$DEBUG" -ge "$1" ]]; then
+
if [[ "$DEBUG" -ge "$1" ]]; then
shift
+
shift
echo "$@"
+
echo "$@"
fi
+
fi
 
}
 
}
  +
  +
[[ -d "$PREDIR" ]] && for depth in {10..1}; do
  +
find "$PREDIR" -mindepth $depth -maxdepth $depth ! -type d \
  +
| fgrep -v /.svn/ \
  +
| sort \
  +
| while read i; do
  +
PREFILES=($PREFILES $i)
  +
done
  +
done
  +
  +
for script_i in $PREFILES[@]; do
  +
if [[ -r "$script_i" ]]; then
  +
debug 2 Sourcing "$script_i"...
  +
. "$script_i"
  +
else
  +
debug 1 WARNING: "$script_i" is unreadable.
  +
fi
  +
done
   
 
debug 2 Sourcing functions...
 
debug 2 Sourcing functions...
558. sor: 582. sor:
 
iptables_init
 
iptables_init
   
  +
# Can't assume no sourced script will read from stdin, so assmble list of
  +
# files to source before sourcing first one:
 
for dir in "$CONFDIR" "$SCRIPTDIR"; do
 
for dir in "$CONFDIR" "$SCRIPTDIR"; do
for depth in {10..1}; do
+
for depth in {10..1}; do
find "$dir" -mindepth $depth -maxdepth $depth ! -type d \
+
find "$dir" -mindepth $depth -maxdepth $depth ! -type d \
| fgrep -v /.svn/ \
+
| fgrep -v /.svn/ \
| sort \
+
| sort \
| while read i; do
+
| while read i; do
if [ -r "$i" ]; then
+
FILES=($FILES $i)
debug 2 Sourcing "$i"...
+
done
. "$i"
+
done
else
+
done
debug 1 WARNING: "$i" is unreadable.
+
fi
+
# Sourced files likely contain loops with "i" as the loop variable...
done
+
for script_i in $FILES[@]; do
done
+
if [[ -r "$script_i" ]]; then
  +
debug 2 Sourcing "$script_i"...
  +
. "$script_i"
  +
else
  +
debug 1 WARNING: "$script_i" is unreadable.
  +
fi
 
done
 
done
   
 
iptables_commit
 
iptables_commit
  +
ret=$?
  +
  +
[[ -d "$POSTDIR" ]] && for depth in {10..1}; do
  +
find "$POSTDIR" -mindepth $depth -maxdepth $depth ! -type d \
  +
| fgrep -v /.svn/ \
  +
| sort \
  +
| while read i; do
  +
POSTFILES=($POSTFILES $i)
  +
done
  +
done
  +
  +
for script_i in $POSTFILES[@]; do
  +
if [[ -r "$script_i" ]]; then
  +
debug 2 Sourcing "$script_i"...
  +
. "$script_i"
  +
else
  +
debug 1 WARNING: "$script_i" is unreadable.
  +
fi
  +
done
  +
exit $ret
 
</pre>
 
</pre>
   
589. sor: 635. sor:
 
# include, e.g.: ". $ACLDIR/other_file". Recursive including is supported;
 
# include, e.g.: ". $ACLDIR/other_file". Recursive including is supported;
 
# be careful to avoid loops! Also, very deep include trees may exhaust file
 
# be careful to avoid loops! Also, very deep include trees may exhaust file
# descriptor limits.
+
# descriptor limits. Shell variables in ACLs are expanded.
 
#
 
#
 
function readacl() {
 
function readacl() {
local i
+
local i
   
egrep -v '^[[:space:]#]*$|^#' "$1" \
+
egrep -v '^[[:space:]#]*$|^#' "$1" \
| sed 's/[[:space:]]*#.*//' \
+
| sed 's/[[:space:]]*#.*//' \
| while read i; do
+
| while read -r i; do
if [[ "$i" == "${i#. }" ]]; then
+
if [[ "$i" == "${i#. }" ]]; then
eval echo "$i"
+
echo "${(e)i}"
else
+
else
readacl "$(eval echo "${i#. }")"
+
readacl "${(e)i#. }"
fi
+
fi
done \
+
done \
| egrep -v '^[[:space:]#]*$|^#' \
+
| egrep -v '^[[:space:]#]*$|^#' \
| sed 's/[[:space:]]*#.*//'
+
| sed 's/[[:space:]]*#.*//'
  +
}
  +
  +
# reads 'ipset' commands from $IPSET_CONF (/etc/firewall/ipset.conf by
  +
# default) and executes them; supports shell variables and includes (since
  +
# it uses readacl()). ipset comments should appear at the end of the ipset
  +
# command line. Otherwise, ipset.conf is like 'ipset save' output.
  +
function restore-ipsets() {
  +
local -a args line
  +
readacl ${IPSET_CONF:-/etc/firewall/ipset.conf} | while read -rA line; do
  +
while [[ -n $line[1] ]]; do
  +
case $line[1] in
  +
# when reading comment, remove quotes and parse the rest of the line into a single word:
  +
comment) shift line; args=($args comment "${line//\"/}"); unset line;;
  +
*) args=($args $line[1]); shift line;;
  +
esac
  +
done
  +
if ! ipset $args; then
  +
echo "failing ipset command was: $args" >&2
  +
fi
  +
unset args
  +
done
 
}
 
}
   
630. sor: 676. sor:
   
 
function buildchain() {
 
function buildchain() {
if [[ "$2" = "" ]]; then
+
if [[ "$2" = "" ]]; then
. "$1"
+
. "$1"
else
+
else
local TABLE="$1"
+
local TABLE="$1"
local PARENT_CHAIN="$2"
+
local PARENT_CHAIN="$2"
local CHAIN="$3"
+
local CHAIN="$3"
local PARENT_SELECTOR="$4"
+
local PARENT_SELECTOR="$4"
local CHILD_SELECTOR="$5"
+
local CHILD_SELECTOR="$5"
local ACLFILE="$6"
+
local ACLFILE="$6"
local PRIMARY_ACTION="$7"
+
local PRIMARY_ACTION="$7"
local SECONDARY_ACTION="$8"
+
local SECONDARY_ACTION="$8"
local SECMESSAGE="$9"
+
local SECMESSAGE="$9"
local SILENTACL="$10"
+
local SILENTACL="$10"
local SILENTDROP="${11:-DROP}"
+
local SILENTDROP="${11:-DROP}"
fi
+
fi
local i=0
+
local i=0
   
if iptables -t $TABLE -N $CHAIN; then # We only build the chain if it didn't already exist.
+
if iptables -t $TABLE -N $CHAIN; then # We only build the chain if it didn't already exist.
readacl "$ACLFILE" | while read i; do
+
readacl "$ACLFILE" | while read i; do
iptables -t $TABLE -A $CHAIN ${=CHILD_SELECTOR} ${=i} -j ${=PRIMARY_ACTION}
+
iptables -t $TABLE -A $CHAIN ${=CHILD_SELECTOR} ${=i} -j ${=PRIMARY_ACTION}
done
+
done
[[ -n "$SILENTACL" ]] && readacl "$SILENTACL" | while read i; do
+
[[ -n "$SILENTACL" ]] && readacl "$SILENTACL" | while read i; do
iptables -t $TABLE -A $CHAIN ${=i} -j ${=SILENTDROP}
+
iptables -t $TABLE -A $CHAIN ${=i} -j ${=SILENTDROP}
done
+
done
[[ -n "$SECMESSAGE" ]] && iptables -t $TABLE -A $CHAIN ${=LOGLIMIT} -j LOG --log-prefix "$SECMESSAGE"
+
[[ -n "$SECMESSAGE" ]] && iptables -t $TABLE -A $CHAIN ${=LOGLIMIT} -j LOG --log-prefix "$SECMESSAGE"
iptables -t $TABLE -A $CHAIN -j ${=SECONDARY_ACTION}
+
iptables -t $TABLE -A $CHAIN -j ${=SECONDARY_ACTION}
fi
+
fi
   
iptables -t $TABLE -A $PARENT_CHAIN ${=PARENT_SELECTOR} -j $CHAIN # enable it in the parent chain
+
iptables -t $TABLE -A $PARENT_CHAIN ${=PARENT_SELECTOR} -j $CHAIN # enable it in the parent chain
 
}
 
}
   
672. sor: 718. sor:
   
 
function buildtcpchain() {
 
function buildtcpchain() {
local PORT="$1"
+
local PORT="$1"
local MYREJECT="${REJECT:-REJECT --reject-with tcp-reset}"
+
local MYREJECT="${REJECT:-REJECT -p tcp --reject-with tcp-reset}"
local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
+
local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
local CHAIN="${PORT}_input"
+
local CHAIN="${PORT}_input"
local ACL="$MYACLDIR/${PORT}"
+
local ACL="$MYACLDIR/${PORT}"
local FRIENDLYNAME="${PORT}"
+
local FRIENDLYNAME="${PORT}"
shift
+
local PARENT=INPUT
while [[ ! "$1" = "" ]]; do
+
shift
case "$1" in
+
while [[ ! "$1" = "" ]]; do
"-c")
+
case "$1" in
shift
+
"-c")
local CHAIN="${1:-${PORT}_input}"
+
shift
shift
+
local CHAIN="${1:-${PORT}_input}"
;;
+
shift
"-a")
+
;;
shift
+
"-a")
local ACL="${1:-$MYACLDIR/${PORT}}"
+
shift
shift
+
local ACL="${1:-$MYACLDIR/${PORT}}"
;;
+
shift
"-n")
+
;;
shift
+
"-n")
local FRIENDLYNAME="${1:-tcp/$PORT}"
+
shift
shift
+
local FRIENDLYNAME="${1:-tcp/$PORT}"
;;
+
shift
*)
+
;;
debug 1 buildtcpchain ignoring unknown parameter \""$1"\".
+
"-p")
shift
+
shift
;;
+
local PARENT="$1"
esac
+
shift
done
+
;;
  +
*)
  +
debug 1 buildtcpchain ignoring unknown parameter \""$1"\".
  +
shift
  +
;;
  +
esac
  +
done
   
buildchain filter INPUT "$CHAIN" \
+
buildchain filter "$PARENT" "$CHAIN" \
"-p tcp --dport $PORT" "-s" \
+
"-p tcp --dport $PORT" "-s" \
"$ACL" \
+
"$ACL" \
ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
+
ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
 
}
 
}
   
720. sor: 766. sor:
   
 
function buildudpchain() {
 
function buildudpchain() {
local PORT="$1"
+
local PORT="$1"
local MYREJECT="${REJECT:-REJECT --reject-with tcp-reset}"
+
local MYREJECT="${REJECT:-REJECT --reject-with tcp-reset}"
local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
+
local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
local CHAIN="${PORT}_input"
+
local CHAIN="${PORT}_input"
local ACL="$MYACLDIR/${PORT}"
+
local ACL="$MYACLDIR/${PORT}"
local FRIENDLYNAME="${PORT}"
+
local FRIENDLYNAME="${PORT}"
shift
+
local PARENT=INPUT
while [[ ! "$1" = "" ]]; do
+
shift
case "$1" in
+
while [[ ! "$1" = "" ]]; do
"-c")
+
case "$1" in
shift
+
"-c")
local CHAIN="${1:-${PORT}_input}"
+
shift
shift
+
local CHAIN="${1:-${PORT}_input}"
;;
+
shift
"-a")
+
;;
shift
+
"-a")
local ACL="${1:-$MYACLDIR/${PORT}}"
+
shift
shift
+
local ACL="${1:-$MYACLDIR/${PORT}}"
;;
+
shift
"-n")
+
;;
shift
+
"-n")
local FRIENDLYNAME="${1:-udp/$PORT}"
+
shift
shift
+
local FRIENDLYNAME="${1:-udp/$PORT}"
;;
+
shift
*)
+
;;
debug 1 buildudpchain ignoring unknown parameter '"'"$1"'"'.
+
"-p")
shift
+
shift
;;
+
local PARENT="$1"
esac
+
shift
done
+
;;
  +
*)
  +
debug 1 buildudpchain ignoring unknown parameter '"'"$1"'"'.
  +
shift
  +
;;
  +
esac
  +
done
   
buildchain filter INPUT "$CHAIN" \
+
buildchain filter "$PARENT" "$CHAIN" \
"-p udp --dport $PORT" "-s" \
+
"-p udp --dport $PORT" "-s" \
"$ACL" \
+
"$ACL" \
ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
+
ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
 
}
 
}
   
863. sor: 909. sor:
 
}
 
}
   
# Get first IP of hostname
+
# Get first IP of hostname; optional 2nd argument specifies fallback IP to report
 
function get_ip_of() {
 
function get_ip_of() {
dnsip $1 | sed 's/[[:space:]].*//g'
+
timeout -k 1 5 dnsip $1 | sed 's/[[:space:]].*//g'
  +
[[ $pipestatus[1] -gt 0 ]] && {
  +
echo "Failed to obtain IP of $1 via DNS.${2:+ Defaulting to $2.}" >&2
  +
[[ -n "$2" ]] && echo -E "$2"
  +
}
 
}
 
}
   
# Get IP of interface
+
# Get main IP of interface
 
function ip_of_if() {
 
function ip_of_if() {
ifconfig $1 | grep 'inet addr:' | sed 's/.*inet addr://;s/ .*//'
+
if [[ -x /usr/bin/ifdata ]]; then
  +
ifdata -pa $1 | grep -v NON-IP
  +
else
  +
ifconfig $1 | sed -n '2s/ [^r]*..//gp'
  +
fi
 
}
 
}
   
 
# Get broadcast address of interface
 
# Get broadcast address of interface
 
function get_if_broadcast() {
 
function get_if_broadcast() {
ifconfig $1 | grep 'inet addr:' | sed 's/.*Bcast://;s/ .*//'
+
if [[ -x /usr/bin/ifdata ]]; then
  +
ifdata -pb $1 | grep -v NON-IP
  +
else
  +
ifconfig $1 | grep 'inet addr:' | sed 's/.*Bcast://;s/ .*//'
  +
fi
  +
}
  +
  +
# returns success if an interface exists, failure if it doesn't
  +
function if_exists() {
  +
if [[ -x /usr/bin/ifdata ]]; then
  +
ifdata -e $1
  +
else
  +
grep -q "^[[:space:]]*$1: " /proc/net/dev
  +
fi
  +
}
  +
  +
# returns success if an interface is 'up', failure if not
  +
function if_up() {
  +
if_exists $1 || return 1
  +
if [[ -x /usr/bin/ifdata ]]; then
  +
ifdata -pf $1 | egrep -q '^On[[:space:]]+Up$'
  +
else
  +
ip link sh $1 | egrep -q "state UP "
  +
fi
  +
}
  +
  +
# Does the interface exist and if yes, does it have an ipv4 IP?
  +
function has_ip() {
  +
ip addr sh dev $1 2>/dev/null | grep -q "^[[:space:]]*inet "
 
}
 
}
   
883. sor: 929. sor:
 
#
 
#
 
function iptables() {
 
function iptables() {
debug 5 Executing /sbin/iptables "$@"
+
debug 5 Executing /sbin/iptables "$@"
if [[ "$1" == "-t" ]]; then
+
local w
TABLE=$2
+
if [[ "$1" == "-t" ]]; then
shift
+
TABLE=$2
shift
+
shift
else
+
shift
TABLE=filter
+
else
fi
+
TABLE=filter
if [[ "$1" == "-N" ]]; then
+
fi
iptables-save -c -t $TABLE | grep "^:$2 " >>"$RESTOREDIR/${TABLE}.chains" || echo ":$2 - [0:0]" >>"$RESTOREDIR/${TABLE}.chains"
+
if [[ "$1" == "-N" ]]; then
shift
+
iptables-save -c -t $TABLE | grep "^:$2 " >>"$RESTOREDIR/${TABLE}.chains" || echo ":$2 - [0:0]" >>"$RESTOREDIR/${TABLE}.chains"
shift
+
shift
elif [[ "$1" == "-P" ]]; then
+
shift
iptables-save -c -t $TABLE | grep "^:$2 $3 " >>"$RESTOREDIR/${TABLE}.policies" || echo ":$2 $3 [0:0]" >>"$RESTOREDIR/${TABLE}.policies"
+
elif [[ "$1" == "-P" ]]; then
else
+
iptables-save -c -t $TABLE | grep "^:$2 $3 " >>"$RESTOREDIR/${TABLE}.policies" || echo ":$2 $3 [0:0]" >>"$RESTOREDIR/${TABLE}.policies"
echo "${(qqq)@}" | sed '
+
else
s/-p tcp/-p tcp -m tcp/
+
while [[ -n "$1" ]]; do
s/-p udp/-p udp -m udp/
+
case $1 in
s/NAT --to /NAT --to-destination /
+
*\ *) echo -n " ${(qqq)1}"; shift;; # quote words with embedded spaces
s@/minute@/min@
+
DNAT) echo -n " DNAT --to-destination"; shift 2;; # convert "DNAT --to" to "DNAT --to-destination"
s@/second@/sec@
+
*) w=${1//\/minute//min}; w=${w//\/second//sec}; echo -n " $w"; shift;; # iptables-save only supports "min" and "sec", not "minute" and "second"
s/"\([^ ]*\)"/\1/g
+
esac
' >>"$RESTOREDIR/${TABLE}.rules"
+
done >>"$RESTOREDIR/${TABLE}.rules"
fi
+
echo >>"$RESTOREDIR/${TABLE}.rules"
  +
fi
 
}
 
}
   
953. sor: 999. sor:
 
iptables -N blacklisted
 
iptables -N blacklisted
   
iptables -A blacklisted ${=LOGLIMIT} -j LOG --log-prefix "FW: blacklisted DROP: "
+
# example:
iptables -A blacklisted ${=ICMP_OUT_LIMIT} -j REJECT --reject-with admin-prohib
+
# REJECT_BEFORE_DROP=(-j REJECT --reject-with admin-prohib $ICMP_OUT_LIMIT -m set --match-set private_nets src -m comment --comment "Internal clients get a helpful ICMP error, subject to rate limiting")
iptables -A blacklisted -j DROP
+
  +
iptables -A blacklisted -j LOG --log-prefix "FW: blacklisted DROP: " $LOGLIMIT -m comment --comment "Log packets to and from blacklisted IPs with a rate limit"
  +
[[ -v REJECT_BEFORE_DROP ]] && iptables -A blacklisted $REJECT_BEFORE_DROP
  +
iptables -A blacklisted -j DROP -m comment --comment "In other cases, we just drop the packets with no ICMP"
 
</pre>
 
</pre>
   
964. sor: 1 010. sor:
 
iptables -N icmp_forward_limit
 
iptables -N icmp_forward_limit
   
iptables -A icmp_forward_limit ${=ICMP_FORWARD_LIMIT} -j ACCEPT
+
iptables -A icmp_forward_limit $ICMP_FORWARD_LIMIT -j ACCEPT
 
iptables -A icmp_forward_limit -j DROP
 
iptables -A icmp_forward_limit -j DROP
   
972. sor: 1 018. sor:
 
done
 
done
   
iptables -A icmp_forward ${=LOGLIMIT} -j LOG --log-prefix "FW: offensive ICMP: "
+
iptables -A icmp_forward $LOGLIMIT -j LOG --log-prefix "FW: offensive ICMP: "
 
iptables -A icmp_forward -j DROP
 
iptables -A icmp_forward -j DROP
 
</pre>
 
</pre>
982. sor: 1 028. sor:
 
iptables -N icmp_input_limit
 
iptables -N icmp_input_limit
   
iptables -A icmp_input_limit ${=ICMP_IN_LIMIT} -j ACCEPT
+
iptables -A icmp_input_limit $ICMP_IN_LIMIT -j ACCEPT
 
iptables -A icmp_input_limit -j DROP
 
iptables -A icmp_input_limit -j DROP
   
990. sor: 1 036. sor:
 
done
 
done
   
iptables -A icmp_input ${=LOGLIMIT} -j LOG --log-prefix "FW: offensive ICMP: "
+
iptables -A icmp_input $LOGLIMIT -j LOG --log-prefix "FW: offensive ICMP: "
 
iptables -A icmp_input -j DROP
 
iptables -A icmp_input -j DROP
 
</pre>
 
</pre>
999. sor: 1 045. sor:
 
iptables -N smtp_refused
 
iptables -N smtp_refused
   
iptables -A smtp_refused ${=LOGLIMIT} -j LOG --log-prefix "FW: SMTP greylist: "
+
iptables -A smtp_refused $LOGLIMIT -j LOG --log-prefix "FW: SMTP greylist: "
 
iptables -A smtp_refused -p tcp -j REJECT --reject-with tcp-reset
 
iptables -A smtp_refused -p tcp -j REJECT --reject-with tcp-reset
 
iptables -A smtp_refused -j DROP
 
iptables -A smtp_refused -j DROP
1 007. sor: 1 053. sor:
 
readacl $ACLDIR/smtp-whitelist \
 
readacl $ACLDIR/smtp-whitelist \
 
| while read i; do
 
| while read i; do
iptables -A smtp -s ${i} -j ACCEPT
+
iptables -A smtp -s $i -j ACCEPT
 
done
 
done
   
1 017. sor: 1 063. sor:
   
 
# If neither (new IP), remember IP and REJECT
 
# If neither (new IP), remember IP and REJECT
iptables -A smtp ${=LOGLIMIT} -j LOG --log-prefix "FW: SMTP greylist: "
+
iptables -A smtp $LOGLIMIT -j LOG --log-prefix "FW: SMTP greylist: "
 
iptables -A smtp -p tcp -m recent --set --name SMTP_grey -j REJECT --reject-with tcp-reset
 
iptables -A smtp -p tcp -m recent --set --name SMTP_grey -j REJECT --reject-with tcp-reset
 
iptables -A smtp -j DROP
 
iptables -A smtp -j DROP
1 027. sor: 1 073. sor:
 
iptables -N tcp_rst_forward
 
iptables -N tcp_rst_forward
   
iptables -A tcp_rst_forward ${=TCP_RST_FORWARD_LIMIT} -j ACCEPT
+
iptables -A tcp_rst_forward $TCP_RST_FORWARD_LIMIT -j ACCEPT
iptables -A tcp_rst_forward ${=LOGLIMIT} -j LOG --log-prefix "FW: tcp rst forward DROP: "
+
iptables -A tcp_rst_forward $LOGLIMIT -j LOG --log-prefix "FW: tcp rst forward DROP: "
 
iptables -A tcp_rst_forward -j DROP
 
iptables -A tcp_rst_forward -j DROP
 
</pre>
 
</pre>
1 044. sor: 1 090. sor:
 
# Doesn't scale well for long blacklists/high traffic
 
# Doesn't scale well for long blacklists/high traffic
 
readacl $ACLDIR/blacklist \
 
readacl $ACLDIR/blacklist \
| while read i; do
+
| while read -A i; do
iptables -A INPUT -s ${=i} -j blacklisted
+
iptables -A INPUT -s $i -j blacklisted
 
done
 
done
   
1 060. sor: 1 106. sor:
 
# Filter strange/spoofed packets
 
# Filter strange/spoofed packets
 
readacl $ACLDIR/martian \
 
readacl $ACLDIR/martian \
| while read i; do
+
| while read -A i; do
iptables -A INPUT ${=i} -j martian
+
iptables -A INPUT $i -j martian
 
done
 
done
   
 
# Enable all public services (with no ACLs)
 
# Enable all public services (with no ACLs)
 
readacl $ACLDIR/publicservices \
 
readacl $ACLDIR/publicservices \
| while read i; do
+
| while read -A i; do
iptables -A INPUT ${=i} -j ACCEPT
+
iptables -A INPUT $i -j ACCEPT
 
done
 
done
   
1 119. sor: 1 165. sor:
 
# Some stuff is too common to log, drop it silently
 
# Some stuff is too common to log, drop it silently
 
readacl $ACLDIR/silentdrop \
 
readacl $ACLDIR/silentdrop \
| while read i; do
+
| while read -A i; do
iptables -A INPUT ${=i} -j DROP
+
iptables -A INPUT $i -j DROP
 
done
 
done
   
 
# Everything else, we drop noisily.
 
# Everything else, we drop noisily.
iptables -A INPUT ${=LOGLIMIT} -j LOG --log-prefix "FW: input catch-all: "
+
iptables -A INPUT $LOGLIMIT -j LOG --log-prefix "FW: input catch-all: "
 
iptables -A INPUT -j DROP
 
iptables -A INPUT -j DROP
 
</pre>
 
</pre>
1 145. sor: 1 191. sor:
   
 
readacl $ACLDIR/blacklist \
 
readacl $ACLDIR/blacklist \
| while read i; do
+
| while read -A i; do
iptables -A FORWARD -s ${=i} -j blacklisted
+
iptables -A FORWARD -s $i -j blacklisted
 
done
 
done
   
 
readacl $ACLDIR/martian \
 
readacl $ACLDIR/martian \
| while read i; do
+
| while read -A i; do
iptables -A FORWARD ${=i} -j martian
+
iptables -A FORWARD $i -j martian
 
done
 
done
   
iptables -A FORWARD ${=LOGLIMIT} -j LOG --log-prefix "FW: forward catch-all: "
+
iptables -A FORWARD $LOGLIMIT -j LOG --log-prefix "FW: forward catch-all: "
 
iptables -A FORWARD -j DROP
 
iptables -A FORWARD -j DROP
 
</pre>
 
</pre>
1 162. sor: 1 208. sor:
 
* Annak ellenére, hogy látszólag az iptables-t hívja, iptables-restore-ral fogja betölteni a szabályokat, atomi módon
 
* Annak ellenére, hogy látszólag az iptables-t hívja, iptables-restore-ral fogja betölteni a szabályokat, atomi módon
 
* Gond: hosszú ACL-ek kezelése nem hatékony, főleg, ha több nagy ACL direktszorzatára van szükség
 
* Gond: hosszú ACL-ek kezelése nem hatékony, főleg, ha több nagy ACL direktszorzatára van szükség
** Pl. a tűzfal mögött van 32 db domino-szerver, és ezeket 100 kliens érheti el => 3200 szabály
+
** Pl. a tűzfal mögött van 32 db Lotus Domino-szerver, és ezeket 100 kliens érheti el => 3200 szabály
** Megoldás (lesz majd): ipset
+
** Megoldás: ipset
   
 
== Dinamikus hosztnevek IP-változásainak követése ==
 
== Dinamikus hosztnevek IP-változásainak követése ==

A lap 2021. április 9., 18:04-kori változata

Az iptables a Linux kernelben található Netfilter csomagszűrő userspace komponense; egy olyan program, amellyel a Netfilter beállításait kezelhetjük.

A továbbiakban nem teszünk éles különbséget az iptables és a Netfilter között; a szövegkörnyezetből mindig egyértelmű lesz, melyikről van szó.

Tartalomjegyzék

1 Fogalmak

Az iptables működésének megértéséhez fontos tudni, mi a csomagszűrő (packet filter) és mi a kapcsolatkövető tűzfal (connection tracking firewall).

1.1 Tűzfal

A tűzfal általánosságban egy olyan hálózati eszköz (általában router), amely kettő vagy több hálózatot köt össze és a rajta áthaladó forgalmat "jólformálttá" teszi annak érdekében, hogy az egyes interface-eihez tartozó hálózatokat kölcsönösen megvédje a többiből érkező "érvénytelen" (vagy legalábbis érdektelen), esetleg rossz szándékú kommunikációtól.

Különböző tűzfalmegoldásokkal más-más mértékű "jólformáltság" érhető el.

Tűzfalakkal kikényszeríthetjük a biztonsági/hálózati házirend betartását is.

1.2 Csomagszűrő

A csomagszűrő minden egyes csomagról önmagában dönti el, hogy áthaladhat-e a tűzfalon. A döntéshez felhasználhat mindent, amit az adott csomagról tud, akár a tartalmát is, de általában a fejléc alapján születik a döntés.

Előnye, hogy állapotmentes, így elvileg akárhány kapcsolatot tud kezelni.

Hátránya, hogy buta és rugalmatlan: pl. nem tudja megállapítani, hogy egy új TCP-kapcsolat egy már fennálló FTP-kapcsolathoz tartozó adatkapcsolat-e, vagy hogy a 443-as porton SSH- vagy https-forgalom zajlik.

A Netfilterrel építhetünk ilyet, de általában jobb kapcsolatkövető tűzfalat csinálni (kivéve, ha a kapcsolatkövetés miatt keletkező többletterhelés túl nagy).

1.3 Kapcsolatkövető csomagszűrő

Olyan tűzfal, amely nyomon követi a rajta keresztül felépített hálózati kapcsolatok állapotát, és ezt az információt is fel tudja használni a döntés során.

Az állapot-információ nyilvántartásához memóriára van szüksége; minél több kapcsolatot tart nyilván, annál több memóriára. Ezért aztán DoS-támadás indítható ellene úgy, hogy a támadó sok-sok kapcsolatot nyit; egy idő után vagy elfogy a tűzfal memóriája, vagy eldob új vagy régi kapcsolatokat, vagy - ha nem megfelelő adatszerkezetet használ - a használhatatlanságig lelassul.

1.4 NAT, PAT

Network Address Translation, Port Address Translation: a tűzfalon átmenő kapcsolatokban átírja a cél- és/vagy forrásportot és/vagy -címet valami másra, az adott kapcsolathoz tartozó összes csomagban ugyanúgy.

A Linux összemossa a tűzfalfunkcióval.

1.5 Proxy-alapú tűzfal

Olyan tűzfal, amely nem a hálózati, hanem az alkalmazási rétegben működik, és a rajta átmenő forgalmat is alkalmazásszinten képes vizsgálni/módosítani/szűrni.

Pl:

  • A pop3-mal letöltött levelekből ki tudja szűrni a vírust/spamet.
  • SSH-ban tudja tiltani a tunnelezést úgy, hogy az interaktív kapcsolatot viszont engedélyezi.
  • Ki tudja kényszeríteni, hogy a kliensek csak olyan HTTPS-szerverrel tudjanak kommunikálni, amelynek a tanúsítványa valamilyen megadott szempontrendszer szerint "rendben van".
    • Ez úgy működik, hogy a tűzfalban van egy hitelesítő hatóság (CA), amiben a kliens megbízik;
    • a https-kapcsolatot a tűzfal TCP-szinten a saját IP-címére irányítja;
    • mielőtt a kliensnek válaszolna, ő maga SSL handshake-et kezdeményez az eredeti célszerverrel;
      • így megtudja, annak a tanúsítványában milyen Common Name attribútum szerepel;
    • ellenőrzi, hogy a szerver tanúsítványa megfelel-e a helyi előírásoknak (nem vonták-e vissza, megbízható hatóság írta-e alá, nem járt-e le stb.);
    • ha a szerver tanúsítványa megfelelő, akkor a tűzfal készít egy saját tanúsítványt, ami a távoli szerver nevére (Common Name) szól, és a saját CA-jával aláírja;
    • ezt a tanúsítványt mutatja a kliensnek, amely el is fogadja, mivel a tűzfal CA-jában megbízik;
    • ezután a titkosítás két különálló szakaszon valósul meg: a kliens és a tűzfal között ill. a tűzfal és a szerver között van egy-egy HTTPS session.
    • Így a tűzfal az elvileg titkosítottan átvitt adatok belsejében is tud pl. vírust keresni.
    • Ha a szerver tanúsítványa nem volt megfelelő, a proxy elutasítja a kapcsolat felépítését, és a kliens így járt.
    • Ezt semmilyen alacsonyabb szinten működő proxy nem tudja megcsinálni.
    • A Zorp biztosan tud ilyet; valószínűleg a Microsoft ISA is.

Hátrányok:

  • minden alkalmazást (protokollt) külön kell támogatni;
    • ide értve a nem szabványos megvalósításokat is;
    • általában minden protokollhoz külön proxyt kell futtatni;
    • ha fejlődik a protokoll, a proxyt külön fejleszteni kell;
  • nagyobb összetettség -> több hibalehetőség (esetleg sebezhetőség);


1.6 Fogalmak a Netfilter/iptables körül

Alulról felfelé haladva az iptables a következő fogalmakkal dolgozik:

  • match (illesztés): egy feltétel, amelynek egy csomag meg kell, hogy feleljen. Pl.:
    • forrás-IP
    • protokoll
    • célport
    • a kapcsolat állapota
    • melyik interface-en jön be vagy megy ki
    • rengeteg más match van
  • akció (target): döntés arról, hogy mi történjen egy csomaggal, pl.:
    • engedélyezés
    • eldobás
    • visszautasítás (pl. TCP RST-vel vagy icmp admin-prohib üzenettel)
    • ugrás másik láncra (l. lejjebb)
    • van még jópár akció
  • szabály (rule): ÉS kapcsolatban levő matchek és egy akció együttese. Ha minden match illeszkedik, az adott csomagon az adott akciót kell végrehajtani.
  • lánc (chain): a szabályokat "láncokra" tudjuk felfűzni. Ha egy csomag eljut egy láncba, akkor a kernel sorban minden szabályt kiértékel, amíg az első olyat meg nem találja, amire a csomag illeszkedik. Ennek az akcióját végrehajtja, és a későbbi szabályokat figyelmen kívül hagyja (de van olyan akció is, ami nem "végleges", pl. a naplózás; ha ilyen volt az illeszkedő szabályban, akkor a lánc kiértékelése a következő szabállyal folytatódik).
    • láncokat definiálhatunk mi is, de van néhány speciális, beépített lánc (l. később)
    • a beépített láncoknak lehet "policy"-je, ami azt adja meg, mi történjen azokkal a csomagokkal, amelyekről egyetlen szabály sem rendelkezett
  • tábla: láncok vannak benne. Bizonyos akciók csak bizonyos táblákban értelmezettek; pl. van nat tábla, amiben NATolhatunk. A csomagok meghatározott sorrendben járják be a táblákat.

A csomag útjának megértésében sokat segít a - sajnos nem feltétlenül naprakész, teljes és pontos, de ASCII-ban is nézhető - "Kernel Packet Traveling Diagram", vagy a valamivel frissebb, SVG-formátumú Netfilter Packet Flow Diagram.

2 Táblák

  • filter: INPUT, OUTPUT és FORWARD lánc; szűrésre való
  • nat: PREROUTING, OUTPUT és POSTROUTING lánc; címfordításra való
  • mangle: PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING lánc; bizonyos csomagmódosításokat lehet itt elvégezni
  • raw: PREROUTING és OUTPUT lánc; megjelölhetjük benne azokat a csomagokat, amelyekre nem kérünk kapcsolatkövetést.

3 Az iptables parancssora

  • iptables [-t table] -A chain rule-specification [options]
új szabály felvétele a lánc végén
  • iptables [-t table] -D chain rule-specification [options]
szabály törlése a láncból a törlendő szabály megadásával
  • iptables [-t table] -D chain rulenum [options]
szabály törlése a láncból a sorszáma megadásával
  • iptables [-t table] -I chain [rulenum] rule-specification [options]
szabály beszúrása a láncba (scriptben csak nagy körültekintéssel használjuk)
  • iptables [-t table] -R chain rulenum rule-specification [options]
szabály cseréje (scriptben csak nagy körültekintéssel használjuk)
  • iptables [-t table] -L [chain] [options]
lánc listázása
  • iptables [-t table] -F [chain] [options]
lánc törlése (flush)
  • iptables [-t table] -Z [chain] [options]
csomag- és byte-számlálók törlése
  • iptables [-t table] -N chain
új lánc létrehozása
  • iptables [-t table] -X [chain]
lánc törlése (csak, ha már nincs benne szabály, és rá se hivatkozik szabály)
  • iptables [-t table] -P chain target [options]
policy beállítása
  • iptables [-t table] -E old-chain-name new-chain-name
lánc átnevezése

Példa pár szabály felvételére:

iptables -A INPUT -p tcp --dport ssh -s 1.2.3.4 -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j REJECT --reject-with tcp-reset
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

A -j-vel ugorhatunk más láncra is:

iptables -N ssh_input
iptables -A INPUT -p tcp --dport ssh -j ssh_input
iptables -A ssh_input -s 1.2.3.4 -j ACCEPT
iptables -A ssh_input -s 2.3.4.5 -j ACCEPT
iptables -A ssh_input -s 3.4.5.192/26 -j ACCEPT
iptables -A ssh_input -m limit --limit 3/minute -j LOG --log-prefix "FW: ssh_input REJECT: "
iptables -A ssh_input -p tcp -j REJECT --reject-with tcp-reset
iptables -A ssh_input -j DROP

Itt -j ("jump") helyett írhattam volna -g-t ("goto") is; a különbség az, hogy -j esetén, ha a meghívott láncban nincs illeszkedő szabály, akkor a -j-s szabály után folytatódik a szabályok kiértékelése (tehát ez "gosub"), míg -g esetén, ha nincs egyezés, akkor az utolsó kiértékelt -j után. Mi van, ha beépített láncban használjuk a goto-t?

Sem a matchek, sem az akció megadása nem kötelező. Akciómentes szabályt használhatunk pl. forgalomszámolásra, mivel minden szabályhoz tartozik két számláló, amelyek az illeszkedő csomagokat ill. az általuk szállított byte-okat számolják.

4 Kapcsolók

  • -c: csomag- és byte-számlálók kézi beállítása
  • -n: ne legyen reverse-DNS-feloldás a listázáskor (ott van helyette az adnsresfilter, ha muszáj)
  • -v: szószátyárabb output (nemcsak listázásnál)
  • -x: számlálók pontos értékkel, nem SI prefixekkel jelennek meg
  • --line-numbers: minden szabály elé odaírja, hanyadik a láncban
    • ez ugye az -I, -D, -R parancsok előtt hasznos...

5 Match-ek

  • -p, --protocol protokoll
  • -s, --source cím[/maszk]: cím lehet hosztnév is; ha több IP tartozik hozzá, minden IP-hez külön szabály képződik
  • -i, --in-interface név
  • -o, --out-interface név
  • -f, --fragment
  • --icmp-type típus
  • --sport, --dport port[:port]: tcp, udp forrás- ill. célport
  • --tcp-flags vizsgálandók melyiklegyenbeállítva
  • --syn == --tcp-flags SYN,RST,ACK,FIN SYN
  • -m modulnév: bővített matchek. Ezek közül néhány érdekesebb:
    • -m account: forgalomstatisztika, házi feladat; [1]
    • -m addrtype: forrás- vagy célcím osztálya (UNICAST, BROADCAST, MULTICAST stb.)
    • -m childlevel: "főkapcsolat" vagy "gyermekkapcsolat" (pl. ftp-data)
    • -m comment: no-op, kommentálni lehet vele a szabályt: iptables -A INPUT -s 192.168.0.0/16 -m comment --comment "A privatized IP block" -j valahova
    • -m condition: létrehoz nekünk egy file-t a /proc-ban, és attól függően illeszkedik vagy nem, hogy abba a file-ba egyest vagy nullát írunk
    • -m connbytes: az adott kapcsolathoz tartozó adatforgalom nagysága alapján matchel (csomagszám, byte-szám vagy átlagos csomagméret)
    • -m connlimit: kapcsolatok számának korlátozása. Példák:
# allow 2 telnet connections per client host
iptables -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT

# you can also match the other way around:
iptables -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT

# limit the nr of parallel http requests to 16 per class C sized network (24 bit netmask)
iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16 --connlimit-mask 24 -j REJECT
    • -m connmark: a kapcsolatokat meg lehet jelölni a mangle táblában; ezzel a matchel vizsgáljuk, hogy egy adott kapcsolaton egy adott jelzés van-e
    • -m connrate: a kapcsolat aktuális adatátviteli sebességét nézi
    • -m conntrack: a kapcsolatkövető mechanizmus belső paraméterei alapján matchel. Értelmes felhasználás: házi feladat. :)
    • -m ecn: explicit congestion notification, RFC3168: házi feladat
    • -m fuzzy: fuzzy ratelimit. Értelmes felhasználás: házi feladat. :)
    • -m hashlimit: rátalimit, de nem szabályonként, hanem szabályonként és cél- vagy forráscímenként vagy cél- vagy forrásportonként. "Egyetlen kliens se kapcsolódhasson másodpercenként 5-nél többször az SMTP-szerverre."
    • -m iprange: net/maszk helyett ip1-ip2 alakban adható meg tartomány
    • -m length: csomagméret
    • -m limit: rátalimit. A szabály legfeljebb x alkalommal illeszkedik y idő alatt (lyukasvödörrel van megcsinálva, úgyhogy egy véges börsztöt is elfogad). Pl. logoláshoz jó.
    • -m mac: ethernet-forráscím
    • -m mark: a kapcsolatok megjelöléséhez hasonlóan egyes csomagokat is meg lehet jelölni, erre illeszthetünk ezzel a match-csel
    • -m mport: több port sorolható fel
    • -m multiport: majdnem ugyanaz, de a felsorolásban szerepelhet tartomány is
    • -m nth: minden n. alkalommal illeszkedik (pl. minden n. csomagot logolunk, vagy a lakótárs minden 4. torrentcsomagját eldobjuk, hátha magától is leáll a torrentezéssel :)
    • -m osf: passzív OS fingerprinting (pl. "csak linuxos gépek küldhessenek levelet"). Nem bombabiztos, inkább játék.
    • -m owner: a csomagot küldő folyamat UID, GID, PID, SID (session ID) értékei, ill. a processz neve alapján illeszt
    • -m physdev: bridge interface esetén a fizikai interface-re illeszt
    • -m pkttype: adatkapcsolati rétegbeli címtípus (unicast|broadcast|multicast)
    • -m psd: portscan-detektor. Nemigen van értelme.
    • -m quota: forgalmi kvótát lehet csinálni vele (az adott szabályra illeszkedő csomagok csökkentik a byte-számlálót). Többprocesszoros rendszeren jelenleg nem működik.
    • -m random: véletlenszerűen illeszkedik (vagy nem); pl. véletlen csomagvesztés szimulálására jó (vagy lakótárs torrentezési szokásainak megváltoztatására)
    • -m recent: táblázatot csinálhatunk vele azokról az IP-kről, amelyeket valamilyen kontextusban "láttunk", aztán később vizsgálhatjuk, hogy elemei-e a táblázatnak; pl. primitív SMTP greylistingre jó, de sokminden másra is lehet használni: házi feladat
    • -m ipset: egyetlen szabállyal több IP-re is illeszthetünk (ez pl. akkor jó, ha ACL-t akarunk csinálni); külön programmal, az ipset-tel lehet az egyes halmazokat adminisztrálni. L. később. Egyelőre sajnos nincs benne a hivatalos kernelben.
    • -m state: a kapcsolat állapota
    • -m string: a csomag tartalma
    • -m time: dátumhoz ill. napszakhoz kötött illesztés
    • -m tos
    • -m ttl

6 Akciók

  • ACCEPT
  • REJECT
  • DROP
  • LOG: naplóüzenetet generál, de folytatja a következő szabállyal
  • MARK
  • SNAT
  • DNAT
  • RETURN
  • MASQUERADE
  • CLASSIFY: forgalmi osztályba sorolás (aztán az osztálynak lehet sorbanállási prioritása, l. Linux Advanced Routing and Traffic Control HOWTO)
  • CLUSTERIP: elvileg terheléselosztó clustert lehet vele csinálni. Házi feladat. :)
  • CONNMARK: a kapcsolathoz tartozó jelölés beállítása
  • IPMARK: a forrás-IP-címből számítja ki a csomagra illesztendő jelölést (akkor jó, ha rengeteg usernek egyéni sávszélességkorlátokat ill. saját várakozási sort szeretnénk csinálni)
  • IPV4OPTSSTRIP: leszedi az opciókat az IPv4-es csomagokról - mikor van értelme?
  • LOG: naplózza az illeszkedő csomagokat; a feldolgozás a következő szabálynál folytatódik.
  • MARK: csomag megjelölése
  • NETMAP: tömeges NAT, címtér-tömörítés nélkül (pl. egy /16-os hálózatot leképez egy másikra)
  • QUEUE/NFQUEUE: átadja a csomagot egy userspace programnak (ami már fut és regisztrálta magát a kernelben)
  • NOTRACK: az illeszkedő csomagokra nem kérünk kapcsolatkövetést. Erőforrást takarít meg; mondjuk beállíthatjuk a DNS-szerverünk felé menő 53/udp csomagokon.
  • REDIRECT: átírja a célcímet a lokális gép címére, a portot megadhatjuk (pl. transzparens http-proxyhoz)
  • ROUTE: routing-gányolás. Megadhatjuk, melyik interface-en menjen ki a csomag, vagy hogy melyiken jött be (!), vagy hogy melyik gatewayen át kell küldeni. Le is tudja másolni a csomagot, így lehet pl. monitoring portot eszkábálni linuxos switchre.
  • SET: hozzáadja a forrás- vagy cél-IP-t vagy -portot egy ipset-hez, vagy törli belőle (l. később)
  • TARPIT: tcp-s "szurokgödör"; váratja a túloldalt, a kapcsolatot nem hagyja lebontani (pl. féregterjedés lassítására ajánlják - de csak buta féreg ellen jó, ill. buta portscan ellen is)
  • TRACE: bekapcsolja a nyomkövetést az adott csomagon. Naplóüzenet keletkezik, valahányszor a csomag illeszkedik egy szabályra.
  • TTL: a TTL-mezőt állítgathatjuk vele - miért akarnánk ilyet csinálni?
  • ULOG: összetett naplózás. Házi feladat.
  • XOR: egyszerű "titkosítás". Közben, úgy tűnik, megszűnt.

7 Tűzfalscriptek írása

Általános jótanács: ha nem ülünk a gép előtt, mindig hagyjunk egérutat új tűzfalkonfiguráció élesítésekor; nem jó egy elgépelés miatt utazni.

7.1 Naiv módszer

  • Rakjuk össze a szabályrendszert a parancssorból. Az iptables-save elmenti, utána rebootkor, vagy amikor kell, az iptables-restore-ral betölthetjük.
  • Előnyök:
    • Egyszerű
    • Gyorsan megvan
    • Újratöltéskor a szabályok betöltése atomi
    • Viszonylag könnyű permanens módosításokat automatizálni
  • Hátrányok:
    • Karbantartás nehézkes
    • Dinamikus szabályok készítése nehézkes (pl. dinamikus hostnevek IP-címeinek követése)
      • Bár ipset-tel megoldható
    • Kommentálni csak az egyes szabályok szintjén lehet; hosszabb magyarázatot nem tudunk elhelyezni közben
    • Könnyen átláthatatlanná válik
    • Nem választja el a "kódot" a konfigurációtól
    • Nehéz a konfiguráció bizonyos részeit más gépen újrahasznosítani
    • Nehéz nem azonos, de hasonló tűzfalakat szinkronban tartani
    • Jogok részleges delegálása lehetetlen

Példa:

# iptables -t nat -A POSTROUTING -j MASQUERADE -o ppp0 -m comment --comment "Replace source address on outgoing packets"
# iptables        -A FORWARD     -j ACCEPT             -m comment --comment "Explicitly no filtering"
# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables-save >/etc/firewall/saved

Figyelem! Ez a példa nem jó!

  • Ész nélkül mindent forwardol mindenhonnan mindenhova
  • INPUT-szűrés sincs benne
  • Ez kb. egy minimális ADSL-megosztási konfiguráció
  • De legalább kommentezve van!

7.2 Spagettiscript

  • Írjunk egy monolitikus scriptet, ami először töröl minden szabályt, majd betölti a szükséges szabályokat
  • Előnyök:
    • Egyszerű
    • Viszonylag gyorsan megvan
    • Dinamikus szabályokat is megadhatunk (betöltéskor lesz DNS lookup)
    • A script kommentálható
    • Minimális mértékben elválasztható a kód és a konfiguráció (pl. a script elején shell-változókban beállíthatjuk a "konfigurációt")
    • A script bizonyos részeit átvihetjük más gépre
  • Hátrányok:
    • Ha több dinamikus szabály is felhasználja ugyanazt a hosztnevet, feleslegesen lassú
      • Különösen, ha az adott domain DNS-szerverei egyáltalán nem válaszolnak
    • Könnyen átláthatatlanná válik
    • Nehéz nem azonos, de hasonló tűzfalakat szinkronban tartani
    • A tűzfalszabályok betöltése nem atomi
    • Nehéz permanens módosításokat automatizálni
    • Jogok részleges delegálása lehetetlen

Példa:

#!/bin/sh
# Remove any existing rules from all chains
iptables -F
iptables -F -t nat
iptables -F -t mangle

# Remove any pre-existing user-defined chains
iptables -X
iptables -X -t nat
iptables -X -t mangle

# Zero counters
iptables -Z

# Set the default policy to drop
iptables -P INPUT   DROP
iptables -P OUTPUT  DROP
iptables -P FORWARD DROP

# Allow unlimited traffic on the loopback interface
iptables -A INPUT   -j ACCEPT -i lo 
iptables -A OUTPUT  -j ACCEPT -o lo

iptables -A INPUT   -j ACCEPT -m state --state ESTABLISHED,RELATED 

iptables -A INPUT   -j ACCEPT -p tcp --dport ssh   -s 1.2.3.4

iptables -A FORWARD -j ACCEPT -m state --state ESTABLISHED,RELATED 

iptables -A FORWARD -j ACCEPT -p tcp --dport www   -d 2.3.4.5 
iptables -A FORWARD -j ACCEPT -p tcp --dport https -d 2.3.4.5 

iptables -A FORWARD -j ACCEPT -p tcp --dport smtp  -d 2.3.4.6

iptables -A FORWARD -j ACCEPT -p tcp --dport www   -s 2.3.4.7 # http proxy can talk to net
iptables -A FORWARD -j ACCEPT -p tcp --dport https -s 2.3.4.7 # http proxy can talk to net
iptables -A FORWARD -j ACCEPT -p tcp --dport smtp  -s 2.3.4.6 # mailserver too

# intranet clients can access DMZ
iptables -A FORWARD -j ACCEPT -i eth2 -o eth1 -m state --state NEW

iptables -A OUTPUT  -j ACCEPT -m ESTABLISHED,RELATED
iptables -A OUTPUT  -j ACCEPT                      -d 1.2.3.4
iptables -A OUTPUT  -j ACCEPT         -o eth1  # firewall can access DMZ
  • Ez sem igazán jó példa, inkább állatorvosi ló
  • Ami követendő lehet belőle: előre írtam a rendelkező részt, és "táblázatszerűen" tagoltam a scriptet, így jobban olvasható, mint ömlesztve

7.3 Strukturált script

  • "Shell-tömbök" adják a konfiguráció ACL-jeit (pl. WEBSERVERS="192.168.1.2 192.168.1.3 192.168.1.4")
    • Főként zsh-ban érdemes valódi tömböket használni: WEBSERVERS=(192.168.1.2 192.168.1.3 192.168.1.4)
  • Shell-függvények felelősek bizonyos láncok összeállításáért (pl. ACCEPT() proto tcp from 0/0 to "$WEBSERVERS" srcport any dstport 80 parent INPUT)
  • Vagy ha nem is függvények, legalább ciklusok
  • A DNS-alapú szabályokhoz szükséges DNS-feloldásokat egyszer végezzük el, az eredményt tároljuk és újrafelhasználjuk
  • Előnyök:
    • Jól szétválasztja a kódot és a konfigurációt
    • Átláthatóvá tehető
    • Aránylag gyorsan lefut (bár sok szabály felvétele még mindig lassú)
  • Hátrányok:
    • A shell-függvényeket meg kell hozzá írni, és ez nem feltétlenül triviális (vagy: van kész megoldás?)
    • A kódot rajtunk kívül senki sem érti majd
    • A tűzfalszabályok betöltése nem atomi
    • Nehéz (bár már könnyebb) nem azonos, de hasonló tűzfalakat szinkronban tartani
    • Nehéz permanens módosításokat automatizálni
    • Jogok részleges delegálása lehetetlen
    • Megnehezíti az implementációt, ha olyan DNS-feloldásokat is cache-elni akarunk, amelyek egynél több címet adnak vissza
    • Az ipset igazából sokkal elegánsabb megoldás, mint a shell-tömb, és 2021-ben már teljesen használható is

Példa (figyelem! nem jó!):

#!/bin/zsh

LOCK=/var/run/firewall.lock
[ -f $LOCK ] && exit 0 # Pozor! Ez így nem jó! Versenyhelyzet! Így nem lehet shellben kölcsönös kizárást csinálni!
touch $LOCK

myip=1.2.3.4
broadcast=1.2.3.255

function iptables() {
	/sbin/iptables "$@" || echo "failed: iptables $@"
}

for i in ip_tables iptable_filter ipt_limit ipt_mac ipt_owner ip_nat_ftp ip_nat_irc ipt_unclean ipt_REJECT ipt_REDIRECT; do
	modprobe $i 2>/dev/null >/dev/null
done

#
# CHAINS: Ezeket a chaineket kell letrehozni
#
CHAINS=(trusted samba ftp squid printer icmpch telnet mysql
fwdrop spoof icmpdrop trustrej xrej localrej ftprej fullban squidrej)

#
# LOCALONLY a csak localhostrol mukodo szolgaltatasok listaja
#
LOCALONLY=(mysql)

#
# TRUSTONLY: Ami csak a "trusted" gepekrol megy
#
TRUSTONLY=(samba printer telnet)

#
# TOTALBAN: Akiktol semmit nem fogadunk
#
TOTALBAN=(
2.3.4.5
3.4.5.0/24
)

#
# A TRUSTED-ben azokat az ip-ket soroljuk fel, ahonnan a legtobb dolgot
# engedjuk (pl. printert, sambat is).
#
TRUSTED=(
1.2.3.0/24
10.1.2.128/25
azengepem.dyndns.org
)

iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -F
iptables -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP

# Uj chain-ek letrehozasa
for i in "$CHAINS[@]" ; do
	iptables -N $i
done

iptables -A fwdrop -m limit --limit 10/minute -j LOG --log-prefix "FORWARD DROP: "
iptables -A fwdrop -j DROP

iptables -A spoof -m limit --limit 10/minute -j LOG --log-prefix "spoofed packet: "
iptables -A spoof -j DROP

# Spoof protection
iptables -A INPUT -i tap+ -j ACCEPT
iptables -A OUTPUT -o tap+ -j ACCEPT
iptables -A INPUT -i br+ -j ACCEPT
iptables -A OUTPUT -o br+ -j ACCEPT
# az etherneten fogadjuk a tolunk jovo broadcastokat es multicastokat
iptables -A INPUT -i eth+ -s $myip -d $broadcast -j ACCEPT
iptables -A INPUT -i eth+ -s $myip -d 224.0.0.0/4 -j ACCEPT
# nem fogadjuk a .0 vegu cimre kuldott csomagokat, az nem hivatalos broadcast
iptables -A INPUT -i eth+ -d 1.2.3.0/32 -j DROP
# Az etheren nem fogadjuk a 127.0.0.akarhanyas IP-rol jovo csomagokat
iptables -A INPUT -i eth+ -s 127.0.0.0/8 -j spoof
# Az etheren nem fogadjuk a tolunk jovo csomagokat
iptables -A INPUT -i eth+ -s $myip -j spoof
# fogadjuk a nem-icmp jellegu teljes broadcastokat
iptables -A INPUT -i eth+ -p ! icmp -d 255.255.255.255 -j ACCEPT

# Trusted chain felepitese
for i in "$TRUSTED[@]"; do
	iptables -A trusted -s $i -j ACCEPT
done
iptables -A trusted -i lo -j ACCEPT
iptables -A trusted -j trustrej

# Csak trusted hostokrol elerheto:
for i in "$TRUSTONLY[@]"; do 
	iptables -A $i -j trusted
	iptables -A $i -j trustrej
done

iptables -A trustrej -d $broadcast -j DROP
iptables -A trustrej -m limit --limit 10/minute -j LOG --log-prefix "not trusted: "
iptables -A trustrej -j REJECT --reject-with icmp-port-unreachable

for i in "$LOCALONLY[@]"; do
	iptables -A $i -s $myip -j ACCEPT
	iptables -A $i -i lo -j ACCEPT
	iptables -A $i -j localrej
done

iptables -A localrej -m limit --limit 10/minute -j LOG --log-prefix "not local: "
iptables -A localrej -j REJECT --reject-with icmp-port-unreachable

for i in ftp telnet printer; do
	iptables -A INPUT -p tcp --dport $i -j $i
done

[...]
  • Ez minden volt, csak átlátható nem, pedig fiatal korában olyannak indult

7.4 Strukturált, modularizált script

  • Mint fent, de rc.d mechanizmust használva szétszedjük sok kicsi file-ra
  • Az ACL-ekhez használjunk felsorolás-file-okat (ha ezek include-olhatják egymást, az tovább csökkenti az adminisztrációs terheket)
  • Előnyök:
    • Teljesen szétválasztja a kódot és a konfigurációt
    • Átláthatóvá tehető
    • Könnyű nem azonos, de hasonló tűzfalakat szinkronban tartani
    • Jogok részleges delegálása körülményes, de megoldható
  • Hátrányok:
    • A shell-függvényeket meg kell hozzá írni (bár van kész megoldás, az "iptablez" by yours truly)
    • A kódot rajtunk kívül senki sem érti majd
    • A tűzfalszabályok betöltése nem atomi
  • További javítási lehetőség:
    • Ne az iptables-t hívogassuk, hanem állítsunk elő iptables-restore formátumú szövegfile-t, majd a végén töltsük be azt; így atomivá válik a szabályok betöltése

Példa (ilyet használtam 2012 körül; ez az "iptablez"):

#!/bin/zsh

#exec 2>/tmp/fw2.log
#exec >/tmp/fw-out.log

BASEDIR=/etc/firewall

DEFAULTS=$BASEDIR/firewall-defaults

[[ -r "$DEFAULTS" ]] && . $DEFAULTS

# Itt a defaultokat nem előre állítjuk be, hogy aztán a config felülbírálja,
# hanem utólag, ha a configban nem volt megadva semmi; így is lehet, csak
# így a config nem állíthat változót üres sztringre
CONFDIR=${CONFDIR:-$BASEDIR/conf.d}
SCRIPTDIR=${SCRIPTDIR:-$BASEDIR/script.d}
PREDIR=${PREDIR:-$BASEDIR/pre.d}
POSTDIR=${POSTDIR:-$BASEDIR/post.d}
ACLDIR=${ACLDIR:-$BASEDIR/acl.d}
RESTOREDIR=$BASEDIR/restore
[[ "$1" = "" ]] || DEBUG=$1
DEBUG=${DEBUG:-1}
TRANSITIONAL_POLICY=${TRANSITIONAL_POLICY:-ACCEPT}      # what to set policies to while the firewall is being initialized
UNLOAD_MODULES=${UNLOAD_MODULES:-0}

function debug() {
        if [[ "$DEBUG" -ge "$1" ]]; then
                shift
                echo "$@"
        fi
}

[[ -d "$PREDIR" ]] && for depth in {10..1}; do
        find "$PREDIR" -mindepth $depth -maxdepth $depth ! -type d \
        | fgrep -v /.svn/ \
        | sort \
        | while read i; do
                PREFILES=($PREFILES $i)
        done
done

for script_i in $PREFILES[@]; do
        if [[ -r "$script_i" ]]; then
                debug 2 Sourcing "$script_i"...
                . "$script_i"
        else
                debug 1 WARNING: "$script_i" is unreadable.
        fi
done

debug 2 Sourcing functions...
. "$BASEDIR/functions"

iptables_init

# Can't assume no sourced script will read from stdin, so assmble list of
# files to source before sourcing first one:
for dir in "$CONFDIR" "$SCRIPTDIR"; do
        for depth in {10..1}; do
                find "$dir" -mindepth $depth -maxdepth $depth ! -type d \
                | fgrep -v /.svn/ \
                | sort \
                | while read i; do
                        FILES=($FILES $i)
                done
        done
done

# Sourced files likely contain loops with "i" as the loop variable...
for script_i in $FILES[@]; do
        if [[ -r "$script_i" ]]; then
                debug 2 Sourcing "$script_i"...
                . "$script_i"
        else
                debug 1 WARNING: "$script_i" is unreadable.
        fi
done

iptables_commit
ret=$?

[[ -d "$POSTDIR" ]] && for depth in {10..1}; do
        find "$POSTDIR" -mindepth $depth -maxdepth $depth ! -type d \
        | fgrep -v /.svn/ \
        | sort \
        | while read i; do
                        POSTFILES=($POSTFILES $i)
        done
done

for script_i in $POSTFILES[@]; do
        if [[ -r "$script_i" ]]; then
                debug 2 Sourcing "$script_i"...
                . "$script_i"
        else
                debug 1 WARNING: "$script_i" is unreadable.
        fi
done
exit $ret

Nézzük a functions file-t (ma már azért néhány dolgot másképp csinálnék, egyszer majd aktualizálom):

#
# scriptlets to ease the construction of iptables firewalls
#

# readacl()
#
# returns non-comment lines from the ACL file $1
# supports includes; a line starting with ". " specifies a filename to
# include, e.g.: ". $ACLDIR/other_file". Recursive including is supported;
# be careful to avoid loops! Also, very deep include trees may exhaust file
# descriptor limits. Shell variables in ACLs are expanded.
#
function readacl() {
        local i

        egrep -v '^[[:space:]#]*$|^#' "$1" \
                | sed 's/[[:space:]]*#.*//' \
                | while read -r i; do
                        if [[ "$i" == "${i#. }" ]]; then
                                echo "${(e)i}"
                        else
                                readacl "${(e)i#. }"
                        fi
                done \
                | egrep -v '^[[:space:]#]*$|^#' \
                | sed 's/[[:space:]]*#.*//'
}

# reads 'ipset' commands from $IPSET_CONF (/etc/firewall/ipset.conf by
# default) and executes them; supports shell variables and includes (since
# it uses readacl()). ipset comments should appear at the end of the ipset
# command line. Otherwise, ipset.conf is like 'ipset save' output.
function restore-ipsets() {
        local -a args line
        readacl ${IPSET_CONF:-/etc/firewall/ipset.conf} | while read -rA line; do
                while [[ -n $line[1] ]]; do
                        case $line[1] in
                                # when reading comment, remove quotes and parse the rest of the line into a single word:
                                comment)        shift line; args=($args comment "${line//\"/}"); unset line;;
                                *)              args=($args $line[1]); shift line;;
                        esac
                done
                if ! ipset $args; then
                        echo "failing ipset command was: $args" >&2
                fi
                unset args
        done 
}

#
# Stuff for the INPUT and OUTPUT chains (mostly anyway)
#
# buildchain(), buildtcpchain() and buildudpchain()
#

# buildchain()
#
# Usage: buildchain table PARENTCHAIN name parentselector childselector
# ACLFILE action1 action2 message2 silentdropacl silentdropaction
#
# childselector can be empty, in which case it should prefix each line in
# the ACLFILE.
#
# example: buildchain filter ssh INPUT "-p tcp --dport ssh" "-s" \
#       /etc/firewall/acls/ssh_allow ACCEPT REJECT "FW: ssh REJECT: "
#       /etc/firewall/acls/sshprobes 
#
# Will use LOGLIMIT, if set. If called with only one parameter, assumes it
# is a config file and will source it.
#

function buildchain() {
        if [[ "$2" = "" ]]; then
                . "$1"
        else
                local TABLE="$1"
                local PARENT_CHAIN="$2"
                local CHAIN="$3"
                local PARENT_SELECTOR="$4"
                local CHILD_SELECTOR="$5"
                local ACLFILE="$6"
                local PRIMARY_ACTION="$7"
                local SECONDARY_ACTION="$8"
                local SECMESSAGE="$9"
                local SILENTACL="$10"
                local SILENTDROP="${11:-DROP}"
        fi
        local i=0

        if iptables -t $TABLE -N $CHAIN; then   # We only build the chain if it didn't already exist.
                readacl "$ACLFILE" | while read i; do
                        iptables -t $TABLE -A $CHAIN ${=CHILD_SELECTOR} ${=i} -j ${=PRIMARY_ACTION}
                done
                [[ -n "$SILENTACL" ]] && readacl "$SILENTACL" | while read i; do
                        iptables -t $TABLE -A $CHAIN ${=i} -j ${=SILENTDROP}
                done
                [[ -n "$SECMESSAGE" ]] && iptables -t $TABLE -A $CHAIN ${=LOGLIMIT} -j LOG --log-prefix "$SECMESSAGE"
                iptables -t $TABLE -A $CHAIN -j ${=SECONDARY_ACTION}
        fi

        iptables -t $TABLE -A $PARENT_CHAIN ${=PARENT_SELECTOR} -j $CHAIN       # enable it in the parent chain
}

# buildtcpchain()
#
# Usage: buildtcpchain { servicename|portnumber }
#
# buildtcpchain ssh is equivalent to (a shorthand for):
#
# buildchain filter INPUT ssh_input "-p tcp --dport ssh" "-s"
#       $ACLDIR/ssh ACCEPT ${=REJECT} "FW: ssh DROP: "
#

function buildtcpchain() {
        local PORT="$1"
        local MYREJECT="${REJECT:-REJECT -p tcp --reject-with tcp-reset}"
        local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
        local CHAIN="${PORT}_input"
        local ACL="$MYACLDIR/${PORT}"
        local FRIENDLYNAME="${PORT}"
        local PARENT=INPUT
        shift
        while [[ ! "$1" = "" ]]; do
                case "$1" in
                        "-c")
                                shift
                                local CHAIN="${1:-${PORT}_input}"
                                shift
                                ;;
                        "-a")
                                shift
                                local ACL="${1:-$MYACLDIR/${PORT}}"
                                shift
                                ;;
                        "-n")
                                shift
                                local FRIENDLYNAME="${1:-tcp/$PORT}"
                                shift
                                ;;
                        "-p")
                                shift
                                local PARENT="$1"
                                shift
                                ;;
                        *)
                                debug 1 buildtcpchain ignoring unknown parameter \""$1"\".
                                shift
                                ;;
                esac
        done

        buildchain filter "$PARENT" "$CHAIN" \
                "-p tcp --dport $PORT" "-s" \
                "$ACL" \
                ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
}

# buildudpchain()
#
# Usage: buildudpchain { servicename|portnumber }
#
# buildudpchain ntp is equivalent to (a shorthand for):
#
# buildchain filter INPUT ntp_input "-p udp --dport ntp" "-s"
#       $ACLDIR/ntp ACCEPT ${=REJECT} "FW: ntp DROP: "
#

function buildudpchain() {
        local PORT="$1"
        local MYREJECT="${REJECT:-REJECT --reject-with tcp-reset}"
        local MYACLDIR="${ACLDIR:-/etc/firewall/acl.d}"
        local CHAIN="${PORT}_input"
        local ACL="$MYACLDIR/${PORT}"
        local FRIENDLYNAME="${PORT}"
        local PARENT=INPUT
        shift
        while [[ ! "$1" = "" ]]; do
                case "$1" in
                        "-c")
                                shift
                                local CHAIN="${1:-${PORT}_input}"
                                shift
                                ;;
                        "-a")
                                shift
                                local ACL="${1:-$MYACLDIR/${PORT}}"
                                shift
                                ;;
                        "-n")
                                shift
                                local FRIENDLYNAME="${1:-udp/$PORT}"
                                shift
                                ;;
                        "-p")
                                shift
                                local PARENT="$1"
                                shift
                                ;;
                        *)
                                debug 1 buildudpchain ignoring unknown parameter '"'"$1"'"'.
                                shift
                                ;;
                esac
        done

        buildchain filter "$PARENT" "$CHAIN" \
                "-p udp --dport $PORT" "-s" \
                "$ACL" \
                ACCEPT "$MYREJECT" "FW: $FRIENDLYNAME DROP: "
}

#
# Stuff for the FORWARD chain
#
# We need a function to police multipoint-multipoint traffic. pt-mpt, mpt-pt
# and pt-pt are special cases (which could be handled specially, but
# aren't). In fact, buildchain() can be used for these with an appropriately
# chosen parentselector.
#
# build_multipoint_chain() is the most generic function.
#
# It will create two chains, like this:
#
# something_1:
# -s sip1 -j something_2 # -s is childselector1
# -s sip2 -j something_2
# [...]
# -j LOG --log-message "FW: something source DROP: " # message1
# -j REJECT # REJECT is action2
#
# something_2:
# -d dip1 -j ACCEPT # -d is childselector2
# -d dip2 -j ACCEPT # ACCEPT is action1
# [...]
# -j LOG --log-message "FW: something destination DROP: " # message2
# -j REJECT # REJECT is action2
#
# Usage: build_multipoint_chain table PARENTCHAIN name parentselector childselector1
# ACLFILE1 childselector2 ACLFILE2 action1 action2 message1 message2
#
# This function performs an optimiziation based on the size of the ACLs
# (it is faster to use the smaller one first). It is your responsibility to
# not call it with chain names that already exist.

function build_multipoint_chain() {
	if [[ "$2" = "" ]]; then
		. "$1"
	else
		local TABLE="$1"
		local PARENT_CHAIN="$2"
		local CHAIN_1="${3}_1"
		local CHAIN_2="${3}_2"
		local PARENT_SELECTOR="$4"
		local ACLFILE_1="$6"
		local ACLFILE_2="$8"

		local ACL_1_SIZE="$(readacl "$ACLFILE_1" | wc -l)"
		local ACL_2_SIZE="$(readacl "$ACLFILE_2" | wc -l)"

		if [[ "$ACL_1_SIZE" -le "$ACL_2_SIZE" ]]; then
			local CHILD_1_SELECTOR="$5"
			local CHILD_2_SELECTOR="$7"
			local MESSAGE_1="$11"
			local MESSAGE_2="$12"
		else
			local CHILD_1_SELECTOR="$7"
			local CHILD_2_SELECTOR="$5"
			local MESSAGE_1="$12"
			local MESSAGE_2="$11"
			ACLFILE_1="$8"
			ACLFILE_2="$6"
		fi

		local PRIMARY_ACTION="$9"
		local SECONDARY_ACTION="$10"
	fi
	local i=0

	iptables -t $TABLE -N $CHAIN_1
	iptables -t $TABLE -N $CHAIN_2

	# build first chain
	readacl "$ACLFILE_1" | while read i; do
		iptables -t $TABLE -A $CHAIN_1 ${=CHILD_1_SELECTOR} ${=i} -j $CHAIN_2
	done
	iptables -t $TABLE -A $CHAIN_1 ${=LOGLIMIT} -j LOG --log-prefix "$MESSAGE_1"
	iptables -t $TABLE -A $CHAIN_1 -j ${=SECONDAY_ACTION}

	# build second chain
	readacl "$ACLFILE_2" | while read i; do
		iptables -t $TABLE -A $CHAIN_2 ${=CHILD_2_SELECTOR} ${=i} -j ${=PRIMARY_ACTION}
	done
	iptables -t $TABLE -A $CHAIN_1 ${=LOGLIMIT} -j LOG --log-prefix "$MESSAGE_2"
	iptables -t $TABLE -A $CHAIN_1 -j ${=SECONDAY_ACTION}

	iptables -t $TABLE -A $PARENT_CHAIN ${=PARENT_SELECTOR} -j $CHAIN_1     # enable it in the parent chain
}

#
# get_ports_of()
#
# Usage: get_ports_of {REGEX} {u|t}
#
# returns a comma-separated list of ports that a process matching REGEX
# listens on (as displayed by netstat). u is for udp, t is for tcp.
# REGEX is an extended regular expression.
#
function get_ports_of() {
	echo \
		$(netstat -n${2}lp \
			| egrep -- "$1" \
			| cut -d: -f2\
			| sed 's/[[:space:]].*//' \
		) \
	| tr ' ' ','
}

# Get first IP of hostname; optional 2nd argument specifies fallback IP to report
function get_ip_of() {
        timeout -k 1 5 dnsip $1 | sed 's/[[:space:]].*//g'
        [[ $pipestatus[1] -gt 0 ]] && {
                echo "Failed to obtain IP of $1 via DNS.${2:+ Defaulting to $2.}" >&2
                [[ -n "$2" ]] && echo -E "$2"
        }
}

# Get main IP of interface
function ip_of_if() {
        if [[ -x /usr/bin/ifdata ]]; then
                ifdata -pa $1 | grep -v NON-IP
        else
                ifconfig $1 | sed -n '2s/ [^r]*..//gp'
        fi
}

# Get broadcast address of interface
function get_if_broadcast() {
        if [[ -x /usr/bin/ifdata ]]; then
                ifdata -pb $1 | grep -v NON-IP
        else
                ifconfig $1 | grep 'inet addr:' | sed 's/.*Bcast://;s/ .*//'
        fi
}

# returns success if an interface exists, failure if it doesn't
function if_exists() {
        if [[ -x /usr/bin/ifdata ]]; then
                ifdata -e $1
        else
                grep -q "^[[:space:]]*$1: " /proc/net/dev
        fi
}

# returns success if an interface is 'up', failure if not
function if_up() {
        if_exists $1 || return 1
        if [[ -x /usr/bin/ifdata ]]; then
                ifdata -pf $1 | egrep -q '^On[[:space:]]+Up$'
        else
                ip link sh $1 | egrep -q "state UP "
        fi
}

# Does the interface exist and if yes, does it have an ipv4 IP?
function has_ip() {
        ip addr sh dev $1 2>/dev/null | grep -q "^[[:space:]]*inet "
}

#
# Don't actually call iptables; rather, build an iptables-restore compatible
# text file.
#
function iptables() {
        debug 5 Executing /sbin/iptables "$@"
        local w
        if [[ "$1" == "-t" ]]; then
                TABLE=$2
                shift
                shift
        else
                TABLE=filter
        fi
        if [[ "$1" == "-N" ]]; then
                iptables-save -c -t $TABLE | grep "^:$2 " >>"$RESTOREDIR/${TABLE}.chains" || echo ":$2 - [0:0]" >>"$RESTOREDIR/${TABLE}.chains"
                shift
                shift
        elif [[ "$1" == "-P" ]]; then
                iptables-save -c -t $TABLE | grep "^:$2 $3 " >>"$RESTOREDIR/${TABLE}.policies" || echo ":$2 $3 [0:0]" >>"$RESTOREDIR/${TABLE}.policies"
        else
                while [[ -n "$1" ]]; do
                        case $1 in
                                *\ *)   echo -n " ${(qqq)1}"; shift;;                                           # quote words with embedded spaces
                                DNAT)   echo -n " DNAT --to-destination"; shift 2;;                             # convert "DNAT --to" to "DNAT --to-destination"
                                *)      w=${1//\/minute//min}; w=${w//\/second//sec}; echo -n " $w"; shift;;    # iptables-save only supports "min" and "sec", not "minute" and "second"
                        esac
                done >>"$RESTOREDIR/${TABLE}.rules"
                echo >>"$RESTOREDIR/${TABLE}.rules"
        fi
}

# Set up the work directory.
function iptables_init() {
	rm -rf "$RESTOREDIR"
	mkdir -p "$RESTOREDIR"
}

# Load the rules we created.
function iptables_commit() {
	PWD="$(pwd)"
	cd "$RESTOREDIR"
	for table in filter nat mangle; do
		touch ${table}.policies
		if [[ (-e ${table}.chains) || (-e ${table}.policies) || (-e ${table}.rules) ]]; then
			echo '*'"$table" >>commit
			if [[ "$table" == "filter" ]]; then
				builtin="INPUT OUTPUT FORWARD"
			elif [[ "$table" == "nat" ]]; then
				builtin="PREROUTING POSTROUTING OUTPUT"
			elif [[ "$table" == "mangle" ]]; then
				builtin="PREROUTING INPUT FORWARD OUTPUT POSTROUTING"
			else
				echo "Unknown table $table!"
				return 1
			fi
			for chain in "${=builtin}"; do
				grep ^:$chain ${table}.policies >>commit || iptables-save -c -t $table | grep "^:$chain " >>commit
			done
			[[ -e ${table}.chains ]] && cat ${table}.chains >>commit
			[[ -e ${table}.rules ]] && cat ${table}.rules >>commit
			echo COMMIT >>commit
		fi
	done
	iptables-restore <commit
	cd "$PWD"
}

És nézzünk meg néhány konkrét scriptletet a script.d-ből:

  • 200blacklisted:
iptables -N blacklisted

# example:
# REJECT_BEFORE_DROP=(-j REJECT --reject-with admin-prohib $ICMP_OUT_LIMIT -m set --match-set private_nets src -m comment --comment "Internal clients get a helpful ICMP error, subject to rate limiting")

iptables -A blacklisted -j LOG          --log-prefix "FW: blacklisted DROP: "   $LOGLIMIT       -m comment --comment "Log packets to and from blacklisted IPs with a rate limit"
[[ -v REJECT_BEFORE_DROP ]] && iptables -A blacklisted $REJECT_BEFORE_DROP
iptables -A blacklisted -j DROP                                                                 -m comment --comment "In other cases, we just drop the packets with no ICMP"
  • 200icmp_forward:
iptables -N icmp_forward
iptables -N icmp_forward_limit

iptables -A icmp_forward_limit $ICMP_FORWARD_LIMIT -j ACCEPT
iptables -A icmp_forward_limit -j DROP

readacl $ACLDIR/usefulicmp \
	| while read i; do
		iptables -A icmp_forward -p icmp --icmp-type $i -j icmp_forward_limit
	done

iptables -A icmp_forward $LOGLIMIT -j LOG --log-prefix "FW: offensive ICMP: "
iptables -A icmp_forward -j DROP
  • 200icmp_input:
iptables -N icmp_input
iptables -N icmp_input_limit

iptables -A icmp_input_limit $ICMP_IN_LIMIT -j ACCEPT
iptables -A icmp_input_limit -j DROP

readacl $ACLDIR/usefulicmp \
	| while read i; do
		iptables -A icmp_input -p icmp --icmp-type $i -j icmp_input_limit
	done

iptables -A icmp_input $LOGLIMIT -j LOG --log-prefix "FW: offensive ICMP: "
iptables -A icmp_input -j DROP
  • 200smtp (primitív greylisting):
iptables -N smtp_refused

iptables -A smtp_refused $LOGLIMIT -j LOG --log-prefix "FW: SMTP greylist: "
iptables -A smtp_refused -p tcp -j REJECT --reject-with tcp-reset
iptables -A smtp_refused -j DROP

iptables -N smtp

readacl $ACLDIR/smtp-whitelist \
	| while read i; do
		iptables -A smtp -s $i -j ACCEPT
	done

# If first connection arrived in last 180 seconds, REJECT
iptables -A smtp -m recent --rcheck --name SMTP_grey --seconds ${SMTP_GREYLIST_TIME:-180} -j smtp_refused

# If first connection arrived earlier than 180 seconds, ACCEPT
iptables -A smtp -m recent --rcheck --name SMTP_grey -j ACCEPT

# If neither (new IP), remember IP and REJECT
iptables -A smtp $LOGLIMIT -j LOG --log-prefix "FW: SMTP greylist: "
iptables -A smtp -p tcp -m recent --set --name SMTP_grey -j REJECT --reject-with tcp-reset
iptables -A smtp -j DROP
  • 200tcp_rst_forward:
iptables -N tcp_rst_forward

iptables -A tcp_rst_forward $TCP_RST_FORWARD_LIMIT -j ACCEPT
iptables -A tcp_rst_forward $LOGLIMIT -j LOG --log-prefix "FW: tcp rst forward DROP: "
iptables -A tcp_rst_forward -j DROP
  • 500input:
iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -i tap+ -s 192.168.0.0/16 -j ACCEPT
iptables -A INPUT -i br+ -s 192.168.0.0/16 -j ACCEPT

iptables -A INPUT -p icmp -j icmp_input

# Doesn't scale well for long blacklists/high traffic
readacl $ACLDIR/blacklist \
	| while read -A i; do
		iptables -A INPUT -s $i -j blacklisted
	done

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -A INPUT -s $ME -d $ETH_BROADCAST -j ACCEPT
iptables -A INPUT -s $ME -d $TAP_BROADCAST -j ACCEPT
iptables -A INPUT -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT  # dhcp
iptables -A INPUT -s $DHCPSUBNET -d 255.255.255.255 -p udp --sport 68 --dport 67 -j ACCEPT     # dhcp
iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT

iptables -A INPUT -p igmp -j ACCEPT     # maybe ratelimit?

# Filter strange/spoofed packets
readacl $ACLDIR/martian \
	| while read -A i; do
		iptables -A INPUT $i -j martian
	done

# Enable all public services (with no ACLs)
readacl $ACLDIR/publicservices \
	| while read -A i; do
		iptables -A INPUT $i -j ACCEPT
	done

iptables -A INPUT -p tcp --dport 25 -m state --state NEW -j smtp

# Simple TCP services
buildtcpchain 8000 -c privoxy -a $ACLDIR/privoxy -n privoxy
buildtcpchain pop3 -a $ACLDIR/plaintext
buildtcpchain imap2 -a $ACLDIR/plaintext
buildtcpchain munin
buildtcpchain mysql
buildtcpchain printer
buildtcpchain rsync
buildudpchain syslog

# Simple UDP services
buildudpchain ntp
buildudpchain 5000 -c openvpn -a $ACLDIR/openvpn -n openvpn

# DNS
buildtcpchain domain -c dns -a $ACLDIR/dns -n dns
iptables -A INPUT -p udp --dport domain -j dns

# IPP (cups)
buildtcpchain ipp
iptables -A INPUT -p udp --dport ipp -j ipp_input

# squid
buildchain filter INPUT squid "-p tcp -m multiport --dports 3128,31280" "-s" $ACLDIR/squid \
	ACCEPT "REJECT --reject-with admin-prohib" "FW: squid DROP: "
iptables -A INPUT -p udp -m multiport --dports $(get_ports_of /squid u) -j squid

# telnet, telnets
buildtcpchain telnet -a $ACLDIR/plaintext
buildtcpchain telnets -a $ACLDIR/trusted

# rpc
buildchain filter INPUT rpc_input "-p tcp --dport sunrpc" "-s" $ACLDIR/trusted ACCEPT DROP "FW: rpc DROP: " $ACLDIR/silent_rpc_drop DROP
iptables -A INPUT -p tcp -m multiport --dports 2049,$(get_ports_of "/rpc|[[:space:]]-[[:space:]]*$" t) -j rpc_input
iptables -A INPUT -p udp -m multiport --dports sunrpc,$(get_ports_of "/rpc|[[:space:]]-[[:space:]]*$" u) -j rpc_input

# ident ban
buildchain filter INPUT auth "-p tcp --dport auth" "-p tcp -s" $ACLDIR/identban \
	"REJECT --reject-with tcp-reset" ACCEPT

# samba
buildchain filter INPUT samba "-p tcp -m multiport --dports 135,137,138,139,445" "-s" \
	$ACLDIR/samba ACCEPT DROP "FW: samba DROP: " $ACLDIR/sambasilent DROP
iptables -A INPUT -p udp -m multiport --dports 135,137,138,139,445 -j samba

# Some stuff is too common to log, drop it silently
readacl $ACLDIR/silentdrop \
	| while read -A i; do
		iptables -A INPUT $i -j DROP
	done

# Everything else, we drop noisily.
iptables -A INPUT $LOGLIMIT -j LOG --log-prefix "FW: input catch-all: "
iptables -A INPUT -j DROP
  • 500forward:
iptables -A FORWARD -p icmp -j icmp_forward
iptables -A FORWARD -p tcp --tcp-flags RST RST -j tcp_rst_forward

iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -A FORWARD -i tap+ -s 192.168.0.0/16 -j ACCEPT
iptables -A FORWARD -i br+ -s 192.168.0.0/16 -j ACCEPT
iptables -A FORWARD -o tap0 -j ACCEPT

# anything with source addresses not in the above range is suspicious
iptables -A FORWARD -i tap+ -j martian
iptables -A FORWARD -i br+ -j martian

readacl $ACLDIR/blacklist \
	| while read -A i; do
		iptables -A FORWARD -s $i -j blacklisted
	done

readacl $ACLDIR/martian \
	| while read -A i; do
		iptables -A FORWARD $i -j martian
	done

iptables -A FORWARD $LOGLIMIT -j LOG --log-prefix "FW: forward catch-all: "
iptables -A FORWARD -j DROP
  • Ez nem a teljes konfiguráció volt (pl. az OUTPUTban is az icmp_forward segítségével rátalimitáljuk a kiküldött icmp-ket)
  • Látható, hogy a modularitásnak köszönhetően egyes modulok átvihetők más gépekre akár a többitől függetlenül is
  • Annak ellenére, hogy látszólag az iptables-t hívja, iptables-restore-ral fogja betölteni a szabályokat, atomi módon
  • Gond: hosszú ACL-ek kezelése nem hatékony, főleg, ha több nagy ACL direktszorzatára van szükség
    • Pl. a tűzfal mögött van 32 db Lotus Domino-szerver, és ezeket 100 kliens érheti el => 3200 szabály
    • Megoldás: ipset

8 Dinamikus hosztnevek IP-változásainak követése

  • Primitív "protoVPN", nem ad igazi biztonságot, de jobb, mint az egész Internetnek nyújtani egy nem nyilvános szolgáltatást

A következő script újratölti a tűzfalat, ha megváltozik egy figyelt dinamikus IP (és pl. runitből futtatható):

#!/bin/zsh
exec 2>&1

SVNAME=$(basename $(pwd))
CONFIG=/etc/default/"$SVNAME"

INTERVAL=150
FWCOMMAND="chpst -l /var/lock/firewall.lock tryto /etc/firewall/firewall"
FWDIR=/etc/firewall
DYNHOSTS=($(cat $(dirname $0)/dyndnshosts))
DNSTIMEOUT=30
STATEDIR=/var/lib/dyndns-firewall-trigger

[[ -r "$CONFIG" ]] && . "$CONFIG"

chpst -u nobody sleep $INTERVAL

if [[ ! -d "$STATEDIR" ]]; then
        mkdir $STATEDIR
        chmod 700 $STATEDIR
fi

TRIGGER=0
for host in "$DYNHOSTS[@]"; do
        if rgrep -q "$host" $FWDIR; then
                [[ -f $STATEDIR/$host ]] || touch $STATEDIR/$host
                OLDIP="$(cat $STATEDIR/$host)"
                echo -n Resolving "$host"." "
                CURRENTIP="$(tryto -t $DNSTIMEOUT -k $[DNSTIMEOUT+3] dnsip $host | sed 's/[[:space:]]//g')"
                if [[ ! "$OLDIP" == "$CURRENTIP" ]] && [[ "$CURRENTIP" =~ "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" ]]; then
                        echo "$CURRENTIP" >$STATEDIR/$host
                        echo -n "IP of $host changed from \"$OLDIP\" to \"$CURRENTIP\"". Will reload firewall.
                        TRIGGER=1
                fi
                echo
        fi
done

if [[ "$TRIGGER" = "1" ]]; then
        exec ${=FWCOMMAND}
fi

9 SSH-jelszópróbálgatók automatikus kitűzfalazása

  • Az ilyesmivel csak óvatosan, nehogy self-DoS legyen
#!/bin/zsh
# Needs to run as root because it invokes iptables
exec 2>&1
CONFIG=/etc/ssh-hammerblock/config
SSHLOG=/var/log/socklog/ssh/current
STATEDIR=/var/state/ssh-hammerblock
THRESHOLD=10
TMPREAPER_TIME=1h
RULE="-I INPUT -s __IP__ -j blacklisted"
[[ -r "$CONFIG" ]] && . "$CONFIG"
mkdir -p $STATEDIR
chmod 700 $STATEDIR
xtail $SSHLOG 2>/dev/null \
	| sed -nu '/Failed/{s/.*for .* from //;s/ .*//;p};/Invalid user/{s/.*Invalid user .* from //;p}' \
	| while read IP; do
		tmpreaper $TMPREAPER_TIME $STATEDIR/./
		COUNT=$(cat "$STATEDIR/$IP" 2>/dev/null)
		((COUNT++))
		echo -n "$COUNT" >"$STATEDIR/$IP"
		if [[ "$COUNT" -ge "$THRESHOLD" ]]; then
			iptables ${=RULE//__IP__/$IP}
			rm -f "$STATEDIR/$IP"
		fi
	done
  • xtail helyett használhattuk volna az svlogd-nak azt a képességét, hogy bizonyos sorokat a standard errorra tud írni
    • az stderr lehetett volna egy fifoba irányítva, amiből ez a script olvas
    • gyorsabb lett volna (nem kell regexp-illesztés)
    • de kevésbé robusztus, mert ha a scriptünk elszáll, blokkolódik a logolás stb.

10 Ajánlott irodalom

A Skype-pal kapcsolatban:

11 Potenciális zh-kérdések

  • Mit jelent a tűzfalak kontextusában a "zónaszemlélet", és miért érdemes a "zónásításra" törekedni?
  • Mi a különbség az egyszerű csomagszűrő és a kapcsolatkövető tűzfal között?
  • Milyen különböző összetettségű tűzfalmegoldásokat ismer? Mindegyiket jellemezze néhány szóban!
  • Mi a NAT? Mikor szokták használni?
  • Mikor lehet értelme az olyan NATnak, ami egy címteret egy vele azonos méretű másikra képez le?
  • Milyen keretet ad a Netfilter a tűzfalszabályoknak? (Táblák, láncok...)
  • Milyen problémákat okozhat, ha nem IP-címekkel, hanem hosztnevekkel adunk meg tűzfalszabályokat?
  • Hogyan lehet atomi művelettel kicserélni az összes tűzfalszabályt a Netfilterben, és miért fontos ez?
  • Hasonlítsa össze a DROP és a REJECT iptables-akció működését! Mikor és miért érdemes az egyiket vagy a másikat használni?
  • Milyen esetekben érdemes a Netfilterben rátalimitet használni és miért?
  • Miért várjuk a Netfilter ipset nevű bővítését, mint a messiást? Milyen problémá(k)ra lesz megoldás?
  • Ismertesse egy tűzfalrendszer tervezésével kapcsolatos legfontosabb szempontokat, és ahol lehet, az előadáson megismert megoldásokat értékelje ezen szempontok szerint! Mire célszerű ügyelni a tűzfalszabályok megtervezése során annak érdekében, hogy lehetőleg ne DoS-oljuk saját magunkat?
  • Mi a különbség a szabályalapú és a szignatúraalapú IDS között?

A zh második részéhez (ahol egy darab a4-es lapnyi segédeszközt szabad használni):

  • Adott az alábbi tűzfalscript. Milyen problémákra számíthatunk, ha ezt használjuk?
  • Adott az iptables dokumentációjának releváns része. Írjon olyan tűzfalszabályokat, amelyek - teszemazt - minden hálózati kapcsolat első ezer byte-ját kiküldik a tűzfal monitoring-portján! (Nem kell, hogy 100%-ig tökéletes legyen; az elv legyen jó.)
Személyes eszközök