diff --git a/README.md b/README.md index 561e4f09..429fc275 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ wget -O basic-install.sh https://install.pi-hole.net bash basic-install.sh ``` -Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to [use the Raspberry Pi as its DNS server](http://pi-hole.net/faq/how-do-i-use-the-pi-hole-as-my-dns-server/). +Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to use Pi-hole as their DNS server. ## What is Pi-hole and how do I install it?
@@ -73,7 +73,7 @@ Once installed, [configure your router to have **DHCP clients use the Pi as thei ## Technical Details -The Pi-hole is an **advertising-aware DNS/Web server**. If an ad domain is queried, a small Web page or GIF is delivered in place of the advertisement. You can also [replace ads with any image you want](http://pi-hole.net/faq/is-it-possible-to-change-the-blank-page-that-takes-place-of-the-ads-to-something-else/) since it is just a simple Webpage taking place of the ads. +The Pi-hole is an **advertising-aware DNS/Web server**. If an ad domain is queried, a small Web page or GIF is delivered in place of the advertisement. ### Gravity @@ -91,13 +91,31 @@ The [Web interface](https://github.com/pi-hole/AdminLTE#pi-hole-admin-dashboard) `http://192.168.1.x/admin/index.php` or `http://pi.hole/admin` -![Pi-hole Advanced Stats Dashboard](https://assets.pi-hole.net/static/dashboard.png) +![Pi-hole Advanced Stats Dashboard](https://assets.pi-hole.net/static/dashboard212.png) ### Whitelist and blacklist Domains can be whitelisted and blacklisted using either the web interface or the command line. See [the wiki page](https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting) for more details + +### Settings + +The settings page lets you control and configure your Pi-hole. You can do things like: + +- enable Pi-hole's built-in DHCP server +- exclude domains from the graphs +- configure upstream DNS servers +- and more! + +![Settings page](https://assets.pi-hole.net/static/settings212.png) + +#### Built-in DHCP Server + +Pi-hole ships with a built-in DHCP server. This allows you to let your network devices use Pi-hole as their DNS server if your router does not let you adjust the DHCP options. + ## API @@ -117,7 +135,7 @@ The same output can be achieved on the CLI by running `chronometer.sh -j` ## Real-time Statistics -You can view [real-time stats](http://pi-hole.net/faq/install-the-real-time-lcd-monitor-chronometer/) via `ssh` or on an [2.8" LCD screen](http://amzn.to/1P0q1Fj). This is accomplished via [`chronometer.sh`](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/chronometer.sh). ![Pi-hole LCD](http://i.imgur.com/nBEqycp.jpg) +You can view [real-time stats](https://discourse.pi-hole.net/t/how-do-i-view-my-pi-holes-stats-over-ssh-or-on-an-lcd-using-chronometer/240) via `ssh` or on an [2.8" LCD screen](http://amzn.to/1P0q1Fj). This is accomplished via [`chronometer.sh`](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/chronometer.sh). ![Pi-hole LCD](http://i.imgur.com/nBEqycp.jpg) ## Pi-hole Projects diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 93b0cbb1..3dce9c3e 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -22,7 +22,7 @@ function GetJSONValue { retVal=$(echo $1 | sed 's/\\\\\//\//g' | \ sed 's/[{}]//g' | \ awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | \ - sed 's/\"\:\"/\|/g' | \ + sed 's/\"\:/\|/g' | \ sed 's/[\,]/ /g' | \ sed 's/\"//g' | \ grep -w $2) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index dce274c5..7cbe6beb 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -58,7 +58,7 @@ EscapeRegexp() { # This way we may safely insert an arbitrary # string in our regular expressions # Also remove leading "." if present - echo $* | sed 's/^\.//' | sed "s/[]\\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g" + echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g" } HandleOther(){ @@ -66,7 +66,7 @@ HandleOther(){ domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") #check validity of domain - validDomain=$(echo "${domain}" | grep -P '^(?!.*[^a-z0-9-\.].*)\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b') + validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/') if [ -z "${validDomain}" ]; then echo "::: $1 is not a valid argument or domain name" else diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 4e1f63a9..ecd9e86b 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -19,74 +19,17 @@ readonly ADMIN_INTERFACE_DIR="/var/www/html/admin" readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git" readonly PI_HOLE_FILES_DIR="/etc/.pihole" -is_repo() { - # Use git to check if directory is currently under VCS, return the value - local directory="${1}" - local curdir - local rc +PH_TEST=true +source ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh - curdir="${PWD}" - cd "${directory}" &> /dev/null || return 1 - git status --short &> /dev/null - rc=$? - cd "${curdir}" &> /dev/null || return 1 - return "${rc}" -} - -prep_repo() { - # Prepare directory for local repository building - local directory="${1}" - - rm -rf "${directory}" &> /dev/null - return -} - -make_repo() { - # Remove the non-repod interface and clone the interface - local remoteRepo="${2}" - local directory="${1}" - - (prep_repo "${directory}" && git clone -q --depth 1 "${remoteRepo}" "${directory}") - return -} - -update_repo() { - local directory="${1}" - local curdir - - curdir="${PWD}" - cd "${directory}" &> /dev/null || return 1 - # Pull the latest commits - # Stash all files not tracked for later retrieval - git stash --all --quiet - # Force a clean working directory for cloning - git clean --force -d - # Fetch latest changes and apply - git pull --quiet - cd "${curdir}" &> /dev/null || return 1 -} - -getGitFiles() { - # Setup git repos for directory and repository passed - # as arguments 1 and 2 - local directory="${1}" - local remoteRepo="${2}" - echo ":::" - echo "::: Checking for existing repository..." - if is_repo "${directory}"; then - echo -n "::: Updating repository in ${directory}..." - update_repo "${directory}" || (echo "*** Error: Could not update local repository. Contact support."; exit 1) - echo " done!" - else - echo -n "::: Cloning ${remoteRepo} into ${directory}..." - make_repo "${directory}" "${remoteRepo}" || (echo "Unable to clone repository, please contact support"; exit 1) - echo " done!" - fi -} +# is_repo() sourced from basic-install.sh +# make_repo() sourced from basic-install.sh +# update_repo() source from basic-install.sh +# getGitFiles() sourced from basic-install.sh GitCheckUpdateAvail() { local directory="${1}" - curdir=$PWD; + curdir=$PWD cd "${directory}" # Fetch latest changes in this repo @@ -105,22 +48,22 @@ GitCheckUpdateAvail() { # defaults to the current one. REMOTE="$(git rev-parse @{upstream})" - # Change back to original directory - cd "${curdir}" - if [[ ${#LOCAL} == 0 ]]; then - echo "::: Error: Local revision could not be optained, ask Pi-hole support." + echo "::: Error: Local revision could not be obtained, ask Pi-hole support." echo "::: Additional debugging output:" git status exit fi if [[ ${#REMOTE} == 0 ]]; then - echo "::: Error: Remote revision could not be optained, ask Pi-hole support." + echo "::: Error: Remote revision could not be obtained, ask Pi-hole support." echo "::: Additional debugging output:" git status exit fi + # Change back to original directory + cd "${curdir}" + if [[ "${LOCAL}" != "${REMOTE}" ]]; then # Local branch is behind remote branch -> Update return 0 @@ -188,21 +131,21 @@ main() { echo "::: Pi-hole Web Admin files out of date" getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}" - elif ${core_update} && ! ${web_update} ; then - echo ":::" - echo "::: Pi-hole core files out of date" - getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - /etc/.pihole/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 + elif ${core_update} && ! ${web_update} ; then + echo ":::" + echo "::: Pi-hole core files out of date" + getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 - elif ${core_update} && ${web_update} ; then - echo ":::" - echo "::: Updating Everything" - getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - /etc/.pihole/automated\ install/basic-install.sh --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 - else - echo "*** Update script has malfunctioned, fallthrough reached. Please contact support" - exit 1 - fi + elif ${core_update} && ${web_update} ; then + echo ":::" + echo "::: Updating Everything" + getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 + else + echo "*** Update script has malfunctioned, fallthrough reached. Please contact support" + exit 1 + fi else # Web Admin not installed, so only verify if core is up to date @@ -214,7 +157,7 @@ main() { echo ":::" echo "::: Pi-hole core files out of date" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - /etc/.pihole/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 fi fi diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 02610d85..fd882bd3 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -12,6 +12,8 @@ readonly setupVars="/etc/pihole/setupVars.conf" readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf" readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" +# 03 -> wildcards +readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" helpFunc() { cat << EOM @@ -313,6 +315,31 @@ ResolutionSettings() { fi } +AddDHCPStaticAddress() { + + mac="${args[2]}" + ip="${args[3]}" + host="${args[4]}" + + if [[ "${ip}" == "noip" ]]; then + # Static host name + echo "dhcp-host=${mac},${host}" >> "${dhcpstaticconfig}" + elif [[ "${host}" == "nohost" ]]; then + # Static IP + echo "dhcp-host=${mac},${ip}" >> "${dhcpstaticconfig}" + else + # Full info given + echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}" + fi +} + +RemoveDHCPStaticAddress() { + + mac="${args[2]}" + sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" + +} + main() { args=("$@") @@ -334,6 +361,8 @@ main() { "-h" | "--help" ) helpFunc;; "privacymode" ) SetPrivacyMode;; "resolve" ) ResolutionSettings;; + "addstaticdhcp" ) AddDHCPStaticAddress;; + "removestaticdhcp" ) RemoveDHCPStaticAddress;; * ) helpFunc;; esac diff --git a/advanced/logrotate b/advanced/logrotate index e9be016d..570e7548 100644 --- a/advanced/logrotate +++ b/advanced/logrotate @@ -1,4 +1,5 @@ /var/log/pihole.log { + # su # daily copytruncate rotate 5 diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 9dcceab2..26890641 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -152,15 +152,17 @@ make_repo() { update_repo() { local directory="${1}" + local curdir + curdir="${PWD}" + cd "${directory}" &> /dev/null || return 1 # Pull the latest commits echo -n "::: Updating repo in ${1}..." - if [[ -d "${directory}" ]]; then - cd "${directory}" - git stash -q &> /dev/null || true # Okay for stash failure - git pull -q &> /dev/null || return $? - echo " done!" - fi + git stash --all --quiet &> /dev/null || true # Okay for stash failure + git clean --force -d || true # Okay for already clean directory + git pull --quiet &> /dev/null || return $? + echo " done!" + cd "${curdir}" &> /dev/null || return 1 return 0 } @@ -172,9 +174,13 @@ getGitFiles() { echo ":::" echo "::: Checking for existing repository..." if is_repo "${directory}"; then - update_repo "${directory}" || return 1 + echo -n "::: Updating repository in ${directory}..." + update_repo "${directory}" || { echo "*** Error: Could not update local repository. Contact support."; exit 1; } + echo " done!" else - make_repo "${directory}" "${remoteRepo}" || return 1 + echo -n "::: Cloning ${remoteRepo} into ${directory}..." + make_repo "${directory}" "${remoteRepo}" || { echo "Unable to clone repository, please contact support"; exit 1; } + echo " done!" fi return 0 } @@ -364,7 +370,7 @@ It is also possible to use a DHCP reservation, but if you are going to do that, setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP - echo "## interface ${PIHOLE_INTERFACE} + echo "interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null @@ -837,21 +843,23 @@ installPiholeWeb() { if [ -f "/var/www/html/pihole/blockingpage.css" ]; then echo "::: Existing blockingpage.css detected, not overwriting" else - echo -n "::: index.css missing, replacing... " + echo -n "::: blockingpage.css missing, replacing... " cp /etc/.pihole/advanced/blockingpage.css /var/www/html/pihole echo " done!" fi else - mkdir /var/www/html/pihole + echo "::: Creating directory for blocking page" + install -d /var/www/html/pihole + install -D /etc/.pihole/advanced/{index,blockingpage}.* /var/www/html/pihole/ if [ -f /var/www/html/index.lighttpd.html ]; then mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig else printf "\n:::\tNo default index.lighttpd.html file found... not backing up" fi - cp /etc/.pihole/advanced/index.* /var/www/html/pihole/. echo " done!" fi + # Install Sudoer file echo ":::" echo -n "::: Installing sudoer file..." @@ -987,7 +995,7 @@ installLogrotate() { # the local properties of the /var/log directory logusergroup="$(stat -c '%U %G' /var/log)" if [[ ! -z $logusergroup ]]; then - echo "su ${logusergroup}" >> /etc/pihole/logrotate + sed -i "s/# su #/su ${logusergroup}/" /etc/pihole/logrotate fi echo " done!" } diff --git a/gravity.sh b/gravity.sh index b1cd57a6..1c9da96a 100755 --- a/gravity.sh +++ b/gravity.sh @@ -32,6 +32,7 @@ adListDefault=/etc/pihole/adlists.default whitelistScript="pihole -w" whitelistFile=/etc/pihole/whitelist.txt blacklistFile=/etc/pihole/blacklist.txt +readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" #Source the setupVars from install script for the IP setupVars=/etc/pihole/setupVars.conf @@ -235,6 +236,21 @@ gravity_Blacklist() { } +gravity_Wildcard() { + # Return number of wildcards in output - don't actually handle wildcards + if [[ -f "${wildcardlist}" ]]; then + numWildcards=$(grep -c ^ "${wildcardlist}") + if [[ -n "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then + let numWildcards/=2 + fi + plural=; [[ "$numWildcards" != "1" ]] && plural=s + echo "::: Wildcard blocked domain${plural}: $numWildcards" + else + echo "::: No wildcards used!" + fi + +} + gravity_Whitelist() { #${piholeDir}/${eventHorizon}) echo ":::" @@ -401,6 +417,7 @@ else fi gravity_Whitelist gravity_Blacklist +gravity_Wildcard gravity_hostFormat gravity_blackbody diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 211364ed..f8e2ec3d 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -167,6 +167,113 @@ def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole): assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT' in firewall_calls assert 'iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT' in firewall_calls +def test_installPiholeWeb_fresh_install_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed on a fresh build ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + +def test_installPiholeWeb_empty_directory_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed in an emtpy directory ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + mkdir -p /var/www/html/pihole + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout + assert 'index.php missing, replacing...' in installWeb.stdout + assert 'index.js missing, replacing...' in installWeb.stdout + assert 'blockingpage.css missing, replacing...' in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + +def test_installPiholeWeb_index_php_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed when necessary ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + mkdir -p /var/www/html/pihole + touch /var/www/html/pihole/index.php + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout + assert 'Existing index.php detected, not overwriting' in installWeb.stdout + assert 'index.js missing, replacing...' in installWeb.stdout + assert 'blockingpage.css missing, replacing...' in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + +def test_installPiholeWeb_index_js_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed when necessary ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + mkdir -p /var/www/html/pihole + touch /var/www/html/pihole/index.js + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout + assert 'index.php missing, replacing...' in installWeb.stdout + assert 'Existing index.js detected, not overwriting' in installWeb.stdout + assert 'blockingpage.css missing, replacing...' in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + +def test_installPiholeWeb_blockingpage_css_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed when necessary ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + mkdir -p /var/www/html/pihole + touch /var/www/html/pihole/blockingpage.css + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout + assert 'index.php missing, replacing...' in installWeb.stdout + assert 'index.js missing, replacing...' in installWeb.stdout + assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + +def test_installPiholeWeb_already_populated_no_errors(Pihole): + ''' confirms all web page assets from Core repo are installed when necessary ''' + installWeb = Pihole.run(''' + source /opt/pihole/basic-install.sh + mkdir -p /var/www/html/pihole + touch /var/www/html/pihole/index.php + touch /var/www/html/pihole/index.js + touch /var/www/html/pihole/blockingpage.css + installPiholeWeb + ''') + assert 'Installing pihole custom index page...' in installWeb.stdout + assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout + assert 'Existing index.php detected, not overwriting' in installWeb.stdout + assert 'index.php missing, replacing...' not in installWeb.stdout + assert 'Existing index.js detected, not overwriting' in installWeb.stdout + assert 'index.js missing, replacing...' not in installWeb.stdout + assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout + assert 'blockingpage.css missing, replacing... ' not in installWeb.stdout + web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + assert 'index.php' in web_directory + assert 'index.js' in web_directory + assert 'blockingpage.css' in web_directory + # Helper functions def mock_command(script, args, container): ''' Allows for setup of commands we don't really want to have to run for real in unit tests '''