Restarts on fail; DNS over TLS only when connected to VPN; readme update

This commit is contained in:
Quentin McGaw
2018-09-21 16:39:08 +02:00
parent 6929947611
commit 2b7c7cc62a
3 changed files with 71 additions and 59 deletions

View File

@@ -1,9 +1,9 @@
FROM alpine:3.8 FROM alpine:3.8
LABEL maintainer="quentin.mcgaw@gmail.com" \ LABEL maintainer="quentin.mcgaw@gmail.com" \
description="VPN client to private internet access servers using OpenVPN, Alpine and IPtables firewall" \ description="VPN client to private internet access servers using OpenVPN, IPtables firewall, DNS over TLS with Unbound and Alpine Linux" \
download="5.7MB" \ download="???MB" \
size="8.94MB" \ size="15.7MB" \
ram="11MB" \ ram="13MB" \
cpu_usage="Low" \ cpu_usage="Low" \
github="https://github.com/qdm12/private-internet-access-docker" github="https://github.com/qdm12/private-internet-access-docker"
HEALTHCHECK --interval=1m --timeout=10s --start-period=10s --retries=1 \ HEALTHCHECK --interval=1m --timeout=10s --start-period=10s --retries=1 \

View File

@@ -1,6 +1,6 @@
# Private Internet Access Client (OpenVPN on Alpine Linux) # Private Internet Access Client (OpenVPN+Iptables+DNS over TLS on Alpine Linux)
Docker VPN client to private internet access servers using [OpenVPN](https://openvpn.net/) and Iptables on Alpine Linux. Docker VPN client to private internet access servers using [OpenVPN](https://openvpn.net/), Iptables and Unbound (Cloudflare DNS over TLS) on Alpine Linux.
Optionally set the protocol (TCP, UDP) and the level of encryption using Docker environment variables. Optionally set the protocol (TCP, UDP) and the level of encryption using Docker environment variables.
@@ -24,28 +24,28 @@ A killswitch is implemented with the *iptables* firewall, only allowing traffic
| Download size | Image size | RAM usage | CPU usage | | Download size | Image size | RAM usage | CPU usage |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 5MB | 8.94MB | 11MB | Low | | ???MB | 15.7MB | 14MB | Low |
It is based on: ## Features
- [Alpine 3.8](https://alpinelinux.org) - Uses [OpenVPN 2.4.6-r3](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/openvpn) to connect to PIA servers
- [OpenVPN 2.4.6-r3](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/openvpn) - The firewall [IPtables 1.6.2-r0](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/iptables) enforces the container to communicate only through the VPN or with other containers in its virtual network
- [IPtables 1.6.2-r0](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/iptables) - Your DNS queries are encrypted using [Unbound 1.7.3-r0](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/unbound) configure with Cloudflare's 1.1.1.1 DNS over TLS
- CA-Certificates for the healthcheck (through HTTPS) - Malicious domain names resolution is blocked with [Unbound 1.7.3-r0](https://pkgs.alpinelinux.org/package/v3.8/main/x86_64/unbound)
- Lightweight, based on [Alpine 3.8](https://alpinelinux.org)
- Restarts OpenVPN on failure using another IP address corresponding to the PIA server domain name (usually 10 IPs per subdomain name)
- Regular Docker healthchecks using wget on duckduckgo.com
- Connect other containers to it
It requires: ## Requirements
- A Private Internet Access **username** and **password** - [Sign up](https://www.privateinternetaccess.com/pages/buy-vpn/) - A Private Internet Access **username** and **password** - [Sign up](https://www.privateinternetaccess.com/pages/buy-vpn/)
- [Docker](https://docs.docker.com/install/) installed on the host - [Docker](https://docs.docker.com/install/) installed on the host
- If you use an advanced firewall:
The PIA *.ovpn* configuration files are downloaded from [the PIA website](https://www.privateinternetaccess.com/openvpn/openvpn.zip) when the Docker image is built. You can build the image yourself if you are paranoid. - Allow outgoing TCP port 501 for TCP strong encryption
- Allow outgoing TCP port 502 for TCP normal encryption
You might also want to use [my Cloudflare DNS over TLS Docker container](https://hub.docker.com/r/qmcgaw/cloudflare-dns-server/) to connect to any PIA server so that: - Allow outgoing UDP port 1197 for UDP strong encryption
- Allow outgoing UDP port 1198 for UDP normal encryption
- Man-in-the-middle (ISP, hacker, government) can't block you from resolving the PIA server domain name
*For example, `austria.privateinternetaccess.com` maps to `185.216.34.229`*
- Man-in-the-middle (ISP, hacker, government) can't see to which server you connect nor when.
*As the domain name are sent to 1.1.1.1 over TLS, there is no way to examine what domains you are asking to be resolved*
## Setup ## Setup
@@ -217,14 +217,13 @@ For more containers, add more `--link pia:xxx` and modify *nginx.conf* according
## EXTRA: For the paranoids ## EXTRA: For the paranoids
- You might want to build the image yourself - You might want to build the Docker image yourself
- The download and unziping is done at build for the ones not able to download the zip files with their ISPs. - The download and unziping is done at build for the ones not able to download the zip files with their ISPs.
- Checksums for PIA openvpn zip files are not used as these files change often - Checksums for PIA openvpn zip files are not used as these files change often
- You should use strong encryption for the environment variable `ENCRYPTION` - You should use strong encryption for the environment variable `ENCRYPTION`
- Let me know if you have any extra idea :) !
### TODOs ### TODOs
- More iptables restrictions
- Rework readme with unbound required for VPN
- Block malicious websites with Unbound - Block malicious websites with Unbound
- Add checks when launching PIA $? - Add checks when launching PIA $?

View File

@@ -2,16 +2,7 @@
printf "=== PIA CONTAINER ===" printf "=== PIA CONTAINER ==="
############################################ cd /openvpn-$PROTOCOL-$ENCRYPTION
# SETTING DNS OVER TLS TO 1.1.1.1 / 1.0.0.1
############################################
printf "\nChanging DNS to localhost..."
echo "nameserver 127.0.0.1" > /etc/resolv.conf
echo "options ndots:0" >> /etc/resolv.conf
printf "DONE"
printf "\nLaunching Unbound daemon to connect to Cloudflare DNS 1.1.1.1 at its TLS endpoint..."
unbound
printf "DONE"
############################################ ############################################
# ORIGINAL IP FOR HEALTHCHECK # ORIGINAL IP FOR HEALTHCHECK
@@ -35,13 +26,17 @@ printf "\n * Port: $PORT"
printf "\n * Domain: $PIADOMAIN" printf "\n * Domain: $PIADOMAIN"
printf "\n * Detecting IP addresses corresponding to $PIADOMAIN..." printf "\n * Detecting IP addresses corresponding to $PIADOMAIN..."
VPNIPS=$(nslookup $PIADOMAIN localhost | tail -n +5 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}') VPNIPS=$(nslookup $PIADOMAIN localhost | tail -n +5 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
VPNIPSLENGTH=0
for ip in $VPNIPS for ip in $VPNIPS
do do
printf "\n $ip" printf "\n $ip"
VPNIPSLENGTH=$((VPNIPSLENGTH+1))
done done
printf "\n * Deleting all iptables rules..." printf "\n * Deleting all iptables rules..."
iptables --flush iptables --flush
iptables --delete-chain iptables --delete-chain
iptables -t nat --flush
iptables -t nat --delete-chain
ip6tables --flush ip6tables --flush
ip6tables --delete-chain ip6tables --delete-chain
printf "DONE" printf "DONE"
@@ -52,10 +47,8 @@ ip6tables -P OUTPUT DROP 2>/dev/null
printf "\n * Adding rules to accept local loopback traffic..." printf "\n * Adding rules to accept local loopback traffic..."
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null
ip6tables -A OUTPUT -o lo -j ACCEPT 2>/dev/null ip6tables -A OUTPUT -o lo -j ACCEPT 2>/dev/null
ip6tables -A INPUT -i lo -j ACCEPT 2>/dev/null
printf "DONE" printf "DONE"
printf "\n * Adding rules to accept traffic of subnet $SUBNET..." printf "\n * Adding rules to accept traffic of subnet $SUBNET..."
iptables -A OUTPUT -d $SUBNET -j ACCEPT iptables -A OUTPUT -d $SUBNET -j ACCEPT
@@ -65,41 +58,61 @@ for ip in $VPNIPS
do do
printf "\n * Adding rules to accept traffic with $ip on port $PROTOCOL $PORT..." printf "\n * Adding rules to accept traffic with $ip on port $PROTOCOL $PORT..."
iptables -A OUTPUT -j ACCEPT -d $ip -o eth0 -p $PROTOCOL -m $PROTOCOL --dport $PORT iptables -A OUTPUT -j ACCEPT -d $ip -o eth0 -p $PROTOCOL -m $PROTOCOL --dport $PORT
iptables -A INPUT -j ACCEPT -s $ip -i eth0 -p $PROTOCOL -m $PROTOCOL --sport $PORT
ip6tables -A OUTPUT -j ACCEPT -d $ip -o eth0 -p $PROTOCOL -m $PROTOCOL --dport $PORT 2>/dev/null ip6tables -A OUTPUT -j ACCEPT -d $ip -o eth0 -p $PROTOCOL -m $PROTOCOL --dport $PORT 2>/dev/null
ip6tables -A INPUT -j ACCEPT -s $ip -i eth0 -p $PROTOCOL -m $PROTOCOL --sport $PORT 2>/dev/null
printf "DONE" printf "DONE"
done done
printf "\n * Adding rules to accept traffic going through the tun device..." printf "\n * Adding rules to accept traffic going through the tun device..."
iptables -A OUTPUT -o tun0 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT
iptables -A INPUT -i tun0 -j ACCEPT
ip6tables -A OUTPUT -o tun0 -j ACCEPT 2>/dev/null ip6tables -A OUTPUT -o tun0 -j ACCEPT 2>/dev/null
ip6tables -A INPUT -i tun0 -j ACCEPT 2>/dev/null
printf "DONE" printf "DONE"
printf "\n * Allowing outgoing DNS queries on port 53 UDP..." #printf "\n * Allowing outgoing DNS queries on port 53 UDP..."
iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT #iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
ip6tables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT 2>/dev/null #ip6tables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT 2>/dev/null
#printf "DONE"
############################################
# SETTING DNS OVER TLS TO 1.1.1.1 / 1.0.0.1
############################################
printf "\nLaunching Unbound daemon to connect to Cloudflare DNS 1.1.1.1 at its TLS endpoint..."
unbound
printf "DONE"
printf "\nChanging DNS to localhost..."
echo "nameserver 127.0.0.1" > /etc/resolv.conf
echo "options ndots:0" >> /etc/resolv.conf
printf "DONE" printf "DONE"
############################################ ############################################
# SUMMARY # USE NON-ROOT USER
############################################ ############################################
printf "\nStarting OpenVPN using the following parameters:"
printf "\n * Domain: $PIADOMAIN"
printf "\n * Port: $PORT"
printf "\n * Protocol: $PROTOCOL"
printf "\n * Encryption: $ENCRYPTION"
############################################
# OPENVPN LAUNCH
############################################
cd /openvpn-$PROTOCOL-$ENCRYPTION
printf "\nSwitching from root to nonrootuser..." printf "\nSwitching from root to nonrootuser..."
su -l nonrootuser su -l nonrootuser
printf "DONE\n" printf "DONE"
openvpn --config "$REGION.ovpn" --auth-user-pass /auth.conf
############################################ ############################################
# CLEANUP # OPENVPN LAUNCH (retry with next VPN IP if fail)
############################################ ############################################
printf "\nExiting...\n\n" failed=1
i=1
PREVIOUSIP=$PIADOMAIN
while [ $failed != 0 ]
do
VPNIP=$(echo $VPNIPS | cut -d' ' -f$i)
sed -i "s/$PREVIOUSIP/$VPNIP/g" $REGION.ovpn
PREVIOUSIP=$VPNIP
printf "\nStarting OpenVPN using the following parameters:"
printf "\n * Region: $REGION"
printf "\n * Encryption: $ENCRYPTION"
printf "\n * Address: $PROTOCOL://$VPNIP:$PORT"
printf "\n\n"
openvpn --config "$REGION.ovpn" --auth-user-pass /auth.conf
failed=$?
if [[ $failed != 0 ]]; then
echo "==> Openvpn failed with error code: $failed"
i=$((i+1))
if [[ $i -gt $VPNIPSLENGTH ]]; then
i=0
fi
else
echo "==> Openvpn stopped cleanly"
fi
done