ProtonVPN Wireguard on NetBSD

Setup instructions for typical single-user NetBSD-10+ systems

Intro:

Unlike on Linux, NetBSD currently uses a completely independent implemenation of the Wireguard protocol and thus it doesn't work quite the same way as the "official" implementations.

Key differences:

Setup:

The following steps are for a typical single-user NetBSD system and assumes the following:

Steps (do the following as root / superuser):

  1. extract the PrivateKey from the ProtonVPN peer config file and write it to the /etc/wg/wg0 file:
    
        $ mkdir /etc/wg
        $ awk '^PrivateKey/{print $3 > "/etc/wg/wg0"}' proton-CN-ST_PN.conf
        $ cat /etc/wg/wg0
        z54***********************zg=
        $ chmod 600 /etc/wg/wg0
    
    
  2. create the /etc/ifconfig.wg0 VPN pseudo-interface config file using the remaining contents of the Proton peer config file proton-CN-ST_PN.conf:
    
        # /etc/ifconfig.wg0
        description 'Wireguard VPN pseudo-device'
        ## proton-CN-ST_PN
        inet 10.2.0.2/32    # Address IPv4
        inet6 fd00:2::2/96  # Address IPv6 (if available)
        !wgconfig $int set private-key /etc/wg/$int
        !wgconfig $int add peer Proton z54**********zg= \  # PublicKey
          --allowed-ips='0.0.0.0/0,::/0' \  # AllowedIPs (comma-separated)
          --endpoint=212.102.44.166:51820   # Endpoint
        down
    
       note: we explicitly set the interface down else it'll come up
             by default; this allows use of wgctl managment script
             for toggling the VPN on or off as needed.
    
    
  3. add "if_wg" module to /etc/modules.conf:
    
        $ echo 'if_wg >> /etc/modules.conf  # creates file if needed
    
       note: to skip reboot after setup run the following:
    
             $ sudo modload if_wg   # successful if no error reported
             $ modstat -n if_wg     # display status of loaded module
             NAME     CLASS    SOURCE   FLAG  REFS    SIZE REQUIRES
             if_wg    driver   filesys  -        0       - sodium,blake2s
    
             $ sudo service network restart   # should setup wg0 interface
             $ ifconfig -ld                   # list configured & down NICs
             wg0
    
    
  4. (optional) using specifics from local system and the ProtonVPN peer config file, edit variabless in the wgctl script as needed and place under ~/bin/:
    
       ex:  $ sed -n '/^# Vars/,/^$/p' ~/bin/wgctl
        # Vars:
        WGint='wg0'              # wg(4) pseudo-NIC
        Ngate='192.168.1.1'      # non-WG gateway
        # proton-CN-ST_PN
        Wserv='212.102.44.166'   # VPN Endpoint (minus port)
        Waddr='0.0.0.0'          # VPN AllowedIPs
        Wgate='10.2.0.2'         # VPN Address (minus subnet)
        WGns4='10.2.0.1'         # VPN DNS
        WGns6='2a07:b944::2:1'   # VPN DNS (IPv6; if avail.)
    
       note: to get your system's non-WG gateway run the following
             *BEFORE* you bring the VPN up:
    
              $ netstat -r |grep default
              default    192.168.1.1   UGS   -  -  -  fxp0
    
             => 192.168.1.1 is your default gateway
    
    

    Lastly, copy script to ~/bin/wgctl and make it executable:

    
        $ [ -d ~/bin ] && mkdir ~/bin  # creates ~/bin if doesn't exist
        $ cp ~/Downloads/wgctl ~/bin/  # example; adjust as needed
        $ chmod 740 ~/bin/wgctl        # only owner can execute or edit
    
    

VPN Managment:

Assuming the if_wg module is loaded, /etc/wg/wg0 is in place, and wg0 is configured via /etc/ifconfig.wg0 Proton's Wireguard VPN is ready to be launched. You may want to reboot the system and check dmesg(8) output for any errors relating to the above setup. If all looks fine the VPN is ready to use, either manually or via the wgctl.sh script.

Manual VPN managment, aka "the hard way":

note: uses example values from Setup section.

Bringing the VPN up:


    $ sudo ifconfig  wg0  up
    $ sudo route add  212.102.44.166  192.168.1.1
    $ sudo route change  default  10.2.0.2
    $ echo 'nameserver  10.2.0.1  2a07:b944::2:1' |sudo resolvconf -x -a wg0 -

A ping-based "PersistentKeepAlive":


    $ ( while [ -z "$(ifconfig -d wg0)" ]
	do
	  ping -o 212.102.44.166 >/dev/null 2>&1
	  sleep 10
	done ;) &

Bringing the VPN down:


    $ sudo ifconfig  wg0  down
    $ sudo route delete  212.102.44.166
    $ sudo route change  default  192.168.1.1
    $ sudo resolvconf -f -d wg0


Managing VPN via wgctl script, aka "the easy way":

The wgctl.sh script basically automates the various steps shown in the Manual VPN management section. Currently only a single Wireguard peer is supported.

The wgctl script has several options; use -h to list:


    $ wgctl -h

    toggle Wireguard interface UP/DOWN or get status

      usage:  wgctl [-h|-u|-d|-t|-s|-p|-i|-n]

        -h  usage
        -u  bring VPN up
        -d  bring VPN down
        -t  test connection
        -s  show status (default)
        -p  show peer info
        -i  show interface info
        -n  show DNS info

Bringing the VPN up:


    $ wgctl -u
    Password: ********
    add host 212.102.44.166: gateway 192.168.1.1
    change net default: gateway 10.2.0.2
    adding nameserver

     => Wireguard interface wg0 is UP.

Use -t to test the VPN connection:


    $ wgctl -t

    nslookup: ProtonVPN
    Server:	10.2.0.1
    Address:	10.2.0.1#53

    Non-authoritative answer:
    Name:	protonvpn.com
    Address: 185.159.159.140

    PING protonvpn.com (185.159.159.140): 56 data bytes
    64 bytes from 185.159.159.140: icmp_seq=0 ttl=41 time=139.170075 ms
    64 bytes from 185.159.159.140: icmp_seq=1 ttl=41 time=140.068808 ms
    64 bytes from 185.159.159.140: icmp_seq=2 ttl=41 time=147.346394 ms

    ----protonvpn.com PING Statistics----
    3 packets transmitted, 3 packets received, 0.0% packet loss
    round-trip min/avg/max/stddev = 139.170075/142.195092/147.346394/4.483733 ms

Displaying the peer, interface and DNS info:


    $ wgctl -p

    interface: wg0
            private-key: (hidden)
            listen-port: (none)
            peer: Proton
                    public-key: z54+LsnV9L6PyS/MO4dPfJ650jiOVLVevYrWf2WsDzg=
                    endpoint: 212.102.44.166:51820
                    preshared-key: (hidden)
                    allowed-ips: 0.0.0.0/0,::/0
                    latest-handshake: Wed May 27 16:45:21 2026

    $ wgctl -i

    wg0: flags=0x8041 mtu 1420
            description: "Wireguard VPN pseudo-device"
            linkstate: up
            status: active
            input: 2914 packets, 902728 bytes
            output: 2792 packets, 828635 bytes
            inet6 fe80::217:31ff:fe24:7ef4%wg0/64 flags 0 scopeid 0x3
            inet6 fd00:2::2/96 flags 0
            inet 10.2.0.2/32 flags 0

    $ wgctl -n

    # resolv.conf from wg0
    nameserver 10.2.0.1 2a07:b944::2:1

Use -d to bring the VPN back down:


    $ wgctl -d
    Password: ********
    delete host 212.102.44.166
    change net default: gateway 192.168.1.1
    removing nameserver(s) 10.2.0.1 2a07:b944::2:1

     => Wireguard interface wg0 is DOWN.


Limitations and other issues:

Limited number of peers supported:

NetBSD's Wireguard implementation is still considered experimental and currently only supports a maximum of 6 peers. While it may be possible to to extend the perceived peer count via dynamic additions and destructions within /etc/wg/, it would likely pose some coding challenges.

Unresponsive ssh(1) sessions:

On some systems ssh sessions can become unresponsive when run through the VPN; if this happens try setting the following in ~/.ssh/config:


    ServerAliveInterval 10  # 0 is default, ie. *no* signal is sent
    ServerAliveCountMax 3   # 3 is default

According to the ssh_config(5) manpage the above values are multiplied to obtain the period of unresponsiveness in before ssh disconnects, ie. 3*10 = 30 seconds. May require experimentation..


Refs: