From eb63e75379854d53971733cd35dc1a33dbe84d9b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 17:06:20 +0100 Subject: [PATCH 01/60] No need for pipe here --- pihole | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pihole b/pihole index d8217fed..45cd0af0 100755 --- a/pihole +++ b/pihole @@ -70,7 +70,7 @@ setupLCDFunction() { queryFunc() { domain="${2}" for list in /etc/pihole/list.*; do - count=$(grep ${domain} $list | wc -l) + count=$(grep -c ${domain} $list) echo "::: ${list} (${count} results)" if [[ ${count} > 0 ]]; then grep ${domain} ${list} From 6cd3c9347282dd21602c59d35ef9873316edea56 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 17:09:49 +0100 Subject: [PATCH 02/60] Also query white- and blacklist --- pihole | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pihole b/pihole index 45cd0af0..5abdc01c 100755 --- a/pihole +++ b/pihole @@ -69,7 +69,8 @@ setupLCDFunction() { queryFunc() { domain="${2}" - for list in /etc/pihole/list.*; do + lists=( /etc/pihole/list.* /etc/pihole/whitelist.txt /etc/pihole/blacklist.txt) + for list in ${lists[@]}; do count=$(grep -c ${domain} $list) echo "::: ${list} (${count} results)" if [[ ${count} > 0 ]]; then From 74cb79252cf22109b863bf8d022afa52f87da2c5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Dec 2016 17:12:28 +0100 Subject: [PATCH 03/60] Only looking for blocking entries (do not scan whitelist.txt) --- pihole | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pihole b/pihole index 5abdc01c..8adb15ed 100755 --- a/pihole +++ b/pihole @@ -69,7 +69,7 @@ setupLCDFunction() { queryFunc() { domain="${2}" - lists=( /etc/pihole/list.* /etc/pihole/whitelist.txt /etc/pihole/blacklist.txt) + lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) for list in ${lists[@]}; do count=$(grep -c ${domain} $list) echo "::: ${list} (${count} results)" From fb72ac9904818e72ce486eede6c9b10ae89011ab Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 08:18:49 +0100 Subject: [PATCH 04/60] Show only exact matches for pihole -q --- pihole | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pihole b/pihole index 8adb15ed..e1210dc8 100755 --- a/pihole +++ b/pihole @@ -71,10 +71,10 @@ queryFunc() { domain="${2}" lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) for list in ${lists[@]}; do - count=$(grep -c ${domain} $list) + count=$(grep -c -E "(^|\s)${domain}($|\s)" $list) echo "::: ${list} (${count} results)" if [[ ${count} > 0 ]]; then - grep ${domain} ${list} + grep -E "(^|\s)${domain}($|\s)" ${list} fi echo "" done From 294df8690cd9bf7ef4560d76e9fffb6578814c4a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 09:55:17 +0100 Subject: [PATCH 05/60] Do only one grep on each of the lists and count the number of non-empty lines in the result. Improves speed by factor of 2x --- pihole | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pihole b/pihole index e1210dc8..a59bbc3c 100755 --- a/pihole +++ b/pihole @@ -67,14 +67,20 @@ setupLCDFunction() { exit 0 } +scanList(){ + grep -E "(^|\s)${domain}($|\s)" "$1" +} + queryFunc() { domain="${2}" lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) for list in ${lists[@]}; do - count=$(grep -c -E "(^|\s)${domain}($|\s)" $list) + result=$(scanList $list) + # Remove empty lines before couting number of results + count=$(sed '/^\s*$/d' <<< "$result" | wc -l) echo "::: ${list} (${count} results)" if [[ ${count} > 0 ]]; then - grep -E "(^|\s)${domain}($|\s)" ${list} + echo $result fi echo "" done From c0886cb5c699d30149d71a0e1d33355234cc0f3d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 6 Dec 2016 13:18:01 +0100 Subject: [PATCH 06/60] pihole -q is partial matching (as before), pihole -q -exact is exact matching (new behavior) --- pihole | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pihole b/pihole index a59bbc3c..40d45e6a 100755 --- a/pihole +++ b/pihole @@ -68,19 +68,27 @@ setupLCDFunction() { } scanList(){ - grep -E "(^|\s)${domain}($|\s)" "$1" + domain="${1}" + list="${2}" + method="${3}" + if [[ ${method} == "-exact" ]] ; then + grep -E "(^|\s)${domain}($|\s)" "${list}" + else + grep "${domain}" "${list}" + fi } queryFunc() { domain="${2}" + method="${3}" lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) for list in ${lists[@]}; do - result=$(scanList $list) + 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 + echo "${result}" fi echo "" done @@ -224,6 +232,7 @@ helpFunc() { ::: -h, help Show this help dialog ::: -v, version Show current versions ::: -q, query Query the adlists for a specific domain +::: Use pihole -q domain -exact if you want to see 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 :(! From 0a423ffd40bed3719b532c638d2439760b3abc6a Mon Sep 17 00:00:00 2001 From: trinapicot Date: Wed, 7 Dec 2016 20:31:34 -0700 Subject: [PATCH 07/60] Fix check for lighttpd install before configuration directory removal --- automated install/uninstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/uninstall.sh b/automated install/uninstall.sh index 49ab197d..2bc4dd25 100755 --- a/automated install/uninstall.sh +++ b/automated install/uninstall.sh @@ -136,7 +136,7 @@ removeNoPurge() { fi echo "::: Removing config files and scripts..." - package_check ${i} > /dev/null + package_check lighttpd > /dev/null if [ $? -eq 1 ]; then ${SUDO} rm -rf /etc/lighttpd/ &> /dev/null else From 5120d9ec33a570fe4ad07e5619c27cc18c21b4ea Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 8 Dec 2016 21:15:03 +0100 Subject: [PATCH 08/60] Add tail option --- pihole | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pihole b/pihole index 8adb15ed..9e727809 100755 --- a/pihole +++ b/pihole @@ -198,6 +198,10 @@ piholeStatus() { fi } +tailFunc() { + tail -F /var/log/pihole.log + exit 0 +} helpFunc() { cat << EOM @@ -211,6 +215,7 @@ helpFunc() { ::: -b, blacklist Blacklist domains ::: -d, debug Start a debugging session if having trouble ::: -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 ::: -g, updateGravity Update the list of ad-serving domains ::: -s, setupLCD Automatically configures the Pi to use the 2.8 LCD screen to display stats on it @@ -256,5 +261,6 @@ case "${1}" in "status" ) piholeStatus "$2";; "restartdns" ) restartDNS;; "-a" | "admin" ) webpageFunc "$@";; + "-t" | "tail" ) tailFunc;; * ) helpFunc;; esac From b9e0e88fe91aa0a2efb06165640a084117a9efd4 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Thu, 8 Dec 2016 15:47:30 -0500 Subject: [PATCH 09/60] Update pihole --- pihole | 1 + 1 file changed, 1 insertion(+) diff --git a/pihole b/pihole index 8adb15ed..6c99895b 100755 --- a/pihole +++ b/pihole @@ -212,6 +212,7 @@ helpFunc() { ::: -d, debug Start a debugging session if having trouble ::: -f, flush Flush the pihole.log file ::: -up, updatePihole Update Pi-hole +::: -r, reconfigure Reconfigure or Repair Pi-hole ::: -g, updateGravity Update the list of ad-serving domains ::: -s, setupLCD Automatically configures the Pi to use the 2.8 LCD screen to display stats on it ::: -c, chronometer Calculates stats and displays to an LCD From b591df55b0c44540ced89561ae53a9846b636236 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Thu, 8 Dec 2016 16:35:50 -0500 Subject: [PATCH 10/60] Tell user how to exit tail command --- pihole | 1 + 1 file changed, 1 insertion(+) diff --git a/pihole b/pihole index 9e727809..2ab51c05 100755 --- a/pihole +++ b/pihole @@ -199,6 +199,7 @@ piholeStatus() { } tailFunc() { + echo "Press Ctrl-C to exit" tail -F /var/log/pihole.log exit 0 } From bf867bd9fdc4e3e6374d80c92d4db936fd7b456f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 11 Dec 2016 16:54:27 +0100 Subject: [PATCH 11/60] Set DNS servers from web UI --- advanced/Scripts/webpage.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 0a79d853..839f6e6d 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -57,11 +57,22 @@ SetWebPassword(){ } +SetDNSServers(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;' /etc/pihole/setupVars.conf + # Save setting to file + echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf + echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; + "setdns" ) SetDNSServers;; "-h" | "--help" ) helpFunc;; esac done From 9ac378ae09a5c484b891963bb78a9b657a518116 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 11 Dec 2016 19:30:04 +0100 Subject: [PATCH 12/60] Set domains and clients which are excluded by the API --- advanced/Scripts/webpage.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 839f6e6d..35ba6875 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -67,12 +67,30 @@ SetDNSServers(){ } +SetExcludeDomains(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/API_EXCLUDE_DOMAINS/d;' /etc/pihole/setupVars.conf + # Save setting to file + echo "API_EXCLUDE_DOMAINS=${args[2]}" >> /etc/pihole/setupVars.conf +} + +SetExcludeClients(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/API_EXCLUDE_CLIENTS/d;' /etc/pihole/setupVars.conf + # Save setting to file + echo "API_EXCLUDE_CLIENTS=${args[2]}" >> /etc/pihole/setupVars.conf +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; "setdns" ) SetDNSServers;; + "setexcludedomains" ) SetExcludeDomains;; + "setexcludeclients" ) SetExcludeClients;; "-h" | "--help" ) helpFunc;; esac done From 2bafa2f2ac685798d120ab1393940354978d4e75 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 11 Dec 2016 22:33:27 +0100 Subject: [PATCH 13/60] Add reboot option --- advanced/Scripts/webpage.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 35ba6875..bdd3a17f 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -83,6 +83,12 @@ SetExcludeClients(){ echo "API_EXCLUDE_CLIENTS=${args[2]}" >> /etc/pihole/setupVars.conf } +Reboot(){ + + reboot + +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; @@ -91,6 +97,7 @@ for var in "$@"; do "setdns" ) SetDNSServers;; "setexcludedomains" ) SetExcludeDomains;; "setexcludeclients" ) SetExcludeClients;; + "reboot" ) Reboot;; "-h" | "--help" ) helpFunc;; esac done From aaab3306a87c7e8384bbe0c4e46cb0c6e56acc03 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 10:38:21 +0100 Subject: [PATCH 14/60] Add restart DNS capability --- advanced/Scripts/webpage.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index bdd3a17f..a7a76161 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -89,6 +89,16 @@ Reboot(){ } +RestartDNS(){ + + if [ -x "$(command -v systemctl)" ]; then + systemctl restart dnsmasq &> /dev/null + else + service dnsmasq restart &> /dev/null + fi + +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; @@ -98,6 +108,7 @@ for var in "$@"; do "setexcludedomains" ) SetExcludeDomains;; "setexcludeclients" ) SetExcludeClients;; "reboot" ) Reboot;; + "restartdns" ) RestartDNS;; "-h" | "--help" ) helpFunc;; esac done From d4f83cb1d4792283fd0d7f844518fc02a33c5c24 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 13:15:07 +0100 Subject: [PATCH 15/60] Be able to set API_QUERY_LOG_SHOW --- advanced/Scripts/webpage.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index a7a76161..4df11f38 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -99,6 +99,14 @@ RestartDNS(){ } +SetQueryLogOptions(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/API_QUERY_LOG_SHOW/d;' /etc/pihole/setupVars.conf + # Save setting to file + echo "API_QUERY_LOG_SHOW=${args[2]}" >> /etc/pihole/setupVars.conf +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; @@ -109,6 +117,7 @@ for var in "$@"; do "setexcludeclients" ) SetExcludeClients;; "reboot" ) Reboot;; "restartdns" ) RestartDNS;; + "setquerylog" ) SetQueryLogOptions;; "-h" | "--help" ) helpFunc;; esac done From 2f4b7ce3dd3749acc5513ab63214cdd38f265609 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 14:16:13 +0100 Subject: [PATCH 16/60] Update dnsmasq file when changing upstream DNS servers and restart the service to get it active --- advanced/Scripts/webpage.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 4df11f38..6ae2c327 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -65,6 +65,14 @@ SetDNSServers(){ echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + # Replace within actual dnsmasq config file + sed -i '/server=/d;' /etc/dnsmasq.d/01-pihole.conf + echo "server=${args[2]}" >> /etc/dnsmasq.d/01-pihole.conf + echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + + # Restart dnsmasq to load new configuration + RestartDNS + } SetExcludeDomains(){ From 8dc0dc4d694a3873134c8cbf3d75d3b0fe18f815 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 15:34:05 +0100 Subject: [PATCH 17/60] Add DHCP-Server backend --- advanced/Scripts/webpage.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6ae2c327..a930856f 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -115,6 +115,40 @@ SetQueryLogOptions(){ echo "API_QUERY_LOG_SHOW=${args[2]}" >> /etc/pihole/setupVars.conf } +EnableDHCP(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/DHCP_/d;' /etc/pihole/setupVars.conf + echo "DHCP_ACTIVE=true" >> /etc/pihole/setupVars.conf + echo "DHCP_START=${args[2]}" >> /etc/pihole/setupVars.conf + echo "DHCP_END=${args[3]}" >> /etc/pihole/setupVars.conf + echo "DHCP_ROUTER=${args[4]}" >> /etc/pihole/setupVars.conf + + # Remove setting from file + sed -i '/dhcp-/d;' /etc/dnsmasq.d/01-pihole.conf + # Save setting to file + echo "dhcp-range=${args[2]},${args[3]},infinite" >> /etc/dnsmasq.d/01-pihole.conf + echo "dhcp-option=option:router,${args[4]}" >> /etc/dnsmasq.d/01-pihole.conf + # Changes the behaviour from strict RFC compliance so that DHCP requests on unknown leases from unknown hosts are not ignored. This allows new hosts to get a lease without a tedious timeout under all circumstances. It also allows dnsmasq to rebuild its lease database without each client needing to reacquire a lease, if the database is lost. + echo "dhcp-authoritative" >> /etc/dnsmasq.d/01-pihole.conf + # Use the specified file to store DHCP lease information + echo "dhcp-leasefile=/etc/pihole/dhcp.leases" >> /etc/dnsmasq.d/01-pihole.conf + + RestartDNS +} + +DisableDHCP(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/DHCP_ACTIVE/d;' /etc/pihole/setupVars.conf + echo "DHCP_ACTIVE=false" >> /etc/pihole/setupVars.conf + + # Remove setting from file + sed -i '/dhcp-/d;' /etc/dnsmasq.d/01-pihole.conf + + RestartDNS +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; @@ -126,6 +160,8 @@ for var in "$@"; do "reboot" ) Reboot;; "restartdns" ) RestartDNS;; "setquerylog" ) SetQueryLogOptions;; + "enabledhcp" ) EnableDHCP;; + "disabledhcp" ) DisableDHCP;; "-h" | "--help" ) helpFunc;; esac done From a00034a6a7a12f0fe587b59b34466e063d79912e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 17:03:53 +0100 Subject: [PATCH 18/60] Add password to webUI after update if there is none --- automated install/basic-install.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 33444873..08963637 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -994,6 +994,11 @@ configureSelinux() { } displayFinalMessage() { + if [ ! -z $pw ]; then + pwstring="Note: As security measure a password has been installed for your web interface\n The currently set password is\n ${pw}\n\n You can always change it using\n pihole -a -p new_password" + else + pswsting="" + fi # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: @@ -1003,7 +1008,8 @@ IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. -View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin +${string}" ${r} ${c} } update_dialogs() { @@ -1122,6 +1128,12 @@ main() { # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} + # Add password to web UI if there is none + if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then + pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) + pihole -a -p ${pw} + fi + if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage fi @@ -1144,6 +1156,15 @@ main() { echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" + if [ ! -z $pw ]; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" + fi fi echo ":::" From bc91716082fbd7dc139f434e7326a31216e811fe Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 12 Dec 2016 17:14:52 +0100 Subject: [PATCH 19/60] Suppress logging of DHCP actions to /var/log/pihole.log --- advanced/Scripts/webpage.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index a930856f..21de7067 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -133,6 +133,9 @@ EnableDHCP(){ echo "dhcp-authoritative" >> /etc/dnsmasq.d/01-pihole.conf # Use the specified file to store DHCP lease information echo "dhcp-leasefile=/etc/pihole/dhcp.leases" >> /etc/dnsmasq.d/01-pihole.conf + # Suppress logging of the routine operation of these protocols. Errors and problems will still be logged, though. + echo "quiet-dhcp" >> /etc/dnsmasq.d/01-pihole.conf + echo "quiet-dhcp6" >> /etc/dnsmasq.d/01-pihole.conf RestartDNS } From 8a2ee95e4ac9dc07a7bec69e0048721a25e83b58 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Mon, 12 Dec 2016 15:30:47 -0500 Subject: [PATCH 20/60] Update ADZHOSTS list --- adlists.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adlists.default b/adlists.default index 57860b3f..92422db7 100644 --- a/adlists.default +++ b/adlists.default @@ -28,7 +28,7 @@ https://hosts-file.net/ad_servers.txt #http://adblock.mahakala.is/ # ADZHOSTS list. Has been known to block legitimate domains -#http://optimate.dl.sourceforge.net/project/adzhosts/HOSTS.txt +#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 From b8d2bfc890c36c9418e5e8d066e45b6fc65810f1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 13 Dec 2016 12:42:40 +0100 Subject: [PATCH 21/60] Wait 5 seconds before rebooting so the web UI can finish loading before being unreachable due to the reboot --- advanced/Scripts/webpage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 21de7067..4730118d 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -93,7 +93,7 @@ SetExcludeClients(){ Reboot(){ - reboot + nohup bash -c "sleep 5; reboot" &> /dev/null Date: Tue, 13 Dec 2016 14:59:52 +0100 Subject: [PATCH 22/60] Add "Interface appearence" option --- advanced/Scripts/webpage.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 4730118d..5863d2dc 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -152,6 +152,14 @@ DisableDHCP(){ RestartDNS } +SetWebUILayout(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/WEBUIBOXEDLAYOUT/d;' /etc/pihole/setupVars.conf + echo "WEBUIBOXEDLAYOUT=${args[2]}" >> /etc/pihole/setupVars.conf + +} + for var in "$@"; do case "${var}" in "-p" | "password" ) SetWebPassword;; @@ -165,6 +173,7 @@ for var in "$@"; do "setquerylog" ) SetQueryLogOptions;; "enabledhcp" ) EnableDHCP;; "disabledhcp" ) DisableDHCP;; + "layout" ) SetWebUILayout;; "-h" | "--help" ) helpFunc;; esac done From 004ba283789d38cbad4054f7d0824f2850e03e20 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 13 Dec 2016 15:52:28 +0100 Subject: [PATCH 23/60] Prevent web UI password change by web user --- advanced/Scripts/webpage.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 0a79d853..db7d2026 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -41,6 +41,18 @@ SetTemperatureUnit(){ SetWebPassword(){ + if[ "$SUDO_USER" == "www-data" ]; then + echo "Security measure: user www-data is not allowed to change webUI password!" + echo "Exiting" + exit 1 + fi + + if[ "$SUDO_USER" == "lighttpd" ]; then + echo "Security measure: user lighttpd is not allowed to change webUI password!" + echo "Exiting" + exit 1 + fi + # Remove password from file (create backup setupVars.conf.bak) sed -i.bak '/WEBPASSWORD/d' /etc/pihole/setupVars.conf # Set password only if there is one to be set From 207422f83a4deccb0e932f78fbd0db02707810b6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 13 Dec 2016 15:54:41 +0100 Subject: [PATCH 24/60] Added a missing space --- advanced/Scripts/webpage.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index db7d2026..0beb688f 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -41,13 +41,13 @@ SetTemperatureUnit(){ SetWebPassword(){ - if[ "$SUDO_USER" == "www-data" ]; then + if [ "${SUDO_USER}" == "www-data" ]; then echo "Security measure: user www-data is not allowed to change webUI password!" echo "Exiting" exit 1 fi - if[ "$SUDO_USER" == "lighttpd" ]; then + if [ "${SUDO_USER}" == "lighttpd" ]; then echo "Security measure: user lighttpd is not allowed to change webUI password!" echo "Exiting" exit 1 From 1556adb678a11e1f2c66474330d515b6eb62aa99 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 14 Dec 2016 12:12:36 +0100 Subject: [PATCH 25/60] Also remove quiet-dhcp setting when disabling/enabling the Pi-Hole DHCP server --- advanced/Scripts/webpage.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 179580b4..1ee41b15 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -137,7 +137,7 @@ EnableDHCP(){ echo "DHCP_ROUTER=${args[4]}" >> /etc/pihole/setupVars.conf # Remove setting from file - sed -i '/dhcp-/d;' /etc/dnsmasq.d/01-pihole.conf + sed -i '/dhcp-/d;/quiet-dhcp/d;' /etc/dnsmasq.d/01-pihole.conf # Save setting to file echo "dhcp-range=${args[2]},${args[3]},infinite" >> /etc/dnsmasq.d/01-pihole.conf echo "dhcp-option=option:router,${args[4]}" >> /etc/dnsmasq.d/01-pihole.conf @@ -159,7 +159,7 @@ DisableDHCP(){ echo "DHCP_ACTIVE=false" >> /etc/pihole/setupVars.conf # Remove setting from file - sed -i '/dhcp-/d;' /etc/dnsmasq.d/01-pihole.conf + sed -i '/dhcp-/d;/quiet-dhcp/d;' /etc/dnsmasq.d/01-pihole.conf RestartDNS } From ba015c191845fe279ef8b5943c3b213d5dbf0f88 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 14 Dec 2016 14:24:09 +0100 Subject: [PATCH 26/60] Set domain name via Settings page --- advanced/Scripts/webpage.sh | 50 ++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 1ee41b15..4f7fa43b 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -172,23 +172,39 @@ SetWebUILayout(){ } -for var in "$@"; do - case "${var}" in - "-p" | "password" ) SetWebPassword;; - "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; - "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; - "setdns" ) SetDNSServers;; - "setexcludedomains" ) SetExcludeDomains;; - "setexcludeclients" ) SetExcludeClients;; - "reboot" ) Reboot;; - "restartdns" ) RestartDNS;; - "setquerylog" ) SetQueryLogOptions;; - "enabledhcp" ) EnableDHCP;; - "disabledhcp" ) DisableDHCP;; - "layout" ) SetWebUILayout;; - "-h" | "--help" ) helpFunc;; - esac -done +SetDNSDomainName(){ + + # Remove setting from file (create backup setupVars.conf.bak) + sed -i.bak '/PIHOLE_DOMAIN/d;' /etc/pihole/setupVars.conf + # Save setting to file + echo "PIHOLE_DOMAIN=${args[2]}" >> /etc/pihole/setupVars.conf + + # Replace within actual dnsmasq config file + sed -i '/domain=/d;' /etc/dnsmasq.d/01-pihole.conf + echo "domain=${args[2]}" >> /etc/dnsmasq.d/01-pihole.conf + + # Restart dnsmasq to load new configuration + RestartDNS + +} + +case "${args[1]}" in + "-p" | "password" ) SetWebPassword;; + "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; + "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; + "setdns" ) SetDNSServers;; + "setexcludedomains" ) SetExcludeDomains;; + "setexcludeclients" ) SetExcludeClients;; + "reboot" ) Reboot;; + "restartdns" ) RestartDNS;; + "setquerylog" ) SetQueryLogOptions;; + "enabledhcp" ) EnableDHCP;; + "disabledhcp" ) DisableDHCP;; + "layout" ) SetWebUILayout;; + "-h" | "--help" ) helpFunc;; + "domainname" ) SetDNSDomainName;; + * ) helpFunc;; +esac shift From 85e3f37503f0da4e85099614ea01ff5f7516051f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 14 Dec 2016 16:09:57 +0100 Subject: [PATCH 27/60] dnsmasq "domain-needed" property --- advanced/Scripts/webpage.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 4f7fa43b..3309fc99 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -72,7 +72,7 @@ SetWebPassword(){ SetDNSServers(){ # Remove setting from file (create backup setupVars.conf.bak) - sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;' /etc/pihole/setupVars.conf + sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/DNS_FQDN_REQUIRED/d;' /etc/pihole/setupVars.conf # Save setting to file echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf @@ -82,6 +82,18 @@ SetDNSServers(){ echo "server=${args[2]}" >> /etc/dnsmasq.d/01-pihole.conf echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + # Remove domain-needed entry + sed -i '/domain-needed/d;' /etc/dnsmasq.d/01-pihole.conf + + # Readd it if required + if [[ "${args[4]}" == "domain-needed" ]]; then + echo "domain-needed" >> /etc/dnsmasq.d/01-pihole.conf + echo "DNS_FQDN_REQUIRED=true" >> /etc/pihole/setupVars.conf + else + echo "DNS_FQDN_REQUIRED=false" >> /etc/pihole/setupVars.conf + fi + # Leave it deleted if not wanted + # Restart dnsmasq to load new configuration RestartDNS From 2977168da139d0f547d898fa7baba181a653fef8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 14 Dec 2016 16:16:25 +0100 Subject: [PATCH 28/60] dnsmasq "bogus-priv" property --- advanced/Scripts/webpage.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 3309fc99..d07e5c94 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -90,9 +90,21 @@ SetDNSServers(){ echo "domain-needed" >> /etc/dnsmasq.d/01-pihole.conf echo "DNS_FQDN_REQUIRED=true" >> /etc/pihole/setupVars.conf else + # Leave it deleted if not wanted echo "DNS_FQDN_REQUIRED=false" >> /etc/pihole/setupVars.conf fi - # Leave it deleted if not wanted + + # Remove bogus-priv entry + sed -i '/bogus-priv/d;' /etc/dnsmasq.d/01-pihole.conf + + # Readd it if required + if [[ "${args[5]}" == "bogus-priv" ]]; then + echo "bogus-priv" >> /etc/dnsmasq.d/01-pihole.conf + echo "DNS_BOGUS_PRIV=true" >> /etc/pihole/setupVars.conf + else + # Leave it deleted if not wanted + echo "DNS_BOGUS_PRIV=false" >> /etc/pihole/setupVars.conf + fi # Restart dnsmasq to load new configuration RestartDNS From 9a2ffabc33d09277025da20c37b3d7dc3e89a84b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 14 Dec 2016 17:33:20 +0100 Subject: [PATCH 29/60] Fix missing path information --- advanced/pihole.cron | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/advanced/pihole.cron b/advanced/pihole.cron index 77b7c1ca..8311acfb 100644 --- a/advanced/pihole.cron +++ b/advanced/pihole.cron @@ -8,7 +8,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# +# # This file is under source-control of the Pi-hole installation and update # scripts, any changes made to this file will be overwritten when the softare # is updated or re-installed. Please make any changes to the appropriate crontab @@ -16,11 +16,11 @@ # Pi-hole: Update the ad sources once a week on Sunday at 01:59 # Download any updates from the adlists -59 1 * * 7 root /usr/local/bin/pihole updateGravity +59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity # Pi-hole: Update Pi-hole! Uncomment to enable auto update -#30 2 * * 7 root /usr/local/bin/pihole updatePihole +#30 2 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updatePihole # Pi-hole: Flush the log daily at 00:00 so it doesn't get out of control # Stats will be viewable in the Web interface thanks to the cron job above -00 00 * * * root /usr/local/bin/pihole flush +00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush From 92f48248846af59b372404896163fba217d4372f Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Wed, 14 Dec 2016 13:52:14 -0500 Subject: [PATCH 30/60] Automatically enable IPv6 --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 33444873..afa44701 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -297,7 +297,7 @@ use4andor6() { # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on - IPv6 "Block ads over IPv6" off) + IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} From db89fa9881f0a2047311e6e058c1e8d32904dfbd Mon Sep 17 00:00:00 2001 From: Promofaux Date: Wed, 14 Dec 2016 19:26:55 +0000 Subject: [PATCH 31/60] initial support for additional hosts file --- gravity.sh | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/gravity.sh b/gravity.sh index 0184f193..1c1c397f 100755 --- a/gravity.sh +++ b/gravity.sh @@ -45,11 +45,13 @@ fi #Remove the /* from the end of the IPv4addr. IPV4_ADDRESS=${IPV4_ADDRESS%/*} +IPV6_ADDRESS=${IPV6_ADDRESS} # Variables for various stages of downloading and formatting the list basename=pihole piholeDir=/etc/${basename} adList=${piholeDir}/gravity.list +localList=${piholeDir}/local.list justDomainsExtension=domains matterAndLight=${basename}.0.matterandlight.txt supernova=${basename}.1.supernova.txt @@ -241,26 +243,37 @@ gravity_unique() { gravity_hostFormat() { # Format domain list as "192.168.x.x domain.com" echo -n "::: Formatting domains into a HOSTS file..." - # Check vars from setupVars.conf to see if we're using IPv4, IPv6, Or both. - if [[ -n "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then + rm ${localList} + if [[ -f /etc/hostname ]]; then + hostname=$(> ${piholeDir}/${accretionDisc} + echo -e "${IPV4_ADDRESS} ${hostname}\n${IPV6_ADDRESS} ${hostname}\n${IPV4_ADDRESS} pi.hole\n${IPV6_ADDRESS} pi.hole" > ${localList} + # Both IPv4 and IPv6 + cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPV4_ADDRESS" -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc} - elif [[ -n "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then + elif [[ -n "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then - # Only IPv4 - cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPV4_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0}' >> ${piholeDir}/${accretionDisc} + echo -e "${IPV4_ADDRESS} ${hostname}\n${IPV4_ADDRESS} pi.hole" > ${localList} + # Only IPv4 + cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPV4_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0}' >> ${piholeDir}/${accretionDisc} - elif [[ -z "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then + elif [[ -z "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then - # Only IPv6 - cat ${piholeDir}/${eventHorizon} | awk -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc} + echo -e "${IPV6_ADDRESS} ${hostname}\n${IPV6_ADDRESS} pi.hole" > ${localList} + # Only IPv6 + cat ${piholeDir}/${eventHorizon} | awk -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc} - elif [[ -z "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then - echo "::: No IP Values found! Please run 'pihole -r' and choose reconfigure to restore values" - exit 1 - fi + elif [[ -z "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then + echo "::: No IP Values found! Please run 'pihole -r' and choose reconfigure to restore values" + exit 1 + fi # Copy the file over as /etc/pihole/gravity.list so dnsmasq can use it cp ${piholeDir}/${accretionDisc} ${adList} From 684ac98c8ed6c365ec94f0a0894b0a1ef58e3602 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Wed, 14 Dec 2016 19:28:01 +0000 Subject: [PATCH 32/60] Remove hostname and pi.hole entries, move to /etc/pihole/local.list --- advanced/01-pihole.conf | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index f4707ef4..0ddf1caa 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -20,13 +20,8 @@ # OR IN /etc/dnsmasq.conf # ############################################################################### -address=/pi.hole/@IPv4@ -address=/pi.hole/@IPv6@ - -address=/@HOSTNAME@/@IPv4@ -address=/@HOSTNAME@/@IPv6@ - addn-hosts=/etc/pihole/gravity.list +addn-hosts=/etc/pihole/local.list domain-needed From c10ec5548f0ce1c45052e80235e825120b9dde40 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Wed, 14 Dec 2016 19:29:44 +0000 Subject: [PATCH 33/60] remove code that replaces tokens in 01-pihole.conf --- automated install/basic-install.sh | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index afa44701..9464d03b 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -612,36 +612,6 @@ version_check_dnsmasq() { sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi - #sed -i "s/@HOSTNAME@/$hostname/" ${dnsmasq_pihole_01_location} - - if [[ -f /etc/hostname ]]; then - hostname=$( Date: Wed, 14 Dec 2016 19:31:57 +0000 Subject: [PATCH 34/60] check for list before removing it --- gravity.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index 1c1c397f..9ddd7beb 100755 --- a/gravity.sh +++ b/gravity.sh @@ -243,7 +243,11 @@ gravity_unique() { gravity_hostFormat() { # Format domain list as "192.168.x.x domain.com" echo -n "::: Formatting domains into a HOSTS file..." - rm ${localList} + + if [[ -f ${localList} ]]; then + rm ${localList} + fi + if [[ -f /etc/hostname ]]; then hostname=$( Date: Wed, 14 Dec 2016 19:33:15 +0000 Subject: [PATCH 35/60] no need to remove list, because of echo > --- gravity.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gravity.sh b/gravity.sh index 9ddd7beb..58d0eef9 100755 --- a/gravity.sh +++ b/gravity.sh @@ -244,10 +244,6 @@ gravity_hostFormat() { # Format domain list as "192.168.x.x domain.com" echo -n "::: Formatting domains into a HOSTS file..." - if [[ -f ${localList} ]]; then - rm ${localList} - fi - if [[ -f /etc/hostname ]]; then hostname=$( Date: Wed, 14 Dec 2016 19:42:20 +0000 Subject: [PATCH 36/60] specifically comment out gravity.list addn-host --- pihole | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pihole b/pihole index 6d5f8c9d..5509df47 100755 --- a/pihole +++ b/pihole @@ -135,7 +135,7 @@ restartDNS() { piholeEnable() { if [[ "${1}" == "0" ]] ; then #Disable Pihole - sed -i 's/^addn-hosts/#addn-hosts/' /etc/dnsmasq.d/01-pihole.conf + 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 From e7713a9028fce828cfb02406bc972fa42c4bcc17 Mon Sep 17 00:00:00 2001 From: Promofaux Date: Wed, 14 Dec 2016 19:53:40 +0000 Subject: [PATCH 37/60] add missing pihole autocomplete commands --- advanced/bash-completion/pihole | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/bash-completion/pihole b/advanced/bash-completion/pihole index dd3f050d..05baa820 100644 --- a/advanced/bash-completion/pihole +++ b/advanced/bash-completion/pihole @@ -3,7 +3,7 @@ _pihole() { COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="blacklist chronometer debug flush help query reconfigure setupLCD uninstall updateGravity updatePihole version whitelist" + opts="admin blacklist chronometer debug disable enable flush help logging query reconfigure restartdns setupLCD status tail uninstall updateGravity updatePihole version whitelist" COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 From f81dadc5d08c647ca4ddbd72caf1e5c310ea4ac7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 15 Dec 2016 14:10:55 +0100 Subject: [PATCH 38/60] Fix final message --- automated install/basic-install.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 08963637..b8f0c2dc 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -994,11 +994,7 @@ configureSelinux() { } displayFinalMessage() { - if [ ! -z $pw ]; then - pwstring="Note: As security measure a password has been installed for your web interface\n The currently set password is\n ${pw}\n\n You can always change it using\n pihole -a -p new_password" - else - pswsting="" - fi + if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: @@ -1009,7 +1005,23 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin -${string}" ${r} ${c} + +Note: As security measure a password has been installed for your web interface +The currently set password is + ${1} +You can always change it using + pihole -a -p new_password" ${r} ${c} + else + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + +IPv4: ${IPV4_ADDRESS%/*} +IPv6: ${IPV6_ADDRESS} + +If you set a new IP address, you should restart the Pi. + +The install log is in /etc/pihole. +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} + fi } update_dialogs() { @@ -1129,13 +1141,14 @@ main() { mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none + 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then - displayFinalMessage + displayFinalMessage ${pw} fi echo "::: Restarting services..." From e2898217d21b09d2b5a00f7125cf54dce211d140 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 15 Dec 2016 15:20:41 +0100 Subject: [PATCH 39/60] Made message shorter --- automated install/basic-install.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index b8f0c2dc..23d57cff 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1005,12 +1005,7 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin - -Note: As security measure a password has been installed for your web interface -The currently set password is - ${1} -You can always change it using - pihole -a -p new_password" ${r} ${c} +The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: @@ -1169,7 +1164,7 @@ main() { echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" - if [ ! -z $pw ]; then + if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" From 1eeaa012347e5e7ed76eda6e4e4f92402e58b490 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 15 Dec 2016 15:22:18 +0100 Subject: [PATCH 40/60] Always put the information that we added a password if we did (not only on update but also on fresh install) --- automated install/basic-install.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 23d57cff..d96c7eb4 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1164,15 +1164,16 @@ main() { echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" - if (( ${#pw} > 0 )) ; then - echo ":::" - echo "::: Note: As security measure a password has been installed for your web interface" - echo "::: The currently set password is" - echo "::: ${pw}" - echo ":::" - echo "::: You can always change it using" - echo "::: pihole -a -p new_password" - fi + fi + + if (( ${#pw} > 0 )) ; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" fi echo ":::" From ec8c848106841c0e7900d7772025c99c30364d3f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 16 Dec 2016 12:53:00 +0100 Subject: [PATCH 41/60] Don't kill already existing setupVars.conf on update (and fresh install) --- automated install/basic-install.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 9464d03b..4744d7d5 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -872,10 +872,8 @@ configureFirewall() { } finalExports() { - #If it already exists, lets overwrite it with the new values. - if [[ -f ${setupVars} ]]; then - rm ${setupVars} - fi + # Update variables in setupVars.conf file + sed -i.update.bak '/PIHOLE_DOMAIN/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" From 7872f68a45145b713971bab9af6cc56860a8fbf6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 16 Dec 2016 13:13:35 +0100 Subject: [PATCH 42/60] Only try to remove parts inthe file if the file already exists --- automated install/basic-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 4744d7d5..0e82cb31 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -873,7 +873,9 @@ configureFirewall() { finalExports() { # Update variables in setupVars.conf file - sed -i.update.bak '/PIHOLE_DOMAIN/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + if [ -e "${setupVars}" ]; then + sed -i.update.bak '/PIHOLE_DOMAIN/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" From d4c378ed5d1b0c21af598c12b2013547469b97f7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 16 Dec 2016 13:20:25 +0100 Subject: [PATCH 43/60] Remove PIHOLE_INTERFACE if already set (not PIHOLE_DOMAIN) --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0e82cb31..4671f49e 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -874,7 +874,7 @@ configureFirewall() { finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_DOMAIN/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" From 8cb01cdd294584372a94cebb20ebfd6fea0adf0f Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Fri, 16 Dec 2016 15:08:30 -0800 Subject: [PATCH 44/60] One step to rename variables. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 8abf9af8..a04cac3f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -915,13 +915,12 @@ accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - sed -i 's/IPv4addr/IPv4_address/g' ${setupVars} - sed -i 's/piholeIPv6/IPv6_address/g' ${setupVars} - # Account for renaming of global variables. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} From 715838cf892f9718b4a9afc94210a872b3c268ef Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 19 Dec 2016 13:42:42 +0100 Subject: [PATCH 45/60] Allow disabling second DNS server --- advanced/Scripts/webpage.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index d07e5c94..6251b158 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -75,12 +75,18 @@ SetDNSServers(){ sed -i.bak '/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/DNS_FQDN_REQUIRED/d;' /etc/pihole/setupVars.conf # Save setting to file echo "PIHOLE_DNS_1=${args[2]}" >> /etc/pihole/setupVars.conf - echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + if [[ "${args[3]}" != "none" ]]; then + echo "PIHOLE_DNS_2=${args[3]}" >> /etc/pihole/setupVars.conf + else + echo "PIHOLE_DNS_2=" >> /etc/pihole/setupVars.conf + fi # Replace within actual dnsmasq config file sed -i '/server=/d;' /etc/dnsmasq.d/01-pihole.conf echo "server=${args[2]}" >> /etc/dnsmasq.d/01-pihole.conf - echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + if [[ "${args[3]}" != "none" ]]; then + echo "server=${args[3]}" >> /etc/dnsmasq.d/01-pihole.conf + fi # Remove domain-needed entry sed -i '/domain-needed/d;' /etc/dnsmasq.d/01-pihole.conf From da363070c7770f738c18c13120828edf722f149c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 20 Dec 2016 12:21:08 +0100 Subject: [PATCH 46/60] Added Kelvin --- advanced/Scripts/webpage.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6251b158..6b54abb7 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -31,11 +31,7 @@ SetTemperatureUnit(){ # Remove setting from file (create backup setupVars.conf.bak) sed -i.bak '/TEMPERATUREUNIT/d' /etc/pihole/setupVars.conf # Save setting to file - if [[ $unit == "F" ]] ; then - echo "TEMPERATUREUNIT=F" >> /etc/pihole/setupVars.conf - else - echo "TEMPERATUREUNIT=C" >> /etc/pihole/setupVars.conf - fi + echo "TEMPERATUREUNIT=${unit}" >> /etc/pihole/setupVars.conf } @@ -222,6 +218,7 @@ case "${args[1]}" in "-p" | "password" ) SetWebPassword;; "-c" | "celsius" ) unit="C"; SetTemperatureUnit;; "-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;; + "-k" | "kelvin" ) unit="K"; SetTemperatureUnit;; "setdns" ) SetDNSServers;; "setexcludedomains" ) SetExcludeDomains;; "setexcludeclients" ) SetExcludeClients;; From ee8f29d178ef18d68057564af71f4ab1da45a3cc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 20 Dec 2016 16:28:28 +0100 Subject: [PATCH 47/60] Fixed typo, extended help text --- advanced/Scripts/webpage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 6b54abb7..07706346 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -19,8 +19,9 @@ helpFunc() { ::: ::: Options: ::: -p, password Set web interface password, an empty input will remove any previously set password -::: -c, celsius Set Celcius temperature unit +::: -c, celsius Set Celsius temperature unit ::: -f, fahrenheit Set Fahrenheit temperature unit +::: -k, kelvin Set Kelvin temperature unit ::: -h, --help Show this help dialog EOM exit 0 From d0e3c546f80c86f2eef82df82bcfab88cd137417 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:00:37 -0800 Subject: [PATCH 48/60] CRLF to CR (Win to Unix line endings) Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1155 +--------------------------- 1 file changed, 1 insertion(+), 1154 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..96c8ece3 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1,1154 +1 @@ -#!/usr/bin/env bash -# Pi-hole: A black hole for Internet advertisements -# (c) 2015, 2016 by Jacob Salmela -# Network-wide ad blocking via your Raspberry Pi -# http://pi-hole.net -# Installs Pi-hole -# -# Pi-hole is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. - -# pi-hole.net/donate -# -# Install with this command (from your Pi): -# -# curl -L install.pi-hole.net | bash - -set -e -######## VARIABLES ######### -tmpLog=/tmp/pihole-install.log -instalLogLoc=/etc/pihole/install.log -setupVars=/etc/pihole/setupVars.conf - -webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" -webInterfaceDir="/var/www/html/admin" -piholeGitUrl="https://github.com/pi-hole/pi-hole.git" -PI_HOLE_LOCAL_REPO="/etc/.pihole" -PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) -useUpdateVars=false - -IPV4_ADDRESS="" -IPV6_ADDRESS="" -QUERY_LOGGING=true - -# Find the rows and columns will default to 80x24 is it can not be detected -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo $screen_size | awk '{print $1}') -columns=$(echo $screen_size | awk '{print $2}') - -# Divide by two so the dialogs take up half of the screen, which looks nice. -r=$(( rows / 2 )) -c=$(( columns / 2 )) -# Unless the screen is tiny -r=$(( r < 20 ? 20 : r )) -c=$(( c < 70 ? 70 : c )) - -######## Undocumented Flags. Shhh ######## -skipSpaceCheck=false -reconfigure=false -runUnattended=false - -######## FIRST CHECK ######## -# Must be root to install -echo ":::" -if [[ ${EUID} -eq 0 ]]; then - echo "::: You are root." -else - echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" - echo "::: system networking, it requires elevated rights. Please check the contents of the script for" - echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." - echo ":::" - echo "::: Detecting the presence of the sudo utility for continuation of this install..." - - if [ -x "$(command -v sudo)" ]; then - echo "::: Utility sudo located." - exec curl -sSL https://install.pi-hole.net | sudo bash "$@" - exit $? - else - echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." - exit 1 - fi -fi - -# Compatibility - -if [[ $(command -v apt-get) ]]; then - #Debian Family - ############################################# - PKG_MANAGER="apt-get" - PKG_CACHE="/var/lib/apt/lists/" - UPDATE_PKG_CACHE="${PKG_MANAGER} update" - PKG_UPDATE="${PKG_MANAGER} upgrade" - PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" - # ######################################### - # fixes for dependancy differences - # Debian 7 doesn't have iproute2 use iproute - ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" - # Prefer the php metapackage if it's there, fall back on the php5 pacakges - ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" - # ######################################### - INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) - PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) - LIGHTTPD_USER="www-data" - LIGHTTPD_GROUP="www-data" - LIGHTTPD_CFG="lighttpd.conf.debian" - DNSMASQ_USER="dnsmasq" - - package_check_install() { - dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" - } -elif [ $(command -v rpm) ]; then - # Fedora Family - if [ $(command -v dnf) ]; then - PKG_MANAGER="dnf" - else - PKG_MANAGER="yum" - fi - PKG_CACHE="/var/cache/${PKG_MANAGER}" - UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" - PKG_UPDATE="${PKG_MANAGER} update -y" - PKG_INSTALL="${PKG_MANAGER} install -y" - PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" - INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) - PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) - - if grep -q 'Fedora' /etc/redhat-release; then - remove_deps=(epel-release); - PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); - fi - LIGHTTPD_USER="lighttpd" - LIGHTTPD_GROUP="lighttpd" - LIGHTTPD_CFG="lighttpd.conf.fedora" - DNSMASQ_USER="nobody" - - package_check_install() { - rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" - } -else - echo "OS distribution not supported" - exit -fi - -####### FUNCTIONS ########## -spinner() { - local pid=$1 - local delay=0.50 - local spinstr='/-\|' - - while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do - local temp=${spinstr#?} - printf " [%c] " "${spinstr}" - local spinstr=${temp}${spinstr%"$temp"} - sleep ${delay} - printf "\b\b\b\b\b\b" - done - printf " \b\b\b\b" -} - -is_repo() { - # Use git to check if directory is currently under VCS, return the value - local directory="${1}" - git -C "${directory}" status --short &> /dev/null - return -} - -make_repo() { - local directory="${1}" - local remoteRepo="${2}" - # Remove the non-repod interface and clone the interface - echo -n "::: Cloning $remoteRepo into $directory..." - rm -rf "${directory}" - git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! - echo " done!" -} - -update_repo() { - local directory="${1}" - # Pull the latest commits - echo -n "::: Updating repo in $1..." - cd "${directory}" || exit 1 - git stash -q > /dev/null & spinner $! - git pull -q > /dev/null & spinner $! - echo " done!" -} - -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 - update_repo "${directory}" - else - make_repo "${directory}" "${remoteRepo}" - fi -} - -find_IPv4_information() { - # Find IP used to route to outside world - IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') - IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') - IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') -} - -get_available_interfaces() { - # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. - availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) -} - -welcomeDialogs() { - # Display the welcome dialog - whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} - - # Support for a part-time dev - whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} - - # Explain the need for a static address - whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. - -In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} -} - -verifyFreeDiskSpace() { - - # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) - # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. - echo "::: Verifying free disk space..." - local required_free_kilobytes=51200 - local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') - - # - Unknown free disk space , not a integer - if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then - echo "::: Unknown free disk space!" - echo "::: We were unable to determine available free disk space on this system." - echo "::: You may override this check and force the installation, however, it is not recommended" - echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" - echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" - exit 1 - # - 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 "::: 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'" - echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" - - echo "Insufficient free space, exiting..." - exit 1 - - fi - -} - - -chooseInterface() { - # Turn the available interfaces into an array so it can be used with a whiptail dialog - local interfacesArray=() - # Number of available interfaces - local interfaceCount - # Whiptail variable storage - local chooseInterfaceCmd - # Temporary Whiptail options storage - local chooseInterfaceOptions - # Loop sentinel variable - local firstLoop=1 - - while read -r line; do - mode="OFF" - if [[ ${firstLoop} -eq 1 ]]; then - firstLoop=0 - mode="ON" - fi - interfacesArray+=("${line}" "available" "${mode}") - done <<< "${availableInterfaces}" - - # Find out how many interfaces are available to choose from - interfaceCount=$(echo "${availableInterfaces}" | wc -l) - chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) - chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]]; then - for desiredInterface in ${chooseInterfaceOptions}; do - PIHOLE_INTERFACE=${desiredInterface} - echo "::: Using interface: $PIHOLE_INTERFACE" - done - else - echo "::: Cancel selected, exiting...." - exit 1 - fi -} - -useIPv6dialog() { - # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') - whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} -} - - -use4andor6() { - local useIPv4 - local useIPv6 - # Let use select IPv4 and/or IPv6 - cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) - options=(IPv4 "Block ads over IPv4" on - IPv6 "Block ads over IPv6" on) - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - for choice in ${choices} - do - case ${choice} in - IPv4 ) useIPv4=true;; - IPv6 ) useIPv6=true;; - esac - done - if [[ ${useIPv4} ]]; then - find_IPv4_information - getStaticIPv4Settings - setStaticIPv4 - fi - if [[ ${useIPv6} ]]; then - useIPv6dialog - fi - echo "::: IPv4 address: ${IPV4_ADDRESS}" - echo "::: IPv6 address: ${IPV6_ADDRESS}" - if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then - echo "::: Cannot continue, neither IPv4 or IPv6 selected" - echo "::: Exiting" - exit 1 - fi - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi -} - -getStaticIPv4Settings() { - # Ask if the user wants to use DHCP settings as their static IP - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. - whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. -If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. -It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} - # Nothing else to do since the variables are already set above - else - # Otherwise, we need to ask the user to input their desired settings. - # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) - # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ ${ipSettingsCorrect} = True ]]; do - # Ask for the IPv4 address - IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" - # Ask for the gateway - IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 gateway: ${IPv4gw}" - # Give the user a chance to review their settings before moving on - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # After that's done, the loop ends and we move on - ipSettingsCorrect=True - else - # If the settings are wrong, the loop continues - ipSettingsCorrect=False - fi - else - # Cancelling gateway settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - else - # Cancelling IPv4 settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - done - # End the if statement for DHCP vs. static - fi -} - -setDHCPCD() { - # Append these lines to dhcpcd.conf to enable a static IP - echo "## interface ${PIHOLE_INTERFACE} - static ip_address=${IPV4_ADDRESS} - static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null -} - -setStaticIPv4() { - local IFCFG_FILE - local IPADDR - local CIDR - if [[ -f /etc/dhcpcd.conf ]]; then - # Debian Family - if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then - echo "::: Static IP already configured" - else - setDHCPCD - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then - # Fedora Family - IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} - if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then - echo "::: Static IP already configured" - else - IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) - CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) - # Backup existing interface configuration: - cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig - # Build Interface configuration file: - { - echo "# Configured via Pi-Hole installer" - echo "DEVICE=$PIHOLE_INTERFACE" - echo "BOOTPROTO=none" - echo "ONBOOT=yes" - echo "IPADDR=$IPADDR" - echo "PREFIX=$CIDR" - echo "GATEWAY=$IPv4gw" - echo "DNS1=$PIHOLE_DNS_1" - echo "DNS2=$PIHOLE_DNS_2" - echo "USERCTL=no" - }> "${IFCFG_FILE}" - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - if [ -x "$(command -v nmcli)" ];then - # Tell NetworkManager to read our new sysconfig file - nmcli con load "${IFCFG_FILE}" > /dev/null - fi - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - else - echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" - exit 1 - fi -} - -valid_ip() { - local ip=${1} - local stat=1 - - if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then - OIFS=$IFS - IFS='.' - ip=(${ip}) - IFS=${OIFS} - [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ - && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] - stat=$? - fi - return ${stat} -} - -setDNS() { - DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) - DNSChooseOptions=(Google "" on - OpenDNS "" off - Level3 "" off - Norton "" off - Comodo "" off - Custom "" off) - DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - case ${DNSchoices} in - Google) - echo "::: Using Google DNS servers." - PIHOLE_DNS_1="8.8.8.8" - PIHOLE_DNS_2="8.8.4.4" - ;; - OpenDNS) - echo "::: Using OpenDNS servers." - PIHOLE_DNS_1="208.67.222.222" - PIHOLE_DNS_2="208.67.220.220" - ;; - Level3) - echo "::: Using Level3 servers." - PIHOLE_DNS_1="4.2.2.1" - PIHOLE_DNS_2="4.2.2.2" - ;; - Norton) - echo "::: Using Norton ConnectSafe servers." - PIHOLE_DNS_1="199.85.126.10" - PIHOLE_DNS_2="199.85.127.10" - ;; - Comodo) - echo "::: Using Comodo Secure servers." - PIHOLE_DNS_1="8.26.56.26" - PIHOLE_DNS_2="8.20.247.20" - ;; - Custom) - until [[ ${DNSSettingsCorrect} = True ]]; do - strInvalid="Invalid" - if [ ! ${PIHOLE_DNS_1} ]; then - if [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="" - else - prePopulate=", ${PIHOLE_DNS_2}" - fi - elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}" - elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" - fi - - piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) - - if [[ $? = 0 ]]; then - PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') - PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') - if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then - PIHOLE_DNS_1=${strInvalid} - fi - if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then - PIHOLE_DNS_2=${strInvalid} - fi - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then - PIHOLE_DNS_1="" - fi - if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - PIHOLE_DNS_2="" - fi - DNSSettingsCorrect=False - else - if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then - DNSSettingsCorrect=True - else - # If the settings are wrong, the loop continues - DNSSettingsCorrect=False - fi - fi - done - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi -} - -setLogging() { - local LogToggleCommand - local LogChooseOptions - local LogChoices - - LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) - LogChooseOptions=("On (Reccomended)" "" on - Off "" off) - LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) - case ${LogChoices} in - "On (Recommended)") - echo "::: Logging On." - QUERY_LOGGING=true - ;; - Off) - echo "::: Logging Off." - QUERY_LOGGING=false - ;; - esac -} - - -version_check_dnsmasq() { - # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory - 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." - mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} - echo " done." - echo -n "::: Restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - else - echo " it is not a pi-hole file, leaving alone!" - fi - else - echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - fi - - echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." - cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} - echo " done." - sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} - if [[ "${PIHOLE_DNS_1}" != "" ]]; then - sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} - fi - if [[ "${PIHOLE_DNS_2}" != "" ]]; then - sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} - fi - - sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} - - if [[ "${QUERY_LOGGING}" == false ]] ; then - #Disable Logging - sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} - else - #Enable Logging - sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} - fi -} - -remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done -} - -clean_existing() { - # Clean an exiting installation to prepare for upgrade/reinstall - # ${1} Directory to clean; ${2} Array of files to remove - local clean_directory="${1}" - local old_files=${2} - - for script in "${old_files[@]}"; do - rm -f "${clean_directory}${script}.sh" - done - -} - -installScripts() { - # Install the scripts from repository to their various locations - readonly install_dir="/opt/pihole/" - - echo ":::" - echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." - - # Clear out script files from Pi-hole scripts directory. - clean_existing "${install_dir}" "${PI_HOLE_FILES}" - - # Install files from local core repository - if is_repo "${PI_HOLE_LOCAL_REPO}"; then - cd "${PI_HOLE_LOCAL_REPO}" - install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh - install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh - install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh - install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole - install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole - echo " done." - else - echo " *** ERROR: Local repo ${core_repo} not found, exiting." - exit 1 - fi -} - -installConfigs() { - # Install the configs from /etc/.pihole to their various locations - echo ":::" - echo "::: Installing configs..." - version_check_dnsmasq - if [ ! -d "/etc/lighttpd" ]; then - mkdir /etc/lighttpd - chown "${USER}":root /etc/lighttpd - 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 - mkdir -p /var/run/lighttpd - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd - mkdir -p /var/cache/lighttpd/compress - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress - mkdir -p /var/cache/lighttpd/uploads - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads -} - -stop_service() { - # Stop service passed in as argument. - # Can softfail, as process may not be installed when this is called - echo ":::" - echo -n "::: Stopping ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl stop "${1}" &> /dev/null & spinner $! || true - else - service "${1}" stop &> /dev/null & spinner $! || true - fi - echo " done." -} - -start_service() { - # Start/Restart service passed in as argument - # This should not fail, it's an error if it does - echo ":::" - echo -n "::: Starting ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl restart "${1}" &> /dev/null & spinner $! - else - service "${1}" restart &> /dev/null & spinner $! - fi - echo " done." -} - -enable_service() { - # Enable service so that it will start with next reboot - echo ":::" - echo -n "::: Enabling ${1} service to start on reboot..." - if [ -x "$(command -v systemctl)" ]; then - systemctl enable "${1}" &> /dev/null & spinner $! - else - update-rc.d "${1}" defaults &> /dev/null & spinner $! - fi - echo " done." -} - -update_pacakge_cache() { - #Running apt-get update/upgrade with minimal output can cause some issues with - #requiring user input (e.g password for phpmyadmin see #218) - - #Check to see if apt-get update has already been run today - #it needs to have been run at least once on new installs! - timestamp=$(stat -c %Y ${PKG_CACHE}) - timestampAsDate=$(date -d @"${timestamp}" "+%b %e") - today=$(date "+%b %e") - - if [ ! "${today}" == "${timestampAsDate}" ]; then - #update package lists - echo ":::" - echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." - ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! - echo " done!" - fi -} - -notify_package_updates_available() { - # Let user know if they have outdated packages on their system and - # advise them to run a package update at soonest possible. - echo ":::" - echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." - updatesToInstall=$(eval "${PKG_COUNT}") - echo " done!" - echo ":::" - if [[ ${updatesToInstall} -eq "0" ]]; then - 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 run '${PKG_UPDATE}' after installing Pi-Hole! " - echo ":::" - fi -} - -install_dependent_packages() { - # Install packages passed in via argument array - # No spinner - conflicts with set -e - declare -a argArray1=("${!1}") - - for i in "${argArray1[@]}"; do - echo -n "::: Checking for $i..." - package_check_install "${i}" &> /dev/null - echo " installed!" - done -} - -CreateLogFile() { - # Create logfiles if necessary - echo ":::" - echo -n "::: Creating log file and changing owner to dnsmasq..." - if [ ! -f /var/log/pihole.log ]; then - touch /var/log/pihole.log - chmod 644 /var/log/pihole.log - chown "${DNSMASQ_USER}":root /var/log/pihole.log - echo " done!" - else - echo " already exists!" - fi -} - -installPiholeWeb() { - # Install the web interface - echo ":::" - echo "::: Installing pihole custom index page..." - if [ -d "/var/www/html/pihole" ]; then - if [ -f "/var/www/html/pihole/index.html" ]; then - echo "::: Existing index.html detected, not overwriting" - else - echo -n "::: index.html missing, replacing... " - cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ - echo " done!" - fi - - if [ -f "/var/www/html/pihole/index.js" ]; then - echo "::: Existing index.js detected, not overwriting" - else - echo -n "::: index.js missing, replacing... " - cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ - echo " done!" - fi - - else - mkdir /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..." - mkdir -p /etc/sudoers.d/ - cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole - chmod 0440 /etc/sudoers.d/pihole - echo " done!" -} - -installCron() { - # Install the cron job - echo ":::" - echo -n "::: Installing latest Cron script..." - cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole - echo " done!" -} - -runGravity() { - # Run gravity.sh to build blacklists - echo ":::" - echo "::: Preparing to run gravity.sh to refresh hosts..." - if ls /etc/pihole/list* 1> /dev/null 2>&1; then - echo "::: Cleaning up previous install (preserving whitelist/blacklist)" - rm /etc/pihole/list.* - fi - echo "::: Running gravity.sh" - /opt/pihole/gravity.sh -} - -create_pihole_user() { - # Check if user pihole exists and create if not - echo "::: Checking if user 'pihole' exists..." - id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) -} - -configureFirewall() { - # Allow HTTP and DNS traffic - if [ -x "$(command -v firewall-cmd)" ]; then - firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ - && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" - elif [ -x "$(command -v iptables)" ]; then - echo "::: Configuring iptables for httpd and dnsmasq.." - iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT - iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT - iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT - else - echo "::: No firewall detected.. skipping firewall configuration." - fi -} - -finalExports() { - # Update variables in setupVars.conf file - if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" - fi - { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" - }>> "${setupVars}" -} - -installPihole() { - # Install base files and web interface - create_pihole_user - if [ ! -d "/var/www/html" ]; then - mkdir -p /var/www/html - fi - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html - chmod 775 /var/www/html - usermod -a -G ${LIGHTTPD_GROUP} pihole - if [ -x "$(command -v lighty-enable-mod)" ]; then - lighty-enable-mod fastcgi fastcgi-php > /dev/null || true - else - printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" - fi - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports - runGravity -} - -accountForRefactor() { - # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. - - # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - - sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} - sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} - sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} - -} - -updatePihole() { - accountForRefactor - # Source ${setupVars} for use in the rest of the functions. - . ${setupVars} - # Install base files and web interface - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports #re-export setupVars.conf to account for any new vars added in new versions - runGravity -} - -configureSelinux() { - if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Detected\n" - printf ":::\tChecking for SELinux policy development packages..." - package_check_install "selinux-policy-devel" > /dev/null - echo " installed!" - printf ":::\tEnabling httpd server side includes (SSI).. " - setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" - printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" - if ! [ -x "$(command -v systemctl)" ]; then - sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te - fi - checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te - semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod - semodule -i /etc/pihole/pihole.pp - rm -f /etc/pihole/pihole.mod - semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." - fi -} - -displayFinalMessage() { - if (( ${#1} > 0 )) ; then - # Final completion message to user - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: - -IPv4: ${IPV4_ADDRESS%/*} -IPv6: ${IPV6_ADDRESS} - -If you set a new IP address, you should restart the Pi. - -The install log is in /etc/pihole. -View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin -The currently set password is ${1}" ${r} ${c} - else - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: - -IPv4: ${IPV4_ADDRESS%/*} -IPv6: ${IPV6_ADDRESS} - -If you set a new IP address, you should restart the Pi. - -The install log is in /etc/pihole. -View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} - fi -} - -update_dialogs() { - # reconfigure - if [ "${reconfigure}" = true ]; then - opt1a="Repair" - opt1b="This will retain existing settings" - strAdd="You will remain on the same version" - else - opt1a="Update" - opt1b="This will retain existing settings." - strAdd="You will be updated to the latest version." - fi - opt2a="Reconfigure" - opt2b="This will allow you to enter new settings" - - UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ - "${opt1a}" "${opt1b}" \ - "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) - - if [[ $? = 0 ]];then - case ${UpdateCmd} in - ${opt1a}) - echo "::: ${opt1a} option selected." - useUpdateVars=true - ;; - ${opt2a}) - echo "::: ${opt2a} option selected" - useUpdateVars=false - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi - -} - -main() { -# Check arguments for the undocumented flags - for var in "$@"; do - case "$var" in - "--reconfigure" ) reconfigure=true;; - "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; - "--unattended" ) runUnattended=true;; - esac - done - - if [[ -f ${setupVars} ]]; then - if [[ "${runUnattended}" == true ]]; then - echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" - useUpdateVars=true - else - update_dialogs - fi - fi - - # Start the installer - # Verify there is enough disk space for the install - if [[ "${skipSpaceCheck}" == true ]]; then - echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" - else - verifyFreeDiskSpace - fi - - # Update package cache - update_pacakge_cache - - # Notify user of package availability - notify_package_updates_available - - # Install packages used by this installation script - install_dependent_packages INSTALLER_DEPS[@] - - if [[ "${reconfigure}" == true ]]; then - echo "::: --reconfigure passed to install script. Not downloading/updating local repos" - else - # Get Git files for Core and Admin - getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} - getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} - fi - - if [[ ${useUpdateVars} == false ]]; then - # Display welcome dialogs - welcomeDialogs - # Create directory for Pi-hole storage - mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts - # Stop resolver and webserver while installing proceses - stop_service dnsmasq - stop_service lighttpd - # Determine available interfaces - get_available_interfaces - # Find interfaces and let the user choose one - chooseInterface - # Let the user decide if they want to block ads over IPv4 and/or IPv6 - use4andor6 - # Decide what upstream DNS Servers to use - setDNS - # Let the user decide if they want query logging enabled... - setLogging - - # Install packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] - - # Install and log everything to a file - installPihole | tee ${tmpLog} - else - # update packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] - - updatePihole | tee ${tmpLog} - fi - - # Move the log file into /etc/pihole for storage - mv ${tmpLog} ${instalLogLoc} - - # Add password to web UI if there is none - 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) - pihole -a -p ${pw} - fi - - if [[ "${useUpdateVars}" == false ]]; then - displayFinalMessage ${pw} - fi - - echo "::: Restarting services..." - # Start services - start_service dnsmasq - enable_service dnsmasq - start_service lighttpd - enable_service lighttpd - echo "::: done." - - echo ":::" - if [[ "${useUpdateVars}" == false ]]; then - echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" - echo "::: ${IPV4_ADDRESS%/*}" - echo "::: ${IPV6_ADDRESS}" - echo ":::" - echo "::: If you set a new IP address, you should restart the Pi." - echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" - else - echo "::: Update complete!" - fi - - if (( ${#pw} > 0 )) ; then - echo ":::" - echo "::: Note: As security measure a password has been installed for your web interface" - echo "::: The currently set password is" - echo "::: ${pw}" - echo ":::" - echo "::: You can always change it using" - echo "::: pihole -a -p new_password" - fi - - echo ":::" - echo "::: The install log is located at: /etc/pihole/install.log" -} - -if [[ "${PH_TEST}" != true ]] ; then - main "$@" -fi +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## spinner() { local pid=$1 local delay=0.50 local spinstr='/-\|' while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do local temp=${spinstr#?} printf " [%c] " "${spinstr}" local spinstr=${temp}${spinstr%"$temp"} sleep ${delay} printf "\b\b\b\b\b\b" done printf " \b\b\b\b" } is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q > /dev/null & spinner $! git pull -q > /dev/null & spinner $! echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null & spinner $! || true else service "${1}" stop &> /dev/null & spinner $! || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null & spinner $! else service "${1}" restart &> /dev/null & spinner $! fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null & spinner $! else update-rc.d "${1}" defaults &> /dev/null & spinner $! fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 5c43df66a8e94ca08709a322ae8b0dfcb4b9e7e3 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:07:36 -0800 Subject: [PATCH 49/60] Remove spinner function, masked return values. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 96c8ece3..d96c7547 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=( apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=( iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG} ) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=( iproute net-tools procps-ng newt git ) PIHOLE_DEPS=( epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat ) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## spinner() { local pid=$1 local delay=0.50 local spinstr='/-\|' while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do local temp=${spinstr#?} printf " [%c] " "${spinstr}" local spinstr=${temp}${spinstr%"$temp"} sleep ${delay} printf "\b\b\b\b\b\b" done printf " \b\b\b\b" } is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q > /dev/null & spinner $! git pull -q > /dev/null & spinner $! echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null & spinner $! || true else service "${1}" stop &> /dev/null & spinner $! || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null & spinner $! else service "${1}" restart &> /dev/null & spinner $! fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null & spinner $! else update-rc.d "${1}" defaults &> /dev/null & spinner $! fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=(iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG}) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(iproute net-tools procps-ng newt git) PIHOLE_DEPS=(epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 9a626948f8b0b72578a562d16bbbc84d13ea2466 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 15:39:32 -0800 Subject: [PATCH 50/60] Use debconf for Deb/Ubu package installations. Signed-off-by: Dan Schaper Alphabetic sort of dependency names Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index d96c7547..c64c3864 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils whiptail git dhcpcd5) PIHOLE_DEPS=(iputils-ping lsof dnsutils bc dnsmasq lighttpd ${phpVer}-common ${phpVer}-cgi curl unzip wget sudo netcat cron ${IPROUTE_PKG}) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" PKG_INSTALL="${PKG_MANAGER} install -y" PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(iproute net-tools procps-ng newt git) PIHOLE_DEPS=(epel-release bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common php-cli php curl unzip wget findutils cronie sudo nmap-ncat) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" 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) PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") if command -v debconf-apt-progress &> /dev/null; then debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" else for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done fi } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file From 7f3b0030ea478e4c5867b8e173a0f5b68aec8688 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 16:47:43 -0800 Subject: [PATCH 51/60] LF Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1144 +++++++++++++++++++++++++++- 1 file changed, 1143 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c64c3864..0c66094f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1 +1,1143 @@ -#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2015, 2016 by Jacob Salmela # Network-wide ad blocking via your Raspberry Pi # http://pi-hole.net # Installs Pi-hole # # Pi-hole is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # pi-hole.net/donate # # Install with this command (from your Pi): # # curl -L install.pi-hole.net | bash set -e ######## VARIABLES ######### tmpLog=/tmp/pihole-install.log instalLogLoc=/etc/pihole/install.log setupVars=/etc/pihole/setupVars.conf webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) useUpdateVars=false IPV4_ADDRESS="" IPV6_ADDRESS="" QUERY_LOGGING=true # Find the rows and columns will default to 80x24 is it can not be detected screen_size=$(stty size 2>/dev/null || echo 24 80) rows=$(echo $screen_size | awk '{print $1}') columns=$(echo $screen_size | awk '{print $2}') # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) c=$(( columns / 2 )) # Unless the screen is tiny r=$(( r < 20 ? 20 : r )) c=$(( c < 70 ? 70 : c )) ######## Undocumented Flags. Shhh ######## skipSpaceCheck=false reconfigure=false runUnattended=false ######## FIRST CHECK ######## # Must be root to install echo ":::" if [[ ${EUID} -eq 0 ]]; then echo "::: You are root." else echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" echo "::: system networking, it requires elevated rights. Please check the contents of the script for" echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? else echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." exit 1 fi fi # Compatibility if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" # ######################################### # fixes for dependancy differences # Debian 7 doesn't have iproute2 use iproute ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" # Prefer the php metapackage if it's there, fall back on the php5 pacakges ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" # ######################################### INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) LIGHTTPD_USER="www-data" LIGHTTPD_GROUP="www-data" LIGHTTPD_CFG="lighttpd.conf.debian" DNSMASQ_USER="dnsmasq" package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } elif [ $(command -v rpm) ]; then # Fedora Family if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" PKG_UPDATE="${PKG_MANAGER} update -y" 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) PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) if grep -q 'Fedora' /etc/redhat-release; then remove_deps=(epel-release); PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); fi LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" DNSMASQ_USER="nobody" package_check_install() { rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" } else echo "OS distribution not supported" exit fi ####### FUNCTIONS ########## is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" git -C "${directory}" status --short &> /dev/null return } make_repo() { local directory="${1}" local remoteRepo="${2}" # Remove the non-repod interface and clone the interface echo -n "::: Cloning $remoteRepo into $directory..." rm -rf "${directory}" git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null echo " done!" } update_repo() { local directory="${1}" # Pull the latest commits echo -n "::: Updating repo in $1..." cd "${directory}" || exit 1 git stash -q &> /dev/null git pull -q &> /dev/null echo " done!" } 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 update_repo "${directory}" else make_repo "${directory}" "${remoteRepo}" fi } find_IPv4_information() { # Find IP used to route to outside world IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { # Display the welcome dialog whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Support for a part-time dev whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. echo "::: Verifying free disk space..." local required_free_kilobytes=51200 local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # - Unknown free disk space , not a integer if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then echo "::: Unknown free disk space!" echo "::: We were unable to determine available free disk space on this system." echo "::: You may override this check and force the installation, however, it is not recommended" echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" exit 1 # - 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 "::: 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'" echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" echo "Insufficient free space, exiting..." exit 1 fi } chooseInterface() { # Turn the available interfaces into an array so it can be used with a whiptail dialog local interfacesArray=() # Number of available interfaces local interfaceCount # Whiptail variable storage local chooseInterfaceCmd # Temporary Whiptail options storage local chooseInterfaceOptions # Loop sentinel variable local firstLoop=1 while read -r line; do mode="OFF" if [[ ${firstLoop} -eq 1 ]]; then firstLoop=0 mode="ON" fi interfacesArray+=("${line}" "available" "${mode}") done <<< "${availableInterfaces}" # Find out how many interfaces are available to choose from interfaceCount=$(echo "${availableInterfaces}" | wc -l) chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]]; then for desiredInterface in ${chooseInterfaceOptions}; do PIHOLE_INTERFACE=${desiredInterface} echo "::: Using interface: $PIHOLE_INTERFACE" done else echo "::: Cancel selected, exiting...." exit 1 fi } useIPv6dialog() { # Show the IPv6 address used for blocking IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { local useIPv4 local useIPv6 # Let use select IPv4 and/or IPv6 cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) options=(IPv4 "Block ads over IPv4" on IPv6 "Block ads over IPv6" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then for choice in ${choices} do case ${choice} in IPv4 ) useIPv4=true;; IPv6 ) useIPv6=true;; esac done if [[ ${useIPv4} ]]; then find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi if [[ ${useIPv6} ]]; then useIPv6dialog fi echo "::: IPv4 address: ${IPV4_ADDRESS}" echo "::: IPv6 address: ${IPV6_ADDRESS}" if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then echo "::: Cannot continue, neither IPv4 or IPv6 selected" echo "::: Exiting" exit 1 fi else echo "::: Cancel selected. Exiting..." exit 1 fi } getStaticIPv4Settings() { # Ask if the user wants to use DHCP settings as their static IP if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} # Nothing else to do since the variables are already set above else # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary until [[ ${ipSettingsCorrect} = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then echo "::: Your static IPv4 gateway: ${IPv4gw}" # Give the user a chance to review their settings before moving on if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? IP address: ${IPV4_ADDRESS} Gateway: ${IPv4gw}" ${r} ${c}); then # After that's done, the loop ends and we move on ipSettingsCorrect=True else # If the settings are wrong, the loop continues ipSettingsCorrect=False fi else # Cancelling gateway settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi else # Cancelling IPv4 settings window ipSettingsCorrect=False echo "::: Cancel selected. Exiting..." exit 1 fi done # End the if statement for DHCP vs. static fi } setDHCPCD() { # Append these lines to dhcpcd.conf to enable a static IP echo "## interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { local IFCFG_FILE local IPADDR local CIDR if [[ -f /etc/dhcpcd.conf ]]; then # Debian Family if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo "::: Static IP already configured" else setDHCPCD ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then # Fedora Family IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then echo "::: Static IP already configured" else IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) # Backup existing interface configuration: cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig # Build Interface configuration file: { echo "# Configured via Pi-Hole installer" echo "DEVICE=$PIHOLE_INTERFACE" echo "BOOTPROTO=none" echo "ONBOOT=yes" echo "IPADDR=$IPADDR" echo "PREFIX=$CIDR" echo "GATEWAY=$IPv4gw" echo "DNS1=$PIHOLE_DNS_1" echo "DNS2=$PIHOLE_DNS_2" echo "USERCTL=no" }> "${IFCFG_FILE}" ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" if [ -x "$(command -v nmcli)" ];then # Tell NetworkManager to read our new sysconfig file nmcli con load "${IFCFG_FILE}" > /dev/null fi echo ":::" echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." echo ":::" fi else echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" exit 1 fi } valid_ip() { local ip=${1} local stat=1 if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=(${ip}) IFS=${OIFS} [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return ${stat} } setDNS() { DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) DNSChooseOptions=(Google "" on OpenDNS "" off Level3 "" off Norton "" off Comodo "" off Custom "" off) DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) if [[ $? = 0 ]];then case ${DNSchoices} in Google) echo "::: Using Google DNS servers." PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) echo "::: Using OpenDNS servers." PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) echo "::: Using Level3 servers." PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; Norton) echo "::: Using Norton ConnectSafe servers." PIHOLE_DNS_1="199.85.126.10" PIHOLE_DNS_2="199.85.127.10" ;; Comodo) echo "::: Using Comodo Secure servers." PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; Custom) until [[ ${DNSSettingsCorrect} = True ]]; do strInvalid="Invalid" if [ ! ${PIHOLE_DNS_1} ]; then if [ ! ${PIHOLE_DNS_2} ]; then prePopulate="" else prePopulate=", ${PIHOLE_DNS_2}" fi elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}" elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) if [[ $? = 0 ]]; then PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then PIHOLE_DNS_1=${strInvalid} fi if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then PIHOLE_DNS_2=${strInvalid} fi else echo "::: Cancel selected, exiting...." exit 1 fi if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi DNSSettingsCorrect=False else if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then DNSSettingsCorrect=True else # If the settings are wrong, the loop continues DNSSettingsCorrect=False fi fi done ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } setLogging() { local LogToggleCommand local LogChooseOptions local LogChoices LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) LogChooseOptions=("On (Reccomended)" "" on Off "" off) LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) case ${LogChoices} in "On (Recommended)") echo "::: Logging On." QUERY_LOGGING=true ;; Off) echo "::: Logging Off." QUERY_LOGGING=false ;; esac } version_check_dnsmasq() { # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} echo " done." echo -n "::: Restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." else echo " it is not a pi-hole file, leaving alone!" fi else echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." cp ${dnsmasq_original_config} ${dnsmasq_conf} echo " done." fi echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} echo " done." sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} if [[ "${PIHOLE_DNS_1}" != "" ]]; then sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} fi if [[ "${PIHOLE_DNS_2}" != "" ]]; then sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} else sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} fi sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else #Enable Logging sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} fi } remove_legacy_scripts() { #Tidy up /usr/local/bin directory if installing over previous install. oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) for i in "${oldFiles[@]}"; do if [ -f "/usr/local/bin/$i.sh" ]; then rm /usr/local/bin/"$i".sh fi done } clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove local clean_directory="${1}" local old_files=${2} for script in "${old_files[@]}"; do rm -f "${clean_directory}${script}.sh" done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" echo ":::" echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." # Clear out script files from Pi-hole scripts directory. clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 fi } installConfigs() { # Install the configs from /etc/.pihole to their various locations echo ":::" echo "::: Installing configs..." version_check_dnsmasq if [ ! -d "/etc/lighttpd" ]; then mkdir /etc/lighttpd chown "${USER}":root /etc/lighttpd 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 mkdir -p /var/run/lighttpd chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd mkdir -p /var/cache/lighttpd/compress chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress mkdir -p /var/cache/lighttpd/uploads chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called echo ":::" echo -n "::: Stopping ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi echo " done." } start_service() { # Start/Restart service passed in as argument # This should not fail, it's an error if it does echo ":::" echo -n "::: Starting ${1} service..." if [ -x "$(command -v systemctl)" ]; then systemctl restart "${1}" &> /dev/null else service "${1}" restart &> /dev/null fi echo " done." } enable_service() { # Enable service so that it will start with next reboot echo ":::" echo -n "::: Enabling ${1} service to start on reboot..." if [ -x "$(command -v systemctl)" ]; then systemctl enable "${1}" &> /dev/null else update-rc.d "${1}" defaults &> /dev/null fi echo " done." } update_pacakge_cache() { #Running apt-get update/upgrade with minimal output can cause some issues with #requiring user input (e.g password for phpmyadmin see #218) #Check to see if apt-get update has already been run today #it needs to have been run at least once on new installs! timestamp=$(stat -c %Y ${PKG_CACHE}) timestampAsDate=$(date -d @"${timestamp}" "+%b %e") today=$(date "+%b %e") if [ ! "${today}" == "${timestampAsDate}" ]; then #update package lists echo ":::" echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." ${UPDATE_PKG_CACHE} &> /dev/null echo " done!" fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. echo ":::" echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." updatesToInstall=$(eval "${PKG_COUNT}") echo " done!" echo ":::" if [[ ${updatesToInstall} -eq "0" ]]; then 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 run '${PKG_UPDATE}' after installing Pi-Hole! " echo ":::" fi } install_dependent_packages() { # Install packages passed in via argument array # No spinner - conflicts with set -e declare -a argArray1=("${!1}") if command -v debconf-apt-progress &> /dev/null; then debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" else for i in "${argArray1[@]}"; do echo -n "::: Checking for $i..." package_check_install "${i}" &> /dev/null echo " installed!" done fi } CreateLogFile() { # Create logfiles if necessary echo ":::" echo -n "::: Creating log file and changing owner to dnsmasq..." if [ ! -f /var/log/pihole.log ]; then touch /var/log/pihole.log chmod 644 /var/log/pihole.log chown "${DNSMASQ_USER}":root /var/log/pihole.log echo " done!" else echo " already exists!" fi } installPiholeWeb() { # Install the web interface echo ":::" echo "::: Installing pihole custom index page..." if [ -d "/var/www/html/pihole" ]; then if [ -f "/var/www/html/pihole/index.html" ]; then echo "::: Existing index.html detected, not overwriting" else echo -n "::: index.html missing, replacing... " cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ echo " done!" fi if [ -f "/var/www/html/pihole/index.js" ]; then echo "::: Existing index.js detected, not overwriting" else echo -n "::: index.js missing, replacing... " cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ echo " done!" fi else mkdir /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..." mkdir -p /etc/sudoers.d/ cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole chmod 0440 /etc/sudoers.d/pihole echo " done!" } installCron() { # Install the cron job echo ":::" echo -n "::: Installing latest Cron script..." cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole echo " done!" } runGravity() { # Run gravity.sh to build blacklists echo ":::" echo "::: Preparing to run gravity.sh to refresh hosts..." if ls /etc/pihole/list* 1> /dev/null 2>&1; then echo "::: Cleaning up previous install (preserving whitelist/blacklist)" rm /etc/pihole/list.* fi echo "::: Running gravity.sh" /opt/pihole/gravity.sh } create_pihole_user() { # Check if user pihole exists and create if not echo "::: Checking if user 'pihole' exists..." id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { # Allow HTTP and DNS traffic if [ -x "$(command -v firewall-cmd)" ]; then firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" elif [ -x "$(command -v iptables)" ]; then echo "::: Configuring iptables for httpd and dnsmasq.." iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT else echo "::: No firewall detected.. skipping firewall configuration." fi } finalExports() { # Update variables in setupVars.conf file if [ -e "${setupVars}" ]; then sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" fi { echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { # Install base files and web interface create_pihole_user if [ ! -d "/var/www/html" ]; then mkdir -p /var/www/html fi chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html chmod 775 /var/www/html usermod -a -G ${LIGHTTPD_GROUP} pihole if [ -x "$(command -v lighty-enable-mod)" ]; then lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" fi installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports runGravity } accountForRefactor() { # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. # Refactoring of install script has changed the name of a couple of variables. Sort them out here. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } updatePihole() { accountForRefactor # Source ${setupVars} for use in the rest of the functions. . ${setupVars} # Install base files and web interface installScripts installConfigs CreateLogFile configureSelinux installPiholeWeb installCron configureFirewall finalExports #re-export setupVars.conf to account for any new vars added in new versions runGravity } configureSelinux() { if [ -x "$(command -v getenforce)" ]; then printf "\n::: SELinux Detected\n" printf ":::\tChecking for SELinux policy development packages..." package_check_install "selinux-policy-devel" > /dev/null echo " installed!" printf ":::\tEnabling httpd server side includes (SSI).. " setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" if ! [ -x "$(command -v systemctl)" ]; then sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te fi checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod semodule -i /etc/pihole/pihole.pp rm -f /etc/pihole/pihole.mod semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." fi } displayFinalMessage() { if (( ${#1} > 0 )) ; then # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} else whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} fi } update_dialogs() { # reconfigure if [ "${reconfigure}" = true ]; then opt1a="Repair" opt1b="This will retain existing settings" strAdd="You will remain on the same version" else opt1a="Update" opt1b="This will retain existing settings." strAdd="You will be updated to the latest version." fi opt2a="Reconfigure" opt2b="This will allow you to enter new settings" UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) if [[ $? = 0 ]];then case ${UpdateCmd} in ${opt1a}) echo "::: ${opt1a} option selected." useUpdateVars=true ;; ${opt2a}) echo "::: ${opt2a} option selected" useUpdateVars=false ;; esac else echo "::: Cancel selected. Exiting..." exit 1 fi } main() { # Check arguments for the undocumented flags for var in "$@"; do case "$var" in "--reconfigure" ) reconfigure=true;; "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; "--unattended" ) runUnattended=true;; esac done if [[ -f ${setupVars} ]]; then if [[ "${runUnattended}" == true ]]; then echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" useUpdateVars=true else update_dialogs fi fi # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" else verifyFreeDiskSpace fi # Update package cache update_pacakge_cache # Notify user of package availability notify_package_updates_available # Install packages used by this installation script install_dependent_packages INSTALLER_DEPS[@] if [[ "${reconfigure}" == true ]]; then echo "::: --reconfigure passed to install script. Not downloading/updating local repos" else # Get Git files for Core and Admin getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} fi if [[ ${useUpdateVars} == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ # Remove legacy scripts from previous storage location remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd # Determine available interfaces get_available_interfaces # Find interfaces and let the user choose one chooseInterface # Let the user decide if they want to block ads over IPv4 and/or IPv6 use4andor6 # Decide what upstream DNS Servers to use setDNS # Let the user decide if they want query logging enabled... setLogging # Install packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] # Install and log everything to a file installPihole | tee ${tmpLog} else # update packages used by the Pi-hole install_dependent_packages PIHOLE_DEPS[@] updatePihole | tee ${tmpLog} fi # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} # Add password to web UI if there is none 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) pihole -a -p ${pw} fi if [[ "${useUpdateVars}" == false ]]; then displayFinalMessage ${pw} fi echo "::: Restarting services..." # Start services start_service dnsmasq enable_service dnsmasq start_service lighttpd enable_service lighttpd echo "::: done." echo ":::" if [[ "${useUpdateVars}" == false ]]; then echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" echo "::: ${IPV4_ADDRESS%/*}" echo "::: ${IPV6_ADDRESS}" echo ":::" echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" else echo "::: Update complete!" fi if (( ${#pw} > 0 )) ; then echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" echo ":::" echo "::: You can always change it using" echo "::: pihole -a -p new_password" fi echo ":::" echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then main "$@" fi \ No newline at end of file +#!/usr/bin/env bash +# Pi-hole: A black hole for Internet advertisements +# (c) 2015, 2016 by Jacob Salmela +# Network-wide ad blocking via your Raspberry Pi +# http://pi-hole.net +# Installs Pi-hole +# +# Pi-hole is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# pi-hole.net/donate +# +# Install with this command (from your Pi): +# +# curl -L install.pi-hole.net | bash + +set -e +######## VARIABLES ######### +tmpLog=/tmp/pihole-install.log +instalLogLoc=/etc/pihole/install.log +setupVars=/etc/pihole/setupVars.conf + +webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" +webInterfaceDir="/var/www/html/admin" +piholeGitUrl="https://github.com/pi-hole/pi-hole.git" +PI_HOLE_LOCAL_REPO="/etc/.pihole" +PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version) +useUpdateVars=false + +IPV4_ADDRESS="" +IPV6_ADDRESS="" +QUERY_LOGGING=true + +# Find the rows and columns will default to 80x24 is it can not be detected +screen_size=$(stty size 2>/dev/null || echo 24 80) +rows=$(echo $screen_size | awk '{print $1}') +columns=$(echo $screen_size | awk '{print $2}') + +# Divide by two so the dialogs take up half of the screen, which looks nice. +r=$(( rows / 2 )) +c=$(( columns / 2 )) +# Unless the screen is tiny +r=$(( r < 20 ? 20 : r )) +c=$(( c < 70 ? 70 : c )) + +######## Undocumented Flags. Shhh ######## +skipSpaceCheck=false +reconfigure=false +runUnattended=false + +######## FIRST CHECK ######## +# Must be root to install +echo ":::" +if [[ ${EUID} -eq 0 ]]; then + echo "::: You are root." +else + echo "::: Script called with non-root privileges. The Pi-hole installs server packages and configures" + echo "::: system networking, it requires elevated rights. Please check the contents of the script for" + echo "::: any concerns with this requirement. Please be sure to download this script from a trusted source." + echo ":::" + echo "::: Detecting the presence of the sudo utility for continuation of this install..." + + if [ -x "$(command -v sudo)" ]; then + echo "::: Utility sudo located." + exec curl -sSL https://install.pi-hole.net | sudo bash "$@" + exit $? + else + echo "::: sudo is needed for the Web interface to run pihole commands. Please run this script as root and it will be automatically installed." + exit 1 + fi +fi + +# Compatibility + +if [[ $(command -v apt-get) ]]; then + #Debian Family + ############################################# + PKG_MANAGER="apt-get" + PKG_CACHE="/var/lib/apt/lists/" + UPDATE_PKG_CACHE="${PKG_MANAGER} update" + PKG_UPDATE="${PKG_MANAGER} upgrade" + PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing 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" + # ######################################### + # fixes for dependancy differences + # Debian 7 doesn't have iproute2 use iproute + ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1 && IPROUTE_PKG="iproute2" || IPROUTE_PKG="iproute" + # Prefer the php metapackage if it's there, fall back on the php5 pacakges + ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1 && phpVer="php" || phpVer="php5" + # ######################################### + INSTALLER_DEPS=(apt-utils debconf dhcpcd5 git whiptail) + PIHOLE_DEPS=(bc cron curl dnsmasq dnsutils ${IPROUTE_PKG} iputils-ping lighttpd lsof netcat ${phpVer}-common ${phpVer}-cgi sudo unzip wget) + LIGHTTPD_USER="www-data" + LIGHTTPD_GROUP="www-data" + LIGHTTPD_CFG="lighttpd.conf.debian" + DNSMASQ_USER="dnsmasq" + + package_check_install() { + dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" + } +elif [ $(command -v rpm) ]; then + # Fedora Family + if [ $(command -v dnf) ]; then + PKG_MANAGER="dnf" + else + PKG_MANAGER="yum" + fi + PKG_CACHE="/var/cache/${PKG_MANAGER}" + UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" + PKG_UPDATE="${PKG_MANAGER} update -y" + 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) + PIHOLE_DEPS=(bc bind-utils cronie curl dnsmasq epel-release findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget) + + if grep -q 'Fedora' /etc/redhat-release; then + remove_deps=(epel-release); + PIHOLE_DEPS=( ${PIHOLE_DEPS[@]/$remove_deps} ); + fi + LIGHTTPD_USER="lighttpd" + LIGHTTPD_GROUP="lighttpd" + LIGHTTPD_CFG="lighttpd.conf.fedora" + DNSMASQ_USER="nobody" + + package_check_install() { + rpm -qa | grep ^"${1}"- > /dev/null || ${PKG_INSTALL} "${1}" + } +else + echo "OS distribution not supported" + exit +fi + +####### FUNCTIONS ########## +is_repo() { + # Use git to check if directory is currently under VCS, return the value + local directory="${1}" + git -C "${directory}" status --short &> /dev/null + return +} + +make_repo() { + local directory="${1}" + local remoteRepo="${2}" + # Remove the non-repod interface and clone the interface + echo -n "::: Cloning $remoteRepo into $directory..." + rm -rf "${directory}" + git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null + echo " done!" +} + +update_repo() { + local directory="${1}" + # Pull the latest commits + echo -n "::: Updating repo in $1..." + cd "${directory}" || exit 1 + git stash -q &> /dev/null + git pull -q &> /dev/null + echo " done!" +} + +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 + update_repo "${directory}" + else + make_repo "${directory}" "${remoteRepo}" + fi +} + +find_IPv4_information() { + # Find IP used to route to outside world + IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') + IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') + IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') +} + +get_available_interfaces() { + # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. + availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) +} + +welcomeDialogs() { + # Display the welcome dialog + whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} + + # Support for a part-time dev + whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} + + # Explain the need for a static address + whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. + +In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} +} + +verifyFreeDiskSpace() { + + # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) + # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. + echo "::: Verifying free disk space..." + local required_free_kilobytes=51200 + local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') + + # - Unknown free disk space , not a integer + if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then + echo "::: Unknown free disk space!" + echo "::: We were unable to determine available free disk space on this system." + echo "::: You may override this check and force the installation, however, it is not recommended" + echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" + echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" + exit 1 + # - 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 "::: 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'" + echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" + + echo "Insufficient free space, exiting..." + exit 1 + + fi + +} + + +chooseInterface() { + # Turn the available interfaces into an array so it can be used with a whiptail dialog + local interfacesArray=() + # Number of available interfaces + local interfaceCount + # Whiptail variable storage + local chooseInterfaceCmd + # Temporary Whiptail options storage + local chooseInterfaceOptions + # Loop sentinel variable + local firstLoop=1 + + while read -r line; do + mode="OFF" + if [[ ${firstLoop} -eq 1 ]]; then + firstLoop=0 + mode="ON" + fi + interfacesArray+=("${line}" "available" "${mode}") + done <<< "${availableInterfaces}" + + # Find out how many interfaces are available to choose from + interfaceCount=$(echo "${availableInterfaces}" | wc -l) + chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) + chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]]; then + for desiredInterface in ${chooseInterfaceOptions}; do + PIHOLE_INTERFACE=${desiredInterface} + echo "::: Using interface: $PIHOLE_INTERFACE" + done + else + echo "::: Cancel selected, exiting...." + exit 1 + fi +} + +useIPv6dialog() { + # Show the IPv6 address used for blocking + IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} +} + + +use4andor6() { + local useIPv4 + local useIPv6 + # Let use select IPv4 and/or IPv6 + cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) + options=(IPv4 "Block ads over IPv4" on + IPv6 "Block ads over IPv6" on) + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + for choice in ${choices} + do + case ${choice} in + IPv4 ) useIPv4=true;; + IPv6 ) useIPv6=true;; + esac + done + if [[ ${useIPv4} ]]; then + find_IPv4_information + getStaticIPv4Settings + setStaticIPv4 + fi + if [[ ${useIPv6} ]]; then + useIPv6dialog + fi + echo "::: IPv4 address: ${IPV4_ADDRESS}" + echo "::: IPv6 address: ${IPV6_ADDRESS}" + if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then + echo "::: Cannot continue, neither IPv4 or IPv6 selected" + echo "::: Exiting" + exit 1 + fi + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi +} + +getStaticIPv4Settings() { + # Ask if the user wants to use DHCP settings as their static IP + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. + whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. +If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. +It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} + # Nothing else to do since the variables are already set above + else + # Otherwise, we need to ask the user to input their desired settings. + # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) + # Start a loop to let the user enter their information with the chance to go back and edit it if necessary + until [[ ${ipSettingsCorrect} = True ]]; do + # Ask for the IPv4 address + IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" + # Ask for the gateway + IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 gateway: ${IPv4gw}" + # Give the user a chance to review their settings before moving on + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # After that's done, the loop ends and we move on + ipSettingsCorrect=True + else + # If the settings are wrong, the loop continues + ipSettingsCorrect=False + fi + else + # Cancelling gateway settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + else + # Cancelling IPv4 settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + done + # End the if statement for DHCP vs. static + fi +} + +setDHCPCD() { + # Append these lines to dhcpcd.conf to enable a static IP + echo "## interface ${PIHOLE_INTERFACE} + static ip_address=${IPV4_ADDRESS} + static routers=${IPv4gw} + static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null +} + +setStaticIPv4() { + local IFCFG_FILE + local IPADDR + local CIDR + if [[ -f /etc/dhcpcd.conf ]]; then + # Debian Family + if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then + echo "::: Static IP already configured" + else + setDHCPCD + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then + # Fedora Family + IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} + if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then + echo "::: Static IP already configured" + else + IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) + CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) + # Backup existing interface configuration: + cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig + # Build Interface configuration file: + { + echo "# Configured via Pi-Hole installer" + echo "DEVICE=$PIHOLE_INTERFACE" + echo "BOOTPROTO=none" + echo "ONBOOT=yes" + echo "IPADDR=$IPADDR" + echo "PREFIX=$CIDR" + echo "GATEWAY=$IPv4gw" + echo "DNS1=$PIHOLE_DNS_1" + echo "DNS2=$PIHOLE_DNS_2" + echo "USERCTL=no" + }> "${IFCFG_FILE}" + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + if [ -x "$(command -v nmcli)" ];then + # Tell NetworkManager to read our new sysconfig file + nmcli con load "${IFCFG_FILE}" > /dev/null + fi + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + else + echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" + exit 1 + fi +} + +valid_ip() { + local ip=${1} + local stat=1 + + if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=(${ip}) + IFS=${OIFS} + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return ${stat} +} + +setDNS() { + DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) + DNSChooseOptions=(Google "" on + OpenDNS "" off + Level3 "" off + Norton "" off + Comodo "" off + Custom "" off) + DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + case ${DNSchoices} in + Google) + echo "::: Using Google DNS servers." + PIHOLE_DNS_1="8.8.8.8" + PIHOLE_DNS_2="8.8.4.4" + ;; + OpenDNS) + echo "::: Using OpenDNS servers." + PIHOLE_DNS_1="208.67.222.222" + PIHOLE_DNS_2="208.67.220.220" + ;; + Level3) + echo "::: Using Level3 servers." + PIHOLE_DNS_1="4.2.2.1" + PIHOLE_DNS_2="4.2.2.2" + ;; + Norton) + echo "::: Using Norton ConnectSafe servers." + PIHOLE_DNS_1="199.85.126.10" + PIHOLE_DNS_2="199.85.127.10" + ;; + Comodo) + echo "::: Using Comodo Secure servers." + PIHOLE_DNS_1="8.26.56.26" + PIHOLE_DNS_2="8.20.247.20" + ;; + Custom) + until [[ ${DNSSettingsCorrect} = True ]]; do + strInvalid="Invalid" + if [ ! ${PIHOLE_DNS_1} ]; then + if [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="" + else + prePopulate=", ${PIHOLE_DNS_2}" + fi + elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}" + elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" + fi + + piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) + + if [[ $? = 0 ]]; then + PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') + PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') + if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then + PIHOLE_DNS_1=${strInvalid} + fi + if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then + PIHOLE_DNS_2=${strInvalid} + fi + else + echo "::: Cancel selected, exiting...." + exit 1 + fi + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then + PIHOLE_DNS_1="" + fi + if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + PIHOLE_DNS_2="" + fi + DNSSettingsCorrect=False + else + if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then + DNSSettingsCorrect=True + else + # If the settings are wrong, the loop continues + DNSSettingsCorrect=False + fi + fi + done + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi +} + +setLogging() { + local LogToggleCommand + local LogChooseOptions + local LogChoices + + LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) + LogChooseOptions=("On (Reccomended)" "" on + Off "" off) + LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) + case ${LogChoices} in + "On (Recommended)") + echo "::: Logging On." + QUERY_LOGGING=true + ;; + Off) + echo "::: Logging Off." + QUERY_LOGGING=false + ;; + esac +} + + +version_check_dnsmasq() { + # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory + 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." + mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} + echo " done." + echo -n "::: Restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + else + echo " it is not a pi-hole file, leaving alone!" + fi + else + echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + fi + + echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." + cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} + echo " done." + sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} + if [[ "${PIHOLE_DNS_1}" != "" ]]; then + sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} + fi + if [[ "${PIHOLE_DNS_2}" != "" ]]; then + sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} + fi + + sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} + + if [[ "${QUERY_LOGGING}" == false ]] ; then + #Disable Logging + sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} + else + #Enable Logging + sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location} + fi +} + +remove_legacy_scripts() { + #Tidy up /usr/local/bin directory if installing over previous install. + oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) + for i in "${oldFiles[@]}"; do + if [ -f "/usr/local/bin/$i.sh" ]; then + rm /usr/local/bin/"$i".sh + fi + done +} + +clean_existing() { + # Clean an exiting installation to prepare for upgrade/reinstall + # ${1} Directory to clean; ${2} Array of files to remove + local clean_directory="${1}" + local old_files=${2} + + for script in "${old_files[@]}"; do + rm -f "${clean_directory}${script}.sh" + done + +} + +installScripts() { + # Install the scripts from repository to their various locations + readonly install_dir="/opt/pihole/" + + echo ":::" + echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." + + # Clear out script files from Pi-hole scripts directory. + clean_existing "${install_dir}" "${PI_HOLE_FILES}" + + # Install files from local core repository + if is_repo "${PI_HOLE_LOCAL_REPO}"; then + cd "${PI_HOLE_LOCAL_REPO}" + install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh + install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh + install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh + install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole + install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole + echo " done." + else + echo " *** ERROR: Local repo ${core_repo} not found, exiting." + exit 1 + fi +} + +installConfigs() { + # Install the configs from /etc/.pihole to their various locations + echo ":::" + echo "::: Installing configs..." + version_check_dnsmasq + if [ ! -d "/etc/lighttpd" ]; then + mkdir /etc/lighttpd + chown "${USER}":root /etc/lighttpd + 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 + mkdir -p /var/run/lighttpd + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd + mkdir -p /var/cache/lighttpd/compress + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress + mkdir -p /var/cache/lighttpd/uploads + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads +} + +stop_service() { + # Stop service passed in as argument. + # Can softfail, as process may not be installed when this is called + echo ":::" + echo -n "::: Stopping ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl stop "${1}" &> /dev/null || true + else + service "${1}" stop &> /dev/null || true + fi + echo " done." +} + +start_service() { + # Start/Restart service passed in as argument + # This should not fail, it's an error if it does + echo ":::" + echo -n "::: Starting ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl restart "${1}" &> /dev/null + else + service "${1}" restart &> /dev/null + fi + echo " done." +} + +enable_service() { + # Enable service so that it will start with next reboot + echo ":::" + echo -n "::: Enabling ${1} service to start on reboot..." + if [ -x "$(command -v systemctl)" ]; then + systemctl enable "${1}" &> /dev/null + else + update-rc.d "${1}" defaults &> /dev/null + fi + echo " done." +} + +update_pacakge_cache() { + #Running apt-get update/upgrade with minimal output can cause some issues with + #requiring user input (e.g password for phpmyadmin see #218) + + #Check to see if apt-get update has already been run today + #it needs to have been run at least once on new installs! + timestamp=$(stat -c %Y ${PKG_CACHE}) + timestampAsDate=$(date -d @"${timestamp}" "+%b %e") + today=$(date "+%b %e") + + if [ ! "${today}" == "${timestampAsDate}" ]; then + #update package lists + echo ":::" + echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." + ${UPDATE_PKG_CACHE} &> /dev/null + echo " done!" + fi +} + +notify_package_updates_available() { + # Let user know if they have outdated packages on their system and + # advise them to run a package update at soonest possible. + echo ":::" + echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." + updatesToInstall=$(eval "${PKG_COUNT}") + echo " done!" + echo ":::" + if [[ ${updatesToInstall} -eq "0" ]]; then + 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 run '${PKG_UPDATE}' after installing Pi-Hole! " + echo ":::" + fi +} + +install_dependent_packages() { + # Install packages passed in via argument array + # No spinner - conflicts with set -e + declare -a argArray1=("${!1}") + + if command -v debconf-apt-progress &> /dev/null; then + debconf-apt-progress -- ${PKG_INSTALL} "${argArray1[@]}" + else + for i in "${argArray1[@]}"; do + echo -n "::: Checking for $i..." + package_check_install "${i}" &> /dev/null + echo " installed!" + done + fi +} + +CreateLogFile() { + # Create logfiles if necessary + echo ":::" + echo -n "::: Creating log file and changing owner to dnsmasq..." + if [ ! -f /var/log/pihole.log ]; then + touch /var/log/pihole.log + chmod 644 /var/log/pihole.log + chown "${DNSMASQ_USER}":root /var/log/pihole.log + echo " done!" + else + echo " already exists!" + fi +} + +installPiholeWeb() { + # Install the web interface + echo ":::" + echo "::: Installing pihole custom index page..." + if [ -d "/var/www/html/pihole" ]; then + if [ -f "/var/www/html/pihole/index.html" ]; then + echo "::: Existing index.html detected, not overwriting" + else + echo -n "::: index.html missing, replacing... " + cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ + echo " done!" + fi + + if [ -f "/var/www/html/pihole/index.js" ]; then + echo "::: Existing index.js detected, not overwriting" + else + echo -n "::: index.js missing, replacing... " + cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ + echo " done!" + fi + + else + mkdir /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..." + mkdir -p /etc/sudoers.d/ + cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + chmod 0440 /etc/sudoers.d/pihole + echo " done!" +} + +installCron() { + # Install the cron job + echo ":::" + echo -n "::: Installing latest Cron script..." + cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole + echo " done!" +} + +runGravity() { + # Run gravity.sh to build blacklists + echo ":::" + echo "::: Preparing to run gravity.sh to refresh hosts..." + if ls /etc/pihole/list* 1> /dev/null 2>&1; then + echo "::: Cleaning up previous install (preserving whitelist/blacklist)" + rm /etc/pihole/list.* + fi + echo "::: Running gravity.sh" + /opt/pihole/gravity.sh +} + +create_pihole_user() { + # Check if user pihole exists and create if not + echo "::: Checking if user 'pihole' exists..." + id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) +} + +configureFirewall() { + # Allow HTTP and DNS traffic + if [ -x "$(command -v firewall-cmd)" ]; then + firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ + && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" + elif [ -x "$(command -v iptables)" ]; then + echo "::: Configuring iptables for httpd and dnsmasq.." + iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT + iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT + iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT + else + echo "::: No firewall detected.. skipping firewall configuration." + fi +} + +finalExports() { + # Update variables in setupVars.conf file + if [ -e "${setupVars}" ]; then + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + fi + { + echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" + echo "IPV4_ADDRESS=${IPV4_ADDRESS}" + echo "IPV6_ADDRESS=${IPV6_ADDRESS}" + echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" + echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" + echo "QUERY_LOGGING=${QUERY_LOGGING}" + }>> "${setupVars}" +} + +installPihole() { + # Install base files and web interface + create_pihole_user + if [ ! -d "/var/www/html" ]; then + mkdir -p /var/www/html + fi + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html + chmod 775 /var/www/html + usermod -a -G ${LIGHTTPD_GROUP} pihole + if [ -x "$(command -v lighty-enable-mod)" ]; then + lighty-enable-mod fastcgi fastcgi-php > /dev/null || true + else + printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" + fi + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports + runGravity +} + +accountForRefactor() { + # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. + + # Refactoring of install script has changed the name of a couple of variables. Sort them out here. + + sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} + sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} + sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} + +} + +updatePihole() { + accountForRefactor + # Source ${setupVars} for use in the rest of the functions. + . ${setupVars} + # Install base files and web interface + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports #re-export setupVars.conf to account for any new vars added in new versions + runGravity +} + +configureSelinux() { + if [ -x "$(command -v getenforce)" ]; then + printf "\n::: SELinux Detected\n" + printf ":::\tChecking for SELinux policy development packages..." + package_check_install "selinux-policy-devel" > /dev/null + echo " installed!" + printf ":::\tEnabling httpd server side includes (SSI).. " + setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" + printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" + if ! [ -x "$(command -v systemctl)" ]; then + sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te + fi + checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te + semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod + semodule -i /etc/pihole/pihole.pp + rm -f /etc/pihole/pihole.mod + semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." + fi +} + +displayFinalMessage() { + if (( ${#1} > 0 )) ; then + # Final completion message to user + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + +IPv4: ${IPV4_ADDRESS%/*} +IPv6: ${IPV6_ADDRESS} + +If you set a new IP address, you should restart the Pi. + +The install log is in /etc/pihole. +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin +The currently set password is ${1}" ${r} ${c} + else + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + +IPv4: ${IPV4_ADDRESS%/*} +IPv6: ${IPV6_ADDRESS} + +If you set a new IP address, you should restart the Pi. + +The install log is in /etc/pihole. +View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} + fi +} + +update_dialogs() { + # reconfigure + if [ "${reconfigure}" = true ]; then + opt1a="Repair" + opt1b="This will retain existing settings" + strAdd="You will remain on the same version" + else + opt1a="Update" + opt1b="This will retain existing settings." + strAdd="You will be updated to the latest version." + fi + opt2a="Reconfigure" + opt2b="This will allow you to enter new settings" + + UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ + "${opt1a}" "${opt1b}" \ + "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) + + if [[ $? = 0 ]];then + case ${UpdateCmd} in + ${opt1a}) + echo "::: ${opt1a} option selected." + useUpdateVars=true + ;; + ${opt2a}) + echo "::: ${opt2a} option selected" + useUpdateVars=false + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi + +} + +main() { +# Check arguments for the undocumented flags + for var in "$@"; do + case "$var" in + "--reconfigure" ) reconfigure=true;; + "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; + "--unattended" ) runUnattended=true;; + esac + done + + if [[ -f ${setupVars} ]]; then + if [[ "${runUnattended}" == true ]]; then + echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" + useUpdateVars=true + else + update_dialogs + fi + fi + + # Start the installer + # Verify there is enough disk space for the install + if [[ "${skipSpaceCheck}" == true ]]; then + echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" + else + verifyFreeDiskSpace + fi + + # Update package cache + update_pacakge_cache + + # Notify user of package availability + notify_package_updates_available + + # Install packages used by this installation script + install_dependent_packages INSTALLER_DEPS[@] + + if [[ "${reconfigure}" == true ]]; then + echo "::: --reconfigure passed to install script. Not downloading/updating local repos" + else + # Get Git files for Core and Admin + getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} + getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} + fi + + if [[ ${useUpdateVars} == false ]]; then + # Display welcome dialogs + welcomeDialogs + # Create directory for Pi-hole storage + mkdir -p /etc/pihole/ + # Remove legacy scripts from previous storage location + remove_legacy_scripts + # Stop resolver and webserver while installing proceses + stop_service dnsmasq + stop_service lighttpd + # Determine available interfaces + get_available_interfaces + # Find interfaces and let the user choose one + chooseInterface + # Let the user decide if they want to block ads over IPv4 and/or IPv6 + use4andor6 + # Decide what upstream DNS Servers to use + setDNS + # Let the user decide if they want query logging enabled... + setLogging + + # Install packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] + + # Install and log everything to a file + installPihole | tee ${tmpLog} + else + # update packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] + + updatePihole | tee ${tmpLog} + fi + + # Move the log file into /etc/pihole for storage + mv ${tmpLog} ${instalLogLoc} + + # Add password to web UI if there is none + 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) + pihole -a -p ${pw} + fi + + if [[ "${useUpdateVars}" == false ]]; then + displayFinalMessage ${pw} + fi + + echo "::: Restarting services..." + # Start services + start_service dnsmasq + enable_service dnsmasq + start_service lighttpd + enable_service lighttpd + echo "::: done." + + echo ":::" + if [[ "${useUpdateVars}" == false ]]; then + echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" + echo "::: ${IPV4_ADDRESS%/*}" + echo "::: ${IPV6_ADDRESS}" + echo ":::" + echo "::: If you set a new IP address, you should restart the Pi." + echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" + else + echo "::: Update complete!" + fi + + if (( ${#pw} > 0 )) ; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" + fi + + echo ":::" + echo "::: The install log is located at: /etc/pihole/install.log" +} + +if [[ "${PH_TEST}" != true ]] ; then + main "$@" +fi From c72ffae4a2c7bc234d08b9168db482727419de8d Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:02:51 -0800 Subject: [PATCH 52/60] Don't install extra packages (openresolv was installed over resolvconf). No longer need --fix-missing. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0c66094f..55c4716c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -81,7 +81,7 @@ if [[ $(command -v apt-get) ]]; then PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_UPDATE="${PKG_MANAGER} upgrade" - PKG_INSTALL="${PKG_MANAGER} --yes --fix-missing install" + 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" # ######################################### From 156a51c945f3c2e537347be467d5a8aabb26c49d Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:04:46 -0800 Subject: [PATCH 53/60] Remove extra display only variable. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 55c4716c..e10fa196 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -80,7 +80,6 @@ if [[ $(command -v apt-get) ]]; then PKG_MANAGER="apt-get" PKG_CACHE="/var/lib/apt/lists/" UPDATE_PKG_CACHE="${PKG_MANAGER} update" - PKG_UPDATE="${PKG_MANAGER} upgrade" 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" @@ -110,7 +109,6 @@ elif [ $(command -v rpm) ]; then fi PKG_CACHE="/var/cache/${PKG_MANAGER}" UPDATE_PKG_CACHE="${PKG_MANAGER} check-update" - PKG_UPDATE="${PKG_MANAGER} update -y" 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) @@ -744,7 +742,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 run '${PKG_UPDATE}' after installing Pi-Hole! " + echo "::: We recommend you update your OS after installing Pi-Hole! " echo ":::" fi } From ff2783f9fc884e24d2e8d88918f4aeb01ffeb985 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:22:57 -0800 Subject: [PATCH 54/60] Tabs to spaces and formatting. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1502 ++++++++++++++-------------- 1 file changed, 749 insertions(+), 753 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..3cd00ac1 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -158,393 +158,391 @@ is_repo() { make_repo() { local directory="${1}" - local remoteRepo="${2}" - # Remove the non-repod interface and clone the interface - echo -n "::: Cloning $remoteRepo into $directory..." - rm -rf "${directory}" - git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! - echo " done!" + local remoteRepo="${2}" + # Remove the non-repod interface and clone the interface + echo -n "::: Cloning $remoteRepo into $directory..." + rm -rf "${directory}" + git clone -q --depth 1 "${remoteRepo}" "${directory}" > /dev/null & spinner $! + echo " done!" } update_repo() { - local directory="${1}" - # Pull the latest commits - echo -n "::: Updating repo in $1..." - cd "${directory}" || exit 1 - git stash -q > /dev/null & spinner $! - git pull -q > /dev/null & spinner $! - echo " done!" + local directory="${1}" + # Pull the latest commits + echo -n "::: Updating repo in $1..." + cd "${directory}" || exit 1 + git stash -q > /dev/null & spinner $! + git pull -q > /dev/null & spinner $! + echo " done!" } 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 - update_repo "${directory}" - else - make_repo "${directory}" "${remoteRepo}" - fi + # 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 + update_repo "${directory}" + else + make_repo "${directory}" "${remoteRepo}" + fi } find_IPv4_information() { - # Find IP used to route to outside world - IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') - IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') - IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') + # Find IP used to route to outside world + IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}') + IPV4_ADDRESS=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}') + IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}') } get_available_interfaces() { - # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. - availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) + # Get available interfaces. Consider only getting UP interfaces in the future, and leaving DOWN interfaces out of list. + availableInterfaces=$(ip -o link | awk '{print $2}' | grep -v "lo" | cut -d':' -f1 | cut -d'@' -f1) } welcomeDialogs() { - # Display the welcome dialog - whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} + # Display the welcome dialog + whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} - # Support for a part-time dev - whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} + # Support for a part-time dev + whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} - # Explain the need for a static address - whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. + # Explain the need for a static address + whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } verifyFreeDiskSpace() { - # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) - # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. - echo "::: Verifying free disk space..." - local required_free_kilobytes=51200 - local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') + # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.) + # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables. + echo "::: Verifying free disk space..." + local required_free_kilobytes=51200 + local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') - # - Unknown free disk space , not a integer - if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then - echo "::: Unknown free disk space!" - echo "::: We were unable to determine available free disk space on this system." - echo "::: You may override this check and force the installation, however, it is not recommended" - echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" - echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" - exit 1 - # - 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 "::: 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'" - echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" - - echo "Insufficient free space, exiting..." - exit 1 - - fi + # - Unknown free disk space , not a integer + if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then + echo "::: Unknown free disk space!" + echo "::: We were unable to determine available free disk space on this system." + echo "::: You may override this check and force the installation, however, it is not recommended" + echo "::: To do so, pass the argument '--i_do_not_follow_recommendations' to the install script" + echo "::: eg. curl -L https://install.pi-hole.net | bash /dev/stdin --i_do_not_follow_recommendations" + exit 1 + # - 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 "::: 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'" + echo "::: After rebooting, run this installation again. (curl -L https://install.pi-hole.net | bash)" + echo "Insufficient free space, exiting..." + exit 1 + fi } chooseInterface() { - # Turn the available interfaces into an array so it can be used with a whiptail dialog - local interfacesArray=() - # Number of available interfaces - local interfaceCount - # Whiptail variable storage - local chooseInterfaceCmd - # Temporary Whiptail options storage - local chooseInterfaceOptions - # Loop sentinel variable - local firstLoop=1 + # Turn the available interfaces into an array so it can be used with a whiptail dialog + local interfacesArray=() + # Number of available interfaces + local interfaceCount + # Whiptail variable storage + local chooseInterfaceCmd + # Temporary Whiptail options storage + local chooseInterfaceOptions + # Loop sentinel variable + local firstLoop=1 - while read -r line; do - mode="OFF" - if [[ ${firstLoop} -eq 1 ]]; then - firstLoop=0 - mode="ON" - fi - interfacesArray+=("${line}" "available" "${mode}") - done <<< "${availableInterfaces}" + while read -r line; do + mode="OFF" + if [[ ${firstLoop} -eq 1 ]]; then + firstLoop=0 + mode="ON" + fi + interfacesArray+=("${line}" "available" "${mode}") + done <<< "${availableInterfaces}" - # Find out how many interfaces are available to choose from - interfaceCount=$(echo "${availableInterfaces}" | wc -l) - chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) - chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]]; then - for desiredInterface in ${chooseInterfaceOptions}; do - PIHOLE_INTERFACE=${desiredInterface} - echo "::: Using interface: $PIHOLE_INTERFACE" - done - else - echo "::: Cancel selected, exiting...." - exit 1 - fi + # Find out how many interfaces are available to choose from + interfaceCount=$(echo "${availableInterfaces}" | wc -l) + chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount}) + chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]]; then + for desiredInterface in ${chooseInterfaceOptions}; do + PIHOLE_INTERFACE=${desiredInterface} + echo "::: Using interface: $PIHOLE_INTERFACE" + done + else + echo "::: Cancel selected, exiting...." + exit 1 + fi } useIPv6dialog() { - # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') - whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} + # Show the IPv6 address used for blocking + IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} } use4andor6() { - local useIPv4 - local useIPv6 - # Let use select IPv4 and/or IPv6 - cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) - options=(IPv4 "Block ads over IPv4" on - IPv6 "Block ads over IPv6" on) - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - for choice in ${choices} - do - case ${choice} in - IPv4 ) useIPv4=true;; - IPv6 ) useIPv6=true;; - esac - done - if [[ ${useIPv4} ]]; then - find_IPv4_information - getStaticIPv4Settings - setStaticIPv4 - fi - if [[ ${useIPv6} ]]; then - useIPv6dialog - fi - echo "::: IPv4 address: ${IPV4_ADDRESS}" - echo "::: IPv6 address: ${IPV6_ADDRESS}" - if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then - echo "::: Cannot continue, neither IPv4 or IPv6 selected" - echo "::: Exiting" - exit 1 - fi - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + local useIPv4 + local useIPv6 + # Let use select IPv4 and/or IPv6 + cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2) + options=(IPv4 "Block ads over IPv4" on + IPv6 "Block ads over IPv6" on) + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + for choice in ${choices} + do + case ${choice} in + IPv4 ) useIPv4=true;; + IPv6 ) useIPv6=true;; + esac + done + if [[ ${useIPv4} ]]; then + find_IPv4_information + getStaticIPv4Settings + setStaticIPv4 + fi + if [[ ${useIPv6} ]]; then + useIPv6dialog + fi + echo "::: IPv4 address: ${IPV4_ADDRESS}" + echo "::: IPv6 address: ${IPV6_ADDRESS}" + if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then + echo "::: Cannot continue, neither IPv4 or IPv6 selected" + echo "::: Exiting" + exit 1 + fi + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } getStaticIPv4Settings() { - # Ask if the user wants to use DHCP settings as their static IP - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. - whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. + # Ask if the user wants to use DHCP settings as their static IP + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. + whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c} - # Nothing else to do since the variables are already set above - else - # Otherwise, we need to ask the user to input their desired settings. - # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) - # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ ${ipSettingsCorrect} = True ]]; do - # Ask for the IPv4 address - IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" - # Ask for the gateway - IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - echo "::: Your static IPv4 gateway: ${IPv4gw}" - # Give the user a chance to review their settings before moving on - if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? - IP address: ${IPV4_ADDRESS} - Gateway: ${IPv4gw}" ${r} ${c}); then - # After that's done, the loop ends and we move on - ipSettingsCorrect=True - else - # If the settings are wrong, the loop continues - ipSettingsCorrect=False - fi - else - # Cancelling gateway settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - else - # Cancelling IPv4 settings window - ipSettingsCorrect=False - echo "::: Cancel selected. Exiting..." - exit 1 - fi - done - # End the if statement for DHCP vs. static - fi + # Nothing else to do since the variables are already set above + else + # Otherwise, we need to ask the user to input their desired settings. + # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) + # Start a loop to let the user enter their information with the chance to go back and edit it if necessary + until [[ ${ipSettingsCorrect} = True ]]; do + # Ask for the IPv4 address + IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 address: ${IPV4_ADDRESS}" + # Ask for the gateway + IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) + if [[ $? = 0 ]]; then + echo "::: Your static IPv4 gateway: ${IPv4gw}" + # Give the user a chance to review their settings before moving on + if (whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? + IP address: ${IPV4_ADDRESS} + Gateway: ${IPv4gw}" ${r} ${c}); then + # After that's done, the loop ends and we move on + ipSettingsCorrect=True + else + # If the settings are wrong, the loop continues + ipSettingsCorrect=False + fi + else + # Cancelling gateway settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + else + # Cancelling IPv4 settings window + ipSettingsCorrect=False + echo "::: Cancel selected. Exiting..." + exit 1 + fi + done + # End the if statement for DHCP vs. static + fi } setDHCPCD() { - # Append these lines to dhcpcd.conf to enable a static IP - echo "## interface ${PIHOLE_INTERFACE} - static ip_address=${IPV4_ADDRESS} - static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null + # Append these lines to dhcpcd.conf to enable a static IP + echo "## interface ${PIHOLE_INTERFACE} + static ip_address=${IPV4_ADDRESS} + static routers=${IPv4gw} + static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { - local IFCFG_FILE - local IPADDR - local CIDR - if [[ -f /etc/dhcpcd.conf ]]; then - # Debian Family - if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then - echo "::: Static IP already configured" - else - setDHCPCD - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then - # Fedora Family - IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} - if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then - echo "::: Static IP already configured" - else - IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) - CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) - # Backup existing interface configuration: - cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig - # Build Interface configuration file: - { - echo "# Configured via Pi-Hole installer" - echo "DEVICE=$PIHOLE_INTERFACE" - echo "BOOTPROTO=none" - echo "ONBOOT=yes" - echo "IPADDR=$IPADDR" - echo "PREFIX=$CIDR" - echo "GATEWAY=$IPv4gw" - echo "DNS1=$PIHOLE_DNS_1" - echo "DNS2=$PIHOLE_DNS_2" - echo "USERCTL=no" - }> "${IFCFG_FILE}" - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - if [ -x "$(command -v nmcli)" ];then - # Tell NetworkManager to read our new sysconfig file - nmcli con load "${IFCFG_FILE}" > /dev/null - fi - echo ":::" - echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." - echo ":::" - fi - else - echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" - exit 1 - fi + local IFCFG_FILE + local IPADDR + local CIDR + if [[ -f /etc/dhcpcd.conf ]]; then + # Debian Family + if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then + echo "::: Static IP already configured" + else + setDHCPCD + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then + # Fedora Family + IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} + if grep -q "${IPV4_ADDRESS}" "${IFCFG_FILE}"; then + echo "::: Static IP already configured" + else + IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) + CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) + # Backup existing interface configuration: + cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig + # Build Interface configuration file: + { + echo "# Configured via Pi-Hole installer" + echo "DEVICE=$PIHOLE_INTERFACE" + echo "BOOTPROTO=none" + echo "ONBOOT=yes" + echo "IPADDR=$IPADDR" + echo "PREFIX=$CIDR" + echo "GATEWAY=$IPv4gw" + echo "DNS1=$PIHOLE_DNS_1" + echo "DNS2=$PIHOLE_DNS_2" + echo "USERCTL=no" + }> "${IFCFG_FILE}" + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + if [ -x "$(command -v nmcli)" ];then + # Tell NetworkManager to read our new sysconfig file + nmcli con load "${IFCFG_FILE}" > /dev/null + fi + echo ":::" + echo "::: Setting IP to ${IPV4_ADDRESS}. You may need to restart after the install is complete." + echo ":::" + fi + else + echo "::: Warning: Unable to locate configuration file to set static IPv4 address!" + exit 1 + fi } valid_ip() { - local ip=${1} - local stat=1 + local ip=${1} + local stat=1 - if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then - OIFS=$IFS - IFS='.' - ip=(${ip}) - IFS=${OIFS} - [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ - && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] - stat=$? - fi - return ${stat} + if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=(${ip}) + IFS=${OIFS} + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return ${stat} } setDNS() { - DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) - DNSChooseOptions=(Google "" on - OpenDNS "" off - Level3 "" off - Norton "" off - Comodo "" off - Custom "" off) - DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) - if [[ $? = 0 ]];then - case ${DNSchoices} in - Google) - echo "::: Using Google DNS servers." - PIHOLE_DNS_1="8.8.8.8" - PIHOLE_DNS_2="8.8.4.4" - ;; - OpenDNS) - echo "::: Using OpenDNS servers." - PIHOLE_DNS_1="208.67.222.222" - PIHOLE_DNS_2="208.67.220.220" - ;; - Level3) - echo "::: Using Level3 servers." - PIHOLE_DNS_1="4.2.2.1" - PIHOLE_DNS_2="4.2.2.2" - ;; - Norton) - echo "::: Using Norton ConnectSafe servers." - PIHOLE_DNS_1="199.85.126.10" - PIHOLE_DNS_2="199.85.127.10" - ;; - Comodo) - echo "::: Using Comodo Secure servers." - PIHOLE_DNS_1="8.26.56.26" - PIHOLE_DNS_2="8.20.247.20" - ;; - Custom) - until [[ ${DNSSettingsCorrect} = True ]]; do - strInvalid="Invalid" - if [ ! ${PIHOLE_DNS_1} ]; then - if [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="" - else - prePopulate=", ${PIHOLE_DNS_2}" - fi - elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}" - elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then - prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" - fi + DNSChooseCmd=(whiptail --separate-output --radiolist "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6) + DNSChooseOptions=(Google "" on + OpenDNS "" off + Level3 "" off + Norton "" off + Comodo "" off + Custom "" off) + DNSchoices=$("${DNSChooseCmd[@]}" "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) + if [[ $? = 0 ]];then + case ${DNSchoices} in + Google) + echo "::: Using Google DNS servers." + PIHOLE_DNS_1="8.8.8.8" + PIHOLE_DNS_2="8.8.4.4" + ;; + OpenDNS) + echo "::: Using OpenDNS servers." + PIHOLE_DNS_1="208.67.222.222" + PIHOLE_DNS_2="208.67.220.220" + ;; + Level3) + echo "::: Using Level3 servers." + PIHOLE_DNS_1="4.2.2.1" + PIHOLE_DNS_2="4.2.2.2" + ;; + Norton) + echo "::: Using Norton ConnectSafe servers." + PIHOLE_DNS_1="199.85.126.10" + PIHOLE_DNS_2="199.85.127.10" + ;; + Comodo) + echo "::: Using Comodo Secure servers." + PIHOLE_DNS_1="8.26.56.26" + PIHOLE_DNS_2="8.20.247.20" + ;; + Custom) + until [[ ${DNSSettingsCorrect} = True ]]; do + strInvalid="Invalid" + if [ ! ${PIHOLE_DNS_1} ]; then + if [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="" + else + prePopulate=", ${PIHOLE_DNS_2}" + fi + elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}" + elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then + prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" + fi - piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) + piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) - if [[ $? = 0 ]]; then - PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') - PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') - if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then - PIHOLE_DNS_1=${strInvalid} - fi - if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then - PIHOLE_DNS_2=${strInvalid} - fi - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then - PIHOLE_DNS_1="" - fi - if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then - PIHOLE_DNS_2="" - fi - DNSSettingsCorrect=False - else - if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then - DNSSettingsCorrect=True - else - # If the settings are wrong, the loop continues - DNSSettingsCorrect=False - fi - fi - done - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + if [[ $? = 0 ]]; then + PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') + PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') + if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then + PIHOLE_DNS_1=${strInvalid} + fi + if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then + PIHOLE_DNS_2=${strInvalid} + fi + else + echo "::: Cancel selected, exiting...." + exit 1 + fi + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} + if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then + PIHOLE_DNS_1="" + fi + if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + PIHOLE_DNS_2="" + fi + DNSSettingsCorrect=False + else + if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then + DNSSettingsCorrect=True + else + # If the settings are wrong, the loop continues + DNSSettingsCorrect=False + fi + fi + done + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } setLogging() { @@ -552,69 +550,69 @@ setLogging() { local LogChooseOptions local LogChoices - LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) - LogChooseOptions=("On (Reccomended)" "" on - Off "" off) - LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) - case ${LogChoices} in - "On (Recommended)") - echo "::: Logging On." - QUERY_LOGGING=true - ;; - Off) - echo "::: Logging Off." - QUERY_LOGGING=false - ;; - esac + LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) + LogChooseOptions=("On (Reccomended)" "" on + Off "" off) + LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo "::: Cancel selected. Exiting..." && exit 1) + case ${LogChoices} in + "On (Recommended)") + echo "::: Logging On." + QUERY_LOGGING=true + ;; + Off) + echo "::: Logging Off." + QUERY_LOGGING=false + ;; + esac } version_check_dnsmasq() { - # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory - 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_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" + # Check if /etc/dnsmasq.conf is from pihole. If so replace with an original and install new in .d directory + 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_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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." - mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} - echo " done." - echo -n "::: Restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - else - echo " it is not a pi-hole file, leaving alone!" - fi - else - echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." - cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo " done." - fi + 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 -n "::: Backing up dnsmasq.conf to dnsmasq.conf.orig..." + mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} + echo " done." + echo -n "::: Restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + else + echo " it is not a pi-hole file, leaving alone!" + fi + else + echo -n "::: No dnsmasq.conf found.. restoring default dnsmasq.conf..." + cp ${dnsmasq_original_config} ${dnsmasq_conf} + echo " done." + fi - echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." - cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} - echo " done." - sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} - if [[ "${PIHOLE_DNS_1}" != "" ]]; then - sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} - fi - if [[ "${PIHOLE_DNS_2}" != "" ]]; then - sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} - else - sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} - fi + echo -n "::: Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." + cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} + echo " done." + sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} + if [[ "${PIHOLE_DNS_1}" != "" ]]; then + sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location} + fi + if [[ "${PIHOLE_DNS_2}" != "" ]]; then + sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location} + else + sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location} + fi - sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} + sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf} - if [[ "${QUERY_LOGGING}" == false ]] ; then + if [[ "${QUERY_LOGGING}" == false ]] ; then #Disable Logging sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location} else @@ -624,13 +622,13 @@ version_check_dnsmasq() { } remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done + #Tidy up /usr/local/bin directory if installing over previous install. + oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) + for i in "${oldFiles[@]}"; do + if [ -f "/usr/local/bin/$i.sh" ]; then + rm /usr/local/bin/"$i".sh + fi + done } clean_existing() { @@ -639,21 +637,20 @@ clean_existing() { local clean_directory="${1}" local old_files=${2} - for script in "${old_files[@]}"; do - rm -f "${clean_directory}${script}.sh" - done - + for script in "${old_files[@]}"; do + rm -f "${clean_directory}${script}.sh" + done } installScripts() { # Install the scripts from repository to their various locations readonly install_dir="/opt/pihole/" - echo ":::" - echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." + echo ":::" + echo -n "::: Installing scripts from ${PI_HOLE_LOCAL_REPO}..." - # Clear out script files from Pi-hole scripts directory. - clean_existing "${install_dir}" "${PI_HOLE_FILES}" + # Clear out script files from Pi-hole scripts directory. + clean_existing "${install_dir}" "${PI_HOLE_FILES}" # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then @@ -663,7 +660,7 @@ installScripts() { install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole - echo " done." + echo " done." else echo " *** ERROR: Local repo ${core_repo} not found, exiting." exit 1 @@ -671,258 +668,258 @@ installScripts() { } installConfigs() { - # Install the configs from /etc/.pihole to their various locations - echo ":::" - echo "::: Installing configs..." - version_check_dnsmasq - if [ ! -d "/etc/lighttpd" ]; then - mkdir /etc/lighttpd - chown "${USER}":root /etc/lighttpd - 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 - mkdir -p /var/run/lighttpd - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd - mkdir -p /var/cache/lighttpd/compress - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress - mkdir -p /var/cache/lighttpd/uploads - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads + # Install the configs from /etc/.pihole to their various locations + echo ":::" + echo "::: Installing configs..." + version_check_dnsmasq + if [ ! -d "/etc/lighttpd" ]; then + mkdir /etc/lighttpd + chown "${USER}":root /etc/lighttpd + 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 + mkdir -p /var/run/lighttpd + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd + mkdir -p /var/cache/lighttpd/compress + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress + mkdir -p /var/cache/lighttpd/uploads + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads } stop_service() { - # Stop service passed in as argument. - # Can softfail, as process may not be installed when this is called - echo ":::" - echo -n "::: Stopping ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl stop "${1}" &> /dev/null & spinner $! || true - else - service "${1}" stop &> /dev/null & spinner $! || true - fi - echo " done." + # Stop service passed in as argument. + # Can softfail, as process may not be installed when this is called + echo ":::" + echo -n "::: Stopping ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl stop "${1}" &> /dev/null & spinner $! || true + else + service "${1}" stop &> /dev/null & spinner $! || true + fi + echo " done." } start_service() { - # Start/Restart service passed in as argument - # This should not fail, it's an error if it does - echo ":::" - echo -n "::: Starting ${1} service..." - if [ -x "$(command -v systemctl)" ]; then - systemctl restart "${1}" &> /dev/null & spinner $! - else - service "${1}" restart &> /dev/null & spinner $! - fi - echo " done." + # Start/Restart service passed in as argument + # This should not fail, it's an error if it does + echo ":::" + echo -n "::: Starting ${1} service..." + if [ -x "$(command -v systemctl)" ]; then + systemctl restart "${1}" &> /dev/null & spinner $! + else + service "${1}" restart &> /dev/null & spinner $! + fi + echo " done." } enable_service() { - # Enable service so that it will start with next reboot - echo ":::" - echo -n "::: Enabling ${1} service to start on reboot..." - if [ -x "$(command -v systemctl)" ]; then - systemctl enable "${1}" &> /dev/null & spinner $! - else - update-rc.d "${1}" defaults &> /dev/null & spinner $! - fi - echo " done." + # Enable service so that it will start with next reboot + echo ":::" + echo -n "::: Enabling ${1} service to start on reboot..." + if [ -x "$(command -v systemctl)" ]; then + systemctl enable "${1}" &> /dev/null & spinner $! + else + update-rc.d "${1}" defaults &> /dev/null & spinner $! + fi + echo " done." } update_pacakge_cache() { - #Running apt-get update/upgrade with minimal output can cause some issues with - #requiring user input (e.g password for phpmyadmin see #218) + #Running apt-get update/upgrade with minimal output can cause some issues with + #requiring user input (e.g password for phpmyadmin see #218) - #Check to see if apt-get update has already been run today - #it needs to have been run at least once on new installs! - timestamp=$(stat -c %Y ${PKG_CACHE}) - timestampAsDate=$(date -d @"${timestamp}" "+%b %e") - today=$(date "+%b %e") + #Check to see if apt-get update has already been run today + #it needs to have been run at least once on new installs! + timestamp=$(stat -c %Y ${PKG_CACHE}) + timestampAsDate=$(date -d @"${timestamp}" "+%b %e") + today=$(date "+%b %e") - if [ ! "${today}" == "${timestampAsDate}" ]; then - #update package lists - echo ":::" - echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." - ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! - echo " done!" - fi + if [ ! "${today}" == "${timestampAsDate}" ]; then + #update package lists + echo ":::" + echo -n "::: ${PKG_MANAGER} update has not been run today. Running now..." + ${UPDATE_PKG_CACHE} &> /dev/null & spinner $! + echo " done!" + fi } notify_package_updates_available() { # Let user know if they have outdated packages on their system and # advise them to run a package update at soonest possible. - echo ":::" - echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." - updatesToInstall=$(eval "${PKG_COUNT}") - echo " done!" - echo ":::" - if [[ ${updatesToInstall} -eq "0" ]]; then - 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 run '${PKG_UPDATE}' after installing Pi-Hole! " - echo ":::" - fi + echo ":::" + echo -n "::: Checking ${PKG_MANAGER} for upgraded packages...." + updatesToInstall=$(eval "${PKG_COUNT}") + echo " done!" + echo ":::" + if [[ ${updatesToInstall} -eq "0" ]]; then + 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 run '${PKG_UPDATE}' after installing Pi-Hole! " + echo ":::" + fi } install_dependent_packages() { - # Install packages passed in via argument array - # No spinner - conflicts with set -e - declare -a argArray1=("${!1}") + # Install packages passed in via argument array + # No spinner - conflicts with set -e + declare -a argArray1=("${!1}") - for i in "${argArray1[@]}"; do - echo -n "::: Checking for $i..." - package_check_install "${i}" &> /dev/null - echo " installed!" - done + for i in "${argArray1[@]}"; do + echo -n "::: Checking for $i..." + package_check_install "${i}" &> /dev/null + echo " installed!" + done } CreateLogFile() { - # Create logfiles if necessary - echo ":::" - echo -n "::: Creating log file and changing owner to dnsmasq..." - if [ ! -f /var/log/pihole.log ]; then - touch /var/log/pihole.log - chmod 644 /var/log/pihole.log - chown "${DNSMASQ_USER}":root /var/log/pihole.log - echo " done!" - else - echo " already exists!" - fi + # Create logfiles if necessary + echo ":::" + echo -n "::: Creating log file and changing owner to dnsmasq..." + if [ ! -f /var/log/pihole.log ]; then + touch /var/log/pihole.log + chmod 644 /var/log/pihole.log + chown "${DNSMASQ_USER}":root /var/log/pihole.log + echo " done!" + else + echo " already exists!" + fi } installPiholeWeb() { - # Install the web interface - echo ":::" - echo "::: Installing pihole custom index page..." - if [ -d "/var/www/html/pihole" ]; then - if [ -f "/var/www/html/pihole/index.html" ]; then - echo "::: Existing index.html detected, not overwriting" - else - echo -n "::: index.html missing, replacing... " - cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ - echo " done!" - fi + # Install the web interface + echo ":::" + echo "::: Installing pihole custom index page..." + if [ -d "/var/www/html/pihole" ]; then + if [ -f "/var/www/html/pihole/index.html" ]; then + echo "::: Existing index.html detected, not overwriting" + else + echo -n "::: index.html missing, replacing... " + cp /etc/.pihole/advanced/index.html /var/www/html/pihole/ + echo " done!" + fi - if [ -f "/var/www/html/pihole/index.js" ]; then - echo "::: Existing index.js detected, not overwriting" - else - echo -n "::: index.js missing, replacing... " - cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ - echo " done!" - fi + if [ -f "/var/www/html/pihole/index.js" ]; then + echo "::: Existing index.js detected, not overwriting" + else + echo -n "::: index.js missing, replacing... " + cp /etc/.pihole/advanced/index.js /var/www/html/pihole/ + echo " done!" + fi - else - mkdir /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..." - mkdir -p /etc/sudoers.d/ - cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole - chmod 0440 /etc/sudoers.d/pihole - echo " done!" + else + mkdir /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..." + mkdir -p /etc/sudoers.d/ + cp /etc/.pihole/advanced/pihole.sudo /etc/sudoers.d/pihole + chmod 0440 /etc/sudoers.d/pihole + echo " done!" } installCron() { - # Install the cron job - echo ":::" - echo -n "::: Installing latest Cron script..." - cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole - echo " done!" + # Install the cron job + echo ":::" + echo -n "::: Installing latest Cron script..." + cp /etc/.pihole/advanced/pihole.cron /etc/cron.d/pihole + echo " done!" } runGravity() { - # Run gravity.sh to build blacklists - echo ":::" - echo "::: Preparing to run gravity.sh to refresh hosts..." - if ls /etc/pihole/list* 1> /dev/null 2>&1; then - echo "::: Cleaning up previous install (preserving whitelist/blacklist)" - rm /etc/pihole/list.* - fi - echo "::: Running gravity.sh" - /opt/pihole/gravity.sh + # Run gravity.sh to build blacklists + echo ":::" + echo "::: Preparing to run gravity.sh to refresh hosts..." + if ls /etc/pihole/list* 1> /dev/null 2>&1; then + echo "::: Cleaning up previous install (preserving whitelist/blacklist)" + rm /etc/pihole/list.* + fi + echo "::: Running gravity.sh" + /opt/pihole/gravity.sh } create_pihole_user() { - # Check if user pihole exists and create if not - echo "::: Checking if user 'pihole' exists..." - id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) + # Check if user pihole exists and create if not + echo "::: Checking if user 'pihole' exists..." + id -u pihole &> /dev/null && echo "::: User 'pihole' already exists" || (echo "::: User 'pihole' doesn't exist. Creating..." && useradd -r -s /usr/sbin/nologin pihole) } configureFirewall() { - # Allow HTTP and DNS traffic - if [ -x "$(command -v firewall-cmd)" ]; then - firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ - && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" - elif [ -x "$(command -v iptables)" ]; then - echo "::: Configuring iptables for httpd and dnsmasq.." - iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT - iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT - iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT - else - echo "::: No firewall detected.. skipping firewall configuration." - fi + # Allow HTTP and DNS traffic + if [ -x "$(command -v firewall-cmd)" ]; then + firewall-cmd --state &> /dev/null && ( echo "::: Configuring firewalld for httpd and dnsmasq.." && firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --permanent --add-port=53/tcp \ + && firewall-cmd --permanent --add-port=53/udp && firewall-cmd --reload) || echo "::: FirewallD not enabled" + elif [ -x "$(command -v iptables)" ]; then + echo "::: Configuring iptables for httpd and dnsmasq.." + iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT + iptables -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT + iptables -A INPUT -p udp -m udp --dport 53 -j ACCEPT + else + echo "::: No firewall detected.. skipping firewall configuration." + fi } finalExports() { - # Update variables in setupVars.conf file - if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" - fi + # Update variables in setupVars.conf file + if [ -e "${setupVars}" ]; then + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;' "${setupVars}" + fi { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" + echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" + echo "IPV4_ADDRESS=${IPV4_ADDRESS}" + echo "IPV6_ADDRESS=${IPV6_ADDRESS}" + echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" + echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" + echo "QUERY_LOGGING=${QUERY_LOGGING}" }>> "${setupVars}" } installPihole() { - # Install base files and web interface - create_pihole_user - if [ ! -d "/var/www/html" ]; then - mkdir -p /var/www/html - fi - chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html - chmod 775 /var/www/html - usermod -a -G ${LIGHTTPD_GROUP} pihole - if [ -x "$(command -v lighty-enable-mod)" ]; then - lighty-enable-mod fastcgi fastcgi-php > /dev/null || true - else - printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" - fi - installScripts - installConfigs - CreateLogFile - configureSelinux - installPiholeWeb - installCron - configureFirewall - finalExports - runGravity + # Install base files and web interface + create_pihole_user + if [ ! -d "/var/www/html" ]; then + mkdir -p /var/www/html + fi + chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html + chmod 775 /var/www/html + usermod -a -G ${LIGHTTPD_GROUP} pihole + if [ -x "$(command -v lighty-enable-mod)" ]; then + lighty-enable-mod fastcgi fastcgi-php > /dev/null || true + else + printf "\n:::\tWarning: 'lighty-enable-mod' utility not found. Please ensure fastcgi is enabled if you experience issues.\n" + fi + installScripts + installConfigs + CreateLogFile + configureSelinux + installPiholeWeb + installCron + configureFirewall + finalExports + runGravity } accountForRefactor() { - # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. + # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. - # Refactoring of install script has changed the name of a couple of variables. Sort them out here. + # Refactoring of install script has changed the name of a couple of variables. Sort them out here. - sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} - sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} - sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} - sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} - sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} + sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars} + sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars} + sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars} + sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars} + sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars} } @@ -943,29 +940,29 @@ updatePihole() { } configureSelinux() { - if [ -x "$(command -v getenforce)" ]; then - printf "\n::: SELinux Detected\n" - printf ":::\tChecking for SELinux policy development packages..." - package_check_install "selinux-policy-devel" > /dev/null - echo " installed!" - printf ":::\tEnabling httpd server side includes (SSI).. " - setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" - printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" - if ! [ -x "$(command -v systemctl)" ]; then - sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te - fi - checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te - semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod - semodule -i /etc/pihole/pihole.pp - rm -f /etc/pihole/pihole.mod - semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." - fi + if [ -x "$(command -v getenforce)" ]; then + printf "\n::: SELinux Detected\n" + printf ":::\tChecking for SELinux policy development packages..." + package_check_install "selinux-policy-devel" > /dev/null + echo " installed!" + printf ":::\tEnabling httpd server side includes (SSI).. " + setsebool -P httpd_ssi_exec on &> /dev/null && echo "Success" || echo "SELinux not enabled" + printf "\n:::\tCompiling Pi-Hole SELinux policy..\n" + if ! [ -x "$(command -v systemctl)" ]; then + sed -i.bak '/systemd/d' /etc/.pihole/advanced/selinux/pihole.te + fi + checkmodule -M -m -o /etc/pihole/pihole.mod /etc/.pihole/advanced/selinux/pihole.te + semodule_package -o /etc/pihole/pihole.pp -m /etc/pihole/pihole.mod + semodule -i /etc/pihole/pihole.pp + rm -f /etc/pihole/pihole.mod + semodule -l | grep pihole &> /dev/null && echo "::: Installed Pi-Hole SELinux policy" || echo "::: Warning: Pi-Hole SELinux policy did not install." + fi } displayFinalMessage() { - if (( ${#1} > 0 )) ; then - # Final completion message to user - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + if (( ${#1} > 0 )) ; then + # Final completion message to user + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} @@ -975,8 +972,8 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin The currently set password is ${1}" ${r} ${c} - else - whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: + else + whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: IPv4: ${IPV4_ADDRESS%/*} IPv6: ${IPV6_ADDRESS} @@ -985,170 +982,169 @@ If you set a new IP address, you should restart the Pi. The install log is in /etc/pihole. View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" ${r} ${c} - fi + fi } update_dialogs() { - # reconfigure - if [ "${reconfigure}" = true ]; then - opt1a="Repair" - opt1b="This will retain existing settings" - strAdd="You will remain on the same version" - else - opt1a="Update" - opt1b="This will retain existing settings." - strAdd="You will be updated to the latest version." - fi - opt2a="Reconfigure" - opt2b="This will allow you to enter new settings" + # reconfigure + if [ "${reconfigure}" = true ]; then + opt1a="Repair" + opt1b="This will retain existing settings" + strAdd="You will remain on the same version" + else + opt1a="Update" + opt1b="This will retain existing settings." + strAdd="You will be updated to the latest version." + fi + opt2a="Reconfigure" + opt2b="This will allow you to enter new settings" - UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ - "${opt1a}" "${opt1b}" \ - "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) - - if [[ $? = 0 ]];then - case ${UpdateCmd} in - ${opt1a}) - echo "::: ${opt1a} option selected." - useUpdateVars=true - ;; - ${opt2a}) - echo "::: ${opt2a} option selected" - useUpdateVars=false - ;; - esac - else - echo "::: Cancel selected. Exiting..." - exit 1 - fi + UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ + "${opt1a}" "${opt1b}" \ + "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) + if [[ $? = 0 ]];then + case ${UpdateCmd} in + ${opt1a}) + echo "::: ${opt1a} option selected." + useUpdateVars=true + ;; + ${opt2a}) + echo "::: ${opt2a} option selected" + useUpdateVars=false + ;; + esac + else + echo "::: Cancel selected. Exiting..." + exit 1 + fi } main() { # Check arguments for the undocumented flags - for var in "$@"; do - case "$var" in - "--reconfigure" ) reconfigure=true;; - "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; - "--unattended" ) runUnattended=true;; - esac - done + for var in "$@"; do + case "$var" in + "--reconfigure" ) reconfigure=true;; + "--i_do_not_follow_recommendations" ) skipSpaceCheck=false;; + "--unattended" ) runUnattended=true;; + esac + done - if [[ -f ${setupVars} ]]; then - if [[ "${runUnattended}" == true ]]; then - echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" - useUpdateVars=true - else - update_dialogs - fi - fi + if [[ -f ${setupVars} ]]; then + if [[ "${runUnattended}" == true ]]; then + echo "::: --unattended passed to install script, no whiptail dialogs will be displayed" + useUpdateVars=true + else + update_dialogs + fi + fi - # Start the installer - # Verify there is enough disk space for the install - if [[ "${skipSpaceCheck}" == true ]]; then - echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" - else - verifyFreeDiskSpace - fi + # Start the installer + # Verify there is enough disk space for the install + if [[ "${skipSpaceCheck}" == true ]]; then + echo "::: --i_do_not_follow_recommendations passed to script, skipping free disk space verification!" + else + verifyFreeDiskSpace + fi - # Update package cache - update_pacakge_cache + # Update package cache + update_pacakge_cache - # Notify user of package availability - notify_package_updates_available + # Notify user of package availability + notify_package_updates_available - # Install packages used by this installation script - install_dependent_packages INSTALLER_DEPS[@] + # Install packages used by this installation script + install_dependent_packages INSTALLER_DEPS[@] - if [[ "${reconfigure}" == true ]]; then - echo "::: --reconfigure passed to install script. Not downloading/updating local repos" - else - # Get Git files for Core and Admin - getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} - getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} - fi + if [[ "${reconfigure}" == true ]]; then + echo "::: --reconfigure passed to install script. Not downloading/updating local repos" + else + # Get Git files for Core and Admin + getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} + getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} + fi - if [[ ${useUpdateVars} == false ]]; then - # Display welcome dialogs - welcomeDialogs - # Create directory for Pi-hole storage - mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts - # Stop resolver and webserver while installing proceses - stop_service dnsmasq - stop_service lighttpd - # Determine available interfaces - get_available_interfaces - # Find interfaces and let the user choose one - chooseInterface - # Let the user decide if they want to block ads over IPv4 and/or IPv6 - use4andor6 - # Decide what upstream DNS Servers to use - setDNS - # Let the user decide if they want query logging enabled... - setLogging + if [[ ${useUpdateVars} == false ]]; then + # Display welcome dialogs + welcomeDialogs + # Create directory for Pi-hole storage + mkdir -p /etc/pihole/ + # Remove legacy scripts from previous storage location + remove_legacy_scripts + # Stop resolver and webserver while installing proceses + stop_service dnsmasq + stop_service lighttpd + # Determine available interfaces + get_available_interfaces + # Find interfaces and let the user choose one + chooseInterface + # Let the user decide if they want to block ads over IPv4 and/or IPv6 + use4andor6 + # Decide what upstream DNS Servers to use + setDNS + # Let the user decide if they want query logging enabled... + setLogging - # Install packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] + # Install packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] - # Install and log everything to a file + # Install and log everything to a file installPihole | tee ${tmpLog} - else - # update packages used by the Pi-hole - install_dependent_packages PIHOLE_DEPS[@] + else + # update packages used by the Pi-hole + install_dependent_packages PIHOLE_DEPS[@] - updatePihole | tee ${tmpLog} - fi + updatePihole | tee ${tmpLog} + fi - # Move the log file into /etc/pihole for storage - mv ${tmpLog} ${instalLogLoc} + # Move the log file into /etc/pihole for storage + mv ${tmpLog} ${instalLogLoc} - # Add password to web UI if there is none - 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) - pihole -a -p ${pw} - fi + # Add password to web UI if there is none + 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) + pihole -a -p ${pw} + fi - if [[ "${useUpdateVars}" == false ]]; then - displayFinalMessage ${pw} - fi + if [[ "${useUpdateVars}" == false ]]; then + displayFinalMessage ${pw} + fi - echo "::: Restarting services..." - # Start services - start_service dnsmasq - enable_service dnsmasq - start_service lighttpd - enable_service lighttpd - echo "::: done." + echo "::: Restarting services..." + # Start services + start_service dnsmasq + enable_service dnsmasq + start_service lighttpd + enable_service lighttpd + echo "::: done." - echo ":::" - if [[ "${useUpdateVars}" == false ]]; then - echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" - echo "::: ${IPV4_ADDRESS%/*}" - echo "::: ${IPV6_ADDRESS}" - echo ":::" - echo "::: If you set a new IP address, you should restart the Pi." + echo ":::" + if [[ "${useUpdateVars}" == false ]]; then + echo "::: Installation Complete! Configure your devices to use the Pi-hole as their DNS server using:" + echo "::: ${IPV4_ADDRESS%/*}" + echo "::: ${IPV6_ADDRESS}" + echo ":::" + echo "::: If you set a new IP address, you should restart the Pi." echo "::: View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" - else - echo "::: Update complete!" - fi + else + echo "::: Update complete!" + fi - if (( ${#pw} > 0 )) ; then - echo ":::" - echo "::: Note: As security measure a password has been installed for your web interface" - echo "::: The currently set password is" - echo "::: ${pw}" - echo ":::" - echo "::: You can always change it using" - echo "::: pihole -a -p new_password" - fi + if (( ${#pw} > 0 )) ; then + echo ":::" + echo "::: Note: As security measure a password has been installed for your web interface" + echo "::: The currently set password is" + echo "::: ${pw}" + echo ":::" + echo "::: You can always change it using" + echo "::: pihole -a -p new_password" + fi - echo ":::" - echo "::: The install log is located at: /etc/pihole/install.log" + echo ":::" + echo "::: The install log is located at: /etc/pihole/install.log" } if [[ "${PH_TEST}" != true ]] ; then - main "$@" + main "$@" fi From 8117ec8e20fe7253009b87922cdbc9d9e50a5ddc Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Tue, 20 Dec 2016 17:50:44 -0800 Subject: [PATCH 55/60] Remove v1 legacy detection code. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a04cac3f..77d28651 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -62,7 +62,7 @@ else echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." - if [ -x "$(command -v sudo)" ]; then + if command -v sudo; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? @@ -74,7 +74,7 @@ fi # Compatibility -if [[ $(command -v apt-get) ]]; then +if command -v apt-get; then #Debian Family ############################################# PKG_MANAGER="apt-get" @@ -101,9 +101,9 @@ if [[ $(command -v apt-get) ]]; then package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } -elif [ $(command -v rpm) ]; then +elif command -v rpm; then # Fedora Family - if [ $(command -v dnf) ]; then + if command -v dnf; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" @@ -623,16 +623,6 @@ version_check_dnsmasq() { fi } -remove_legacy_scripts() { - #Tidy up /usr/local/bin directory if installing over previous install. - oldFiles=( gravity chronometer whitelist blacklist piholeLogFlush updateDashboard uninstall setupLCD piholeDebug) - for i in "${oldFiles[@]}"; do - if [ -f "/usr/local/bin/$i.sh" ]; then - rm /usr/local/bin/"$i".sh - fi - done -} - clean_existing() { # Clean an exiting installation to prepare for upgrade/reinstall # ${1} Directory to clean; ${2} Array of files to remove From 58261098fbb2847eb7e08f4610c998e165b4e9f1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 21 Dec 2016 12:07:44 +0100 Subject: [PATCH 56/60] We should not call a function that Dan deleted --- automated install/basic-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0c47aacd..cddc4dbf 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1059,8 +1059,6 @@ main() { welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ - # Remove legacy scripts from previous storage location - remove_legacy_scripts # Stop resolver and webserver while installing proceses stop_service dnsmasq stop_service lighttpd From 8841bdd2523be3774b245064173979c12d436df7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 21 Dec 2016 12:57:02 +0100 Subject: [PATCH 57/60] Reset the if's --- automated install/basic-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index cddc4dbf..fb8570e0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -62,7 +62,7 @@ else echo ":::" echo "::: Detecting the presence of the sudo utility for continuation of this install..." - if command -v sudo; then + if [ -x "$(command -v sudo)" ]; then echo "::: Utility sudo located." exec curl -sSL https://install.pi-hole.net | sudo bash "$@" exit $? @@ -74,7 +74,7 @@ fi # Compatibility -if command -v apt-get; then +if [[ $(command -v apt-get) ]]; then #Debian Family ############################################# PKG_MANAGER="apt-get" @@ -101,9 +101,9 @@ if command -v apt-get; then package_check_install() { dpkg-query -W -f='${Status}' "${1}" 2>/dev/null | grep -c "ok installed" || ${PKG_INSTALL} "${1}" } -elif command -v rpm; then +elif [ $(command -v rpm) ]; then # Fedora Family - if command -v dnf; then + if [ $(command -v dnf) ]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" From 4244f716e0c7b029f4fe086135bad39d22297b00 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Wed, 21 Dec 2016 22:59:14 -0800 Subject: [PATCH 58/60] Create /opt/pihole before trying to install into it. Signed-off-by: Dan Schaper --- automated install/basic-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3dacdfff..b03b98d0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -628,6 +628,7 @@ installScripts() { # Install files from local core repository if is_repo "${PI_HOLE_LOCAL_REPO}"; then cd "${PI_HOLE_LOCAL_REPO}" + install -o "${USER}" -Dm755 -d /opt/pihole install -o "${USER}" -Dm755 -t /opt/pihole/ gravity.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./advanced/Scripts/*.sh install -o "${USER}" -Dm755 -t /opt/pihole/ ./automated\ install/uninstall.sh From c9c28cb59afe904887a15412fe3a793466852aeb Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 03:33:32 -0600 Subject: [PATCH 59/60] replace 'git -C' with long version The -C argument was introduced in git 1.8.4. CentOS 7.3 at the time of this commit provides v1.8.3.1. see: https://git.kaarsemaker.net/git/commit/44e1e4d67d5148c245db362cc48c3cc6c2ec82ca/ Fixes #1004 --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3dacdfff..9b19c19f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -135,7 +135,7 @@ fi is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - git -C "${directory}" status --short &> /dev/null + (cd ${directory} && git status --short) &> /dev/null return } From cbf84c1840fecce74b8554058aa32e9b8b4dfd18 Mon Sep 17 00:00:00 2001 From: bcambl Date: Thu, 22 Dec 2016 04:32:42 -0600 Subject: [PATCH 60/60] replace subshell with command chain --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 9b19c19f..bb2de017 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -135,8 +135,8 @@ fi is_repo() { # Use git to check if directory is currently under VCS, return the value local directory="${1}" - (cd ${directory} && git status --short) &> /dev/null - return + curdir=$PWD; cd $directory; git status --short &> /dev/null; rc=$?; cd $curdir + return $rc } make_repo() {