This guide reflects my personal notes for personal use; it expects you to have an up-and-running OpenWRT firmware on your router, an existing dynamic DNS service available as well as know your way around Terminal i.e. CLI and SSH. These instructions below are published for people to compare notes and understand the process like I did. Kindly understand that I cannot be held responsible for any mistakes or malfunctions on your side.
I recently decided to install a secondary OpenVPN server on my Asus RT-58U router, as a backup gateway to my home network.
1. Package Installation
To set up and configure an OpenVPN server so we can connect to our home’s local network, we need to first install the following packages:
$ opkg update $ opkg install openvpn-openssl openvpn-easy-rsa $ opkg luci-app-openvpn ipset $ /etc/init.d/uhttpd restart
Additionally, as my preferred editor is nano
and it doesn’t come with OpenWRT, it’s a good idea to install that one, too:
$ opkg install nano
So we have first updated/refreshed the latest available packages from the source repository; then we installed the 4 needed packages; finally, we just restarted the router’s web server daemon.
Note: In case there is a need to re-install a package and its dependencies, use the --force-reinstall
parameter:
$ opkg install --force-reinstall openvpn-easy-rsa
2. Generating Necessary Keys
Next, we need to generate the required server keys and certificates using easy-rsa
. It is recommended that we move our easy-rsa
files from the default location so that we don’t accidentally overwrite those in case of a system or firmware update.
For a fresh installation on an OpenWRT firmware:
$ mkdir /etc/config/openvpn-asus $ mv /etc/easy-rsa/* /etc/config/openvpn-asus/ $ ln -s /etc/config/openvpn-asus /etc/easy-rsa
For an existing installation after an OpenWRT firmware update:
$ ls -la /etc/config/openvpn-asus $ rm -rf /etc/easy-rsa $ ln -s /etc/config/openvpn-asus /etc/easy-rsa
The idea here is that we create an independent folder to store all of our keys instead of the /etc/easy-rsa/
folder, as the latter does not survive a firmware update; we only need to link symbolically this original (and needed) folder /etc/easy-rsa/
to point to our own folder under /etc/config/
that does survive a firmware update.
Now, we must generate the certificates for the server and client(s). We need to start by editing a few lines in the /etc/easy-rsa/vars
file and enable stronger 2048-bit encryption. Edit this file with e.g. nano
and change the following variables with your own details:
set_var EASYRSA_REQ_COUNTRY "My Country" set_var EASYRSA_REQ_PROVINCE "My Province" set_var EASYRSA_REQ_CITY "My City" set_var EASYRSA_REQ_ORG "My Organisation" set_var EASYRSA_REQ_EMAIL "My Mail Address" set_var EASYRSA_REQ_OU "My Department Name"
Note: The first parameter EASYRSA_REQ_COUNTRY
requires a 2-digit country code like GR, FR, DE, IT, NL, UK, DK etc.
Now uncomment, further down, the following line by removing #
so it appears like this:
set_var EASYRSA_KEY_SIZE 2048
Next, we generate the “Diffie-Hellman” key agreement parameters, our “Certificate Authority” and pre-shared key pairs, signed locally. Run each command at a time by first moving to the needed directory.
cd /etc/easy-rsa source vars
We first set the needed configuration parameters, by entering these on the CLI:
export EASYRSA_PKI="/etc/easy-rsa/pki"; \ export EASYRSA_REQ_CN="ovpnca"; \ export EASYRSA_BATCH="1"
Now, we remove and re-initialise the PKI directory. If you are curious, you can do ls -la ./pki
before and after this procedure, to check the contents. Simply run:
$ easyrsa init-pki
Now let’s generate the “Diffie-Hellman” parameters (will take some minutes) and create a new “Certificate Authority” certificate, by validating their creation via cat
afterwards:
$ easyrsa gen-dh $ cat /etc/easy-rsa/pki/dh.pem $ easyrsa build-ca nopass $ cat /etc/easy-rsa/pki/ca.crt
Note: You may have noticed that we selected to not have a password set, by using nopass
parameter.
Next, we need to generate the server keys and the client keys, both signed locally:
easyrsa build-server-full server nopass cat /etc/easy-rsa/pki/private/server.key easyrsa build-client-full client nopass cat /etc/easy-rsa/pki/private/client.key
Finally, we need to generate the TLS pre-shared keys (PSK) for our installation:
$ openvpn --genkey --secret ${EASYRSA_PKI}/tc.pem $ cat /etc/easy-rsa/pki/tc.pem
Note: Remember that EASYRSA_PKI
was defined earlier.
3. Set Firewall Parameters
Next, we need to define and change the firewall parameters. For this server installation, we consider the VPN network as private and we assign the VPN interface directly to our LAN zone to minimise firewall setup. Then, we permit (i.e. open) access to the VPN server from WAN (Wide Area Network) zone.
First we set some more needed configuration parameters, such as the firewall port and protocol, by entering these on the CLI:
OVPN_PORT="1193"; \ OVPN_PROTO="udp";
Note: Despite the standard VPN port being 1194, here we set it to 1193 as this VPN server installation is a backup VPN (since one exists already on a NAS server running OpenMediaVault, in the same home network). For a single VPN server in a home network, change to default value 1194 where needed.
Run the following two multi-line commands on CLI:
uci rename firewall.@zone[0]="lan"; \ uci rename firewall.@zone[1]="wan"; \ uci del_list firewall.lan.device="tun+"; \ uci add_list firewall.lan.device="tun+"; \ uci -q delete firewall.ovpn;
and
uci set firewall.ovpn="rule"; \ uci set firewall.ovpn.name="Allow-OpenVPN"; \ uci set firewall.ovpn.src="wan"; \ uci set firewall.ovpn.dest_port="${OVPN_PORT}"; \ uci set firewall.ovpn.proto="${OVPN_PROTO}"; \ uci set firewall.ovpn.target="ACCEPT"; \ uci commit firewall; \ /etc/init.d/firewall restart
Note: Again, remember that OVPN_PORT
and OVPN_PROTO
were defined earlier. Now let’s verify the new firewall entries that we saved above, by running:
$ uci show firewall.ovpn
In the meantime, we also need to check if packet forwarding is enabled; it should be by default. A result “1” denotes that it is active:
$ cat /proc/sys/net/ipv4/ip_forward
4. Create OpenVPN Server Instance
Now, we must create our OpenVPN instance (named “asus_server”) that will be soon visible in the OpenWRT interface (by going to menu VPN and clicking on OpenVPN page):
export EASYRSA_PKI="/etc/easy-rsa/pki"; \ uci set openvpn.asus_server="openvpn"; \ uci set openvpn.asus_server.enabled="1"; \ uci set openvpn.asus_server.dev="tun"; \ uci set openvpn.asus_server.port="1193"; \ uci set openvpn.asus_server.proto="udp"; \ uci set openvpn.asus_server.comp_lzo="no"; \ uci set openvpn.asus_server.log="/tmp/openvpn.log"; \ uci set openvpn.asus_server.status="/var/log/openvpn.log"; \ uci set openvpn.asus_server.verb="3"; \ uci set openvpn.asus_server.mute="5"; \ uci set openvpn.asus_server.keepalive="10 120"; \ uci set openvpn.asus_server.persist_key="1"; \ uci set openvpn.asus_server.persist_tun="1"; \ uci set openvpn.asus_server.user="nobody"; \ uci set openvpn.asus_server.group="nogroup"; \ uci set openvpn.asus_server.ca="${EASYRSA_PKI}/ca.crt"; \ uci set openvpn.asus_server.cert="${EASYRSA_PKI}/issued/server.crt"; \ uci set openvpn.asus_server.dh="${EASYRSA_PKI}/dh.pem"; \ uci set openvpn.asus_server.key="${EASYRSA_PKI}/private/server.key"; \ uci set openvpn.asus_server.mode="server"; \ uci set openvpn.asus_server.server="192.168.0.0 255.255.255.0"; \ uci set openvpn.asus_server.tls_server="1"; \ uci set openvpn.asus_server.topology="subnet"; \ uci set openvpn.asus_server.route_gateway="dhcp"; \ uci set openvpn.asus_server.client_to_client="1"; \ uci commit openvpn
Finally, we need to define the settings to be “pushed” to clients that will connect, reflecting the above parameters:
uci add_list openvpn.asus_server.push="comp-lzo no"; \ uci add_list openvpn.asus_server.push="persist-key"; \ uci add_list openvpn.asus_server.push="persist-tun"; \ uci add_list openvpn.asus_server.push="user nobody"; \ uci add_list openvpn.asus_server.push="user nogroup"; \ uci add_list openvpn.asus_server.push="topology subnet"; \ uci add_list openvpn.asus_server.push="route-gateway dhcp"; \ uci add_list openvpn.asus_server.push="redirect-gateway def1"; \ uci add_list openvpn.asus_server.push="192.168.0.0 255.255.255.0"; \ uci add_list openvpn.asus_server.push="dhcp-option DNS 9.9.9.9"; \ uci add_list openvpn.asus_server.push="dhcp-option DNS 1.1.1.1"; \ uci commit openvpn
So, what have we achieved with the commands above? In the first part, we perform the following server operations:
Similarly, in the second part, we define the same settings to be communicated to the client as needed:
(*) Extremely useful if the client’s local network cannot be trusted e.g. airports, workplace or coffee shops.
We can now restart the service to apply our changes:
$ /etc/init.d/openvpn restart
To view the above settings at any moment:
$ uci show openvpn.asus_server $ uci show firewall.ovpn
We need to be sure that the OpenVPN server is enabled and has started:
$ /etc/init.d/openvpn enable $ /etc/init.d/openvpn start
At any moment, we can check the logs for a successful server initialization and the respective service is running as expected:
$ cat /tmp/openvpn.log $ cat /var/log/openvpn.log $ pgrep -f -a openvpn
A likely output from these logs above indicating success, would be:
$ cat /tmp/openvpn.log OpenVPN 2.4.7 arm-openwrt-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [MH/PKTINFO] [AEAD] library versions: OpenSSL 1.1.1j 16 Feb 2021, LZO 2.10 Diffie-Hellman initialized with 2048 bit key TUN/TAP device tun0 opened TUN/TAP TX queue length set to 100 /sbin/ifconfig tun0 192.168.0.1 netmask 255.255.255.0 mtu 1500 broadcast 192.168.0.255 Could not determine IPv4/IPv6 protocol. Using AF_INET Socket Buffers: R=[163840->163840] S=[163840->163840] UDPv4 link local (bound): [AF_INET][undef]:1193 UDPv4 link remote: [AF_UNSPEC] GID set to nogroup UID set to nobody MULTI: multi_init called, r=256 v=256 IFCONFIG POOL: base=192.168.0.2 size=252, ipv6=0 Initialization Sequence Completed $ cat /var/log/openvpn.log OpenVPN CLIENT LIST Updated,Sat Feb 20 16:20:30 2021 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref GLOBAL STATS Max bcast/mcast queue length,0 END
5. Creating Client Configuration File
With the OpenVPN server now in place and running, we can finally create the client’s configuration .ovpn
file to be used with the remote client computer. In a plain-text file called for example Asus.ovpn
enter the parameters found below, using your favourite editor on your computer. Below is a manual process that will require some copying and pasting from your Terminal window.
auth-nocache client comp-lzo no dev tun mute 10 nobind persist-key persist-tun proto udp remote YOUR-DOMAIN-NAME 1193 remote-cert-tls server resolv-retry infinite script-security 1 verb 3 <key> # Insert here the client's generated private key from: # cat /etc/easy-rsa/pki/private/client.key </key> <cert> # Insert here the client's generated certificate from: # cat /etc/easy-rsa/pki/issued/client.crt </cert> <ca> # Place here the server's generated certificate from: # cat /etc/easy-rsa/pki/ca.crt </ca>
We are now ready to use VPN client tools such as Tunnelblick on macOS. As we have not defined a user or password, connecting to our router will simply need to run the VPN client software and launch our configuration.
IMPORTANT NOTE: You need to replace the YOUR-DOMAIN-NAME
entry found above with either your fixed WAN IP address if you have one, or a dynamic DNS name, such as those kindly provided by DuckDNS. This is an absolutely needed parameter as the client computer must know where to connect, obviously!
N.B. For those wondering, like myself, as to why there is no <tls-auth>
section in the above configuration, according to the OpenVPN – Getting Started / How-To page:
Please note that you cannot use “–tls-crypt” and “–tls-auth” options at the same time […] Using “tls-crypt” also does not need to care about the KEYDIR as it is handled automatically.
Sources & Further Reading
The knowledge acquired and the guide above would not have been possible without the following sources:
You must be logged in to post a comment.