Tutorial for OpenVPN TAP Bridge Mode

I’m using my raspberry pi as a NAS with a samba server to provide the content through my home network. I wanted to have that content directly accessible from the rest of the world but without the traffic to be visible to anybody else than me. I then decide to use OpenVPN TAP in bridged mode.

OpenVPN Tap: Setup

  • TL-841 as router
  • Raspberry Pi as NAS
  • Internal IP : 192.168.42.0/24
  • Range IP router: 192.168.42.10 – 192.168.42.120
  • Range IP OpenVPN: 192.168.42.128 – 192.168.42.254

Preparation

First, important point, if you have a DHCP server on your router, be sure to configure it to not assign IP address for the whole subnet, but only a part (as I’ve done in the setup). If you don’t, you could encounter 2 devices sharing the same IP, and trust me you don’t want that.

Download scripts/conf

You need to download and extract my scripts and configuration. I’ll walk you through each of those. The following command will download the files and extract them into /tmp/openvpn-scripts:

wget -O /tmp/master.zip https://gist.github.com/Belphemur/3b03eaad96172b2159fc/archive/master.zip && mkdir /tmp/openvpn-scripts && unzip /tmp/master.zip -d /tmp/openvpn-scripts/

 

Explanation of the command: Wget download the gist on GitHub with my scripts then mkdir create a temporary directory where unzip extract them. I advise you to run it WITHOUT root or sudo it is not needed.

Installing OpenVPN

sudo apt-get install openvpn

Enable TLS

We need the easy-rsa to easily create our root certificate, the certificate of the server and the one for each client. Using those cert, the client will authenticate themselves to the server. No need for login/password.

On Wheezy:

mkdir /etc/openvpn/easy-rsa
sudo cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /etc/openvpn/easy-rsa/

On Jessie:

sudo apt-get install easy-rsa
sudo mkdir /etc/openvpn/easy-rsa
sudo cp -R /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/

On Stretch:

sudo apt-get install easy-rsa
sudo mkdir /etc/openvpn/easy-rsa
sudo cp -R /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
sudo ln -s /etc/openvpn/easy-rsa/openssl-1.0.0.cnf /etc/openvpn/easy-rsa/openssl.cnf

Configuring TLS

Edit /etc/openvpn/easy-rsa/vars bottom according to your organization.

Look for those in the file and change them for your needs.

export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="Fort-Funston"
export KEY_EMAIL="[email protected]"
export [email protected]
export KEY_SIZE=4096

Now execute those command to prepare for the generation of the certificate and keys:

cd /etc/openvpn/easy-rsa/
sudo -s #because of the sourcing of the ./vars file you need to be root to do this
mkdir keys
touch keys/index.txt
echo 01 > keys/serial
. ./vars # set environment variables
./clean-all

 

From Debian wiki on OpenVPN:

Remember:

  • only .key files should be kept confidential.
  • .crt and .csr files can be sent over insecure channels such as plaintext email.
  • do not need to copy a .key file between computers.
  • each computer will have its own certificate/key pair.

Generating the certs and keys

Those commands will generate the root certificate, the server certificate and the DH keys. I give two choices to generate the DH Param, either you use your own machine or DHTool free service.

Generate DH Param yourself

cd /etc/openvpn/easy-rsa/
./build-ca
./build-key-server server
./build-dh #takes time

Use DHTool service

Read more about DHTool on this blog post: Public Diffie-Hellman Parameter Service/Tool

cd /etc/openvpn/easy-rsa/
./build-ca
./build-key-server server
curl https://2ton.com.au/dhparam/${KEY_SIZE} > ${KEY_DIR}/dh{$KEY_SIZE}.pem

Disabling Current Systemd Service

By default when you’re installing the OpenVPN server, the installer take care of setting OpenVPN to start with your system.

We don’t want that since you’re installing another service configuration that takes care of the Bridge.

sudo systemctl disable openvpn
sudo systemctl stop openvpn

Packet Forwarding

Because we’re going to create a bridge, we need to set the kernel to let the IP packet transit through it. To do this, we’ll use sysctl.

You’ll need to edit the file /etc/sysctl.conf to add the following line: net.ipv4.ip_forward = 1

Then you reload the configuration: sysctl -p /etc/sysctl.conf

The packet switching is not set in your kernel and will stay after reboot.

Bridge Scripts

Following the OpenVPN tutorial on how to create a bridge and make it work with OpenVPN, I created my own scripts to do this.  First, you need to install the bridge-utils, scripts used to create network bridge then create a directory to put my scripts into it.

sudo apt-get install bridge-utils

My bridge scripts are in 3 parts:

  1. Bridge-conf: to configure the scripts
  2. Bridge-start: to start the bridge
  3. Bridge-stop: to destroy the bridge

My scripts are made to restore the system to its previous state after destroying the bridge. I tested them and they work on Debian Jessie on a raspberry pi.

You’ll find all the following file in /tmp/openvpn-scripts/ if you used the command to download the gist.

Bridge-conf

You NEED to modify this file with your own configuration.

sudo ip addr

or

sudo ifconfig

Should give you the information you need to put here. The idea is to make your bridge act like your normal Ethernet interface with the same properties

Bridge-start

Bridge-stop

Server Configuration

In the same folder, you find the server.conf. The file contains the configuration for OpenVPN in server mode.

You should update the port, protocol (proto) and the server-bridge with your network setting.

  • Port: the port your sever will listen on
  • Proto: which protocol used (either TCP or UDP)
  • server-bridge:
    • 192.168.42.2: the internal IP of your device in your home network
    • 255.255.255.0: it’s mask (represent the /24)
    • 192.168.42.128: starting IP to get assigned to the clients
    • 192.168.42.254: the last IP to be assigned to the clients

You’ll find also this:

#set the dns servers
push "dhcp-option DNS 192.168.42.1"
#set the WINS server (SAMBA)
push "dhcp-option WINS 192.168.42.2"

It needs to be changed also, the first one to point at your router (that surely act as a DNS server) and the second one to your SAMBA server (NAS). In my case, it’s the same IP as the server because I’m hosting the Samba server and the OpenVPN server on the same device.

Client Configuration Script

Last file that needs to be changed for your needs, build-client. The script generates a configuration file for OpenVPN in the folder you launch it. Either you edit the configuration script with your variable or use the environment variable to set it (See Generate a client configuration)

You’ll find a section VARIABLES with :

  • PROTO: The protocol used on the server. (Need to be the same as you set on the server)
  • REMOTE: The IP address or HOST of your server. It needs to be your external IP address or an hostname (like exmaple.com) that point to your external IP. (I’m using no-ip as a DDNS service). If you leave it empty, the script will retrieve your external IP for you.
  • PORT: the port used by the server
  • COMPRESS: the compression algorithm used
  • OPENVPN_CLIENT_DIRECTIVE: if you need to add another directive in the client configuration

Installing the configuration and scripts

Now that the preparation is ready, let’s put all the script and configuration where they should be.

OpenVPN configuration

Move the server.conf into /etc/openvpn/

sudo mv /tmp/openvpn-scripts/server.conf /etc/openvpn/

Bridge scripts

sudo mkdir /etc/openvpn/bridge
sudo mv /tmp/openvpn-scripts/bridge-* /etc/openvpn/bridge/
sudo chmod +x /etc/openvpn/bridge/bridge-*

Client Generator Script

sudo mv /tmp/openvpn-scripts/build-client /etc/openvpn/easy-rsa/
sudo chmod +x /etc/openvpn/easy-rsa/build-client

Service Script for systemd

If you are using Debian Jessie or your distribution is using systemd as an init do this to add service unit for OpenVPN Bridged.

sudo mv /tmp/openvpn-scripts/openvpn@.service /etc/systemd/system/
sudo systemctl daemon-reload

To make SystemD start the service at boot time

sudo systemctl enable [email protected]

Generate a client configuration

Now we will generate a client configuration that will be used on your computer to connect to the OpenVPN server. Here we build for the client OnMyWay.

cd /etc/openvpn/easy-rsa/
sudo ./build-client OnMyWay

or

cd /etc/openvpn/easy-rsa/
sudo PROTO=udp REMOTE=example.com PORT=1255 COMPRESS=comp-lzo ./build-client OnMyWay

 

Using the environment variable is another way to generate a client configuration.

You need to say yes to the signing of the cert and the commit.

You should see something like this:

/etc/openvpn/easy-rsa /home/pi
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/keys
Generating a 2048 bit RSA private key
..................................................................................+++
...................................+++
writing new private key to 'OnMyWay.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [NY]:
Locality Name (eg, city) [New-York]:
Organization Name (eg, company) [Raspy]:
Organizational Unit Name (eg, section) [RaspberryPiOrg]:
Common Name (eg, your name or your server's hostname) [OnMyWay]:
Name [RaspyKey]:
Email Address [[email protected]]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /etc/openvpn/easy-rsa/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName                                             :PRINTABLE:'US'
stateOrProvinceName                                     :PRINTABLE:'NY'
localityName                                            :PRINTABLE:'New-York'
organizationName                                        :PRINTABLE:'Raspy'
organizationalUnitName                                  :PRINTABLE:'RaspberryPiOrg'
commonName                                              :PRINTABLE:'OnMyWay'
name                                                    :PRINTABLE:'RaspyKey'
emailAddress                                            :IA5STRING:[email protected]'
Certificate is to be certified until Jan 18 17:49:16 2025 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

 

You’ll find an OnMyWay.ovpn in the same folder you are. It’s the configuration with the certificate embedded into it.

Revoke a user

sudo -s
cd /etc/openvpn/easy-rsa
. ./vars
./revoke-full OnMyWay
exit

Start the server

If you used my service unit for systemd :

sudo service [email protected] start

else

sudo /etc/openvpn/bridge/bridge-start
sudo service openvpn start

Stop the server

If you used my service unit for systemd :

sudo service [email protected] stop

else

sudo service openvpn stop
sudo /etc/openvpn/bridge/bridge-stop

 

Antoine Aflalo Written by:

18 Comments

  1. Colin Law
    12th June 2016
    Reply

    When I run the wget command sequence at the start, the tar command gives the error:
    gzip: stdin has more than one entry–rest ignored
    tar: Child returned status 2
    tar: Error is not recoverable: exiting now

    This is running on a Pi 3 running Jessie.
    I managed to extract the files ok using the archive manager on Ubuntu.

  2. Colin Law
    12th June 2016
    Reply

    Should the lines
    sudo cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0/* easy-rsa/
    and
    sudo cp -R /usr/share/easy-rsa/* easy-rsa/
    be
    sudo cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /etc/openvpn/easy-rsa/
    and
    sudo cp -R /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/

    Also I think first it is necessary to make the directory
    sudo mkdir /etc/openvpn/easy-rsa

    • Antoine Aflalo
      13th June 2016
      Reply

      Good point I correct it. It’s clearer this way.

  3. davel
    4th October 2016
    Reply

    Great manual, but I can not find, however, such information for someone who is the first time meets with bridget-utils and openvpn. You have not entered assumptions.

    What IP address for raspberry (openvpn hub – probably 192.168.42.2) ?
    IP address of interface eth0 is the same as the address br0? In your case is 192.168.42.2, in my 192.168.160.248 (eth0)

    After the modifications for own network, I have a problem with the automatic start of the bridge.

    My assumptions:
    The IP address for eth0 my Raspberry Pi is 192.168.160.248.
    Adress for br0 is this same, br0 is one interface. br0 = tap0 + eth0. This same adress as for eth0 interface.

    I assume that: this is also the address to be entered in serwer.conf, in this place: “server-bridge 192.168.160.248 255.255.255.0 192.168.160.2 192.168.160.5”

    After adjusting the whole I have such a situation, openvpn starts, the unit to it they are combined correctly but there is no bridge. I only interface eth0, Io and eth0 no address (169.254.30.75), the service manual takeoff lose connection with raspberry. You can steer me where you think I make a mistake? Sorry for poor English

    dev tap0
    #tun-mtu 1500
    #tun-ipv6
    tls-server
    proto udp
    port 5555

    ca /etc/openvpn/ca.crt
    cert /etc/openvpn/server.crt
    key /etc/openvpn/server.key
    dh /etc/openvpn/dh2048.pem

    topology subnet

    user nobody
    group nogroup

    server-bridge 192.168.160.248 255.255.255.0 192.168.160.2 192.168.160.5
    ;client-to-client

    #server-ipv6 2001:db8::/64

    mssfix
    persist-key
    persist-tun

    #log /var/log/openvpn
    status /var/log/openvpn-status-8888.log
    verb 4
    client-to-client

    keepalive 10 120
    mute 50

    #set the dns servers
    ;push “dhcp-option DNS 192.168.160.1”

    #set the WINS server (SAMBA)
    ;push “dhcp-option WINS 192.168.160.248”

    #For windows, to make the network recognized
    ;push “route 192.168.90.0 255.255.255.0 192.168.90.1”
    ;push “route 192.168.3.0 255.255.255.0 192.168.3.1”
    cipher AES-256-CBC
    auth SHA512

    log-append /var/log/openvpn
    comp-lzo
    #replay-window 128

    • Antoine Aflalo
      4th October 2016
      Reply

      Hello,
      The assumptions are the setup where it’s exactly how you described it.

      Did you modify the bridge-conf with your current network, the ip of your raspberry pi in that network, the mac address of that interface etc ?

      What happens when you manually start the script: /etc/openvpn/bridge/bridge-start ?

  4. davel
    4th October 2016
    Reply

    I think that somewhere is a loop, because after a manual start losing my connection to the console. Tomorrow I will try to connect the RPI to a monitor. In the console, it is difficult to observe what actually happens, the logs are not sufficient. Openvpn starting properly, two way tunel A-B is connected. (A) no pings to (B), (B) no pinging (A). Remote machine have correct address from the pool (from server.conf). Next machine connected to server received next address from the pool. Only bridge is problem. First conclusion: error in server.conf. After tests, my conclusion is error in the config bridge or interfaces. ETH0 is static (iface, address, netmask, gw)

    • Antoine Aflalo
      4th October 2016
      Reply

      I added a part about Packet Forwarding. Maybe this is what your configuration is missing.

  5. davel
    4th October 2016
    Reply

    There’s a simple mistake, this is not a loop. I made a mistake in bridge-conf, in the IP address. Bridge does not start after system start, but I can manually run it. Stations has established connection. Only not pinging. I enable STP on br0 & filtered port in tcpdump. Packets on UDP, looks ok. Maybe this is any problem on firewall, RPI is in the lan, port VPN is forwarding from Mikrotik router. Thanks for Your time 😉

    21:46:40.072685 STP 802.1d, Config, Flags [none], bridge-id 810f.e0:5f:b9:c0:4f:40.8014, length 42
    21:46:40.369236 ARP, Request who-has 192.168.160.144 tell 192.168.160.168, length 46
    21:46:40.419985 ARP, Request who-has 192.168.160.152 tell 192.168.160.168, length 46
    21:46:40.469300 ARP, Request who-has 192.168.160.174 tell 192.168.160.168, length 46
    21:46:40.469409 ARP, Request who-has 192.168.160.18 tell 192.168.160.168, length 46
    21:46:40.539222 ARP, Request who-has 192.168.160.2 tell 192.168.160.168, length 46

  6. Pehen
    6th February 2017
    Reply

    Great write up and scripts.

    Ive started using your bridge start and stop scripts, and edited my rig that was already setup thinking that I could use the openvpn@ systemd code to get the scripts to run properly when openvpn starts and stops, but its not working for some reason. I have to manually run the bridge start command before starting openvpn for it to all work.

    Any pointers on things to check with regards to the openvpn@ service?

    • Antoine Aflalo
      7th February 2017
      Reply

      When I wrote the systemd script I wasn’t aware of the difference between network.target and network-online.target.

      I updated the service script, it nows start correcty as soon as the network is active and configure the bridge.

      Can you confirm me it works for you too ?

  7. StephKax
    14th February 2017
    Reply

    Hi All,
    I have the same problem.
    Unable to create br0 interface by using openvpn@.service.
    After a reboot : KO
    After stop and start by hand : KO
    But the br0 is created by using ./etc/openvpn/bridge/bridge-start the start openvpn.
    I’m trying to debug but i’m not a guru 🙂

    Thanks

    • Antoine Aflalo
      14th February 2017
      Reply

      Have you tried with the version I updated more or less 5 days ago ?

      This one should work correctly for boot and starting manually.

  8. Stephkax
    14th February 2017
    Reply

    Dear Antoine,
    Yes i’m usine the last script on your repository, i’have s’en the modification in the unit .
    However, it doesn’t work at boot or by hand.

    Try to : tail -f daemon log but no error,. How CAN i add verbosity ?

    • Antoine Aflalo
      14th February 2017
      Reply

      Are you using Debian Jessie ?

      Can you copy paste what you get when you do: journalctl -a -u [email protected]

  9. Stephkax
    14th February 2017
    Reply

    Just: started openvpn connections to server
    No br0

  10. Stephkax
    14th February 2017
    Reply

    And yes, j’essie Fresh install last version

  11. StephKax
    14th February 2017
    Reply

    Status just after reboot:

    [email protected]:~ $ sudo systemctl status openvpn@.server
    ● openvpn@.server.service – OpenVPN server .server
    Loaded: loaded (/etc/systemd/system/openvpn@.service; disabled)
    Active: inactive (dead)
    Docs: man:openvpn(8)
    https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage
    https://community.openvpn.net/openvpn/wiki/HOWTO
    https://www.aaflalo.me/2015/01/openvpn-tap-bridge-mode

    tap0 interface is created but no br0

    And the daemon log after stop / start service

    Feb 14 12:54:03 raspberrypi systemd[1]: Starting OpenVPN connection to server…
    Feb 14 12:54:03 raspberrypi systemd[1]: Started OpenVPN connection to server.
    Feb 14 12:54:03 raspberrypi dhcpcd[591]: tap0: adding address fe80::8765:135e:ef34:7a30
    Feb 14 12:54:03 raspberrypi dhcpcd[591]: if_addaddress6: Permission denied
    Feb 14 12:54:03 raspberrypi dhcpcd[591]: tap0: waiting for carrier
    Feb 14 12:54:03 raspberrypi dhcpcd[591]: tap0: carrier acquired
    Feb 14 12:54:04 raspberrypi dhcpcd[591]: tap0: IAID 7f:fd:34:92
    Feb 14 12:54:04 raspberrypi dhcpcd[591]: tap0: soliciting an IPv6 router
    Feb 14 12:54:04 raspberrypi dhcpcd[591]: tap0: soliciting a DHCP lease
    Feb 14 12:54:14 raspberrypi dhcpcd[591]: tap0: using IPv4LL address 169.254.203.155
    Feb 14 12:54:14 raspberrypi avahi-daemon[342]: Joining mDNS multicast group on interface tap0.IPv4 with address 169.254.203.155.
    Feb 14 12:54:14 raspberrypi dhcpcd[591]: tap0: adding route to 169.254.0.0/16
    Feb 14 12:54:14 raspberrypi avahi-daemon[342]: New relevant interface tap0.IPv4 for mDNS.
    Feb 14 12:54:14 raspberrypi avahi-daemon[342]: Registering new address record for 169.254.203.155 on tap0.IPv4.
    Feb 14 12:54:16 raspberrypi ntpd[621]: Listen normally on 6 tap0 169.254.203.155 UDP 123
    Feb 14 12:54:16 raspberrypi ntpd[621]: peers refreshed

    same result: after stop, tap0 is killed and after start: tap0 ok but no br0

    • Antoine Aflalo
      14th February 2017
      Reply

      The problem has been resolved by email.

      I’m editing the tutorial, you need to be sure the normal daemon of OpenVPN is not started/enabled.

Leave a Reply

Your email address will not be published. Required fields are marked *