| februari 2012 | |||||||
| wk | zo | ma | di | wo | do | vr | za |
| 05 | 1 | 2 | 3 | 4 | |||
| 06 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 07 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 08 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 09 | 26 | 27 | 28 | 29 | |||
Kijk ook op de agenda-pagina.
De meeste computers hebben intussen wel een virusscanner aan boord. Dat is hard nodig, want
virussen, trojan horses en wormen vormen nog steeds een bedreiging voor de veiligheid en stabiliteit.
Maar er is tegenwoordig helaas meer aan de hand en dan helpt alleen een virusscanner niet meer.
Er is een goede kans, dat een worm of ander electronisch ongedierte zich toch op het systeem heeft
weten te nestelen. Zo'n beestje gaat dan vaak ongemerkt verbinding zoeken met de buitenwereld en
dat willen we niet. Wat we ook niet willen, is de onophoudelijke stroom van spam-mailtjes met allerlei
aanbiedingen van viagra, barkrukken, palmbomen, neon-reclames, rolexen, goedkope software en noem
al die rommel maar op. Vaak worden die niet alleen rechtstreeks via mail verstuurd, maar ook
formulieren op sites zijn een steeds meer geliefd medium, omdat op die manier de bron van de spam
kan worden gemaskeerd. En dan biedt een firewall uitkomst.
De reden dat deze pagina is ontstaan, is het feit dat er steeds maar weer geprobeerd wordt, misbruik te maken van de web-formulieren op mijn site. Die zijn wel goed dicht getimmerd, maar het kost de webserver energie, die beter kan worden benut. Een firewall kan daar efficienter mee aan de gang.
We gaan een firewall opzetten. Dit voorbeeld gaat uit van een UNIX systeem, waarop iptables is geïnstalleerd. Daarmee gaan we een vuurmuur bouwen, die in staat is om bepaalde (reeksen) IP-adressen de toegang tot bepaalde diensten op het systeem te ontzeggen. En als we toch bezig zijn, kunnen we ons gelijk wapenen tegen een aantal (Distributed) Denial-of-Service (DDoS) aanvallen.
We gaan er in dit voorbeeld vanuit, dat we geen firewall maken tussen het LAN en het
Internet, maar een lokale firewall op een server, die we daarmee willen beschermen. We gaan er
ook geen uitgebreide iptables-tutorial van maken, want die zijn er al, zoals bijvoorbeeld deze:
Iptables Tutorial 1.2.2 door Oskar Andreasson.
We zullen de volgende toegangspolicy hanteren:
Zoals de naam al zegt, is iptables opgebouwd uit een aantal tabellen. Wij gebruiken in dit voorbeeld alleen de (impliciete) tabel filter. De andere tabellen hebben voornamelijk betrekking op de situatie van een firewall tussen twee netwerken, of om TCP/IP pakketten aan te passen. In een tabel zijn een of meer chains mogelijk, terwijl de chains op hun beurt weer gebruik maken van targets.
iptables is standaard uitgerust met een aantal chains. Deze chains zijn te beschouwen als invoer-, uitvoer- en doorvoer-kanalen:
Ook is het mogelijk, zelf chains te definiëren. Dat is handig, zoals we later zullen zien. We gaan straks zelf twee chains toevoegen:
In elke chain kunnen we regels maken, waarin we vastleggen wat er in een bepaalde situatie met het netwerkverkeer moet gebeuren. De regels verwijzen naar een aantal mogelijke targets:
We gaan een shell-script maken, waarin het iptables commando wordt gebruik om de firewall
op te zetten. Het begin van het script ziet er zo uit:
#! /bin/bash
De eerste regel vertelt het systeem, dat het script door de Bourne Again shell moet worden uitgevoerd;
de tweede regel is puur commentaar, zoals alle regels die beginnen met #.
# Dit is een shell script voor initialisatie van iptables.
We maken eerst alle chains leeg. Daarna verwijderen we alle eventueel eerder zelf verzonnen
niet-standaard chains.
#! /bin/bash
De optie -F staat voor flush, terwijl optie -X voor het weggooien van de
niet-standaard chains zorgt.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
Nu gaan we de default policies voor de standaard chains instellen. Een policy bepaalt, wat er
gebeurt als een pakket de hele chain heeft doorlopen, zonder naar een target te zijn gestuurd.
#! /bin/bash
De optie -P stelt een policy in; voor INPUT en OUTPUT accepteren we alles, terwijl
in de FORWARD chain alles wordt genegeerd. We zijn tenslotte geen doorgangshuis.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
We maken twee eigen chains aan: blacklist voor verkeer van adressen die op onze zwarte
lijst staan, bad_packets voor regels tegen DDoS aanvallen.
#! /bin/bash
De optie -N staat voor nieuwe chain toevoegen.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
Nu begint het echte werk: de regeltjes. Eerst maken we in de INPUT chain een paar regeltjes, die
ervoor gaan zorgen dat al het nieuwe verkeer met bestemming poort 80 (http) of 443 (https)
naar de blacklist wordt gestuurd. Waarom 80 en 443? Omdat onze webformulieren met een van die twee
protocollen worden benaderd! Bovendien worden alle andere poorten in mijn geval al ergens anders
de nek om gedraaid. Maar als jouw machine ook via poort 22 (ssh) bereikbaar is, kun je daarvoor
natuurlijk een extra regeltje toevoegen.
#! /bin/bash
Met -A INPUT geven we aan, dat we een regel toevoegen (ADD) aan de INPUT chain. Met
-p tcp beperken we de regel tot TCP pakketten (dus geen UDP of ICMP).
Met --dport geven we de doelpoort aan. Met -j blacklist geven we aan, dat al het
verkeer dat aan deze criteria voldoet, naar de blacklist moet springen (jumpen).
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
# Instellen INPUT chain
iptables -A INPUT -p tcp --dport 80 -j blacklist
iptables -A INPUT -p tcp --dport 443 -j blacklist
We voegen aan de INPUT chain gelijk ook een regel toe, die zorgt dat al het verkeer dat de blacklist
heeft overleefd, door de bad_packets chain bekeken wordt op DDoS kenmerken. Bedenk dat de regels door
iptables in volgorde (van boven naar beneden) worden doorlopen. We willen éérst de
snodaards via de zwarte lijst uitsluiten; wat overblijft gaat de bad_packets chain in. De nieuwe
regel moet dus achter de verwijzingen naar de blacklist komen.
#! /bin/bash
Deze regel stuurt alles wat de blacklist heeft overleefd, zonder verdere ciriteria door naar
de bad_packets chain.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
# Instellen INPUT chain
iptables -A INPUT -p tcp --dport 80 -j blacklist
iptables -A INPUT -p tcp --dport 443 -j blacklist
iptables -A INPUT -j bad_packets
Een eigen chain heeft niet veel zin, als er geen regels in zitten. De regels voor de bad_packets
chain zijn gebaseerd op bepaalde eigenschappen van het TCP/IP protocol. Het zou te ver voeren om
daar op deze plaats nader op in te gaan. Maar geloof me, ze werken! Dus voeren we ze in.
#! /bin/bash
Dat ziet er ingewikkeld uit en dat is het ook. Maar deze regels zijn uiterst effectief tegen de
meest voorkomende vormen van DDoS aanvallen. De eerste drie regels blokkeren niet, maar limiteren
de hoeveelheid verkeer. De vierde blokkeert, maar zegt niets, terwijl de laatste netjes laat weten
dat de packets worden geweigerd.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
# Instellen INPUT chain
iptables -A INPUT -p tcp --dport 80 -j blacklist
iptables -A INPUT -p tcp --dport 443 -j blacklist
iptables -A INPUT -j bad_packets
# Vullen chain bad_packets
# SYN-flood aanvallen
iptables -A bad_packets -p tcp --syn -m limit --limit 1/s -j ACCEPT
# Portscan aanvallen
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK,FIN,RST RST \
-m limit --limit 1/s -j ACCEPT
# Ping-flood aanvallen met behulp van het ICMP protocol
iptables -A bad_packets -p icmp --icmp-type echo-request -m limit \
--limit 1/s -j ACCEPT
# Heel gemeen: wel een nieuwe connectie, maar geen SYN bit gezet.
iptables -A bad_packets -p tcp ! --syn -m state --state NEW -j DROP
# TCP Spoofing met Sequence Number voorspelling
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK SYN,ACK \
-m state --state NEW -j REJECT --reject-with tcp-reset
In de blacklist chain gaan we regels maken om ons bekende IP-adressen of adresreeksen de toegang te weigeren. Om nu te voorkomen, dat we steeds dit script moeten wijzigen als er weer een snodaard bij komt, zitten de adressen in een apart bestandje. In dit voorbeeld gaan we uit van het bestand /etc/iptables/blacklist.txt, met één adres(-reeks) per regel. Dat bestand kan er zo uitzien:
62.212.130.16 66.150.160.0/25 66.192.59.0/24 66.220.126.0/24 66.232.113.98 66.232.118.17 67.23.0.0/16Regel 1 is een enkelvoudig adres, de regels 2 t/m 4 bevatten adresreeksen (CIDR notatie), de regels 5 en 6 zijn losse IP-adressen en regel 7 is weer een reeks. Dit is maar een snippet; het echte bestand bevat inmiddels 5.750 regels en groeit voortdurend. Overigens is iptables erg flexibel; de laatste reeks had bijvoorbeeld ook mogen worden omschreven als 67.23/16 zonder de laatste twee 0-bytes. Ook de notaties 67.23.0.0/255.255.0.0 en 67.23/255.255.0.0 worden geaccepteerd. De regels staan hier netjes op volgorde; voor iptables hoeft dat niet, maar voor het onderhoud is het wel zo makkelijk.
In het script controleren we eerst even of het bestandje wel bestaat en lezen we met een loopje de
adressen in en bouwen daarmee in iptables de regels op. (twee in iptables tegen 1 in de blacklist)
#! /bin/bash
Voor elk IP-adres of IP-reeks worden steeds twee regels aangemaakt. De eerste springt naar het
LOG target, dat een logregel naar het kernel-log schrijft en terugkeert naar de blacklist
chain. De tweede laat door middel van het DROP target het betreffende pakketje vrolijk aan
scherven vallen. Einde oefening voor dat pakketje dus; daar gebeurt nooit meer iets mee.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
# Instellen INPUT chain
iptables -A INPUT -p tcp --dport 80 -j blacklist
iptables -A INPUT -p tcp --dport 443 -j blacklist
iptables -A INPUT -j bad_packets
# Vullen chain bad_packets
# SYN-flood aanvallen
iptables -A bad_packets -p tcp --syn -m limit --limit 1/s -j ACCEPT
# Portscan aanvallen
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK,FIN,RST RST \
-m limit --limit 1/s -j ACCEPT
# Ping-flood aanvallen met behulp van het ICMP protocol
iptables -A bad_packets -p icmp --icmp-type echo-request -m limit \
--limit 1/s -j ACCEPT
# Heel gemeen: wel een nieuwe connectie, maar geen SYN bit gezet.
iptables -A bad_packets -p tcp ! --syn -m state --state NEW -j DROP
# TCP Spoofing met Sequence Number voorspelling
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK SYN,ACK \
-m state --state NEW -j REJECT --reject-with tcp-reset
# Vullen chain blacklist
if [ -f /etc/iptables/blacklist.txt ]; then
for HOST in `cat /etc/iptables/blacklist.txt`
do
iptables -A blacklist -s $HOST -j LOG --log-level info
iptables -A blacklist -s $HOST -j DROP
done
fi
N.B. In sommige forums wordt gesuggereerd, dat als een adres niet in deze blacklist chain voorkomt,
het pakketje dus zal worden geaccepteerd. Mis! Aan het einde van de blacklist-chain wordt in dat geval terug
gesprongen naar de volgende regel van de INPUT-chain, na het uitstapje naar de blacklist.
Een impliciete -j RETURN dus. Maar wie dat niet vertrouwt, kan 'm ook expliciet opnemen.
Hieronder staat die er in het blauw even bij.
Moet er een nieuwe uitsluiting worden toegevoegd, dan volstaat het dus om een regel toe te voegen aan het bestand /etc/iptables/blacklist.txt en vervolgens dit script opnieuw uit te voeren, zodat alle regels opnieuw worden opgebouwd.
We kunnen nu dit script opslaan en uitvoeren, maar hoe weten we dan wat het resultaat is geworden?
We voegen nog een regel toe, om een listing van de inhoud van iptables te produceren:
#! /bin/bash
De optie -L geeft een listing van alle rules in alle chains. De optie -n is zeer aan
te raden. Als we die namelijk niet toevoegen, zal bij het uitlijsten worden geprobeerd, de IP-adressen
via DNS te vertalen naar hostnamen. Dat maakt het geheel nogal traag. De optie -n laat alles
numeriek, niet alleen adressen maar ook poorten en protocollen.
# Dit is een shell script voor initialisatie van iptables.
# Flush chains en delete non-standaard chains
iptables -F
iptables -X
# Instellen policies standaard chains
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Aanmaken eigen chains
iptables -N bad_packets
iptables -N blacklist
# Instellen INPUT chain
iptables -A INPUT -p tcp --dport 80 -j blacklist
iptables -A INPUT -p tcp --dport 443 -j blacklist
iptables -A INPUT -j bad_packets
# Vullen chain bad_packets
# SYN-flood aanvallen
iptables -A bad_packets -p tcp --syn -m limit --limit 1/s -j ACCEPT
# Portscan aanvallen
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK,FIN,RST RST \
-m limit --limit 1/s -j ACCEPT
# Ping-flood aanvallen met behulp van het ICMP protocol
iptables -A bad_packets -p icmp --icmp-type echo-request -m limit \
--limit 1/s -j ACCEPT
# Heel gemeen: wel een nieuwe connectie, maar geen SYN bit gezet.
iptables -A bad_packets -p tcp ! --syn -m state --state NEW -j DROP
# TCP Spoofing met Sequence Number voorspelling
iptables -A bad_packets -p tcp --tcp-flags SYN,ACK SYN,ACK \
-m state --state NEW -j REJECT --reject-with tcp-reset
# Vullen chain blacklist
if [ -f /etc/iptables/blacklist.txt ]; then
for HOST in `cat /etc/iptables/blacklist.txt`
do
iptables -A blacklist -s $HOST -j LOG --log-level info
iptables -A blacklist -s $HOST -j DROP
done
iptables -A blacklist -j RETURN
fi
# Laat de inhoud van iptables zien
iptables -n -L
Het script dat we in het voorgaande hebben opgebouwd, slaan we op met als naam iptinit. Vergeet niet om het uitvoerbaar te maken: chmod 700 iptinit (alleen de eigenaar mag aan de firewall knoeien).
We voeren het script nu uit. Als alles goed gaat, ziet de listing er ongeveer zo uit:
Chain FORWARD (policy DROP) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination blacklist tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 blacklist tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 bad_packets all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain bad_packets (1 reference) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp flags:0x16/0x02 limit: avg 1/sec burst 5 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp flags:0x17/0x04 limit: avg 1/sec burst 5 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmp type 8 limit: avg 1/sec burst 5 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x16/0x02 state NEW REJECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp flags:0x12/0x12 state NEW reject-with tcp-reset Chain blacklist (2 references) target prot opt source destination LOG all -- 65.219.2.96/28 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 65.219.2.96/28 0.0.0.0/0 LOG all -- 66.150.160.0/25 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 66.150.160.0/25 0.0.0.0/0 LOG all -- 66.192.59.0/24 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 66.192.59.0/24 0.0.0.0/0 LOG all -- 66.220.126.0/24 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 66.220.126.0/24 0.0.0.0/0 LOG all -- 66.232.113.98 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 66.232.113.98 0.0.0.0/0 LOG all -- 66.232.118.17 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 66.232.118.17 0.0.0.0/0 LOG all -- 67.23.0.0/17 0.0.0.0/0 LOG flags 0 level 6 DROP all -- 67.23.0.0/17 0.0.0.0/0
Als we het script opnieuw draaien, al dan niet met een aangevulde blacklist-file, of zelfs als we uit de losse hand een regeltje toevoegen, hoeft de iptables daemon niet te worden herstart. De aanpassing is dynamisch. En zolang we het systeem niet rebooten of stoppen, is er niets aan het handje. De snodaards worden braaf geweerd, de loggings vliegen ons om de oren en de server is er een stuk veiliger op geworden. Maar er zit nog een klein addertje onder het gras: de standaard instellingen van de iptables installatie. Standaard is iptables zo ingesteld, dat er niets wordt bewaard.
Dus na een crash of (re)boot zijn alle chains weer maagdelijk leeg. Al ons werk voor niets, tenzij we ons scriptje weer even draaien. Er worden weliswaar tools meegeleverd om de rules op te slaan (iptables-save) en weer in te stellen (iptables-restore), maar daar moet je maar net aan denken. Meestal heb je wel andere dingen aan je hoofd. Op het Internet worden veel en soms nogal ingewikkelde oplossingen aangedragen, die er vaak op neer komen dat je het scriptje opneemt in cron, of met allerlei tools gaat goochelen. Een hoop geknutsel en tamelijk omslachtig.
Maar het kan gelukkig veel simpeler, door het configuratiebestand van iptables aan te passen. Dat is het bestand /etc/sysconfig/iptables-config. Daarin zijn twee settings van belang: IPTABLES_SAVE_ON_STOP en IPTABLES_SAVE_ON_RESTART. Die staan standaard ingesteld op ="no". Maak daarvan bij beiden ="yes" en iptables zal er na stoppen en weer opstarten van het systeem voor zorgen, dat alle regeltjes in ere worden hersteld.
Er is een flink aantal tools beschikbaar om firewalls via een prachtige grafische interface op te zetten. Een paar bekende zijn Firestarter, Firewall Builder en knetfilter. Sommige zijn niet veel meer dan een schil rond iptables; Firestarter heeft bovendien de onhebbelijke eigenschap de daemon van iptables te vervangen door een eigen daemon. Stuk voor stuk doen ze net niet wat ik wil op de manier zoals ik het wil. Je bent het inzicht kwijt in wat er precies wordt gegenereerd en het geeft een boel onnodige overhead.
Ik heb er een paar uitgeprobeerd, om na het uitlijsten van de resultaten als de donder mijn eigen script te draaien om de boel weer recht te breien! Aha, kijk, daarom begint het script dus met alles leeg te maken en weg te gooien. Juist! Al doende leert men (poetsen).
De hiervoor beschreven methode heeft het voordeel, dat je een veel beter begrip krijgt van de werking van iptables, waardoor je op den duur de firewall tot in detail naar je hand kunt zetten. Wat mij betreft dus geen tools.
Dit geheel is gebaseerd op en getest met een systeem, draaiend met Fedora Core 4. Andere Linux distributies hebben wellicht de bestanden op een andere plaats staan of hebben andere default instellingen. Dat doet echter aan het principe niet af. En iptables is dermate ingeburgerd, dat het ook op gangbare ADSL-modems zoals die van Thomson/Alcatel te vinden is. Die modems zijn via een webinterface te configureren, maar voor het configureren van de firewall is de command-line interface via telnet de enige weg en de standaard documentatie houdt niet over.
Voor Windows systemen zijn ook firewalls beschikbaar, die qua look-and-feel meestal aanzienlijk
zullen afwijken, maar in principe hetzelfde (kunnen) doen.
Maar een Windows systeem als server gebruiken is, in tegenstelling tot wat Microsoft ons wil doen
geloven, niet echt een goed idee. En voor een werkstation zijn de simpeler oplossingen mooi genoeg.