From 4bcaec6a332ab867b501b0d57a142743eef046e0 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Sun, 28 Oct 2018 14:08:14 +0100 Subject: [PATCH] Big refactoring (more secured, more modular) - Region change to "CA Montreal" - Using external data images for malicious hostnames - Added malicious IP addresses blocking with Unbound - Unbound has DNS rebinding protection --- Dockerfile | 71 +++++++--------------------------------------- README.md | 2 +- docker-compose.yml | 40 +++++++++++++------------- entrypoint.sh | 37 +++++++++++++++++------- unbound.conf | 10 +++++++ 5 files changed, 69 insertions(+), 91 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9497718b..a521875d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,54 +1,3 @@ -FROM alpine:3.8 AS rootanchors -RUN apk add -q --update --no-cache --progress wget perl-xml-xpath -RUN wget -q https://www.internic.net/domain/named.root -O named.root && \ - echo "3a434d30e41db78c2ccfdaf29be460d0 named.root" > hashes.md5 && \ - md5sum -c hashes.md5 -RUN wget -q https://data.iana.org/root-anchors/root-anchors.xml -O root-anchors.xml && \ - echo "1b2a628d1ff22d4dc7645cfc89f21b6a575526439c6706ecf853e6fff7099dc8 root-anchors.xml" > hashes.sha256 && \ - sha256sum -c hashes.sha256 && \ - KEYTAGS=$(xpath -q -e '/TrustAnchor/KeyDigest/KeyTag/node()' root-anchors.xml) && \ - ALGORITHMS=$(xpath -q -e '/TrustAnchor/KeyDigest/Algorithm/node()' root-anchors.xml) && \ - DIGESTTYPES=$(xpath -q -e '/TrustAnchor/KeyDigest/DigestType/node()' root-anchors.xml) && \ - DIGESTS=$(xpath -q -e '/TrustAnchor/KeyDigest/Digest/node()' root-anchors.xml) && \ - i=1 && \ - while [ 1 ]; do \ - KEYTAG=$(echo $KEYTAGS | cut -d" " -f$i); \ - [ "$KEYTAG" != "" ] || break; \ - ALGORITHM=$(echo $ALGORITHMS | cut -d" " -f$i); \ - DIGESTTYPE=$(echo $DIGESTTYPES | cut -d" " -f$i); \ - DIGEST=$(echo $DIGESTS | cut -d" " -f$i); \ - echo ". IN DS $KEYTAG $ALGORITHM $DIGESTTYPE $DIGEST" >> /root.key; \ - i=`expr $i + 1`; \ - done; - -FROM alpine:3.8 AS blocks -RUN apk add -q --update --no-cache --progress wget ca-certificates sed -RUN hostnames=$(wget -qO- https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | \ - sed '/\(^[ \|\t]*#\)\|\(^[ ]\+\)\|\(^$\)\|\(^[\n\|\r\|\r\n][ \|\t]*$\)\|\(^127.0.0.1\)\|\(^255.255.255.255\)\|\(^::1\)\|\(^fe80\)\|\(^ff00\)\|\(^ff02\)\|\(^0.0.0.0 0.0.0.0\)/d' | \ - sed 's/\([ \|\t]*#.*$\)\|\(\r\)\|\(0.0.0.0 \)//g')$'\n'$( \ - wget -qO- https://raw.githubusercontent.com/CHEF-KOCH/NSABlocklist/master/HOSTS | \ - sed '/\(^[ \|\t]*#\)\|\(^[ ]\+\)\|\(^$\)\|\(^[\n\|\r\|\r\n][ \|\t]*$\)\|\(^127.0.0.1\)/d' | \ - sed 's/\([ \|\t]*#.*$\)\|\(\r\)\|\(0.0.0.0 \)//g')$'\n'$( \ - wget -qO- https://raw.githubusercontent.com/k0nsl/unbound-blocklist/master/blocks.conf | \ - sed '/\(^[ \|\t]*#\)\|\(^[ ]\+\)\|\(^$\)\|\(^[\n\|\r\|\r\n][ \|\t]*$\)\|\(^local-data\)/d' | \ - sed 's/\([ \|\t]*#.*$\)\|\(\r\)\|\(local-zone: \"\)\|\(\" redirect\)//g')$'\n'$( \ - wget -qO- https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt | \ - sed '/\(^[ \|\t]*#\)\|\(^[ ]\+\)\|\(^$\)\|\(^[\n\|\r\|\r\n][ \|\t]*$\)\|\(::$\)/d' | \ - sed 's/\([ \|\t]*#.*$\)\|\(\r\)\|\(address=\/\)\|\(\/0.0.0.0$\)//g')$'\n'$( \ - wget -qO- https://raw.githubusercontent.com/notracking/hosts-blocklists/master/hostnames.txt | \ - sed '/\(^[ \|\t]*#\)\|\(^[ ]\+\)\|\(^$\)\|\(^[\n\|\r\|\r\n][ \|\t]*$\)\|\(^::\)/d' | \ - sed 's/\([ \|\t]*#.*$\)\|\(\r\)\|\(^0.0.0.0 \)//g') && \ - COUNT_BEFORE=$(echo "$hostnames" | sed '/^\s*$/d' | wc -l) && \ - hostnames=$(echo "$hostnames" | sort | uniq | sed '/\(psma01.com.\)\|\(psma02.com.\)\|\(psma03.com.\)\|\(MEZIAMUSSUCEMAQUEUE.SU\)/d') && \ - COUNT_AFTER=$(echo "$hostnames" | sed '/^\s*$/d' | wc -l) && \ - echo "Removed $((COUNT_BEFORE-$COUNT_AFTER)) duplicates from $COUNT_BEFORE hostnames" && \ - COUNT_BEFORE=$(echo "$hostnames" | sed '/^\s*$/d' | wc -l) && \ - hostnames=$(echo "$hostnames" | sed '/\(maxmind.com\)/Id') && \ - COUNT_AFTER=$(echo "$hostnames" | sed '/^\s*$/d' | wc -l) && \ - echo "Removed $((COUNT_BEFORE-$COUNT_AFTER)) entries manually (see Dockerfile)" && \ - for hostname in $hostnames; do echo "local-zone: \""$hostname"\" static" >> blocks-malicious.conf; done && \ - tar -cjf blocks-malicious.conf.bz2 blocks-malicious.conf - FROM alpine:3.8 LABEL maintainer="quentin.mcgaw@gmail.com" \ description="VPN client to private internet access servers using OpenVPN, IPtables firewall, DNS over TLS with Unbound and Alpine Linux" \ @@ -57,16 +6,16 @@ LABEL maintainer="quentin.mcgaw@gmail.com" \ ram="13MB" \ cpu_usage="Low" \ github="https://github.com/qdm12/private-internet-access-docker" -COPY --from=rootanchors /named.root /etc/unbound/root.hints -COPY --from=rootanchors /root.key /etc/unbound/root.key -COPY --from=blocks /blocks-malicious.conf.bz2 /etc/unbound/blocks-malicious.conf.bz2 -HEALTHCHECK --interval=5m --timeout=15s --start-period=10s --retries=2 \ - CMD if [[ "$(wget -qqO- 'https://duckduckgo.com/?q=what+is+my+ip' | grep -ow 'Your IP address is [0-9.]*[0-9]' | grep -ow '[0-9][0-9.]*')" == "$INITIAL_IP" ]]; then echo "IP address is the same as the non VPN IP address"; exit 1; fi ENV ENCRYPTION=strong \ PROTOCOL=tcp \ - REGION=Germany \ + REGION="CA Montreal" \ BLOCK_MALICIOUS=off -RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables unbound && \ +HEALTHCHECK --interval=5m --timeout=15s --start-period=10s --retries=2 \ + CMD if [[ "$(wget -qqO- 'https://duckduckgo.com/?q=what+is+my+ip' | grep -ow 'Your IP address is [0-9.]*[0-9]' | grep -ow '[0-9][0-9.]*')" == "$INITIAL_IP" ]]; then echo "IP address is the same as the non VPN IP address"; exit 1; fi +COPY --from=qmcgaw/dns-trustanchor /named.root /etc/unbound/root.hints +COPY --from=qmcgaw/dns-trustanchor /root.key /etc/unbound/root.key +RUN echo https://dl-3.alpinelinux.org/alpine/v3.8/main > /etc/apk/repositories && \ + apk add -q --progress --no-cache --update openvpn wget ca-certificates iptables unbound && \ apk add -q --progress --no-cache --update --virtual=build-dependencies unzip && \ mkdir /openvpn-udp-normal /openvpn-udp-strong /openvpn-tcp-normal /openvpn-tcp-strong && \ wget -q https://www.privateinternetaccess.com/openvpn/openvpn.zip \ @@ -82,6 +31,8 @@ RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables u chown unbound /etc/unbound/root.key && \ adduser -S nonrootuser COPY unbound.conf /etc/unbound/unbound.conf -COPY entrypoint.sh / -RUN chmod +x /entrypoint.sh +COPY --from=qmcgaw/malicious-hostnames /malicious-hostnames.bz2 /etc/unbound/malicious-hostnames.bz2 +COPY --from=qmcgaw/malicious-ips /malicious-ips.bz2 /etc/unbound/malicious-ips.bz2 +COPY entrypoint.sh /entrypoint.sh +RUN chmod 700 /entrypoint.sh ENTRYPOINT /entrypoint.sh diff --git a/README.md b/README.md index 6656f7d3..3522c7f4 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ A killswitch is implemented with the *iptables* firewall, only allowing traffic docker run -d --name=pia \ --cap-add=NET_ADMIN --device=/dev/net/tun --network=pianet \ -v /yourhostpath/auth.conf:/auth.conf:ro \ - -e REGION=Germany -e PROTOCOL=udp -e ENCRYPTION=normal \ + -e REGION="CA Montreal" -e PROTOCOL=udp -e ENCRYPTION=normal \ qmcgaw/private-internet-access ``` diff --git a/docker-compose.yml b/docker-compose.yml index 6e46d750..033cdfb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,24 @@ version: '3' services: - pia: - image: qmcgaw/private-internet-access - container_name: pia - cap_add: - - NET_ADMIN - devices: - - /dev/net/tun - networks: - - pianet - volumes: - - /yourpath/auth.conf:/auth.conf:ro - environment: - - PROTOCOL=tcp - - ENCRYPTION=strong - - REGION=Germany - restart: always - + pia: + build: . + image: qmcgaw/private-internet-access + container_name: pia + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun + networks: + - pianet + volumes: + - /yourpath/auth.conf:/auth.conf:ro + environment: + - PROTOCOL=tcp + - ENCRYPTION=strong + - REGION=CA Montreal + restart: always + networks: - pianet: - external: true \ No newline at end of file + pianet: + external: true + \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 7b3a683a..a56b0c11 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -7,32 +7,47 @@ printf "\n =========================================" printf "\n =========================================" printf "\n == by github.com/qdm12 - Quentin McGaw ==\n" +printf "\nOpenVPN version: $(openvpn --version | head -n 1 | grep -oE "OpenVPN [0-9\.]* " | cut -d" " -f2)" +printf "\nUnbound version: $(unbound -h | grep "Version" | cut -d" " -f2)" +printf "\nIptables version: $(iptables --version | cut -d" " -f2)" + ############################################ # CHECK PARAMETERS ############################################ cat "/openvpn-$PROTOCOL-$ENCRYPTION/$REGION.ovpn" &> /dev/null if [[ "$?" != 0 ]]; then printf "/openvpn-$PROTOCOL-$ENCRYPTION/$REGION.ovpn is not accessible\nSleeping for 10 seconds before exit...\n"; sleep 10; exit 1; fi +# TODO more ############################################ # CHECK FOR TUN DEVICE ############################################ while [ "$(cat /dev/net/tun 2>&1 /dev/null)" != "cat: read error: File descriptor in bad state" ]; -do - printf "\nTUN device is not opened, sleeping for 30 seconds..."; - sleep 30; -done +do printf "\nTUN device is not opened, sleeping for 30 seconds..."; sleep 30; done printf "\nTUN device is opened" ############################################ -# BLOCKING MALICIOUS HOSTS WITH UNBOUND +# BLOCKING MALICIOUS HOSTNAMES AND IPs WITH UNBOUND ############################################ touch /etc/unbound/blocks-malicious.conf -printf "\nUnbound malicious hosts blocking is $BLOCK_MALICIOUS" -if [[ "$BLOCK_MALICIOUS" == "on" ]]; then - printf "\nExtracting blocks-malicious.conf.bz2..." - tar -xjf /etc/unbound/blocks-malicious.conf.bz2 -C /etc/unbound/ - rm /etc/unbound/blocks-malicious.conf.bz2 - printf "DONE" +printf "\nUnbound malicious hostnames blocking is $BLOCK_MALICIOUS" +if [ "$BLOCK_MALICIOUS" = "on" ] && [ ! -f /etc/unbound/blocks-malicious.conf ]; then + printf "Extracting malicious hostnames archive..." + tar -xjf /etc/unbound/malicious-hostnames.bz2 -C /etc/unbound/ + printf "DONE\n" + printf "Extracting malicious IPs archive..." + tar -xjf /etc/unbound/malicious-ips.bz2 -C /etc/unbound/ + printf "DONE\n" + printf "Building blocks-malicious.conf for Unbound..." + while read hostname; do + echo "local-zone: \""$hostname"\" static" >> /etc/unbound/blocks-malicious.conf + done < /etc/unbound/malicious-hostnames + while read ip; do + echo "private-address: $ip" >> /etc/unbound/blocks-malicious.conf + done < /etc/unbound/malicious-ips + printf "$(cat /etc/unbound/malicious-hostnames | wc -l ) malicious hostnames and $(cat /etc/unbound/malicious-ips | wc -l) malicious IP addresses added\n" + rm -f /etc/unbound/malicious-hostnames* /etc/unbound/malicious-ips* +else + touch /etc/unbound/blocks-malicious.conf fi ############################################ diff --git a/unbound.conf b/unbound.conf index 924660cf..04802019 100644 --- a/unbound.conf +++ b/unbound.conf @@ -30,6 +30,16 @@ server: harden-referral-path: yes harden-algo-downgrade: yes # set above to no if there is any problem + # Prevent DNS rebinding + private-address: 127.0.0.1/8 + private-address: 10.0.0.0/8 + private-address: 172.16.0.0/12 + private-address: 192.168.0.0/16 + private-address: 169.254.0.0/16 + private-address: ::1/128 + private-address: fc00::/7 + private-address: fe80::/10 + private-address: ::ffff:0:0/96 # network do-ip4: yes