1
0
mirror of https://github.com/pi-hole/pi-hole synced 2024-12-21 14:28:07 +00:00

unit test for umask problems in #3177 and #2730 (#3191)

* add test for file permissions of $webroot

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* changes sudo to su for running command as user www-data

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* installs PIHOLE_WEB_DEPS to create LIGHTTPD_USER

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* changes stdout to rc

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* use installPihole instead of installPiholeWeb in test

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* try installation process with main

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* mock systemctl

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* removes stickler errors

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* start lighttpd and make webpage test optional

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* test all files and directories in $webroot

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* fix stickler and codefactor warnings

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* set permission for /var/cache if it did not exist before

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* add test case for pihole files

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* fix stickler errors

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* revert "set permission for /var/cache if it did not exist before" and make lighttpd start work

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* add --add-cap=NET_ADMIN to enable FTL start

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* specify DNS server for cURL

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* check files created by FTL

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* reorder code and change nameserver in /etc/resolv.conf

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* resolve with dig instead of relying on /etc/resolv.conf

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* set IP to 127.0.0.1 in setupVars.conf for blockpage tests

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* resolve domain with dig and remove debug output

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* fix stickler errors

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* no git pull in Github Action runs for pull requests

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* --cap-add=ALL test

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* fix stickler errors

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* remove debug code

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* update_repo patch for CentOS 7 in Github Actions

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* removes TODOs and stickler warnings

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* adds trailing slash to domain

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* use only first result from dig

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* domain name resolution does not work reliably in docker container

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* repair executable permission

Signed-off-by: pvogt09 <50047961+pvogt09@users.noreply.github.com>

* Create mock_command_passthrough that allows intercepting of specific arguments - everything else is passed through to the proper command. Use this new command instead of making changes in basic-install.sh to make the tests pass.

Signed-off-by: Adam Warner <me@adamwarner.co.uk>

Co-authored-by: Adam Warner <me@adamwarner.co.uk>
This commit is contained in:
pvogt09 2021-11-11 17:44:57 +01:00 committed by GitHub
parent ac4a975be5
commit cedd1a2591
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 581 additions and 6 deletions

View File

@ -18,8 +18,8 @@ py.test -vv -n auto -m "build_stage"
py.test -vv -n auto -m "not build_stage"
```
The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change.
The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change.
# How do I debug python?
Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :)
Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :)

View File

@ -64,7 +64,7 @@ def args(request):
'''
-t became required when tput began being used
'''
return '-t -d'
return '-t -d --cap-add=ALL'
@pytest.fixture(params=[
@ -100,7 +100,7 @@ def mock_command(script, args, container):
in unit tests
'''
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent('''\
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script))
@ -121,13 +121,75 @@ def mock_command(script, args, container):
scriptlog=script))
def mock_command_passthrough(script, args, container):
'''
Per other mock_command* functions, allows intercepting of commands we don't want to run for real
in unit tests, however also allows only specific arguments to be mocked. Anything not defined will
be passed through to the actual command.
Example use-case: mocking `git pull` but still allowing `git clone` to work as intended
'''
orig_script_path = check_output('which {}'.format(script))
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script))
for k, v in args.items():
case = dedent('''
{arg})
echo {res}
exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1]))
mock_script += case
mock_script += dedent(r'''
*)
{orig_script_path} "\$@"
;;'''.format(orig_script_path=orig_script_path))
mock_script += dedent('''
esac''')
container.run('''
cat <<EOF> {script}\n{content}\nEOF
chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
content=mock_script,
scriptlog=script))
def mock_command_run(script, args, container):
'''
Allows for setup of commands we don't really want to have to run for real
in unit tests
'''
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script))
for k, v in args.items():
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}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
content=mock_script,
scriptlog=script))
def mock_command_2(script, args, container):
'''
Allows for setup of commands we don't really want to have to run for real
in unit tests
'''
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent('''\
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script))
@ -147,7 +209,6 @@ def mock_command_2(script, args, container):
content=mock_script,
scriptlog=script))
def run_script(Pihole, script):
result = Pihole.run(script)
assert result.rc == 0

View File

@ -1,3 +1,4 @@
import pytest
from textwrap import dedent
import re
from .conftest import (
@ -6,7 +7,9 @@ from .conftest import (
info_box,
cross_box,
mock_command,
mock_command_run,
mock_command_2,
mock_command_passthrough,
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
'''
installWeb = Pihole.run('''
umask 0027
source /opt/pihole/basic-install.sh
installPiholeWeb
''')
@ -129,6 +133,516 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
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):
'''
confirms package cache was updated without any errors