HowTo vnet-Jail with DHCP

Andy_m4

Well-Known Member
Hallo,

ich stehe gerade irgendwie auf dem Schlauch.
Ich bin unter FreeBSD 14.2-RELEASE-p1 GENERIC amd64

Ich habe eine vnet-Jail erstellt. Meine /etc/jail.conf sieht folgendermaßen aus:
Bash:
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
exec.consolelog = "/var/log/jail_${name}_console.log";

testjail {
    host.hostname="${name}";
    path = "/jails/${name}";
    mount.devfs;
    devfs_ruleset = 100; # vnet-devfs Rules + bpf

    vnet;
    vnet.interface = "e0b_testjail";             # vnet interface(s)
    exec.prestart += "jib addm testjail bge0";   # bridge interface(s)
    exec.start    += "/sbin/dhclient e0b_testjail";
    exec.poststop += "jib destroy testjail";     # destroy interface(s)

    allow.raw_sockets = 1;
}
jib ist das Skript, aus /usr/share/examples/jib

Das devfs-Ruleset hab ich in der /etc/devfs.rules definiert:
Code:
[devfsrules_jail_vnet_bpf=100]
add include $devfsrules_jail_vnet
add path 'bpf*' unhide

$devfsrules_jail_vnet ist in der /etc/default/devfs.rules definiert.

Ich kann die Jail starten. Die läuft auch und auch die Network-Bridges und epair-Devices werden ordnungsgemäß erstellt.
Code:
# ifconfig
bge0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=c0099<RXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,VLAN_HWTSO,LINKSTATE>
        ether 70:10:6f:ca:54:16
        inet 192.168.179.5 netmask 0xffffff00 broadcast 192.168.179.255
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
        options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3
        groups: lo
        nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
bge0bridge: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=0
        ether 58:9c:fc:10:ff:cb
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200
        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
        member: e0a_testjail flags=143<LEARNING,DISCOVER,AUTOEDGE,AUTOPTP>
                ifmaxaddr 0 port 5 priority 128 path cost 2000
        member: bge0 flags=143<LEARNING,DISCOVER,AUTOEDGE,AUTOPTP>
                ifmaxaddr 0 port 1 priority 128 path cost 20000
        groups: bridge
        nd6 options=9<PERFORMNUD,IFDISABLED>
e0a_testjail: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=8<VLAN_MTU>
        ether 02:00:dd:ca:54:16
        hwaddr 02:4c:ff:96:2e:0a
        groups: epair
        media: Ethernet 10Gbase-T (10Gbase-T <full-duplex>)
        status: active
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

Auch die Vergabe der IP-Adresse klappt.
Code:
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
        options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x7
        groups: lo
        nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
e0b_nginx: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=8<VLAN_MTU>
        ether 0e:00:dd:ca:54:16
        hwaddr 02:4c:ff:96:2e:0b
        inet 192.168.179.69 netmask 0xffffff00 broadcast 192.168.179.255
        groups: epair
        media: Ethernet 10Gbase-T (10Gbase-T <full-duplex>)
        status: active
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

Allerdings kann ich aus der Jail heraus nicht "pingen". Es kommt immer:

Code:
# ping 192.168.179.1
PING 192.168.179.1 (192.168.179.1): 56 data bytes
ping: sendto: Permission denied
ping: sendto: Permission denied
^C

Das Permission denied verwundert mich. Ich hatte deshalb auch das allow.raw_sockets = 1; hinzugefügt, was aber zu keinen Änderungen geführt hat.
 
Ok. Anscheinend habe ich den Fehler gefunden. Manchmal hilft es anscheinend, einfach mal alles in Gedanken durchzugehen. :-)

Auf dem Host ist ipfw aktiv. Die Firewall ist da auch offen aber anscheinend führt es dazu, das diese dann auch in der Jail aktiv ist.
In der Jail ipfw list aufgerufen ergibt:
Code:
65535 deny ip from any to any
Wenn man die Regeln (in der Jail) anpasst, dann lässt sich das Problem fixen.
Eine Möglichkeit ist, entsprechend dem Handbuch
https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-ipfw-enable
die rc.conf der Jail anzupassen:
Bash:
firewall_enable="YES"
firewall_type="open"

Dann klappts auch mit dem ping und das allow.raw_sockets in der /etc/jail.conf brauchts dann natürlich auch nicht mehr (außerdem können wir die name-Variable gleich noch besser nutzen und das physische Interface als Variable definieren):
Bash:
##### ggf. anpassen, das es dem tatsaechlichen physischen Network-Interface entspricht:
$netzwerkintf = "bge0";
 
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
exec.consolelog = "/var/log/jail_${name}_console.log";
host.hostname="${name}";
path = "/jails/${name}";


testjail {
    mount.devfs;
    devfs_ruleset = 100; # vnet-devfs Rules + bpf

    vnet;
    vnet.interface = "e0b_${name}";                       # vnet interface(s)
    exec.prestart += "jib addm ${name} ${netzwerkintf}";  # bridge interface(s)
    exec.start    += "/sbin/dhclient e0b_${name}";
    exec.poststop += "jib destroy ${name}";               # destroy interface(s)
}

Der Titel des Threads kann daher gerne geändert werden in HowTo vnet-Jail with DHCP :)
 
Zuletzt bearbeitet:
Na dann kann ich das ja auch noch ergänzen. :-)

Nämlich, wie man eine (Thick-)Jail generell erstellt. Das ist zwar auch im Handbuch (Handbuch/Jails) beschrieben, aber an manchen Stellen weiche ich davon ab.

Wenn man ZFS hat, kann man dessen Möglichkeiten natürlich nutzen.
In dem man nämlich für die Jail ein Dataset erstellt:
Bash:
zfs create zroot/jails/testjail

# Falls man Compression haben will (hier mal exemplarisch mit zstd)
zfs set compression=zstd zroot/jails/testjail

# Speicherplatzbegrenzung?
zfs set quota=20G zroot/jails/testjail

# Wenn man keine Zugriffszeit braucht:
zfs set atime=off zroot/jails/testjail
siehe: zfs-create(8), zfs-set(8), zfsprops(7)

Zum einfachen und schnellen Installieren einer Jail kann man bsdinstall nehmen. Das sorgt dann auch gleich dafür, das die /etc/localtime und /etc/resolv.conf vom Host übernommen wird.
Bash:
bsdinstall jail /jails/testjail

Damit Jails auch beim booten mit gestartet werden, tragen wie folgendes (wie auch im Handbuch beschrieben) in die /etc/rc.conf ein:
Bash:
jail_enable="YES"
jail_parallel_start="YES"
jail_list="testjail" # falls nicht alle in der /etc/jail.conf definierten Jails beim Bootup gestartet werden sollen
Die jail_ Variablen sind in der /etc/defaults/rc.conf beschrieben.

Nach dem wir das getan haben und die Jail in der /etc/jail.conf (siehe Posting #2 und Posting #1) definiert haben, können wir sie starten:
Bash:
service jail start testjail
Laufende Jails anschauen können wir uns mit jls.

Updaten können wir vom Host-System aus mit freebsd-update:
Bash:
freebsd-update -j testjail fetch install
(was man auch immer schnellstmöglich tun sollte, weil das in der Jail installierte FreeBSD-System keine Patches hat; es ist auf RELEASE-Stand)

Software-Packages installieren können wir vom Host-System aus mit pkg:
Bash:
pkg -j testjail install XYZ
Wenn man sich eher aus dem lastest-Repository bedienen möchte, dann muss man in der Jail (entsprechend dem Handbuch zu Packages) Anpassungen vornehmen:
Bash:
mkdir -p /jails/testjail/usr/local/etc/pkg/repos
echo 'FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest" }' > /jails/testjail/usr/local/etc/pkg/repos/FreeBSD.conf
pkg -j testjail update -f

Wenn man mehrere Jails hat, können Updates etwas mühevoll sein. Allerdings kann man ja jls benutzen, um über alle laufenden(!) Jails zu iterieren. Und so kann man das ein bisschen automatisieren:
Bash:
#!/bin/sh
for j in $(jls | grep -v JID | awk '{print $3}'); do
  freebsd-update -j "$j" fetch install
  pkg -j "$j" upgrade
done
(siehe auch pkg-upgrade)

Im Prinzip kann man das auch "unattended" machen, wenn man an dem pkg-Befehl noch ein --yes dran hängt und beim freebsd-update als "Pager" cat definiert:
Bash:
#!/bin/sh
for j in $(jls | grep -v JID | awk '{print $3}'); do
  env PAGER=/bin/cat freebsd-update -j "$j" fetch install
  pkg -j "$j" upgrade --yes
done

Man kann auch Tools wie checkrestart nutzen, um zu schauen, ob eine Jail neu gestartet werden muss (aber Achtung; checkrestart ist nicht 100%ig zuverlässig!)
Bash:
#!/bin/sh

PAGER=/bin/cat

for j in $(jls | grep -v JID | awk '{print $3}'); do
  freebsd-update -j "$j" fetch
  freebsd-update -j "$j" install
  if [ $? -eq 2 ]; then # Exitcode 2 bedeutet, das Updates installiert wurden
    # es kann durchaus sinnvoll sein, schon hier vorsorglich ein
    service jail restart "$j"
    # zu machen bzw. checkrestart zu benutzen
  fi
  pkg -j "$j" upgrade --yes
  output=$(/usr/local/bin/checkrestart -j "$j")
  if [ $? -ne 0 ] || [ -n "$output" ]; then
    echo "$output"
    service jail restart "$j"
  fi
done
was sich dann auch als CRON-Job nutzen lässt, so man das riskieren will.

Anregungen, Korrekturen, Ergänzungen, Fragen sind, wie immer, erwünscht. :-)
 
ist ein wenig kürzer.
Da hast du völlig recht. Schließlich macht service jail start ... letztlich auch nix anderes. Ich dachte nur, es wäre so konsistenter zu dem, was ich vorher in der /etc/rc.conf angerührt hab.
Wenn man nix in der /etc/rc.conf hat und man eigentlich nur so ne Jail hat, die man hin und wieder on the fly anwirft (hab zum Beispiel eine Jail die rein für Testzwecke da ist), ist quasi jail -c ... ja "the way to go".

Dem Jail-Kommando kann man auch all die Parameter mitgeben, die man auch in der /etc/jail.conf hinterlegen kann, weshalb der dann auch ausreichend ist, wenn man ad-hoc mal ne Jail erstellen will. Das ist insbesondere dann ganz nett, wenn man gar keine Full-Jail hat, sondern es beispielsweise nur darum geht einzelne Applikationen zu sandboxen.
 
Ich dachte nur, es wäre so konsistenter zu dem, was ich vorher in der /etc/rc.conf angerührt hab.
Klar, ich bin auch so ein Gewohnheitsmensch und hätte ich mir von Anfang an service jail ...angewöhnt, wäre das nur schwerlich wieder rauszuprügeln und da man mit 40, gerade in der heutigen Zeit, nicht mehr viel Lebensbalken hat, ziehe ich lieber den Kürzeren. ;)

Zu Beginn eines neuen RELEASE bastel ich mir auch gerne jeweils eine "golden jail". zstd-19, fertig gepatcht, mit timezone, resolv.conf und mit snapshot z.B. @R142, die man dann einfach einsatzbereit klonen kann.
 
die man dann einfach einsatzbereit klonen kann.
Das ist ja auch keine verkehrte Idee. Die greift ja in ähnlicher Weise auch unter anderem das Handbuch auf, um Thin-Jails zu realisieren (vgl. Thin-Jails mit ZFS).

Apropos Thin-Jails. Gerade wenn man mehrere Jails hat, bin ich durchaus ein Fan davon. Am liebsten würde ich das ja mit UnionFS machen, wo man über die Basis-Jail quasi einen Layer drüber legt, so das man einerseits die Basis-Jail gut updaten kann, andererseits dann auch ein übersichtlichen Thin-Jail-Verzeichnisbaum hat, in dem dann nur die Änderungen zur Basis-Jail sichtbar sind.

Die Lösung krankt so ein bisschen daran, das das UnionFS gelinde gesagt nicht im besten Zustand (vgl. dazu auch die BUGS-Section der Manpage) ist. Ich hoffe mal, da wird sich zeitnah was dran ändern (siehe auch: https://freebsdfoundation.org/project/unionfs-stability-and-enhancement/). Man kann sich so etwas auch mit nullfs hinbasteln, ist aber nicht so elegant (immerhin supportet nullfs inzwischen auch einzelne Dateien und nicht nur Verzeichnisse, weshalb man das fein-granularer aufbauen kann; aber schön ists trotzdem nicht).

Das zweite Ding was mir noch fehlt ist:

Man braucht ja in den seltensten Fällen ein vollständiges FreeBSD-System für eine Jail. Viele Sachen (Manpages, Compiler, Docs etc.) sind ja häufig für den Betrieb gar nicht notwendig (oder sogar problematisch, weil wenn ein Angreifer eine Jail übernimmt, will man dem ja möglichst wenig Tools hinstellen).

Zwar kann man sich das irgendwie hinfrickeln, in dem man z.B. das System mit einer angepassten src.conf selbst kompiliert. Aber schön und einfach ist das alles nicht.
Aber auch dafür ist ja ne praktikable Lösung im Anflug (bzw. sogar schon eingeschränkt benutzbar). PkgBase (was nebenbei dann noch das Problem erledigt, das man nur noch pkg fürs updaten braucht und nicht mehr auf das - noch positiv formuliert - angestaubte freebsd-update angewiesen ist).
btw: Viele jüngere Veränderungen in pkg sind ja auch PkgBase-motiviert.

Jedenfalls wenn die beiden Sachen funktionieren und offiziell im Release stecken, bin ich fürs erste wieder glücklich. :-)
 
In Posting #4 hat sich ein Tippfehler eingeschlichen. Und zwar bei den Skripts (der wurde dann durch Copy&Paste noch repliziert).

Und zwar in der Zeile:
Bash:
for j in $(jls | grep -v JID | awk '{print $3}'); do

ist das print $3 falsch und muss durch print $2 (für Selektierung des Jail-Names) oder print $1 (für Selektierung der Jail-ID) ersetzen werden.

Außerdem finde ich das mit dem grep -v JID (invert-match) nicht so schön. Das stolpert nämlich über Jails, die mit JID bezeichnet sind. Man kann dafür auch tail -n +2 nehmen, um ab der Zweiten Zeile auszugeben (siehe tail(1)).

Insgesamt wird dann daraus:
Bash:
for j in $(jls | tail -n +2 | awk '{print $2}'); do
 
Zurück
Oben