Firewall mit pf

Server nach außen abschotten mit pf

pf als Firewall-System wurde für openBSD entwickelt und ist äußerst leistungsfähig. Wir werden aber nur grundlegende Regeln und Funktionen betrachten. Es gibt sehr umfangreiche Anleitungen und Dokumentationen. Aber wenn du das Prinzip verstanden hast, dann findest du dich damit schnell zurecht.

Dieses Tutorial ist lediglich als Grundlage zu verstehen. Wenn du dieses Tutorial durchgelesen hast und verstanden hast was hier passiert, dann kannst du dich auf den tiefer gehenden Webseiten (siehe Links) weiter einlesen.
Ich beschreibe hier nicht alle Funktionen von pf, das wäre viel zu umfangreich. Es gibt ein sehr gutes Handbuch, welches auch in der Linksammlung zu finden ist. Weiters gehe ich hier von einem Server aus und nicht von einem Gateway, sodass Weiterleitungsregeln o.ä. hier nicht angesprochen werden.

ACHTUNG: Um die Firewall zu konfigurieren sind root-Rechte erforderlich. Du kannst die Befehle also alle mit einem vorangestellen 'sudo' ausführen. Details dazu im Kapitel "Einführung in sudo".

Um pf beim Systemstart zu starten, müssen wir es in der Datei /etc/rc.conf aktivieren. Dafür schreiben wir folgendes in die Datei:

pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
pflog_logfile="/var/log/pflog"

  1. Wenn wir mit pf experimentieren, kann schnell mal ein Fehler unterlaufen. Um zu verhindern, dass wir uns versehentlich aussperren, erstellen wir einen cronjob, der in regelmäßigen Abständen die Firewall wieder deaktiviert:

    # crontab -e

    Jetzt tragen wir folgende Zeilen ein:

    ### Firewall alle 10 Minuten deaktivieren ###
    */10   *   *   *   *   root   /usr/local/bin/sudo /sbin/pfctl -d

    Durch Abspeichern wird der cronjob aktiviert.
    Tipp: der Befehl crontab -l listet alle installierten user-cronjobs auf.
  2. Wir beginnen unsere Experimente indem wir die Datei pf.conf im Home-Verzeichnis erstellen. Diese werden wir später mit sudo laden und nach erfolgreichem Test über die Datei /etc/pf.conf kopieren.
    Zunächst aber ein paar Grundlagen:

    • Makros sind eine Art Array, also eine Liste von Daten. In unserem Fall Ports, wie wir sie aus der Datei /etc/services bereits kennen. Wir verwenden diese Makros um eine Regel für mehrere Ports gültig zu machen, ohne diese Regel für jeden Port neu einzufügen.
      Ein Makro sieht so aus (Port 80 für Webserver, 2031 für unseren SSH):

      tcp_pass = "{ 80 2031 }"
    • pf liest Regeln von oben nach unten
    • Port-Ranges können so geschrieben werden: bspw. 120:135
    • pass on => ein- und ausgehender Datenverkehr
      pass in => eingehender Datenverkehr
      pass out => ausgehender Datenverkehr
    • Das Wort 'quick' in einer Regel bewirkt, dass bei Zutreffen der Regel die nachfolgenden Regeln ignoriert werden.
    • Der Tag 'keep state' sorgt dafür, dass eine einmal hergestellte Verbindung nicht nochmal geprüft wird. Wenn du also einen Webserver betreibst und er über den Port 80 angesprochen wird, werden die Firewall-Regeln nicht  noch einmal durchlaufen, sondern pf geht davon aus, dass diese Verbindung erlaubt ist, da sie ja schon besteht.
  3. Wir öffnen also unsere Datei aus dem Homeverzeichnis. Der Übersichtlichkeit zu liebe nenne ich meine Test-Datei pf-ben.conf. Zunächst legen wir ein Makro für die eingebauten Interfaces an, falls wir die Konfiguration mal auf einen anderen Server übertragen wollen.
    Hinweis: lnc0 musst du natürlich durch dein Interface ersetzen.

    if = "{ lnc0 }"
  4. Anschließend legen wir zwei Makros an: offene tcp- und udp-Ports:

    tcp_pass = "{ 2031 }"

    udp_pass = "{ 2031 }"
  5. Nach diesen Makros (und nur da!) ergänzen wir nun unsere erste Regel, und zwar zur Paket-Normalisierung:

    scrub in all
  6. Jetzt schützen wir uns noch gegen spoofed oder forged IP-Adressen und blocken alle Ports um nur ausgesuchte freizugeben:

    antispoof for $if
    block in all

  7. Unsere erste Regel sieht also folgendermaßen aus:

    if = "{ lnc0 }"
    tcp_pass = "{ 2031 }"
    udp_pass = "{ 2031 }"
    scrub in all
    antispoof for $if
    block in all

  8. Bevor wir nun unseren ersten Port öffnen, will ich noch auf zwei Besonderheiten von pf eingehen: tables und anchors.
    • tables  - Speichern mehrere IP-Adressen oder ganzer IP-Kreise

      Die tables werden über den Regeln eingetragen und meist mit dem Zusatz 'quick', der oben schon erwähnt wurde, versehen, sodass diese IP-Adressen direkt verarbeitet werden, ohne extra alle Regeln durchlaufen.
      Du kannst diese Tabellen beispielsweise einsetzen, wenn du Zugriffen aus dem eigenen IP-Netz (bspw. 192.168.0.XXX) grundsätzlich erlauben willst. Dann könntest du weit oben eine entsprechende Regel anlegen, die jeden Datenverkehr erlaubt.

      Die Syntax zum Anlegen einer Tabelle lautet wie folgt:

      table <intranet> { 192.168.0.0/24, 192.168.1.0/24, !192.168.0.1 }

      Hinweis: Die spitzen Klammern müssen bleiben. 'intranet' ist hier der Name der Tabelle. Die letzte IP-Adresse wird durch das '!' ausgeschlossen.

      Um nun, wie oben beispielhaft erwähnt, jede Verbindung aus den in der Tabelle 'intranet' hinterlegten IP-Netzen zu erlauben, könntest du folgende Regel möglichst weit oben einfügen (pf liest ja von oben nach unten!):

      pass in quick from <intranet> to any keep state

      Um nun die angelegten Tabellen zu prüfen, kannst du diese über folgenden Befehl einsehen:

      # pfctl -t intranet -T show

      Um die Tabelle nun zu laden ohne den Server neu zu starten (das kann im Produktivbetrieb nicht so ohne weiteres möglich sein), kannst du dies mit folgendem Befehl tun (bei jeder Änderung an der Tabelle muss dies getan werden!):

      # pfctl -t intranet -Tl -f /etc/pf.conf

      Hinweis: /etc/pf.conf ist die Datei in der wir die Tabelle hinzugefügt haben. Ich würde also, wie oben erwähnt, zunächst /home/sz_benedikt/pf-ben.conf schreiben.

      Um eine Tabelle nun wieder zu leeren, gibst du folgenden Befehl ein:

      # pfctl -t intranet -T flush
    • anchors - Speichern Regeln, die on the fly geladen und entladen werden können

      Es gibt eigentlich zwei Hauptszenarien, in denen man anchors einsetzen will:

      Fall 1: Man will nur gelegentlich Ports öffnen, bspw. bei einem Serverupdate
      Wir speichern einen Regelsatz in die Datei '/etc/ftp-anchor' (Name frei wählbar) und fügen folgende Zeile ans Ende unserer pf.conf ein:

      anchor ftpanchor

      Jetzt können wir den Regelsatz mit folgendem Befehl laden und damit aktivieren:

      # pfctl -a ftpanchor -f /etc/ftp-anchor

      Um den anchor nun zu prüfen, verwende folgenden Befehl:

      # pfctl -a ftpanchor -s rules
      Fall 2: Man will nur gelegentlich Ports schließen, bspw. bei Verdacht auf eine Bruteforce-Attacke
      Wir speichern wieder einen Regelsatz in die Datei '/etc/ssh-anchor' (Name frei wählbar) und fügen folgende Zeile ans Ende unserer pf.conf ein:

      anchor sshanchor

      Bisher ist es identisch zu Fall 1, allerdings schreiben wir jetzt noch zusätzlich in die Zeile darunter

      load anchor sshanchor from "/etc/ssh-anchor"

      Dies ist vergleichbar mit einem 'include'-Befehl.

      Um nun einen anchor wieder zu entladen, verwendest du folgenden Befehl:

      # pfctl -a sshanchor -F rules

      Dann kannst du wieder prüfen ob der anchor entladen ist:

      # pfctl -a sshanchor -s rules

      Für jede Art von Regel gibt es eine eigene Anchor-Syntax. Siehe hierzu bitte im Handbuch nach.
  9. Jetzt noch eine kleine Befehlsübersicht für pf. Beachte bitte auch das Unterkapitel "pf: Bruteforce und mehr" sowie das Kapitel "pf-Überwachung: pftop".

    • pf deaktivieren: pfctl -d
    • pf aktivieren: pfctl -e
    • Regeln aus einem anderen Verzeichnis laden: pfctl -ef /home/sz_benedikt/pf-ben.conf
    • Default-Regeln laden: pfctl -f /etc/pf.conf
    • kurzer Status von pf: pfctl -s info
    • Detailstatus zu jeder Regel: pfctl -vs info
  10. Um die Regeln, die ich in der Datei /home/sz_benedikt/pf-ben.conf nun dauerhaft als /etc/pf.conf zu speichern, entferne ich wieder den cronjob (Zeile einfach löschen oder ein '#' davor schreiben -> crontab -e) gehe wie folgt vor:

    # cp /home/sz_benedikt/pf-ben.conf /etc/pf.conf

    # pfctl -f /etc/pf.conf

    Hiermit werden die Regeln aus pf.conf geladen und die, die sich noch im Arbeitsspeicher befinden, gelöscht!

    # pfctl -s rules

    Hiermit gebe ich die nun aktivierten Regeln aus und kann nochmals prüfen ob alles stimmt und die richtige Datei geladen ist.
  11. Um dir ein Beispiel zu zeigen, poste ich meine 'pf.conf'. Beachte bitte, dass ich noch zwei Regeln eingebaut habe, die PING und TRACEROUTE erlauben:

    ### INTERFACES ###
    if = "{ lo0, rl0 }"

    ### SETTINGS ###
    set block-policy drop

    ### OFFENE TCP/UDP-PORTS ###
    tcp_pass = "{ 53 2031 }"
    udp_pass = "{ 53 2031 }"
    icmp_types = "echoreq"

    ### NORMALISATION ###
    scrub in all
    antispoof for $if

    ### TABLES ###
    table <intranet>   { 192.168.0.0/24 }
    table <bruteforce> persist

    ### RULES ###
    set skip on lo0
    block all
    block quick from <bruteforce>
    pass in quick from <intranet> to any keep state
    pass in on $if proto tcp from any to any port $tcp_pass flags S/SA keep state (max-src-conn 100, max-src-conn-rate 15/5, overload <bruteforce> flush global)
    pass in on $if proto udp to any port $udp_pass keep state
    pass out quick all keep state

    # PING #
    pass in on $if inet proto icmp all icmp-type $icmp_types keep state

    # TRACEROUTE #
    pass in on $if inet proto udp from any to any port 33433 >< 33626 keep state

Einen Kommentar hinzufügen

Einen Kommentar hinzufügen

This is a captcha-picture. It is used to prevent mass-access by robots. (see: www.captcha.net)
Code im diesem Bild:
Ihr Name(*):
Kommentar(*):
 
  • August 9, 2010, 1:26 pm - Benedikt

    Der * steht für "jede IP". Ob das gewollt ist musst du wissen, ich binde meine Dienste in der Regel an genau eine IP-Adresse um Konflikte oder unerwartetes Verhalten zu vermeiden. Ob das einen Einfluss auf die Erreichbarkeit deiner munin-Instanz hat, kommt auf deine Firewall-Konfiguration an.

    Hast du den Port 4949 auf gemacht?
    Läuft munin in einer Jail?
    Falls ja, hast du eine Redirect-Regel angelegt (rdr)?

    Vielleicht kannst du mir deine PF-Konfiguration per Mail schicken, dann schau ich kurz drauf ob ich was sehe.

    Viel Erfolg,
    Benedikt

  • August 9, 2010, 1:21 pm - jens

    hi,

    ich habe deine config benutzt und alles funktionier so, wie ich es mir wünsche. jedoch läuft mein munin nicht mehr. wenn ich pf deaktiviere, dann läuft es. mein munin service lauscht auf tcp4 0 0 *.4949 *.* LISTEN ; ich dachte local laufen alle ports oder kommt das wegen dem *?

    hast du ne idee, wie ich mein munin mit pf am laufen bekomme?

  • March 3, 2008, 10:52 pm - d.uNd.eE

    Danke für die schnelle Antwort.

    Hat sich aber schon eledigt,
    Es ging um die Anweisung, um sowohl eingehend als auch ausgehend zu öffnen, oben steht "pass on", hatte ich falsch verstanden und

    "pass on on $if..."
    statt
    "pass on $if..."

    geschrieben.
    So funkts.

    so far
    d.uNd.eE

  • February 25, 2008, 7:38 am - Benedikt

    @d.uNd.eE: Ich weiß nicht in welcher Referenz du geschaut hast, aber wenn du bspw. hier schaust (http://www.de.openbsd.org/faq/pf/de/tagging.html), dann ist direkt das erste Beispiel unter &quot;Markierungen den Paketen zuweisen&quot; mit &quot;pass in on&quot; versehen.

    Vielleicht hast du bei deiner Regel das &quot;in&quot; bzw. &quot;out&quot; vergessen? Es heißt nicht &quot;pass on&quot; sondern entweder &quot;pass in on&quot; oder &quot;pass out on&quot;.

    Es funktioniert auf jeden Fall. Irgendwo hast du entweder nen Tippfehler oder eben &quot;in/out&quot; vergessen. Kannst du deine Regel hier mal schreiben oder mir per Mail schicken? Die Adresse findest du unter &quot;Kontakt&quot;.

    Viel Erfolg beim Fehlerfinden,
    Benedikt

  • February 24, 2008, 11:46 pm - d.uNd.eE

    Hi Benedikt,
    erstmal danke für dieses hochgeniale tut...hat mir bisher schon häufig sehr gute Dienste geleistet xD

    Jetzt hab ich ma so ne kleine Frage...die Richtung "on"8also die Anweisung "pass on...") funkt bei mir nicht. Der Ausdruck "on" ist auch in der O-BSD Referenz:PF nicht erwähnt.
    Wäre nett, wenn du mal schreiben kannst, was man machen muss, damit on auch funktioniert.

    Meine ports sind up-to-date, und doe Software auch aktualisiert, ALTQ ist "disabeled", hängt das vllcht damit zusammen?

    So far
    d.uNd.eE

  • December 8, 2007, 10:35 am - Benedikt

    Danke.

  • December 8, 2007, 10:30 am - Cheasy

    sed -e 's/set skip on l0/set skip on lo0/'
    in der Beispiel-Konfiguration

  • October 11, 2007, 1:46 pm - Benedikt

    @McSnoop und t_S: Seit ihr sicher dass es ein FEHLER ist oder lediglich die Info, dass ALTQ im Kernel nicht aktiviert ist?

    Im Generic-Kernel ist ALTQ nicht aktiviert, daher wird die Information ausgegeben. In den obigen Beispielen werden keine ALTQ-Regeln verwendet, daher sollte hier auch keine FEHLER-Meldung auftauchen, lediglich eben der Hinweis.

    Die Firewall funktioniert trotz des Hinweises.

    Gruß,
    Benedikt

  • October 11, 2007, 1:33 pm - t_S

    @McSnoop

    hatte das gleiche Problem wie du --> Fehler "ALTQ im Kernel aktivieren"

    Lösung:

    1)
    Fals /usr/src/sys/ nicht vorhanden

    Man ruft als root das Programm /stand/sysinstall auf und wählt Configure -> Distributions -> src -> sys zur Installation aus. =>

    # sysinstall

    Configure -> Distributions -> src -> sys Installieren

    2)

    Wenn danach /usr/src/sys/ vorhanden ist:

    # cd /usr/src/sys/i386/conf

    # mkdir /root/kernels

    # cp GENERIC /root/kernels/MYKERNEL

    # ln -s /root/kernels/MYKERNEL

    # vi /root/kernels/MYKERNEL

    Miit Diesen Angaben erweitern:

    options ALTQ
    options ALTQ_CBQ # Class Bases Queuing (CBQ)
    options ALTQ_RED # Random Early Detection (RED)
    options ALTQ_RIO # RED In/Out
    options ALTQ_HFSC # Hierarchical Packet Scheduler (HFSC)
    options ALTQ_PRIQ # Priority Queuing (PRIQ)
    options ALTQ_NOPCC # Wird von SMP benötigt

    ->Speichern

    # cd /usr/src/sys/i386/conf/

    # config /root/kernels/MYKERNEL

    # cd ../compile/MYKERNEL

    # make depend

    # make && make install

    -> Bei mir hatz funtkioniert =)

  • October 5, 2007, 11:16 pm - Benedikt

    @Aron: Schreib mir doch bitte ne E-Mail, dann können wir das konstruktiv besprechen, sodass die Tutorials entsprechend verbessert werden. Ich freue mich über jedwede Art der konstruktiven Kritik.

    Ich halte es nicht für sinnvoll, das hier zu besprechen :-)

    Gruß,
    Benedikt

  • October 5, 2007, 10:21 pm - Aron

    Wassen das für nen Traceroute Quatsch? :-)
    Und überhaupt, warum fast alle ICMP-Pakete Blocken. Du treibst hier das Gerücht das man dadurch ein mehr an Sicherheit hat weiter mit voller professionellen Überzeugung *hust*

    Was das Tracerouting angeht, so gibt es hier mehr als eine Implementierung. Du geht auf die Unix ein. Es gibt da aber noch die eigentliche ICMP. Und sogar TCP ist vorhanden.

    echorep, unreach, redir, echoreq, trace, timex, timereq, timerep sollte man somit alle mal als Server im Internet akzeptieren.

    Aron

  • September 19, 2007, 9:16 pm - Benedikt

    Was gibst du genau in die Konsole ein und wie sieht deine pf.conf aus?

    Schreib mir am besten eine E-Mail, da kannst du das sauber anhängen, hier geht das nicht gut.

    Danke,
    Benedikt

  • September 19, 2007, 9:10 pm - McSnoop

    Ich arbeite gerade dein Server Tutorial ab und komme hier an dem Punkt jetzt nicht weiter.

    pfctl bringt nur die Ausschrift: "ALTQ im Kernel aktivieren" Ich habe nichts am Kernel verändert, der is frisch.