docker-openvpn icon indicating copy to clipboard operation
docker-openvpn copied to clipboard

How do you set up a bridge VPN (TAP) with this?

Open melyux opened this issue 5 years ago • 14 comments

Simply using the -t option changes the the device to tap0, but doesn't change the server directive to server-bridge. But even after doing that, TAP mode doesn't work. Is there a way to make the OpenVPN server provide a TAP bridged VPN?

melyux avatar Apr 01 '19 23:04 melyux

Interested in knowing this as well, I have unsuccessfully tried to set it up and looked for dockerized examples of a tap setup to no avail.

ellamosi avatar Apr 08 '19 18:04 ellamosi

I'm working on fork for doing this and I have it functional right now but with some manual tweaking. Multi-arch docker images are on docker hub. I'm running this on BeagleBoneBlack (armv7, Debian GNU/Linux 10 (buster)).

My tweaks:

  • change server to server-bridge in openvpn.config
#server 192.168.255.0 255.255.255.0
server-bridge 192.168.1.199 255.255.255.0 192.168.1.191 192.168.1.198
  • run docker with --net host
export OVPN_DATA=ovpn-data-tap-host

docker run \
	-v $OVPN_DATA:/etc/openvpn \
	--log-opt max-size=10m \
	--name openvpn-tap-host -d \
	--net host \
	--restart always \
	--cap-add=NET_ADMIN \
	aktur/openvpn
  • adding bridge-start executable script, my example with hardcoded values:
bash-5.0# cat /etc/openvpn/bridge-start 
#!/bin/bash

#################################
# Set up Ethernet bridge on Linux
# Requires: bridge-utils
#################################

# Define Bridge Interface
br="br0"

# Define list of TAP interfaces to be bridged,
# for example tap="tap0 tap1 tap2".
tap="tap0"

# Define physical ethernet interface to be bridged
# with TAP interface(s) above.
eth="eth0"
eth_ip="192.168.1.199"
eth_netmask="255.255.255.0"
eth_broadcast="192.168.1.255"
gateway="192.168.1.1"

for t in $tap; do
    openvpn --mktun --dev $t
done

brctl addbr $br
brctl addif $br $eth

for t in $tap; do
    brctl addif $br $t
done

for t in $tap; do
    ifconfig $t 0.0.0.0 promisc up
done

ifconfig $eth 0.0.0.0 promisc up

ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast

# Add default route if eth is also gateway port
route add default gw $gateway $br

aktur avatar Nov 25 '20 06:11 aktur

@aktur Hi there, I'm trying to use your fork to get this up and running. It seems like the container is starting fine and one client can connect to it. A second connection is not possible. Are there additional steps to be taken for this?

When do you run the bridge-start script? Is this done by the container. I did not see this in the container logs and running it before starting the container results in many errors: tap0 does not (yet) exist, ifconfig command not found (host is arch so ip is used).

Thank you very much!

Mailblocker avatar Jan 10 '21 15:01 Mailblocker

@Mailblocker I have no problems with connecting a second client to my tap server. Are you using different keys for both connections?

bridge-start is run by the container. I'm not sure if there should be any logs visible. No need to start it manually, you only need to put correct values corresponding to your network configuration in it.

aktur avatar Jan 10 '21 16:01 aktur

@aktur Thank you for the quick reply.

I'm using different keys for both connections. Maybe I did something wrong in the initial setup. How do I tell the openvpn server to use tap mode? Only by setting the openvpn.conf parameter dev from tun to tap? I think this line if [ $OVPN_DEVICE == "tap" ]; then is not processed in my setup. At least I dont see any bridges created on the host system after starting up the container.

Mailblocker avatar Jan 10 '21 17:01 Mailblocker

@Mailblocker the tap device you set indeed in the initial setup. In the Quick start section you have the line

docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm aktur/openvpn ovpn_genconfig -u udp://VPN.SERVERNAME.COM # add -t for TAP device 

therefore you must add -t when running ovpn_genconfig

aktur avatar Jan 10 '21 18:01 aktur

@aktur Thank you very much! Damn I missed that, I will try the setup from scratch this week.

Mailblocker avatar Jan 10 '21 19:01 Mailblocker

Hi @aktur I gave it another shot today but I couldn't fully succeed. This is the steps I have taken from a clean start:

1. export OVPN_DATA=absolute_path_to_data_volume
2. export OVPN_URL=url_to_my_server
3. echo OVPN_DATA=$OVPN_DATA > .env
4. docker-compose.yml

    version: '3.8'

    services:
      openvpn_tap:
        container_name: openvpn_tap
        image: aktur/openvpn
        restart: always
        cap_add:
         - NET_ADMIN
        network_mode: "host"
        volumes:
         - ${OVPN_DATA}:/etc/openvpn

5. docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm aktur/openvpn ovpn_genconfig -u udp://$OVPN_URL -t
6. docker run -v $OVPN_DATA:/etc/openvpn --rm -it aktur/openvpn ovpn_initpki
    6.1 insert password for ca cert
7. docker-compose up -d openvpn_tap 
9. export CLIENTNAME=some_user_name
8. docker run -v $OVPN_DATA:/etc/openvpn --rm -it aktur/openvpn easyrsa build-client-full $CLIENTNAME
    8.1 enter password for user twice
    8.2 enter password for ca cert to sign the user cert
9. docker run -v $OVPN_DATA:/etc/openvpn --rm aktur/openvpn ovpn_getclient $CLIENTNAME > $CLIENTNAME.ovpn
10. Copy the client file to my machine: scp $CLIENTNAME.ovpn some_user@some_machine:~
11. Edit openvpn conf first line to: server-bridge 192.168.2.42 255.255.255.0 192.168.2.101 192.168.2.200
12. Create bridge-start at $OVPN_DATA/bridge-start with content:
    #!/bin/bash

    #################################
    # Set up Ethernet bridge on Linux
    # Requires: bridge-utils
    #################################

    # Define Bridge Interface
    br="br0"

    # Define list of TAP interfaces to be bridged,
    # for example tap="tap0 tap1 tap2".
    tap="tap0"

    # Define physical ethernet interface to be bridged
    # with TAP interface(s) above.
    eth="wlp1s0"
    eth_ip="192.168.2.42"
    eth_netmask="255.255.255.0"
    eth_broadcast="192.168.2.255"
    gateway="192.168.2.1"

    for t in $tap; do
        openvpn --mktun --dev $t
    done

    brctl addbr $br
    brctl addif $br $eth

    for t in $tap; do
        brctl addif $br $t
    done

    for t in $tap; do
        ifconfig $t 0.0.0.0 promisc up
    done

    ifconfig $eth 0.0.0.0 promisc up

    ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast

    # Add default route if eth is also gateway port
    route add default gw $gateway $br
    
12. chmod +x  $OVPN_DATA/bridge-start 
13. docker-compose down
14. docker-compose up

What did work:

  • I could see that the openvpn.conf now is set to use tap
  • I can see that the bridge-start script is executed when spinning up the container in its startup (not using -d for detached mode)

But as soon as the bridge-start script is executed my machine loses the network connection. My interface (wlp1s0) loses its ipv4 and can not access the network anymore. At the same time I am unable to connect via SSH or OpenVPN at this point.

Any ideas?

I really appreciate your help.

Mailblocker avatar Jan 12 '21 22:01 Mailblocker

Hi @Mailblocker,

Quick question: is your LAN you want to bridge with OpenVPN 192.168.2.0/24?

Anyway, it is clear that you have problems with bridging - this is in fact a complete separate problem from openvpn. Namely, you can run openvpn in tap mode without bridge but then unicast/multicast traffic won't propagate. On the other hand, you can set up a bridge outside of openvpn and test it.

Anyway, I try to help you with networking.

  • it is correct that after bringing up a bridge your eth interface does not have ipv4 address anymore. Now it is a part of bridge and the ip should be attached to br0 interface.
  • check your bridge with brctl show. Here is mine:
bash-5.0# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.024274f6de62	no
br0		8000.b0d5cc1aa80c	no		eth0
							tap0
bash-5.0# ifconfig
br0       Link encap:Ethernet  HWaddr B0:D5:CC:1A:A8:0C  
          inet addr:192.168.1.199  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::861:44ff:fe0b:b2c8/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:91488 errors:0 dropped:1701 overruns:0 frame:0
          TX packets:84103 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:9862348 (9.4 MiB)  TX bytes:15960545 (15.2 MiB)

eth0      Link encap:Ethernet  HWaddr B0:D5:CC:1A:A8:0C  
          inet6 addr: fe80::b2d5:ccff:fe1a:a80c/64 Scope:Link
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
          RX packets:93672 errors:0 dropped:811 overruns:0 frame:0
          TX packets:87200 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:12007595 (11.4 MiB)  TX bytes:16390066 (15.6 MiB)
          Interrupt:55 
...

Also, check your routing table. In my case I use the same physical interface for both openvpn and default gateway.

bash-5.0# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 br0
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 tap0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 br0

As you can see, there isn't even eth0 listed in the routing table, all traffic is routed through bridge and multicasted to tap0 (openvpn) and eth0 (physical).

All in all, if you still have a problem grab some networking book and read about ethernet bridging.

Also, I've noticed that you start and stop main openvpn container openvpn_tap many times, in pt. 7 and 13, 14. This shouldn't be necessary and changing routing tables can lock you out if you don't have a proper shutting down configuration, I.e. tearing down the bridge end removing taps from it.

aktur avatar Jan 13 '21 09:01 aktur

@Mailblocker also do you have a firewall? Then you have to unblock br0 interface too.

aktur avatar Jan 13 '21 13:01 aktur

Hi everyone, I finally got the time to sit down and figure out what my problems were. I changed my setup slightly by using the ethernet adapter instead of the wifi interface.

I needed to take some additional steps to get it running on my laptop:

  1. Disable the wifi interface. I think my router got the route/connection wrong when running it at the same time as the bridge configuration.
  2. Disable DHCP for the ethernet interface. Before doing so my ethernet interface always got a new IP after setting up the bridge which was causing problems.
  3. As a client do not connect to the VPN from within the same network. This also caused troubles within my internal network, probably my router couldn't figure out where to send the packages to.
  4. Also I needed to alter my iptables on the server to enable forwarding for internet access of the clients connected to the VPN (good hint about the firewall, thanks!)

Currently I'm using the following script manually to enable the bridge setup:

#!/bin/bash

sudo systemctl disable dhcpcd.service
sudo ip addr delete 192.168.2.20/24 dev enp1s0

sudo brctl addbr br0

sudo brctl addif br0 enp1s0
sudo brctl addif br0 tap0

sudo dhcpcd br0

sudo iptables -A INPUT -i br0 -j ACCEPT
sudo iptables -A FORWARD -i br0 -j ACCEPT

ip addr
brctl show

The only thing I noticed is that the openvpn container does not allow creating user certificates when running in network_mode: "host". I get the error Cannot create container for service openvpn: conflicting options: host type networking can't be used with links. This would result in undefines behaviour.

But that's no problem. I don't need to add users often so I can just change the mode back and add users then.

@aktur I wanted to say thank you! Is there a wait I can donate you at least a coffee?

edit: One last question came up to my mind: Do I need the docker image of you aktur or does this setup also work with the original image? The bridge script would not be started when firing up the container but anything else?

Mailblocker avatar Feb 07 '21 16:02 Mailblocker

I'm glad to herar that it is working for you.

Adding users uses just easyrsa and it does not require any network connection to run. Perhaps you can tweak your docker compose command?

My (very old) website is https://mqlservice.net/ and the PayPal button is still there. Feel free to use it :-)

About which docker images you need: I'm using cheap ARM devices for may network bridging (Raspberry Pi and BeagleBoneBlack) therefore I need ARM docker images. The original image is for AMD64 arch only, mine are multiarch, so you can use the same docker run commands on each platform. There could be other small differences, as my syncing with forked repo github action fails (due to that it does not work on altered master branch and I should fix it, but had not yet time to do it for this hobby project ;) )

aktur avatar Feb 08 '21 09:02 aktur

Hello there, I've been trying to setup this within a docker network:

├── openvpn server
├── service1
├── service2
└── ...

so any clients connecting to the VPN will be able to see all containers within the docker virtual network.

But im getting these errors when starting the container:

root@localhost:~# docker logs -f ovpn 
2021-09-19 16:15:23 TUN/TAP device tap0 opened
2021-09-19 16:15:23 Persist state set to: ON
iptables: No chain/target/match by that name.
iptables: No chain/target/match by that name.
Enabling IPv6 Forwarding
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.forwarding = 1
net.ipv6.conf.all.forwarding = 1
Setup ethernet bridge...
/usr/local/bin/ovpn_run: line 109: ./bridge-start: Permission denied

this is the full script:

#!/bin/sh

fqdn=`cat /root/config/globals.json | jq -r .FQDN`

mkdir -p /root/ovpn-data
mkdir -p /root/ovpn-clients
cp client-add.sh /root
cp bridge-start /root/ovpn-data

docker run -v /root/ovpn-data:/etc/openvpn --rm aktur/openvpn ovpn_genconfig -u udp://$fqdn -t
docker run -v /root/ovpn-data:/etc/openvpn --rm -it aktur/openvpn ovpn_initpki nopass
# edit /root/ovpn-data/openvpn.conf 
docker run -v /root/ovpn-data:/etc/openvpn -d -p 1194:1194/udp --network=linet --cap-add=NET_ADMIN --name ovpn aktur/openvpn

k1r0s avatar Sep 19 '21 18:09 k1r0s

not work with bridge mode in k8s environment

sxyandapp avatar Mar 08 '22 09:03 sxyandapp