Skip to content

Commit

Permalink
Merge pull request bubuntux#2 from azinchen/master
Browse files Browse the repository at this point in the history
Utilize api.nordvpn.com and filter server list
  • Loading branch information
Julio Gutierrez authored Jun 16, 2018
2 parents dabaa62 + a12a0d5 commit d4d7a8d
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 76 deletions.
31 changes: 17 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
FROM alpine:3.7
FROM alpine:latest

LABEL maintainer="Julio Gutierrez <bubuntux@gmail.com>"
MAINTAINER Alexander Zinchenko <alexander@zinchenko.com>

COPY nordVpn.sh /usr/bin
CMD nordVpn.sh

HEALTHCHECK --start-period=5s --timeout=15s --interval=60s \
CMD curl -fL 'https://api.ipify.org' || exit 1
HEALTHCHECK --start-period=15s --timeout=15s --interval=60s \
CMD curl -fL 'https://api.ipify.org' || exit 1

ENV URL_NORDVPN_API="https://api.nordvpn.com/server" \
URL_RECOMMENDED_SERVERS="https://nordvpn.com/wp-admin/admin-ajax.php?action=servers_recommendations" \
URL_OVPN_FILES="https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip" \
MAX_LOAD=70

VOLUME ["/vpn/ovpn/"]

# Install dependencies
RUN apk --no-cache --no-progress upgrade && \
apk --no-cache --no-progress add curl unzip iptables ip6tables jq openvpn && \
# Download ovpn files
curl https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip -o /tmp/ovpn.zip && \
unzip -q /tmp/ovpn.zip -d /tmp/ovpn && \
mkdir -p /vpn/ovpn/ && \
mv /tmp/ovpn/*/*.ovpn /vpn/ovpn/ && \
# Cleanup
rm -rf /tmp/*
RUN apk --no-cache --no-progress update && \
apk --no-cache --no-progress upgrade && \
apk --no-cache --no-progress add bash curl unzip iptables ip6tables jq openvpn tini && \
mkdir -p /vpn/ovpn/

ENTRYPOINT ["/sbin/tini", "--", "/usr/bin/nordVpn.sh"]
31 changes: 17 additions & 14 deletions Dockerfile.armhf
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
FROM resin/armhf-alpine:3.7
FROM resin/armhf-alpine:latest

LABEL maintainer="Julio Gutierrez <bubuntux@gmail.com>"
MAINTAINER Alexander Zinchenko <alexander@zinchenko.com>

COPY nordVpn.sh /usr/bin
CMD nordVpn.sh

HEALTHCHECK --start-period=5s --timeout=15s --interval=60s \
CMD curl -fL 'https://api.ipify.org' || exit 1
HEALTHCHECK --start-period=15s --timeout=15s --interval=60s \
CMD curl -fL 'https://api.ipify.org' || exit 1

ENV URL_NORDVPN_API="https://api.nordvpn.com/server" \
URL_RECOMMENDED_SERVERS="https://nordvpn.com/wp-admin/admin-ajax.php?action=servers_recommendations" \
URL_OVPN_FILES="https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip" \
MAX_LOAD=70

VOLUME ["/vpn/ovpn/"]

RUN ["cross-build-start"]
# Install dependencies
RUN apk --no-cache --no-progress upgrade && \
apk --no-cache --no-progress add curl unzip iptables ip6tables jq openvpn && \
# Download ovpn files
curl https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip -o /tmp/ovpn.zip && \
unzip -q /tmp/ovpn.zip -d /tmp/ovpn && \
mkdir -p /vpn/ovpn/ && \
mv /tmp/ovpn/*/*.ovpn /vpn/ovpn/ && \
# Cleanup
rm -rf /tmp/*
RUN apk --no-cache --no-progress update && \
apk --no-cache --no-progress upgrade && \
apk --no-cache --no-progress add bash curl unzip iptables ip6tables jq openvpn tini && \
mkdir -p /vpn/ovpn/
RUN ["cross-build-end"]

ENTRYPOINT ["/sbin/tini", "--", "/usr/bin/nordVpn.sh"]
69 changes: 36 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,48 @@
[![logo](https://github.com/bubuntux/nordvpn/raw/master/NordVpn_logo.png)](https://ref.nordvpn.com/?id=171828599)
[![logo](https://github.com/azinchen/nordvpn/raw/master/NordVpn_logo.png)](https://www.nordvpn.com/)

# NordVpn
# NordVPN

This is an OpenVPN client docker container that use recommended Nordvpn servers. It makes routing containers'
traffic through OpenVPN easy.
This is an OpenVPN client docker container that use least loaded NordVPN servers. It makes routing containers' traffic through OpenVPN easy.

# What is OpenVPN?

OpenVPN is an open-source software application that implements virtual private
network (VPN) techniques for creating secure point-to-point or site-to-site
connections in routed or bridged configurations and remote access facilities.
It uses a custom security protocol that utilizes SSL/TLS for key exchange. It is
capable of traversing network address translators (NATs) and firewalls.
OpenVPN is an open-source software application that implements virtual private network (VPN) techniques for creating secure point-to-point or site-to-site connections in routed or bridged configurations and remote access facilities. It uses a custom security protocol that utilizes SSL/TLS for key exchange. It is capable of traversing network address translators (NATs) and firewalls.

# How to use this image

This container was designed to be started first to provide a connection
to other containers (using `--net=container:vpn`, see below *Starting an NordVpn
client instance*).
This container was designed to be started first to provide a connection to other containers (using `--net=container:vpn`, see below *Starting an NordVPN client instance*).

**NOTE**: More than the basic privileges are needed for NordVpn. With docker 1.2
or newer you can use the `--cap-add=NET_ADMIN` and `--device /dev/net/tun`
options. Earlier versions, or with fig, and you'll have to run it in privileged
mode.
**NOTE**: More than the basic privileges are needed for NordVPN. With docker 1.2 or newer you can use the `--cap-add=NET_ADMIN` and `--device /dev/net/tun` options. Earlier versions, or with fig, and you'll have to run it in privileged mode.

**NOTE 2**: If you need a template for using this container with
`docker-compose`, see the example
[file](https://github.com/dperson/openvpn-client/raw/master/docker-compose.yml).
**NOTE 2**: If you need a template for using this container with `docker-compose`, see the example [file](https://github.com/dperson/openvpn-client/raw/master/docker-compose.yml).

## Starting an NordVpn instance
## Starting an NordVPN instance

docker run -ti --cap-add=NET_ADMIN --device /dev/net/tun --name vpn \
-e [email protected] -e PASS=password -d bubuntux/nordvpn
docker run -ti --cap-add=NET_ADMIN --device /dev/net/tun --name vpn\
-e [email protected] -e PASS=password
-e COUNRTY=country1;country2 -e CATEGORY=category1;category2 \
-e PROTOCOL=protocol -d azinchen/nordvpn

Once it's up other containers can be started using it's network connection:

docker run -it --net=container:vpn -d some/docker-container

## Filter NordVPN servers

This container selects least loaded server from NordVPN pool. Server list can be filtered by setting `COUNTRY`, `CATEGORY` and/or `PROTOCOL` environment variables. If filtered list is empty, recommended server is selected.

## Local Network access to services connecting to the internet through the VPN.

The environmenta variable NETWORK must be your local network that you would connect to the server running the docker containers on. Running the following on your docker host should give you the correct network: `ip route | awk '!/ (docker0|br-)/ && /src/ {print $1}'`

docker run -ti --cap-add=NET_ADMIN --device /dev/net/tun --name vpn \
-p 8080:80 -e NETWORK=192.168.1.0/24 \
-e [email protected] -e PASS=password -d bubuntux/nordvpn
-e [email protected] -e PASS=password -d azinchen/nordvpn

Now just create the second container _without_ the `-p` parameter, only inlcude the `--net=container:vpn`, the port should be declare in the vpn container.

docker run -ti --rm --net=container:vpn -d bubuntux/riot-web

now the service provided by the second container would be available from the host machine (http://localhost:8080) or anywhere inside the local network (http://192.168.1.xxx:8080).

## Local Network access to services connecting to the internet through the VPN using a Web proxy.
Expand All @@ -57,9 +51,7 @@ now the service provided by the second container would be available from the hos
--link vpn:<service_name> -d dperson/nginx \
-w "http://<service_name>:<PORT>/<URI>;/<PATH>"

Which will start a Nginx web server on local ports 80 and 443, and proxy any
requests under `/<PATH>` to the to `http://<service_name>:<PORT>/<URI>`. To use
a concrete example:
Which will start a Nginx web server on local ports 80 and 443, and proxy any requests under `/<PATH>` to the to `http://<service_name>:<PORT>/<URI>`. To use a concrete example:

docker run -it --name bit --net=container:vpn -d bubundut/nordvpn
docker run -it --name web -p 80:80 -p 443:443 --link vpn:bit \
Expand All @@ -76,12 +68,23 @@ For multiple services (non-existant 'foo' used as an example):

ENVIRONMENT VARIABLES (only available with `docker run`)

* `USER` - User for NordVpn account.
* `PASS` - Password for NordVpn account.
* `NETWORK` - CIDR network (IE 192.168.1.0/24), add a route to allows replies once the VPN is up
* `NETWORK6` - CIDR IPv6 network (IE fe00:d34d:b33f::/64), add a route to allows replies once the VPN is up
* `COUNTRY` - Use servers from countries in the list (IE Australia;New Zeland). Several countries can be selected using semicolon.
* `CATEGORY` - Use servers from specific categories (IE P2P;Anti DDoS). Several categories can be selected using semicolon. Allowed categories are:
* `Anti DDoS`
* `Dedicated IP servers`
* `Double VPN`
* `Obfuscated Servers`
* `Onion Over VPN`
* `P2P`
* `Standard VPN servers`
* `PROTOCOL` - Specify OpenVPN protocol. Only one protocol can be selected. Allowed protocols are:
* `openvpn_udp`
* `openvpn_tcp`
* `USER` - User for NordVPN account.
* `PASS` - Password for NordVPN account.
* `NETWORK` - CIDR network (IE 192.168.1.0/24), add a route to allows replies once the VPN is up.
* `NETWORK6` - CIDR IPv6 network (IE fe00:d34d:b33f::/64), add a route to allows replies once the VPN is up.

## Issues

If you have any problems with or questions about this image, please contact me
through a [GitHub issue](https://github.com/bubuntux/nordvpn/issues).
If you have any problems with or questions about this image, please contact me through a [GitHub issue](https://github.com/azinchen/nordvpn/issues) or [email](mailto:[email protected]).
148 changes: 133 additions & 15 deletions nordVpn.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash

# Firewall everything has to go through the vpn
iptables -F OUTPUT
Expand All @@ -19,8 +19,10 @@ iptables -A OUTPUT -o eth0 -p udp --dport 1194 -j ACCEPT
ip6tables -A OUTPUT -o eth0 -p udp --dport 1194 -j ACCEPT 2> /dev/null
iptables -A OUTPUT -o eth0 -p tcp --dport 1194 -j ACCEPT
ip6tables -A OUTPUT -o eth0 -p tcp --dport 1194 -j ACCEPT 2> /dev/null
iptables -A OUTPUT -o eth0 -d nordvpn.com -j ACCEPT
ip6tables -A OUTPUT -o eth0 -d nordvpn.com -j ACCEPT 2> /dev/null

iptables_domain=`echo $URL_NORDVPN_API | awk -F/ '{print $3}'`
iptables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT
ip6tables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT 2> /dev/null

if [ ! -z $NETWORK ]; then
gw=`ip route | awk '/default/ {print $3}'`
Expand All @@ -38,27 +40,143 @@ base_dir="/vpn"
ovpn_dir="$base_dir/ovpn"
auth_file="$base_dir/auth"

# Get NordVpn server recomendations
recomendations=`curl -s https://nordvpn.com/wp-admin/admin-ajax.php?action=servers_recommendations |\
jq -r '.[] | .hostname' | shuf`
if [ `ls -A $ovpn_dir | wc -l` -eq 0 ]
then
echo "Server configs not found. Download configs from NordVPN"
iptables_domain=`echo $URL_OVPN_FILES | awk -F/ '{print $3}'`
iptables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT
ip6tables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT 2> /dev/null
curl -s $URL_OVPN_FILES -o /tmp/ovpn.zip
unzip -q /tmp/ovpn.zip -d /tmp/ovpn
mv /tmp/ovpn/*/*.ovpn $ovpn_dir
rm -rf /tmp/*
fi

# Use api.nordvpn.com
servers=`curl -s $URL_NORDVPN_API`
servers=`echo $servers | jq -c '.[] | select(.features.openvpn_udp == true)' &&\
echo $servers | jq -c '.[] | select(.features.openvpn_tcp == true)'`
servers=`echo $servers | jq -s -a -c 'unique'`
pool_length=`echo $servers | jq 'length'`
echo "OpenVPN servers in pool: $pool_length"
servers=`echo $servers | jq -c '.[]'`

IFS=';'

if [[ !($pool_length -eq 0) ]]; then
if [[ -z "${COUNTRY}" ]]; then
echo "Country not set, skip filtering"
else
echo "Filter pool by country: $COUNTRY"
read -ra countries <<< "$COUNTRY"
for country in "${countries[@]}"; do
filtered="$filtered"`echo $servers | jq -c 'select(.country == "'$country'")'`
done
filtered=`echo $filtered | jq -s -a -c 'unique'`
pool_length=`echo $filtered | jq 'length'`
echo "Servers in filtered pool: $pool_length"
servers=`echo $filtered | jq -c '.[]'`
fi
fi

if [[ !($pool_length -eq 0) ]]; then
if [[ -z "${CATEGORY}" ]]; then
echo "Category not set, skip filtering"
else
echo "Filter pool by category: $CATEGORY"
read -ra categories <<< "$CATEGORY"
filtered="$servers"
for category in "${categories[@]}"; do
filtered=`echo $filtered | jq -c 'select(.categories[].name == "'$category'")'`
done
filtered=`echo $filtered | jq -s -a -c 'unique'`
pool_length=`echo $filtered | jq 'length'`
echo "Servers in filtered pool: $pool_length"
servers=`echo $filtered | jq -c '.[]'`
fi
fi

if [[ !($pool_length -eq 0) ]]; then
if [[ -z "${PROTOCOL}" ]]; then
echo "Protocol not set, skip filtering"
else
echo "Filter pool by protocol: $PROTOCOL"
filtered=`echo $servers | jq -c 'select(.features.'$PROTOCOL' == true)' | jq -s -a -c 'unique'`
pool_length=`echo $filtered | jq 'length'`
echo "Servers in filtered pool: $pool_length"
servers=`echo $filtered | jq -c '.[]'`
fi
fi

if [[ !($pool_length -eq 0) ]]; then
echo "Filter pool by load, less than $MAX_LOAD%"
servers=`echo $servers | jq -c 'select(.load <= '$MAX_LOAD')'`
pool_length=`echo $servers | jq -s -a -c 'unique' | jq 'length'`
echo "Servers in filtered pool: $pool_length"
servers=`echo $servers | jq -s -c 'sort_by(.load)[]'`
fi

if [[ !($pool_length -eq 0) ]]; then
echo "--- Top 20 servers in filtered pool ---"
echo `echo $servers | jq -r '"\(.domain) \(.load)%"' | head -n 20`
echo "---------------------------------------"
fi

servers=`echo $servers | jq -r '.domain'`
IFS=$'\n'
read -ra filtered <<< "$servers"

for recomendation in ${recomendations}; do # Prefer UDP
config_file="${ovpn_dir}/${recomendation}.udp.ovpn"
if [ -r "$config_file" ]; then
config="$config_file"
break
for server in "${filtered[@]}"; do
if [[ -z "${PROTOCOL}" ]] || [[ "${PROTOCOL}" == "openvpn_udp" ]]; then
config_file="${ovpn_dir}/${server}.udp.ovpn"
if [ -r "$config_file" ]; then
config="$config_file"
break
else
echo "UDP config for server $server not found"
fi
fi
if [[ -z "${PROTOCOL}" ]] || [[ "${PROTOCOL}" == "openvpn_tcp" ]]; then
config_file="${ovpn_dir}/${server}.tcp.ovpn"
if [ -r "$config_file" ]; then
config="$config_file"
break
else
echo "TCP config for server $server not found"
fi
fi
done
if [ -z $config ]; then # Use TCP if UDP not available
for recomendation in ${recomendations}; do
config_file="${ovpn_dir}/${recomendation}.tcp.ovpn"

if [ -z $config ]; then
echo "Filtered pool is empty or configs not found. Select server from recommended list"
iptables_domain=`echo $URL_RECOMMENDED_SERVERS | awk -F/ '{print $3}'`
iptables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT
ip6tables -A OUTPUT -o eth0 -d $iptables_domain -j ACCEPT 2> /dev/null
recommendations=`curl -s $URL_RECOMMENDED_SERVERS | jq -r '.[] | .hostname' | shuf`
for server in ${recommendations}; do # Prefer UDP
config_file="${ovpn_dir}/${server}.udp.ovpn"
if [ -r "$config_file" ]; then
config="$config_file"
break
else
echo "UDP config for server $server not found"
fi
done
if [ -z $config ]; then # Use TCP if UDP not available
for server in ${recommendations}; do
config_file="${ovpn_dir}/${server}.tcp.ovpn"
if [ -r "$config_file" ]; then
config="$config_file"
break
else
echo "TCP config for server $server not found"
fi
done
fi
fi
if [ -z $config ]; then # If recomendation was not found, use a random server

if [ -z $config ]; then
echo "List of recommended servers is empty or configs not found. Select random server from available configs."
config="${ovpn_dir}/`ls ${ovpn_dir} | shuf -n 1`"
fi

Expand Down

0 comments on commit d4d7a8d

Please sign in to comment.