Merge pull request #1158 from pi-hole/development

[RELEASE] Pi-hole Core 2.12
pull/1203/head v2.12
Mcat12 7 years ago committed by GitHub
commit 8cb4304c13

@ -42,7 +42,7 @@ _If you wish to read over the script before running it, run `nano basic-install.
```
git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd Pi-hole/automated_installer/
cd Pi-hole/automated\ install/
bash basic-install.sh
```
@ -55,17 +55,11 @@ bash basic-install.sh
Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to [use the Raspberry Pi as its DNS server](http://pi-hole.net/faq/how-do-i-use-the-pi-hole-as-my-dns-server/).
## Installing the Pi-hole (Click to Watch!)
## What is Pi-hole and how do I install it?
<p align="center">
<a href=https://www.youtube.com/watch?v=TzFLJqUeirA><img src="https://assets.pi-hole.net/static/global.png"></a>
<a href=https://www.youtube.com/watch?v=vKWjx1AQYgs><img src="https://assets.pi-hole.net/static/global.png"></a>
</p>
## Would you like to know more?
**Watch the 60-second video below to get a quick overview**
<p align="center">
<a href=https://youtu.be/9Eti3xibiho><img src="https://assets.pi-hole.net/static/blackhole_web.png"></a>
</p>
## Get Help Or Connect With Us On The Web

@ -17,56 +17,21 @@ gravity="/etc/pihole/gravity.list"
. /etc/pihole/setupVars.conf
CalcBlockedDomains() {
if [ -e "${gravity}" ]; then
# if BOTH IPV4 and IPV6 are in use, then we need to divide total domains by 2.
if [[ -n "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]]; then
blockedDomainsTotal=$(wc -l /etc/pihole/gravity.list | awk '{print $1/2}')
else
# only one is set.
blockedDomainsTotal=$(wc -l /etc/pihole/gravity.list | awk '{print $1}')
fi
else
blockedDomainsTotal="Err."
fi
}
CalcQueriesToday() {
if [ -e "${piLog}" ]; then
queriesToday=$(awk '/query\[/ {print $6}' < "${piLog}" | wc -l)
else
queriesToday="Err."
fi
}
CalcblockedToday() {
if [ -e "${piLog}" ] && [ -e "${gravity}" ];then
blockedToday=$(awk '/\/etc\/pihole\/gravity.list/ && !/address/ {print $6}' < "${piLog}" | wc -l)
else
blockedToday="Err."
fi
}
CalcPercentBlockedToday() {
if [ "${queriesToday}" != "Err." ] && [ "${blockedToday}" != "Err." ]; then
if [ "${queriesToday}" != 0 ]; then #Fixes divide by zero error :)
#scale 2 rounds the number down, so we'll do scale 4 and then trim the last 2 zeros
percentBlockedToday=$(echo "scale=4; ${blockedToday}/${queriesToday}*100" | bc)
percentBlockedToday=$(sed 's/.\{2\}$//' <<< "${percentBlockedToday}")
else
percentBlockedToday=0
fi
fi
# Borrowed/modified from https://gist.github.com/cjus/1047794
function GetJSONValue {
retVal=$(echo $1 | sed 's/\\\\\//\//g' | \
sed 's/[{}]//g' | \
awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | \
sed 's/\"\:\"/\|/g' | \
sed 's/[\,]/ /g' | \
sed 's/\"//g' | \
grep -w $2)
echo ${retVal##*|}
}
outputJSON() {
CalcQueriesToday
CalcblockedToday
CalcPercentBlockedToday
CalcBlockedDomains
printf '{"domains_being_blocked":"%s","dns_queries_today":"%s","ads_blocked_today":"%s","ads_percentage_today":"%s"}\n' "$blockedDomainsTotal" "$queriesToday" "$blockedToday" "$percentBlockedToday"
json=$(curl -s -X GET http://127.0.0.1/admin/api.php?summaryRaw)
echo ${json}
}
normalChrono() {
@ -87,22 +52,17 @@ normalChrono() {
# Uncomment to continually read the log file and display the current domain being blocked
#tail -f /var/log/pihole.log | awk '/\/etc\/pihole\/gravity.list/ {if ($7 != "address" && $7 != "name" && $7 != "/etc/pihole/gravity.list") print $7; else;}'
#uncomment next 4 lines to use original query count calculation
#today=$(date "+%b %e")
#todaysQueryCount=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ {print $7}' | wc -l)
#todaysQueryCountV4=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ && /\[A\]/ {print $7}' | wc -l)
#todaysQueryCountV6=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ && /\[AAAA\]/ {print $7}' | wc -l)
json=$(curl -s -X GET http://127.0.0.1/admin/api.php?summaryRaw)
CalcQueriesToday
CalcblockedToday
CalcPercentBlockedToday
domains=$(printf "%'.f" $(GetJSONValue ${json} "domains_being_blocked")) #add commas in
queries=$(printf "%'.f" $(GetJSONValue ${json} "dns_queries_today"))
blocked=$(printf "%'.f" $(GetJSONValue ${json} "ads_blocked_today"))
LC_NUMERIC=C percentage=$(printf "%0.2f\n" $(GetJSONValue ${json} "ads_percentage_today")) #2 decimal places
CalcBlockedDomains
echo "Blocking: ${domains}"
echo "Queries: ${queries}"
echo "Blocking: ${blockedDomainsTotal}"
echo "Queries: ${queriesToday}" #same total calculation as dashboard
echo "Pi-holed: ${blockedToday} (${percentBlockedToday}%)"
echo "Pi-holed: ${blocked} (${percentage}%)"
sleep 5
done

@ -15,6 +15,7 @@ basename=pihole
piholeDir=/etc/${basename}
whitelist=${piholeDir}/whitelist.txt
blacklist=${piholeDir}/blacklist.txt
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
reload=false
addmode=true
verbose=true
@ -47,13 +48,17 @@ helpFunc() {
::: -h, --help Show this help dialog
::: -l, --list Display your ${word}listed domains
EOM
if [[ "${letter}" == "b" ]]; then
echo "::: -wild, --wildcard Add whitecard entry (only blacklist)"
fi
exit 0
}
EscapeRegexp() {
# This way we may safely insert an arbitrary
# string in our regular expressions
echo $* | sed "s/[]\\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
# Also remove leading "." if present
echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
}
HandleOther(){
@ -61,7 +66,7 @@ HandleOther(){
domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1")
#check validity of domain
validDomain=$(perl -ne "print if /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/" <<< "$domain")
validDomain=$(echo "${domain}" | perl -lne 'print if /^(?!.*[^a-z0-9-\.].*)\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)*[a-z]{2,63}\b/')
if [ -z "${validDomain}" ]; then
echo "::: $1 is not a valid argument or domain name"
else
@ -89,22 +94,51 @@ AddDomain() {
list="$2"
domain=$(EscapeRegexp "$1")
bool=true
#Is the domain in the list we want to add it to?
grep -Ex -q "${domain}" ${list} > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
#domain not found in the whitelist file, add it!
if [[ "${verbose}" == true ]]; then
echo "::: Adding $1 to $list..."
fi
reload=true
# Add it to the list we want to add it to
echo "$1" >> ${list}
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} already exists in ${list}, no need to add!"
fi
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
bool=true
#Is the domain in the list we want to add it to?
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
#domain not found in the whitelist file, add it!
if [[ "${verbose}" == true ]]; then
echo "::: Adding $1 to $list..."
fi
reload=true
# Add it to the list we want to add it to
echo "$1" >> "${list}"
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} already exists in ${list}, no need to add!"
fi
fi
elif [[ "${list}" == "${wildcardlist}" ]]; then
source "${piholeDir}/setupVars.conf"
#Remove the /* from the end of the IPv4addr.
IPV4_ADDRESS=${IPV4_ADDRESS%/*}
IPV6_ADDRESS=${IPV6_ADDRESS}
bool=true
#Is the domain in the list?
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
if [[ "${verbose}" == true ]]; then
echo "::: Adding $1 to wildcard blacklist..."
fi
reload=true
echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}"
if [[ ${#IPV6_ADDRESS} > 0 ]] ; then
echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}"
fi
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} already exists in wildcard blacklist, no need to add!"
fi
fi
fi
}
@ -112,18 +146,38 @@ RemoveDomain() {
list="$2"
domain=$(EscapeRegexp "$1")
bool=true
#Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
grep -Ex -q "${domain}" ${list} > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo "::: Removing $1 from $list..."
# /I flag: search case-insensitive
sed -i "/${domain}/Id" ${list}
reload=true
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} does not exist in ${list}, no need to remove!"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
bool=true
#Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo "::: Removing $1 from $list..."
# /I flag: search case-insensitive
sed -i "/${domain}/Id" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} does not exist in ${list}, no need to remove!"
fi
fi
elif [[ "${list}" == "${wildcardlist}" ]]; then
bool=true
#Is it in the list?
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo "::: Removing $1 from $list..."
# /I flag: search case-insensitive
sed -i "/address=\/${domain}/Id" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo "::: ${1} does not exist in ${list}, no need to remove!"
fi
fi
fi
}
@ -153,6 +207,7 @@ for var in "$@"; do
case "${var}" in
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
"-wild" | "wildcard" ) listMain="${wildcardlist}";;
"-nr"| "--noreload" ) reload=false;;
"-d" | "--delmode" ) addmode=false;;
"-f" | "--force" ) force=true;;

@ -122,6 +122,13 @@ version_check() {
&& log_echo -r "${light_ver}" || (log_echo "lighttpd not installed." && error_found=1)
local php_ver="$(php -v |& head -n1)" \
&& log_echo -r "${php_ver}" || (log_echo "PHP not installed." && error_found=1)
(local pi_hole_branch="$(cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD)" && log_echo -r "Pi-hole branch: ${pi_hole_branch}") || log_echo "Unable to obtain Pi-hole branch"
(local pi_hole_rev="$(cd /etc/.pihole/ && git describe --long --dirty --tags)" && log_echo -r "Pi-hole rev: ${pi_hole_rev}") || log_echo "Unable to obtain Pi-hole revision"
(local admin_branch="$(cd /var/www/html/admin && git rev-parse --abbrev-ref HEAD)" && log_echo -r "AdminLTE branch: ${admin_branch}") || log_echo "Unable to obtain AdminLTE branch"
(local admin_rev="$(cd /var/www/html/admin && git describe --long --dirty --tags)" && log_echo -r "AdminLTE rev: ${admin_rev}") || log_echo "Unable to obtain AdminLTE revision"
return "${error_found}"
}
@ -354,10 +361,21 @@ files_check "${ADLISTFILE}"
header_write "Analyzing gravity.list"
gravity_length=$(wc -l "${GRAVITYFILE}") \
gravity_length=$(grep -c ^ "${GRAVITYFILE}") \
&& log_write "${GRAVITYFILE} is ${gravity_length} lines long." \
|| log_echo "Warning: No gravity.list file found!"
header_write "Analyzing pihole.log"
pihole_length=$(grep -c ^ "${PIHOLELOG}") \
&& log_write "${PIHOLELOG} is ${pihole_length} lines long." \
|| log_echo "Warning: No pihole.log file found!"
pihole_size=$(du -h "${PIHOLELOG}" | awk '{ print $1 }') \
&& log_write "${PIHOLELOG} is ${pihole_size}." \
|| log_echo "Warning: No pihole.log file found!"
# Continuously append the pihole.log file to the pihole_debug.log file
dumpPiHoleLog() {
trap '{ echo -e "\n::: Finishing debug write from interrupt... Quitting!" ; exit 1; }' INT

@ -11,5 +11,10 @@
# (at your option) any later version.
echo -n "::: Flushing /var/log/pihole.log ..."
echo " " > /var/log/pihole.log
# Test if logrotate is available on this system
if command -v /usr/sbin/logrotate &> /dev/null; then
/usr/sbin/logrotate --force /etc/pihole/logrotate
else
echo " " > /var/log/pihole.log
fi
echo "... done!"

@ -92,12 +92,17 @@ SetWebPassword(){
ProcessDNSSettings() {
source "${setupVars}"
delete_dnsmasq_setting "server="
add_dnsmasq_setting "server" "${PIHOLE_DNS_1}"
if [[ "${PIHOLE_DNS_2}" != "" ]]; then
add_dnsmasq_setting "server" "${PIHOLE_DNS_2}"
fi
delete_dnsmasq_setting "server"
COUNTER=1
while [[ 1 ]]; do
var=PIHOLE_DNS_${COUNTER}
if [ -z "${!var}" ]; then
break;
fi
add_dnsmasq_setting "server" "${!var}"
let COUNTER=COUNTER+1
done
delete_dnsmasq_setting "domain-needed"
@ -111,31 +116,45 @@ ProcessDNSSettings() {
add_dnsmasq_setting "bogus-priv"
fi
delete_dnsmasq_setting "dnssec"
delete_dnsmasq_setting "trust-anchor="
if [[ "${DNSSEC}" == true ]]; then
echo "dnssec
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
" >> "${dnsmasqconfig}"
fi
}
SetDNSServers(){
# Save setting to file
change_setting "PIHOLE_DNS_1" "${args[2]}"
if [[ "${args[3]}" != "none" ]]; then
change_setting "PIHOLE_DNS_2" "${args[3]}"
else
change_setting "PIHOLE_DNS_2" ""
fi
if [[ "${args[4]}" == "domain-needed" ]]; then
delete_setting "PIHOLE_DNS"
IFS=',' read -r -a array <<< "${args[2]}"
for index in "${!array[@]}"
do
add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}"
done
if [[ "${args[3]}" == "domain-needed" ]]; then
change_setting "DNS_FQDN_REQUIRED" "true"
else
change_setting "DNS_FQDN_REQUIRED" "false"
fi
if [[ "${args[4]}" == "bogus-priv" || "${args[5]}" == "bogus-priv" ]]; then
if [[ "${args[4]}" == "bogus-priv" ]]; then
change_setting "DNS_BOGUS_PRIV" "true"
else
change_setting "DNS_BOGUS_PRIV" "false"
fi
if [[ "${args[5]}" == "dnssec" ]]; then
change_setting "DNSSEC" "true"
else
change_setting "DNSSEC" "false"
fi
ProcessDNSSettings
# Restart dnsmasq to load new configuration
@ -213,10 +232,13 @@ dhcp-authoritative
dhcp-range=${DHCP_START},${DHCP_END},${leasetime}
dhcp-option=option:router,${DHCP_ROUTER}
dhcp-leasefile=/etc/pihole/dhcp.leases
domain=${PIHOLE_DOMAIN}
#quiet-dhcp
" > "${dhcpconfig}"
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
fi
if [[ "${DHCP_IPv6}" == "true" ]]; then
echo "#quiet-dhcp6
#enable-ra
@ -227,7 +249,7 @@ ra-param=*,0,0
fi
else
rm "${dhcpconfig}"
rm "${dhcpconfig}" &> /dev/null
fi
}

@ -74,7 +74,9 @@ if($uri == "/")
<a class='safe33' id="whitelisting">Whitelist this page</a>
<a class='safe33' href='javascript:window.close()'>Close window</a>
</div>
<div style="width: 98%; text-align: center; padding: 10px;" hidden="true" id="whitelistingform">Password required!<br/>
<div style="width: 98%; text-align: center; padding: 10px;" hidden="true" id="whitelistingform">
<p>Note that whitelisting domains which are blocked using the wildcard method won't work.</p>
<p>Password required!</p><br/>
<form>
<input name="list" type="hidden" value="white"><br/>
Domain:<br/>
@ -88,6 +90,16 @@ if($uri == "/")
</main>
<footer>Generated <?php echo date('D g:i A, M d'); ?> by Pi-hole <?php echo $piHoleVersion; ?></footer>
<script src="http://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
<script>
// Create event for when the output is appended to
(function($) {
var origAppend = $.fn.append;
$.fn.append = function () {
return origAppend.apply(this, arguments).trigger("append");
};
})(jQuery);
</script>
<script src="http://pi.hole/admin/scripts/pi-hole/js/queryads.js"></script>
<script>
function inIframe () {
@ -115,6 +127,15 @@ else
$( "#whitelisting" ).on( "click", function(){ $( "#whitelistingform" ).removeAttr( "hidden" ); });
// Remove whitelist functionality if the domain was blocked because of a wildcard
$( "#output" ).bind("append", function(){
if($( "#output" ).contents()[0].data.indexOf("Wildcard blocking") !== -1)
{
$( "#whitelisting" ).hide();
$( "#whitelistingform" ).hide();
}
});
function add() {
var domain = $("#domain");
var pw = $("#pw");

@ -0,0 +1,9 @@
/var/log/pihole.log {
daily
copytruncate
rotate 5
compress
delaycompress
notifempty
nomail
}

@ -23,4 +23,7 @@
# 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
# The flush script will use logrotate if available
00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate

@ -27,7 +27,8 @@ 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)
PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version gravity uninstall webpage)
PI_HOLE_INSTALL_DIR="/opt/pihole"
useUpdateVars=false
IPV4_ADDRESS=""
@ -52,24 +53,32 @@ reconfigure=false
runUnattended=false
# Compatibility
distro_check() {
if command -v apt-get &> /dev/null; then
#Debian Family
#############################################
PKG_MANAGER="apt-get"
UPDATE_PKG_CACHE="${PKG_MANAGER} update"
PKG_INSTALL="${PKG_MANAGER} --yes --no-install-recommends 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"
# #########################################
# 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"
if ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1; then
iproute_pkg="iproute2"
else
iproute_pkg="iproute"
fi
# 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"
if ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1; then
phpVer="php"
else
phpVer="php5"
fi
# #########################################
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)
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"
@ -85,7 +94,7 @@ elif command -v rpm &> /dev/null; then
# Fedora and family update cache on every PKG_INSTALL call, no need for a separate update.
UPDATE_PKG_CACHE=":"
PKG_INSTALL="${PKG_MANAGER} install -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 findutils lighttpd lighttpd-fastcgi nmap-ncat php php-common php-cli sudo unzip wget)
@ -102,8 +111,8 @@ else
echo "OS distribution not supported"
exit
fi
}
####### FUNCTIONS ##########
is_repo() {
# Use git to check if directory is currently under VCS, return the value 128
# if directory is not a repo. Return 1 if directory does not exist.
@ -168,15 +177,19 @@ getGitFiles() {
}
find_IPv4_information() {
local route
# 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 route get 8.8.8.8| awk '{print $7}')
IPv4gw=$(ip route get 8.8.8.8 | awk '{print $3}')
route=$(ip route get 8.8.8.8)
IPv4dev=$(awk '{for (i=1; i<=NF; i++) if ($i~/dev/) print $(i+1)}' <<< "${route}")
IPv4bare=$(awk '{print $7}' <<< "${route}")
IPV4_ADDRESS=$(ip -o -f inet addr show | grep "${IPv4bare}" | awk '{print $4}' | awk 'END {print}')
IPv4gw=$(awk '{print $3}' <<< "${route}")
}
get_available_interfaces() {
# Get available UP interfaces.
availableInterfaces=$(ip -o link | grep "state UP" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1)
availableInterfaces=$(ip -o link | grep -v "state DOWN\|lo" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1)
}
welcomeDialogs() {
@ -235,32 +248,28 @@ chooseInterface() {
# Loop sentinel variable
local firstLoop=1
if [[ $(echo "${availableInterfaces}" | wc -l) -eq 1 ]]; then
PIHOLE_INTERFACE="${availableInterfaces}"
return
fi
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
if [[ ${interfaceCount} -eq 1 ]]; then
PIHOLE_INTERFACE="${availableInterfaces}"
else
echo "::: Cancel selected, exiting...."
exit 1
while read -r line; do
mode="OFF"
if [[ ${firstLoop} -eq 1 ]]; then
firstLoop=0
mode="ON"
fi
interfacesArray+=("${line}" "available" "${mode}")
done <<< "${availableInterfaces}"
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount})
chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \
{ echo "::: Cancel selected. Exiting"; exit 1; }
for desiredInterface in ${chooseInterfaceOptions}; do
PIHOLE_INTERFACE=${desiredInterface}
echo "::: Using interface: $PIHOLE_INTERFACE"
done
fi
}
@ -281,41 +290,37 @@ use4andor6() {
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..."
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { echo "::: Cancel selected. Exiting"; exit 1; }
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
}
getStaticIPv4Settings() {
local ipSettingsCorrect
# 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?
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
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.
@ -326,36 +331,29 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
# 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
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) || \
# Cancelling IPv4 settings window
{ ipSettingsCorrect=False; echo "::: Cancel selected. Exiting..."; exit 1; }
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
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) || \
# Cancelling gateway settings window
{ ipSettingsCorrect=False; echo "::: Cancel selected. Exiting..."; exit 1; }
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
# If the settings are wrong, the loop continues
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
@ -439,95 +437,88 @@ valid_ip() {
}
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}"
local DNSSettingsCorrect
DNSChooseOptions=(Google ""
OpenDNS ""
Level3 ""
Norton ""
Comodo ""
Custom "")
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 6 \
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \
{ echo "::: Cancel selected. Exiting"; exit 1; }
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
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) || \
{ echo "::: Cancel selected. Exiting"; exit 1; }
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
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_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
if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then
PIHOLE_DNS_2=""
fi
done
;;
esac
else
echo "::: Cancel selected. Exiting..."
exit 1
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
}
setLogging() {
@ -610,35 +601,35 @@ 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}
shift
local old_files=( "$@" )
for script in "${old_files[@]}"; do
rm -f "${clean_directory}${script}.sh"
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}"
clean_existing "${PI_HOLE_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 -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
install -o "${USER}" -Dm755 -d "${PI_HOLE_INSTALL_DIR}"
install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" gravity.sh
install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/*.sh
install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./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."
echo " *** ERROR: Local repo ${PI_HOLE_LOCAL_REPO} not found, exiting."
exit 1
fi
}
@ -757,7 +748,7 @@ install_dependent_packages() {
fi
done
if [[ ${#installArray[@]} -gt 0 ]]; then
debconf-apt-progress -- ${PKG_INSTALL} "${installArray[@]}"
debconf-apt-progress -- "${PKG_INSTALL[@]}" "${installArray[@]}"
return
fi
return 0
@ -774,7 +765,7 @@ install_dependent_packages() {
fi
done
if [[ ${#installArray[@]} -gt 0 ]]; then
${PKG_INSTALL} "${installArray[@]}" &> /dev/null
"${PKG_INSTALL[@]}" "${installArray[@]}" &> /dev/null
return
fi
return 0
@ -818,21 +809,23 @@ installPiholeWeb() {
if [ -f "/var/www/html/pihole/blockingpage.css" ]; then
echo "::: Existing blockingpage.css detected, not overwriting"
else
echo -n "::: index.css missing, replacing... "
echo -n "::: blockingpage.css missing, replacing... "
cp /etc/.pihole/advanced/blockingpage.css /var/www/html/pihole
echo " done!"
fi
else
mkdir /var/www/html/pihole
echo "::: Creating directory for blocking page"
install -d /var/www/html/pihole
install -D /etc/.pihole/advanced/{index,blockingpage}.* /var/www/html/pihole/
if [ -f /var/www/html/index.lighttpd.html ]; then
mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig
else
printf "\n:::\tNo default index.lighttpd.html file found... not backing up"
fi
cp /etc/.pihole/advanced/index.* /var/www/html/pihole/.
echo " done!"
fi
# Install Sudoer file
echo ":::"
echo -n "::: Installing sudoer file..."
@ -878,29 +871,42 @@ runGravity() {
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)
if id -u pihole &> /dev/null; then
echo "::: User 'pihole' already exists"
else
echo "::: User 'pihole' doesn't exist. Creating..."
useradd -r -s /usr/sbin/nologin pihole
fi
}
configureFirewall() {
# Allow HTTP and DNS traffic
if firewall-cmd --state &> /dev/null; then
echo "::: Configuring FirewallD for httpd and dnsmasq.."
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\n\nPi-hole currently requires HTTP and DNS port access.\n\n\n\nInstall Pi-hole default firewall rules?" ${r} ${c} || \
{ echo -e ":::\n::: Not installing firewall rulesets."; return 0; }
echo -e ":::\n:::\n Configuring FirewallD for httpd and dnsmasq."
firewall-cmd --permanent --add-port=80/tcp --add-port=53/tcp --add-port=53/udp
firewall-cmd --reload
return 0
# Check for proper kernel modules to prevent failure
elif modinfo ip_tables &> /dev/null; then
elif modinfo ip_tables &> /dev/null && command -v iptables &> /dev/null; then
# If chain Policy is not ACCEPT or last Rule is not ACCEPT
# then check and insert our Rules above the DROP/REJECT Rule.
if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\n\nPi-hole currently requires HTTP and DNS port access.\n\n\n\nInstall Pi-hole default firewall rules?" ${r} ${c} || \
{ echo -e ":::\n::: Not installing firewall rulesets."; return 0; }
echo -e ":::\n::: Installing new IPTables firewall rulesets."
# Check chain first, otherwise a new rule will duplicate old ones
echo "::: Configuring iptables for httpd and dnsmasq.."
iptables -C INPUT -p tcp -m tcp --dport 80 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -C INPUT -p tcp -m tcp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT
iptables -C INPUT -p udp -m udp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT
return 0
fi
else
echo "::: No active firewall detected.. skipping firewall configuration."
echo -e ":::\n::: No active firewall detected.. skipping firewall configuration."
return 0
fi
echo -e ":::\n::: Skipping firewall configuration."
}
finalExports() {
@ -930,6 +936,24 @@ finalExports() {
fi
}
installLogrotate() {
# Install the logrotate script
echo ":::"
echo -n "::: Installing latest logrotate script..."
cp /etc/.pihole/advanced/logrotate /etc/pihole/logrotate
# Different operating systems have different user / group
# settings for logrotate that makes it impossible to create
# a static logrotate file that will work with e.g.
# Rasbian and Ubuntu at the same time. Hence, we have to
# customize the logrotate script here in order to reflect
# the local properties of the /var/log directory
logusergroup="$(stat -c '%U %G' /var/log)"
if [[ ! -z $logusergroup ]]; then
echo "su ${logusergroup}" >> /etc/pihole/logrotate
fi
echo " done!"
}
installPihole() {
# Install base files and web interface
create_pihole_user
@ -949,6 +973,7 @@ installPihole() {
CreateLogFile
installPiholeWeb
installCron
installLogrotate
configureFirewall
finalExports
runGravity
@ -972,14 +997,14 @@ accountForRefactor() {
updatePihole() {
accountForRefactor
# Source ${setupVars} for use in the rest of the functions.
. ${setupVars}
source ${setupVars}
# Install base files and web interface
installScripts
installConfigs
CreateLogFile
installPiholeWeb
installCron
configureFirewall
installLogrotate
finalExports #re-export setupVars.conf to account for any new vars added in new versions
runGravity
}
@ -993,15 +1018,11 @@ checkSelinux() {
enforceMode=$(getenforce)
echo "${enforceMode}"
if [[ "${enforceMode}" == "Enforcing" ]]; then
if (whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c}); then
echo ":::"
echo "::: Continuing installation with SELinux Enforcing."
echo "::: Please refer to official SELinux documentation to create a custom policy."
else
echo ":::"
echo "::: Not continuing install after SELinux Enforcing detected."
exit 1
fi
whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c} || \
{ echo ":::"; echo "::: Not continuing install after SELinux Enforcing detected."; exit 1; }
echo ":::"
echo "::: Continuing installation with SELinux Enforcing."
echo "::: Please refer to official SELinux documentation to create a custom policy."
fi
fi
}
@ -1037,23 +1058,19 @@ update_dialogs() {
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)
"${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) || \
{ echo "::: Cancel selected. Exiting"; exit 1; }
if [[ $? = 0 ]];then
case ${UpdateCmd} in
${opt1a})
echo "::: ${opt1a} option selected."
useUpdateVars=true
;;
${opt2a})
echo "::: ${opt2a} option selected"
useUpdateVars=false
;;
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() {
@ -1072,7 +1089,7 @@ main() {
if command -v sudo &> /dev/null; then
echo "::: Utility sudo located."
exec curl -sSL https://install.pi-hole.net | sudo bash "$@"
exec curl -sSL https://raw.githubusercontent.com/pi-hole/pi-hole/master/automated%20install/basic-install.sh | 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."
@ -1080,6 +1097,9 @@ main() {
fi
fi
# Check for supported distribution
distro_check
# Check arguments for the undocumented flags
for var in "$@"; do
case "$var" in
@ -1170,11 +1190,11 @@ main() {
pw=""
if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then
pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
pihole -a -p ${pw}
/usr/local/bin/pihole -a -p "${pw}"
fi
if [[ "${useUpdateVars}" == false ]]; then
displayFinalMessage ${pw}
displayFinalMessage "${pw}"
fi
echo "::: Restarting services..."

@ -76,7 +76,7 @@ gravity_collapse() {
#custom file found, use this instead of default
echo -n "::: Custom adList file detected. Reading..."
sources=()
while read -r line; do
while IFS= read -r line || [[ -n "$line" ]]; do
#Do not read commented out or blank lines
if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then
echo "" > /dev/null
@ -89,7 +89,7 @@ gravity_collapse() {
#no custom file found, use defaults!
echo -n "::: No custom adlist file detected, reading from default file..."
sources=()
while read -r line; do
while IFS= read -r line || [[ -n "$line" ]]; do
#Do not read commented out or blank lines
if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then
echo "" > /dev/null
@ -388,7 +388,7 @@ if [[ "${forceGrav}" == true ]]; then
fi
#Overwrite adlists.default from /etc/.pihole in case any changes have been made. Changes should be saved in /etc/adlists.list
#cp /etc/.pihole/adlists.default /etc/pihole/adlists.default
cp /etc/.pihole/adlists.default /etc/pihole/adlists.default
gravity_collapse
gravity_spinup
if [[ "${skipDownload}" == false ]]; then

@ -11,6 +11,7 @@
# (at your option) any later version.
PI_HOLE_SCRIPT_DIR="/opt/pihole"
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
# Must be root to use this tool
if [[ ! $EUID -eq 0 ]];then
if [ -x "$(command -v sudo)" ];then
@ -38,6 +39,11 @@ blacklistFunc() {
exit 0
}
wildcardFunc() {
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
exit 0
}
debugFunc() {
"${PI_HOLE_SCRIPT_DIR}"/piholeDebug.sh
exit 0
@ -63,11 +69,6 @@ updateGravityFunc() {
exit 0
}
setupLCDFunction() {
"${PI_HOLE_SCRIPT_DIR}"/setupLCD.sh
exit 0
}
scanList(){
domain="${1}"
list="${2}"
@ -79,19 +80,52 @@ scanList(){
fi
}
processWildcards() {
IFS="." read -r -a array <<< "${1}"
for (( i=${#array[@]}-1; i>=0; i-- )); do
ar=""
for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do
if [[ $j == $((${#array[@]}-1)) ]]; then
ar="${array[$j]}"
else
ar="${array[$j]}.${ar}"
fi
done
echo "${ar}"
done
}
queryFunc() {
domain="${2}"
method="${3}"
lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt)
for list in ${lists[@]}; do
result=$(scanList ${domain} ${list} ${method})
if [ -e "${list}" ]; then
result=$(scanList ${domain} ${list} ${method})
# Remove empty lines before couting number of results
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
echo "::: ${list} (${count} results)"
if [[ ${count} > 0 ]]; then
echo "${result}"
fi
echo ""
else
echo "::: ${list} does not exist"
echo ""
fi
done
# Scan for possible wildcard matches
local wildcards=($(processWildcards "${domain}"))
for domain in ${wildcards[@]}; do
result=$(scanList "\/${domain}\/" ${wildcardlist})
# Remove empty lines before couting number of results
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
echo "::: ${list} (${count} results)"
if [[ ${count} > 0 ]]; then
echo "::: Wildcard blocking ${domain} (${count} results)"
echo "${result}"
echo ""
fi
echo ""
done
exit 0
}
@ -248,7 +282,6 @@ helpFunc() {
::: -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
::: -h, help Show this help dialog
::: -v, version Show current versions
@ -275,12 +308,12 @@ fi
case "${1}" in
"-w" | "whitelist" ) whitelistFunc "$@";;
"-b" | "blacklist" ) blacklistFunc "$@";;
"-wild" | "wildcard" ) wildcardFunc "$@";;
"-d" | "debug" ) debugFunc;;
"-f" | "flush" ) flushFunc;;
"-up" | "updatePihole" ) updatePiholeFunc;;
"-r" | "reconfigure" ) reconfigurePiholeFunc;;
"-g" | "updateGravity" ) updateGravityFunc "$@";;
"-s" | "setupLCD" ) setupLCDFunction;;
"-c" | "chronometer" ) chronometerFunc "$@";;
"-h" | "help" ) helpFunc;;
"-v" | "version" ) versionFunc "$@";;

@ -64,35 +64,237 @@ def test_setupVars_saved_to_file(Pihole):
for k,v in SETUPVARS.iteritems():
assert "{}={}".format(k, v) in output
def test_configureFirewall_firewalld_no_errors(Pihole):
''' confirms firewalld rules are applied when appopriate '''
mock_command('firewall-cmd', '0', Pihole)
def test_configureFirewall_firewalld_running_no_errors(Pihole):
''' confirms firewalld rules are applied when firewallD is running '''
# firewallD returns 'running' as status
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
# Whiptail dialog returns Ok for user prompt
mock_command('whiptail', {'*':('', 0)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = '::: Configuring FirewallD for httpd and dnsmasq.'
expected_stdout = 'Configuring FirewallD for httpd and dnsmasq.'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout
assert 'firewall-cmd --state' in firewall_calls
assert 'firewall-cmd --permanent --add-port=80/tcp --add-port=53/tcp --add-port=53/udp' in firewall_calls
assert 'firewall-cmd --reload' in firewall_calls
def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
''' confirms firewalld rules are not applied when firewallD is not running '''
# firewallD returns non-running status
mock_command('firewall-cmd', {'*':('not running', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'No active firewall detected.. skipping firewall configuration.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
''' confirms firewalld rules are not applied when firewallD is running, user declines ruleset '''
# firewallD returns running status
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*':('', 1)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_no_firewall(Pihole):
''' confirms firewall skipped no daemon is running '''
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'No active firewall detected'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_declined_no_errors(Pihole):
''' confirms IPTables rules are not applied when IPTables is running, user declines ruleset '''
# iptables command exists
mock_command('iptables', {'*':('', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*':('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*':('', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_rules_exist_no_errors(Pihole):
''' confirms IPTables rules are not applied when IPTables is running and rules exist '''
# iptables command exists and returns 0 on calls (should return 0 on iptables -C)
mock_command('iptables', {'-S':('-P INPUT DENY', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*':('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*':('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT' not in firewall_calls
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT' not in firewall_calls
assert 'iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT' not in firewall_calls
def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
''' confirms IPTables rules are applied when IPTables is running and rules do not exist '''
# iptables command and returns 0 on calls (should return 1 on iptables -C)
mock_command('iptables', {'-S':('-P INPUT DENY', '0'), '-C':('', 1), '-I':('', 0)}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*':('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*':('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT' in firewall_calls
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT' in firewall_calls
assert 'iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT' in firewall_calls
def test_installPiholeWeb_fresh_install_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed on a fresh build '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_empty_directory_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed in an emtpy directory '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_index_php_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.php
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_index_js_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.js
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_blockingpage_css_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/blockingpage.css
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_already_populated_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.php
touch /var/www/html/pihole/index.js
touch /var/www/html/pihole/blockingpage.css
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
assert 'index.php missing, replacing...' not in installWeb.stdout
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
assert 'index.js missing, replacing...' not in installWeb.stdout
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
assert 'blockingpage.css missing, replacing... ' not in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
# Helper functions
def mock_command(script, result, container):
def mock_command(script, args, container):
''' Allows for setup of commands we don't really want to have to run for real in unit tests '''
''' TODO: support array of results that enable the results to change over multiple executions of a command '''
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent('''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
exit {retcode}
'''.format(script=script, retcode=result))
case "\$1" in'''.format(script=script))
for k, v in args.iteritems():
case = dedent('''
{arg})
echo {res}
exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1]))
mock_script += case
mock_script += dedent('''
esac''')
container.run('''
cat <<EOF> {script}\n{content}\nEOF
chmod +x {script}
'''.format(script=full_script_path, content=mock_script))
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, content=mock_script, scriptlog=script))
def run_script(Pihole, script):
result = Pihole.run(script)

Loading…
Cancel
Save