Skip to main content

Create Network Bridge in FreeBSD for Jails and VMs

How to configure internal network to isolate FreeBSD Jails and Virtual Machines from the public Internet.

If you're the kind that runs their own server on the public Internet, you have to worry about a whole bunch of concerns regarding security of your server and the services running on it. All to ensure your data and compute resources aren't abused. One layer of security that you can use to lock down your server is to run disparate services in separate containers that isolate the services from one another. That way, when any one of the services gets compromised or goes rogue because of a bug - rest of your services and the server itself do not come crashing down, nor is all of your data at risk.

Another layer in securing your services is an internal network. FreeBSD's container technology (Jails) has been available for 20+ years, and the lessons learned since then are baked into the system to make it robust and easy to administer Jails. The OS comes with built-in ways to create private networks within the host and over Virtual Private Networks (VPNs). We'll look at setting up a VPN in another post. I will also cover installing iocage and vm (vm-bhyve) in later posts. This task is the prerequisite for having the base ready to do so.

Today, let's create a bridge where we can attach our Jails and VMs. What we want to achieve is an additional network interface available on our server. This interface is a bridge where iocage can bind the jails. We will use sysrc as described in previous post to set up the bridge and in next post, configure pf - the Packet Firewall to selectively allow traffic in and out to the public Internet.

Let's begin!

At the moment, the /etc/rc.conf looks like this for my test VM:

# System
clear_tmp_enable="YES"
dumpdev="NO"
hostname="freebsd12"

# Services
syslogd_flags="-ss"
sendmail_enable="NONE"
sshd_enable="YES"

# Network
ifconfig_vtnet0_name="public"
ifconfig_public="DHCP"

And running ifconfig shows this output:

[root@freebsd12 ~]# ifconfig
public: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=80028<VLAN_MTU,JUMBO_MTU,LINKSTATE>
        ether 58:9c:fc:08:9f:0a
        inet 10.0.166.170 netmask 0xffff0000 broadcast 10.0.255.255
        media: Ethernet 10Gbase-T <full-duplex>
        status: active
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
        inet 127.0.0.1 netmask 0xff000000
        groups: lo
        nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>

We have renamed the vtnet0 interface to public since it connects to the outside world. The IP address on this VM is within the private network as it is a test VM, but if you're running a VPS, say on on Vultr.com, it will have a public IP.

Now we will create a bridge interface through /etc/rc.conf and name it private. We'll use the sysrc utility to interact with the rc.conf file.

[root@freebsd12 ~]# sysrc cloned_interfaces="bridge0"
cloned_interfaces:  -> bridge0

This does not yet create the bridge, we can use ifconfig to list all the interfaces and confirm that.

[root@freebsd12 ~]# ifconfig -l
public lo0

Next, we give the bridge a custom name private:

[root@freebsd12 ~]# sysrc ifconfig_bridge0_name="private"
ifconfig_bridge0_name:  -> private

And finally, we configure the bridge with a private IPv4 address:

[root@freebsd12 ~]# sysrc ifconfig_private="inet 172.16.0.1 netmask 255.255.0.0"
ifconfig_private:  -> inet 172.16.0.1 netmask 255.255.0.0

These are the lines added to /etc/rc.conf by our commands above:

cloned_interfaces="bridge0"
ifconfig_bridge0_name="private"
ifconfig_private="inet 172.16.0.0 netmask 255.255.0.0"

At this point we can reboot the VM.

Once it comes up, we can verify that we have one additional network interface:

[root@freebsd12 ~]# ifconfig -l
public lo0 private

And we can query the new interface to look at its properties:

[root@freebsd12 ~]# ifconfig private
private: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 02:81:70:cd:50:00
        inet 172.16.0.0 netmask 0xffff0000 broadcast 172.16.255.255
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto stp-rstp maxaddr 2000 timeout 1200
        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
        groups: bridge
        nd6 options=9<PERFORMNUD,IFDISABLED>

We created the private network interface for our Jails and VMs, however at the moment, it cannot pass packets to the outside world, we will look at configuring pf to allow Network Address Translation (NAT) in the next post.