FreeBSD and Hetzner Cloud Private Networking

Posted on Nov 28, 2022 | By Andrei Buzoianu | 10 minutes read

In my ever-developing curiosity I’ve recently tried to mix Hetzner private network, Cloud, Dedicated servers and FreeBSD all together. In June 2022, Hetzner announced Flexible Networking for its Cloud offerings which means that a newly created Cloud Server will no longer automatically include public IP addresses (IPv4, IPv6). The total price for cloud servers with public IP addresses will be the same as before, but by removing the Primary IPv4 we have the ability to reduce costs. Servers without public network still need access to package repositories and similar resources, thus let us explore adding FreeBSD in the mix.

Overview

The network topology we want to achieve:

Hetzner Connected Dedicated

Connectivity works by using the existing private network mechanisms for both systems:

  • For cloud servers, we will create a “Network” and add cloud servers to it.
  • For dedicated root servers, we will create a “vSwitch” and attach our dedicated root servers to it.

Networks provide private environments for servers to communicate directly. Every server receives a private IP address that is not on the internet. Instead, dedicated network interfaces are used to provide private layer 3 (OSI model) links between Hetzner Cloud servers. The vSwitch feature is a tool for our dedicated root servers that lets us connect servers in multiple locations to each other using virtual layer 2 networks. On a Hetzner account, on the Robot administration interface, we can create and configure vSwitches using the “vSwitches” button in the server overview:

Hetzner vSwitch

Using hcloud the command-line interface for Hetzner Cloud

hcloud is a command-line interface for interacting with Hetzner Cloud.

Installation

You can download pre-built binaries for Linux, FreeBSD, macOS, and Windows on the releases page.

$ wget -q https://github.com/hetznercloud/cli/releases/download/v1.30.3/hcloud-linux-amd64.tar.gz
$ tar -zxvf hcloud-linux-amd64.tar.gz
LICENSE
README.md
hcloud

Move the binary somewhere in your $PATH:

$ mv hcloud .local/bin/

Configuration

To configure hcloud we need an API token. Create an API token, by signing in into the Hetzner Cloud Console choose a Project, then go to Security -> API Tokens, and generate a new token. Make sure to copy the token because it won’t be shown to you again. A token is bound to a Project, to interact with the API of another Project you have to create a new token inside the Project

$ hcloud context create mytest
Token: 
Context mytest created and activated

List our context:

$ hcloud context list
ACTIVE   NAME
*        mytest

You can also use the following environment variables to configure hcloud:

  • HCLOUD_TOKEN: Instead of creating a context, you can set the token via the HCLOUD_TOKEN environment variable.
  • HCLOUD_CONTEXT: When using hcloud in scripts, we can configure a per-directory context by setting HCLOUD_CONTEXT=my-context

Adding SSH keys

To create a SSH key for our gateway server:

$ hcloud ssh-key create --name andreibuzoianu --public-key-from-file .ssh/id_ed25519
$ hcloud ssh-key list
ID        NAME             FINGERPRINT
8520345   andreibuzoianu   9e:1d:08:cf:41:e6:e5:00:60:4b:a3:20:ab:b1:79:ec

Hetzner Cloud Network

$ hcloud network create --name mytest --ip-range 172.16.0.0/23
Network 2146252 created
$ hcloud network add-subnet mytest --type cloud --network-zone eu-central --ip-range 172.16.0.0/24
700ms [==================================] 100.00%
Subnet added to network 2146252
$ hcloud network add-subnet mytest --type vswitch --network-zone eu-central --ip-range 172.16.1.0/24 --vswitch-id 44385
600ms [==================================] 100.00%
Subnet added to network 2146252

Add a default gateway (IP of the FreeBSD server) for our network:

$ hcloud  network add-route mytest --destination 0.0.0.0/0 --gateway 172.16.0.10
1.8s [===================================] 100.00%
Route added to network 2146252

Same can be achived using the Hetzner Cloud Console:

Hetzner Network Route

In Hetzner Cloud Console, we can now see the route:

Hetzner Network Show Routes

To list our newly created network:

$ hcloud  network list
ID        NAME       IP RANGE        SERVERS
2146252   mytest     172.16.0.0/23   0 server

Or in Hetzner Cloud Console:

Hetzner Network

To get all the information related to our network:

$ hcloud  network describe mytest 
ID:		2146252
Name:		mytest
Created:	Fri Nov 25 22:27:43 EEST 2022 (27 minutes ago)
IP Range:	172.16.0.0/23
Subnets:
  - Type:		cloud
    Network Zone:	eu-central
    IP Range:		172.16.0.0/24
    Gateway:		172.16.0.1
  - Type:		vswitch
    Network Zone:	eu-central
    IP Range:		172.16.1.0/24
    Gateway:		172.16.0.1
    vSwitch ID:		44385
Routes:
  - Destination:	0.0.0.0/0
    Gateway:		172.16.0.10
Protection:
  Delete:	no
Labels:
  No labels

Creating and installing the FreeBSD gateway

To create a new server we can use hcloud:

$ hcloud server create --name gateway --image debian-9 --type cx11 --ssh-key andreibuzoianu
   6s [====================================================================] 100%
Server 325211 created

Hetzner recommends using their Image functionality to install virtual servers, but they also provide some stock ISOs so we can install more exotic operating systems by ourself. When creating the server we did use debian-9 image since we are required to provide an image. Next we’ll attach an FreeBSD ISO to the server.

Currently listed ISOs for FreeBSD:

$ hcloud iso list | grep FreeBSD
ID      NAME                                     DESCRIPTION                     TYPE
7300    FreeBSD-13.0-RELEASE-amd64-bootonly.iso  FreeBSD 13.0 (amd64/netinstall) public
14376   FreeBSD-13.1-RELEASE-amd64-bootonly.iso  FreeBSD 13.1 (amd64/netinstall) public

To attach an FreeBSD ISO to our Server use:

$ hcloud server attach-iso gateway FreeBSD-13.1-RELEASE-amd64-bootonly.iso

Select Power from the right menu and then Power-Cycle to restart the Server and boot from the FreeBSD ISO:

Power Cycle

Select Console from top right:

Console (VNC console)

Then install FreeBSD using the text-based installation program named bsdinstall. Use DHCP for IPv4 configuration which will automatically configure the network interface correctly for Hetzner.

FreeBSD gateway configuration

In FreeBSD, the rc.conf file contains system configuration information, such as descriptive information about the local host name, configuration details for any potential network interfaces and which services should be started up at system initial boot time.

Excerpt from rc.conf to show the network configuration:

ifconfig_vtnet0="DHCP"

ifconfig_vtnet1="inet 172.16.0.10 netmask 255.255.255.255 broadcast 172.16.0.255 descr LAN"

static_routes="cloudgateway cloudnet dedicated"
route_cloudgateway="-host 172.16.0.1 -interface vtnet1"
route_cloudnet="-net 172.16.0.0/24 172.16.0.1"
route_dedicated="-net 172.16.1.0/24 172.16.0.1"

Ensure IP forwarding is enabled:

$ sysctl -a|grep ip.forwarding
net.inet.ip.forwarding: 1

To make it persistent:

$ cat /etc/rc.conf.d/routing 
gateway_enable="YES"

Configuration of Hetzner Cloud Servers and Dedicated Servers

When creating cloud servers disable the public network, in Hetzner Cloud Console, for that particular server:

Disable Public Network

w1 routing table:

[w1 ~]$ ip r l
default via 172.16.0.1 dev enp7s0 proto static metric 100 
172.16.0.1 dev enp7s0 proto static scope link metric 100 

w1 network interface:

[w1 ~]$ ip a l enp7s0
2: enp7s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UP group default qlen 1000
    link/ether 86:00:00:26:b4:07 brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.3/32 scope global noprefixroute enp7s0
       valid_lft forever preferred_lft forever

The dedicated server is also a FreeBSD server. Excerpt from rc.conf:

hostname="dedicated"

vlans_igb0="cloud"
create_args_cloud="vlan 4000"
ifconfig_cloud="inet 172.16.1.2 netmask 255.255.255.0 mtu 1400"

static_routes="cloud"
route_cloud="-net 172.16.0.0/24 172.16.1.1"

The virtual interface as configured:

dedicated ~$ ifconfig cloud
cloud: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1400
	options=4600703<RXCSUM,TXCSUM,TSO4,TSO6,LRO,RXCSUM_IPV6,TXCSUM_IPV6,NOMAP>
	ether a8:a1:59:82:3c:d4
	inet 172.16.1.2 netmask 0xffffff00 broadcast 172.16.1.255
	groups: vlan
	vlan: 4000 vlanproto: 802.1q vlanpcp: 0 parent interface: igb0
	media: Ethernet autoselect (1000baseT <full-duplex>)
	status: active
	nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

The dedicated server routing table related to Hetzner Networks:

dedicated ~$ netstat -rn|grep 172.16
172.16.0.0/24        172.16.1.1         UGS       cloud
172.16.1.0/24        link#5             U         cloud
172.16.1.2           link#5             UHS         lo0

Testing our setup

gateway ~ $ ping -c 1 172.16.0.1
PING 172.16.0.1 (172.16.0.1): 56 data bytes
64 bytes from 172.16.0.1: icmp_seq=0 ttl=255 time=0.482 ms

--- 172.16.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.482/0.482/0.482/0.000 ms

gateway ~ $ ping -c 1 172.16.0.3
PING 172.16.0.3 (172.16.0.3): 56 data bytes
64 bytes from 172.16.0.3: icmp_seq=0 ttl=63 time=1.116 ms

--- 172.16.0.3 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.116/1.116/1.116/0.000 ms

gateway ~ $ ping -c 1 172.16.1.2
PING 172.16.1.2 (172.16.1.2): 56 data bytes
64 bytes from 172.16.1.2: icmp_seq=0 ttl=63 time=1.638 ms

--- 172.16.1.2 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.638/1.638/1.638/0.000 ms

gateway ~ $ mtr -r -c 1 172.16.1.2
Start: 2022-11-25T22:46:29+0300
HOST: gateway                Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.0.1            0.0%     1    5.7   5.7   5.7   5.7   0.0
  2.|-- 169.254.255.255       0.0%     1    1.2   1.2   1.2   1.2   0.0
  3.|-- 172.16.1.2            0.0%     1    1.1   1.1   1.1   1.1   0.0

gateway ~ $ mtr -r -c 1 172.16.0.3
Start: 2022-11-25T22:47:06+0300
HOST: gateway                Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.0.1            0.0%     1    5.0   5.0   5.0   5.0   0.0
  2.|-- 172.16.0.3            0.0%     1    1.4   1.4   1.4   1.4   0.0

dedicated ~$ ping -c 1 172.16.0.10
PING 172.16.0.10 (172.16.0.10): 56 data bytes
64 bytes from 172.16.0.10: icmp_seq=0 ttl=62 time=1.680 ms

--- 172.16.0.10 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.680/1.680/1.680/0.000 ms

dedicated ~$ mtr -n -r -c 1 172.16.0.10
Start: 2022-11-26T09:22:55+0000
HOST: nasogor                     Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.1.1                 0.0%     1    1.2   1.2   1.2   1.2   0.0
  2.|-- 172.16.0.10                0.0%     1    1.2   1.2   1.2   1.2   0.0

dedicated ~$ mtr -n -r -c 1 172.16.0.3
Start: 2022-11-26T09:25:35+0000
HOST: nasogor                     Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.1.1                 0.0%     1    0.6   0.6   0.6   0.6   0.0
  2.|-- 172.16.0.3                 0.0%     1    1.0   1.0   1.0   1.0   0.0

[w1 ~]$ mtr -n -r -c 1 172.16.1.2
Start: 2022-11-25T19:50:24+0000
HOST: w1                          Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.0.1                 0.0%     1    3.4   3.4   3.4   3.4   0.0
  2.|-- 169.254.255.255            0.0%     1   13.3  13.3  13.3  13.3   0.0
  3.|-- 172.16.1.2                 0.0%     1    1.1   1.1   1.1   1.1   0.0

[w1 ~]$ mtr -n -r -c 1 1.1.1.1
Start: 2022-11-26T06:07:44+0000
HOST: w1                          Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 172.16.0.1                 0.0%     1    3.4   3.4   3.4   3.4   0.0
  2.|-- 172.16.0.10                0.0%     1    1.2   1.2   1.2   1.2   0.0
  3.|-- 172.31.1.1                 0.0%     1    5.2   5.2   5.2   5.2   0.0
  4.|-- 159.69.96.44               0.0%     1    1.6   1.6   1.6   1.6   0.0
  5.|-- ???                       100.0     1    0.0   0.0   0.0   0.0   0.0
  6.|-- 213.239.225.45             0.0%     1   37.9  37.9  37.9  37.9   0.0
  7.|-- 213.239.239.137            0.0%     1   16.2  16.2  16.2  16.2   0.0
  8.|-- 213.239.229.73             0.0%     1    6.7   6.7   6.7   6.7   0.0
  9.|-- 213.239.224.178            0.0%     1    6.8   6.8   6.8   6.8   0.0
 10.|-- 162.158.84.254             0.0%     1    7.3   7.3   7.3   7.3   0.0
 11.|-- 172.70.240.3               0.0%     1   37.4  37.4  37.4  37.4   0.0
 12.|-- 1.1.1.1                    0.0%     1    7.0   7.0   7.0   7.0   0.0

Final thoughts

I’ve had a lot of fun leveraging Hetzner Networks and FreeBSD on virtual instances. Networks allows servers to communicate through a private network and setup complex network topologies, while FreeBSD added a nice touch to it.

References