summaryrefslogtreecommitdiff
path: root/content/bsd/freebsd-homelab/dhcp-vnet-jail.md
blob: 1a472791e66660e917403f85b1c899207c571a71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
+++
title = "Template for jail with an external IP assigned via DHCP"
author = ["Michał Sapka"]
date = 2024-10-29T22:03:00+01:00
categories = ["bsd"]
draft = false
weight = 3001
primary_menu = "bsd"
image_dir = "bsd"
image_max_width = 600
abstract = "Running old adventure games"
[menu]
  [menu.bsd]
    weight = 3001
    identifier = "template-for-jail-with-an-external-ip-assigned-via-dhcp"
    parent = "freebsd-homelab"
+++

The idea behind FreeBSD homelab is simple: to utilize the Jail system.
Jails are great!

What I want is to have jails with:

-   dedicated, external IP
-   IPs are assigned via DHCP server
-   I am able to access files outside if the jails

I was able to achieve most of this by following [FreeBSD handbook](https://docs.freebsd.org/en/books/handbook/jails/), [Rubenerd's post](https://rubenerd.com/starting-with-freebsd-jails/), and [FreeBSD Wiki](https://wiki.freebsd.org/Jails), but I also received some help from different individuals whose names I can't recall.

I use classic jails created from ZFS snapshots, but (as to the best of my knowledge), any jail will work with the following configuration.
Unless specified, all code here goes to `/etc/jails.conf`.

```shell
config1;

jail1 {
    config2;
}

jail2 {
    config3;
}
```

jail1 gets configured with `config1` and `config2`, while jail2 gets `config1` and `config3`.


## Jail configuration {#jail-configuration}

First, we start with standard configuration regarding starting, stopping and logging.
Notice the `#{name}`.
It's a variable which fill be filled with the name of the jail.

```shell
# STARTUP/LOGGING
exec.clean;
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
```

Then we add permissions which will enable `vnet` - the system allowing for jail to have their own, (virtual) network stack.
Even though everything goes through host's network stack, for all intends and purposes we can pretend that each jail has it's own (virtual) NIC.

```shell
# PERMISSIONS
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 5;
vnet;
```

Note, that we need to configure this `devfs_ruleset`. Create `etc/defvs.rules`:

```shell
[devfsrules_jails=5]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'bpf*' unhide
```

back to `jail.conf`, we set hostname and path for the container.
Adjust to your liking.

```shell
host.hostname = "${name}.dune.local";
path = "/usr/local/jails/containers/${name}";
```

Now for the actual network configuration.
We will configure for the jail system to:

-   create an `epair(4)` and use for network communication
-   destroy this `epair` upon stopping a jail

<!--listend-->

```shell
$epair = "epair${id}";
$bridge = "bridge0";
vnet.interface = "${epair}b";
exec.start += "dhclient ${epair}b";
exec.prestart  = "/sbin/ifconfig ${epair} create up";
exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
exec.prestart += "/sbin/ifconfig ${epair}b ether ${mac}";

exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "/sbin/ifconfig ${epair}a destroy";
```

For this to work, we need to create a `if_bridge(4)` on our host machine.
Make sure that your `rc.conf` has:

```shell
cloned_interfaces="bridge0"
ifconfig_bridge0="addm em0 up"
```

(replace `em0` with appropriate device)

Ok, now we just need to have our jail ready.
First, create it as it presented in the [FreeBSD handbook](https://docs.freebsd.org/en/books/handbook/jails/).

Then, configure the jail:

```shell
jail {
    $id=1;
    $mac="2:bf:b9:4c:4f:0b";

    exec.prestart += "mount -a -F /etc/fstab.$name";
    exec.poststop += "umount -a -F /etc/fstab.$name";
}
```

Explanations:

-   `$id` will be used when creating matching `epair`
-   `$mac` will force a given mac address for the virtual network card.
    This will ensure that FreeBSD won't change it, and we can assign fixed `IP` on the router level
-   This jail has attached network storage.
    You don't want the jail itself to even know what it is, so we're forcing the host to execute `/etc/fstab.$name`, and mount the shares.
    Note, that you need to mount the drives in directory relative to **host's** root, so something like:

<!--listend-->

```shell
10.0.1.200:/volume2/movies /usr/local/jails/containers/servarr/mnt/movies nfs rw 0 0
```

(this attaches an NFS share in read-write mode)


## Putting it all together {#putting-it-all-together}

The entire `jail.conf` looks like:

```shell
exec.clean;
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 5;
vnet;
host.hostname = "${name}.dune.local";
path = "/usr/local/jails/containers/${name}";
$epair = "epair${id}";
$bridge = "bridge0";
vnet.interface = "${epair}b";
exec.start += "dhclient ${epair}b";
exec.prestart  = "/sbin/ifconfig ${epair} create up";
exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
exec.prestart += "/sbin/ifconfig ${epair}b ether ${mac}";
exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "/sbin/ifconfig ${epair}a destroy";

jail {
    $id=1;
    $mac="2:bf:b9:4c:4f:0b";

    exec.prestart += "mount -a -F /etc/fstab.$name";
    exec.poststop += "umount -a -F /etc/fstab.$name";
}
```


## Improve me {#improve-me}

Is this perfect?
No!
There are people doing magical things with jails.
If this makes no sense to you, or if you've done it better - make sure to [contact me](/contact).