In today’s digital age, many of us have smart devices or applications at home, but how can we access them from anywhere in the world without exposing them to potential threats? The answer is: with a VPN! A home VPN not only provides a secure and encrypted connection, but also allows us to access our home network, files, and devices remotely. In this blog post, I will share my home VPN setup and also explain how to use a DNS server to identify devices and applications using domain names instead of IP addresses.

influxdb telegraf mqtt

For your knowledge

WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN.

BIND 9 is the most commonly used DNS server software on the Internet. Performs both of the main DNS server roles, acting as an authoritative name server for DNS zones and as a recursive resolver in the network.

Project structure

Dockerfile
docker-compose.yml
wg-ui
 ├── wireguard
 │   └── ...
 └── dns-bind9
    ├── named.conf.options
    ├── named.conf.local
    └── db.vpn.vpn

To replicate my setup you can adapt the following docker-compose.
Note: the WireGuard image is not official. It is a image with wireguard and a UI to manage the clients.

docker-compose
docker-compose.yaml
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
version: "3.8"
services:
  wg-ui:
    container_name: wg-ui
    image: weejewel/wg-easy
    environment:
      - WG_HOST=mydomain.com
      - PASSWORD=portal_password
      - WG_DEFAULT_DNS=10.8.1.3
      - WG_DEFAULT_ADDRESS=10.8.0.x
    volumes:
      - ./wg-ui/wireguard:/etc/wireguard
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    networks:
      vpn:
        ipv4_address: 10.8.1.2
  dns-bind9:
    container_name: "dns-bind9"
    image: "dns-bind9"
    build:
      context: "./"
      dockerfile: "Dockerfile"
    volumes:
      - ./wg-ui/dns-bind9/named.conf.options:/etc/bind/named.conf.options
      - ./wg-ui/dns-bind9/named.conf.local:/etc/bind/named.conf.local
      - ./wg-ui/dns-bind9/db.vpn.vpn:/etc/bind/zones/db.vpn.vpn
    restart: unless-stopped
    ports:
      - "53:53"
    networks:
      vpn:
        ipv4_address: 10.8.1.3

networks:
  vpn:
    name: "vpn"
    ipam:
      config:
        - subnet: 10.8.1.0/24

There are some images in docker hub for bind9 but I want to have a clean image to install more tools if needed. So to create this custom image you will need the following DockerFile:

Dockerfile
Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM debian:buster-slim as production

RUN apt-get update &&\
  apt-get install -y \
  iputils-ping \
  traceroute \
  bind9 \
  bind9utils \
  bind9-doc \
  && rm -rf /var/lib/apt/lists/*

# Enable IPv4
RUN sed -i 's/OPTIONS=.*/OPTIONS="-4 -u bind"/' /etc/default/bind9

# Expose Ports
EXPOSE 53/tcp
EXPOSE 53/udp

# Run eternal loop
CMD ["/usr/sbin/named", "-g", "-c", "/etc/bind/named.conf", "-u", "bind"]

Manage WireGuard

As I said in the beginning, the image that I am using for WireGuard is not official. It is an image of a project that combines WireGuard and a UI to manage the clients.
Checkout the features:

  • All-in-one: WireGuard + Web UI.
  • Easy installation, simple to use.
  • List, create, edit, delete, enable & disable clients.
  • Show a client’s QR code.
  • Download a client’s configuration file.
  • Statistics for which clients are connected.
  • Tx/Rx charts for each connected client.
  • Gravatar support.

wireguard UI

With this setup any container in the same vpn network will be accessible by any device connected to the VPN. The devices will get an IP in this 10.8.0.x range and you must set static IPs to other containers in this range 10.8.1.x.

variables meaning
WG_HOST Public hostname of your VPN server.
PASSWORD When set, requires a password when logging in to the Web UI.
WG_DEFAULT_DNS DNS server clients will use.
WG_DEFAULT_ADDRESS Clients IP address range.
NET_ADMIN Check https://man7.org/linux/man-pages/man7/capabilities.7.html
SYS_MODULE Check https://man7.org/linux/man-pages/man7/capabilities.7.html
net.ipv4.ip_forward=1 Sets up port forwarding on the docker network interface
net.ipv4.conf.all.src_valid_mark=1 Sets up a sysconfig entry to allow IP source addresses
ipv4_address Container static IP address

Bind9 configuration

To configure the DNS server you will need to create the following files:
named.conf.options - Config for the cache directory, interface to listen and DNS forwarders (8.8.8.8 Google and 1.1.1.1 Cloudflare).

named.conf.options
1
2
3
4
5
6
7
8
9
10
11
options {
    directory "/var/cache/bind";

    recursion yes;
    listen-on { any; };

    forwarders {
            8.8.8.8;
            1.1.1.1;
    };
};

named.conf.local - Config file to organize multiple zones. In this example I am using only one zone for the domain vpn.vpn

named.conf.local
1
2
3
4
zone "vpn.vpn" {
    type master;
    file "/etc/bind/zones/db.vpn.vpn";
};

db.vpn.vpn - Config of complete zone file for the domain vpn.vpn, which illustrates a number of common features. In this example I am only using A records (IP address of a domain).

db.vpn.vpn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$TTL    604800
@       IN      SOA     vpn.vpn. root.localhost. (
                  1       ; Serial
             604800     ; Refresh
              86400     ; Retry
            2419200     ; Expire
             604800 )   ; Negative Cache TTL
;
; name servers - NS records
     IN      NS      localhost.

; name servers - A records
dashboard.vpn.vpn.     IN      A      10.8.1.6
device1.vpn.vpn.       IN      A      10.8.0.4
device2.vpn.vpn.       IN      A      10.8.0.5

With this config you are able to access your dashboard service via dashboard.vpn.vpn (docker container) or access one device via device1.vpn.vpn (vpn client). One drawback of this setup is that it is necessary to configure each DNS record manually.