Gluetun VPN client

Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, Mullvad and Windscribe VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and Tinyproxy

ANNOUNCEMENT: Support for Windscribe

Build status Docker Pulls Docker Stars

GitHub last commit GitHub commit activity GitHub issues

Image size Image version Join Slack channel

Click to show base components

Features

  • Based on Alpine 3.11 for a small Docker image below 50MB
  • Supports Private Internet Access, Mullvad and Windscribe servers
  • DNS over TLS baked in with service provider(s) of your choice
  • DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses
  • Choose the vpn network protocol, udp or tcp
  • Built in firewall kill switch to allow traffic only with needed PIA servers and LAN devices
  • Built in SOCKS5 proxy (Shadowsocks, tunnels TCP+UDP)
  • Built in HTTP proxy (Tinyproxy, tunnels TCP)
  • Connect other containers to it
  • Connect LAN devices to it
  • Compatible with amd64, i686 (32 bit), ARM 64 bit, ARM 32 bit v6 and v7 🎆

Private Internet Access

  • Pick the region
  • Pick the level of encryption
  • Enable port forwarding

Mullvad

Windscribe

Extra niche features

  • Possibility of split horizon DNS by selecting multiple DNS over TLS providers
  • Subprograms all drop root privileges once launched
  • Subprograms output streams are all merged together
  • Can work as a Kubernetes sidecar container, thanks @rorph

Setup

  1. Requirements

    • Docker 1.13, in order to have Docker API 1.25 which supports init (and, if you use docker-compose, docker-compose version 1.22.0)

    • A Private Internet Access username and password (sign up) or Mullvad user ID (sign up)

    • External firewall requirements, if you have one

      • At start only
        • Allow outbound TCP 443 to github.com
        • If DOT=on, allow outbound TCP 853 to allow Unbound to resolve github.com and the PIA subdomain name if you use PIA.
        • If DOT=off and VPNSP=pia, allow outbound UDP 53 to your DNS provider to resolve the PIA subdomain name.
      • If VPNSP=pia, PIA_ENCRYPTION=strong and PROTOCOL=udp: allow outbound UDP 1197 to the corresponding VPN server IPs
      • If VPNSP=pia, PIA_ENCRYPTION=normal and PROTOCOL=udp: allow outbound UDP 1198 to the corresponding VPN server IPs
      • If VPNSP=pia, PIA_ENCRYPTION=strong and PROTOCOL=tcp: allow outbound TCP 501 to the corresponding VPN server IPs
      • If VPNSP=pia, PIA_ENCRYPTION=normal and PROTOCOL=tcp: allow outbound TCP 502 to the corresponding VPN server IPs
      • If VPNSP=mullvad and PORT=, please refer to the mapping of Mullvad servers in these source code lines to find the corresponding UDP port number and IP address(es) of your choice
      • If VPNSP=mullvad and PORT=53, allow outbound UDP 53 to the corresponding VPN server IPs, which you can fine in the mapping of Mullvad servers
      • If VPNSP=mullvad and PORT=80, allow outbound TCP 80 to the corresponding VPN server IPs, which you can fine in the mapping of Mullvad servers
      • If VPNSP=mullvad and PORT=443, allow outbound TCP 443 to the corresponding VPN server IPs, which you can fine in the mapping of Mullvad servers
      • If VPNSP=windscribe and PROTOCOL=udp: allow outbound UDP 443 to the corresponding VPN server IPs
      • If VPNSP=windscribe and PROTOCOL=tcp: allow outbound TCP 1194 to the corresponding VPN server IPs
      • If SHADOWSOCKS=on, allow inbound TCP 8388 and UDP 8388 from your LAN
      • If TINYPROXY=on, allow inbound TCP 8888 from your LAN

  2. Launch the container with:

    docker run -d --init --name=pia --cap-add=NET_ADMIN \
    -e REGION="CA Montreal" -e USER=js89ds7 -e PASSWORD=8fd9s239G \
    qmcgaw/private-internet-access
    

    or use docker-compose.yml with:

    docker-compose up -d
    

    Note that you can:

    • Change the many environment variables available
    • Use -p 8888:8888/tcp to access the HTTP web proxy (and put your LAN in EXTRA_SUBNETS environment variable)
    • Use -p 8388:8388/tcp -p 8388:8388/udp to access the SOCKS5 proxy (and put your LAN in EXTRA_SUBNETS environment variable)
    • Pass additional arguments to openvpn using Docker's command function (commands after the image name)
  3. You can update the image with docker pull qmcgaw/private-internet-access:latest. There are also docker tags for older versions available:

    • qmcgaw/private-internet-access:v2 linked to the v2 release (Golang based, only PIA)
    • qmcgaw/private-internet-access:v1 linked to the v1 release (shell scripting based, no support, only PIA)
    • qmcgaw/private-internet-access:old tag, which is the latest shell scripting version (shell scripting based, no support, only PIA)

Testing

Check the PIA IP address matches your expectations

docker run --rm --network=container:pia alpine:3.11 wget -qO- https://ipinfo.io

Environment variables

Note: VPNSP means VPN service provider

Environment variable Default Properties PIA Mullvad Windscribe Description Choices
VPNSP pia VPN Service Provider pia, mullvad, windscribe
REGION Austria VPN server region One of the PIA regions or of the Windscribe regions
COUNTRY Sweden Optional VPN server country One of the Mullvad countries
CITY Optional VPN server city One of the Mullvad cities
ISP Optional VPN server ISP One of the Mullvad ISP
PORT Optional Custom VPN port to use Mullvad: 80 or 443 for TCP; or 53 for UDP. Leave blank for default Mullvad server port. Windscribe see this list of ports
PROTOCOL udp Network protocol to use tcp, udp
ENCRYPTION strong Encryption preset normal, strong
USER To fill PIA/Windscribe username or Mullvad user ID
PASSWORD To fill PIA/Windscribe password
DOT on Activate DNS over TLS on, off
DOT_PROVIDERS cloudflare Comma delimited list of DNS over TLS providers cloudflare, google, quad9, quadrant, cleanbrowsing, securedns, libredns
DOT_CACHING on DNS over TLS Unbound caching on, off
DOT_IPV6 on DNS over TLS IPv6 resolution on, off
DOT_PRIVATE_ADDRESS All private CIDRs ranges Comma separated list of CIDRs or single IP addresses Unbound won't resolve to. Note that the default setting prevents DNS rebinding
DOT_VERBOSITY 1 DNS over TLS Unbound verbosity level 0, 1, 2, 3, 4, 5
DOT_VERBOSITY_DETAILS 0 Unbound details verbosity level 0, 1, 2, 3, 4
DOT_VALIDATION_LOGLEVEL 0 Unbound validation log level 0, 1, 2
BLOCK_MALICIOUS on Block malicious hostnames and IPs with Unbound DNS over TLS on, off
BLOCK_SURVEILLANCE off Block surveillance hostnames and IPs with Unbound DNS over TLS on, off
BLOCK_ADS off Block ads hostnames and IPs with Unbound DNS over TLS on, off
UNBLOCK Optional Comma separated list of domain names to leave unblocked In example domain1.com,x.domain2.co.uk
EXTRA_SUBNETS Optional Comma separated subnets allowed in the container firewall In example 192.168.1.0/24,192.168.10.121,10.0.0.5/28
PORT_FORWARDING off Enable port forwarding on the VPN server on, off
PORT_FORWARDING_STATUS_FILE /forwarded_port File path to store the forwarded port number Any valid file path
TINYPROXY off Enable the internal HTTP proxy tinyproxy on, off
TINYPROXY_LOG Info Tinyproxy log level Info, Connect, Notice, Warning, Error, Critical
TINYPROXY_PORT 8888 Internal port number for Tinyproxy to listen on 1024 to 65535
TINYPROXY_USER Username to use to connect to the HTTP proxy
TINYPROXY_PASSWORD Password to use to connect to the HTTP proxy
SHADOWSOCKS off Enable the internal SOCKS5 proxy Shadowsocks on, off
SHADOWSOCKS_LOG off Enable Shadowsocks logging on, off
SHADOWSOCKS_PORT 8388 Internal port number for Shadowsocks to listen on 1024 to 65535
SHADOWSOCKS_PASSWORD Passsword to use to connect to the SOCKS5 proxy
SHADOWSOCKS_METHOD chacha20-ietf-poly1305 Method to use for Shadowsocks One of these ciphers
TZ Optional Specify a timezone to use In example Europe/London
OPENVPN_VERBOSITY 1 Openvpn verbosity level 0, 1, 2, 3, 4, 5, 6
OPENVPN_ROOT no Run OpenVPN as root yes, no
OPENVPN_TARGET_IP Optional Specify a target VPN server IP address to use In example 199.65.55.100
OPENVPN_CIPHER Optional Specify a custom cipher to use. It will also set ncp-disable if using AES GCM for PIA In example aes-256-gcm
OPENVPN_AUTH Optional Specify a custom auth algorithm to use In example sha256
UID 1000 User ID to run as non root and for ownership of files written
GID 1000 Group ID to run as non root and for ownership of files written

Connect to it

There are various ways to achieve this, depending on your use case.

  • Connect containers in the same docker-compose.yml as PIA

    Add network_mode: "service:pia" to your docker-compose.yml (no need for depends_on)

  • Connect other containers to PIA

    Add --network=container:pia when launching the container, provided PIA is already running

  • Connect containers from another docker-compose.yml

    Add network_mode: "container:pia" to your docker-compose.yml, provided PIA is already running

  • Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)

    You might want to use Shadowsocks instead which tunnels UDP as well as TCP, whereas Tinyproxy only tunnels TCP.

    1. Setup a HTTP proxy client, such as SwitchyOmega for Chrome
    2. Ensure the PIA container is launched with:
      • port 8888 published -p 8888:8888/tcp
      • your LAN subnet, i.e. 192.168.1.0/24, set as -e EXTRA_SUBNETS=192.168.1.0/24
    3. With your HTTP proxy client, connect to the Docker host (i.e. 192.168.1.10) on port 8888. You need to enter your credentials if you set them with TINYPROXY_USER and TINYPROXY_PASSWORD.
    4. If you set TINYPROXY_LOG to Info, more information will be logged in the Docker logs

  • Connect LAN devices through the built-in SOCKS5 proxy *Shadowsocks* (per app, system wide, etc.)

    1. Setup a SOCKS5 proxy client, there is a list of ShadowSocks clients for all platforms
      • note some clients do not tunnel UDP so your DNS queries will be done locally and not through PIA and its built in DNS over TLS
      • Clients that support such UDP tunneling are, as far as I know:
        • iOS: Potatso Lite
        • OSX: ShadowsocksX
        • Android: Shadowsocks by Max Lv
    2. Ensure the PIA container is launched with:
      • port 8388 published -p 8388:8388/tcp -p 8388:8388/udp
      • your LAN subnet, i.e. 192.168.1.0/24, set as -e EXTRA_SUBNETS=192.168.1.0/24
    3. With your SOCKS5 proxy client
      • Enter the Docker host (i.e. 192.168.1.10) as the server IP
      • Enter port TCP (and UDP, if available) 8388 as the server port
      • Use the password you have set with SHADOWSOCKS_PASSWORD
      • Choose the encryption method/algorithm to the method you specified in SHADOWSOCKS_METHOD
    4. If you set SHADOWSOCKS_LOG to on, (a lot) more information will be logged in the Docker logs

  • Access ports of containers connected to PIA

    In example, to access port 8000 of container xyz and 9000 of container abc connected to PIA, publish ports 8000 and 9000 for the PIA container and access them as you would with any other container

  • Access ports of containers connected to PIA, all in the same docker-compose.yml

    In example, to access port 8000 of container xyz and 9000 of container abc connected to PIA, publish port 8000 and 9000 for the PIA container. The docker-compose.yml file would look like:

    version: '3.7'
    services:
      pia:
        image: qmcgaw/private-internet-access
        container_name: pia
        init: true
        cap_add:
          - NET_ADMIN
        environment:
          - USER=js89ds7
          - PASSWORD=8fd9s239G
        ports:
          - 8000:8000/tcp
          - 9000:9000/tcp
      abc:
        image: abc
        container_name: abc
        network_mode: "service:pia"
      xyz:
        image: xyz
        container_name: xyz
        network_mode: "service:pia"
    

Private Internet Access port forwarding

Note that not all regions support port forwarding.

When PORT_FORWARDING=on, a port will be forwarded on the PIA server side and written to the file specified by PORT_FORWARDING_STATUS_FILE=/forwarded_port.

It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client.

FAQ

Openvpn disconnects because of a ping timeout

It happens especially on some PIA servers where they change their configuration or the server goes offline.

You will obtain an error similar to:

openvpn: Wed Mar 18 22:13:00 2020 [3a51ae90324bcb0719cb399b650c64d4] Inactivity timeout (--ping-restart), restarting,
openvpn: Wed Mar 18 22:13:00 2020 SIGUSR1[soft,ping-restart] received, process restarting,
...
openvpn: Wed Mar 18 22:13:17 2020 Preserving previous TUN/TAP instance: tun0,
openvpn: Wed Mar 18 22:13:17 2020 NOTE: Pulled options changed on restart, will need to close and reopen TUN/TAP device.,
openvpn: Wed Mar 18 22:13:17 2020 ERROR: Linux route delete command failed: external program exited with error status: 2,
openvpn: Wed Mar 18 22:13:17 2020 ERROR: Linux route delete command failed: external program exited with error status: 2,
openvpn: Wed Mar 18 22:13:17 2020 ERROR: Linux route delete command failed: external program exited with error status: 2,
openvpn: Wed Mar 18 22:13:17 2020 ERROR: Linux route delete command failed: external program exited with error status: 2,
openvpn: Wed Mar 18 22:13:17 2020 /sbin/ip addr del dev tun0 local 10.6.11.6 peer 10.6.11.5,
openvpn: Wed Mar 18 22:13:17 2020 Linux ip addr del failed: external program exited with error status: 2,
openvpn: Wed Mar 18 22:13:18 2020 ERROR: Cannot ioctl TUNSETIFF tun: Operation not permitted (errno=1),
openvpn: Wed Mar 18 22:13:18 2020 Exiting due to fatal error,
exit status 1

To fix it, you would have to run openvpn with root, by setting the environment variable OPENVPN_ROOT=yes.

Private Internet Access: Why do I see openvpn warnings at start?

You might see some warnings similar to:

openvpn: Sat Feb 22 15:55:02 2020 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'link-mtu' is used inconsistently, local='link-mtu 1569', remote='link-mtu 1542'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'cipher' is used inconsistently, local='cipher AES-256-CBC', remote='cipher BF-CBC'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'auth' is used inconsistently, local='auth SHA256', remote='auth SHA1'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'keysize' is used inconsistently, local='keysize 256', remote='keysize 128'
openvpn: Sat Feb 22 15:55:02 2020 WARNING: 'comp-lzo' is present in remote config but missing in local config, remote='comp-lzo'
openvpn: Sat Feb 22 15:55:02 2020 [a121ce520d670b71bfd3aa475485539b] Peer Connection Initiated with [AF_INET]xx.xx.xx.xx:1197

It is mainly because the option disable-occ was removed for transparency with you.

Private Internet Access explains here why the warnings show up.

What files does it download at start before tunneling?

At start, the Go entrypoint only downloads, depending on your settings:

How to build Docker images of older or alternate versions

First, install Git.

The following will build the Docker image locally and replace the previous one you built or pulled.

  • Build the latest image

    docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git
    
  • Find a commit you want to build for, in example 095623925a9cc0e5cf89d5b9b510714792267d9b, then:

    docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git#095623925a9cc0e5cf89d5b9b510714792267d9b
    
  • Find a branch you want to build for, in example mullvad, then:

    docker build -t qmcgaw/private-internet-access https://github.com/qdm12/private-internet-access-docker.git#mullvad
    

Mullvad does not work with IPv6?

By default, the Mullvad server tunnels both ipv4 and ipv6, hence openvpn will try to create an ipv6 route. To allow the container to create such route, you have to specify net.ipv6.conf.all.disable_ipv6=0 at runtime, using either:

  • For a Docker run command, the flag: --sysctl net.ipv6.conf.all.disable_ipv6=0

  • In a docker-compose file:

        sysctls:
          - net.ipv6.conf.all.disable_ipv6=0
    

What's all this Go code?

The Go code is a big rewrite of the previous shell entrypoint, it allows for:

  • better testing
  • better maintainability
  • ease of implementing new features
  • faster boot
  • asynchronous/parallel operations

It is mostly made of the internal directory and the entry Go file cmd/main.go.

How to test DNS over TLS?

How to fix OpenVPN failing to start?

You can try:

  • Installing the tun kernel module on your host with insmod /lib/modules/tun.ko or modprobe tun
  • Adding --device=/dev/net/tun to your docker run command (equivalent for docker-compose, kubernetes, etc.)

Development

  1. Setup your environment

    Using VSCode and Docker

    1. Install Docker
      • On Windows, share a drive with Docker Desktop and have the project on that partition
      • On OSX, share your project directory with Docker Desktop
    2. With Visual Studio Code, install the remote containers extension
    3. In Visual Studio Code, press on F1 and select Remote-Containers: Open Folder in Container...
    4. Your dev environment is ready to go!... and it's running in a container 👍

    Locally

    Install Go, Docker and Git; then:

    go mod download
    go get github.com/golang/mock/gomock
    go get github.com/golang/mock/mockgen
    

    And finally install golangci-lint

  2. Commands available:

    # Build the entrypoint binary
    go build cmd/main.go
    # Test the entrypoint code
    go test ./...
    # Lint the code
    golangci-lint run
    # Build the Docker image
    docker build -t qmcgaw/private-internet-access .
    
  3. The Go code is in the Go file cmd/main.go and the internal directory, you might want to start reading the main.go file.

  4. See Contributing for more information on how to contribute to this repository.

Contributors

Thanks for all the contributions, whether small or not so small!

TODOs

Expand me

  • Gotify support for notificactions
  • Periodic update of malicious block lists with Unbound restart
  • Improve healthcheck
    • Check IP address belongs to selected region
    • Check for DNS provider somehow if this is even possible
  • Support for other VPN protocols
    • Wireguard (wireguard-go)
  • Show new versions/commits available at start
  • Colors & emojis
    • Setup
    • Logging streams
  • More unit tests
  • Write in Go

License

This repository is under an MIT license

Description
VPN client in a thin Docker container for multiple VPN providers, written in Go, and using OpenVPN or Wireguard, DNS over TLS, with a few proxy servers built-in.
Readme MIT 33 MiB
Languages
Go 99.4%
Dockerfile 0.6%