Mastodon Mastodon Mastodon Eelco's Tech Blog – Using VPN to secure NFS between two hosted servers

Using VPN to secure NFS between two hosted servers

Posted on Sun 11 December 2022 in linux

For a small test setup I wanted to share a disk on a Hetzner VPS with a Hetzner dedicated server. Hetzner has the concept of private networks, but only on their 'Cloud' offering, so that does not extend to dedicated bare metal servers.

Sharing files over NFS seemed like the easiest choice, but sharing NFS data over the public Internet does not seem to be a thing ;-)

At first, I looked at setting up NFS version 4 which supports encryption. Apparently the only way to get encryption native to NFS v4 is setting up Kerberos. Settings up kerberos is entirely possible, but it seemed a bit too much for what I was trying to achieve right now.

The other option I found was using stunnel to wrap NFS traffic in TLS. But just glacing over the guide I found, I figured it was waaaay to involved and complex.

So, I chose the route of setting up a private network between the two machines using Wireguard VPN. Setting up a point to point link with wireguard is relativly straightforward and lightweight. To make it even easier, I created a small ansible role to do the work, so the setup only includes putting in the right YAML data.

Then it was as simple as configuring the nfs service to share a directory over the VPN private IP range.

Sample wireguard playbook setup:


- name: "Ensure the wireguard package is present"
    name: "wireguard"
    state: "present"

- name: "Set up interface configurations"
    src: "wg.conf.j2"
    dest: "/etc/wireguard/{{ }}.conf"
    owner: "root"
    group: "root"
    mode: "0440"
  loop: "{{ wireguard.interfaces }}"

- name: "Enable systemd service for configs"
    name: "wg-quick@{{}}"
    state: "started"
    enabled: true
  loop: "{{ wireguard.interfaces }}"


PrivateKey = {{ item.private_key }}
Address = {{ item.ipv4_address }}
ListenPort = {{ item.port }}

{% for peer in item.peers %}
PublicKey = {{ peer.public_key }}
AllowedIPs = {{ peer.ipv4_address }}
Endpoint = {{ peer.endpoint }}
{% endfor %}

##  Key connection alive ##
PersistentKeepalive = 20

sample yaml data:

    - name: "wg0"
      private_key: !vault |
      ipv4_address: "10.XX.XX.1/24"
      port: "1194"
        - public_key: "PakL8ZoFK1gdQ9tE+1EyfrPLeBcVIwLBQVn8Sazk0gc="
          ipv4_address: "10.XX.XX.6/32"
          endpoint: ""