diff --git a/.gitignore b/.gitignore index d9233ba..f7a5585 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# My personal test non-stock docker-compose.yml file -doco.yml - *.swp + +# WIP/test stuff +doco.yml diff --git a/Dockerfile b/Dockerfile new file mode 120000 index 0000000..dfd6ae7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +alpine.docker \ No newline at end of file diff --git a/alpine.docker b/alpine.docker index e0be565..ce4704f 100644 --- a/alpine.docker +++ b/alpine.docker @@ -1,29 +1,44 @@ FROM alpine:edge MAINTAINER adam@diginc.us -RUN apk add --update --repository http://dl-1.alpinelinux.org/alpine/edge/testing/ tini -ENTRYPOINT ["tini", "--"] - -RUN apk update && \ - apk add \ +RUN apk add --update \ dnsmasq \ - lighttpd \ + nginx \ ca-certificates \ - php-fpm php-json php-zlib php-xml \ - php-pdo php-phar php-openssl \ - php-gd php-iconv php-mcrypt && \ - apk add -u musl && \ + php-fpm php-json libxml2 \ + bash curl perl && \ + apk add --update --repository http://dl-1.alpinelinux.org/alpine/edge/testing/ tini && \ rm -rf /var/cache/apk/* -# Hit wall with /usr/share/lighttpd/*.pl not existing on alpine, -# will need custom configs -# maybe just switch to nginx... -COPY ./pi-hole/gravity.sh /usr/local/bin/ +# Customized from submodules +COPY ./alpine/gravity.sh /usr/local/bin/ +COPY ./alpine/nginx.conf /etc/nginx/nginx.conf +# Original upstream pihole code being used COPY ./pi-hole/advanced/Scripts/* /usr/local/bin/ -COPY ./pi-hole/advanced/lighttpd.conf /etc/lighttpd/lighttpd.conf COPY ./pi-hole/advanced/dnsmasq.conf /etc/dnsmasq.conf COPY ./pi-hole/advanced/index.html /var/www/html/pihole/index.html COPY ./AdminLTE /var/www/html/admin -COPY start.sh / +# Things installer did +RUN mkdir -p /etc/pihole/ && \ + mkdir -p /var/www/html/pihole && \ + mkdir -p /var/www/html/admin/ && \ + chown nginx:nginx /var/www/html && \ + chmod 775 /var/www/html && \ + touch /var/log/pihole.log && \ + chmod 644 /var/log/pihole.log && \ + chown dnsmasq:root /var/log/pihole.log && \ + sed -i "s/@INT@/eth0/" /etc/dnsmasq.conf + +# This chould be eliminated if the (upstream) files were +x in git +RUN chmod +x /usr/local/bin/*.sh +# Fix for docker +RUN grep -q '^user=root' || echo 'user=root' >> /etc/dnsmasq.conf + +COPY ./start.sh / + +EXPOSE 53 53/udp +EXPOSE 80 + +ENTRYPOINT ["tini", "--"] CMD /start.sh diff --git a/alpine/gravity.sh b/alpine/gravity.sh new file mode 100755 index 0000000..e993f25 --- /dev/null +++ b/alpine/gravity.sh @@ -0,0 +1,331 @@ +#!/usr/bin/env bash +# Pi-hole: A black hole for Internet advertisements +# (c) 2015 by Jacob Salmela +# Network-wide ad blocking via your Raspberry Pi +# http://pi-hole.net +# Compiles a list of ad-serving domains by downloading them from multiple sources +# +# Pi-hole is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# Run this script as root or under sudo +echo ":::" +if [[ $EUID -eq 0 ]];then + echo "::: You are root." +else + echo "::: sudo will be used." + # Check if it is actually installed + # If it isn't, exit because the install cannot complete + if [[ $(dpkg-query -s sudo) ]];then + export SUDO="sudo" + else + echo "::: Please install sudo or run this script as root." + exit 1 + fi +fi + +piholeIPfile=/tmp/piholeIP +piholeIPv6file=/etc/pihole/.useIPv6 + +if [[ -f $piholeIPfile ]];then + # If the file exists, it means it was exported from the installation script and we should use that value instead of detecting it in this script + piholeIP=$(cat $piholeIPfile) + rm $piholeIPfile +else + # Otherwise, the IP address can be taken directly from the machine, which will happen when the script is run by the user and not the installation script + IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') + piholeIPCIDR=$(ip -o -f inet addr show dev $IPv4dev | awk '{print $4}' | awk 'END {print}') + piholeIP=${piholeIPCIDR%/*} +fi + +if [[ -f $piholeIPv6file ]];then + # If the file exists, then the user previously chose to use IPv6 in the automated installer + piholeIPv6=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') +fi + +# Ad-list sources--one per line in single quotes +# The mahakala source is commented out due to many users having issues with it blocking legitimate domains. +# Uncomment at your own risk +sources=('https://adaway.org/hosts.txt' +'http://adblock.gjtech.net/?format=unix-hosts' +#'http://adblock.mahakala.is/' +'http://hosts-file.net/ad_servers.txt' +'http://www.malwaredomainlist.com/hostslist/hosts.txt' +'http://pgl.yoyo.org/adservers/serverlist.php?' +'http://someonewhocares.org/hosts/hosts' +'http://winhelp2002.mvps.org/hosts.txt' +'http://mirror1.malwaredomains.com/files/justdomains') + +# Variables for various stages of downloading and formatting the list +basename=pihole +piholeDir=/etc/$basename +adList=$piholeDir/gravity.list +blacklist=$piholeDir/blacklist.txt +whitelist=$piholeDir/whitelist.txt +latentWhitelist=$piholeDir/latentWhitelist.txt +justDomainsExtension=domains +matterandlight=$basename.0.matterandlight.txt +supernova=$basename.1.supernova.txt +eventHorizon=$basename.2.eventHorizon.txt +accretionDisc=$basename.3.accretionDisc.txt +eyeOfTheNeedle=$basename.4.wormhole.txt + +# After setting defaults, check if there's local overrides +if [[ -r $piholeDir/pihole.conf ]];then + echo "::: Local calibration requested..." + . $piholeDir/pihole.conf +fi + + +spinner(){ + local pid=$1 + local delay=0.001 + local spinstr='/-\|' + + spin='-\|/' + i=0 + while $SUDO kill -0 $pid 2>/dev/null + do + i=$(( (i+1) %4 )) + printf "\b${spin:$i:1}" + sleep .1 + done + printf "\b" +} +########################### +# collapse - begin formation of pihole +function gravity_collapse() { + echo -n "::: Neutrino emissions detected..." + + # Create the pihole resource directory if it doesn't exist. Future files will be stored here + if [[ -d $piholeDir ]];then + # Temporary hack to allow non-root access to pihole directory + # Will update later, needed for existing installs, new installs should + # create this directory as non-root + $SUDO chmod 777 $piholeDir + find "$piholeDir" -type f -exec $SUDO chmod 666 {} \; & spinner $! + echo "." + else + echo -n "::: Creating pihole directory..." + mkdir $piholeDir & spinner $! + echo " done!" + fi +} + +# patternCheck - check to see if curl downloaded any new files. +function gravity_patternCheck() { + patternBuffer=$1 + # check if the patternbuffer is a non-zero length file + if [[ -s "$patternBuffer" ]];then + # Some of the blocklists are copyright, they need to be downloaded + # and stored as is. They can be processed for content after they + # have been saved. + cp $patternBuffer $saveLocation + echo " List updated, transport successful!" + else + # curl didn't download any host files, probably because of the date check + echo " No changes detected, transport skipped!" + fi +} + +# transport - curl the specified url with any needed command extentions +function gravity_transport() { + url=$1 + cmd_ext=$2 + agent=$3 + + # tmp file, so we don't have to store the (long!) lists in RAM + patternBuffer=$(mktemp) + heisenbergCompensator="" + if [[ -r $saveLocation ]]; then + # if domain has been saved, add file for date check to only download newer + heisenbergCompensator="-z $saveLocation" + fi + + # Silently curl url + curl -s $cmd_ext $heisenbergCompensator -A "$agent" $url > $patternBuffer + # Check for list updates + gravity_patternCheck $patternBuffer + + # Cleanup + rm -f $patternBuffer +} + +# spinup - main gravity function +function gravity_spinup() { + echo "::: " + # Loop through domain list. Download each one and remove commented lines (lines beginning with '# 'or '/') and # blank lines + for ((i = 0; i < "${#sources[@]}"; i++)) + do + url=${sources[$i]} + # Get just the domain from the URL + domain=$(echo "$url" | cut -d'/' -f3) + + # Save the file as list.#.domain + saveLocation=$piholeDir/list.$i.$domain.$justDomainsExtension + activeDomains[$i]=$saveLocation + + agent="Mozilla/10.0" + + echo -n "::: Getting $domain list..." + + # Use a case statement to download lists that need special cURL commands + # to complete properly and reset the user agent when required + case "$domain" in + "adblock.mahakala.is") + agent='Mozilla/5.0 (X11; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0' + cmd_ext="-e http://forum.xda-developers.com/" + ;; + + "pgl.yoyo.org") + cmd_ext="-d mimetype=plaintext -d hostformat=hosts" + ;; + + # Default is a simple request + *) cmd_ext="" + esac + gravity_transport $url $cmd_ext $agent + done +} + +# Schwarzchild - aggregate domains to one list and add blacklisted domains +function gravity_Schwarzchild() { + echo "::: " + # Find all active domains and compile them into one file and remove CRs + echo -n "::: Aggregating list of domains..." + truncate -s 0 $piholeDir/$matterandlight & spinner $! + for i in "${activeDomains[@]}" + do + cat $i |tr -d '\r' >> $piholeDir/$matterandlight + done + echo " done!" + +} + + +function gravity_Blacklist(){ + # Append blacklist entries if they exist + echo -n "::: Running blacklist script to update HOSTS file...." + blacklist.sh -f -nr -q > /dev/null & spinner $! + + numBlacklisted=$(wc -l < "/etc/pihole/blacklist.txt") + plural=; [[ "$numBlacklisted" != "1" ]] && plural=s + echo " $numBlacklisted domain${plural} blacklisted!" + + +} + + +function gravity_Whitelist() { + echo ":::" + # Prevent our sources from being pulled into the hole + plural=; [[ "${sources[@]}" != "1" ]] && plural=s + echo -n "::: Adding ${#sources[@]} ad list source${plural} to the whitelist..." + + urls=() + for url in ${sources[@]} + do + tmp=$(echo "$url" | awk -F '/' '{print $3}') + urls=("${urls[@]}" $tmp) + done + echo " done!" + + echo -n "::: Running whitelist script to update HOSTS file...." + whitelist.sh -f -nr -q ${urls[@]} > /dev/null & spinner $! + + numWhitelisted=$(wc -l < "/etc/pihole/whitelist.txt") + plural=; [[ "$numWhitelisted" != "1" ]] && plural=s + echo " $numWhitelisted domain${plural} whitelisted!" + + + +} + +function gravity_unique() { + # Sort and remove duplicates + echo -n "::: Removing duplicate domains...." + sort -u $piholeDir/$supernova > $piholeDir/$eventHorizon & spinner $! + echo " done!" + numberOf=$(wc -l < $piholeDir/$eventHorizon) + echo "::: $numberOf unique domains trapped in the event horizon." +} + +function gravity_hostFormat() { + # Format domain list as "192.168.x.x domain.com" + echo "::: Formatting domains into a HOSTS file..." + # If there is a value in the $piholeIPv6, then IPv6 will be used, so the awk command modified to create a line for both protocols + if [[ -n $piholeIPv6 ]];then + #Add dummy domain Pi-Hole.IsWorking.OK to the top of gravity.list to make ping result return a friendlier looking domain! + echo -e "$piholeIP Pi-Hole.IsWorking.OK \n$piholeIPv6 Pi-Hole.IsWorking.OK" > $piholeDir/$accretionDisc + cat $piholeDir/$eventHorizon | awk -v ipv4addr="$piholeIP" -v ipv6addr="$piholeIPv6" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> $piholeDir/$accretionDisc + + else + # Otherwise, just create gravity.list as normal using IPv4 + #Add dummy domain Pi-Hole.IsWorking.OK to the top of gravity.list to make ping result return a friendlier looking domain! + echo -e "$piholeIP Pi-Hole.IsWorking.OK" > $piholeDir/$accretionDisc + cat $piholeDir/$eventHorizon | awk -v ipv4addr="$piholeIP" '{sub(/\r$/,""); print ipv4addr" "$0}' >> $piholeDir/$accretionDisc + fi + # Copy the file over as /etc/pihole/gravity.list so dnsmasq can use it + cp $piholeDir/$accretionDisc $adList +} + +# blackbody - remove any remnant files from script processes +function gravity_blackbody() { + # Loop through list files + for file in $piholeDir/*.$justDomainsExtension + do + # If list is in active array then leave it (noop) else rm the list + if [[ " ${activeDomains[@]} " =~ " ${file} " ]]; then + : + else + rm -f $file + fi + done +} + +function gravity_advanced() { + + + # Remove comments and print only the domain name + # Most of the lists downloaded are already in hosts file format but the spacing/formating is not contigious + # This helps with that and makes it easier to read + # It also helps with debugging so each stage of the script can be researched more in depth + echo -n "::: Formatting list of domains to remove comments...." + awk '($1 !~ /^#/) { if (NF>1) {print $2} else {print $1}}' $piholeDir/$matterandlight | sed -nr -e 's/\.{2,}/./g' -e '/\./p' > $piholeDir/$supernova & spinner $! + echo " done!" + + numberOf=$(wc -l < $piholeDir/$supernova) + echo "::: $numberOf domains being pulled in by gravity..." + + gravity_unique + +} + +function gravity_reload() { + # Reload hosts file + echo ":::" + echo -n "::: Refresh lists in dnsmasq..." + dnsmasqPid=$(pidof dnsmasq) + + if [[ $dnsmasqPid ]]; then + # service already running - reload config + $SUDO kill -HUP $dnsmasqPid & spinner $! + else + # service not running, start it up + $SUDO service dnsmasq start & spinner $! + fi + echo " done!" +} + + +gravity_collapse +gravity_spinup +gravity_Schwarzchild +gravity_advanced +gravity_hostFormat +gravity_blackbody +gravity_Whitelist +gravity_Blacklist +#gravity_reload diff --git a/alpine/nginx.conf b/alpine/nginx.conf new file mode 100644 index 0000000..a80b8ca --- /dev/null +++ b/alpine/nginx.conf @@ -0,0 +1,38 @@ +worker_processes 1; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + server { + listen 80; + listen [::]:80; + root /var/www/html; + index index.php index.html index.nginx-debian.html; + + error_page 404 /pihole/index.html; + + location ~ ^/admin/ { + add_header X-Pi-hole "The Pi-hole Web interface is working!"; + + location ~ .php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + } + + location / { + add_header X-Pi-hole "A black hole for Internet advertisements." always; + } + } + +} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index bfc000f..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -debian: - build: . - dockerfile: debian.docker - ports: - - "53:53/tcp" - - "53:53/udp" - - "8053:80/tcp" - cap_add: - - NET_ADMIN diff --git a/docker-compose.yml b/docker-compose.yml new file mode 120000 index 0000000..b26e16b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1 @@ +doco-alpine.yml \ No newline at end of file diff --git a/doco-alpine.yml b/doco-alpine.yml new file mode 100644 index 0000000..93ae13c --- /dev/null +++ b/doco-alpine.yml @@ -0,0 +1,9 @@ +alpine: + build: . + dockerfile: alpine.docker + ports: + - "53:53/tcp" + - "53:53/udp" + - "8054:80/tcp" + cap_add: + - NET_ADMIN diff --git a/doco-debian.yml b/doco-debian.yml new file mode 100644 index 0000000..bfc000f --- /dev/null +++ b/doco-debian.yml @@ -0,0 +1,9 @@ +debian: + build: . + dockerfile: debian.docker + ports: + - "53:53/tcp" + - "53:53/udp" + - "8053:80/tcp" + cap_add: + - NET_ADMIN diff --git a/make_symlinks.sh b/make_symlinks.sh new file mode 100755 index 0000000..c3b1930 --- /dev/null +++ b/make_symlinks.sh @@ -0,0 +1,11 @@ +#!/bin/sh -e +supportedTags='^(alpine|debian)$' +if ! (echo $1 | grep -Pq "$supportedTags") ; then + echo "$1 is not a supported tag"; exit 1; +fi + +unlink docker-compose.yml +unlink Dockerfile + +ln -s doco-${1}.yml docker-compose.yml +ln -s ${1}.docker Dockerfile diff --git a/start.sh b/start.sh index 8a61a09..0fd40f8 100755 --- a/start.sh +++ b/start.sh @@ -1,8 +1,13 @@ #!/bin/sh +gravity.sh # pi-hole version minus the service dnsmasq start +dnsmasq --test || exit 1 +dnsmasq -service lighttpd start -gravity.sh # dnsmasq start included +php-fpm -t || exit 1 +php-fpm -tail -f /var/log/lighttpd/*.log /var/log/pihole.log +nginx -t || exit 1 +nginx +tail -F /var/log/nginx/*.log /var/log/pihole.log