Skip to main content

Use VPN in a Docker Container

ยท 8 min read
Dominik Nowak
caution

Most of the content from this post was migrated to our Docker Platform tutorial. Content left here will be getting gradually more and more outdated.

Encapsulating software within a container brings a lot of benefits, such as quicker deployment, easier development and - last but not least - isolation of your host system from the application.

In this blog post I will show you how to install and configure a VPN client directly inside a docker container without a need of installing anything on your host system.

Thanks to that other computers from a VPN network will have access only to that container and not to your host system!

Because a container has it's own VPN IPv6 network, you can also easily move that container to other hosts without changing anything in your system configuration.

tip

If you want to connect your existing multi-container system over the internet, then using a separate Docker VPN container will be a better move.

We cover that topic in a blog post introducing Docker VPN sidecar container.

I will show you how to do that in a few easy steps...

Connect over VPN network to a docker container directly to gain SSH access to the docker container over the internet

Aboutโ€‹

This example is really basic - we host a simple, static website with Nginx.

You can however threat this example as a reference how to install a VPN client inside a container for your own more, sophisticated projects.

Host System Requirements

The steps mentioned in the following part of the article were tested on the following host system configuration:

  Operating System: Ubuntu 20.04.1 LTS
Kernel: Linux 5.4.0-62-generic
Architecture: x86-64
Docker version: 19.03.8, build afacb8b7f0

If you run your container inside a Virtual Machine, or VPS, make sure your host system has Nested Virtualization enabled.

Eg. on Virtual Box you need to run:

$ VBoxManage modifyvm your-vm-name --nested-hw-virt on

The project is avaialble on GitHub.

Basically, it consists of three files listed bellow:

docker-vpn/Dockerfile
FROM ubuntu:20.10

# install Husarnet client
RUN apt update -y && \
apt install -y curl && \
apt install -y gnupg2 && \
apt install -y systemd && \
curl https://install.husarnet.com/install.sh | bash

RUN update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

# install webserver service
RUN apt install -y nginx

# some optional modules
RUN apt install -y vim
RUN apt install -y iputils-ping

# Find your JOINCODE at https://app.husarnet.com
ENV JOINCODE=""
ENV HOSTNAME=my-container-1

# HTTP PORT
EXPOSE 80

# copy project files into the image
COPY init-container.sh /opt
COPY src /var/www/html/

# initialize a container
CMD /opt/init-container.sh

You could notice that init-container.sh seems to be long, however mainly due to a rich terminal log. The same result might be achieved in much shorter, but less informative way:

init-vpn-minimal.sh
# enable IPv6 needed by lower layers of VPN
sysctl net.ipv6.conf.lo.disable_ipv6=0

# start a daemon
husarnet daemon > /dev/null 2>&1 &

# delay to make sure VPN daemon started
sleep 5

# join to VPN network using its unique Join Code
husarnet join ${JOINCODE} ${HOSTNAME}

In the last line we provide hostname for a container (whatever you like), and a Join Code - kind of unique access token for a VPN network. In the next sections I will show you where to find it.

After short intro, let's get started!

Running an exampleโ€‹

Getting the codeโ€‹

git clone https://github.com/husarnet/blog-examples.git
cd blog-examples/docker-vpn/

Building an imageโ€‹

Make sure init-container.sh is executable. If not:

sudo chmod +x init-container.sh

Then build an image:

sudo docker build -t docker-vpn .

Starting a containerโ€‹

Execute in a Linux terminal:

sudo docker run --rm -it \
--env HOSTNAME='docker-vpn-1' \
--env JOINCODE='fc94:b01d:1803:8dd8:3333:2222:1234:1111/xxxxxxxxxxxxxxxxx' \
-v docker-vpn-v:/var/lib/husarnet \
-v /dev/net/tun:/dev/net/tun \
--cap-add NET_ADMIN \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
docker-vpn

description:

  • HOSTNAME='docker-vpn-1' - is an easy to use hostname, that you can use instead of Husarnet IPv6 addr to access your container over the internet
  • JOINCODE='fc94:b01d:1803:8dd8:3333:2222:1234:1111/xxxxxxxxxxxxxxxxx' - is an unique Join Code from your Husarnet network.
Get Your VPN Join Code

You will find it at:

https://app.husarnet.com -> choosen network -> [Add element] button -> join code tab

Find your VPN Join Code

  • -v my-container-1-v:/var/lib/husarnet - you need to make /var/lib/husarnet as a volume to preserve it's state for example if you would like to update the image your container is based on. If you would like to run multiple containers on your host machine remember to provide unique volume name for each container (in our case HOSTNAME-v).

If you also want to modify index.html file in your IDE, and see changes in your container withour re-executing docker build command, create a bind mount by adding also this flag in the docker run command:

-v "/home/blog-examples/docker-vpn/src:/var/www/html/:ro" \

remember to provide a full path to your src folder!

Resultsโ€‹

After running a container you should see a log like this:

blog-examples/docker-vpn$ sudo docker run --rm -it \
> --env HOSTNAME='docker-vpn-1' \
> --env JOINCODE='fc94:b01d:1803:8dd8:b293:5c7d:7639:932a/xxxxxxxxxxxxxxxxxxxxxx' \
> -v docker-vpn-v:/var/lib/husarnet \
> -v "/home/blog-examples/docker-vpn/src:/var/www/html/:ro" \
> -v /dev/net/tun:/dev/net/tun \
> --cap-add NET_ADMIN \
> --sysctl net.ipv6.conf.all.disable_ipv6=0 \
> docker-vpn

โณ [1/2] Initializing Husarnet Client:
waiting...
waiting...
waiting...
waiting...
success

๐Ÿ”ฅ [2/2] Connecting to Husarnet network as "docker-vpn-1":
[101617015] joining...
[101619016] joining...
done

*******************************************
๐Ÿ’ก Tip
To access a webserver visit:
๐Ÿ‘‰ http://[fc94:5e70:7ab8:5880:79d6:119d:c65e:fd3f]:80 ๐Ÿ‘ˆ
in your web browser ๐Ÿ’ป
*******************************************

root@3fb1b9a13cba:/#

At this point any computer that is in the same Husarnet VPN network as your container, can access a webserver by using docker-vpn-1 hostname or by using a containers IPv6 address as described in a log output from a container above.

You should see something like this ๐Ÿ‘‡

a webserver running in a docker over vpn

Summaryโ€‹

I presented in this article:

  • how to write a Dockerfile for creating a container image with pre-installed VPN client
  • how to run a simple website with external access only for computers from the same VPN network

I hope you will find this article helpful.

In case of any questions related to this blog post please contact us using Husarnet Community Forum or via email at support@husarnet.com.

Read also ...โ€‹