diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..e5626a07 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,38 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = tab +tab_width = 2 +charset = utf-8 +trim_trailing_whitespace = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,py}] +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Indentation override for all JS under lib directory +[scripts/**.js] +indent_style = space +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c985b972..3014625b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ - [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md). - [] The issue I am reporting can be *replicated* -- [] The issue I'm reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)). +- [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)). **How familiar are you with the codebase?:** diff --git a/.pullapprove.yml b/.pullapprove.yml index 39566b34..188a64f2 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -10,7 +10,7 @@ group_defaults: reset_on_push: enabled: true reject_value: -2 - approve_regex: '^(Approved|:shipit:|:\+1:|Engage)' + approve_regex: '^(Approved|:shipit:|:\+1:|Engage|:taco:)' reject_regex: '^(Rejected|:-1:|Borg)' author_approval: auto: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d342a8b5..8cb7ccb9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,4 +35,5 @@ When requesting or submitting new features, first consider whether it might be u - Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!) - Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles. - Commit Unix line endings. +- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen) - (Optional fun) keep to the theme of Star Trek/black holes/gravity. diff --git a/README.md b/README.md index 0a5f7f4c..75c548e5 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@

- +

## The multi-platform, network-wide ad blocker -Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole blocks ads at the DNS-level, so all your devices are protected. +Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole™ blocks ads at the DNS-level, so all your devices are protected. - Web Browsers - Cell Phones @@ -53,9 +53,9 @@ 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 Pi-hole as their 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? +## 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. +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 @@ -83,7 +83,7 @@ The [gravity.sh](https://github.com/pi-hole/pi-hole/blob/master/gravity.sh) does #### Other Operating Systems -The automated install is only for a clean install of a Debian family or Fedora based system, such as the Raspberry Pi. However, this script will work for most UNIX-like systems, some with some slight **modifications** that we can help you work through. If you can install `dnsmasq` and a Webserver, it should work OK. If there are other platforms you'd like supported, let us know. +The automated install is only for a clean install of a Debian family or Fedora based system, such as the Raspberry Pi. However, this script will work for most UNIX-like systems, some with some slight **modifications** that we can help you work through. If you can install `dnsmasq` and a web server, it should work OK. If there are other platforms you'd like supported, let us know. ### Web Interface @@ -102,7 +102,7 @@ Domains can be whitelisted and blacklisted using either the web interface or the ### Settings -The settings page lets you control and configure your Pi-hole. You can do things like: +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 @@ -113,7 +113,7 @@ The settings page lets you control and configure your Pi-hole. You can do thing #### 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. +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.

@@ -137,14 +137,14 @@ The same output can be achieved on the CLI by running `chronometer.sh -j` 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 +## Pi-hole™ Projects - [An ad blocking Magic Mirror](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware) - [Pi-hole stats in your Mac's menu bar](https://getbitbar.com/plugins/Network/pi-hole.1m.py) - [Get LED alerts for each blocked ad](http://thetimmy.silvernight.org/pages/endisbutton/) - [Pi-hole on Ubuntu 14.04 on VirtualBox](http://hbalagtas.blogspot.com/2016/02/adblocking-with-pi-hole-and-ubuntu-1404.html) - [Docker Pi-hole container (x86 and ARM)](https://hub.docker.com/r/diginc/pi-hole/) -- [Splunk: Pi-hole Visualizser](https://splunkbase.splunk.com/app/3023/) +- [Splunk: Pi-hole Visualiser](https://splunkbase.splunk.com/app/3023/) - [Pi-hole Chrome extension](https://chrome.google.com/webstore/detail/pi-hole-list-editor/hlnoeoejkllgkjbnnnhfolapllcnaglh) ([open source](https://github.com/packtloss/pihole-extension)) - [Go Bananas for CHiP-hole ad blocking](https://www.hackster.io/jacobsalmela/chip-hole-network-wide-ad-blocker-98e037) - [Sky-Hole](http://dlaa.me/blog/post/skyhole) @@ -154,7 +154,7 @@ You can view [real-time stats](https://discourse.pi-hole.net/t/how-do-i-view-my- - [Minibian Pi-hole](http://munkjensen.net/wiki/index.php/See_my_Pi-Hole#Minibian_Pi-hole) - [Windows Tray Stat Application](https://github.com/goldbattle/copernicus) - [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f) -- [Pi-Hole Prometheus exporter](https://github.com/nlamirault/pihole_exporter) : a [Prometheus](https://prometheus.io/) exporter for Pi-Hole +- [Pi-hole Prometheus exporter](https://github.com/nlamirault/pihole_exporter): a [Prometheus](https://prometheus.io/) exporter for Pi-hole - [Pi-hole Droid - open source Android client](https://github.com/friimaind/pi-hole-droid) ## Coverage diff --git a/adlists.default b/adlists.default index 4a5bca3c..cbc1bfb3 100644 --- a/adlists.default +++ b/adlists.default @@ -1,53 +1,23 @@ -## Pi-hole ad-list default sources. Updated 29/10/2016 ######################### -# # -# To make changes to this file: # -# 1. run `cp /etc/pihole/adlists.default /etc/pihole/adlists.list` # -# 2. run `nano /etc/pihole/adlists.list` # -# 3. Uncomment or comment any of the below lists # -# # -# Know of any other lists? Feel free to let us know about them, or add them # -# to this file! # -################################################################################ - # The below list amalgamates several lists we used previously. # See `https://github.com/StevenBlack/hosts` for details +##StevenBlack's list https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts -# Other lists we consider safe: +##MalwareDomains https://mirror1.malwaredomains.com/files/justdomains + +##Cameleon http://sysctl.org/cameleon/hosts + +##Zeustracker https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist + +##Disconnect.me Tracking https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt + +##Disconnect.me Ads https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt -# hosts-file.net list. Updated frequently, but has been known to block legitimate sites. +##Hosts-file.net https://hosts-file.net/ad_servers.txt -# Mahakala list. Has been known to block legitimate domains including the entire .com range. -# Warning: Due to the sheer size of this list, the web admin console will be unresponsive. -#https://adblock.mahakala.is/ - -# ADZHOSTS list. Has been known to block legitimate domains -#http://pilotfiber.dl.sourceforge.net/project/adzhosts/HOSTS.txt - -# Windows 10 telemetry list -#https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/win10/spy.txt - -# Securemecca.com list - Also blocks "adult" sites (pornography/gambling etc) -#http://securemecca.com/Downloads/hosts.txt - -# Quidsup's tracker list -#https://raw.githubusercontent.com/quidsup/notrack/master/trackers.txt - -# Block the BBC News website Breaking News banner -#https://raw.githubusercontent.com/BreakingTheNews/BreakingTheNews.github.io/master/hosts - -# Untested Lists: -#https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt -#https://raw.githubusercontent.com/Dawsey21/Lists/master/main-blacklist.txt -#http://malwaredomains.lehigh.edu/files/domains.txt -# Following two lists should be used simultaneously: (readme https://github.com/notracking/hosts-blocklists/) -#https://raw.github.com/notracking/hosts-blocklists/master/hostnames.txt -#https://raw.github.com/notracking/hosts-blocklists/master/domains.txt -# Combination of several host files on the internet (warning some facebook domains are also blocked but you can go to facebook.com). See https://github.com/mat1th/Dns-add-block for more information. -#https://raw.githubusercontent.com/mat1th/Dns-add-block/master/hosts diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 0ddf1caa..1b157f88 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -25,6 +25,8 @@ addn-hosts=/etc/pihole/local.list domain-needed +localise-queries + bogus-priv no-resolv diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 67ff495b..b5d54e5f 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -8,101 +8,98 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. -#Functions############################################################################################################## +# Functions piLog="/var/log/pihole.log" gravity="/etc/pihole/gravity.list" . /etc/pihole/setupVars.conf function GetFTLData { - # Open connection to FTL - exec 3<>/dev/tcp/localhost/"$(cat /var/run/pihole-FTL.port)" + # Open connection to FTL + exec 3<>/dev/tcp/localhost/"$(cat /var/run/pihole-FTL.port)" - # Test if connection is open - if { >&3; } 2> /dev/null; then - # Send command to FTL - echo -e ">$1" >&3 + # Test if connection is open + if { >&3; } 2> /dev/null; then + # Send command to FTL + echo -e ">$1" >&3 - # Read input + # Read input + read -r -t 1 LINE <&3 + until [ ! $? ] || [[ "$LINE" == *"EOM"* ]]; do + echo "$LINE" >&1 read -r -t 1 LINE <&3 - until [ ! $? ] || [[ "$LINE" == *"EOM"* ]]; do - echo "$LINE" >&1 - read -r -t 1 LINE <&3 - done + done - # Close connection - exec 3>&- - exec 3<&- - fi + # Close connection + exec 3>&- + exec 3<&- + fi } outputJSON() { - get_summary_data - echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}" + get_summary_data + echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}" } get_summary_data() { - local summary=$(GetFTLData "stats") - domains_being_blocked_raw=$(grep "domains_being_blocked" <<< "${summary}" | grep -Eo "[0-9]+$") - domains_being_blocked=$(printf "%'.f" ${domains_being_blocked_raw}) - dns_queries_today_raw=$(grep "dns_queries_today" <<< "$summary" | grep -Eo "[0-9]+$") - dns_queries_today=$(printf "%'.f" ${dns_queries_today_raw}) - ads_blocked_today_raw=$(grep "ads_blocked_today" <<< "$summary" | grep -Eo "[0-9]+$") - ads_blocked_today=$(printf "%'.f" ${ads_blocked_today_raw}) - ads_percentage_today_raw=$(grep "ads_percentage_today" <<< "$summary" | grep -Eo "[0-9.]+$") - LC_NUMERIC=C ads_percentage_today=$(printf "%'.f" ${ads_percentage_today_raw}) + local summary=$(GetFTLData "stats") + domains_being_blocked_raw=$(grep "domains_being_blocked" <<< "${summary}" | grep -Eo "[0-9]+$") + domains_being_blocked=$(printf "%'.f" ${domains_being_blocked_raw}) + dns_queries_today_raw=$(grep "dns_queries_today" <<< "$summary" | grep -Eo "[0-9]+$") + dns_queries_today=$(printf "%'.f" ${dns_queries_today_raw}) + ads_blocked_today_raw=$(grep "ads_blocked_today" <<< "$summary" | grep -Eo "[0-9]+$") + ads_blocked_today=$(printf "%'.f" ${ads_blocked_today_raw}) + ads_percentage_today_raw=$(grep "ads_percentage_today" <<< "$summary" | grep -Eo "[0-9.]+$") + LC_NUMERIC=C ads_percentage_today=$(printf "%'.f" ${ads_percentage_today_raw}) } normalChrono() { - for (( ; ; )); do - get_summary_data - domain=$(GetFTLData recentBlocked) - clear - # Displays a colorful Pi-hole logo - echo " ___ _ _ _" - echo "| _ (_)___| |_ ___| |___" - echo "| _/ |___| ' \/ _ \ / -_)" - echo "|_| |_| |_||_\___/_\___|" - echo "" - echo " ${IPV4_ADDRESS}" - echo "" - uptime | cut -d' ' -f11- - #uptime -p #Doesn't work on all versions of uptime - uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}' - echo "-------------------------------" - echo "Recently blocked:" - echo " $domain" + for (( ; ; )); do + get_summary_data + domain=$(GetFTLData recentBlocked) + clear + # Displays a colorful Pi-hole logo + echo " ___ _ _ _" + echo "| _ (_)___| |_ ___| |___" + echo "| _/ |___| ' \/ _ \ / -_)" + echo "|_| |_| |_||_\___/_\___|" + echo "" + echo " ${IPV4_ADDRESS}" + echo "" + uptime | cut -d' ' -f11- + #uptime -p # Doesn't work on all versions of uptime + uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}' + echo "-------------------------------" + echo "Recently blocked:" + echo " $domain" - echo "Blocking: ${domains_being_blocked}" - echo "Queries: ${dns_queries_today}" - echo "Pi-holed: ${ads_blocked_today} (${ads_percentage_today}%)" + echo "Blocking: ${domains_being_blocked}" + echo "Queries: ${dns_queries_today}" + echo "Pi-holed: ${ads_blocked_today} (${ads_percentage_today}%)" - sleep 5 - done + sleep 5 + done } displayHelp() { - cat << EOM -::: Displays stats about your piHole! -::: -::: Usage: sudo pihole -c [optional:-j] -::: Note: If no option is passed, then stats are displayed on screen, updated every 5 seconds -::: -::: Options: -::: -j, --json output stats as JSON formatted string -::: -h, --help display this help text -EOM - exit 0 + echo "Usage: pihole -c [options] +Example: 'pihole -c -j' +Calculates stats and displays to an LCD + +Options: + -j, --json Output stats as JSON formatted string + -h, --help Display this help text" + exit 0 } if [[ $# = 0 ]]; then - normalChrono + normalChrono fi for var in "$@"; do - case "$var" in - "-j" | "--json" ) outputJSON;; - "-h" | "--help" ) displayHelp;; - * ) exit 1;; - esac + case "$var" in + "-j" | "--json" ) outputJSON;; + "-h" | "--help" ) displayHelp;; + * ) exit 1;; + esac done diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 8eb0543d..378d8402 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -3,14 +3,12 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Whitelists and blacklists domains +# Whitelist and blacklist domains # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - - -#globals +# Globals basename=pihole piholeDir=/etc/${basename} whitelist=${piholeDir}/whitelist.txt @@ -27,123 +25,123 @@ listMain="" listAlt="" helpFunc() { + if [[ "${listMain}" == "${whitelist}" ]]; then + param="w" + type="white" + elif [[ "${listMain}" == "${wildcardlist}" ]]; then + param="wild" + type="wildcard black" + else + param="b" + type="black" + fi - if [[ ${listMain} == ${whitelist} ]]; then - param="w" - word="white" - elif [[ ${listMain} == ${wildcardlist} ]]; then - param="wild" - word="wildcard" - else - param="b" - word="black" - fi + echo "Usage: pihole -${param} [options] +Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com' +${type^}list one or more domains - cat << EOM -::: Immediately add one or more domains to the ${word}list -::: -::: Usage: pihole -${param} domain1 [domain2 ...] -::: -::: Options: -::: -d, --delmode Remove domains from the ${word}list -::: -nr, --noreload Update ${word}list without refreshing dnsmasq -::: -q, --quiet Output is less verbose -::: -h, --help Show this help dialog -::: -l, --list Display domains on the ${word}list -EOM -if [[ "${param}" == "wild" ]]; then - echo "::: -wild, --wildcard Add wildcard entry (only blacklist)" -fi - exit 0 +Options:" + + if [[ "${listMain}" == "${wildcardlist}" ]]; then + echo " -wild, --wildcard Block all subdomains of specified domain" + fi + +echo " -d, --delmode Remove domain(s) from the ${type}list + -nr, --noreload Update ${type}list without refreshing dnsmasq + -q, --quiet Make output less verbose + -h, --help Show this help dialog + -l, --list Display all your ${type}listed domains" + + exit 0 } 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" + # 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" } -HandleOther(){ - # First, convert everything to lowercase - domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") +HandleOther() { + # First, convert everything to lowercase + domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") - #check validity of domain - 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 - domList=("${domList[@]}" ${validDomain}) - fi + # Check validity of domain + 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 + domList=("${domList[@]}" ${validDomain}) + fi } PoplistFile() { - #check whitelist file exists, and if not, create it - if [[ ! -f ${whitelist} ]]; then - touch ${whitelist} - fi - for dom in "${domList[@]}"; do - # Logic : If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other - if ${addmode}; then - AddDomain "${dom}" "${listMain}" - RemoveDomain "${dom}" "${listAlt}" - RemoveDomain "${dom}" "${wildcardlist}" - else - RemoveDomain "${dom}" "${listMain}" - fi - done + # Check whitelist file exists, and if not, create it + if [[ ! -f ${whitelist} ]]; then + touch ${whitelist} + fi + + for dom in "${domList[@]}"; do + # Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other + if ${addmode}; then + AddDomain "${dom}" "${listMain}" + RemoveDomain "${dom}" "${listAlt}" + if [[ "${listMain}" == "${whitelist}" || "${listMain}" == "${blacklist}" ]]; then + RemoveDomain "${dom}" "${wildcardlist}" + fi + else + RemoveDomain "${dom}" "${listMain}" + fi + done } AddDomain() { - list="$2" - domain=$(EscapeRegexp "$1") + list="$2" + domain=$(EscapeRegexp "$1") - if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then + if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then + bool=true + # Is the domain in the list we want to add it to? + grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false - bool=true - #Is the domain in the list we want to add it to? - grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == false ]]; then + # Domain not found in the whitelist file, add it! + if [[ "${verbose}" == true ]]; then + echo "::: Adding $1 to $list..." + fi + reload=true + # Add it to the list we want to add it to + echo "$1" >> "${list}" + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} already exists in ${list}, no need to add!" + fi + fi + elif [[ "${list}" == "${wildcardlist}" ]]; then + source "${piholeDir}/setupVars.conf" + # Remove the /* from the end of the IPv4addr. + IPV4_ADDRESS=${IPV4_ADDRESS%/*} + IPV6_ADDRESS=${IPV6_ADDRESS} - if [[ "${bool}" == false ]]; then - #domain not found in the whitelist file, add it! - if [[ "${verbose}" == true ]]; then - echo "::: Adding $1 to $list..." - fi - reload=true - # Add it to the list we want to add it to - echo "$1" >> "${list}" - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} already exists in ${list}, no need to add!" - fi - fi + bool=true + # Is the domain in the list? + grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - elif [[ "${list}" == "${wildcardlist}" ]]; then - - source "${piholeDir}/setupVars.conf" - #Remove the /* from the end of the IPv4addr. - IPV4_ADDRESS=${IPV4_ADDRESS%/*} - IPV6_ADDRESS=${IPV6_ADDRESS} - - bool=true - #Is the domain in the list? - grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - - if [[ "${bool}" == false ]]; then - if [[ "${verbose}" == true ]]; then - echo "::: Adding $1 to wildcard blacklist..." - fi - reload=true - echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}" - if [[ ${#IPV6_ADDRESS} > 0 ]] ; then - echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}" - fi - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} already exists in wildcard blacklist, no need to add!" - fi - fi - fi + if [[ "${bool}" == false ]]; then + if [[ "${verbose}" == true ]]; then + echo "::: Adding $1 to wildcard blacklist..." + fi + reload=true + echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}" + if [[ "${#IPV6_ADDRESS}" > 0 ]]; then + echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}" + fi + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} already exists in wildcard blacklist, no need to add!" + fi + fi + fi } RemoveDomain() { @@ -151,85 +149,82 @@ RemoveDomain() { domain=$(EscapeRegexp "$1") if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then - - bool=true - #Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa - grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false - if [[ "${bool}" == true ]]; then - # Remove it from the other one - echo "::: Removing $1 from $list..." - # /I flag: search case-insensitive - sed -i "/${domain}/Id" "${list}" - reload=true - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} does not exist in ${list}, no need to remove!" - fi + bool=true + # Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa + grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == true ]]; then + # Remove it from the other one + echo "::: Removing $1 from $list..." + # /I flag: search case-insensitive + sed -i "/${domain}/Id" "${list}" + reload=true + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} does not exist in ${list}, no need to remove!" fi - + fi elif [[ "${list}" == "${wildcardlist}" ]]; then - - bool=true - #Is it in the list? - grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - if [[ "${bool}" == true ]]; then - # Remove it from the other one - echo "::: Removing $1 from $list..." - # /I flag: search case-insensitive - sed -i "/address=\/${domain}/Id" "${list}" - reload=true - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} does not exist in ${list}, no need to remove!" - fi + bool=true + # Is it in the list? + grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == true ]]; then + # Remove it from the other one + echo "::: Removing $1 from $list..." + # /I flag: search case-insensitive + sed -i "/address=\/${domain}/Id" "${list}" + reload=true + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} does not exist in ${list}, no need to remove!" fi + fi fi } Reload() { - # Reload hosts file - pihole -g -sd + # Reload hosts file + pihole -g -sd } Displaylist() { - if [[ ${listMain} == ${whitelist} ]]; then - string="gravity resistant domains" - else - string="domains caught in the sinkhole" - fi - verbose=false - echo -e " Displaying $string \n" - count=1 - while IFS= read -r RD; do - echo "${count}: ${RD}" - count=$((count+1)) - done < "${listMain}" - exit 0; + if [[ "${listMain}" == "${whitelist}" ]]; then + string="gravity resistant domains" + else + string="domains caught in the sinkhole" + fi + verbose=false + echo -e "Displaying $string:\n" + count=1 + while IFS= read -r RD; do + echo "${count}: ${RD}" + count=$((count+1)) + done < "${listMain}" + exit 0; } for var in "$@"; do - case "${var}" in - "-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";; - "-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";; - "-wild" | "wildcard" ) listMain="${wildcardlist}";; - "-nr"| "--noreload" ) reload=false;; - "-d" | "--delmode" ) addmode=false;; - "-f" | "--force" ) force=true;; - "-q" | "--quiet" ) verbose=false;; - "-h" | "--help" ) helpFunc;; - "-l" | "--list" ) Displaylist;; - * ) HandleOther "${var}";; - esac + case "${var}" in + "-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";; + "-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";; + "-wild" | "wildcard" ) listMain="${wildcardlist}";; + "-nr"| "--noreload" ) reload=false;; + "-d" | "--delmode" ) addmode=false;; + "-f" | "--force" ) force=true;; + "-q" | "--quiet" ) verbose=false;; + "-h" | "--help" ) helpFunc;; + "-l" | "--list" ) Displaylist;; + * ) HandleOther "${var}";; + esac done shift if [[ $# = 0 ]]; then - helpFunc + helpFunc fi PoplistFile if ${reload}; then - Reload + Reload fi diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 3b7abbef..286edb2e 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -3,7 +3,7 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Checkout other branches than master +# Switch Pi-hole subsystems to a different Github branch # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. @@ -18,9 +18,12 @@ PH_TEST="true" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" # setupVars set in basic-install.sh source "${setupVars}" - update="false" +# Colour codes +red="\e[1;31m" +def="\e[0m" + fully_fetch_repo() { # Add upstream branches to shallow clone local directory="${1}" @@ -35,7 +38,7 @@ fully_fetch_repo() { return 0 } -get_available_branches(){ +get_available_branches() { # Return available branches local directory="${1}" @@ -54,6 +57,8 @@ fetch_checkout_pull_branch() { # Set the reference for the requested branch, fetch, check it put and pull it cd "${directory}" git remote set-branches origin "${branch}" || return 1 + git stash --all --quiet &> /dev/null || true + git clean --force -d || true git fetch --quiet || return 1 checkout_pull_branch "${directory}" "${branch}" || return 1 } @@ -79,23 +84,23 @@ checkout_pull_branch() { } warning1() { - echo "::: Note that changing the branch is a severe change of your Pi-hole system." - echo "::: This is not supported unless one of the developers explicitly asks you to do this!" - read -r -p "::: Have you read and understood this? [y/N] " response + echo " Please note that changing branches severely alters your Pi-hole subsystems" + echo " Features that work on the master branch, may not on a development branch" + echo -e " ${red}This feature is NOT supported unless a Pi-hole developer explicitly asks!${def}" + read -r -p " Have you read and understood this? [Y/N] " response case ${response} in [yY][eE][sS]|[yY]) - echo "::: Continuing." + echo "::: Continuing with branch change." return 0 ;; *) - echo "::: Aborting." + echo "::: Branch change has been cancelled." return 1 ;; esac } -checkout() -{ +checkout() { local corebranches local webbranches @@ -104,7 +109,7 @@ checkout() #This is unlikely if ! is_repo "${PI_HOLE_FILES_DIR}" ; then - echo "::: Critical Error: Core Pi-Hole repo is missing from system!" + echo "::: Critical Error: Core Pi-hole repo is missing from system!" echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" exit 1; fi @@ -192,11 +197,10 @@ checkout() if [[ ! "${1}" == "web" && "${update}" == "true" ]]; then echo "::: Running installer to upgrade your installation" if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then - exit 0 + exit 0 else - echo "Unable to complete update, contact Pi-hole" - exit 1 + echo "Unable to complete update, contact Pi-hole" + exit 1 fi fi } - diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index df9ed4f0..8020cc80 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -27,6 +27,7 @@ PIHOLELOG="/var/log/pihole.log" PIHOLEGITDIR="/etc/.pihole/" ADMINGITDIR="/var/www/html/admin/" WHITELISTMATCHES="/tmp/whitelistmatches.list" +readonly FTLLOG="/var/log/pihole-FTL.log" TIMEOUT=60 # Header info and introduction @@ -43,16 +44,11 @@ cat << EOM ::: Please read and note any issues, and follow any directions advised during this process. EOM -# Ensure the file exists, create if not, clear if exists. -truncate --size=0 "${DEBUG_LOG}" -chmod 644 ${DEBUG_LOG} -chown "$USER":pihole ${DEBUG_LOG} - source ${VARSFILE} ### Private functions exist here ### log_write() { - echo "${1}" >> "${DEBUG_LOG}" + echo "${@}" >&3 } log_echo() { @@ -77,14 +73,14 @@ log_echo() { header_write() { log_echo "" - log_echo "${1}" + log_echo "---= ${1}" log_write "" } file_parse() { while read -r line; do if [ ! -z "${line}" ]; then - [[ "${line}" =~ ^#.*$ || ! "${line}" ]] && continue + [[ "${line}" =~ ^#.*$ || ! "${line}" || "${line}" == "WEBPASSWORD="* ]] && continue log_write "${line}" fi done < "${1}" @@ -231,6 +227,7 @@ ipv6_check() { ip_check() { local protocol=${1} local gravity=${2} + header_write "Checking IPv${protocol} Stack" local ip_addr_list="$(ip -${protocol} addr show dev ${PIHOLE_INTERFACE} | awk -F ' ' '{ for(i=1;i<=NF;i++) if ($i ~ '/^inet/') print $(i+1) }')" if [[ -n ${ip_addr_list} ]]; then @@ -263,18 +260,18 @@ ip_ping_check() { if [[ -n ${ip_def_gateway} ]]; then echo -n "::: Pinging default IPv${protocol} gateway: " if ! ping_gateway="$(${cmd} -q -W 3 -c 3 -n ${ip_def_gateway} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then - echo "Gateway did not respond." + log_echo "Gateway did not respond." return 1 else - echo "Gateway responded." + log_echo "Gateway responded." log_write "${ping_gateway}" fi echo -n "::: Pinging Internet via IPv${protocol}: " if ! ping_inet="$(${cmd} -q -W 3 -c 3 -n ${g_addr} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then - echo "Query did not respond." + log_echo "Query did not respond." return 1 else - echo "Query responded." + log_echo "Query responded." log_write "${ping_inet}" fi else @@ -363,9 +360,9 @@ testChaos(){ log_write "Pi-hole dnsmasq specific records lookups" log_write "Cache Size:" - dig +short chaos txt cachesize.bind >> ${DEBUG_LOG} + log_write $(dig +short chaos txt cachesize.bind) log_write "Upstream Servers:" - dig +short chaos txt servers.bind >> ${DEBUG_LOG} + log_write $(dig +short chaos txt servers.bind) log_write "" } @@ -375,10 +372,8 @@ checkProcesses() { echo "::: Logging status of lighttpd, dnsmasq and pihole-FTL..." PROCESSES=( lighttpd dnsmasq pihole-FTL ) for i in "${PROCESSES[@]}"; do - log_write "" - log_write "${i}" - log_write " processes status:" - systemctl -l status "${i}" >> "${DEBUG_LOG}" + log_write "Status for ${i} daemon:" + log_write $(systemctl is-active "${i}") done log_write "" } @@ -417,7 +412,7 @@ dumpPiHoleLog() { if [ -e "${PIHOLELOG}" ]; then # Dummy process to use for flagging down tail to terminate countdown & - tail -n0 -f --pid=$! "${PIHOLELOG}" >> ${DEBUG_LOG} + tail -n0 -f --pid=$! "${PIHOLELOG}" >&4 else log_write "No pihole.log file found!" printf ":::\tNo pihole.log file found!\n" @@ -428,6 +423,16 @@ dumpPiHoleLog() { finalWork() { local tricorder echo "::: Finshed debugging!" + + # Ensure the file exists, create if not, clear if exists. + truncate --size=0 "${DEBUG_LOG}" + chmod 644 ${DEBUG_LOG} + chown "$USER":pihole ${DEBUG_LOG} + # copy working temp file to final log location + cat /proc/$$/fd/3 >> "${DEBUG_LOG}" + # Straight dump of tailing the logs, can sanitize later if needed. + cat /proc/$$/fd/4 >> "${DEBUG_LOG}" + echo "::: The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only." if [[ "${AUTOMATED}" ]]; then echo "::: Debug script running in automated mode, uploading log to tricorder..." @@ -456,6 +461,17 @@ finalWork() { } ### END FUNCTIONS ### +# Create temporary file for log +TEMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX) +# Open handle 3 for templog +exec 3>"$TEMPLOG" +# Delete templog, but allow for addressing via file handle. +rm "$TEMPLOG" + +# Create temporary file for logdump using file handle 4 +DUMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX) +exec 4>"$DUMPLOG" +rm "$DUMPLOG" # Gather version of required packages / repositories version_check || echo "REQUIRED FILES MISSING" @@ -508,6 +524,18 @@ header_write "Analyzing pihole.log" && log_write "${PIHOLELOG} is ${pihole_size}." \ || log_echo "Warning: No pihole.log file found!" +header_write "Analyzing pihole-FTL.log" + + FTL_length=$(grep -c ^ "${FTLLOG}") \ + && log_write "${FTLLOG} is ${FTL_length} lines long." \ + || log_echo "Warning: No pihole-FTL.log file found!" + + FTL_size=$(du -h "${FTLLOG}" | awk '{ print $1 }') \ + && log_write "${FTLLOG} is ${FTL_size}." \ + || log_echo "Warning: No pihole-FTL.log file found!" + +tail -n50 "${FTLLOG}" >&3 + trap finalWork EXIT ### Method calls for additional logging ### diff --git a/advanced/Scripts/piholeLogFlush.sh b/advanced/Scripts/piholeLogFlush.sh index 63754db2..8e4c8266 100755 --- a/advanced/Scripts/piholeLogFlush.sh +++ b/advanced/Scripts/piholeLogFlush.sh @@ -3,18 +3,22 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Flushes /var/log/pihole.log +# Flushes Pi-hole's log file # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - - echo -n "::: Flushing /var/log/pihole.log ..." # Test if logrotate is available on this system -if command -v /usr/sbin/logrotate &> /dev/null; then +if command -v /usr/sbin/logrotate >/dev/null; then + # Flush twice to move all data out of sight of FTL + /usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3 /usr/sbin/logrotate --force /etc/pihole/logrotate else + # Flush both pihole.log and pihole.log.1 (if existing) echo " " > /var/log/pihole.log + if [ -f /var/log/pihole.log.1 ]; then + echo " " > /var/log/pihole.log.1 + fi fi echo "... done!" diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 4a2e4045..4fceb931 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -90,10 +90,11 @@ FTLcheckUpdate() { main() { local pihole_version_current local web_version_current + source "${setupVars}" #This is unlikely if ! is_repo "${PI_HOLE_FILES_DIR}" ; then - echo "::: Critical Error: Core Pi-Hole repo is missing from system!" + echo "::: Critical Error: Core Pi-hole repo is missing from system!" echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" exit 1; fi diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh index 7f96e29a..f5e0f51d 100755 --- a/advanced/Scripts/version.sh +++ b/advanced/Scripts/version.sh @@ -3,24 +3,29 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# shows version numbers +# Show version numbers # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. # Variables DEFAULT="-1" -PHGITDIR="/etc/.pihole/" +COREGITDIR="/etc/.pihole/" WEBGITDIR="/var/www/html/admin/" getLocalVersion() { + # FTL requires a different method + if [[ "$1" == "FTL" ]]; then + pihole-FTL version + return 0 + fi + # Get the tagged version of the local repository local directory="${1}" local version - cd "${directory}" || { echo "${DEFAULT}"; return 1; } - version=$(git describe --tags --always || \ - echo "${DEFAULT}") + cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; } + version=$(git describe --tags --always || echo "$DEFAULT") if [[ "${version}" =~ ^v ]]; then echo "${version}" elif [[ "${version}" == "${DEFAULT}" ]]; then @@ -33,13 +38,18 @@ getLocalVersion() { } getLocalHash() { + # Local FTL hash does not exist on filesystem + if [[ "$1" == "FTL" ]]; then + echo "N/A" + return 0 + fi + # Get the short hash of the local repository local directory="${1}" local hash - cd "${directory}" || { echo "${DEFAULT}"; return 1; } - hash=$(git rev-parse --short HEAD || \ - echo "${DEFAULT}") + cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; } + hash=$(git rev-parse --short HEAD || echo "$DEFAULT") if [[ "${hash}" == "${DEFAULT}" ]]; then echo "ERROR" return 1 @@ -49,12 +59,33 @@ getLocalHash() { return 0 } +getRemoteHash(){ + # Remote FTL hash is not applicable + if [[ "$1" == "FTL" ]]; then + echo "N/A" + return 0 + fi + + local daemon="${1}" + local branch="${2}" + + hash=$(git ls-remote --heads "https://github.com/pi-hole/${daemon}" | \ + awk -v bra="$branch" '$0~bra {print substr($0,0,8);exit}') + if [[ -n "$hash" ]]; then + echo "$hash" + else + echo "ERROR" + return 1 + fi + return 0 +} + getRemoteVersion(){ # Get the version from the remote origin local daemon="${1}" local version - version=$(curl --silent --fail https://api.github.com/repos/pi-hole/${daemon}/releases/latest | \ + version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \ awk -F: '$1 ~/tag_name/ { print $2 }' | \ tr -cd '[[:alnum:]]._-') if [[ "${version}" =~ ^v ]]; then @@ -66,72 +97,72 @@ getRemoteVersion(){ return 0 } -#PHHASHLATEST=$(curl -s https://api.github.com/repos/pi-hole/pi-hole/commits/master | \ -# grep sha | \ -# head -n1 | \ -# awk -F ' ' '{ print $2 }' | \ -# tr -cd '[[:alnum:]]._-') - -#WEBHASHLATEST=$(curl -s https://api.github.com/repos/pi-hole/AdminLTE/commits/master | \ -# grep sha | \ -# head -n1 | \ -# awk -F ' ' '{ print $2 }' | \ -# tr -cd '[[:alnum:]]._-') - - -normalOutput() { - echo "::: Pi-hole version is $(getLocalVersion "${PHGITDIR}") (Latest version is $(getRemoteVersion pi-hole))" - if [ -d "${WEBGITDIR}" ]; then - echo "::: Web-Admin version is $(getLocalVersion "${WEBGITDIR}") (Latest version is $(getRemoteVersion AdminLTE))" - fi -} - -webOutput() { - if [ -d "${WEBGITDIR}" ]; then - case "${1}" in - "-l" | "--latest" ) echo $(getRemoteVersion AdminLTE);; - "-c" | "--current" ) echo $(getLocalVersion "${WEBGITDIR}");; - "-h" | "--hash" ) echo $(getLocalHash "${WEBGITDIR}");; - * ) echo "::: Invalid Option!"; exit 1; - esac - else - echo "::: Web interface not installed!"; exit 1; +versionOutput() { + [[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR + [[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR + [[ "$1" == "FTL" ]] && GITDIR="FTL" + + [[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR) + [[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1") + if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then + [[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR") + [[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)") fi + + if [[ -n "$current" ]] && [[ -n "$latest" ]]; then + output="${1^} version is $current (Latest: $latest)" + elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then + output="Current ${1^} version is $current" + elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then + output="Latest ${1^} version is $latest" + elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then + output="${1^} hash is not applicable" + elif [[ -n "$curHash" ]] && [[ -n "$latHash" ]]; then + output="${1^} hash is $curHash (Latest: $latHash)" + elif [[ -n "$curHash" ]] && [[ -z "$latHash" ]]; then + output="Current ${1^} hash is $curHash" + elif [[ -z "$curHash" ]] && [[ -n "$latHash" ]]; then + output="Latest ${1^} hash is $latHash" + else + errorOutput + fi + + [[ -n "$output" ]] && echo " $output" } -coreOutput() { - case "${1}" in - "-l" | "--latest" ) echo $(getRemoteVersion pi-hole);; - "-c" | "--current" ) echo $(getLocalVersion "${PHGITDIR}");; - "-h" | "--hash" ) echo $(getLocalHash "${PHGITDIR}");; - * ) echo "::: Invalid Option!"; exit 1; - esac +errorOutput() { + echo " Invalid Option! Try 'pihole -v --help' for more information." + exit 1 +} + +defaultOutput() { + versionOutput "pi-hole" "$@" + versionOutput "AdminLTE" "$@" + versionOutput "FTL" "$@" } helpFunc() { - cat << EOM -::: -::: Show Pi-hole/Web Admin versions -::: -::: Usage: pihole -v [ -a | -p ] [ -l | -c ] -::: -::: Options: -::: -a, --admin Show both current and latest versions of web admin -::: -p, --pihole Show both current and latest versions of Pi-hole core files -::: -l, --latest (Only after -a | -p) Return only latest version -::: -c, --current (Only after -a | -p) Return only current version -::: -h, --help Show this help dialog -::: -EOM - exit 0 + echo "Usage: pihole -v [repo | option] [option] +Example: 'pihole -v -p -l' +Show Pi-hole, Admin Console & FTL versions + +Repositories: + -p, --pihole Only retrieve info regarding Pi-hole repository + -a, --admin Only retrieve info regarding AdminLTE repository + -f, --ftl Only retrieve info regarding FTL repository + +Options: + -c, --current Return the current version + -l, --latest Return the latest version + --hash Return the Github hash from your local repositories + -h, --help Show this help dialog" + exit 0 } -if [[ $# = 0 ]]; then - normalOutput -fi - case "${1}" in - "-a" | "--admin" ) shift; webOutput "$@";; - "-p" | "--pihole" ) shift; coreOutput "$@" ;; + "-p" | "--pihole" ) shift; versionOutput "pi-hole" "$@";; + "-a" | "--admin" ) shift; versionOutput "AdminLTE" "$@";; + "-f" | "--ftl" ) shift; versionOutput "FTL" "$@";; "-h" | "--help" ) helpFunc;; + * ) defaultOutput "$@";; esac diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 9637cc6f..8419aa8d 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -8,7 +8,6 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - readonly setupVars="/etc/pihole/setupVars.conf" readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf" readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" @@ -16,23 +15,19 @@ readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" helpFunc() { - cat << EOM -::: Set admin options for the web interface of pihole -::: -::: Usage: pihole -a [options] -::: -::: Options: -::: -p, password Set web interface password, an empty input will remove any previously set password -::: -c, celsius Set Celsius temperature unit -::: -f, fahrenheit Set Fahrenheit temperature unit -::: -k, kelvin Set Kelvin temperature unit -::: -h, --help Show this help dialog -::: -i, interface Setup interface listening behavior of dnsmasq -::: pihole -a -i local : Listen on all interfaces, but allow only queries from -::: devices that are at most one hop away (local devices) -::: pihole -a -i single : Listen only on one interface (see PIHOLE_INTERFACE) -::: pihole -a -i all : Listen on all interfaces, permit all origins -EOM + echo "Usage: pihole -a [options] +Example: pihole -a -p password +Set options for the Admin Console + +Options: + -f, flush Flush the Pi-hole log + -p, password Set Admin Console password + -c, celsius Set Celsius as preferred temperature unit + -f, fahrenheit Set Fahrenheit as preferred temperature unit + -k, kelvin Set Kelvin as preferred temperature unit + -h, --help Show this help dialog + -i, interface Specify dnsmasq's interface listening behavior + Add '-h' for more info on interface usage" exit 0 } @@ -61,14 +56,18 @@ delete_dnsmasq_setting() { sed -i "/${1}/d" "${dnsmasqconfig}" } -SetTemperatureUnit(){ - +SetTemperatureUnit() { change_setting "TEMPERATUREUNIT" "${unit}" - } -SetWebPassword(){ +HashPassword() { + # Compute password hash twice to avoid rainbow table vulnerability + return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//') + return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//') + echo ${return} +} +SetWebPassword() { if [ "${SUDO_USER}" == "www-data" ]; then echo "Security measure: user www-data is not allowed to change webUI password!" echo "Exiting" @@ -81,19 +80,32 @@ SetWebPassword(){ exit 1 fi - # Set password only if there is one to be set - if (( ${#args[2]} > 0 )) ; then - # Compute password hash twice to avoid rainbow table vulnerability - hash=$(echo -n ${args[2]} | sha256sum | sed 's/\s.*$//') - hash=$(echo -n ${hash} | sha256sum | sed 's/\s.*$//') + if (( ${#args[2]} > 0 )) ; then + readonly PASSWORD="${args[2]}" + readonly CONFIRM="${PASSWORD}" + else + read -s -p "Enter New Password (Blank for no password): " PASSWORD + echo "" + + if [ "${PASSWORD}" == "" ]; then + change_setting "WEBPASSWORD" "" + echo "Password Removed" + exit 0 + fi + + read -s -p "Confirm Password: " CONFIRM + echo "" + fi + + if [ "${PASSWORD}" == "${CONFIRM}" ] ; then + hash=$(HashPassword ${PASSWORD}) # Save hash to file change_setting "WEBPASSWORD" "${hash}" echo "New password set" else - change_setting "WEBPASSWORD" "" - echo "Password removed" + echo "Passwords don't match. Your password has not been changed" + exit 1 fi - } ProcessDNSSettings() { @@ -155,8 +167,7 @@ trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE3 } -SetDNSServers(){ - +SetDNSServers() { # Save setting to file delete_setting "PIHOLE_DNS" IFS=',' read -r -a array <<< "${args[2]}" @@ -187,72 +198,59 @@ SetDNSServers(){ # Restart dnsmasq to load new configuration RestartDNS - } -SetExcludeDomains(){ - +SetExcludeDomains() { change_setting "API_EXCLUDE_DOMAINS" "${args[2]}" - } -SetExcludeClients(){ - +SetExcludeClients() { change_setting "API_EXCLUDE_CLIENTS" "${args[2]}" - } -Reboot(){ - +Reboot() { nohup bash -c "sleep 5; reboot" &> /dev/null /dev/null else service dnsmasq restart &> /dev/null fi - } -SetQueryLogOptions(){ - +SetQueryLogOptions() { change_setting "API_QUERY_LOG_SHOW" "${args[2]}" - } ProcessDHCPSettings() { - source "${setupVars}" if [[ "${DHCP_ACTIVE}" == "true" ]]; then + interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//") - interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//") + # Use eth0 as fallback interface + if [ -z ${interface} ]; then + interface="eth0" + fi - # Use eth0 as fallback interface - if [ -z ${interface} ]; then - interface="eth0" - fi + if [[ "${PIHOLE_DOMAIN}" == "" ]]; then + PIHOLE_DOMAIN="local" + change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" + fi - if [[ "${PIHOLE_DOMAIN}" == "" ]]; then - PIHOLE_DOMAIN="local" - change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" - fi + if [[ "${DHCP_LEASETIME}" == "0" ]]; then + leasetime="infinite" + elif [[ "${DHCP_LEASETIME}" == "" ]]; then + leasetime="24h" + change_setting "DHCP_LEASETIME" "${leasetime}" + else + leasetime="${DHCP_LEASETIME}h" + fi - if [[ "${DHCP_LEASETIME}" == "0" ]]; then - leasetime="infinite" - elif [[ "${DHCP_LEASETIME}" == "" ]]; then - leasetime="24h" - change_setting "DHCP_LEASETIME" "${leasetime}" - else - leasetime="${DHCP_LEASETIME}h" - fi - - # Write settings to file - echo "############################################################################### + # Write settings to file + echo "############################################################################### # DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. # # ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE # ############################################################################### @@ -263,26 +261,25 @@ dhcp-leasefile=/etc/pihole/dhcp.leases #quiet-dhcp " > "${dhcpconfig}" -if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then - echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" -fi + if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then + echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" + fi - if [[ "${DHCP_IPv6}" == "true" ]]; then -echo "#quiet-dhcp6 + if [[ "${DHCP_IPv6}" == "true" ]]; then + echo "#quiet-dhcp6 #enable-ra dhcp-option=option6:dns-server,[::] dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime} ra-param=*,0,0 " >> "${dhcpconfig}" - fi + fi else rm "${dhcpconfig}" &> /dev/null fi } -EnableDHCP(){ - +EnableDHCP() { change_setting "DHCP_ACTIVE" "true" change_setting "DHCP_START" "${args[2]}" change_setting "DHCP_END" "${args[3]}" @@ -300,8 +297,7 @@ EnableDHCP(){ RestartDNS } -DisableDHCP(){ - +DisableDHCP() { change_setting "DHCP_ACTIVE" "false" # Remove possible old setting from file @@ -313,24 +309,37 @@ DisableDHCP(){ RestartDNS } -SetWebUILayout(){ - +SetWebUILayout() { change_setting "WEBUIBOXEDLAYOUT" "${args[2]}" - } -SetPrivacyMode(){ +CustomizeAdLists() { + list="/etc/pihole/adlists.list" - if [[ "${args[2]}" == "true" ]] ; then + if [[ "${args[2]}" == "enable" ]]; then + sed -i "\\@${args[3]}@s/^#http/http/g" "${list}" + elif [[ "${args[2]}" == "disable" ]]; then + sed -i "\\@${args[3]}@s/^http/#http/g" "${list}" + elif [[ "${args[2]}" == "add" ]]; then + echo "${args[3]}" >> ${list} + elif [[ "${args[2]}" == "del" ]]; then + var=$(echo "${args[3]}" | sed 's/\//\\\//g') + sed -i "/${var}/Id" "${list}" + else + echo "Not permitted" + return 1 + fi +} + +SetPrivacyMode() { + if [[ "${args[2]}" == "true" ]]; then change_setting "API_PRIVACY_MODE" "true" else change_setting "API_PRIVACY_MODE" "false" fi - } ResolutionSettings() { - typ="${args[2]}" state="${args[3]}" @@ -339,11 +348,9 @@ ResolutionSettings() { elif [[ "${typ}" == "clients" ]]; then change_setting "API_GET_CLIENT_HOSTNAME" "${state}" fi - } AddDHCPStaticAddress() { - mac="${args[2]}" ip="${args[3]}" host="${args[4]}" @@ -358,18 +365,14 @@ AddDHCPStaticAddress() { # Full info given echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}" fi - } RemoveDHCPStaticAddress() { - mac="${args[2]}" sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" - } -SetHostRecord(){ - +SetHostRecord() { if [ -n "${args[3]}" ]; then change_setting "HOSTRECORD" "${args[2]},${args[3]}" echo "Setting host record for ${args[2]} -> ${args[3]}" @@ -382,17 +385,28 @@ SetHostRecord(){ # Restart dnsmasq to load new configuration RestartDNS - } -SetListeningMode(){ - +SetListeningMode() { source "${setupVars}" + + if [[ "$3" == "-h" ]] || [[ "$3" == "--help" ]]; then + echo "Usage: pihole -a -i [interface] +Example: 'pihole -a -i local' +Specify dnsmasq's network interface listening behavior - if [[ "${args[2]}" == "all" ]] ; then +Interfaces: + local Listen on all interfaces, but only allow queries from + devices that are at most one hop away (local devices) + single Listen only on ${PIHOLE_INTERFACE} interface + all Listen on all interfaces, permit all origins" + exit 0 + fi + + if [[ "${args[2]}" == "all" ]]; then echo "Listening on all interfaces, permiting all origins, hope you have a firewall!" change_setting "DNSMASQ_LISTENING" "all" - elif [[ "${args[2]}" == "local" ]] ; then + elif [[ "${args[2]}" == "local" ]]; then echo "Listening on all interfaces, permitting only origins that are at most one hop away (local devices)" change_setting "DNSMASQ_LISTENING" "local" else @@ -407,17 +421,14 @@ SetListeningMode(){ # Restart dnsmasq to load new configuration RestartDNS fi - } -Teleporter() -{ +Teleporter() { local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S") php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip" } main() { - args=("$@") case "${args[1]}" in @@ -440,8 +451,9 @@ main() { "addstaticdhcp" ) AddDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;; "hostrecord" ) SetHostRecord;; - "-i" | "interface" ) SetListeningMode;; + "-i" | "interface" ) SetListeningMode "$@";; "-t" | "teleporter" ) Teleporter;; + "adlist" ) CustomizeAdLists;; * ) helpFunc;; esac @@ -450,5 +462,4 @@ main() { if [[ $# = 0 ]]; then helpFunc fi - } diff --git a/advanced/index.php b/advanced/index.php index c076f92d..1dd5acc7 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -1,15 +1,55 @@ + Website Blocked @@ -144,7 +185,7 @@ function add() { } $.ajax({ - url: "admin/scripts/pi-hole/php/add.php", + url: "/admin/scripts/pi-hole/php/add.php", method: "post", data: {"domain":domain.val(), "list":"white", "pw":pw.val()}, success: function(response) { diff --git a/advanced/pihole-FTL.service b/advanced/pihole-FTL.service index da04738b..30cd140f 100644 --- a/advanced/pihole-FTL.service +++ b/advanced/pihole-FTL.service @@ -13,11 +13,11 @@ FTLUSER=pihole PIDFILE=/var/run/pihole-FTL.pid get_pid() { - cat "$PIDFILE" + pidof "pihole-FTL" } is_running() { - [ -f "$PIDFILE" ] && ps $(get_pid) > /dev/null 2>&1 + ps "$(get_pid)" > /dev/null 2>&1 } # Start the service @@ -36,8 +36,8 @@ start() { # Stop the service stop() { if is_running; then - kill $(get_pid) - for i in {1..10}; do + kill "$(get_pid)" + for i in {1..5}; do if ! is_running; then break fi @@ -48,7 +48,8 @@ stop() { echo if is_running; then - echo "Not stopped; may still be shutting down or shutdown may have failed" + echo "Not stopped; may still be shutting down or shutdown may have failed, killing now" + kill -9 "$(get_pid)" exit 1 else echo "Stopped" @@ -61,16 +62,13 @@ stop() { ### main logic ### case "$1" in - start) - start - ;; stop) stop ;; status) status pihole-FTL ;; - restart|reload|condrestart) + start|restart|reload|condrestart) stop start ;; diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 7c9a83d5..7d5a5d0e 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -86,7 +86,7 @@ if command -v apt-get &> /dev/null; then #Debian Family ############################################# PKG_MANAGER="apt-get" - UPDATE_PKG_CACHE="test_dpkg_lock; ${PKG_MANAGER} update" + UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_INSTALL=(${PKG_MANAGER} --yes --no-install-recommends install) # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" @@ -105,7 +105,7 @@ if command -v apt-get &> /dev/null; then phpVer="php5" fi # ######################################### - INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git ${iproute_pkg} whiptail) + INSTALLER_DEPS=(apt-utils dialog debconf dhcpcd5 git ${iproute_pkg} whiptail) PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils iputils-ping lsof netcat sudo unzip wget) PIHOLE_WEB_DEPS=(lighttpd ${phpVer}-common ${phpVer}-cgi) LIGHTTPD_USER="www-data" @@ -136,7 +136,7 @@ elif command -v rpm &> /dev/null; then UPDATE_PKG_CACHE=":" PKG_INSTALL=(${PKG_MANAGER} install -y) PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" - INSTALLER_DEPS=(git iproute net-tools newt procps-ng) + INSTALLER_DEPS=(dialog git iproute net-tools newt procps-ng) PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq findutils nmap-ncat sudo unzip wget) PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php php-common php-cli) if ! grep -q 'Fedora' /etc/redhat-release; then @@ -233,7 +233,7 @@ find_IPv4_information() { get_available_interfaces() { # Get available UP interfaces. - availableInterfaces=$(ip -o link | grep -v "state DOWN\|lo" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1) + availableInterfaces=$(ip --oneline link show up | grep -v "lo" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { @@ -268,7 +268,7 @@ verifyFreeDiskSpace() { # - Insufficient free disk space elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then echo "::: Insufficient Disk Space!" - echo "::: Your system appears to be low on disk space. pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." + echo "::: Your system appears to be low on disk space. Pi-hole recommends a minimum of $required_free_kilobytes KiloBytes." echo "::: You only have ${existing_free_kilobytes} KiloBytes free." echo "::: If this is a new install you may need to expand your disk." echo "::: Try running 'sudo raspi-config', and choose the 'expand file system option'" @@ -408,7 +408,7 @@ setDHCPCD() { echo "interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null + static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { @@ -438,7 +438,7 @@ setStaticIPv4() { cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { - echo "# Configured via Pi-Hole installer" + echo "# Configured via Pi-hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" @@ -619,14 +619,14 @@ version_check_dnsmasq() { local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig" local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list" - local dnsmasq_original_config="/etc/.pihole/advanced/dnsmasq.conf.original" - local dnsmasq_pihole_01_snippet="/etc/.pihole/advanced/01-pihole.conf" + local dnsmasq_original_config="${PI_HOLE_LOCAL_REPO}/advanced/dnsmasq.conf.original" + local dnsmasq_pihole_01_snippet="${PI_HOLE_LOCAL_REPO}/advanced/01-pihole.conf" local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" if [ -f ${dnsmasq_conf} ]; then echo -n "::: Existing dnsmasq.conf found..." if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then - echo " it is from a previous pi-hole install." + echo " it is from a previous Pi-hole install." echo -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." @@ -634,7 +634,7 @@ version_check_dnsmasq() { cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else - echo " it is not a pi-hole file, leaving alone!" + echo " it is not a Pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." @@ -706,9 +706,9 @@ installScripts() { } installConfigs() { - # Install the configs from /etc/.pihole to their various locations + # Install the configs from PI_HOLE_LOCAL_REPO to their various locations echo ":::" - echo "::: Installing configs..." + echo "::: Installing configs from ${PI_HOLE_LOCAL_REPO}..." version_check_dnsmasq #Only mess with lighttpd configs if user has chosen to install web interface @@ -719,7 +719,7 @@ installConfigs() { elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi - cp /etc/.pihole/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf + cp ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress @@ -797,7 +797,7 @@ notify_package_updates_available() { echo "::: Your system is up to date! Continuing with Pi-hole installation..." else echo "::: There are ${updatesToInstall} updates available for your system!" - echo "::: We recommend you update your OS after installing Pi-Hole! " + echo "::: We recommend you update your OS after installing Pi-hole! " echo ":::" fi else @@ -874,7 +874,7 @@ installPiholeWeb() { echo "::: Existing index.php detected, not overwriting" else echo -n "::: index.php missing, replacing... " - cp /etc/.pihole/advanced/index.php /var/www/html/pihole/ + cp ${PI_HOLE_LOCAL_REPO}/advanced/index.php /var/www/html/pihole/ echo " done!" fi @@ -882,7 +882,7 @@ installPiholeWeb() { echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " - cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ + cp ${PI_HOLE_LOCAL_REPO}/advanced/index.js /var/www/html/pihole/ echo " done!" fi @@ -890,14 +890,14 @@ installPiholeWeb() { echo "::: Existing blockingpage.css detected, not overwriting" else echo -n "::: blockingpage.css missing, replacing... " - cp /etc/.pihole/advanced/blockingpage.css /var/www/html/pihole + cp ${PI_HOLE_LOCAL_REPO}/advanced/blockingpage.css /var/www/html/pihole echo " done!" fi else echo "::: Creating directory for blocking page" install -d /var/www/html/pihole - install -D /etc/.pihole/advanced/{index,blockingpage}.* /var/www/html/pihole/ + install -D ${PI_HOLE_LOCAL_REPO}/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 @@ -910,7 +910,7 @@ installPiholeWeb() { echo ":::" echo -n "::: Installing sudoer file..." mkdir -p /etc/sudoers.d/ - cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + cp ${PI_HOLE_LOCAL_REPO}/advanced/pihole.sudo /etc/sudoers.d/pihole # Add lighttpd user (OS dependent) to sudoers file echo "${LIGHTTPD_USER} ALL=NOPASSWD: /usr/local/bin/pihole" >> /etc/sudoers.d/pihole @@ -928,7 +928,7 @@ installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." - cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole + cp ${PI_HOLE_LOCAL_REPO}/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } @@ -942,7 +942,7 @@ runGravity() { fi # Test if /etc/pihole/adlists.default exists if [[ ! -e /etc/pihole/adlists.default ]]; then - cp /etc/.pihole/adlists.default /etc/pihole/adlists.default + cp ${PI_HOLE_LOCAL_REPO}/adlists.default /etc/pihole/adlists.default fi echo "::: Running gravity.sh" { /opt/pihole/gravity.sh; } @@ -965,7 +965,7 @@ configureFirewall() { whiptail --title "Firewall in use" --yesno "We have detected a running firewall\n\nPi-hole currently requires HTTP and DNS port access.\n\n\n\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ { echo -e ":::\n::: Not installing firewall rulesets."; return 0; } echo -e ":::\n:::\n Configuring FirewallD for httpd and dnsmasq." - firewall-cmd --permanent --add-port=80/tcp --add-port=53/tcp --add-port=53/udp + firewall-cmd --permanent --add-service=http --add-service=dns firewall-cmd --reload return 0 # Check for proper kernel modules to prevent failure @@ -1017,7 +1017,7 @@ finalExports() { # Look for DNS server settings which would have to be reapplied source "${setupVars}" - source "/etc/.pihole/advanced/Scripts/webpage.sh" + source "${PI_HOLE_LOCAL_REPO}/advanced/Scripts/webpage.sh" if [[ "${DNS_FQDN_REQUIRED}" != "" ]] ; then ProcessDNSSettings @@ -1032,7 +1032,7 @@ installLogrotate() { # Install the logrotate script echo ":::" echo -n "::: Installing latest logrotate script..." - cp /etc/.pihole/advanced/logrotate /etc/pihole/logrotate + cp ${PI_HOLE_LOCAL_REPO}/advanced/logrotate /etc/pihole/logrotate # Different operating systems have different user / group # settings for logrotate that makes it impossible to create # a static logrotate file that will work with e.g. @@ -1221,7 +1221,7 @@ FTLinstall() { stop_service pihole-FTL &> /dev/null install -T -m 0755 /tmp/${binary} /usr/bin/pihole-FTL cd "${orig_dir}" - install -T -m 0755 "/etc/.pihole/advanced/pihole-FTL.service" "/etc/init.d/pihole-FTL" + install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/pihole-FTL.service" "/etc/init.d/pihole-FTL" echo "done." return 0 else @@ -1413,7 +1413,8 @@ main() { pw="" if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) - /usr/local/bin/pihole -a -p "${pw}" + . /opt/pihole/webpage.sh + echo "WEBPASSWORD=$(HashPassword ${pw})" >> ${setupVars} fi fi @@ -1460,7 +1461,7 @@ main() { echo "::: ${pw}" echo ":::" echo "::: You can always change it using" - echo "::: pihole -a -p new_password" + echo "::: pihole -a -p" fi fi diff --git a/gravity.sh b/gravity.sh index 08590251..a5231d5e 100755 --- a/gravity.sh +++ b/gravity.sh @@ -26,10 +26,12 @@ EOM exit 0 } +PIHOLE_COMMAND="/usr/local/bin/pihole" adListFile=/etc/pihole/adlists.list -adListDefault=/etc/pihole/adlists.default -whitelistScript="pihole -w" +adListDefault=/etc/pihole/adlists.default #being deprecated +adListRepoDefault=/etc/.pihole/adlists.default +whitelistScript="${PIHOLE_COMMAND} -w" whitelistFile=/etc/pihole/whitelist.txt blacklistFile=/etc/pihole/blacklist.txt readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" @@ -70,36 +72,34 @@ fi ########################### # collapse - begin formation of pihole gravity_collapse() { + + #New Logic: + # Does /etc/pihole/adlists.list exist? If so leave it alone + # If not, cp /etc/.pihole/adlists.default /etc/pihole/adlists.list + # Read from adlists.list + + #The following two blocks will sort out any missing adlists in the /etc/pihole directory, and remove legacy adlists.default + if [ -f ${adListDefault} ] && [ -f ${adListFile} ]; then + rm ${adListDefault} + fi + + if [ ! -f ${adListFile} ]; then + cp ${adListRepoDefault} ${adListFile} + fi + echo "::: Neutrino emissions detected..." echo ":::" - #Decide if we're using a custom ad block list, or defaults. - if [ -f ${adListFile} ]; then - #custom file found, use this instead of default - echo -n "::: Custom adList file detected. Reading..." - sources=() - while IFS= read -r line || [[ -n "$line" ]]; do - #Do not read commented out or blank lines - if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then - echo "" > /dev/null - else - sources+=(${line}) - fi - done < ${adListFile} - echo " done!" - else - #no custom file found, use defaults! - echo -n "::: No custom adlist file detected, reading from default file..." - sources=() - while IFS= read -r line || [[ -n "$line" ]]; do - #Do not read commented out or blank lines - if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then - echo "" > /dev/null - else - sources+=(${line}) - fi - done < ${adListDefault} - echo " done!" - fi + echo -n "::: Pulling source lists into range..." + sources=() + while IFS= read -r line || [[ -n "$line" ]]; do + #Do not read commented out or blank lines + if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then + echo "" > /dev/null + else + sources+=(${line}) + fi + done < ${adListFile} + echo " done!" } # patternCheck - check to see if curl downloaded any new files. @@ -168,6 +168,10 @@ gravity_transport() { # Process result gravity_patternCheck "${patternBuffer}" ${success} "${err}" + # Delete temp file if it hasn't been moved + if [[ -f "${patternBuffer}" ]]; then + rm "${patternBuffer}" + fi } # spinup - main gravity function @@ -183,22 +187,26 @@ gravity_spinup() { saveLocation=${piholeDir}/list.${i}.${domain}.${justDomainsExtension} activeDomains[$i]=${saveLocation} - agent="Mozilla/10.0" + agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36" # 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") + "adblock.mahakala.is") agent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36' cmd_ext="-e http://forum.xda-developers.com/" ;; + "adaway.org") + agent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36' + ;; + "pgl.yoyo.org") cmd_ext="-d mimetype=plaintext -d hostformat=hosts" ;; - # Default is a simple request - *) cmd_ext="" + # Default is a simple request + *) cmd_ext="" esac if [[ "${skipDownload}" == false ]]; then echo -n "::: Getting $domain list..." @@ -385,7 +393,7 @@ gravity_reload() { #Now replace the line in dnsmasq file # sed -i "s/^addn-hosts.*/addn-hosts=$adList/" /etc/dnsmasq.d/01-pihole.conf - pihole restartdns + "${PIHOLE_COMMAND}" restartdns echo " done!" } @@ -403,8 +411,6 @@ if [[ "${forceGrav}" == true ]]; then echo " done!" fi -#Overwrite adlists.default from /etc/.pihole in case any changes have been made. Changes should be saved in /etc/adlists.list -cp /etc/.pihole/adlists.default /etc/pihole/adlists.default gravity_collapse gravity_spinup if [[ "${skipDownload}" == false ]]; then @@ -423,4 +429,4 @@ gravity_hostFormat gravity_blackbody gravity_reload -pihole status +"${PIHOLE_COMMAND}" status diff --git a/pihole b/pihole index dda6d750..feebbc1c 100755 --- a/pihole +++ b/pihole @@ -9,11 +9,11 @@ # Please see LICENSE file for your rights under this license. readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" - readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" + # Must be root to use this tool if [[ ! $EUID -eq 0 ]];then - if [ -x "$(command -v sudo)" ];then + if [[ -x "$(command -v sudo)" ]]; then exec sudo bash "$0" "$@" exit $? else @@ -80,66 +80,49 @@ updateGravityFunc() { exit 0 } -scanList(){ +scanList() { domain="${1}" list="${2}" method="${3}" - if [[ ${method} == "-exact" ]] ; then - grep -i -E "(^|\s)${domain}($|\s)" "${list}" - else - grep -i "${domain}" "${list}" - fi -} -processWildcards() { - IFS="." read -r -a array <<< "${1}" - for (( i=${#array[@]}-1; i>=0; i-- )); do - ar="" - for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do - if [[ $j == $((${#array[@]}-1)) ]]; then - ar="${array[$j]}" - else - ar="${array[$j]}.${ar}" - fi - done - echo "${ar}" - done + if [[ "${method}" == "-exact" ]]; then + grep -i -E -l "(^|\s|\/)${domain}($|\s|\/)" ${list} + else + grep -i "${domain}" ${list} + fi } queryFunc() { - domain="${2}" method="${3}" - lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) - for list in ${lists[@]}; do - if [ -e "${list}" ]; then - result=$(scanList ${domain} ${list} ${method}) - # Remove empty lines before couting number of results - count=$(sed '/^\s*$/d' <<< "$result" | wc -l) - echo "::: ${list} (${count} results)" - if [[ ${count} > 0 ]]; then - echo "${result}" - fi - echo "" - else - echo "::: ${list} does not exist" - echo "" - fi - done - # Scan for possible wildcard matches - if [ -e "${wildcardlist}" ]; then - local wildcards=($(processWildcards "${domain}")) - for domain in ${wildcards[@]}; do - result=$(scanList "\/${domain}\/" ${wildcardlist}) - # Remove empty lines before couting number of results - count=$(sed '/^\s*$/d' <<< "$result" | wc -l) - if [[ ${count} > 0 ]]; then - echo "::: Wildcard blocking ${domain} (${count} results)" - echo "${result}" - echo "" - fi - done + # If domain contains non ASCII characters, convert domain to punycode if python exists + # Cr: https://serverfault.com/a/335079 + if [[ -z "${2}" ]]; then + echo "::: No domain specified" + exit 1 + elif [[ "${2}" = *[![:ascii:]]* ]]; then + [[ "$(which python)" ]] && domain=$(python -c 'import sys;print sys.argv[1].decode("utf-8").encode("idna")' "${2}") + else + domain="${2}" fi + + # Scan Whitelist, Blacklist and Wildcards + lists="/etc/pihole/whitelist.txt /etc/pihole/blacklist.txt $wildcardlist" + result=$(scanList ${domain} "${lists}" ${method}) + if [[ -n "$result" ]]; then + echo "$result" + [[ ! -t 1 ]] && exit 0 + fi + + # Scan Domains lists + result=$(scanList ${domain} "/etc/pihole/*.domains" ${method}) + if [[ -n "$result" ]]; then + sort -t . -k 2 -g <<< "$result" + else + [ -n "$method" ] && exact="exact " + echo "::: No ${exact}results found for ${domain}" + fi + exit 0 } @@ -163,16 +146,16 @@ versionFunc() { restartDNS() { dnsmasqPid=$(pidof dnsmasq) - if [[ ${dnsmasqPid} ]]; then - # service already running - reload config - if [ -x "$(command -v systemctl)" ]; then + if [[ "${dnsmasqPid}" ]]; then + # Service already running - reload config + if [[ -x "$(command -v systemctl)" ]]; then systemctl restart dnsmasq else service dnsmasq restart fi else - # service not running, start it up - if [ -x "$(command -v systemctl)" ]; then + # Service not running, start it up + if [[ -x "$(command -v systemctl)" ]]; then systemctl start dnsmasq else service dnsmasq start @@ -181,16 +164,25 @@ restartDNS() { } piholeEnable() { - if [[ "${1}" == "0" ]] ; then - #Disable Pihole + if [[ "${2}" == "-h" ]] || [[ "${2}" == "--help" ]]; then + echo "Usage: pihole disable [time] +Example: 'pihole disable', or 'pihole disable 5m' +Disable Pi-hole subsystems + +Time: + #s Disable Pi-hole functionality for # second(s) + #m Disable Pi-hole functionality for # minute(s)" + exit 0 + elif [[ "${1}" == "0" ]]; then + # Disable Pi-hole sed -i 's/^addn-hosts=\/etc\/pihole\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf echo "::: Blocking has been disabled!" - if [[ $# > 1 ]] ; then - if [[ ${2} == *"s"* ]] ; then + if [[ $# > 1 ]]; then + if [[ "${2}" == *"s"* ]]; then tt=${2%"s"} echo "::: Blocking will be re-enabled in ${tt} seconds" nohup bash -c "sleep ${tt}; pihole enable" /dev/null & - elif [[ ${2} == *"m"* ]] ; then + elif [[ "${2}" == *"m"* ]]; then tt=${2%"m"} echo "::: Blocking will be re-enabled in ${tt} minutes" tt=$((${tt}*60)) @@ -204,7 +196,7 @@ piholeEnable() { fi fi else - #Enable pihole + # Enable Pi-hole echo "::: Blocking has been enabled!" sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf fi @@ -213,15 +205,23 @@ piholeEnable() { piholeLogging() { shift + if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then + echo "Usage: pihole logging [options] +Example: 'pihole logging on' +Specify whether the Pi-hole log should be used - if [[ "${1}" == "off" ]] ; then - #Disable Logging +Options: + on Enable the Pi-hole log at /var/log/pihole.log + off Disable the Pi-hole log at /var/log/pihole.log" + exit 0 + elif [[ "${1}" == "off" ]]; then + # Disable logging sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf pihole -f echo "::: Logging has been disabled!" - elif [[ "${1}" == "on" ]] ; then - #Enable logging + elif [[ "${1}" == "on" ]]; then + # Enable logging sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf echo "::: Logging has been enabled!" @@ -233,12 +233,12 @@ piholeLogging() { } piholeStatus() { - if [[ $(netstat -plnt | grep -c ':53 ') > 0 ]]; then - if [[ "${1}" != "web" ]] ; then + if [[ "$(netstat -plnt | grep -c ':53 ')" -gt "0" ]]; then + if [[ "${1}" != "web" ]]; then echo "::: DNS service is running" fi else - if [[ "${1}" == "web" ]] ; then + if [[ "${1}" == "web" ]]; then echo "-1"; else echo "::: DNS service is NOT running" @@ -246,28 +246,28 @@ piholeStatus() { return fi - if [[ $(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) ]] ; then - #list is commented out - if [[ "${1}" == "web" ]] ; then + if [[ "$(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then + # List is commented out + if [[ "${1}" == "web" ]]; then echo 0; else echo "::: Pi-hole blocking is Disabled"; fi - elif [[ $(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) ]] ; then - #list set - if [[ "${1}" == "web" ]] ; then + elif [[ "$(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then + # List set + if [[ "${1}" == "web" ]]; then echo 1; else echo "::: Pi-hole blocking is Enabled"; fi else - #addn-host not found - if [[ "${1}" == "web" ]] ; then + # Addn-host not found + if [[ "${1}" == "web" ]]; then echo 99 else echo "::: No hosts file linked to dnsmasq, adding it in enabled state" fi - #add addn-host= to dnsmasq + # Add addn-host= to dnsmasq echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf restartDNS fi @@ -280,46 +280,66 @@ tailFunc() { } piholeCheckoutFunc() { + if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then + echo "Usage: pihole checkout [repo] [branch] +Example: 'pihole checkout master' or 'pihole checkout core dev' +Switch Pi-hole subsystems to a different Github branch + +Repositories: + core [branch] Change the branch of Pi-hole's core subsystem + web [branch] Change the branch of Admin Console subsystem + +Branches: + master Update subsystems to the latest stable release + dev Update subsystems to the latest development release" + exit 0 + fi + source "${PI_HOLE_SCRIPT_DIR}"/piholeCheckout.sh shift checkout "$@" } helpFunc() { - cat << EOM -::: Control all PiHole specific functions! -::: -::: Usage: pihole [options] -::: Add -h after -w (whitelist), -b (blacklist), -c (chronometer), or -a (admin) for more information on usage -::: -::: Options: -::: -w, whitelist Whitelist domain(s) -::: -b, blacklist Blacklist domain(s) (exact match) -::: -wild, wildcard Blacklist whole domain(s) (wildcard) -::: -d, debug Start a debugging session -::: Automated debugging can be enabled with `-a`. -::: 'pihole -d -a' -::: -f, flush Flush the 'pihole.log' file -::: -t, tail Output the last lines of the 'pihole.log' file. Lines are appended as the file grows -::: -up, updatePihole Update Pi-hole components -::: -r, reconfigure Reconfigure or Repair Pi-hole -::: -g, updateGravity Update the list of ad-serving domains -::: -c, chronometer Calculates stats and displays to an LCD -::: -h, help Show this help dialog -::: -v, version Show installed versions of Pi-Hole and Web-Admin -::: -q, query Query the adlists for a specific domain -::: 'pihole -q domain -exact' shows exact matches only -::: -l, logging Enable or Disable logging (pass 'on' or 'off') -::: -a, admin Admin webpage options -::: uninstall Uninstall Pi-Hole from your system :(! -::: status Is Pi-Hole Enabled or Disabled -::: enable Enable Pi-Hole DNS Blocking -::: disable Disable Pi-Hole DNS Blocking -::: Blocking can also be disabled only temporarily, e.g., -::: 'pihole disable 5m' - will disable blocking for 5 minutes -::: restartdns Restart dnsmasq -::: checkout Check out different branches -EOM + echo "Usage: pihole [options] +Example: 'pihole -w -h' +Add '-h' after specific commands for more information on usage + +Whitelist/Blacklist Options: + -w, whitelist Whitelist domain(s) + -b, blacklist Blacklist domain(s) + -wild, wildcard Blacklist domain(s), and all its subdomains + Add '-h' for more info on whitelist/blacklist usage + +Debugging Options: + -d, debug Start a debugging session + Add '-a' to enable automated debugging + -f, flush Flush the Pi-hole log + -r, reconfigure Reconfigure or Repair Pi-hole subsystems + -t, tail View the live output of the Pi-hole log + +Options: + -a, admin Admin Console options + Add '-h' for more info on admin console usage + -c, chronometer Calculates stats and displays to an LCD + Add '-h' for more info on chronometer usage + -g, updateGravity Update the list of ad-serving domains + -h, --help, help Show this help dialog + -l, logging Specify whether the Pi-hole log should be used + Add '-h' for more info on logging usage + -q, query Query the adlists for a specified domain + Add '-exact' AFTER a specified domain for exact match + -up, updatePihole Update Pi-hole subsystems + -v, version Show installed versions of Pi-hole, Admin Console & FTL + Add '-h' for more info on version usage + uninstall Uninstall Pi-hole from your system + status Display the running status of Pi-hole subsystems + enable Enable Pi-hole subsystems + disable Disable Pi-hole subsystems + Add '-h' for more info on disable usage + restartdns Restart Pi-hole subsystems + checkout Switch Pi-hole subsystems to a different Github branch + Add '-h' for more info on checkout usage"; exit 0 } @@ -344,7 +364,7 @@ case "${1}" in "-l" | "logging" ) piholeLogging "$@";; "uninstall" ) uninstallFunc;; "enable" ) piholeEnable 1;; - "disable" ) piholeEnable 0 $2;; + "disable" ) piholeEnable 0 "$2";; "status" ) piholeStatus "$2";; "restartdns" ) restartDNS;; "-a" | "admin" ) webpageFunc "$@";; diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 19c662c6..8e36fc96 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -78,7 +78,7 @@ def test_configureFirewall_firewalld_running_no_errors(Pihole): assert expected_stdout in configureFirewall.stdout firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout assert 'firewall-cmd --state' in firewall_calls - assert 'firewall-cmd --permanent --add-port=80/tcp --add-port=53/tcp --add-port=53/udp' in firewall_calls + assert 'firewall-cmd --permanent --add-service=http --add-service=dns' in firewall_calls assert 'firewall-cmd --reload' in firewall_calls def test_configureFirewall_firewalld_disabled_no_errors(Pihole):