@ -1,3 +1,4 @@
import pytest
from textwrap import dedent
from textwrap import dedent
import re
import re
from . conftest import (
from . conftest import (
@ -6,7 +7,9 @@ from .conftest import (
info_box ,
info_box ,
cross_box ,
cross_box ,
mock_command ,
mock_command ,
mock_command_run ,
mock_command_2 ,
mock_command_2 ,
mock_command_passthrough ,
run_script
run_script
)
)
@ -109,6 +112,7 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
confirms all web page assets from Core repo are installed on a fresh build
confirms all web page assets from Core repo are installed on a fresh build
'''
'''
installWeb = Pihole . run ( '''
installWeb = Pihole . run ( '''
umask 0027
source / opt / pihole / basic - install . sh
source / opt / pihole / basic - install . sh
installPiholeWeb
installPiholeWeb
''' )
''' )
@ -129,6 +133,516 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
assert ' blockingpage.css ' in web_directory
assert ' blockingpage.css ' in web_directory
def get_directories_recursive ( Pihole , directory ) :
if directory is None :
return directory
ls = Pihole . run ( ' ls -d {} ' . format ( directory + ' /*/ ' ) )
directories = list ( filter ( bool , ls . stdout . splitlines ( ) ) )
dirs = directories
for dirval in directories :
dir_rec = get_directories_recursive ( Pihole , dirval )
if isinstance ( dir_rec , str ) :
dirs . extend ( [ dir_rec ] )
else :
dirs . extend ( dir_rec )
return dirs
def test_installPihole_fresh_install_readableFiles ( Pihole ) :
'''
confirms all neccessary files are readable by pihole user
'''
# Whiptail dialog returns Cancel for user prompt
mock_command ( ' whiptail ' , { ' * ' : ( ' ' , ' 0 ' ) } , Pihole )
# mock git pull
mock_command_passthrough ( ' git ' , { ' pull ' : ( ' ' , ' 0 ' ) } , Pihole )
# mock systemctl to not start lighttpd and FTL
mock_command_2 (
' systemctl ' ,
{
' enable lighttpd ' : (
' ' ,
' 0 '
) ,
' restart lighttpd ' : (
' ' ,
' 0 '
) ,
' start lighttpd ' : (
' ' ,
' 0 '
) ,
' enable pihole-FTL ' : (
' ' ,
' 0 '
) ,
' restart pihole-FTL ' : (
' ' ,
' 0 '
) ,
' start pihole-FTL ' : (
' ' ,
' 0 '
) ,
' * ' : (
' echo " systemctl call with $@ " ' ,
' 0 '
) ,
} ,
Pihole
)
# try to install man
Pihole . run ( ' command -v apt-get > /dev/null && apt-get install -qq man ' )
Pihole . run ( ' command -v dnf > /dev/null && dnf install -y man ' )
Pihole . run ( ' command -v yum > /dev/null && yum install -y man ' )
# create configuration file
setup_var_file = ' cat <<EOF> /etc/pihole/setupVars.conf \n '
for k , v in SETUPVARS . items ( ) :
setup_var_file + = " {} = {} \n " . format ( k , v )
setup_var_file + = " INSTALL_WEB_SERVER=true \n "
setup_var_file + = " INSTALL_WEB_INTERFACE=true \n "
setup_var_file + = " EOF \n "
Pihole . run ( setup_var_file )
install = Pihole . run ( '''
export TERM = xterm
export DEBIAN_FRONTEND = noninteractive
umask 0027
runUnattended = true
useUpdateVars = true
source / opt / pihole / basic - install . sh > / dev / null
runUnattended = true
useUpdateVars = true
main
''' )
assert 0 == install . rc
maninstalled = True
if ( info_box + ' man not installed ' ) in install . stdout :
maninstalled = False
piholeuser = ' pihole '
exit_status_success = 0
test_cmd = ' su --shell /bin/bash --command " test - {0} {1} " -p {2} '
# check files in /etc/pihole for read, write and execute permission
check_etc = test_cmd . format ( ' r ' , ' /etc/pihole ' , piholeuser )
actual_rc = Pihole . run ( check_etc ) . rc
assert exit_status_success == actual_rc
check_etc = test_cmd . format ( ' x ' , ' /etc/pihole ' , piholeuser )
actual_rc = Pihole . run ( check_etc ) . rc
assert exit_status_success == actual_rc
# readable and writable dhcp.leases
check_leases = test_cmd . format ( ' r ' , ' /etc/pihole/dhcp.leases ' , piholeuser )
actual_rc = Pihole . run ( check_leases ) . rc
assert exit_status_success == actual_rc
check_leases = test_cmd . format ( ' w ' , ' /etc/pihole/dhcp.leases ' , piholeuser )
actual_rc = Pihole . run ( check_leases ) . rc
# readable dns-servers.conf
assert exit_status_success == actual_rc
check_servers = test_cmd . format (
' r ' , ' /etc/pihole/dns-servers.conf ' , piholeuser )
actual_rc = Pihole . run ( check_servers ) . rc
assert exit_status_success == actual_rc
# readable GitHubVersions
check_version = test_cmd . format (
' r ' , ' /etc/pihole/GitHubVersions ' , piholeuser )
actual_rc = Pihole . run ( check_version ) . rc
assert exit_status_success == actual_rc
# readable install.log
check_install = test_cmd . format (
' r ' , ' /etc/pihole/install.log ' , piholeuser )
actual_rc = Pihole . run ( check_install ) . rc
assert exit_status_success == actual_rc
# readable localbranches
check_localbranch = test_cmd . format (
' r ' , ' /etc/pihole/localbranches ' , piholeuser )
actual_rc = Pihole . run ( check_localbranch ) . rc
assert exit_status_success == actual_rc
# readable localversions
check_localversion = test_cmd . format (
' r ' , ' /etc/pihole/localversions ' , piholeuser )
actual_rc = Pihole . run ( check_localversion ) . rc
assert exit_status_success == actual_rc
# readable logrotate
check_logrotate = test_cmd . format (
' r ' , ' /etc/pihole/logrotate ' , piholeuser )
actual_rc = Pihole . run ( check_logrotate ) . rc
assert exit_status_success == actual_rc
# readable macvendor.db
check_macvendor = test_cmd . format (
' r ' , ' /etc/pihole/macvendor.db ' , piholeuser )
actual_rc = Pihole . run ( check_macvendor ) . rc
assert exit_status_success == actual_rc
# readable and writeable pihole-FTL.conf
check_FTLconf = test_cmd . format (
' r ' , ' /etc/pihole/pihole-FTL.conf ' , piholeuser )
actual_rc = Pihole . run ( check_FTLconf ) . rc
assert exit_status_success == actual_rc
check_FTLconf = test_cmd . format (
' w ' , ' /etc/pihole/pihole-FTL.conf ' , piholeuser )
actual_rc = Pihole . run ( check_FTLconf ) . rc
assert exit_status_success == actual_rc
# readable setupVars.conf
check_setup = test_cmd . format (
' r ' , ' /etc/pihole/setupVars.conf ' , piholeuser )
actual_rc = Pihole . run ( check_setup ) . rc
assert exit_status_success == actual_rc
# check dnsmasq files
# readable /etc/dnsmasq.conf
check_dnsmasqconf = test_cmd . format (
' r ' , ' /etc/dnsmasq.conf ' , piholeuser )
actual_rc = Pihole . run ( check_dnsmasqconf ) . rc
assert exit_status_success == actual_rc
# readable /etc/dnsmasq.d/01-pihole.conf
check_dnsmasqconf = test_cmd . format (
' r ' , ' /etc/dnsmasq.d ' , piholeuser )
actual_rc = Pihole . run ( check_dnsmasqconf ) . rc
assert exit_status_success == actual_rc
check_dnsmasqconf = test_cmd . format (
' x ' , ' /etc/dnsmasq.d ' , piholeuser )
actual_rc = Pihole . run ( check_dnsmasqconf ) . rc
assert exit_status_success == actual_rc
check_dnsmasqconf = test_cmd . format (
' r ' , ' /etc/dnsmasq.d/01-pihole.conf ' , piholeuser )
actual_rc = Pihole . run ( check_dnsmasqconf ) . rc
assert exit_status_success == actual_rc
# check readable and executable /etc/init.d/pihole-FTL
check_init = test_cmd . format (
' x ' , ' /etc/init.d/pihole-FTL ' , piholeuser )
actual_rc = Pihole . run ( check_init ) . rc
assert exit_status_success == actual_rc
check_init = test_cmd . format (
' r ' , ' /etc/init.d/pihole-FTL ' , piholeuser )
actual_rc = Pihole . run ( check_init ) . rc
assert exit_status_success == actual_rc
# check readable /etc/lighttpd/lighttpd.conf
check_lighttpd = test_cmd . format (
' r ' , ' /etc/lighttpd/lighttpd.conf ' , piholeuser )
actual_rc = Pihole . run ( check_lighttpd ) . rc
assert exit_status_success == actual_rc
# check readable and executable manpages
if maninstalled is True :
check_man = test_cmd . format (
' x ' , ' /usr/local/share/man ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' x ' , ' /usr/local/share/man/man8 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man/man8 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' x ' , ' /usr/local/share/man/man5 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man/man5 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man/man8/pihole.8 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man/man8/pihole-FTL.8 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
check_man = test_cmd . format (
' r ' , ' /usr/local/share/man/man5/pihole-FTL.conf.5 ' , piholeuser )
actual_rc = Pihole . run ( check_man ) . rc
assert exit_status_success == actual_rc
# check not readable sudoers file
check_sudo = test_cmd . format (
' r ' , ' /etc/sudoers.d/pihole ' , piholeuser )
actual_rc = Pihole . run ( check_sudo ) . rc
assert exit_status_success != actual_rc
# check not readable cron file
check_sudo = test_cmd . format (
' x ' , ' /etc/cron.d/ ' , piholeuser )
actual_rc = Pihole . run ( check_sudo ) . rc
assert exit_status_success == actual_rc
check_sudo = test_cmd . format (
' r ' , ' /etc/cron.d/ ' , piholeuser )
actual_rc = Pihole . run ( check_sudo ) . rc
assert exit_status_success == actual_rc
check_sudo = test_cmd . format (
' r ' , ' /etc/cron.d/pihole ' , piholeuser )
actual_rc = Pihole . run ( check_sudo ) . rc
assert exit_status_success == actual_rc
directories = get_directories_recursive ( Pihole , ' /etc/.pihole/ ' )
for directory in directories :
check_pihole = test_cmd . format ( ' r ' , directory , piholeuser )
actual_rc = Pihole . run ( check_pihole ) . rc
check_pihole = test_cmd . format ( ' x ' , directory , piholeuser )
actual_rc = Pihole . run ( check_pihole ) . rc
findfiles = ' find " {} " -maxdepth 1 -type f -exec echo {{ }} \\ ;; '
filelist = Pihole . run ( findfiles . format ( directory ) )
files = list ( filter ( bool , filelist . stdout . splitlines ( ) ) )
for file in files :
check_pihole = test_cmd . format ( ' r ' , file , piholeuser )
actual_rc = Pihole . run ( check_pihole ) . rc
@pytest.mark.parametrize ( " test_webpage " , [ True ] )
def test_installPihole_fresh_install_readableBlockpage ( Pihole , test_webpage ) :
'''
confirms all web page assets from Core repo are readable
by $ LIGHTTPD_USER on a fresh build
'''
piholeWebpage = [
" 127.0.0.1 " ,
# "pi.hole"
]
# Whiptail dialog returns Cancel for user prompt
mock_command ( ' whiptail ' , { ' * ' : ( ' ' , ' 0 ' ) } , Pihole )
# mock git pull
mock_command_passthrough ( ' git ' , { ' pull ' : ( ' ' , ' 0 ' ) } , Pihole )
# mock systemctl to start lighttpd and FTL
ligthttpdcommand = dedent ( r ''' \ " \ "
echo ' starting lighttpd with {} '
if [ command - v " apt-get " > / dev / null 2 > & 1 ] ; then
LIGHTTPD_USER = " www-data "
LIGHTTPD_GROUP = " www-data "
else
LIGHTTPD_USER = " lighttpd "
LIGHTTPD_GROUP = " lighttpd "
fi
mkdir - p " {run} "
chown { usergroup } " {run} "
mkdir - p " {cache} "
chown { usergroup } " /var/cache "
chown { usergroup } " {cache} "
mkdir - p " {compress} "
chown { usergroup } " {compress} "
mkdir - p " {uploads} "
chown { usergroup } " {uploads} "
chmod 0777 / var
chmod 0777 / var / cache
chmod 0777 " {cache} "
find " {run} " - type d - exec chmod 0777 { chmodarg } \; ;
find " {run} " - type f - exec chmod 0666 { chmodarg } \; ;
find " {compress} " - type d - exec chmod 0777 { chmodarg } \; ;
find " {compress} " - type f - exec chmod 0666 { chmodarg } \; ;
find " {uploads} " - type d - exec chmod 0777 { chmodarg } \; ;
find " {uploads} " - type f - exec chmod 0666 { chmodarg } \; ;
/ usr / sbin / lighttpd - tt - f ' {config} '
/ usr / sbin / lighttpd - f ' {config} '
echo \" \" ' ' ' .format(
' {} ' ,
usergroup = ' $ {{ LIGHTTPD_USER}}:$ {{ LIGHTTPD_GROUP}} ' ,
chmodarg = ' {{ }} ' ,
config = ' /etc/lighttpd/lighttpd.conf ' ,
run = ' /var/run/lighttpd ' ,
cache = ' /var/cache/lighttpd ' ,
uploads = ' /var/cache/lighttpd/uploads ' ,
compress = ' /var/cache/lighttpd/compress '
)
)
FTLcommand = dedent ( ''' \" \"
set - x
/ etc / init . d / pihole - FTL restart
echo \" \" ' ' ' )
mock_command_run (
' systemctl ' ,
{
' enable lighttpd ' : (
' ' ,
' 0 '
) ,
' restart lighttpd ' : (
ligthttpdcommand . format ( ' restart ' ) ,
' 0 '
) ,
' start lighttpd ' : (
ligthttpdcommand . format ( ' start ' ) ,
' 0 '
) ,
' enable pihole-FTL ' : (
' ' ,
' 0 '
) ,
' restart pihole-FTL ' : (
FTLcommand ,
' 0 '
) ,
' start pihole-FTL ' : (
FTLcommand ,
' 0 '
) ,
' * ' : (
' echo " systemctl call with $@ " ' ,
' 0 '
) ,
} ,
Pihole
)
# create configuration file
setup_var_file = ' cat <<EOF> /etc/pihole/setupVars.conf \n '
for k , v in SETUPVARS . items ( ) :
setup_var_file + = " {} = {} \n " . format ( k , v )
setup_var_file + = " INSTALL_WEB_SERVER=true \n "
setup_var_file + = " INSTALL_WEB_INTERFACE=true \n "
setup_var_file + = " IPV4_ADDRESS=127.0.0.1 \n "
setup_var_file + = " EOF \n "
Pihole . run ( setup_var_file )
installWeb = Pihole . run ( '''
export TERM = xterm
export DEBIAN_FRONTEND = noninteractive
umask 0027
runUnattended = true
useUpdateVars = true
source / opt / pihole / basic - install . sh > / dev / null
runUnattended = true
useUpdateVars = true
main
echo " LIGHTTPD_USER=$ {LIGHTTPD_USER} "
echo " webroot=$ {webroot} "
echo " INSTALL_WEB_INTERFACE=$ {INSTALL_WEB_INTERFACE} "
echo " INSTALL_WEB_SERVER=$ {INSTALL_WEB_SERVER} "
''' )
assert 0 == installWeb . rc
piholeuser = ' pihole '
webuser = ' '
user = re . findall (
r " ^ \ s*LIGHTTPD_USER=.*$ " , installWeb . stdout , re . MULTILINE )
for match in user :
webuser = match . replace ( ' LIGHTTPD_USER= ' , ' ' ) . strip ( )
webroot = ' '
user = re . findall (
r " ^ \ s*webroot=.*$ " , installWeb . stdout , re . MULTILINE )
for match in user :
webroot = match . replace ( ' webroot= ' , ' ' ) . strip ( )
if not webroot . strip ( ) :
webroot = ' /var/www/html '
installWebInterface = True
interface = re . findall (
r " ^ \ s*INSTALL_WEB_INTERFACE=.*$ " , installWeb . stdout , re . MULTILINE )
for match in interface :
testvalue = match . replace ( ' INSTALL_WEB_INTERFACE= ' , ' ' ) . strip ( ) . lower ( )
if not testvalue . strip ( ) :
installWebInterface = testvalue == " true "
installWebServer = True
server = re . findall (
r " ^ \ s*INSTALL_WEB_SERVER=.*$ " , installWeb . stdout , re . MULTILINE )
for match in server :
testvalue = match . replace ( ' INSTALL_WEB_SERVER= ' , ' ' ) . strip ( ) . lower ( )
if not testvalue . strip ( ) :
installWebServer = testvalue == " true "
# if webserver install was not requested
# at least pihole must be able to read files
if installWebServer is False :
webuser = piholeuser
exit_status_success = 0
test_cmd = ' su --shell /bin/bash --command " test - {0} {1} " -p {2} '
# check files that need a running FTL to be created
# readable and writeable pihole-FTL.db
check_FTLconf = test_cmd . format (
' r ' , ' /etc/pihole/pihole-FTL.db ' , piholeuser )
actual_rc = Pihole . run ( check_FTLconf ) . rc
assert exit_status_success == actual_rc
check_FTLconf = test_cmd . format (
' w ' , ' /etc/pihole/pihole-FTL.db ' , piholeuser )
actual_rc = Pihole . run ( check_FTLconf ) . rc
assert exit_status_success == actual_rc
# check directories above $webroot for read and execute permission
check_var = test_cmd . format ( ' r ' , ' /var ' , webuser )
actual_rc = Pihole . run ( check_var ) . rc
assert exit_status_success == actual_rc
check_var = test_cmd . format ( ' x ' , ' /var ' , webuser )
actual_rc = Pihole . run ( check_var ) . rc
assert exit_status_success == actual_rc
check_www = test_cmd . format ( ' r ' , ' /var/www ' , webuser )
actual_rc = Pihole . run ( check_www ) . rc
assert exit_status_success == actual_rc
check_www = test_cmd . format ( ' x ' , ' /var/www ' , webuser )
actual_rc = Pihole . run ( check_www ) . rc
assert exit_status_success == actual_rc
check_html = test_cmd . format ( ' r ' , ' /var/www/html ' , webuser )
actual_rc = Pihole . run ( check_html ) . rc
assert exit_status_success == actual_rc
check_html = test_cmd . format ( ' x ' , ' /var/www/html ' , webuser )
actual_rc = Pihole . run ( check_html ) . rc
assert exit_status_success == actual_rc
# check directories below $webroot for read and execute permission
check_admin = test_cmd . format ( ' r ' , webroot + ' /admin ' , webuser )
actual_rc = Pihole . run ( check_admin ) . rc
assert exit_status_success == actual_rc
check_admin = test_cmd . format ( ' x ' , webroot + ' /admin ' , webuser )
actual_rc = Pihole . run ( check_admin ) . rc
assert exit_status_success == actual_rc
directories = get_directories_recursive ( Pihole , webroot + ' /admin/*/ ' )
for directory in directories :
check_pihole = test_cmd . format ( ' r ' , directory , webuser )
actual_rc = Pihole . run ( check_pihole ) . rc
check_pihole = test_cmd . format ( ' x ' , directory , webuser )
actual_rc = Pihole . run ( check_pihole ) . rc
findfiles = ' find " {} " -maxdepth 1 -type f -exec echo {{ }} \\ ;; '
filelist = Pihole . run ( findfiles . format ( directory ) )
files = list ( filter ( bool , filelist . stdout . splitlines ( ) ) )
for file in files :
check_pihole = test_cmd . format ( ' r ' , file , webuser )
actual_rc = Pihole . run ( check_pihole ) . rc
# check web interface files
# change nameserver to pi-hole
# setting nameserver in /etc/resolv.conf to pi-hole does
# not work here because of the way docker uses this file
ns = Pihole . run (
r " sed -i ' s/nameserver.*/nameserver 127.0.0.1/ ' /etc/resolv.conf " )
pihole_is_ns = ns . rc == 0
def is_ip ( address ) :
m = re . match ( r " ( \ d { 1,3}) \ .( \ d { 1,3}) \ .( \ d { 1,3}) \ .( \ d { 1,3}) " , address )
return bool ( m )
if installWebInterface is True :
check_pihole = test_cmd . format ( ' r ' , webroot + ' /pihole ' , webuser )
actual_rc = Pihole . run ( check_pihole ) . rc
assert exit_status_success == actual_rc
check_pihole = test_cmd . format ( ' x ' , webroot + ' /pihole ' , webuser )
actual_rc = Pihole . run ( check_pihole ) . rc
assert exit_status_success == actual_rc
# check most important files in $webroot for read permission
check_index = test_cmd . format (
' r ' , webroot + ' /pihole/index.php ' , webuser )
actual_rc = Pihole . run ( check_index ) . rc
assert exit_status_success == actual_rc
check_blockpage = test_cmd . format (
' r ' , webroot + ' /pihole/blockingpage.css ' , webuser )
actual_rc = Pihole . run ( check_blockpage ) . rc
assert exit_status_success == actual_rc
if test_webpage is True :
# check webpage for unreadable files
noPHPfopen = re . compile (
( r " PHP Error( %d +): \ s+fopen([^)]+): \ s+ " +
r " failed to open stream: " +
r " Permission denied in " ) ,
re . I )
# using cURL option --dns-servers is not possible
status = (
' curl -s --head " {} " | ' +
' head -n 1 | ' +
' grep " HTTP/1.[01] [23].. " > /dev/null ' )
digcommand = r " dig A +short {} @127.0.0.1 | head -n 1 "
pagecontent = ' curl --verbose -L " {} " '
for page in piholeWebpage :
testpage = " http:// " + page + " /admin/ "
resolvesuccess = True
if is_ip ( page ) is False :
dig = Pihole . run ( digcommand . format ( page ) )
testpage = " http:// " + dig . stdout . strip ( ) + " /admin/ "
resolvesuccess = dig . rc == 0
if resolvesuccess or pihole_is_ns :
# check HTTP status of blockpage
actual_rc = Pihole . run ( status . format ( testpage ) )
assert exit_status_success == actual_rc . rc
# check for PHP error
actual_output = Pihole . run ( pagecontent . format ( testpage ) )
assert noPHPfopen . match ( actual_output . stdout ) is None
def test_update_package_cache_success_no_errors ( Pihole ) :
def test_update_package_cache_success_no_errors ( Pihole ) :
'''
'''
confirms package cache was updated without any errors
confirms package cache was updated without any errors