Blog: Cyberbezpieczenstwo

Symulacja Firewalla

Symulacja Firewalla
  • 1 319 views

W pracy administratora wielokrotnie spotykałem się z sytuacją konieczności analizy działającego firewalla opartego o iptables.

Udostępnij!

Reguły iptables na bardziej skomplikowanych systemach zarządzanych przez kilku ludzi często mają tendencję do “rośnięcia”. Czasami ktoś jakąś regułę doda, czasami coś zmieni, czasami zostaną dodane nowe reguły przez jakieś oprogramowanie… Administratorzy często mają różny styl tworzenia firewall – ktoś może preferować dodanie reguł na początku łańcucha przez komendę I, ktoś inny na końcu poprzez A. Przy kilku tysiącach reguł, w sytuacji gdy reguły nie były dodawane z jednego miejsca, do tego gdy są porozdzielane na różne łańcuchy, z których często występują odwołania do innych łańcuchów tudzież ruch jest oznaczany za pomocą markowania a potem filtrowany na podstawie oznaczeń – połapanie się co odpowiada za blokowanie albo przepuszczenie konkretnego ruchu nie jest proste. A tym bardziej bezpieczne zmodyfikowanie takiego firewalla i sprawdzenie jak konkretna modyfikacja wpłynie na przepuszczenie konkretnego ruchu.

W artykule postaram się pokazać, jak za pomocą Linuksowego mechanizmu netns służącego do wirtualizacji stosu sieciowego można stosunkowo prosto zasymulować działanie Linuksowego routera oraz znaleźć pod jakie reguły iptables podpada konkretny ruch.

Dla przykładu – zasymulujemy środowisko testowe składające się z:

  • Routera R posiadajacego dwa interfejsy sieciowe:
  1. ppp0 z adresem 172.20.1.2/24 i bramą 172.20.1.1, za którą stoi komputer A
  2. eth0 z adresem 192.l168.1.1/24 który jest bramą dla komputera B

oraz zestawem reguł iptables (zrzut z iptables-save):

# Generated by iptables-save v1.4.21 on Sat Jan  7 15:00:51 2017*filter
:INPUT ACCEPT [0:0]:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]-A FORWARD -s 192.168.0.0/16 -p tcp -m tcp --dport 1000:2000 -j REJECT --reject-with icmp-port-unreachable-A FORWARD -s 192.168.1.0/32 -p tcp -m tcp --dport 1234 -j ACCEPT-A FORWARD -s 192.168.1.0/24 -j ACCEPTCOMMIT# Completed on Sat Jan  7 15:00:51 2017# Generated by iptables-save v1.4.21 on Sat Jan  7 15:00:51 2017*nat:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]:OUTPUT ACCEPT [0:0]:POSTROUTING ACCEPT [0:0]-A POSTROUTING -s 192.168.1.100/32 -j MASQUERADE-A POSTROUTING -s 192.168.1.0/28 -j ACCEPT-A POSTROUTING -s 192.168.1.10/32 -j MASQUERADECOMMIT# Completed on Sat Jan  7 15:00:51 2017

  • Komputera A o adresie 172.20.1.1/24 będącego za interfejsem ppp0 routera R który udostępnia jakąś usługę na porcie 1234
  • Komputera B o adresie 192.168.1.10/24 który będzie próbował podłączyć się na adres 172.20.1.1 na port 1234

Oraz sprawdzimy dlaczego powyższe reguły nie pozwalają na podłączenie się do usługi.

Na początku utwórzmy wirtualny router:

root@aramin:~# ip netns add Rroot@aramin:~# ip netns exec R ip link set dev lo uproot@aramin:~# ip netns exec R sysctl -w net.ipv4.conf.default.forwarding=1

Następnie zobaczmy jak wygląda on “sieciowo” w środku:

root@aramin:~# ip netns exec R bashroot@aramin:~# ip a1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft foreverroot@aramin:~#

Jak widać – po uruchomieniu w nim bash widać tylko jeden interfejs siecowy – loopback.

Utwórzmy więc netnsy dla komputerów A i B:

root@aramin:~# ip netns add Aroot@aramin:~# ip netns exec A ip link set dev lo uproot@aramin:~# ip netns add Broot@aramin:~# ip netns exec B ip link set dev lo up

Komendą ip netns exec B bash – możemy uruchomić powłokę w środowisku sieciowym B.

Następnie – dodajmy interfejsy do konkretnych netnsów

root@aramin:~# ip link add R-ppp0 type veth peer name A-eth0root@aramin:~# ip link set R-ppp0 netns Rroot@aramin:~# ip link set A-eth0 netns Aroot@aramin:~# ip link add R-eth0 type veth peer name B-eth0root@aramin:~# ip link set R-eth0 netns Rroot@aramin:~# ip link set B-eth0 netns B

I pozmieniajmy im nazwy, tak by odpowiadały środowisku które chcemy zamodelować:

ip netns exec R ip link set dev R-ppp0 name ppp0 upip netns exec A ip link set dev A-eth0 name eth0 upip netns exec R ip link set dev R-eth0 name eth0 upip netns exec B ip link set dev B-eth0 name eth0 up
Po tym wszystkim – zobaczmy jak wygląda konfiguracja sieci na routerze:

root@aramin:~# ip netns exec R ip a1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group defaultlink/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever12: ppp0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 5e:10:11:d4:89:34 brd ff:ff:ff:ff:ff:ffinet6 fe80::5c10:11ff:fed4:8934/64 scope linkvalid_lft forever preferred_lft forever14: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether f6:6b:b2:97:53:12 brd ff:ff:ff:ff:ff:ffinet6 fe80::f46b:b2ff:fe97:5312/64 scope linkvalid_lft forever preferred_lft forever

Zostaje jeszcze dodanie adresacji, routingu, maskarady i sprawdźmy czy “ping” przechodzi:

root@aramin:~# # konfiguracja routeraroot@aramin:~# ip netns exec R ip a a 172.20.1.2/24 dev ppp0root@aramin:~# ip netns exec R ip r a default via 172.20.1.1root@aramin:~# ip netns exec R ip a a 192.168.1.1/24 dev eth0root@aramin:~# ip netns exec R iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADEroot@aramin:~#root@aramin:~# # konfiguracja Aroot@aramin:~# ip netns exec A ip a a 172.20.1.1/24 dev eth0root@aramin:~#root@aramin:~# # konfiguracja Broot@aramin:~# ip netns exec B ip a a 192.168.1.10/24 dev eth0root@aramin:~# ip netns exec B ip r a default via 192.168.1.1root@aramin:~#root@aramin:~# ip netns exec B traceroute -n 172.20.1.1traceroute to 172.20.1.1 (172.20.1.1), 30 hops max, 60 byte packets1  192.168.1.1  0.082 ms  0.028 ms  0.026 ms2  172.20.1.1  0.070 ms  0.028 ms  0.033 ms

Tada – traceroute przechodzi.

To spróbujmy czegoś trudniejszego.

Wrzućmy podany na początku zestaw reguł na R:

root@aramin:~# (> cat <<EOF> # Generated by iptables-save v1.4.21 on Sat Jan  7 15:00:51 2017> *filter> :INPUT ACCEPT [0:0]> :FORWARD ACCEPT [0:0]> :OUTPUT ACCEPT [0:0]
> -A FORWARD -s 192.168.0.0/16 -p tcp -m tcp --dport 1000:2000 -j REJECT --reject-with icmp-port-unreachable> -A FORWARD -s 192.168.1.0/32 -p tcp -m tcp --dport 1234 -j ACCEPT> -A FORWARD -s 192.168.1.0/24 -j ACCEPT> COMMIT> # Completed on Sat Jan  7 15:00:51 2017> # Generated by iptables-save v1.4.21 on Sat Jan  7 15:00:51 2017> *nat> :PREROUTING ACCEPT [0:0]> :INPUT ACCEPT [0:0]> :OUTPUT ACCEPT [0:0]
> :POSTROUTING ACCEPT [0:0]> -A POSTROUTING -s 192.168.1.100/32 -j MASQUERADE> -A POSTROUTING -s 192.168.1.0/28 -j ACCEPT> -A POSTROUTING -s 192.168.1.10/32 -j MASQUERADE> COMMIT> # Completed on Sat Jan  7 15:00:51 2017> EOF> ) | ip netns exec R iptables-restoreroot@aramin:~#

I sprawdźmy liczniki:

root@aramin:~# ip netns exec R iptables -vxnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 REJECT     tcp  --  *      *       192.168.0.0/16       0.0.0.0/0            tcp dpts:1000:2000 reject-with icmp-port-unreachable
0        0 ACCEPT     tcp  --  *      *       192.168.1.0          0.0.0.0/0            tcp dpt:1234
0        0 ACCEPT     all  --  *      *       192.168.1.0/24       0.0.0.0/0
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
root@aramin:~# ip netns exec R iptables -vxnL -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 MASQUERADE  all  --  *      *       192.168.1.100        0.0.0.0/0
0        0 ACCEPT     all  --  *      *       192.168.1.0/28       0.0.0.0/0
0        0 MASQUERADE  all  --  *      *       192.168.1.10         0.0.0.0/0

Wszystkie chwilowo wynoszą 0. To teraz uruchomimy prosty serwer na środowisku A na porcie 1234 którego jedynym zadaniem po skutecznym nawiązaniu połączenia TCP będzie wypisanie tekstu HELLO

root@aramin:~# ip netns exec A nc -l -p 1234 -c "echo HELLO" &
[1] 1096

I spróbujmy się do niego podłączyć z środowiska B

root@aramin:~# ip netns exec B nc -n -v -w1 172.20.1.1 1234
(UNKNOWN) [172.20.1.1] 1234 (?) : Connection refused

Jak widać połączenie nieudane – zostało odrzucone. Spójrzmy więc które reguły iptables brały udział w połączeniu:

root@aramin:~# ip netns exec R iptables -vxnL -t nat
Chain PREROUTING (policy ACCEPT 2 packets, 120 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 MASQUERADE  all  —  *      *       192.168.1.100        0.0.0.0/0
0        0 ACCEPT     all  —  *      *       192.168.1.0/28       0.0.0.0/0
0        0 MASQUERADE  all  —  *      *       192.168.1.10         0.0.0.0/0
root@aramin:~# ip netns exec R iptables -vxnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
2      120 REJECT     tcp  —  *      *       192.168.0.0/16       0.0.0.0/0            tcp dpts:1000:2000 reject-with icmp-port-unreachable
0        0 ACCEPT     tcp  —  *      *       192.168.1.0          0.0.0.0/0            tcp dpt:1234
0        0 ACCEPT     all  —  *      *       192.168.1.0/24       0.0.0.0/0
Chain OUTPUT (policy ACCEPT 2 packets, 176 bytes)
pkts      bytes target     prot opt in     out     source               destination

Jak widać – do tablicy nat nic nie doszło, natomiast 2 pakiety zostały odrzucone przez regułę REJECT. Mamy więc pierwszy błąd w firewallu. Usuńmy regułę i ponawiamy próbę:

root@aramin:~# ip netns exec R iptables -D FORWARD -s 192.168.0.0/16 -p tcp --dport 1000:2000 -j REJECT
root@aramin:~# ip netns exec B nc -n -v -w1 172.20.1.1 1234
(UNKNOWN) [172.20.1.1] 1234 (?) : Connection timed out
root@aramin:~# ip netns exec R iptables -vxnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 ACCEPT     tcp  —  *      *       192.168.1.0          0.0.0.0/0            tcp dpt:1234
2      120 ACCEPT     all  —  *      *       192.168.1.0/24       0.0.0.0/0

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
root@aramin:~# ip netns exec R iptables -vxnL -t nat
Chain PREROUTING (policy ACCEPT 3 packets, 180 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 MASQUERADE  all  —  *      *       192.168.1.100        0.0.0.0/0
1       60 ACCEPT     all  —  *      *       192.168.1.0/28       0.0.0.0/0
0        0 MASQUERADE  all  —  *      *       192.168.1.10         0.0.0.0/0

Tym razem widać że pakiety przeszły przez reguły w FORWARD, natomiast w regułach z tablicy nat – podlegały pod regułę ACCEPT, która wykonała się jako pierwsza i co prawda spowodowała przejście pakietu do komputera A, natomiast nie “znatowała” go – przez co nie dotarł do komputera B ruch powrotny – nie udało się więc nawiązać skutecznego połączenia TCP. Usuńmy więc tą regułę i zobaczmy czy działa:

root@aramin:~# ip netns exec R iptables -t nat -D POSTROUTING -s 192.168.1.0/28 -j ACCEPT
root@aramin:~# ip netns exec B nc -n -v -w1 172.20.1.1 1234
(UNKNOWN) [172.20.1.1] 1234 (?) open
HELLO
[1]+  Zakończono             ip netns exec A nc -l -p 1234 -c "echo HELLO"

Działa!

Upewnijmy się jeszcze że ruch przechodzi przez właściwe reguły w tablicy nat:

root@aramin:~# ip netns exec R iptables -vxnL -t nat
Chain PREROUTING (policy ACCEPT 1 packets, 60 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts      bytes target     prot opt in     out     source               destination
0        0 MASQUERADE  all  —  *      *       192.168.1.100        0.0.0.0/0
1       60 MASQUERADE  all  —  *      *       192.168.1.10         0.0.0.0/0

I na koniec usuńmy netnsy:

root@aramin:~# ip netns del Rroot@aramin:~# ip netns del Aroot@aramin:~# ip netns del B

Dzięki netns możemy emulować praktycznie każdy aspekt sieci w linuksie – włączając w to bonding, brydże, można również uruchomić na nich dynamiczny routing w oparciu np o quaggę lub bird, co pozwala na symulowanie bardziej zaawansowanych konfiguracji sieciowych. Warto oczywiście pamiętać że system plików i przestrzeń procesów jest tutaj wspólna z normalnym systemem – więc uruchamianie procesów oznacza że mogą one konfliktować z normalnie uruchomionymi na systemie procesami. Jeżeli jest to niepożądane – warto użyć oprócz netns również np chroot-a – tworząc w ten sposób namiastkę wirtualizacji w stylu lxc, tudzież chociaż wskazać ręcznie osobne katalogi do np plików pid.

 

Wszystkie skrypty – umieściłem dodatkowo na https://github.com/theundefined/netnsplay