Firewall w systemd

Wszystkie współczesne dystrybucje Linuksa zrezygnowały z systemu init i przeszły na system daemon, czyli systemd, który jak każda nowość wzbudzał wiele kontrowersji, ale powoli stał się faktem. Ci, którzy mieli swoje własne aplikacje, daemony lub skrypty startowe muszą je przerabiać. Na swoje potrzeby również przerobiłem firewall znany użytkownikom systemu EOS.
Ten firewall opiera się na pliku konfiguracyjnym zawierającym interfejsy sieciowe, zasady routingu i porty, które użytkownik chce otworzyć. Plik jest umieszczony w specjalnie utworzonym katalogu /etc/firewall i nosi nazwę firewall.porty

EXTIF=
INTIF=
SSH=1
WWW=0
HTTPS=0
FTP=0
MAIL=0
IMAP=0
IMAPS=0
IDENT=0
RSYNC=0
NETWORK=0
PROXY=0
FORWARD=0

W tym pliku dopisujemy sieciowy interfejs zewnętrzny (EXTIF) i wewnętrzny (INTIF), jeśli jest oraz ustawiamy NETWORK na wartość 1, jeżeli mamy dwa interfejsy i chcemy, by komputer był routerem. W tym przypadku także możemy na 1 ustawić zmienną PROXY, jeśli zainstalujemy serwer proxy oraz zmienną FORWARD, o ile zamierzamy jakieś porty forwardować do wewnątrz sieci. W pliku domyślnym otwarty jest jedynie port SSH.
Z tej konfiguracji korzysta skrypt startowy, który również jest w /etc/firewall i nazywa się zwyczajnie – firewall i jeśli go przepisujemy, to musimy mu potem nadać atrybut wykonywalności (chmod 755 /etc/firewall/firewall).

#!/bin/sh
# Firewall
# plik z konfiguracją otwartych portów
. /etc/firewall/firewall.porty
i=`which iptables`
start() {
echo -n "Uruchamiam Firewall"
if [ -e /proc/sys/net/ipv4/tcp_ecn ];then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ -e /proc/sys/net/ipv4/tcp_syncookies ] ; then
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
fi
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 1 > $f
done
$i -F
$i -F -t nat
$i -P INPUT DROP
$i -P FORWARD DROP
$i -P OUTPUT ACCEPT
# interfejs lo
$i -A INPUT -i lo -j ACCEPT
$i -A FORWARD -o lo -j ACCEPT
# blokada wirusów usługi RPC
$i -A INPUT -p tcp --dst 0/0 -m multiport --dport 135,445 -j DROP
$i -A FORWARD -p tcp --dst 0/0 -m multiport --dport 135,445 -j DROP
# Odrzucamy z komunikatem ICMP Port Unreachable polaczenia
# na IDENT oraz SOCKS (czesto sprawdzane przez serwery IRC)
# Jesli udostepniasz te uslugi zaplotkuj (#) odpowiedne linie
if [ "$IDENT" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 113 -j ACCEPT
$i -A INPUT -p tcp -i $EXTIF --dport 1080 -j ACCEPT
else
$i -A INPUT -p tcp --dst 0/0 --dport 113 -j REJECT --reject-with icmp-port-unreachable
fi
$i -A INPUT -p tcp --dst 0/0 --dport 1080 -j REJECT --reject-with icmp-port-unreachable
# reguła HTTP
if [ "$WWW" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 80 -j ACCEPT
fi
# reguła HTTPS
if [ "$HTTPS" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 443 -j ACCEPT
fi
# reguła FTP
if [ "$FTP" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 20 -j ACCEPT
$i -A INPUT -p tcp -i $EXTIF --dport 21 -j ACCEPT
fi
# reguła SMTP POP3
if [ "$MAIL" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 25 -j ACCEPT
$i -A INPUT -p tcp -i $EXTIF --dport 110 -j ACCEPT
$i -A INPUT -p tcp -i $EXTIF --dport 465 -j ACCEPT
$i -A INPUT -p tcp -i $EXTIF --dport 995 -j ACCEPT
fi
# reguła SSH (port SSH można zmienić poniżej na niestandardowy, jesli taki jest w sshd_config)
if [ "$SSH" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 22 -j ACCEPT
fi
# reguła IMAP
if [ "$IMAP" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 143 -j ACCEPT
$i -A INPUT -p udp -i $EXTIF --dport 143 -j ACCEPT
fi
# reguła IMAPS
if [ "$IMAPS" = "1" ]; then
$i -A INPUT -p tcp -i $EXTIF --dport 993 -j ACCEPT
$i -A INPUT -p udp -i $EXTIF --dport 993 -j ACCEPT
fi
# pingi pozwalamy
$i -A INPUT -p icmp --icmp-type echo-request -j ACCEPT -m limit --limit 1/sec
# forwardowanie portów
if [ "$FORWARD" = "1" ]; then
/etc/firewall/forward
fi
# Wszystkie polaczenia z innych interfejsow niz interfejs do internetu pozwalamy.
$i -A INPUT ! -i $EXTIF -j ACCEPT
$i -A FORWARD ! -i $EXTIF -j ACCEPT
# i maskujemy
if [ "$NETWORK" = "1" ]; then
$i -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE
fi<>
# przekierowanie zmuszajace wszystkich do transparentnego korzystania z proxy
if [ "$PROXY" = "1" ]; then
$i -t nat -A PREROUTING -i $INTIF -p tcp --dport 80 -j REDIRECT --to-port 8080
fi
# Zezwalamy na wszystko co odbywa sie w ramach juz dozwolonych polaczen
$i -A INPUT -j ACCEPT -m state --state ESTABLISHED,RELATED
$i -A FORWARD -j ACCEPT -m state --state ESTABLISHED,RELATED
}
stop() {
echo -n "Zatrzymuję firewall, wszystko otwarte."
$i -F INPUT
$i -F FORWARD
$i -F OUTPUT
$i -P INPUT ACCEPT
$i -P FORWARD DROP
$i -P OUTPUT ACCEPT
echo 0 > /proc/sys/net/ipv4/ip_forward
}
restart() {
stop
start
# sprawdzanie czy dziala fail2ban
#PIDFAIL2BAN=`ls /var/run/fail2ban |grep pid|wc -l`
#if [ "$PIDFAIL2BAN" = "1" ]; then
#service fail2ban restart
#fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
iptables -L
;;
*)
echo "Używaj z argumentem: $0 start|stop|restart|status"
;;
esac

Do tego jest jeszcze pomocniczy forward, gdyby ktoś potrzebował forwardować porty do wewnątrz sieci. Musimy utworzyć dwa pliki forward.porty.tcp i forward.porty.udp w /etc/firewall/. W tych plikach umieszczamy adres komputera wewnątrz sieci, port na którym działa jakaś dostępna na nim usługa i port na adresie publicznym, na którym ta usługa ma być widoczna. Przykład: 192.168.1.22 80 82 (komputer w sieci, port serwera www, port na adresie publicznym, na którym ten serwer będzie widoczny). Niektóre usługi wymagają otwarcia portów UDP, np. gry lub torrenty.
Skrypt pod nazwą forward w /etc/firewall wygląda następująco:

#!/bin/bash
# uruchamianie forwardowania portów
. /etc/rc.firewall
i=`which iptables`
CONF=/etc/forward.porty.tcp
CONFudp=/etc/forward.porty.udp
#forwardowanie portow TCP
while read line; do
IP=`echo "$line"`
I1=`echo $IP | awk '{print $1}'`
I2=`echo $IP | awk '{print $2}'`
I3=`echo $IP | awk '{print $3}'`
if [ ! -z "$IP" ]; then
$i -I FORWARD -p tcp -d $I1 --dport $I2 -j ACCEPT
$i -t nat -A PREROUTING -i $EXTIF -p tcp --dport $I3 -j DNAT --to $I1:$I2
fi
done < $CONF

#forwardowanie portow UDP (część jest opcjonalna, można postawić znak # na początku każdej linijki lub tej części nie kopiować)
while read line; do
IP=`echo "$line"`
I1=`echo $IP | awk '{print $1}'`
I2=`echo $IP | awk '{print $2}'`
I3=`echo $IP | awk '{print $3}'`
if [ ! -z "$IP" ]; then
$i -I FORWARD -p udp -d $I1 --dport $I2 -j ACCEPT
$i -t nat -A PREROUTING -i $EXTIF -p udp --dport $I3 -j DNAT --to $I1:$I2
fi
done < $CONFudp

Teraz jeszcze musimy mieć skrypt uruchamiający. Wygląda on następująco:

[Unit]
Description = Firewall
After = syslog.target

[Service]
ExecStart = /etc/firewall/firewall start
RemainAfterExit = yes
ExecStop = /etc/firewall/firewall stop
RuntimeDirectory = etc
Restart = on-failure
KillSignal = SIGTERM
Type = simple

[Install]
WantedBy = multi-user.target

Ten skrypt umieszczamy w /etc/systemd/system/ pod nazwą firewall.service, a następnie wykonujemy dwa polecenia jako root:
systemctl enable firewall.service
systemctl start firewall.service
O ile nie zrobiliśmy żadnych błędów, to od tego momentu polecenie iptables -L pokaże nam zastosowane przez nas reguły. W skrypcie startowym zapewne nie wszystkie wpisy są potrzebne, bo firewall to nie jest daemon, tylko jednorazowo wykonujący się zestaw reguł. Jednak musiałem to zrobić błyskawicznie na działającym systemie i nie miałem czasu sprawdzać.


Dodaj komentarz