ipfw nat und jails

noize

Active Member
Moin @all,

Nachdem ich jetzt schon ein paar Tage an dem Problem sitze und ich keine Ahnung habe was ich noch probieren könnte, frage ich hier mal um Rat.

Das System ist ein FreeBSD 8.1, mit mehreren jails, als Firewall war bis jetzt pf im Einsatz. Jetzt wollte ich mich mal an ipfw in Kombination mit nat versuchen. Die Firewall läuft bereits, allerdings bekomme ich keine Netzwerkverbindung aus der Jail zustande.

Das Netzwerk auf dem Hostsystem funktioniert tadellos. Die Forensuche habe ich bereits bemüht und Google spuckt hierzu auch wenig aus. Das Einzige was ich in Bezug auf ipfw in Verbindung mit Jails gefunden habe war hier:

http://emerge.badfoo.net/2010/05/entry_20.html

Bisher habe ich eine ipfw.rules Datei nach Vorbild aus dem Handbuch erstellt. Dazu habe ich dann Angaben von folgender Quelle mit eingetragen.


Es sind 3 Jails am laufen.

Code:
jail1 = 127.0.10.10                   # Mail
jail2 = 127.0.20.10                   # Mysql
jail3 = 127.0.30.10                   # Apache

Unter PF hat das Setup so funktioniert. Die /etc/resolv.conf ist auf den Nameserver gesetzt, die Einträge in /etc/hosts der jails sind aktuell.

Die /etc/natd.conf ist bisher leer da ich bisher keine Weiterleitungen definiert habe.

/etc/ipfw.rules

Code:
##### ipfw ruleset for saragossa.marascan.net #####
# =================================================

# first step make clean
# =====================

#!/bin/sh 

ipfw -q -f flush

# Some variables needed
# =====================

fwcmd="ipfw -q add"


# network related stuff
# =====================

ext="wlan0"
int="lo0"

router_ip="192.168.0.1"
host_ip="192.168.0.133"

jail1_ip="127.0.10.10"
jail2_ip="127.0.20.10"
jail3_ip="127.0.30.10"



##### =================================================== #####
##### Interface facing Public Internet (Outbound Section) #####
##### =================================================== #####

# Loopback Interface have no restrictions
# =======================================

$fwcmd 00020 allow all from any to any via $ext
$fwcmd 00021 allow all from any to any via $int

$fwcmd 00025 check-state

# first activation of nat on ext
# ==============================

$fwcmd 00005 divert natd all from any to any via $ext
[B]$fwcmd 00006 divert natd ip from $pardona_ip to not me out via $ext
$fwcmd 00007 divert natd ip from $boron_ip to not me out via $ext
$fwcmd 00008 divert natd ip from $nahema_ip to not me out via $ext
$fwcmd 00009 divert natd ip from $rohan_ip to not me out via $ext[/B]

$fwcmd 00010 skipto 10000 ip from any to any diverted

$fwcmd 00011 divert natd ip from not me to any in via $ext
$fwcmd 00012 skipto 10000 ip from any to any diverted


# Allow out access to the public DNS Server
# =========================================

$fwcmd 00100 allow tcp from any to 8.8.8.8 53 out via $ext setup keep-state
$fwcmd 00110 allow udp from any to 8.8.8.8 53 out via $ext keep-state

# Allow out non-secure standard www function
# ==========================================

$fwcmd 00120 allow tcp from any to any 80 out via $ext setup keep-state

# Allow out secure https over TLS SSL
# ===================================

$fwcmd 00130 allow tcp from any to any 443 out via $ext setup keep-state

# Allow getmail & mutt poll or send mails
# =======================================

$fwcmd 00140 allow tcp from any to any 25 out via $ext setup keep-state
$fwcmd 00150 allow tcp from any to any 110 out via $ext setup keep-state

# Allow out FBSD (make install & CVSUp) functions
# User root darf alles ;)
# ===============================================

$fwcmd 00160 allow tcp from me to any out via $ext setup keep-state uid root

# Allow outgoing ping
# ===================

$fwcmd 00170 allow icmp from any to any out via $ext keep-state

# Allow outgoing time
# ===================

$fwcmd 00180 allow tcp from any to any 37 out via $ext setup keep-state

# Allow out nntp news (i. e. news groups)
# =======================================

$fwcmd 00200 allow tcp from any to any 119 out via $ext setup keep-state

# Allow out whois
# ===============

$fwcmd 00210 allow tcp from any to any 43 out via $ext setup keep-state

# Deny and log everything that tries to get out.
# ==============================================

$fwcmd 00299 deny log all from any to any out via $ext



##### ================================================== #####
##### Interface facing Public Internet (Inbound Section) #####
##### ================================================== #####

# Deny all inbound traffic from non-routable reserved 
# address spaces see RFC 1918 for details
# ===================================================

$fwcmd 00300 deny all from 192.168.0.0/16 to any in via $ext
$fwcmd 00301 deny all from 127.16.0.0/12 to any in via $ext
$fwcmd 00302 deny all from 10.0.0.0/8 to any in via $ext
[B]$fwcmd 00303 allow all from 127.0.0.0/8 to any in via $ext[/B]
$fwcmd 00304 deny all from 0.0.0.0/8 to any in via $ext
$fwcmd 00305 deny all from 169.254.0.0/16 to any in via $ext
$fwcmd 00306 deny all from 192.0.2.0/24 to any in via $ext
$fwcmd 00307 deny all from 204.152.64.0/23 to any in via $ext
$fwcmd 00308 deny all from 224.0.0.0/3 to any in via $ext

# Deny public pings
# =================

$fwcmd 00310 deny icmp from any to any in via $ext

# Deny ident
# ==========

$fwcmd 00315 deny tcp from any to any 113 in via $ext

# Deny all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS Windows hosts2 name server request 81
# =============================================================

$fwcmd 00320 deny tcp from any to any 137 in via $ext
$fwcmd 00321 deny tcp from any to any 138 in via $ext
$fwcmd 00322 deny tcp from any to any 139 in via $ext
$fwcmd 00323 deny tcp from any to any 81 in via $ext

# Deny any late arriving packets
# ==============================

$fwcmd 00330 deny all from any to any frag in via $ext

# Deny ACK packets that did not match the dynamic rule table
# ==========================================================

$fwcmd 00332 deny tcp from any to any established in via $ext

# Allow in standard www function because I´m an apache ;)
# =======================================================

$fwcmd 00400 allow tcp from any to me 80 in via $ext setup limit src-addr 2

# Allow incoming ssh scp and sftp from public Internet
# ====================================================

$fwcmd 00410 allow tcp from any to me 1023 in via $ext setup limit src-addr 2

# Reject & Log all incoming connections from the outside
# ======================================================

$fwcmd 00499 deny log all from any to any in via $ext

# Everything else is denied by default
# ====================

[B]$fwcmd 00999 deny log logamount 10000 ip from any to any[/B]

# All Traffic skipped to rule 10000 is allowed
# ============================================================

[B]$fwcmd 10000 allow ip from any to any via $ext[/B]

Die ipfw.rules ist aus dem Handbuch entnommen. Die fett markierten Stellen habe ich geändert, siehe link.

ipfw show liefert folgende Ausgabe:

So wie es aussieht greifen die Regeln 6 - 9 die ich wegen der Jails eingetragen habe nicht. Es greift Regel 10 die auf Regel 10000 am Ende springt. Diese Regel ist auf allow any to any via wlan0 gesetzt.

Code:
 # ipfw show
00005 14139 6184689 divert 8668 ip from any to any via wlan0
00006     0       0 divert 8668 ip from 127.0.10.10 to not me out via wlan0
00007     0       0 divert 8668 ip from 127.0.40.10 to not me out via wlan0
00008     0       0 divert 8668 ip from 127.0.30.10 to not me out via wlan0
00009     0       0 divert 8668 ip from 127.0.20.10 to not me out via wlan0
00010 13819 6136070 skipto 10000 ip from any to any diverted
00011     0       0 divert 8668 ip from not me to any in via wlan0
00012     0       0 skipto 10000 ip from any to any diverted
00020     0       0 allow ip from any to any via wlan0
00021    68    5440 allow ip from any to any via lo0
00025     0       0 check-state
00100     0       0 allow tcp from any to 8.8.8.8 dst-port 53 out via wlan0 setup keep-state
00110     0       0 allow udp from any to 8.8.8.8 dst-port 53 out via wlan0 keep-state
00120     0       0 allow tcp from any to any dst-port 80 out via wlan0 setup keep-state
00130     0       0 allow tcp from any to any dst-port 443 out via wlan0 setup keep-state
00140     0       0 allow tcp from any to any dst-port 25 out via wlan0 setup keep-state
00150     0       0 allow tcp from any to any dst-port 110 out via wlan0 setup keep-state
00160     0       0 allow tcp from me to any out via wlan0 setup uid root keep-state
00170     0       0 allow icmp from any to any out via wlan0 keep-state
00180     0       0 allow tcp from any to any dst-port 37 out via wlan0 setup keep-state
00200     0       0 allow tcp from any to any dst-port 119 out via wlan0 setup keep-state
00210     0       0 allow tcp from any to any dst-port 43 out via wlan0 setup keep-state
00299     0       0 deny log ip from any to any out via wlan0
00300     0       0 deny ip from 192.168.0.0/16 to any in via wlan0
00301     0       0 deny ip from 127.16.0.0/12 to any in via wlan0
00302     0       0 deny ip from 10.0.0.0/8 to any in via wlan0
00303     0       0 allow ip from 127.0.0.0/8 to any in via wlan0
00304     0       0 deny ip from 0.0.0.0/8 to any in via wlan0
00305     0       0 deny ip from 169.254.0.0/16 to any in via wlan0
00306     0       0 deny ip from 192.0.2.0/24 to any in via wlan0
00307     0       0 deny ip from 204.152.64.0/23 to any in via wlan0
00308     0       0 deny ip from 224.0.0.0/3 to any in via wlan0
00310     0       0 deny icmp from any to any in via wlan0
00315     0       0 deny tcp from any to any dst-port 113 in via wlan0
00320     0       0 deny tcp from any to any dst-port 137 in via wlan0
00321     0       0 deny tcp from any to any dst-port 138 in via wlan0
00322     0       0 deny tcp from any to any dst-port 139 in via wlan0
00323     0       0 deny tcp from any to any dst-port 81 in via wlan0
00330     0       0 deny ip from any to any frag in via wlan0
00332     0       0 deny tcp from any to any established in via wlan0
00400     0       0 allow tcp from any to me dst-port 80 in via wlan0 setup limit src-addr 2
00410     0       0 allow tcp from any to me dst-port 1023 in via wlan0 setup limit src-addr 2
00499     0       0 deny log ip from any to any in via wlan0
00999     0       0 deny log logamount 10000 ip from any to any
10000 13819 6136070 allow ip from any to any via wlan0
65535   124   35628 deny ip from any to any


Ich habe bisher wenig Ahnung von Firewalls, darum wollte ich mal zu einer anderen wechseln um meinen Wissensstand zu erweitern.

MfG
 
kleine Anmerkung am Rande: "#!/bin/sh" sollte in der ersten Zeile der Datei stehen, sonst kannst du es auch gleich weglassen.
 
Hab ich geändert, macht aber letztlich keinen Unterschied.

Hab jetzt festgestellt wenn ich in die Regel 10000 ein out einfüge dann funktioniert das skipto Argument oben nicht mehr. Er zeigt zwar an dass diese Regel aktiv wird wenn ich ein fetch oder telnet absetze aber in Regel 10000 tut sich dann nix mehr.

Was ich auch nicht verstehe ist, dass die nat Regeln für die einzelnen Jail ip` s nicht greifen und immer erst die skipto Regel greift. Dachte dass die anderen Regeln für das entsprechende jail greifen würden.

Ich weiß jetzt auch nicht wie die Regeln abgearbeitet werden. Normalerweise müsste dass doch nach der Reihenfolge abgearbeitet werden nach der diese in der ipfw.rules eingetragen sind.
 
Okay, beginnen wir mal oben. natd(8) sollte nicht mehr genutzt werden. ipfw kann nun schon seit längerem echtes NAT, was schneller ist und sich deutlich besser in das Regelwerk integriert.

Ansonsten, wie sollen Regeln 6 bis 9 denn überhaupt greifen können? ipfw basiert auf dem "first match, first serve"-Anstaz, sprich sobald eine Regel greift, wird diese auf das Paket angewandt. Nachfolgende Regeln werden nicht mehr beachtet. Regel 5 schmeißt alles in natd(8) rein, was über dein $ext_if läuft. Die späteren Einschränkungen der Regeln 6 bis 9 sind daher völlig egal, sie werden nie erreicht. Du müsstest es wenn überhaupt umkehren, also erst die Spezialfälle und dann den allgemeinen Fall für alles, was die Spezialfälle nicht gegriffen haben.

Es folgte Zeile 10. Alles was durch natd(8) gelaufen ist nun zurückgekommen ist, wird nach Zeile 10000 geschoben. Die dann auch greift wie sie soll und alles durchlässt. Für das "out" in der Regel ist es wichtig zu verstehen, wie die Pakete durch das System laufen. Deine Pakete von innen können in dem Fall wunderbar heraus, aber es kommen keine Pakete mehr hinein. Der ganze Spaß ist also sinnlos, da der Service damit nicht mehr erreichbar ist.

Dazwischen hast du nun diverse Regeln, die greifen sollen, wenn natd(8) nicht durchlaufen wird. Aber das können sie im Moment nicht. Einmal da wie gesagt Regel 5 zu weit gefasst ist. Weiter unten hast du dann den gleichen Fehler noch mal. Regel 20 ist sehr weit gefasst, sie würde immer greifen, wenn sie denn erreicht werden würde und alles darunter ist damit völlig sinnlos. Auch da musst du umdrehen.
 
Ach, ja. Das mit dem In-Kernel NAT ist in der Manpage zwar gut erklärt aber für den Anfang doch recht kompliziert. Daher hier einmal ein praktisches Minimalbeispiel:
Code:
# NICs
lan_if="em0"

# NAT
nat_if="em0"
nat_ip=`ifconfig $nat_if | grep -v inet6 | grep inet | awk '{ print $2; }'`
jail_lan="10.0.0.0/8"

# CMD
cmd="/sbin/ipfw -q"

# -------------------------------------------------------------------- #

# Alles raus
/sbin/ipfw -q -f flush

# -------------------------------------------------------------------- #

# Alles was einen State hat darf durch
$cmd add 10 check-state

# Vom Host auf die Jails
$cmd add 20 allow all from me to $jail_lan setup keep-state

# -------------------------------------------------------------------- #
#
# NAT für die Jails
$cmd nat 1 config if $nat_if 
$cmd add 30 nat 1 all from $jail_lan to any
$cmd add 40 nat 1 all from any to $nat_ip

# -------------------------------------------------------------------- #

# Alles durchlassen
$cmd add 50 allow all from any to any

Diese Regeln schieben alles, was aus dem Netz 10.0.0.0/8 kommt, durch das NAT. Umgekehrt wird alles, was auf die oben durch die coole awk-Zeile ermittelte NAT-IP will ebenfalls in das NAT geschoben. Die Pakete landen hinterher von allein dort, wo sie hin sollen.
 
grep -v inet6 | grep inet | awk '{ print $2; }'`

==>

awk '$1 == "inet" {print $2}'

Ist auch sicherer, der String "inet" könnte ja auch wo anders vorkommen, z.B. in eine WLAN SSID.
 
Wow, das ging schnell mit den Antworten. Bin jetzt leider nicht vor meinem PC und kann erst morgen die Einstellungen testen. So wie ich das sehe muss ich mir in jedem Fall die Manpage nochmal zu Gemüte führen.

Ich meld mich wenn ich morgen wieder vorm Rechner bin.
 
Hab das jetzt mal mit dem Skript von Yamagi ausprobiert und es funzt. Nur die $jail_ip musste ich auf 127.0.0.0/8 setzen. Vielen Dank an dieser Stelle schonmal.

yamagi schrieb:
Okay, beginnen wir mal oben. natd(8) sollte nicht mehr genutzt werden. ipfw kann nun schon seit längerem echtes NAT, was schneller ist und sich deutlich besser in das Regelwerk integriert.

Das die NAT Funktionalität mittlerweile seit ipfw2 integriert ist habe ich gelesen, allerdings hab ich im Netz nix dazu gefunden wie ich das im Skript aktiviere.

Da die Posts die ich dazu gelesen habe, allesamt schon älter waren und unter

/usr/src/sbin/ipfw

auch ein File ipfw2.c liegt, bin ich davon ausgegangen dass das möglich ist.

yamagi schrieb:
ipfw basiert auf dem "first match, first serve"-Anstaz, sprich sobald eine Regel greift, wird diese auf das Paket angewandt. Nachfolgende Regeln werden nicht mehr beachtet.

Das heißt dann in der Praxis, dass Regeln, die in Einzelfällen greifen sollten immer weit oben im Skript stehen sollten.

Das out hatte ich in einer vorherigen Version noch nicht drinnen. Wenn das gesetzt ist, heißt das dann, dass ich die Pakete nach aussen lasse, aber dann auf dem Rückweg blocke, da ja keine in Regel vorliegt?

Ich bekomm dann also auch keine Rückmeldung vom Server, ob das Paket angekommen ist oder nicht.

yamagi schrieb:
# Alles durchlassen
$cmd add 50 allow all from any to any

Mit dieser letzten Zeile habe ich dann eine Firewall, die generell alles durchlässt, oder oder liege ich da falsch? Die letzte deny Regel, die automatisch gesetzt wird, greift dann überhaupt nicht mehr, da Regel 50 immer matcht.

Wenn ich also dann nur bestimmte ports öffnen will, muss die Regel auf jeden Fall vor die 50 gesetzt werden, da sie sonst nicht mehr erreicht wird.

Es funktioniert auf jeden Fall schon mal. Zum Ausbau des Skripts heist` s erstmal schmökern in der manpage

PS: Die nat_ip Zeile is genial. Dadurch könnte ich mir eine feste IP Zuweisung vom Router sparen ... ;)

Edit: ,,,,,
 
noize schrieb:
Das heißt dann in der Praxis, dass Regeln, die in Einzelfällen greifen sollten immer weit oben im Skript stehen sollten.
Genau. Spezialfälle immer vor den allgemeinen Fällen, da sonst letztere greifen und die Spezialfälle damit niemals erreicht werden.

noize schrieb:
Das out hatte ich in einer vorherigen Version noch nicht drinnen. Wenn das gesetzt ist, heißt das dann, dass ich die Pakete nach aussen lasse, aber dann auf dem Rückweg blocke, da ja keine in Regel vorliegt?

Ich bekomm dann also auch keine Rückmeldung vom Server, ob das Paket angekommen ist oder nicht.
Jein. Also, eine Regel im Stil von
Code:
$cmd add allow from any to any out
lässt zwar Pakete heraus, aber keine herein. Dann tritt genau das ein, was du sagtest. Das Paket erreicht die Gegenseite, die Bestätigung wird aber ausgefiltert und kommt daher niemals bei dir an. Aus diesem Grund gibt es das Keyword "keep state". Damit wird für jede neu aufgebaute Verbindung eine dynamische Regel erstellt, die an der Stelle ins Script eingefügt wird wo "check-state" steht. Das wäre also so:
Code:
$cmd add check-state
$cmd add allow from any to any out keep state
Hier wird für das ausgehende Paket eine dynamische Regel erstellt, durch die die Antworten durchgelassen werden. So erreicht die Bestätigung dich. Von außen kann dennoch keiner eigenmächtig auf dich zugreifen, da eben vorher kein Paket rausgegangen ist und damit keine dynamische Regel erstellt wurde.

noize schrieb:
Mit dieser letzten Zeile habe ich dann eine Firewall, die generell alles durchlässt, oder oder liege ich da falsch? Die letzte deny Regel, die automatisch gesetzt wird, greift dann überhaupt nicht mehr, da Regel 50 immer matcht.
Genau. Meine Regel 50 lässt immer alles durch, die Firewall ist also offen und filtert nichts aus. Das ist natürlich in der Praxis recht sinnlos, hier aber okay, weil ich die IPFW auf dieser Maschine nur für das NAT nutze. Um nur einzelne Ports durchzulassen, müsstest du zwei Dinge tun:
1. Wie du sagst vor Zeile 50 Regeln einfügen, die Pakete auf diesen Ports durchlassen. Auch hier wieder "Spezialfall vor allgemeinen Fall", du musst erst ausgewählten Kram durchlassen, bevor du alles dicht machst.
2. Regel 50 so ändern, dass sie alles blockiert oder sie komplett herausnehmen.
 
Die nat_ip Zeile hat einen Bug. Sollten mehre Addressen auf dem Interface $lan_if definiert sein schlägt sie fehl. Es stehen dann mehrere Zeilen statt einer IP Addresse drin, wenn du das dennoch so machen must schreib ein head -n 1 da hinter.
 
Die NAT Zeile hab ich nicht mit eingetragen da ich die IP bisher statisch vergeben hab. War hilfreich um die NAT IP rauszufinden.

Normalerweise läuft die Firewall immer auf eine generelle Block Regel hinaus. Dies kann man laut Handbuch durch eine Kerneloption auf DEFAULT_TO_ACCEPT stellen, Was für die ersten Gehversuche bzw. bei einer Remote Maschine durchaus angebracht ist. Ansonsten sperrt man sich selbst aus und muss die Service Hotline bemühen. Bei mir steht das Dingens unterm Schreibtisch.

Im Moment bin ich noch am Manpage lesen, danach Skript anpassen und nagel dann den Rest zu.
 
Genau. Gegen aussperren gibt es aber auch einen netten Trick:
1. screen oder tmux öffnen
2. 'sleep 300 ; kldunload ipfw' reinschreiben
3. screen oder tmux in den Hintergrund legen
Sperrt man sich nun aus, muss man nur die 5 Minuten warten und anschließend kommt man wieder rein. Geht alles gut, drückt man einmal strg-c :)

Und zu meiner $nat_ip Zeile: Das war ein kurzer und dreckiger Hack für ein Notebook und sollte so natürlich nicht verwendet werden... Zumindest von niemanden außer mir. :p
 
Zurück
Oben