Linux

Iptables: Εισαγωγικός οδηγός

09/11/2010 11:27 πμ | apoikos από apoikos


Υπότιτλος: Firewall magic made (hopefully) easy

 

 

 

Εισαγωγή

The true power is in you hands (or actually, in the console under your hands ;-) ). Ναι, υπάρχουν πολλά frontends για το netfilter/iptables, όμως μόνο η κονσόλα δίνει τόσο μεγάλη ευελιξία. Επιπλέον αν κατανοήσει κανείς πλήρως τον τρόπο λειτουργίας της iptables, θα έχει ήδη μάθει πολλά για δίκτυα και firewalls.

 

Ο κύριος λόγος λοιπόν για τον οποίο γράφω αυτόν τον οδηγό είναι για να χρησιμεύσει ως μια εισαγωγική απομυθοποίηση του firewalling στο Linux και τελικά να δράσει ως ερέθισμα για περαιτέρω αναζητήσεις δικτυακού τύπου.

 

Γνωρίζω καλά ότι ο παρών οδηγός δεν είναι πλήρης και ενδεχομένως περιέχει κάποιες ανακρίβειες (οι περισσότερες χάριν απλουστεύσεως), ωστόσο είναι πραγματικά δύσκολο να γράφει ένας πλήρης οδηγός στα πλαίσια του ελεύθερου χρόνου που διαθέτω. Θα προσπαθήσω ωστόσο να τον κρατάω ενήμερο και να τον επεκτείνω και σε άλλα θέματα, με σκοπό το στήσμο μιας home gateway σε Linux.

 

Απολαύστε λοιπόν :-)

 

 

 

Netfilter: Το σύστημα φιλτραρίσματος πακέτων του πυρήνα του Linux

Ο πυρήνας του Linux προσφέρει ένα πολύ ευέλικτο και δυνατό σύστημα (framework) φιλτραρίσματος πακέτων, με το όνομα Netfilter. Το Netfilter, εκτός από την υποδομή για το φιλτράρισμα πακέτων, παρέχει επίσης λειτουργίες NAT (Network Address Translation) και τροποποίησης των εισερχόμενων, εξερχόμενων, ή δρομολογούμενων μέσω του υπολογιστή πακέτων, καθιστώντας το Linux ένα πολύ δυνατό εργαλείο για την ανάπτυξη firewalls (προσωπικών ή μη), routers, και gateways με δυνατότητες αντίστοιχες (ή ίσως και καλύτερες) με εκείνες ακριβών εμπορικών εφαρμογών ή ακριβών hardware firewalls/routers. Ας ρίξουμε λοιπόν μια ματιά στην αρχιτεκτονική του Netfilter:

 

 

Περί πινάκων, αλυσίδων και κανόνων

To Netfilter έχει μια σχετικά πολύπλοκη - ωστόσο απόλυτα λογική - διάρθρωση: Ανάλογα με τις τρεις μεγάλες κατηγορίες λειτουργιών (filtering, NAT και mangling), υπάρχουν οι αντίστοιχοι πίνακες (tables): filter, nat και mangle. Κάθε πίνακας έχει κάποιες αλυσίδες, οι οποίες συνήθως αντιστοιχούν τοπολογικά στη θέση που βρίσκονται τα πακέτα (βλ. σχήμα 1). Κάθε αλυσίδα τέλος αποτελείται από μια σειρά κανόνων, τους οποίους διατρέχουν τα πακέτα, ελέγχοντας αν ταιριάζουν στα κριτήρια του κάθε κανόνα στη σειρά. Ναι, ακούγεται πολύπλοκο, αλλά ένα σχήμα θα βοηθήσει πολύ στο να ξεκαθαρίσουν όλα:

netfilter.png

Σχήμα 1: Η δομή του netfilter

 

 

Οι αλυσίδες αναλυτικότερα

Στο σχήμα 2 φαίνεται η σειρά με την οποία διατρέχουν τα πακέτα τις αλυσίδες

 

netfilter-chains.png

Σχήμα 2: Η σειρά διέλευσης από τις αλυσίδες

 

Εδώ αξίζει να παρατηρήσουμε τα εξής:

  • Οι αλύσίδες με ίδια ονόματα δεν ταυτίζονται: η INPUT του filter table είναι τελείως διαφορετική από την INPUT του mangle κλπ.
     
  • Οι αλυσίδες INPUT και OUTPUT αναφέρονται στα πακέτα που προορίζονται για ή φεύγουν από το μηχάνημα που τρέχει Linux. Αν δηλαδή το μηχάνημα αυτό είναι router ή bridge, στις INPUT και OUTPUT περνάνε μόνο τα πακέτα που έχουν σχέση με το ίδιο το μηχάνημα. Όλα τα άλλα, δηλαδή ότι περνάει μέσω του router/bridge, περνάνε από τη FORWARD. Σε περίπτωση λοιπόν που το μηχάνημα είναι απλά ένα workstation, μας αφορά κυρίως η INPUT chain και λιγότερο η OUTPUT, ενώ αν πρόκειται για router ή firewall για ένα ολόκληρο δίκτυο, μας ενδιαφέρει επιπλέον και η FORWARD κατά κύριο λόγο.
     
  • Οι αλυσίδες POSTROUTING και PREROUTING διαχειρίζονται όλα τα πακέτα, δηλαδή και τα διερχόμενα και αυτά του συγκεκριμένου υπολογιστή. Ωστόσο δεν πρέπει να χρησιμοποιούνται για φιλτράρισμα, παρά μόνο για ΝΑΤ και mangling στους αντίστοιχους πίνακες.

 

Τι γίνεται όμως όταν τα πακέτα διατρέχουν τις αλυσίδες;

 

Τα πακέτα διατρέχουν μια αλυσίδα από την κορυφή προς τα κάτω, δηλαδή από τον πρώτο κανόνα προς τον τελευταίο, γι' αυτό και η σειρά με την οποία βάζουμε τους κανόνες έχει σημασία. Κάθε κανόνας της αλυσίδας είναι στην πραγματικότητα ένα conditional if, με κάποια κριτήρια σχετικά με το πακέτο. Τα κριτήρια αυτά μπορούν να είναι ένα ή περισσότερα από τα:

  • Πρωτόκολλο (π.χ. TCP, UDP, ICMP, GRE, IGMP κλπ)
  • Θύρα αφετηρίας ή προορισμού (για όσα πρωτόκολλα αυτή ορίζεται)
  • Διεύθυνση αφετηρίας ή προορισμού

 

Τα παραπάνω κριτήρια είναι αυτά που εν γένει χρησιμοποιούνται σε ένα packet filter. Το netfilter όπως πηγαίνει ακόμα παραπέρα, και επιτρέπει - με τη χρήση modules - την επιλογή πακέτων με βάση:

  • Την κατάσταση της σύνδεσης (stateful inspection, βλ. παρακάτω)
  • Το ρυθμό εισροής πακέτων
  • Τη φυσική διεύθυνση (MAC address)
  • To ποιός χρήστης τρέχει το πρόγραμμα που προκαλεί αυτήν την κίνηση
  • κλπ.

 

Αν το πακέτο συμφωνεί με τα κριτήρια του κανόνα, τότε κατευθύνεται προς έναν προορισμό (target), όπου εφαρμόζεται πάνω του μια ενέργεια. Οι προορισμοί για το μεν φιλτράρισμα μπορούν να είναι οι «κλασσικοί» ACCEPT, DROP, REJECT και LOG, για το nat μπορεί να είναι DNAT (destination NAT), SNAT (source NAT) ή MASQUERADE (dynamic source NAT), ενώ για το mangle διατίθενται διάφοροι «ειδικοί» προορισμοί (π.χ. MARK, CONNMARK, TCPMSS κλπ). Τέλος, σε όλα τα tables μπορούμε να ορίσουμε δικές μας αλυσίδες, οι οποίες μπορούν να χρησιμεύσουν ως προορισμοί για τα πακέτα. Στο σημείο αυτό αξίζει να τονιστεί ότι κάποιο προορισμοί είναι «τερματικοί» (terminating), δηλαδή τα πακέτα σταματούν εκεί και δε διασχίζουν την υπόλοιπη αλυσίδα, ενώ κάποιο άλλοι προορισμοί είναι απλά προορισμοί διέλευσης. Για παράδειγμα οι ACCEPT, DROP και REJECT είναι τερματικοί, αφού είτε αποδεχόμαστε το πακέτο (ACCEPT) και το στέλνουμε προς το δίκτυο, είτε το απορρίπτουμε (DROP και REJECT), ενώ οι προορισμοί LOG και MARK για παράδειγμα δεν είναι τερματικοί.

 

Αν ένα πακέτο διατρέξει μια από τις built-in αλυσίδες χωρίς να ταιριάξει πουθενά, φτάνει στο τέλος της όπου εφαρμόζεται η default policy: λέμε με άλλα λόγια αν θέλουμε να δεχόμαστε ή να απορρίπτουμε ερήμην τα πακέτα που έρχονται και ανάλογα προσαρμόζουμε το firewall μας στη λογική του «δέχομαι τα πάντα εκτός από αυτά που απορρίπτω» ή τη λογική του «απορρίπτω τα πάντα εκτός από αυτά που δέχομαι». Τονίζεται ότι default policy έχουν μόνο οι built-in αλυσίδες και όχι αυτές που δημιουργήθηκαν από το χρήστη: αν ένα πακέτο φτάσει στο τέρμα μιας user-defined αλυσίδας, τότε επιστρέφει στην αλυσίδα από την οποία έφυγε, στον επόμενο κανόνα της. Το ξέρω ότι όλα αυτά φαντάζουν πολύπλοκα, γι αυτό ρίξτε μια ματιά στο επόμενο σχηματάκι:

 

 

chain-traversal.png

Σχήμα 3: Ροή πακέτων σε μια αλυσίδα του filter table

 

 

 

3. Λίγα λόγια για τα πρωτόκολλα

Πρωτού μπούμε στο ζουμί, θεωρώ σκόπιμο να κοιτάξουμε μερικά πραγματάκια σχετικά με τα πιο κοινά πρωτόκολλα. Οι πληροφορίες αυτές δε μας είναι μεν άμεσα χρήσιμες, βοηθούν όμως στην κατανόηση του μηχανισμού με τον οποίο λειτουργεί ένα σύγχρονο δίκτυο.

  • IP: Τo IP πρωτόκολλο είναι το βασικό πρωτόκολλο πάνω στο οποίο «κάθονται» όλα τα συνήθη πρωτόκολλα. Ορίζει μόνο διευθύνσεις παραλήπτη και αποστολέα και δεν έχει καμία άλλη πληροφορία. Το IP είναι stateless, δηλαδή δεν υπάρχει η έννοια της «σύνδεσης»· είναι απλά πακέτα που πάνε από έναν κόμβο του δικτύου προς έναν άλλον.
     
  • UDP (User Datagram Protocol): Το UDP προσφέρει επιπλέον από το IP την έννοια της «θύρας»: κάθε πακέτο, επιπλέον της IP διεύθυνσης, έχει και θύρα αποστολέα και παραλήπτη. Το UDP όπως και το IP είναι stateless, δεν έχει δηλαδή την έννοια της σύνδεσης και έχει πολύ μικρό overhead, αφού το header του κουβαλάει απλά την πληροφορία για τις θύρες. Για το λόγο αυτό χρησιμοποιείται κυρίως σε εφαρμογές low-latency, οι οποίες όμως δε μας ένδιαφέρει να είναι αξιόπιστες (π.χ. VoIP: δε χάθηκε ο κόσμος αν χαθεί ένα πακέτο, δε θα καταλάβεις τη διαφορά στη φωνή).
     
  • TCP (Transmission Control Protocol): Το TCP πάει ένα βήμα παραπέρα, εισάγοντας - εκτός από τις θύρες - και την έννοια της «σύνδεσης». Πρόκειται για ένα stateful πρωτόκολλο, το οποίο έχει έναν ολόκληρο μηχανισμό συνεννοήσης προκειμένου να διατηρεί μια «σύνδεση» ανάμεσα στους δύο κόμβους, προσφέροντας αξιόπιστη επικοινωνία. Ενώ με το UDP δεν μπορούμε να ξέρουμε - σε επίπεδο πρωτοκόλλου - αν το πακέτο παρελήφθη από την άλλη πλευρά, ενώ με το TCP η γνώση αυτή είναι ενσωματωμένη στο πρωτόκολλο. Επιπλέον το TCP παρέχει μηχανισμούς κατακερματισμού μεγάλων ποσοτήτων δεδομένων (fragmentation), ανίχνευσης συμφόρησης (congestion), απόδοσης προτεραιότητας (QoS: Quality of Service) κλπ. Συνολικά είναι ένα πολύ πιο στιβαρό πρωτόκολλο, αλλά γι' αυτό και απαιτεί περισσότερους πόρους για την υλοποίησή του απ' ότι το UDP.
     
    Ενδιαφέρον έχει στην παρούσα φάση να δούμε το βασικό μηχανισμό συνεννόησης του TCP/IP: Κάθε πακέτο φέρει κάποια bits (τα λεγόμενα flags), τα οποία αναλόγως με την τιμή που έχουν (0 ή 1 προφανώς) υποδηλώνουν μια κατάσταση της σύνδεσης. Έστω λοιπόν ότι o κόμβος Α θέλει να επικοινωνήσει με τον κόμβο Β. Η διαδικασία έχει ως εξής:
     
    1. Ο Α στέλνει στον Β ένα TCP πακέτο, με τις σωστές διευθύνσεις και θύρες, και με το SYN flag σημασμένο (1). Η σύνδεση χαρακτηρίζεται ως «νέα».
    2. Ο Β απαντάει με το SYN και το ACK σημασμένα (1).
    3. Ο Α στέλνει πίσω ένα πακέτο με μόνο το ACK σημασμένο. Η σύνδεση χαρακτηρίζεται πλέον ως «established» και μπορεί να αρχίσει κανονικά η ανταλλαγή δεδομένων.

     

    Από όλη αυτή τη διαδικασία, μας ενδιαφέρουν οι χαρακτηρισμοί new και established, τους οποίους και θα χρησιμοποιήσουμε παρακάτω.

     

    [*]ICMP (Internet Control Message Protocol): Όπως λέει και το όνομά του, το ICMP είναι ένα πρωτόκολλο για την ανταλλαγή πληροφοριών ελέγχου μεταξύ των κόμβων του δικτύου. Πρόκειται για ένα απλό, stateless πρωτόκολλο, που απλά ορίζει τύπους πακέτων που επιδέχονται συγκεκριμένες απαντήσεις. Έτσι για παράδειγμα το γνωστό μας PING είναι το ICMP echo-request (ή type 8), το οποίο απαντάται με ICMP echo-reply.

 

 

 

Φιλτράρισμα πακέτων

Τώρα που έχουμε πάρει όλες τις βασικές πληροφορίες σχετικά με τη δομή του netfilter και τα χαρακτηριστικά των κυριότερων πρωτοκόλλων επικοινωνίας, ας δούμε πως κάνουμε το πραγματικό φιλτράρισμα πακέτων.

 

Το netfilter από μόνο του δεν μπορεί να κάνει τίποτα, απλά τρέχοντας στο kernel space. Χρειαζόμαστε ένα user-space εργαλείο, με το οποίο θα χειριζόμαστε τους κανόνες, τις αλυσίδες και τους πίνακες, και το εργαλείο αυτό δεν είναι άλλο από το iptables ;-).

 

Η βασική σύνταξη της εντολής iptables είναι η ακόλουθή:

>
iptables [-t table-name] command chain-name rule-description

 

  • Η παράμετρος table-name ορίζει τον πίνακα με τον οποίο θα ασχοληθούμε και παίρνει τις τιμές filter, nat και mangle. Αν παραληφθεί, τότε η iptables υποθέτει ότι μιλάμε για το filter table.
  • Η command ορίζει τη θέλουμε να κάνουμε με μια αλυσίδα.
  • chain-name είναι το όνομα της αλυσίδας
  • και ακολουθεί η περιγραφή του κανόνα, με τα κριτήρια επιλογής των πακέτων και την ενέργεια που πρέπει να ληφθεί για τα πακέτα που ταιριάζουν.

Οι κυριότερες διαθέσιμες εντολές είναι:

  • Προσθήκη κανόνα στο τέλος της αλυσίδας: iptables -A INPUT -j LOG
  • Προσθήκη κανόνα σε προκαθορισμένη θέση της αλυσίδας, πριν από τον κανόνα που υπήρχε εκεί: iptables -I INPUT 3 -p tcp --dport ssh -j ACCEPT
  • Αλλαγή κανόνα σε συγκεκριμένη θέση της αλυσίδας: iptables -R INPUT 3 -p tcp --dport http -j ACCEPT
  • Διαγραφή κανόνα από συγκεκριμένη θέση της αλυσίδας: iptables -D INPUT 3
  • Διαγραφή όλων των κανόνων της αλυσίδας: iptables -F INPUT
  • Διαγραφή όλων των κανόνων όλων των αλυσίδων όλου του πίνακα: iptables -t nat -F
  • Καθορισμός default policy κάποιας αλυσίδας: iptables -P INPUT DROP
  • Δημιουργία user-defined αλυσίδας: iptables -N a_new_chain
  • Εμφάνιση των κανόνων που περιέχει μια αλυσίδα: iptables -L chain_name (-v)
  • Εμφάνιση όλων των κανόνων όλων των αλυσίδων ενός πίνακα: iptables -t nat -L (-v)

(Σημείωση: με έντονα γράμματα τονίζεται η «εντολή» και οι άμεσες παράμετροί της. Τονίζεται ότι οι παραπάνω εντολές κελύφους είναι παραδείγματα, μην προσπαθήσετε να τις χρησιμοποιήσετε αυτούσιες.)

 

Η περιγραφή του κανόνα ξεκινά με τα κριτήρια που πρέπει να πληροί το πακέτο:

  • Πρωτόκολλο: -p [tcp,icmp,udp,...]
  • Διεύθυνση αποστολέα: -s 1.2.3.4
  • Διεύθυνση παραλήπτη: -d 2.3.4.5
  • Θύρα αποστολής: --sport 123 ή --source-port 1234
  • Θύρα προορισμού: --dport 2345 ή --destination-port 2345
  • Interface εισόδου (μόνο στις INPUT, PREROUTING, FORWARD): -i eth0
  • Interface εξόδου (μόνο στις OUTPUT, POSTROUTING, FORWARD): -o eth1

και ολοκληρώνεται με την ενέργεια-προορισμό: -j [ACCEPT, DROP, REJECT, LOG, ...].

 

Ας δούμε λοιπόν μερικά παραδειγματάκια:

  1. Απαγόρευση πρόσβασης στη mysql:
    >iptables -A INPUT -p tcp --dport 3306 -j DROP


    Εδώ επειδή παραλείψαμε τον αποστολέα, ο κανόνας ταιριάζει για όλους τους αποστολείς.
     

  2. Κρύψιμο από pings, εκτός από ένα συγκεκριμένο subnet
    >iptables -A INPUT -s ! 10.19.145.0/24 -p icmp --icmp-type echo-request -j DROP


     
    Εδώ βλέπουμε δύο ενδιαφέροντα πράγματα: το πρώτο είναι ότι το θαυμαστικό δρα ως λογικό NOT, αντιστρέφοντας το νόημα της εντολής και το δεύτερο είναι το --icmp-type, το οποίο και αποτελεί επιπλεόν κριτήριο το icmp, και παρέχεται επειδή ορίσαμε -p icmp. Για να δούμε όλες τις διαθέσιμες επιλογές για ένα πρωτόκολλο, μπορούμε να χρησιμοποιήσουμε την εντολή iptables -p protocol_name -h.

 

Αφού πλεόν έχουμε το βασικό μηχανισμό για να επιλέγουμε ποια πακέτα θέλουμε να χειριστούμε, ήρθε η ώρα να ρίξουμε μια γρήγορη ματιά στον τρόπο με τον οποίο λειτουργούν οι προορισμοί-ενέργειες (targets). Όπως είπαμε προηγουμένως, οι συνήθεις ενέργειες είναι ACCEPT, DROP, REJECT και LOG. Κάθε μια από αυτές επιτελεί και μια διαφορετική λειτουργία:

  • ACCEPT: με την ACCEPT το firewall αποδέχεται το πακέτο ως «καλό», και το στέλνει εκεί που θα έπρεπε να πάει κανονικά, σύμφωνα με τα στοιχεία που φέρει.
     
  • DROP: όταν κάνουμε DROP ένα πακέτο, τότε ουσιαστικά το σβήνουμε από τη μνήμη και είναι σα να μην υπήρξε ποτέ. Εννοείται ότι δε φτάνει ποτέ στον τελικό του προορισμό.
     
  • REJECT: όταν απορρίπτουμε ένα πακέτο με REJECT, τότε το σβήνουμε από τη μνήμη και δε φτάνει ποτέ στον τελικό του προορισμό. Σε αντίθεση με τη DROP όμως, στέλνουμε ένα μήνυμα πίσω στον αποστολέα, το οποίο συνήθως τον ενημερώνει για το λόγο της απόρριψης. π.χ.:
    >iptables -p tcp --dport 80 -j REJECT --reject-with icmp-host-prohibited


  • LOG: το target αυτό είναι non-terminating, δηλαδή - σε αντίθεση με τα ACCEPT, DROP και REJECT - το πακέτο συνεχίζει την πορεία του στην αλυσίδα. Αυτό που κάνει το LOG είναι ότι γράφει μια καταχώρηση στο syslog του συστήματος, με κάποιες πληροφορίες για το πακέτο που έκανε match. Η προσεκτική χρήση του κανόνα μπορεί να μας παράσχει πολύτιμες πληροφορίες για διάφορες «ύποπτες» δραστηριότητες στο δίκτυό μας.
     
  • Τέλος ως target μπορεί να χρησιμοποιηθεί οποιαδήποτε user-defined αλυσίδα. Η δυνατότητα αυτή διευκολύνει το φιλτράρισμα κατά ομάδες κίνησης. Έστω π.χ. ότι έχουμε ένα μηχάνημα με πολλές κάρτες δικτύου και θέλουμε να εφαρμόσουμε διαφορετικούς κανόνες σε κάθε κάρτα. Βολεύει να κάνουμε το εξής:
    >
    iptables -N in_eth0
    iptables -N in_eth1
    iptables -N in_wlan0
    iptables -A INPUT -i eth0 -j in_eth0
    iptables -A INPUT -i eth1 -j in_eth1
    iptables -A INPUT -i eth2 -j in_eth2
    


    Έτσι μπορούμε να βάζουμε τους κανόνες για κάθε interface σε ξεχωριστή αλυσίδα, κάνοντας τη ζωή μας πολύ πιο εύκολη.

 

 

Matches

Σύμφωνα με αυτά που έχουμε δει μέχρι στιγμής, τα κριτήρια επιλογής πακέτων περιορίζονται κυρίως στις IP's, θύρες και πρωτόκολλα, πράγμα το οποίο δε δίνει και μεγάλη ευελιξία. Ευτυχώς το netfilter αλλά και η iptables διαθέτουν modules (ή matches κατά την ορολογία της iptables), τα οποία προσθέτουν επιπλέον λειτουργικότητα. Τα modules αυτά φορτώνονται με την επιλογή -m module_name, στο κυρίως σώμα της εντολής. Από τα διάφορα modules που υπάρχουν, το πιό χρήσιμο είναι το state match, το οποίο επιτρέπει να ξεχωρίζουμε τις παλιές από τις νέες συνδέσεις. To module λοιπόν αυτό μας επιτρέπει να φτιάξουμε το εξής minimal firewall για ένα workstation:

>
iptables -F
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P INPUT DROP

Με μόλις 3 εντολές έχουμε κλείσει τελείως το σύστημά μας προς τα έξω. Εδώ «όλα τα λεφτά» είναι ότι αφήνουμε επιλεκτικά να περάσουν οι συνδέσεις εκείνες που είναι ήδη ESTABLISHED, δηλαδή αυτές που έχουμε ξεκινήσει εμείς. Το RELATED μας γλιτώνει από διάφορα άλλα προβλήματα, κυρίως με το FTP, το οποίο ανοίγει μια δεύτερη, άσχετη σύνδεση για να φέρει τα δεδομένα. Με το state match και με φορτωμένο το ip_conntrack_ftp στον πυρήνα, το netfilter μπορεί να αναγωνρίσει ότι η σύνδεση αυτή είναι από το FTP session που έχουμε και να την αφήσει να περάσει. Θα θυμάστε ίσως ότι από τα τρία βασικά πρωτόκολλα (UDP, TCP, ICMP), μόνο το TCP είναι stateful, οπότε Θα περίμενε κανείς το state match να δουλεύει μόνο για TCP. Ωστόσο το netfilter είναι αρκετά «έξυπνο» ώστε να καταλαβαίνει ποια UDP πακέτα αποτελούν μια «λογική» σύνδεση και ποιό PONG είναι απάντηση σε ποιο PING, ώστε να δουλεύει και με UDP και ICMP.

 

Τέλος, αξίζει να δωσουμε προσοχή στη 2η γραμμή, η οποία αφήνει όλα τα πακέτα που έρχονται από το loopback interface. Πάντα είναι το πρώτο πράγμα που κάνουμε σε ένα firewall, να αφήνουμε τα πακέτα από το loopback interface. Αυτά είναι τα πακέτα που στέλνονται στο ίδιο το μηχάνημα από τον εαυτό του και αν τα κόψουμε δε θα δουλεύουν διάφορα πράγματα (π.χ. τα X Windows).

 

 

 

Μερικά παραδείγματα rulesets

Ήρθε πλέον η ώρα να δούμε δύο ολοκληρωμένα παραδείγματα απλών firewalls.

 

 

Απλό firewall για web/ftp/mail server

>
iptables -F
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j LOG
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -m limit \
--limit 5/s --limit-burst 10 -j ACCEPT
iptables -p icmp --icmp-type ! echo-request -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p tcp --dport 21 -j ACCEPT
iptables -A INPUT -j LOG
iptables -P INPUT DROP

modprobe ip_conntrack_ftp

Ας δούμε γραμμή-γραμμή τι κάνει το παραπάνω σύνολο κανόνων:

  • >iptables -F


    Καθαρίζουμε ολόκληρο το filter table (σε περίπτωση που έχει κάτι μέσα)
     
     

  • >iptables -A INPUT -i lo -j ACCEPT


    Επιτρέπουμε τα πακέτα που έρχονται από το loopback interface (βλ. παραπάνω)
     
     

  • >iptables -A INPUT -m state --state INVALID -j DROP


    Πετάμε όλα τα «ύποπτα» πακέτα και αυτά που δε συμμορφώνονται με τις προδιαγραφές των πρωτοκόλλων τους
     
     

  • >iptables -A INPUT -p tcp ! --syn -m state --state NEW -j LOG
    iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP


    Καταγράφουμε και πετάμε όλα τα νέα TCP πακέτα (θυμηθείτε τον τρόπο που γίνεται το handshake στο TCP), που δεν έχουν μόνο το SYN flag set. Για καλό δεν είναι συνήθως ;-).
     
     

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


    Αφήνουμε να περάσουν όλες οι established και related συνδέσεις. Αυτό είναι κάτι που κάνουμε πάντα, αλλιώς ενδέχεται να μην παίξουν διάφορα πράγματα. Επιπλέον αφαιρεί φορτίο από τον πυρήνα.
     
     

  • >iptables -A INPUT -p icmp --icmp-type echo-request -m limit \
    --limit 5/s --limit-burst 10 -j ACCEPT


    Επιτρέπουμε 5 ping/s, με ανοχή για 10 μαζεμμένα. Αυτό μας προστατεύει από ένα ενδεχόμενο ping flood, αφήνοντας όμως τα pings να λειτουργούν γενικά (πράγμα το οποίο είναι καλό). Εδώ χρησιμοποιύμε ένα ακόμα match, το limit match. iptables -m limit -h για περισσότερες πληροφορίες ;-)
     
     

  • >iptables -p icmp --icmp-type ! echo-request -j ACCEPT


    Δεχόμαστε όλα τα άλλα icmp πακέτα, αφού επιτελούν σημαντικές λειτουργίες
     
     

  • >iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    iptables -A INPUT -p udp --dport 53 -j ACCEPT
    iptables -A INPUT -p tcp --dport 21 -j ACCEPT
    iptables -A INPUT -p tcp --dport smtp -j ACCEPT
    iptables -A INPUT -p tcp --dport imap -j ACCEPT
    


    Αφήνουμε ανοιχτή την πρόσβαση στις θύρες 80 (web), 22(ssh), 53/udp (DNS), 21 (ftp), 25 (smtp) και 143 (imap). Παρατηρήστε ότι μπορούμε να χρησιμοποιούμε είτε τους αριθμούς των θυρών, είτε τα αντίστοιχα ονόματα των υπηρεσιών, όπως αυτά εμφανίζονται στο /etc/services.
     
     

  • >iptables -A INPUT -j LOG
    iptables -P INPUT DROP
    


    Κάνουμε LOG και DROP όλη την υπόλοιπη κίνηση, αφού κανονικά δε θα έπρεπε κανείς να θέλει κάτι άλλο από το μηχάνημά μας εκτός από αυτά που προσφέρει. Στην πράξη ενδέχεται το LOG να καταγράφει διάφορα άσχετα πράγματα (π.χ. brodacast/multicast κίνηση), και ίσως να γεμίσει τα logs με άχρηστη πληροφορία, οπότε θέλει λίγο finetuning.
     
     

  • >modprobe ip_conntrack_ftp


    Αυτό το module του πυρήνα είναι απαραίτητο για να δουλεύει το FTP σε active και passive mode πίσω από firewall.

 

 

Μικρό «hardware» firewall για την προστασία ενός routable δικτύου (χωρίς NAT δηλαδή)

>
iptables -F
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j LOG
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -m limit \
--limit 5/s --limit-burst 10 -j ACCEPT
iptables -p icmp --icmp-type ! echo-request -j ACCEPT.
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -j LOG
iptables -P INPUT DROP

iptables -N net2lan

iptables -A FORWARD -o eth0 -j net2lan

iptables -A net2lan -m state --state INVALID -j DROP
iptables -A net2lan -p tcp ! --syn -m state --state NEW -j DROP
iptables -A net2lan -m state --state ESTABLISHED,RELATED \
-j ACCEPT
iptables -A net2lan -p icmp --icmp-type echo-request -m limit \
--limit 10/s -j ACCEPT
iptables -A net2lan -p icmp --icmp-type ! Echo-request -j ACCEPT
iptables -A net2lan -d 10.10.10.11 -p tcp --dport 9111 -j ACCEPT
iptables -A net2lan DROP

iptables -N lan2net
iptables -A FORWARD -o eth1 -j lan2net

iptables -A lan2net -p tcp --dport 445 -j DROP
iptables -A lan2net -p tcp --dport 139 -j DROP

modprobe ip_conntrack_ftp
echo "1" > /proc/sys/net/ipv4/ip_forwarding

Στο πρώτο μέρος αυτού του ruleset προστατεύουμε το ίδιο το μηχάνημα, χρησιμοποιώντας την INPUT chain. Είναι ίδιο με το παραπάνω παράδειγμα, εκτός του ότι αφήνουμε μόνο το ssh ανοιχτό (ένα firewall δε χρειάζεται να τρέχει τίποτα άλλο).

 

Το ενδιαφέρον εδώ είναι το δεύτερο κομμάτι, στο οποίο φιλτράρουμε την κίνηση που έρχεται από «έξω» και κατευθύνεται προς το εσωτερικό μας δίκτυο (eth0), στο οποίο βρίσκονται τα μηχανήματα που πρέπει να προστατέψουμε.

 

  • >iptables -N net2lan
    iptables -A FORWARD -o eth0 -j net2lan


    Εδώ φτιάχνουμε μια νέα αλυσίδα, τη net2lan, στην οποία στέλνουμε όλη την κίνηση που διέρχεται από το router/firewall και περνάει προς το εσωτερικό μας δίκτυο.
     
     

  • >iptables -A net2lan -m state --state INVALID -j DROP
    iptables -A net2lan -p tcp ! --syn -m state --state NEW -j DROP
    iptables -A net2lan -m state --state ESTABLISHED,RELATED \
    -j ACCEPT
    iptables -A net2lan -p icmp --icmp-type echo-request -m limit \
    --limit 10/s -j ACCEPT
    iptables -A net2lan -p icmp --icmp-type ! Echo-request -j ACCEPT
    


    Εφαρμόζουμε τους ίδιους κανόνες με πριν, αφήνοντας τις ESTABLISHED και RELATED συνδέσεις, αυτές δηλαδή που έχουν ξεκινήσει τα PC του εσωτερικού δικτύου. Επίσης αφήνουμε τα pings με όριο και τα υπόλοιπα ICMP.
     
     

  • >iptables -A net2lan -d 10.10.10.11 -p tcp --dport 9111 -j ACCEPT


    Αφήνουμε τις νέες συνδέσεις προς τη θύρα 9111 του 10.10.10.11, όπου δέχεται συνδέσεις κάποιο service που τρέχει εκεί.
     
     

  • >iptables -A net2lan DROP


    Θυμηθείτε ότι οι custom αλυσίδες δεν έχουν default policy, οπότε πρέπει να κόψουμε υπόλοιπα πακέτα με έναν έξτρα κανόνα στο τέλος της αλυσίδας, αλλιώς θα επέστρεφαν στη FORWARD και θα περνούσαν.
     
     

  • >iptables -N lan2net
    iptables -A FORWARD -i eth0 -j lan2net
    
    iptables -A lan2net -p tcp --dport 445 -j DROP
    iptables -A lan2net -p tcp --dport 139 -j DROP
    


    Ομοίως φτιάχνουμε και μια αλυσίδα στην οποία στέλνουμε την κίνηση που φεύγει από το LAN μας προς το υπόλοιπο δίκτυο. Στην αλυσίδα αυτή κόβουμε απλά τις θύρες 139 και 445 TCP, οι οποίες χρησιμοποιούνται για το Windows networking και είναι πολύ ευάλωτες σε exploits. Δε θέλουμε λοιπόν αν κάποιο PC εσωτερικά μολυνθεί με κάποιον ιο μέσω mail, να τον διαδώσει προς το εξωτερικό δίκτυο. Τα υπόλοιπα πακέτα απλά επιστρέφουν στην FORWARD και γίνονται ACCEPT (που είναι η default policy για τη FORWARD, εφόσον δεν την αλλάξαμε).
     
     

  • >echo "1" > /proc/sys/net/ipv4/ip_forwarding


    Με την εντολή αυτή ενεργοποιούμε το packet forwarding, ώστε το linux μηχάνημα να δρα ως gateway για τα υπόλοιπα. Αρκεί πλέον να τοποθετήσουμε το μηχάνημα μεταξύ εσωτερικού και εξωτερικού δικτύου, και να το δηλώσουμε ως gateway στα υπόλοιπα pc's, οπότε θα κάνει routing και firewalling ταυτόχρονα.

 

 

 

Χρήσιμα links

  • Linux iptables-HOWTO: (η βασική πηγή έμπνευσης για το 1ο Firewalling fest του AWMN, από την παρουσίαση του οποίου προέκυψε ο παρών οδηγός)
  • IP Sub-networking HOWTO
  • nmap: Ένα πολύ δυνατό εργαλείο για να δοκιμάζετε τα firewalls που φτιάχνετε
  • firewall builder: Γραφικό περιβάλλον, object-oriented, για την κατασκευή firewalls σε Linux και άλλα λειτουργικά. Πολύ καλό και δυνατό, αλλά χρειάζεται να ξέρετε τα βασικά.

 

Τέλος 1ου μέρους :-)