Tag Archives: VPN

OpenWRT Wireguard client

My notes on how to configure an OpenWRT device to be a wireguard client (site to site VPN)

More or less follow the instructions from https://openwrt.org/docs/guide-user/services/vpn/wireguard/client

I commented out the IPv6 stuff as well as the pre-shared key. I also had already defined firewall rules so I skipped that section.

One note: make sure your WG_ADDR has the proper subnet mask (I made the mistake of making it a /32 when it needed to be a /24)


# Configuration parameters
WG_IF="wg0"
WG_SERV="remote_wireguard_server_address"
WG_PORT="remote_wireguard_port"
WG_ADDR="wireguard_subnet/wireguard_subnet_mask (/24 for example)"
#WG_ADDR6="fdf1:e8a1:8d3f:9::2/64"

# Generate keys
#umask go=
#wg genkey | tee wgserver.key | wg pubkey > wgserver.pub
#wg genkey | tee wgclient.key | wg pubkey > wgclient.pub
#wg genpsk > wgclient.psk
 
# Client private key
WG_KEY="$(cat wgclient.key)"
 
# Pre-shared key
#WG_PSK="$(cat wgclient.psk)"
 
# Server public key
WG_PUB="public_key_of_wireguard_server"

# Configure network
uci -q delete network.${WG_IF}
uci set network.${WG_IF}="interface"
uci set network.${WG_IF}.proto="wireguard"
uci set network.${WG_IF}.private_key="${WG_KEY}"
uci add_list network.${WG_IF}.addresses="${WG_ADDR}"
#uci add_list network.${WG_IF}.addresses="${WG_ADDR6}"
 
# Add VPN peers
uci -q delete network.wgserver
uci set network.wgserver="wireguard_${WG_IF}"
uci set network.wgserver.public_key="${WG_PUB}"
#uci set network.wgserver.preshared_key="${WG_PSK}"
uci set network.wgserver.endpoint_host="${WG_SERV}"
uci set network.wgserver.endpoint_port="${WG_PORT}"
uci set network.wgserver.route_allowed_ips="1"
uci set network.wgserver.persistent_keepalive="25"
uci add_list network.wgserver.allowed_ips="10.137.50.0/24"
#uci add_list network.wgserver.allowed_ips="::/0"
uci commit network
/etc/init.d/network restart

Wireguard on a USG Pro 4

I ran into some issues when trying to configure an OpenVPN tunnel between my Ubiquiti USG Pro 4 and a Debian VPS. I was very disappointed to discover that the version of OpenVPN on the USG only supports TLS 1.0! My issue was the Debian side rejecting that as insecure.

Thankfully, it was fairly painless to get Wireguard configured on the USG Pro 4. I was hesitant to do so at first because I knew every time my USG was updated I would lose the wireguard pacakge. Fortunately that can be resolved by configuring a post-install script. Thanks to ilar.in and calypte.cc and this github gist for the steps on how to do so.

curl -O https://raw.githubusercontent.com/britannic/install-edgeos-packages/master/install-pkgs
sudo install -o root -g root -m 0755 install-pkgs /config/scripts/post-config.d/install-pkgs
  • Add wireguard DEB package to persistent storage that the script will look for:
sudo mkdir -p /config/data/install-packages
cd /config/data/install-packages
curl -fLSs https://github.com/WireGuard/wireguard-vyatta-ubnt/releases/download/1.0.20200729-1/ugw4-v1-v1.0.20200729-v1.0.20200513.deb
  • Generate a public/private keypair for USG use
cd /config/auth
umask 077
wg genkey > wg_private.key
wg pubkey < wg_private.key > wg_public.key
  • Generate config.gateway.json config to use wireguard
    "interfaces": {
 ...
        "wireguard": {
            "wg0": {
                "address": "<IP_OF_USG_ON_WG_CLIENT_SUBNET>",
                "listen-port": "<WG_LISTEN_PORT>",
                "peer": {
                    "<ENDPOINT_CLIENT_PUBLIC_KEY>": {
                        "allowed-ips": "0.0.0.0/0",
                        "endpoint":  "<ENDPOINT_CLIENT_ADDRESS>:<ENDPOINT_CLIENT_PORT>
"
                    }
                },
                "private-key": "/config/auth/priv.key",
                "route-allowed-ips": false
            },
            "wg1": {
                "address": "<IP_OF_USG_ON_WG_SERVER_SUBNET",
                "firewall": {
                    "in": {
                        "name": "LAN_IN"
                    },
                    "local": {
                        "name": "LAN_LOCAL"
                    },
                    "out": {
                        "name": "LAN_OUT"
                    }
                },
                "listen-port": "<USG_WG_SERVER_LISTEN_PORT>",
                "mtu": "1352",
                "peer": {
                    "<PUBLIC_KEY_OF_WG_CONNECTING_CLIENT": {
                        "allowed-ips": "<SUBNETS_ON_REMOTE_HOST>"
                    }
                },
                "private-key": "/config/auth/wg-server.priv",
                "route-allowed-ips": true
            }
        }
    },

I have two different wireguard interfaces configured – wg0 to be a client to another server, and wg1 to be a server accepting other clients (site-to-site VPN.)

If you want to have multiple peers defined on a single wireguard interface, encapsulate the peers with brackets like so:

"peer": [{
						"--pubkey--": {
							"allowed-ips": [
								"172.255.252.2/32"
							],
							"persistent-keepalive": 60
						}
					},
					{
						"--pubkey--": {
							"allowed-ips": [
								"172.255.252.3/32"
							],
							"persistent-keepalive": 60
						}
					}
				],

Test configuration first

Before committing your config.gateway.json code, test it line by line by SSHing into the USG-Pro 4 and entering config mode. Then type out your JSON lines one at a time, with each key being a new argument separated by a space. The first section above would look like this:

configure
edit interfaces
set wireguard wg0 address WIREGUARD_ADDRESS
set wireguard wg0 listen-port WG_LISTEN_PORT
set wireguard wg0 peer ENDPOINT_CLIENT_PUBLIC_KEY allowed-ips 0.0.0.0/0
set wireguard wg0 peer ENDPOINT_CLIENT_PUBLIC_KEY endpoint ENDPOINT_ADDRESS:ENDPOINT_PORT
set wireguard wg0 private-key /config/auth/priv.key
set wireguard wg0 route-allowed-ips false
commit

If the commit works without error, you can then drop out of the configure section and look at your wireguard config:

exit
sudo wg show

If all looks well, then copy your config.gateway.json to your controller and trigger a reprovision.

Verify after provisioning: 

sudo netstat -npl | grep <WIREGUARD_PORT> | grep udp

Troubleshooting

USG not connecting to changed endpoint address

If you change the address of the wireguard endpoint, USG pro will not connect to that new address. You have to delete and re-create the interface (thanks to https://github.com/Lochnair/vyatta-wireguard/issues/72#issuecomment-423840448 for the information)

Fix this by deleting the wireguard interface

admin@Firewall:~$ configure
[edit]
admin@Firewall# delete interfaces wireguard
[edit]
admin@Firewall# commit


Then reprovision by making a small change, force provision, then change back, and force another provision (annoying) or alternatively reboot firewall.

Wireguard shows established but ping doesn’t work

Example error:

From 10.99.13.1 icmp_seq=5 Destination Host Unreachable
ping: sendmsg: Required key not available

To figure out what’s going on, enable logging to kernel buffer (dmesg) Thanks to procustodibus.com for the info.

echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

With debug on, ping again and check kernel messages (dmesg)

[Tue Dec 21 22:16:12 2021] wireguard: wg0: No peer has allowed IPs matching 10.99.13.2

This showed I didn’t have my access control properly configured. Modify /etc/wireguard/wg0.conf on your client config and make sure your AllowedIPs are properly letting traffic through.

AllowedIPs = 10.99.13.0/24

USG not allowing connections

Clients unable to connect to USG despite having a good config. Double check your firewall rules. I had neglected to create a WAN LOCAL rule allowing UDP packets on my wireguard port. Once that was configured, handshakes completed successfully.

Firewalls can’t ping each other

I had an issue where the firewalls would pass traffic through, but they couldn’t ping each other. The solution was to add the VPN subnet you created to allowed-ips on both sides of the connection.

Site to site Wireguard VPN between OPNSense & Debian Linux server

I have a Debian linode box acting as a wireguard server. I wanted to join my opnsense firewall to it to allow devices behind it to access the box through the wireguard tunnel. It was not as straightforward as I had hoped, but thankfully I got it all working.

OPNSense side

Documentation link

Install wireguard via GUI

Install the os-wireguard package. Manually drop to the CLI and install the wireguard package as well:
sudo pkg install wireguard

Configure Local instance

  • Name and listen port can be random. Tunnel address is the subnet you wish to expose to the other end (the subnet you wish to have access to the tunnel.)
  • Leave everything else blank and hit save
  • Edit your new connection and copy the Public key, this will need to be sent to the Debian server

Configure Endpoint

  • Name: hostname of Debian server
  • Public Key: Public key of server (can be obtained by running wg show on the server)
  • Shared Secret: blank (unless you’ve configured it on the server)
  • Allowed IPs: IPs or subnets on the Debian server you wish to expose to the client side (the OPNSense box)
  • Endpoint address: DNS name of Debian server
  • Endpoint port: Port Debian wireguard instance is listening on

Enable the VPN

General tab / Enable wireguard checkbox and hit apply.

Debian side

Take down the tunnel

sudo wg-quick down wg0

Edit wireguard config to add peer

sudo vim /etc/wireguard/wg0.conf

[Peer]
PublicKey = <PUBLIC_KEY_YOU_COPIED_IN_LOCAL_INSTANCE_STEP>
AllowedIPs = <IPs or Subnets behind the OPNSense side you wish to be exposed to the Debian side> 

Restart wireguard

sudo wg-quick up wg0

Check connections

Example wg show output below with dummy IPs:

sudo wg show
interface: wg0
  public key: f+/J4JO0aL6kwOaudAvZVa1H2mDzR8Nh3Vfeqq+anF8=
  private key: (hidden)
  listening port: 12345

peer: TuUW7diXcWlaV97z3cQ1/92Zal2Pm9Qz/W2OMN+v20g=
  endpoint: 10.0.0.1:54137
  allowed ips: 10.0.0.2/32
  latest handshake: 17 seconds ago
  transfer: 5.14 KiB received, 3.81 KiB sent

peer: CZuC/+wxvzj9+TiGeyZtcT/lMGZnXsfSs/h5Jtw2VSE=
  endpoint: 8.8.8.8:12345
  allowed ips: 192.168.1.1/32
  latest handshake: 7 minutes, 8 seconds ago
  transfer: 5.89 MiB received, 952.20 MiB sent

The endpoint: line gets populated when a successful VPN connection is made. If it’s missing, the tunnel was not established.

Troubleshooting

OPNSense box

Nothing happens after saving information and enabling tunnel

Make sure latest wireguard package is installed

sudo pkg install wireguard

Get more log output by opening a shell on your OPNSense box and running

sudo /usr/local/etc/rc.d/wireguard start

In my case I was getting this interesting message

[!] Missing WireGuard kernel support (ifconfig: SIOCIFCREATE2: Invalid argument). Falling back to slow userspace implementation.
[#] wireguard-go wg0
┌──────────────────────────────────────────────────────┐
│                                                                                                                                                   │
│   Running wireguard-go is not required because this                                                     │
│   kernel has first class support for WireGuard. For                                                          │
│   information on installing the kernel module,                                                                 │
│   please visit                                                                                                                             │
│         https://www.wireguard.com/install/                                                                           │
└──────────────────────────────────────────────────────┘

I fixed this problem by manually installing wireguard with the pkg install command above.

Debian box

Wireguard config not saving

make sure to stop the tunnel first, otherwise your changes get overwritten by the daemon.

sudo wg-quick down wg0
<make changes>
sudo wg-quick up wg0

Connect Ubiquiti l2tp vpn with NetworkManager in Arch

I’ve recently moved and needed to connect to my (still existing) home network from my desktop. I’ve never had to VPN from my desktop before, so here my notes for getting it working.

Configuration

  1. Install necessary lt2p, pptp, and libreswan packages (I’m using yay as my package manager)
    yay -Sy community/networkmanager-l2tp community/networkmanager-pptp aur/networkmanager-libreswan aur/libreswan
  2. Configure VPN in GNOME settings (close settings window first if it was already open)
    1. Add VPN / Layer 2 Tunneling Protocol (L2TP)
    2. Gateway: IP/DNS of VPN
    3. User Authentication: Type: password
    4. IPsec Settings: Type: Pre-shared Key (PSK)
    5. PPP settings: Only check MSCHAPv2, check everything else. MPPE Security: 128-bit (most secure)

Troubleshooting

If something isn’t working the popup is not very descriptive. Network manager logs are stored in journald, so the best way to troubleshoot is to follow the logs: (-f for follow, -u for unit name)

sudo journalctl -f -u NetworkManager

In my case following the networkmanager logs I could see I didn’t have libreswan fully installed, and installing the libreswan package fixed it.

Add static route in CentOS7

I recently began a project of segmenting my LAN into various VLANs. One issue that cropped up had me banging my head against the wall for days. I had a particular VM that would use OpenVPN to a private VPN provider. I had that same system sending things to a file share via transmission-daemon.

Pre-subnet move everything worked, but once I moved my file server to a different subnet suddenly this VM could not access it while on the VPN. Transmission would hang for some time before finally saying

transmission-daemon.service: Failed with result 'timeout'.

The problem was since my file server was on a different subnet, it was trying to route traffic to it via the default gateway, which in this case was the VPN provider. I had to add a specific route to tell the server to use my LAN network instead of the VPN network in order to restore connectivity to the file server (thanks to this site for the primer.)

I had to create a file /etc/sysconfig/network-scripts/route-eth0 and give it the following line:

192.168.2.0/24 via 192.168.1.1 dev eth0

This instructed my VM to get to the 192.168.2 network via the 192.168.1.1 gateway on eth0. Restart the network service (or reboot) and success!

OPENVPN site to site vpn between USG and openwrt

A new firewall means a new site to site VPN configuration. My current iteration of this is a USG Pro 4 serving as an OpenVPN server and a Netgear Nighthawk R8000 serving as a VPN client joining their two networks together.

First, I had to wrap my head around some concepts. To set this up you need three sets of certificates and a DH file:

  • CA: To generate and validate certificates
  • Server: To encrypt/decrypt traffic for the Server
  • Client: To encrypt/decrypt traffic from the Client
  • DH: Not a certificate but still needed by the server for encryption

The server and client will also need openvpn configurations containing matching encryption/hashing methods, CA public key, and protocol/port settings.

Generate certificates

If you already have PKI infrastructure in place you simply need to generate two sets of keys and a DH file for the server/client to use. If you don’t, the easy-rsa project comes to the rescue. This tutorial uses easy-rsa version 3.

I didn’t want to generate the certificates on my firewall so I picked a Debian system to do the certificate generation. First, install easy-rsa:

sudo apt install easy-rsa

In Debian easy-rsa is installed to /usr/share/easy-rsa/

Optional: Set desired variables by moving /usr/share/easy-rsa/vars.example to /usr/share/easy-rsa/vars and un-commenting / editing to suit your needs (in my case I like to extend the life of my certificates beyond two years.)

Next, create your PKI and generate CA certificates:

/usr/share/easy-rsa/easyrsa init-pki
/usr/share/easy-rsa/easyrsa build-ca

Now create your DH file. Grab a cup of coffee for this one, it can take up to ten minutes to complete:

/usr/share/easy-rsa/easyrsa gen-dh

Then create your server & client certificates. For this guide we are calling the server ovpn-server and the client ovpn-client

#For the server
/usr/share/easy-rsa/easyrsa gen-req ovpn-server nopass 
/usr/share/easy-rsa/easyrsa sign-req server ovpn-server

#For the client
/usr/share/easy-rsa/easyrsa gen-req ovpn-client nopass
/usr/share/easy-rsa/easyrsa sign-req client ovpn-client

You will be asked for a common name. Remember what you put here, you will need it later. If you just hit enter and accept the default the common name will match what was passed in the above commands (ovpn-server for the server certificate and ovpn-client for the client certificate.)

Lastly, copy these files to their respective hosts:

USG Server: CA, Server key & cert, and DH file. (substitute with IP of your device)

scp pki/dh.pem pki/ca.crt pki/private/ovpn-server.key  pki/issued/ovpn-server.crt admin@IP_OF_YOUR_USG:/config/auth/

OpenWRT Client: Client key & cert, and CA cert:

scp pki/private/ovpn-client.key pki/issued/ovpn-client.crt pki/ca.crt root@IP_OF_YOUR_OPENWRT:/etc/config/

USG: VPN Server

Documentation for the EdgeRouter is much easier to find than for the USG. Since they use the same operating system I based this off of this guide from Logan Marchione for the EdgeRouter. SSH into your USG and issue the following, substituting the $variables with the values you desire for your network.

Explanation of variables:

VPN_SUBNET: Used for VPN communication. Must be different from both server and client subnets.
SERVER_SUBNET: Subnet on server side you wish to pass to client network
VPN_PORT: Change this to desired listening port for the OpenVPN server
REMOTE_SUBNET: Subnet on client side you wish to pass to server network
REMOTE_NETMASK: Netmask of client subnet
REMOTE_VPN_IP: Static IP you wish to give the client on the VPN subnet.
REMOTE_CERT_NAME: Common name given to client certificate generated previously.

Replace $variables below before pasting into USG terminal:

configure
#OpenVPN config
set interfaces openvpn vtun0
set interfaces openvpn vtun0 description "OpenVPN Site to Site"
set interfaces openvpn vtun0 mode server
set interfaces openvpn vtun0 encryption aes256
set interfaces openvpn vtun0 hash sha256
set interfaces openvpn vtun0 server subnet $VPN_SUBNET
set interfaces openvpn vtun0 server push-route $SERVER_SUBNET
set interfaces openvpn vtun0 tls ca-cert-file /config/auth/ca.crt
set interfaces openvpn vtun0 tls cert-file /config/auth/ovpn-client.crt
set interfaces openvpn vtun0 tls key-file /config/auth/ovpn-client.key
set interfaces openvpn vtun0 tls dh-file /config/auth/dh.pem
set interfaces openvpn vtun0 openvpn-option "--port $VPN_PORT"
set interfaces openvpn vtun0 openvpn-option --tls-server
set interfaces openvpn vtun0 openvpn-option "--comp-lzo yes"
set interfaces openvpn vtun0 openvpn-option --persist-key
set interfaces openvpn vtun0 openvpn-option --persist-tun
set interfaces openvpn vtun0 openvpn-option "--keepalive 10 120"
set interfaces openvpn vtun0 openvpn-option "--user nobody"
set interfaces openvpn vtun0 openvpn-option "--group nogroup"
set interfaces openvpn vtun0 openvpn-option "--route $REMOTE_SUBNET $REMOTE_NETMASK $REMOTE_VPN_IP"
set interfaces openvpn vtun0 server client $REMOTE_CERT_NAME ip $REMOTE_VPN_IP
set interfaces openvpn vtun0 server client $REMOTE_CERT_NAME subnet $REMOTE_SUBNET $REMOTE_NETMASK

#Firewall config
set firewall name WAN_LOCAL rule 50 action accept
set firewall name WAN_LOCAL rule 50 description "OpenVPN Site to Site"
set firewall name WAN_LOCAL rule 50 destination port $VPN_PORT
set firewall name WAN_LOCAL rule 50 log enable
set firewall name WAN_LOCAL rule 50 protocol udp
commit

If the code above commits successfully, the next step is to add the config to config.gateway.json. The USG’s config is managed by its Unifi controller, so for any of the changes made above to stick we must copy them to /usr/lib/unifi/data/sites/default/config.gateway.json on the controller (create the file if it doesn’t already exist.)

A quick shortcut is to run the mca-ctrl -t dump-cfg command, then parse out the parts you want to go into config.gateway.json as outlined in the UniFi documentation. For the lazy, here is the config.gateway.json generated from the above commands (be sure to modify $variables to suit your needs.)

{
  "firewall": {
    "WAN_LOCAL": {
      "rule": {
        "50": {
          "action": "accept",
          "description": "OpenVPN Site to Site",
          "destination": {
            "port": "$VPN_PORT"
          },
          "log": "enable",
          "protocol": "udp"
        }
      }
    }
  },
  "interfaces": {
    "openvpn": {
      "vtun0": {
        "description": "OpenVPN Site to Site",
        "encryption": "aes256",
        "hash": "sha256",
        "mode": "server",
        "openvpn-option": [
          "--port $VPN_PORT",
          "--tls-server",
          "--comp-lzo yes",
          "--persist-key",
          "--persist-tun",
          "--keepalive 10 120",
          "--user nobody",
          "--group nogroup",
          "--route $REMOTE_SUBNET $REMOTE_NETMASK $REMOTE_VPN_IP"
        ],
        "server": {
          "client": {
            "$REMOTE_CERT_NAME": {
              "ip": "$REMOTE_VPN_IP",
              "subnet": [
                "$REMOTE_SUBNET $REMOTE_NETMASK"
              ]
            }
          },
          "push-route": [
            "$SERVER_SUBNET"
          ],
          "subnet": "$VPN_SUBNET"
        },
        "tls": {
          "ca-cert-file": "/config/auth/ca.crt",
          "cert-file": "/config/auth/ovpn-client.crt",
          "dh-file": "/config/auth/dh.pem",
          "key-file": "/config/auth/ovpn-client.key"
        }
      }
    }
  }
}

OpenWRT: VPN client

Configuration is doable from the GUI but I found much easier with the command line. I got a lot of the configuration from this gist from braian87b

Install openvpn and the luci-app-openvpn packages:

opkg update
opkg install openvpn luci-app-openvpn

OpenVPN config files are located in /etc/config. In addition to the certificates we copied there earlier, we will also want to copy the openvpn client configuration to that directory.

Here is the config file matching the configuration generated above. Again, remember to replace $variables with your config matching what was generated above. Save it to /etc/config/site2site.conf

#/etc/config/site2site.conf
client
dev tun
proto udp
remote $DNS_OR_IP_OF_USG_OPENVPN_SERVER $VPN_PORT
cipher AES-256-CBC
auth SHA256
resolv-retry infinite
nobind
comp-lzo yes
persist-key
persist-tun
verb 3
ca /etc/config/ca.crt
cert /etc/config/ovpn-client.crt
key /etc/config/ovpn-client.key
remote-cert-tls server

With the openvpn config file, client certificate & key, and CA certificate we are ready to configure firewall rules and instruct the router to initiate the VPN connection.

# a new OpenVPN instance:
uci set openvpn.site2site=openvpn
uci set openvpn.site2site.enabled='1'
uci set openvpn.site2site.config='/etc/config/site2site.conf'

# a new network interface for tun:
uci set network.site2sitevpn=interface
uci set network.site2sitevpn.proto='none' #dhcp #none
uci set network.site2sitevpn.ifname='tun0'

# a new firewall zone (for VPN):
uci add firewall zone
uci set firewall.@zone[-1].name='vpn'
uci set firewall.@zone[-1].input='ACCEPT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].forward='ACCEPT'
uci set firewall.@zone[-1].masq='1'
uci set firewall.@zone[-1].mtu_fix='1'
uci add_list firewall.@zone[-1].network='site2sitevpn'

# enable forwarding from LAN to VPN:
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].dest='vpn'

# Finally, you should commit UCI changes:
uci commit

Monitor VPN connection progress by using logread. If all goes well you will see the successful connection established message. If not, you’ll be able to get an idea of what’s wrong.

logread -f

If all goes well you’ll now have a bidirectional VPN between your two sites; however, traffic from the server’s subnet going directly to the client router itself (the OpenWRT device’s IP) will be considered as coming from the WAN interface and will be blocked. If you need to access the OpenWRT device directly from the USG’s subnet, you’ll need to add a firewall rule allowing it to do so:

uci add firewall rule
uci set firewall.@rule[-1]=rule
uci set firewall.@rule[-1].enabled='1'
uci set firewall.@rule[-1].target='ACCEPT'
uci set firewall.@rule[-1].src='wan'
uci set firewall.@rule[-1].name='Allow VPN to access router'
uci set firewall.@rule[-1].src_ip='$SERVER_SUBNET'
uci set firewall.@rule[-1].dest_ip='$INTERNALL_IP_OF_OPENWRT_ROUTER'
uci commit

Troubleshooting

One-sided VPN

I fought for some time with the fact that the VPN was established, but only traffic going from the Client network to the Server network would work. Traffic from the OpenVPN server subnet to the OpenVPN client subnet would simply hang and not work.

I finally found on the ubiquiti forums that this is due to default OpenVPN behavior of restricting traffic from the server subnet to the client subnet (see the OpenVPN how-to for more information.) The solution is to add lines in the server config informing it of the client network and to allow traffic to it. Below is an example USG config allowing informing it of remote subnet 192.168.230/24 and assigning the Client an IP of 10.0.76.253:

set interfaces openvpn vtun5 server client client1 ip 10.0.76.253
set interfaces openvpn vtun5 server client client1 subnet 192.168.230.0/24

VPN status stays “stopped” in OpenWRT

The best way to troubleshoot is to look at the logs in realtime. SSH to the OpenWRT router and run the command “logread -f” then try to initiate the connection again. The errors there will point you to the problem.

Headless Private Internet Access with OpenVPN & Port Forwarding

I wanted to initiate a connection to my VPN provider Private Internet Access (PIA) from one of my headless CentOS 7 servers. Unfortunately PIA software requires a GUI to work. Fortunately, PIA provides ovpn files that fully work with the openVPN software, which does work headlessly.

I followed this guide from htpcguides.com to get me going. I tweaked it a bit to work with CentOS 7. I then used this guide to help me set up port forwarding to work with transmission-daemon.

First, install openvpn & iproute

sudo yum -y install openvpn

Next grab PIA’s ovpn files for their servers:

wget https://www.privateinternetaccess.com/openvpn/openvpn.zip
sudo unzip openvpn.zip -d /etc/openvpn/pia

Now create a credentials file to specify your PIA username and password. The format is simply the first line being your PIA username, and the second line being your PIA password, nothing else.

#Create file
sudo vim /etc/openvpn/pia/.secret

#Enter PIA credentials into the file, then save and exit
USERNAME
PASSWORD

#Change permissions to be readable only by root
sudo chmod 700 /etc/openvpn/pia/.secret

Test if it’s working by manually running OpenVPN

sudo openvpn --config /etc/openvpn/PIA_SITE_FILENAME.ovpn --auth-user-pass /etc/openvpn/pia/.secret

Create a systemd service file to get OpenVPN to run on startup

sudo vim /usr/lib/systemd/system/pia.service

[Unit]
Description=OpenVPN
After=syslog.target network.target

[Service]
PrivateTmp=true
Type=forking
ExecStart=/usr/sbin/openvpn --daemon --config "/etc/openvpn/PIA_SITE_FILENAME.ovpn" --auth-user-pass /etc/openvpn/.secret

[Install]
WantedBy=multi-user.target

Reload systemd daemons and enable at boot:

sudo systemctl daemon-reload
sudo systemctl enable pia.service

Start the service (or reboot) and then use curl to verify your external IP has changed

sudo systemctl start pia.service
curl https://ipinfo.io/ip

Port Forwarding

PIA is awesome because it supports port forwarding to facilitate things like torrents. I took the script from this guide and tweaked it a bit to suit my needs (and to correct a syntax error)

Note: PIA only supports port forwarding from specific regions. Here is the current list.

Download script to pia directory

sudo wget -O /etc/openvpn/pia/portforward.sh https://raw.githubusercontent.com/HTPCGuides/pia-port-forwarding-scripts/master/transmission-pia-port-forwarding.sh
sudo chmod +x /etc/openvpn/portforward.sh

Edit the script to provide PIA username and pass, and Transmission user/pass (if you have that configured.) The variables to change are USERNAME,PASSWORD, TRANSUSER, and TRANSPASS

Test the script by running it manually (make sure both your pia service and transmission services are already running.) I’ve noticed that the script will say success even on failure which is why I suggest using bash -x so you can see actual error messages from PIA regarding port forwarding:

sudo bash -x /etc/openvpn/pia/portforward.sh

After that’s run you can check your transmission daemon to ensure it is listening on the PIA port that’s been forwarded.

Configure to run on startup and every two hours by invoking cron:

sudo crontab -e

@reboot sleep 60 && /etc/openvpn/pia/portforward.sh | while IFS= read -r line; do echo "$(date) $line"; done >> /var/log/pia_portforward.log 2>&1 #PIA Port Forward
0 */2 * * * /etc/openvpn/pia/portforward.sh | while IFS= read -r line; do echo "$(date) $line"; done >> /var/log/pia_portforward.log 2>&1 #PIA Port Forward

If you are paranoid about transmission running without the VPN working you can tweak your transmission service to rely on the pia service before starting. Modify /usr/lib/systemd/system/transmission-daemon.service and tweak the After line and add a Required line so it now reads this:

Required=pia.service
After=network.target pia.service

Don’t forget to run a sudo systemctl daemon-reload afterward.

Troubleshooting

If you get the error message “/etc/openvpn/pia/portforward.sh: line 35: [: : integer expression expected” it means you have to tweak the script a bit (it’s broken?)

Change line 35 to be the following:

if [ "$SPLITVPN" == 1 ]; then

Site to Site VPN between OPNsense & OpenWRT with Tinc

I’m a real glutton for punishment. I decided to upgrade my parents’ router to OpenWRT. The upgrade went smoothly except for one thing: The VPN I had established between my firewall and theirs.

This was a big enough headache that I even ended up switching my firewall from pfSense to OPNsense (Something I had been contemplating doing for a while anyway) hoping it would make things easier. It didn’t. In the end I abandoned OpenVPN entirely and instead went with Tinc.

Tinc is cool because it’s full mesh peer-to-peer instead of the traditional client / server model. If your equipment supports it, I’d definitely choose it over OpenVPN, especially if multiple sites are involved. A basic rundown of its configuration can be found here.

I used this site as a reference for how to set up tinc.  Essentially you decide on a network name, create private & public keys for each host, and configure each host to connect to each other via a config file & folder structure.

Tinc general configuration

On each device create an /etc/tinc/<network name>/hosts directory structure

mkdir -p /etc/tinc/<network name>/hosts
tincd -n <network name> -K 4096

To configure TINC we need some additional configuration files inside the /etc/tinc/<network name> directory

  • tinc.conf
  • tinc-up (script for bringing up the interface)
  • hosts/<hostname> (one for each location)

tinc.conf can be as simple as this:

Name = <name of host>
ConnectTo = <name of other host> 
#Add each host with an additional ConnectTo line

There needs to be a corresponding file in the hosts directory for each host. Example host file:

Address = <External IP of host>
Subnet = <Subnet other host will share>
#Add more subnets with additional Subnet lines

The host file also need’s the host’s public key. Append it to the end of the file:

cat /etc/tinc/<network name>/rsa_key.pub >> /etc/tinc/<network name>/hosts/<host name>

It’s easiest to generate the host files on each respective host, then copy them to all the other hosts.

The last step is to create the tinc-up script

#!/bin/sh
ubus -t 15 wait_for network.interface.$INTERFACE
ip link set $INTERFACE up ip addr add 172.16.0.1/24 dev $INTERFACE

Modify the IP used on each host so they don’t overlap. The private network here is what’s used for inter-host communication.

Make the script executable:

chmod 755 /etc/tinc/<network name>/tinc-up

OPNSense specific configuration

I got this working through an enhanced tinc package for OPNsense located here.  I will copypasta the content from that site here for easier reference:

Installation

The version might change, adjust it if fetch fails

fetch https://raw.githubusercontent.com/EugenMayer/tinc-opnsense/master/dist/os-tincdcustom-latest.txz
pkg install os-tincdcustom-latest.txz

1. your network

  1. copy the /usr/local/etc/tinc/example folder to /usr/local/etc/tinc/yournetwork
  2. enter yournetwork into /usr/local/etc/tinc/nets.boot to let this network be started on boot
  3. create keypairs by runng tincd -n <yournetwork> -K

2. your network configuration and tun device

  1. Edit /usr/local/etc/tinc/yournetwork/tinc.conf set the server you want to connect to and how this server is to be named
  2. Edit /usr/local/etc/tinc/yournetwork/tinc-up and adjust the network/netbitmask

3. finally the host configuration

  1. enter the /usr/local/etc/tinc/yournetwork/hosts folder and rename the files according to what you have chosen for youservername and theotherservername – they must match!
  2. enter the public key of the “this server” you find under /usr/local/etc/tinc/yournetwork/ into the according thisservernamefile and adjust the subnet this server offers (or subnets)
  3. enter the public key of the “other server” into the according theotherservername file and adjust the subnet the other server offers (or subnets)

4. OPNsense Interface/Gateway/Route/FW configuration

Please see this answer for a brief description

  • You need to create a Gateway, which is configured to go through tinc0 with “dynamic” (do not enter an IP on Gateway field)
  • You need to add a route to <remote subnet> through this gateway
  • Add your tinc0 interface in the Interface section. You can configure a ipv4 address or you don’t, does not matter. If you do, use your tinc-up configured address. Doing this enabled you to create FW Rules for the Tinc interface – which we will need.
  • Add a firewall on the Tinc interface to allow communication to local & remote subnets
    • Alternatively, add a single rule for the Tinc interface to allow any/any access (lazy, less secure)
  • Don’t forget to create a firewall rule allowing the port you’ve configured tinc to run on access from the internet.

OpenWRT specific configuration

Openwrt follows the general tinc configuration exactly. Make the appropriate folders and config files in /etc/tinc/<network name>/ and then test your configuration:

tincd -n <network name>

Once connection is established and working:

Create interface for your VPN (network / interfaces / add new interface) Select the name of your tinc network name from the list.

Next bridge your VPN to the LAN by going to Network / Firewall and editing your LAN zone. Select your VPN interface created earlier from the list and hit save & apply.

Run on startup

I could not find clear documentation on getting this to work on startup. There is a startup script for tinc but it doesn’t appear to launch my tinc config. I ended up modifying /etc/init.d/tinc and adding these lines to the start() and stop() functions. You could also just write your own simple init script to accomplish this.

start() {
...
/usr/sbin/tincd -n <network name>
}

stop() {
...
kill `pidof tincd`
}

Troubleshooting

Tincdcustom service won’t start in OPNSense

Starting from the GUI just does nothing, starting from CLI reveals this unhelpful error:

configctl tincdcustom status

Error (1)

From the OPNSense docs I determined which command I can run to see exactly why. The command is located in this configd configuration file: /usr/local/opnsense/service/conf/actions.d/actions_tincdcustom.conf

command:/usr/local/etc/rc.d/tincdcustom start

Doing that command manually revealed what the problem was:

/usr/local/etc/rc.d/tincdcustom start

Please create /usr/local/etc/tinc/nets.boot.

I had skipped step 1.2 of the tincdcustom instalaltion guide:

enter yournetwork into /usr/local/etc/tinc/nets.boot to let this network be started on boot

Once I added a single word – the name of the network I want to start on bootup – to /usr/local/etc/tinc/nets.boot – the daemon started and worked properly.

Running Tinc in verbose mode

Coming from the tinc documentation, I ran tinc in verbose mode on both of my hosts to troubleshoot why a connection wasn’t happening. It was very helpful.

tincd -n netname -d5 -D

Edit 9/9/2018

I had issues with my tinc startup script not working on the openwrt side. I found here that stated you should add this line to the top of your tinc-up config:

ubus -t 15 wait_for network.interface.$INTERFACE

This solved the startup issue for me.

Fix no internet in pfSense OpenVPN

I came across an issue with pfSense where I had created an openVPN connection but it would not work with internet traffic. The VPN connection established fine and I could connect to all internal hosts, but all internet traffic simply didn’t work. I checked the firewall logs and there were no firewall denies.

After scratching my head for a while I came across this post which suggests it might be due to the fact that there was no outbound NAT defined for the VPN network I created.

I went to Firewall / NAT / Outbound and sure enough, there was no outbound NAT rule for my VPN network. I manually created it for my VPN network and voila! Internet over the VPN!

Site to site VPN between pfSense and DD-WRT

I’ve been trying to establish a site-to-site VPN connection between my house and my parents’ for a couple years now. Each time I try I become frustrated and eventually give up. No longer! I’ve finally gotten a site to site VPN working between my pfSense router and my parents’ Netgear Nighthawk R8000 running DD-WRT v3.

It was quite the undertaking for me to get these two systems talking. I drew a lot of inspiration from here. In order to get this to work you need to keep these things in consideration:

  • Protocol, port, device type, encryption cipher, hash algorithm, TLS authentication, and certificate settings all need to match
  • VPN IP addresses need to be assigned to both the server and the client
  • Routes for each network need to be established on both devices
  • Firewalls need to be configured to allow traffic to/from each network through the VPN tunnel

I used the following settings to get things working between these two devices:

OpenVPN Server (pfSense)

If you haven’t already, generate a certificate authority and server certificate. Do this in System / Cert Manager and click Add. When generating the certificate make sure the Certificate Type is Server Certificate.

General

  • Server mode: Peer to Peer (SSL / TLS)
  • Protocol: UDP
  • Device mode: tap

Cryptographic settings

  • Enable authentication of TLS packets: checked
  • Automatically generate a shared TLS authentication key: checked
  • Peer certificate authority & Server Certificate:  Select appropriate CA / certificate from the dropdowns here
  • DH Parameter length: 2048
  • Encryption algorithm: AES-256-CBC
  • Auth digest algorithm: SHA256 (256 BIT)
  • Certificate depth: One (Client + Server)

Tunnel Settings

  • IPv4 Tunnel network: Enter a unique (not existing on either netwonk) network here in CIDR format, ex 10.1.1.0/24
  • Ipv4 Local Network(s): Enter the networks you would like the remote network to access
  • IPv4 Remote Network(s): Enter the networks you would like the local network to access
  • Compression: Enabled with Adaptive Compression

Advanced Configuration

Optional? I found that for some reason the routing table wasn’t properly populated with the remote network on my pfSense server. I added a custom option to take care of that:

route <remote network> <remote subnet mask> <remote VPN IP>

In my example it ended up being “route 192.168.98.0 255.255.255.0 10.54.98.2”

Key export

You will need to export the Certificate Authority certificate as well as the client certificate & private key files for use with dd-wrt. Do this by going to System / Cert Manager. There are little icons to the right of the certificates where you can click to download them.

Export the CA certificate as well as both the certificate and key from whatever was specified in the Server Certificate section from the above OpenVPN configuration.

OpenVPN client (dd-wrt)

Go to Services / VPN and look for the OpenVPN Client section

  • Start OpenVPN Client: Enable
  • Tunnel Device: TAP
  • Tunnel Protocol: UDP
  • Encryption cipher: AES-256 CBC
  • Hash Algorithm: SHA256
  • User Pass Authentication: Disable
  • Advanced Options: Enable
  • LZO Compression: Adaptive
  • NAT: Enable
  • Firewall protection: Disable
  • Bridge TAP to br0: Enable
  • TLS Auth Key: <Paste the contents of the “Key” section under pfSense’s Cryptographic settings area of the OpenVPN server configuration>
  • CA Cert: <Paste contents of downloaded CRT file from pfsense’s CA>
  • Public Client Cert: <Paste contents of downloaded CRT file from pfsense’s certificate section>
  • Private Client Key: <Paste contents of downloaded .key file from pfsense’s certificate section>

IN THEORY this should be all that you have to do. The tunnel should establish and traffic should flow between both networks. Sadly it wasn’t that simple for my setup.

Troubleshooting

One-way VPN

After setting up the tunnel I saw everything was connected but the traffic was unidirectional (remote could ping local network but not visa versa.)

On the pfsense router, I ran

netstat -r | grep 192.168

(change the grep to whatever your remote network is)

I noticed that there were no routes to the remote network. To fix this I appended a server option in the OpenVPN server config (adjust this to match your networks)

route 192.168.98.0 255.255.255.0 10.54.98.2

Blocked by iptables

Adding the route on the server helped but things still weren’t getting through. I enabled logging on the DD-WRT side, consoled into the router, and ran the following:

watch -n 1 "dmesg | grep 192.168.98"

This revealed a lot of dropped packets from my OpenVPN server’s network. After a lot of digging I found this forum post that suggested a couple custom iptables rules to allow traffic between the bridged network and the OpenVPN network (adjust interface names as necessary)

iptables -I FORWARD -i br0 -o tap1 -j ACCEPT 
iptables -I FORWARD -i tap1 -o br0 -j ACCEPT

This doesn’t survive a reboot, so you’ll want to enter those two commands in the Administration / Commands section of the dd-wrt web configuration and click “Save Firewall”

Success

Finally, after a custom routing rule on the pfsense side and a custom iptables rule on the dd-wrt side, two way VPN has been established!