From c1eb35a35e7afd212205c317ee229b968d020734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Tue, 26 Oct 2021 22:46:52 +0200 Subject: [PATCH 01/36] Add custom.list (Local DNS Records) to debug log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/Scripts/piholeDebug.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index cd615825..f033739a 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -88,6 +88,7 @@ PIHOLE_LOCAL_HOSTS_FILE="${PIHOLE_DIRECTORY}/local.list" PIHOLE_LOGROTATE_FILE="${PIHOLE_DIRECTORY}/logrotate" PIHOLE_SETUP_VARS_FILE="${PIHOLE_DIRECTORY}/setupVars.conf" PIHOLE_FTL_CONF_FILE="${PIHOLE_DIRECTORY}/pihole-FTL.conf" +PIHOLE_CUSTOM_HOSTS_FILE="${PIHOLE_DIRECTORY}/custom.list" # Read the value of an FTL config key. The value is printed to stdout. # @@ -179,7 +180,8 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}" "${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}" "${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}" "${RESOLVCONF}" -"${DNSMASQ_CONF}") +"${DNSMASQ_CONF}" +"${PIHOLE_CUSTOM_HOSTS_FILE}") DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net. From c6da1a39183573261642fdc38555cebcf9f753c0 Mon Sep 17 00:00:00 2001 From: David Irvine Date: Thu, 28 Oct 2021 12:09:34 +0200 Subject: [PATCH 02/36] Fix documentation; add some missing zones Signed-off-by: David Irvine --- advanced/06-rfc6761.conf | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/advanced/06-rfc6761.conf b/advanced/06-rfc6761.conf index e03569e8..fcdd0010 100644 --- a/advanced/06-rfc6761.conf +++ b/advanced/06-rfc6761.conf @@ -25,11 +25,12 @@ server=/localhost/ server=/invalid/ # The same RFC requests something similar for -# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa. -# 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa. -# 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa. -# 19.172.in-addr.arpa. 24.172.in-addr.arpa. 31.172.in-addr.arpa. -# 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa. +# 10.in-addr.arpa. 21.172.in-addr.arpa. 27.172.in-addr.arpa. +# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 28.172.in-addr.arpa. +# 17.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa. +# 18.172.in-addr.arpa. 24.172.in-addr.arpa. 30.172.in-addr.arpa. +# 19.172.in-addr.arpa. 25.172.in-addr.arpa. 31.172.in-addr.arpa. +# 20.172.in-addr.arpa. 26.172.in-addr.arpa. 168.192.in-addr.arpa. # Pi-hole implements this via the dnsmasq option "bogus-priv" (see # 01-pihole.conf) because this also covers IPv6. From e73355329542de074caace35fb38908343346955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Tue, 2 Nov 2021 21:22:14 +0100 Subject: [PATCH 03/36] Remove unused code from webpage.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/Scripts/webpage.sh | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index a739d898..2a406565 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -532,25 +532,6 @@ CustomizeAdLists() { fi } -SetPrivacyMode() { - if [[ "${args[2]}" == "true" ]]; then - change_setting "API_PRIVACY_MODE" "true" - else - change_setting "API_PRIVACY_MODE" "false" - fi -} - -ResolutionSettings() { - typ="${args[2]}" - state="${args[3]}" - - if [[ "${typ}" == "forward" ]]; then - change_setting "API_GET_UPSTREAM_DNS_HOSTNAME" "${state}" - elif [[ "${typ}" == "clients" ]]; then - change_setting "API_GET_CLIENT_HOSTNAME" "${state}" - fi -} - AddDHCPStaticAddress() { mac="${args[2]}" ip="${args[3]}" @@ -829,8 +810,6 @@ main() { "layout" ) SetWebUILayout;; "theme" ) SetWebUITheme;; "-h" | "--help" ) helpFunc;; - "privacymode" ) SetPrivacyMode;; - "resolve" ) ResolutionSettings;; "addstaticdhcp" ) AddDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;; "-e" | "email" ) SetAdminEmail "$3";; From 996f8fff28237d916ab44f7f039202ac32ccd75d Mon Sep 17 00:00:00 2001 From: yubiuser Date: Thu, 4 Nov 2021 23:55:16 +0100 Subject: [PATCH 04/36] Recommend apt instead of apt-get if updating the package cache failed (#4421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Only change the recommendation to use apt Signed-off-by: Christian König --- automated install/basic-install.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index babb8213..0f9f8cc0 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1481,8 +1481,14 @@ update_package_cache() { printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" else # Otherwise, show an error and exit + + # In case we used apt-get and apt is also available, we use this as recommendation as we have seen it + # gives more user-friendly (interactive) advice + if [[ ${PKG_MANAGER} == "apt-get" ]] && is_command apt ; then + UPDATE_PKG_CACHE="apt update" + fi printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" - printf " %bError: Unable to update package cache. Please try \"%s\"%b" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}" + printf " %bError: Unable to update package cache. Please try \"%s\"%b\\n" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}" return 1 fi } From ac4a975be55a3533d6425eac72026d5b94dda5f1 Mon Sep 17 00:00:00 2001 From: yubiuser Date: Sat, 6 Nov 2021 21:32:03 +0100 Subject: [PATCH 05/36] Allow users to skip setting static IP adress (#4419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow users to skip setting static IP adresss Signed-off-by: Christian König --- automated install/basic-install.sh | 85 +++++++++++++++++------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 0f9f8cc0..c806a6ae 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -761,7 +761,6 @@ collect_v4andv6_information() { if [[ -f "/etc/dhcpcd.conf" ]]; then # configure networking via dhcpcd getStaticIPv4Settings - setDHCPCD fi find_IPv6_information printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}" @@ -770,47 +769,59 @@ collect_v4andv6_information() { getStaticIPv4Settings() { # Local, named variables local ipSettingsCorrect + local DHCPChoice # Ask if the user wants to use DHCP settings as their static IP # This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions - 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 + DHCPChoice=$(whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --menu --separate-output "Do you want to use your current network settings as a static address? \\n + IP address: ${IPV4_ADDRESS} \\n + Gateway: ${IPv4gw} \\n" "${r}" "${c}" 3\ + "Yes" "Set static IP using current values" \ + "No" "Set static IP using custom values" \ + "Skip" "I will set a static IP later, or have already done so" 3>&2 2>&1 1>&3) || \ + { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } + + case ${DHCPChoice} in + "Yes") # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. -If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. -It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." "${r}" "${c}" - # Nothing else to do since the variables are already set above - else - # Otherwise, we need to ask the user to input their desired settings. - # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) - # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ "${ipSettingsCorrect}" = True ]]; do + If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. + It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." "${r}" "${c}" + # Nothing else to do since the variables are already set above + setDHCPCD + ;; - # 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) || \ - # Canceling IPv4 settings window - { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } - printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" + "No") + # Otherwise, we need to ask the user to input their desired settings. + # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) + # Start a loop to let the user enter their information with the chance to go back and edit it if necessary + until [[ "${ipSettingsCorrect}" = True ]]; do - # Ask for the 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) || \ - # Canceling gateway settings window - { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } - printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}" + # 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) || \ + # Canceling IPv4 settings window + { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" - # 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 - done - # End the if statement for DHCP vs. static - fi + # 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) || \ + # Canceling gateway settings window + { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${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 + done + setDHCPCD + ;; + esac } # Configure networking via dhcpcd @@ -2475,12 +2486,12 @@ main() { get_available_interfaces # Find interfaces and let the user choose one chooseInterface + # find IPv4 and IPv6 information of the device + collect_v4andv6_information # Decide what upstream DNS Servers to use setDNS # Give the user a choice of blocklists to include in their install. Or not. chooseBlocklists - # find IPv4 and IPv6 information of the device - collect_v4andv6_information # Let the user decide if they want the web interface to be installed automatically setAdminFlag # Let the user decide if they want query logging enabled... From cedd1a259105dc231744d58562f0bc80d4294c7a Mon Sep 17 00:00:00 2001 From: pvogt09 <50047961+pvogt09@users.noreply.github.com> Date: Thu, 11 Nov 2021 17:44:57 +0100 Subject: [PATCH 06/36] 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 Co-authored-by: Adam Warner --- test/README.md | 4 +- test/conftest.py | 69 ++++- test/test_automated_install.py | 514 +++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+), 6 deletions(-) diff --git a/test/README.md b/test/README.md index b4dd1122..692155b7 100644 --- a/test/README.md +++ b/test/README.md @@ -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 :) diff --git a/test/conftest.py b/test/conftest.py index 13731eb8..2d388fdd 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -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 < {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 < {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 diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 37ebdad2..304a8783 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -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 < /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 < /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 From cdd4d9ea9e51c8110df832d5fadeb44bc90b5085 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Thu, 18 Nov 2021 01:03:37 +0000 Subject: [PATCH 07/36] Update the tests (#4427) * unpin the requirements and update all to latest available - needs more work still. see notes in `def host()` Signed-off-by: Adam Warner * fix py3 monkey patch of testinfra docker using bash Signed-off-by: Adam Hill * update the other test files to use `host` instead of `Pihole` Address some sticklr and codefactor update python version from 3.7 to 3.8 preload `git` onto the centos/fedora test images, and switch which with command -v in the passthrough mock testinfra is deprecated, use pytest-testinfra Signed-off-by: Adam Warner Co-authored-by: Adam Hill --- .github/workflows/test.yml | 4 +- .gitignore | 1 + test/_centos_7.Dockerfile | 1 + test/_centos_8.Dockerfile | 1 + test/_fedora_33.Dockerfile | 1 + test/_fedora_34.Dockerfile | 1 + test/conftest.py | 101 ++----- test/requirements.txt | 12 +- test/test_automated_install.py | 339 +++++++++++----------- test/test_centos_7_support.py | 22 +- test/test_centos_8_support.py | 22 +- test/test_centos_common_support.py | 48 +-- test/test_centos_fedora_common_support.py | 24 +- test/test_fedora_support.py | 8 +- test/tox.centos_7.ini | 2 +- test/tox.centos_8.ini | 2 +- test/tox.debian_10.ini | 2 +- test/tox.debian_11.ini | 2 +- test/tox.debian_9.ini | 2 +- test/tox.fedora_33.ini | 2 +- test/tox.fedora_34.ini | 2 +- test/tox.ubuntu_16.ini | 2 +- test/tox.ubuntu_18.ini | 2 +- test/tox.ubuntu_20.ini | 2 +- test/tox.ubuntu_21.ini | 2 +- 25 files changed, 285 insertions(+), 322 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49f139e1..17557a87 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,10 +36,10 @@ jobs: name: Checkout repository uses: actions/checkout@v2 - - name: Set up Python 3.7 + name: Set up Python 3.8 uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: pip install -r test/requirements.txt diff --git a/.gitignore b/.gitignore index c19555ed..8016472b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ __pycache__ *.egg-info .idea/ *.iml +.vscode/ diff --git a/test/_centos_7.Dockerfile b/test/_centos_7.Dockerfile index 434242bf..355f4fdb 100644 --- a/test/_centos_7.Dockerfile +++ b/test/_centos_7.Dockerfile @@ -1,4 +1,5 @@ FROM centos:7 +RUN yum install -y git ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/_centos_8.Dockerfile b/test/_centos_8.Dockerfile index afd2dc8a..fddb3ed1 100644 --- a/test/_centos_8.Dockerfile +++ b/test/_centos_8.Dockerfile @@ -1,4 +1,5 @@ FROM centos:8 +RUN yum install -y git ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/_fedora_33.Dockerfile b/test/_fedora_33.Dockerfile index 9ae94c70..5cdd66ee 100644 --- a/test/_fedora_33.Dockerfile +++ b/test/_fedora_33.Dockerfile @@ -1,4 +1,5 @@ FROM fedora:33 +RUN dnf install -y git ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/_fedora_34.Dockerfile b/test/_fedora_34.Dockerfile index 96de18da..fbbaacd6 100644 --- a/test/_fedora_34.Dockerfile +++ b/test/_fedora_34.Dockerfile @@ -1,4 +1,5 @@ FROM fedora:34 +RUN dnf install -y git ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/conftest.py b/test/conftest.py index 2d388fdd..fb7e1eea 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,10 +1,9 @@ import pytest import testinfra +import testinfra.backend.docker +import subprocess from textwrap import dedent -check_output = testinfra.get_backend( - "local://" -).get_module("Command").check_output SETUPVARS = { 'PIHOLE_INTERFACE': 'eth99', @@ -12,85 +11,42 @@ SETUPVARS = { 'PIHOLE_DNS_2': '4.2.2.2' } +IMAGE = 'pytest_pihole:test_container' + tick_box = "[\x1b[1;32m\u2713\x1b[0m]" cross_box = "[\x1b[1;31m\u2717\x1b[0m]" info_box = "[i]" -@pytest.fixture -def Pihole(Docker): - ''' - used to contain some script stubbing, now pretty much an alias. - Also provides bash as the default run function shell - ''' - def run_bash(self, command, *args, **kwargs): - cmd = self.get_command(command, *args) - if self.user is not None: - out = self.run_local( - "docker exec -u %s %s /bin/bash -c %s", - self.user, self.name, cmd) - else: - out = self.run_local( - "docker exec %s /bin/bash -c %s", self.name, cmd) - out.command = self.encode(cmd) - return out +# Monkeypatch sh to bash, if they ever support non hard code /bin/sh this can go away +# https://github.com/pytest-dev/pytest-testinfra/blob/master/testinfra/backend/docker.py +def run_bash(self, command, *args, **kwargs): + cmd = self.get_command(command, *args) + if self.user is not None: + out = self.run_local( + "docker exec -u %s %s /bin/bash -c %s", self.user, self.name, cmd + ) + else: + out = self.run_local("docker exec %s /bin/bash -c %s", self.name, cmd) + out.command = self.encode(cmd) + return out - funcType = type(Docker.run) - Docker.run = funcType(run_bash, Docker) - return Docker + +testinfra.backend.docker.DockerBackend.run = run_bash @pytest.fixture -def Docker(request, args, image, cmd): - ''' - combine our fixtures into a docker run command and setup finalizer to - cleanup - ''' - assert 'docker' in check_output('id'), "Are you in the docker group?" - docker_run = "docker run {} {} {}".format(args, image, cmd) - docker_id = check_output(docker_run) +def host(): + # run a container + docker_id = subprocess.check_output( + ['docker', 'run', '-t', '-d', '--cap-add=ALL', IMAGE]).decode().strip() - def teardown(): - check_output("docker rm -f %s", docker_id) - request.addfinalizer(teardown) + # return a testinfra connection to the container + docker_host = testinfra.get_host("docker://" + docker_id) - docker_container = testinfra.get_backend("docker://" + docker_id) - docker_container.id = docker_id - return docker_container - - -@pytest.fixture -def args(request): - ''' - -t became required when tput began being used - ''' - return '-t -d --cap-add=ALL' - - -@pytest.fixture(params=[ - 'test_container' -]) -def tag(request): - ''' - consumed by image to make the test matrix - ''' - return request.param - - -@pytest.fixture() -def image(request, tag): - ''' - built by test_000_build_containers.py - ''' - return 'pytest_pihole:{}'.format(tag) - - -@pytest.fixture() -def cmd(request): - ''' - default to doing nothing by tailing null, but don't exit - ''' - return 'tail -f /dev/null' + yield docker_host + # at the end of the test suite, destroy the container + subprocess.check_call(['docker', 'rm', '-f', docker_id]) # Helper functions @@ -129,7 +85,7 @@ def mock_command_passthrough(script, args, container): Example use-case: mocking `git pull` but still allowing `git clone` to work as intended ''' - orig_script_path = check_output('which {}'.format(script)) + orig_script_path = container.check_output('command -v {}'.format(script)) full_script_path = '/usr/local/bin/{}'.format(script) mock_script = dedent(r'''\ #!/bin/bash -e @@ -209,6 +165,7 @@ 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 diff --git a/test/requirements.txt b/test/requirements.txt index 17d62ad9..d65ee6a5 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,6 +1,6 @@ -docker-compose==1.23.2 -pytest==4.3.0 -pytest-xdist==1.26.1 -pytest-cov==2.6.1 -testinfra==1.19.0 -tox==3.7.0 +docker-compose +pytest +pytest-xdist +pytest-cov +pytest-testinfra +tox diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 304a8783..911407fc 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -14,14 +14,14 @@ from .conftest import ( ) -def test_supported_package_manager(Pihole): +def test_supported_package_manager(host): ''' confirm installer exits when no supported package manager found ''' # break supported package managers - Pihole.run('rm -rf /usr/bin/apt-get') - Pihole.run('rm -rf /usr/bin/rpm') - package_manager_detect = Pihole.run(''' + host.run('rm -rf /usr/bin/apt-get') + host.run('rm -rf /usr/bin/rpm') + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect ''') @@ -30,7 +30,7 @@ def test_supported_package_manager(Pihole): # assert package_manager_detect.rc == 1 -def test_setupVars_are_sourced_to_global_scope(Pihole): +def test_setupVars_are_sourced_to_global_scope(host): ''' currently update_dialogs sources setupVars with a dot, then various other functions use the variables. @@ -40,7 +40,7 @@ def test_setupVars_are_sourced_to_global_scope(Pihole): for k, v in SETUPVARS.items(): setup_var_file += "{}={}\n".format(k, v) setup_var_file += "EOF\n" - Pihole.run(setup_var_file) + host.run(setup_var_file) script = dedent('''\ set -e @@ -58,13 +58,13 @@ def test_setupVars_are_sourced_to_global_scope(Pihole): printSetupVars ''') - output = run_script(Pihole, script).stdout + output = run_script(host, script).stdout for k, v in SETUPVARS.items(): assert "{}={}".format(k, v) in output -def test_setupVars_saved_to_file(Pihole): +def test_setupVars_saved_to_file(host): ''' confirm saved settings are written to a file for future updates to re-use ''' @@ -72,7 +72,7 @@ def test_setupVars_saved_to_file(Pihole): set_setup_vars = '\n' for k, v in SETUPVARS.items(): set_setup_vars += " {}={}\n".format(k, v) - Pihole.run(set_setup_vars).stdout + host.run(set_setup_vars) script = dedent('''\ set -e @@ -87,17 +87,17 @@ def test_setupVars_saved_to_file(Pihole): cat /etc/pihole/setupVars.conf '''.format(set_setup_vars)) - output = run_script(Pihole, script).stdout + output = run_script(host, script).stdout for k, v in SETUPVARS.items(): assert "{}={}".format(k, v) in output -def test_selinux_not_detected(Pihole): +def test_selinux_not_detected(host): ''' confirms installer continues when SELinux configuration file does not exist ''' - check_selinux = Pihole.run(''' + check_selinux = host.run(''' rm -f /etc/selinux/config source /opt/pihole/basic-install.sh checkSelinux @@ -107,11 +107,11 @@ def test_selinux_not_detected(Pihole): assert check_selinux.rc == 0 -def test_installPiholeWeb_fresh_install_no_errors(Pihole): +def test_installPiholeWeb_fresh_install_no_errors(host): ''' confirms all web page assets from Core repo are installed on a fresh build ''' - installWeb = Pihole.run(''' + installWeb = host.run(''' umask 0027 source /opt/pihole/basic-install.sh installPiholeWeb @@ -128,19 +128,19 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole): assert expected_stdout in installWeb.stdout expected_stdout = tick_box + ' Installing sudoer file' assert expected_stdout in installWeb.stdout - web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout + web_directory = host.run('ls -r /var/www/html/pihole').stdout assert 'index.php' in web_directory assert 'blockingpage.css' in web_directory -def get_directories_recursive(Pihole, directory): +def get_directories_recursive(host, directory): if directory is None: return directory - ls = Pihole.run('ls -d {}'.format(directory + '/*/')) + ls = host.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) + dir_rec = get_directories_recursive(host, dirval) if isinstance(dir_rec, str): dirs.extend([dir_rec]) else: @@ -148,14 +148,14 @@ def get_directories_recursive(Pihole, directory): return dirs -def test_installPihole_fresh_install_readableFiles(Pihole): +def test_installPihole_fresh_install_readableFiles(host): ''' confirms all neccessary files are readable by pihole user ''' # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) + mock_command('whiptail', {'*': ('', '0')}, host) # mock git pull - mock_command_passthrough('git', {'pull': ('', '0')}, Pihole) + mock_command_passthrough('git', {'pull': ('', '0')}, host) # mock systemctl to not start lighttpd and FTL mock_command_2( 'systemctl', @@ -189,12 +189,12 @@ def test_installPihole_fresh_install_readableFiles(Pihole): '0' ), }, - Pihole + host ) # 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') + host.run('command -v apt-get > /dev/null && apt-get install -qq man') + host.run('command -v dnf > /dev/null && dnf install -y man') + host.run('command -v yum > /dev/null && yum install -y man') # create configuration file setup_var_file = 'cat < /etc/pihole/setupVars.conf\n' for k, v in SETUPVARS.items(): @@ -202,8 +202,8 @@ def test_installPihole_fresh_install_readableFiles(Pihole): 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(''' + host.run(setup_var_file) + install = host.run(''' export TERM=xterm export DEBIAN_FRONTEND=noninteractive umask 0027 @@ -223,172 +223,172 @@ def test_installPihole_fresh_install_readableFiles(Pihole): 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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.run(check_sudo).rc assert exit_status_success == actual_rc - directories = get_directories_recursive(Pihole, '/etc/.pihole/') + directories = get_directories_recursive(host, '/etc/.pihole/') for directory in directories: check_pihole = test_cmd.format('r', directory, piholeuser) - actual_rc = Pihole.run(check_pihole).rc + actual_rc = host.run(check_pihole).rc check_pihole = test_cmd.format('x', directory, piholeuser) - actual_rc = Pihole.run(check_pihole).rc + actual_rc = host.run(check_pihole).rc findfiles = 'find "{}" -maxdepth 1 -type f -exec echo {{}} \\;;' - filelist = Pihole.run(findfiles.format(directory)) + filelist = host.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 + actual_rc = host.run(check_pihole).rc @pytest.mark.parametrize("test_webpage", [True]) -def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): +def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): ''' confirms all web page assets from Core repo are readable by $LIGHTTPD_USER on a fresh build @@ -398,9 +398,10 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): # "pi.hole" ] # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) + mock_command('whiptail', {'*': ('', '0')}, host) + # mock git pull - mock_command_passthrough('git', {'pull': ('', '0')}, Pihole) + mock_command_passthrough('git', {'pull': ('', '0')}, host) # mock systemctl to start lighttpd and FTL ligthttpdcommand = dedent(r'''\"\" echo 'starting lighttpd with {}' @@ -478,7 +479,7 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): '0' ), }, - Pihole + host ) # create configuration file setup_var_file = 'cat < /etc/pihole/setupVars.conf\n' @@ -488,8 +489,8 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): 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(''' + host.run(setup_var_file) + installWeb = host.run(''' export TERM=xterm export DEBIAN_FRONTEND=noninteractive umask 0027 @@ -542,55 +543,55 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): # 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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.run(check_admin).rc assert exit_status_success == actual_rc - directories = get_directories_recursive(Pihole, webroot + '/admin/*/') + directories = get_directories_recursive(host, webroot + '/admin/*/') for directory in directories: check_pihole = test_cmd.format('r', directory, webuser) - actual_rc = Pihole.run(check_pihole).rc + actual_rc = host.run(check_pihole).rc check_pihole = test_cmd.format('x', directory, webuser) - actual_rc = Pihole.run(check_pihole).rc + actual_rc = host.run(check_pihole).rc findfiles = 'find "{}" -maxdepth 1 -type f -exec echo {{}} \\;;' - filelist = Pihole.run(findfiles.format(directory)) + filelist = host.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 + actual_rc = host.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( + ns = host.run( r"sed -i 's/nameserver.*/nameserver 127.0.0.1/' /etc/resolv.conf") pihole_is_ns = ns.rc == 0 @@ -599,19 +600,19 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): return bool(m) if installWebInterface is True: check_pihole = test_cmd.format('r', webroot + '/pihole', webuser) - actual_rc = Pihole.run(check_pihole).rc + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.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 + actual_rc = host.run(check_blockpage).rc assert exit_status_success == actual_rc if test_webpage is True: # check webpage for unreadable files @@ -631,23 +632,23 @@ def test_installPihole_fresh_install_readableBlockpage(Pihole, test_webpage): testpage = "http://" + page + "/admin/" resolvesuccess = True if is_ip(page) is False: - dig = Pihole.run(digcommand.format(page)) + dig = host.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)) + actual_rc = host.run(status.format(testpage)) assert exit_status_success == actual_rc.rc # check for PHP error - actual_output = Pihole.run(pagecontent.format(testpage)) + actual_output = host.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(host): ''' confirms package cache was updated without any errors ''' - updateCache = Pihole.run(''' + updateCache = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect update_package_cache @@ -657,12 +658,12 @@ def test_update_package_cache_success_no_errors(Pihole): assert 'error' not in updateCache.stdout.lower() -def test_update_package_cache_failure_no_errors(Pihole): +def test_update_package_cache_failure_no_errors(host): ''' confirms package cache was not updated ''' - mock_command('apt-get', {'update': ('', '1')}, Pihole) - updateCache = Pihole.run(''' + mock_command('apt-get', {'update': ('', '1')}, host) + updateCache = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect update_package_cache @@ -672,12 +673,12 @@ def test_update_package_cache_failure_no_errors(Pihole): assert 'Error: Unable to update package cache.' in updateCache.stdout -def test_FTL_detect_aarch64_no_errors(Pihole): +def test_FTL_detect_aarch64_no_errors(host): ''' confirms only aarch64 package is downloaded for FTL engine ''' # mock uname to return aarch64 platform - mock_command('uname', {'-m': ('aarch64', '0')}, Pihole) + mock_command('uname', {'-m': ('aarch64', '0')}, host) # mock ldd to respond with aarch64 shared library mock_command( 'ldd', @@ -687,9 +688,9 @@ def test_FTL_detect_aarch64_no_errors(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -705,15 +706,15 @@ def test_FTL_detect_aarch64_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_armv4t_no_errors(Pihole): +def test_FTL_detect_armv4t_no_errors(host): ''' confirms only armv4t package is downloaded for FTL engine ''' # mock uname to return armv4t platform - mock_command('uname', {'-m': ('armv4t', '0')}, Pihole) + mock_command('uname', {'-m': ('armv4t', '0')}, host) # mock ldd to respond with ld-linux shared library - mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -729,15 +730,15 @@ def test_FTL_detect_armv4t_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_armv5te_no_errors(Pihole): +def test_FTL_detect_armv5te_no_errors(host): ''' confirms only armv5te package is downloaded for FTL engine ''' # mock uname to return armv5te platform - mock_command('uname', {'-m': ('armv5te', '0')}, Pihole) + mock_command('uname', {'-m': ('armv5te', '0')}, host) # mock ldd to respond with ld-linux shared library - mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -753,15 +754,15 @@ def test_FTL_detect_armv5te_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_armv6l_no_errors(Pihole): +def test_FTL_detect_armv6l_no_errors(host): ''' confirms only armv6l package is downloaded for FTL engine ''' # mock uname to return armv6l platform - mock_command('uname', {'-m': ('armv6l', '0')}, Pihole) + mock_command('uname', {'-m': ('armv6l', '0')}, host) # mock ldd to respond with ld-linux-armhf shared library - mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -778,15 +779,15 @@ def test_FTL_detect_armv6l_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_armv7l_no_errors(Pihole): +def test_FTL_detect_armv7l_no_errors(host): ''' confirms only armv7l package is downloaded for FTL engine ''' # mock uname to return armv7l platform - mock_command('uname', {'-m': ('armv7l', '0')}, Pihole) + mock_command('uname', {'-m': ('armv7l', '0')}, host) # mock ldd to respond with ld-linux-armhf shared library - mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -803,15 +804,15 @@ def test_FTL_detect_armv7l_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_armv8a_no_errors(Pihole): +def test_FTL_detect_armv8a_no_errors(host): ''' confirms only armv8a package is downloaded for FTL engine ''' # mock uname to return armv8a platform - mock_command('uname', {'-m': ('armv8a', '0')}, Pihole) + mock_command('uname', {'-m': ('armv8a', '0')}, host) # mock ldd to respond with ld-linux-armhf shared library - mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -827,11 +828,11 @@ def test_FTL_detect_armv8a_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_x86_64_no_errors(Pihole): +def test_FTL_detect_x86_64_no_errors(host): ''' confirms only x86_64 package is downloaded for FTL engine ''' - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -847,11 +848,11 @@ def test_FTL_detect_x86_64_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_detect_unknown_no_errors(Pihole): +def test_FTL_detect_unknown_no_errors(host): ''' confirms only generic package is downloaded for FTL engine ''' # mock uname to return generic platform - mock_command('uname', {'-m': ('mips', '0')}, Pihole) - detectPlatform = Pihole.run(''' + mock_command('uname', {'-m': ('mips', '0')}, host) + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -863,18 +864,18 @@ def test_FTL_detect_unknown_no_errors(Pihole): assert expected_stdout in detectPlatform.stdout -def test_FTL_download_aarch64_no_errors(Pihole): +def test_FTL_download_aarch64_no_errors(host): ''' confirms only aarch64 package is downloaded for FTL engine ''' # mock whiptail answers and ensure installer dependencies - mock_command('whiptail', {'*': ('', '0')}, Pihole) - Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + host.run(''' source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${INSTALLER_DEPS[@]} ''') - download_binary = Pihole.run(''' + download_binary = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user FTLinstall "pihole-FTL-aarch64-linux-gnu" @@ -884,11 +885,11 @@ def test_FTL_download_aarch64_no_errors(Pihole): assert 'error' not in download_binary.stdout.lower() -def test_FTL_binary_installed_and_responsive_no_errors(Pihole): +def test_FTL_binary_installed_and_responsive_no_errors(host): ''' confirms FTL binary is copied and functional in installed location ''' - installed_binary = Pihole.run(''' + installed_binary = host.run(''' source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -901,11 +902,11 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole): assert expected_stdout in installed_binary.stdout -# def test_FTL_support_files_installed(Pihole): +# def test_FTL_support_files_installed(host): # ''' # confirms FTL support files are installed # ''' -# support_files = Pihole.run(''' +# support_files = host.run(''' # source /opt/pihole/basic-install.sh # FTLdetect # stat -c '%a %n' /var/log/pihole-FTL.log @@ -918,7 +919,7 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole): # assert '644 /var/log/pihole-FTL.log' in support_files.stdout -def test_IPv6_only_link_local(Pihole): +def test_IPv6_only_link_local(host): ''' confirms IPv6 blocking is disabled for Link-local address ''' @@ -931,9 +932,9 @@ def test_IPv6_only_link_local(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh find_IPv6_information ''') @@ -941,7 +942,7 @@ def test_IPv6_only_link_local(Pihole): assert expected_stdout in detectPlatform.stdout -def test_IPv6_only_ULA(Pihole): +def test_IPv6_only_ULA(host): ''' confirms IPv6 blocking is enabled for ULA addresses ''' @@ -954,9 +955,9 @@ def test_IPv6_only_ULA(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh find_IPv6_information ''') @@ -964,7 +965,7 @@ def test_IPv6_only_ULA(Pihole): assert expected_stdout in detectPlatform.stdout -def test_IPv6_only_GUA(Pihole): +def test_IPv6_only_GUA(host): ''' confirms IPv6 blocking is enabled for GUA addresses ''' @@ -977,9 +978,9 @@ def test_IPv6_only_GUA(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh find_IPv6_information ''') @@ -987,7 +988,7 @@ def test_IPv6_only_GUA(Pihole): assert expected_stdout in detectPlatform.stdout -def test_IPv6_GUA_ULA_test(Pihole): +def test_IPv6_GUA_ULA_test(host): ''' confirms IPv6 blocking is enabled for GUA and ULA addresses ''' @@ -1001,9 +1002,9 @@ def test_IPv6_GUA_ULA_test(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh find_IPv6_information ''') @@ -1011,7 +1012,7 @@ def test_IPv6_GUA_ULA_test(Pihole): assert expected_stdout in detectPlatform.stdout -def test_IPv6_ULA_GUA_test(Pihole): +def test_IPv6_ULA_GUA_test(host): ''' confirms IPv6 blocking is enabled for GUA and ULA addresses ''' @@ -1025,9 +1026,9 @@ def test_IPv6_ULA_GUA_test(Pihole): '0' ) }, - Pihole + host ) - detectPlatform = Pihole.run(''' + detectPlatform = host.run(''' source /opt/pihole/basic-install.sh find_IPv6_information ''') @@ -1035,13 +1036,13 @@ def test_IPv6_ULA_GUA_test(Pihole): assert expected_stdout in detectPlatform.stdout -def test_validate_ip(Pihole): +def test_validate_ip(host): ''' Tests valid_ip for various IP addresses ''' def test_address(addr, success=True): - output = Pihole.run(''' + output = host.run(''' source /opt/pihole/basic-install.sh valid_ip "{addr}" '''.format(addr=addr)) @@ -1076,9 +1077,9 @@ def test_validate_ip(Pihole): test_address('0.0.0.0#00001', False) -def test_os_check_fails(Pihole): +def test_os_check_fails(host): ''' Confirms install fails on unsupported OS ''' - Pihole.run(''' + host.run(''' source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${OS_CHECK_DEPS[@]} @@ -1088,7 +1089,7 @@ def test_os_check_fails(Pihole): VERSION_ID="2" EOT ''') - detectOS = Pihole.run('''t + detectOS = host.run('''t source /opt/pihole/basic-install.sh os_check ''') @@ -1096,15 +1097,15 @@ def test_os_check_fails(Pihole): assert expected_stdout in detectOS.stdout -def test_os_check_passes(Pihole): +def test_os_check_passes(host): ''' Confirms OS meets the requirements ''' - Pihole.run(''' + host.run(''' source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${OS_CHECK_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]} ''') - detectOS = Pihole.run(''' + detectOS = host.run(''' source /opt/pihole/basic-install.sh os_check ''') @@ -1112,10 +1113,10 @@ def test_os_check_passes(Pihole): assert expected_stdout in detectOS.stdout -def test_package_manager_has_installer_deps(Pihole): +def test_package_manager_has_installer_deps(host): ''' Confirms OS is able to install the required packages for the installer''' - mock_command('whiptail', {'*': ('', '0')}, Pihole) - output = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + output = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${INSTALLER_DEPS[@]} @@ -1125,10 +1126,10 @@ def test_package_manager_has_installer_deps(Pihole): assert output.rc == 0 -def test_package_manager_has_pihole_deps(Pihole): +def test_package_manager_has_pihole_deps(host): ''' Confirms OS is able to install the required packages for Pi-hole ''' - mock_command('whiptail', {'*': ('', '0')}, Pihole) - output = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + output = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -1139,10 +1140,10 @@ def test_package_manager_has_pihole_deps(Pihole): assert output.rc == 0 -def test_package_manager_has_web_deps(Pihole): +def test_package_manager_has_web_deps(host): ''' Confirms OS is able to install the required packages for web ''' - mock_command('whiptail', {'*': ('', '0')}, Pihole) - output = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + output = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php diff --git a/test/test_centos_7_support.py b/test/test_centos_7_support.py index 14f62637..f72740a6 100644 --- a/test/test_centos_7_support.py +++ b/test/test_centos_7_support.py @@ -5,11 +5,11 @@ from .conftest import ( ) -def test_php_upgrade_default_optout_centos_eq_7(Pihole): +def test_php_upgrade_default_optout_centos_eq_7(host): ''' confirms the default behavior to opt-out of installing PHP7 from REMI ''' - package_manager_detect = Pihole.run(''' + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -17,18 +17,18 @@ def test_php_upgrade_default_optout_centos_eq_7(Pihole): expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' 'Deprecated PHP may be in use.') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_upgrade_user_optout_centos_eq_7(Pihole): +def test_php_upgrade_user_optout_centos_eq_7(host): ''' confirms installer behavior when user opt-out of installing PHP7 from REMI (php not currently installed) ''' # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '1')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '1')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -36,18 +36,18 @@ def test_php_upgrade_user_optout_centos_eq_7(Pihole): expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' 'Deprecated PHP may be in use.') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_upgrade_user_optin_centos_eq_7(Pihole): +def test_php_upgrade_user_optin_centos_eq_7(host): ''' confirms installer behavior when user opt-in to installing PHP7 from REMI (php not currently installed) ''' # Whiptail dialog returns Continue for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -59,5 +59,5 @@ def test_php_upgrade_user_optin_centos_eq_7(Pihole): expected_stdout = tick_box + (' Remi\'s RPM repository has ' 'been enabled for PHP7') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert remi_package.is_installed diff --git a/test/test_centos_8_support.py b/test/test_centos_8_support.py index bbdbb765..464055b4 100644 --- a/test/test_centos_8_support.py +++ b/test/test_centos_8_support.py @@ -5,12 +5,12 @@ from .conftest import ( ) -def test_php_upgrade_default_continue_centos_gte_8(Pihole): +def test_php_upgrade_default_continue_centos_gte_8(host): ''' confirms the latest version of CentOS continues / does not optout (should trigger on CentOS7 only) ''' - package_manager_detect = Pihole.run(''' + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -19,19 +19,19 @@ def test_php_upgrade_default_continue_centos_gte_8(Pihole): ' Deprecated PHP may be in use.') assert unexpected_stdout not in package_manager_detect.stdout # ensure remi was not installed on latest CentOS - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole): +def test_php_upgrade_user_optout_skipped_centos_gte_8(host): ''' confirms installer skips user opt-out of installing PHP7 from REMI on latest CentOS (should trigger on CentOS7 only) (php not currently installed) ''' # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '1')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '1')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -40,19 +40,19 @@ def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole): ' Deprecated PHP may be in use.') assert unexpected_stdout not in package_manager_detect.stdout # ensure remi was not installed on latest CentOS - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole): +def test_php_upgrade_user_optin_skipped_centos_gte_8(host): ''' confirms installer skips user opt-in to installing PHP7 from REMI on latest CentOS (should trigger on CentOS7 only) (php not currently installed) ''' # Whiptail dialog returns Continue for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -64,5 +64,5 @@ def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole): unexpected_stdout = tick_box + (' Remi\'s RPM repository has ' 'been enabled for PHP7') assert unexpected_stdout not in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed diff --git a/test/test_centos_common_support.py b/test/test_centos_common_support.py index 0b36cbae..8903a7a0 100644 --- a/test/test_centos_common_support.py +++ b/test/test_centos_common_support.py @@ -7,13 +7,13 @@ from .conftest import ( ) -def test_release_supported_version_check_centos(Pihole): +def test_release_supported_version_check_centos(host): ''' confirms installer exits on unsupported releases of CentOS ''' # modify /etc/redhat-release to mock an unsupported CentOS release - Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release') - package_manager_detect = Pihole.run(''' + host.run('echo "CentOS Linux release 6.9" > /etc/redhat-release') + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -24,11 +24,11 @@ def test_release_supported_version_check_centos(Pihole): assert expected_stdout in package_manager_detect.stdout -def test_enable_epel_repository_centos(Pihole): +def test_enable_epel_repository_centos(host): ''' confirms the EPEL package repository is enabled when installed on CentOS ''' - package_manager_detect = Pihole.run(''' + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -38,22 +38,22 @@ def test_enable_epel_repository_centos(Pihole): assert expected_stdout in package_manager_detect.stdout expected_stdout = tick_box + ' Installed epel-release' assert expected_stdout in package_manager_detect.stdout - epel_package = Pihole.package('epel-release') + epel_package = host.package('epel-release') assert epel_package.is_installed -def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole): +def test_php_version_lt_7_detected_upgrade_default_optout_centos(host): ''' confirms the default behavior to opt-out of upgrading to PHP7 from REMI ''' # first we will install the default php version to test installer behavior - php_install = Pihole.run('yum install -y php') + php_install = host.run('yum install -y php') assert php_install.rc == 0 - php_package = Pihole.package('php') + php_package = host.package('php') default_centos_php_version = php_package.version.split('.')[0] if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended pytest.skip("Test deprecated . Detected default PHP version >= 7") - package_manager_detect = Pihole.run(''' + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -61,24 +61,24 @@ def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole): expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' 'Deprecated PHP may be in use.') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole): +def test_php_version_lt_7_detected_upgrade_user_optout_centos(host): ''' confirms installer behavior when user opt-out to upgrade to PHP7 via REMI ''' # first we will install the default php version to test installer behavior - php_install = Pihole.run('yum install -y php') + php_install = host.run('yum install -y php') assert php_install.rc == 0 - php_package = Pihole.package('php') + php_package = host.package('php') default_centos_php_version = php_package.version.split('.')[0] if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended pytest.skip("Test deprecated . Detected default PHP version >= 7") # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '1')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '1')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -86,24 +86,24 @@ def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole): expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' 'Deprecated PHP may be in use.') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed -def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole): +def test_php_version_lt_7_detected_upgrade_user_optin_centos(host): ''' confirms installer behavior when user opt-in to upgrade to PHP7 via REMI ''' # first we will install the default php version to test installer behavior - php_install = Pihole.run('yum install -y php') + php_install = host.run('yum install -y php') assert php_install.rc == 0 - php_package = Pihole.package('php') + php_package = host.package('php') default_centos_php_version = php_package.version.split('.')[0] if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended pytest.skip("Test deprecated . Detected default PHP version >= 7") # Whiptail dialog returns Continue for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) - package_manager_detect = Pihole.run(''' + mock_command('whiptail', {'*': ('', '0')}, host) + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php @@ -118,8 +118,8 @@ def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole): expected_stdout = tick_box + (' Remi\'s RPM repository has ' 'been enabled for PHP7') assert expected_stdout in package_manager_detect.stdout - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert remi_package.is_installed - updated_php_package = Pihole.package('php') + updated_php_package = host.package('php') updated_php_version = updated_php_package.version.split('.')[0] assert int(updated_php_version) == 7 diff --git a/test/test_centos_fedora_common_support.py b/test/test_centos_fedora_common_support.py index 21ae6d1d..a2a13048 100644 --- a/test/test_centos_fedora_common_support.py +++ b/test/test_centos_fedora_common_support.py @@ -5,7 +5,7 @@ from .conftest import ( ) -def mock_selinux_config(state, Pihole): +def mock_selinux_config(state, host): ''' Creates a mock SELinux config file with expected content ''' @@ -13,20 +13,20 @@ def mock_selinux_config(state, Pihole): valid_states = ['enforcing', 'permissive', 'disabled'] assert state in valid_states # getenforce returns the running state of SELinux - mock_command('getenforce', {'*': (state.capitalize(), '0')}, Pihole) + mock_command('getenforce', {'*': (state.capitalize(), '0')}, host) # create mock configuration with desired content - Pihole.run(''' + host.run(''' mkdir /etc/selinux echo "SELINUX={state}" > /etc/selinux/config '''.format(state=state.lower())) -def test_selinux_enforcing_exit(Pihole): +def test_selinux_enforcing_exit(host): ''' confirms installer prompts to exit when SELinux is Enforcing by default ''' - mock_selinux_config("enforcing", Pihole) - check_selinux = Pihole.run(''' + mock_selinux_config("enforcing", host) + check_selinux = host.run(''' source /opt/pihole/basic-install.sh checkSelinux ''') @@ -37,12 +37,12 @@ def test_selinux_enforcing_exit(Pihole): assert check_selinux.rc == 1 -def test_selinux_permissive(Pihole): +def test_selinux_permissive(host): ''' confirms installer continues when SELinux is Permissive ''' - mock_selinux_config("permissive", Pihole) - check_selinux = Pihole.run(''' + mock_selinux_config("permissive", host) + check_selinux = host.run(''' source /opt/pihole/basic-install.sh checkSelinux ''') @@ -51,12 +51,12 @@ def test_selinux_permissive(Pihole): assert check_selinux.rc == 0 -def test_selinux_disabled(Pihole): +def test_selinux_disabled(host): ''' confirms installer continues when SELinux is Disabled ''' - mock_selinux_config("disabled", Pihole) - check_selinux = Pihole.run(''' + mock_selinux_config("disabled", host) + check_selinux = host.run(''' source /opt/pihole/basic-install.sh checkSelinux ''') diff --git a/test/test_fedora_support.py b/test/test_fedora_support.py index 3ad84be5..63fde90e 100644 --- a/test/test_fedora_support.py +++ b/test/test_fedora_support.py @@ -1,16 +1,16 @@ -def test_epel_and_remi_not_installed_fedora(Pihole): +def test_epel_and_remi_not_installed_fedora(host): ''' confirms installer does not attempt to install EPEL/REMI repositories on Fedora ''' - package_manager_detect = Pihole.run(''' + package_manager_detect = host.run(''' source /opt/pihole/basic-install.sh package_manager_detect select_rpm_php ''') assert package_manager_detect.stdout == '' - epel_package = Pihole.package('epel-release') + epel_package = host.package('epel-release') assert not epel_package.is_installed - remi_package = Pihole.package('remi-release') + remi_package = host.package('remi-release') assert not remi_package.is_installed diff --git a/test/tox.centos_7.ini b/test/tox.centos_7.ini index c2752698..88940fdd 100644 --- a/test/tox.centos_7.ini +++ b/test/tox.centos_7.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.centos_8.ini b/test/tox.centos_8.ini index c8852bae..5088da16 100644 --- a/test/tox.centos_8.ini +++ b/test/tox.centos_8.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.debian_10.ini b/test/tox.debian_10.ini index e8c5d746..9c2a05d1 100644 --- a/test/tox.debian_10.ini +++ b/test/tox.debian_10.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.debian_11.ini b/test/tox.debian_11.ini index af3c6e36..f3cdbe84 100644 --- a/test/tox.debian_11.ini +++ b/test/tox.debian_11.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.debian_9.ini b/test/tox.debian_9.ini index b33e9927..b46e0a49 100644 --- a/test/tox.debian_9.ini +++ b/test/tox.debian_9.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.fedora_33.ini b/test/tox.fedora_33.ini index 00ea732a..d33fbf53 100644 --- a/test/tox.fedora_33.ini +++ b/test/tox.fedora_33.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.fedora_34.ini b/test/tox.fedora_34.ini index 154662cf..819291fa 100644 --- a/test/tox.fedora_34.ini +++ b/test/tox.fedora_34.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.ubuntu_16.ini b/test/tox.ubuntu_16.ini index 6f484b6b..bce948a2 100644 --- a/test/tox.ubuntu_16.ini +++ b/test/tox.ubuntu_16.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.ubuntu_18.ini b/test/tox.ubuntu_18.ini index 10d4ac09..cf7a3642 100644 --- a/test/tox.ubuntu_18.ini +++ b/test/tox.ubuntu_18.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.ubuntu_20.ini b/test/tox.ubuntu_20.ini index 8b405b28..03b605ce 100644 --- a/test/tox.ubuntu_20.ini +++ b/test/tox.ubuntu_20.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker diff --git a/test/tox.ubuntu_21.ini b/test/tox.ubuntu_21.ini index 651f933b..12b1ac0b 100644 --- a/test/tox.ubuntu_21.ini +++ b/test/tox.ubuntu_21.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py38 [testenv] whitelist_externals = docker From 996a2c74fa0004fdd40b172cd9eec695a2d31768 Mon Sep 17 00:00:00 2001 From: MichaIng Date: Mon, 22 Nov 2021 13:25:13 +0100 Subject: [PATCH 08/36] Use a fixed list height for network interface selection This solves the issue reported here: https://github.com/pi-hole/pi-hole/issues/4196 It replaces the other suggested solution here: https://github.com/pi-hole/pi-hole/pull/4197 The benefit of using a fixed/limited list height, compared to allowing larger whiptail/dialogue dimension, is that it works on small screens as well, where the screen or console size itself is too small to hold the interface list + text above + whiptail frame. It the amount of list elements exceeds the defined list height, automatically a visual scroll bar is added and the list can be scrolled with up/down pageup/pagedown buttons, hence it is generally not required to adjust a list height based on the amount of elements. The fixed height of "6" is chosen since all other "--radiolist" calls use this fixed height as well, it fits and looks good within a 20 rows high whiptail dialogue, and in the common Pi-hole use cases there are no more than 6 network interfaces. Signed-off-by: MichaIng --- automated install/basic-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c806a6ae..5c317edc 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -674,7 +674,7 @@ chooseInterface() { # Feed the available interfaces into this while loop done <<< "${availableInterfaces}" # The whiptail command that will be run, stored in a variable - chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to toggle selection)" "${r}" "${c}" "${interfaceCount}") + chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to toggle selection)" "${r}" "${c}" 6) # Now run the command using the interfaces saved into the array chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \ # If the user chooses Cancel, exit From bc8150adfa12b00b7f04ea0c15270e19de84b381 Mon Sep 17 00:00:00 2001 From: jbzdarkid Date: Wed, 24 Nov 2021 22:41:40 -0800 Subject: [PATCH 09/36] Clean up bash script formatting Done with the help of beautysh (a python-based bash formatter) Signed-off-by: jbzdarkid --- advanced/Scripts/chronometer.sh | 8 +- .../Scripts/database_migration/gravity-db.sh | 10 +- advanced/Scripts/list.sh | 42 +- advanced/Scripts/piholeDebug.sh | 2 +- advanced/Scripts/query.sh | 28 +- advanced/Scripts/update.sh | 2 +- advanced/Scripts/version.sh | 8 +- advanced/Scripts/webpage.sh | 150 +++---- automated install/basic-install.sh | 376 +++++++++--------- gravity.sh | 52 +-- 10 files changed, 339 insertions(+), 339 deletions(-) diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 3f85bdfc..312c484f 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -329,8 +329,8 @@ get_sys_stats() { *) cpu_col="$COL_URG_RED";; esac - # $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED - cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" + # $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED + cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" elif [[ "$temp_unit" == "F" ]]; then cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") @@ -445,7 +445,7 @@ get_strings() { lan_info="Gateway: $net_gateway" dhcp_info="$leased_str$ph_dhcp_num of $ph_dhcp_max" - ads_info="$total_str$ads_blocked_today of $dns_queries_today" + ads_info="$total_str$ads_blocked_today of $dns_queries_today" dns_info="$dns_count DNS servers" [[ "$recent_blocked" == "0" ]] && recent_blocked="${COL_LIGHT_RED}FTL offline${COL_NC}" @@ -488,7 +488,7 @@ chronoFunc() { ${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC} ${COL_DARK_GRAY}$scr_line_str${COL_NC}" else - echo -e "|¯¯¯(¯)_|¯|_ ___|¯|___$phc_ver_str\\n| ¯_/¯|_| ' \\/ _ \\ / -_)$lte_ver_str\\n|_| |_| |_||_\\___/_\\___|$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}" + echo -e "|¯¯¯(¯)_|¯|_ ___|¯|___$phc_ver_str\\n| ¯_/¯|_| ' \\/ _ \\ / -_)$lte_ver_str\\n|_| |_| |_||_\\___/_\\___|$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}" fi printFunc " Hostname: " "$sys_name" "$host_info" diff --git a/advanced/Scripts/database_migration/gravity-db.sh b/advanced/Scripts/database_migration/gravity-db.sh index 0fecf34a..09dc1727 100755 --- a/advanced/Scripts/database_migration/gravity-db.sh +++ b/advanced/Scripts/database_migration/gravity-db.sh @@ -123,9 +123,9 @@ upgrade_gravityDB(){ version=14 fi if [[ "$version" == "14" ]]; then - # Changes the vw_adlist created in 5_to_6 - echo -e " ${INFO} Upgrading gravity database from version 14 to 15" - sqlite3 "${database}" < "${scriptPath}/14_to_15.sql" - version=15 -fi + # Changes the vw_adlist created in 5_to_6 + echo -e " ${INFO} Upgrading gravity database from version 14 to 15" + sqlite3 "${database}" < "${scriptPath}/14_to_15.sql" + version=15 + fi } diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 5bd42d55..bc254515 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -16,7 +16,7 @@ GRAVITYDB="${piholeDir}/gravity.db" # Source pihole-FTL from install script pihole_FTL="${piholeDir}/pihole-FTL.conf" if [[ -f "${pihole_FTL}" ]]; then - source "${pihole_FTL}" + source "${pihole_FTL}" fi # Set this only after sourcing pihole-FTL.conf as the gravity database path may @@ -133,7 +133,7 @@ ProcessDomainList() { else RemoveDomain "${dom}" fi - done + done } AddDomain() { @@ -145,19 +145,19 @@ AddDomain() { requestedListname="$(GetListnameFromTypeId "${typeId}")" if [[ "${num}" -ne 0 ]]; then - existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" - if [[ "${existingTypeId}" == "${typeId}" ]]; then - if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!" + existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" + if [[ "${existingTypeId}" == "${typeId}" ]]; then + if [[ "${verbose}" == true ]]; then + echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!" + fi + else + existingListname="$(GetListnameFromTypeId "${existingTypeId}")" + sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';" + if [[ "${verbose}" == true ]]; then + echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!" + fi fi - else - existingListname="$(GetListnameFromTypeId "${existingTypeId}")" - sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';" - if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!" - fi - fi - return + return fi # Domain not found in the table, add it! @@ -185,10 +185,10 @@ RemoveDomain() { requestedListname="$(GetListnameFromTypeId "${typeId}")" if [[ "${num}" -eq 0 ]]; then - if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!" - fi - return + if [[ "${verbose}" == true ]]; then + echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!" + fi + return fi # Domain found in the table, remove it! @@ -256,8 +256,8 @@ NukeList() { GetComment() { comment="$1" if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then - echo " ${CROSS} Found invalid characters in domain comment!" - exit + echo " ${CROSS} Found invalid characters in domain comment!" + exit fi } @@ -292,7 +292,7 @@ ProcessDomainList # Used on web interface if $web; then -echo "DONE" + echo "DONE" fi if [[ ${reload} == true && ${noReloadRequested} == false ]]; then diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index cd615825..ad58d6c7 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -27,7 +27,7 @@ PIHOLE_COLTABLE_FILE="${PIHOLE_SCRIPTS_DIRECTORY}/COL_TABLE" # These provide the colors we need for making the log more readable if [[ -f ${PIHOLE_COLTABLE_FILE} ]]; then - source ${PIHOLE_COLTABLE_FILE} + source ${PIHOLE_COLTABLE_FILE} else COL_NC='\e[0m' # No Color COL_RED='\e[1;91m' diff --git a/advanced/Scripts/query.sh b/advanced/Scripts/query.sh index 26b4508e..0fd9871a 100755 --- a/advanced/Scripts/query.sh +++ b/advanced/Scripts/query.sh @@ -21,7 +21,7 @@ matchType="match" # Source pihole-FTL from install script pihole_FTL="${piholeDir}/pihole-FTL.conf" if [[ -f "${pihole_FTL}" ]]; then - source "${pihole_FTL}" + source "${pihole_FTL}" fi # Set this only after sourcing pihole-FTL.conf as the gravity database path may @@ -48,7 +48,7 @@ scanList(){ # Iterate through each regexp and check whether it matches the domainQuery # If it does, print the matching regexp and continue looping # Input 1 - regexps | Input 2 - domainQuery - "regex" ) + "regex" ) for list in ${lists}; do if [[ "${domain}" =~ ${list} ]]; then printf "%b\n" "${list}"; @@ -109,15 +109,15 @@ scanDatabaseTable() { # behavior. The "ESCAPE '\'" clause specifies that an underscore preceded by an '\' should be matched # as a literal underscore character. We pretreat the $domain variable accordingly to escape underscores. if [[ "${table}" == "gravity" ]]; then - case "${exact}" in - "exact" ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";; - * ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; - esac + case "${exact}" in + "exact" ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";; + * ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; + esac else - case "${exact}" in - "exact" ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";; - * ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; - esac + case "${exact}" in + "exact" ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";; + * ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; + esac fi # Send prepared query to gravity database @@ -128,8 +128,8 @@ scanDatabaseTable() { fi if [[ "${table}" == "gravity" ]]; then - echo "${result}" - return + echo "${result}" + return fi # Mark domain as having been white-/blacklist matched (global variable) @@ -233,9 +233,9 @@ for result in "${results[@]}"; do adlistAddress="${extra/|*/}" extra="${extra#*|}" if [[ "${extra}" == "0" ]]; then - extra="(disabled)" + extra="(disabled)" else - extra="" + extra="" fi if [[ -n "${blockpage}" ]]; then diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index dae04861..3fab9a95 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -200,7 +200,7 @@ main() { if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \ - echo -e "${basicError}" && exit 1 + echo -e "${basicError}" && exit 1 fi if [[ "${FTL_update}" == true || "${core_update}" == true || "${web_update}" == true ]]; then diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh index 86ac45bc..2cb0a2c9 100755 --- a/advanced/Scripts/version.sh +++ b/advanced/Scripts/version.sh @@ -92,9 +92,9 @@ getRemoteVersion(){ if [[ -f "$cachedVersions" ]]; then IFS=' ' read -r -a arrCache < "$cachedVersions" case $daemon in - "pi-hole" ) echo "${arrCache[0]}";; - "AdminLTE" ) echo "${arrCache[1]}";; - "FTL" ) echo "${arrCache[2]}";; + "pi-hole" ) echo "${arrCache[0]}";; + "AdminLTE" ) echo "${arrCache[1]}";; + "FTL" ) echo "${arrCache[2]}";; esac return 0 @@ -117,7 +117,7 @@ getLocalBranch(){ local directory="${1}" local branch - # Local FTL btranch is stored in /etc/pihole/ftlbranch + # Local FTL btranch is stored in /etc/pihole/ftlbranch if [[ "$1" == "FTL" ]]; then branch="$(pihole-FTL branch)" else diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 2a406565..ec9b2cac 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -122,14 +122,14 @@ SetWebPassword() { read -s -r -p "Enter New Password (Blank for no password): " PASSWORD echo "" - if [ "${PASSWORD}" == "" ]; then - change_setting "WEBPASSWORD" "" - echo -e " ${TICK} Password Removed" - exit 0 - fi + if [ "${PASSWORD}" == "" ]; then + change_setting "WEBPASSWORD" "" + echo -e " ${TICK} Password Removed" + exit 0 + fi - read -s -r -p "Confirm Password: " CONFIRM - echo "" + read -s -r -p "Confirm Password: " CONFIRM + echo "" fi if [ "${PASSWORD}" == "${CONFIRM}" ] ; then @@ -247,8 +247,8 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";; esac else - # Set REV_SERVER_CIDR to whatever value it was set to - REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}" + # Set REV_SERVER_CIDR to whatever value it was set to + REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}" fi # If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it @@ -371,34 +371,34 @@ ProcessDHCPSettings() { source "${setupVars}" if [[ "${DHCP_ACTIVE}" == "true" ]]; then - interface="${PIHOLE_INTERFACE}" + interface="${PIHOLE_INTERFACE}" - # Use eth0 as fallback interface - if [ -z ${interface} ]; then - interface="eth0" - fi + # Use eth0 as fallback interface + if [ -z ${interface} ]; then + interface="eth0" + fi - if [[ "${PIHOLE_DOMAIN}" == "" ]]; then - PIHOLE_DOMAIN="lan" - change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" - fi + if [[ "${PIHOLE_DOMAIN}" == "" ]]; then + PIHOLE_DOMAIN="lan" + change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" + fi - if [[ "${DHCP_LEASETIME}" == "0" ]]; then - leasetime="infinite" - elif [[ "${DHCP_LEASETIME}" == "" ]]; then - leasetime="24" - change_setting "DHCP_LEASETIME" "${leasetime}" - elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then - #Installation is affected by known bug, introduced in a previous version. - #This will automatically clean up setupVars.conf and remove the unnecessary "h" - leasetime="24" - change_setting "DHCP_LEASETIME" "${leasetime}" - else - leasetime="${DHCP_LEASETIME}h" - fi + if [[ "${DHCP_LEASETIME}" == "0" ]]; then + leasetime="infinite" + elif [[ "${DHCP_LEASETIME}" == "" ]]; then + leasetime="24" + change_setting "DHCP_LEASETIME" "${leasetime}" + elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then + #Installation is affected by known bug, introduced in a previous version. + #This will automatically clean up setupVars.conf and remove the unnecessary "h" + leasetime="24" + change_setting "DHCP_LEASETIME" "${leasetime}" + else + leasetime="${DHCP_LEASETIME}h" + fi - # Write settings to file - echo "############################################################################### + # Write settings to file + echo "############################################################################### # DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. # # ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE # ############################################################################### @@ -408,34 +408,34 @@ dhcp-option=option:router,${DHCP_ROUTER} dhcp-leasefile=/etc/pihole/dhcp.leases #quiet-dhcp " > "${dhcpconfig}" - chmod 644 "${dhcpconfig}" + chmod 644 "${dhcpconfig}" - if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then - echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" + if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then + echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" - # When there is a Pi-hole domain set and "Never forward non-FQDNs" is - # ticked, we add `local=/domain/` to tell FTL that this domain is purely - # local and FTL may answer queries from /etc/hosts or DHCP but should - # never forward queries on that domain to any upstream servers - if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then - echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}" + # When there is a Pi-hole domain set and "Never forward non-FQDNs" is + # ticked, we add `local=/domain/` to tell FTL that this domain is purely + # local and FTL may answer queries from /etc/hosts or DHCP but should + # never forward queries on that domain to any upstream servers + if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then + echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}" + fi fi - fi - # Sourced from setupVars - # shellcheck disable=SC2154 - if [[ "${DHCP_rapid_commit}" == "true" ]]; then - echo "dhcp-rapid-commit" >> "${dhcpconfig}" - fi + # Sourced from setupVars + # shellcheck disable=SC2154 + if [[ "${DHCP_rapid_commit}" == "true" ]]; then + echo "dhcp-rapid-commit" >> "${dhcpconfig}" + fi - if [[ "${DHCP_IPv6}" == "true" ]]; then - echo "#quiet-dhcp6 + if [[ "${DHCP_IPv6}" == "true" ]]; then + echo "#quiet-dhcp6 #enable-ra dhcp-option=option6:dns-server,[::] dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600 ra-param=*,0,0 " >> "${dhcpconfig}" - fi + fi else if [[ -f "${dhcpconfig}" ]]; then @@ -605,7 +605,7 @@ Interfaces: single Listen only on ${PIHOLE_INTERFACE} interface all Listen on all interfaces, permit all origins" exit 0 - fi + fi if [[ "${args[2]}" == "all" ]]; then echo -e " ${INFO} Listening on all interfaces, permitting all origins. Please use a firewall!" @@ -654,18 +654,18 @@ addAudit() domains="" for domain in "$@" do - # Check domain to be added. Only continue if it is valid - validDomain="$(checkDomain "${domain}")" - if [[ -n "${validDomain}" ]]; then - # Put comma in between domains when there is - # more than one domains to be added - # SQL INSERT allows adding multiple rows at once using the format - ## INSERT INTO table (domain) VALUES ('abc.de'),('fgh.ij'),('klm.no'),('pqr.st'); - if [[ -n "${domains}" ]]; then - domains="${domains}," + # Check domain to be added. Only continue if it is valid + validDomain="$(checkDomain "${domain}")" + if [[ -n "${validDomain}" ]]; then + # Put comma in between domains when there is + # more than one domains to be added + # SQL INSERT allows adding multiple rows at once using the format + ## INSERT INTO table (domain) VALUES ('abc.de'),('fgh.ij'),('klm.no'),('pqr.st'); + if [[ -n "${domains}" ]]; then + domains="${domains}," + fi + domains="${domains}('${domain}')" fi - domains="${domains}('${domain}')" - fi done # Insert only the domain here. The date_added field will be # filled with its default value (date_added = current timestamp) @@ -707,7 +707,7 @@ AddCustomDNSAddress() { # Restart dnsmasq to load new custom DNS entries only if $reload not false if [[ ! $reload == "false" ]]; then - RestartDNS + RestartDNS fi } @@ -726,14 +726,14 @@ RemoveCustomDNSAddress() { echo -e " ${CROSS} Invalid IP has been passed" exit 1 fi - else - echo " ${CROSS} Invalid Domain passed!" - exit 1 + else + echo " ${CROSS} Invalid Domain passed!" + exit 1 fi # Restart dnsmasq to load new custom DNS entries only if reload is not false if [[ ! $reload == "false" ]]; then - RestartDNS + RestartDNS fi } @@ -748,10 +748,10 @@ AddCustomCNAMERecord() { if [[ -n "${validDomain}" ]]; then validTarget="$(checkDomain "${target}")" if [[ -n "${validTarget}" ]]; then - echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}" + echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}" else - echo " ${CROSS} Invalid Target Passed!" - exit 1 + echo " ${CROSS} Invalid Target Passed!" + exit 1 fi else echo " ${CROSS} Invalid Domain passed!" @@ -759,7 +759,7 @@ AddCustomCNAMERecord() { fi # Restart dnsmasq to load new custom CNAME records only if reload is not false if [[ ! $reload == "false" ]]; then - RestartDNS + RestartDNS fi } @@ -776,8 +776,8 @@ RemoveCustomCNAMERecord() { if [[ -n "${validTarget}" ]]; then sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}" else - echo " ${CROSS} Invalid Target Passed!" - exit 1 + echo " ${CROSS} Invalid Target Passed!" + exit 1 fi else echo " ${CROSS} Invalid Domain passed!" @@ -786,7 +786,7 @@ RemoveCustomCNAMERecord() { # Restart dnsmasq to update removed custom CNAME records only if $reload not false if [[ ! $reload == "false" ]]; then - RestartDNS + RestartDNS fi } diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 5c317edc..cbb8ebae 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -76,7 +76,7 @@ PI_HOLE_CONFIG_DIR="/etc/pihole" PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_BLOCKPAGE_DIR="${webroot}/pihole" if [ -z "$useUpdateVars" ]; then - useUpdateVars=false + useUpdateVars=false fi adlistFile="/etc/pihole/adlists.list" @@ -90,7 +90,7 @@ PRIVACY_LEVEL=0 CACHE_SIZE=10000 if [ -z "${USER}" ]; then - USER="$(id -un)" + USER="$(id -un)" fi # whiptail dialog dimensions: 20 rows and 70 chars width assures to fit on small screens and is known to hold all content. @@ -133,7 +133,7 @@ fi # A simple function that just echoes out our logo in ASCII format # This lets users know that it is a Pi-hole, LLC product show_ascii_berry() { - echo -e " + echo -e " ${COL_LIGHT_GREEN}.;;,. .ccccc:,. :cccclll:. ..,, @@ -261,174 +261,174 @@ os_check() { # Compatibility package_manager_detect() { -# First check to see if apt-get is installed. -if is_command apt-get ; then - # Set some global variables here - # We don't set them earlier since the installed package manager might be rpm, so these values would be different - PKG_MANAGER="apt-get" - # A variable to store the command used to update the package cache - UPDATE_PKG_CACHE="${PKG_MANAGER} update" - # The command we will use to actually install packages - PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install) - # grep -c will return 1 if there are no matches. This is an acceptable condition, so we OR TRUE to prevent set -e exiting the script. - PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" - # Update package cache - update_package_cache || exit 1 - # Check for and determine version number (major and minor) of current php install - local phpVer="php" - if is_command php ; then - printf " %b Existing PHP installation detected : PHP version %s\\n" "${INFO}" "$(php <<< "")" - printf -v phpInsMajor "%d" "$(php <<< "")" - printf -v phpInsMinor "%d" "$(php <<< "")" - phpVer="php$phpInsMajor.$phpInsMinor" - fi - # Packages required to perfom the os_check (stored as an array) - OS_CHECK_DEPS=(grep dnsutils) - # Packages required to run this install script (stored as an array) - INSTALLER_DEPS=(git iproute2 whiptail ca-certificates) - # Packages required to run Pi-hole (stored as an array) - PIHOLE_DEPS=(cron curl iputils-ping lsof psmisc sudo unzip idn2 sqlite3 libcap2-bin dns-root-data libcap2) - # Packages required for the Web admin interface (stored as an array) - # It's useful to separate this from Pi-hole, since the two repos are also setup separately - PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-sqlite3" "${phpVer}-xml" "${phpVer}-intl") - # Prior to PHP8.0, JSON functionality is provided as dedicated module, required by Pi-hole AdminLTE: https://www.php.net/manual/json.installation.php - if [[ -z "${phpInsMajor}" || "${phpInsMajor}" -lt 8 ]]; then - PIHOLE_WEB_DEPS+=("${phpVer}-json") - fi - # The Web server user, - LIGHTTPD_USER="www-data" - # group, - LIGHTTPD_GROUP="www-data" - # and config file - LIGHTTPD_CFG="lighttpd.conf.debian" + # First check to see if apt-get is installed. + if is_command apt-get ; then + # Set some global variables here + # We don't set them earlier since the installed package manager might be rpm, so these values would be different + PKG_MANAGER="apt-get" + # A variable to store the command used to update the package cache + UPDATE_PKG_CACHE="${PKG_MANAGER} update" + # The command we will use to actually install packages + PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install) + # grep -c will return 1 if there are no matches. This is an acceptable condition, so we OR TRUE to prevent set -e exiting the script. + PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" + # Update package cache + update_package_cache || exit 1 + # Check for and determine version number (major and minor) of current php install + local phpVer="php" + if is_command php ; then + printf " %b Existing PHP installation detected : PHP version %s\\n" "${INFO}" "$(php <<< "")" + printf -v phpInsMajor "%d" "$(php <<< "")" + printf -v phpInsMinor "%d" "$(php <<< "")" + phpVer="php$phpInsMajor.$phpInsMinor" + fi + # Packages required to perfom the os_check (stored as an array) + OS_CHECK_DEPS=(grep dnsutils) + # Packages required to run this install script (stored as an array) + INSTALLER_DEPS=(git iproute2 whiptail ca-certificates) + # Packages required to run Pi-hole (stored as an array) + PIHOLE_DEPS=(cron curl iputils-ping lsof psmisc sudo unzip idn2 sqlite3 libcap2-bin dns-root-data libcap2) + # Packages required for the Web admin interface (stored as an array) + # It's useful to separate this from Pi-hole, since the two repos are also setup separately + PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-sqlite3" "${phpVer}-xml" "${phpVer}-intl") + # Prior to PHP8.0, JSON functionality is provided as dedicated module, required by Pi-hole AdminLTE: https://www.php.net/manual/json.installation.php + if [[ -z "${phpInsMajor}" || "${phpInsMajor}" -lt 8 ]]; then + PIHOLE_WEB_DEPS+=("${phpVer}-json") + fi + # The Web server user, + LIGHTTPD_USER="www-data" + # group, + LIGHTTPD_GROUP="www-data" + # and config file + LIGHTTPD_CFG="lighttpd.conf.debian" - # This function waits for dpkg to unlock, which signals that the previous apt-get command has finished. - test_dpkg_lock() { - i=0 - # fuser is a program to show which processes use the named files, sockets, or filesystems - # So while the lock is held, - while fuser /var/lib/dpkg/lock >/dev/null 2>&1 - do - # we wait half a second, - sleep 0.5 - # increase the iterator, - ((i=i+1)) - done - # and then report success once dpkg is unlocked. - return 0 - } + # This function waits for dpkg to unlock, which signals that the previous apt-get command has finished. + test_dpkg_lock() { + i=0 + # fuser is a program to show which processes use the named files, sockets, or filesystems + # So while the lock is held, + while fuser /var/lib/dpkg/lock >/dev/null 2>&1 + do + # we wait half a second, + sleep 0.5 + # increase the iterator, + ((i=i+1)) + done + # and then report success once dpkg is unlocked. + return 0 + } -# If apt-get is not found, check for rpm. -elif is_command rpm ; then - # Then check if dnf or yum is the package manager - if is_command dnf ; then - PKG_MANAGER="dnf" + # If apt-get is not found, check for rpm. + elif is_command rpm ; then + # Then check if dnf or yum is the package manager + if is_command dnf ; then + PKG_MANAGER="dnf" + else + PKG_MANAGER="yum" + fi + + # These variable names match the ones for apt-get. See above for an explanation of what they are for. + PKG_INSTALL=("${PKG_MANAGER}" install -y) + PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" + OS_CHECK_DEPS=(grep bind-utils) + INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig ca-certificates) + PIHOLE_DEPS=(cronie curl findutils sudo unzip libidn2 psmisc sqlite libcap lsof) + PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml php-json php-intl) + LIGHTTPD_USER="lighttpd" + LIGHTTPD_GROUP="lighttpd" + LIGHTTPD_CFG="lighttpd.conf.fedora" + + # If neither apt-get or yum/dnf package managers were found else - PKG_MANAGER="yum" + # we cannot install required packages + printf " %b No supported package manager found\\n" "${CROSS}" + # so exit the installer + exit fi - - # These variable names match the ones for apt-get. See above for an explanation of what they are for. - PKG_INSTALL=("${PKG_MANAGER}" install -y) - PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" - OS_CHECK_DEPS=(grep bind-utils) - INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig ca-certificates) - PIHOLE_DEPS=(cronie curl findutils sudo unzip libidn2 psmisc sqlite libcap lsof) - PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml php-json php-intl) - LIGHTTPD_USER="lighttpd" - LIGHTTPD_GROUP="lighttpd" - LIGHTTPD_CFG="lighttpd.conf.fedora" - -# If neither apt-get or yum/dnf package managers were found -else - # we cannot install required packages - printf " %b No supported package manager found\\n" "${CROSS}" - # so exit the installer - exit -fi } select_rpm_php(){ -# If the host OS is Fedora, -if grep -qiE 'fedora|fedberry' /etc/redhat-release; then - # all required packages should be available by default with the latest fedora release - : # continue -# or if host OS is CentOS, -elif grep -qiE 'centos|scientific' /etc/redhat-release; then - # Pi-Hole currently supports CentOS 7+ with PHP7+ - SUPPORTED_CENTOS_VERSION=7 - SUPPORTED_CENTOS_PHP_VERSION=7 - # Check current CentOS major release version - CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release) - # Check if CentOS version is supported - if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then - printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}" - printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}" - # exit the installer - exit - fi - # php-json is not required on CentOS 7 as it is already compiled into php - # verifiy via `php -m | grep json` - if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then - # create a temporary array as arrays are not designed for use as mutable data structures - CENTOS7_PIHOLE_WEB_DEPS=() - for i in "${!PIHOLE_WEB_DEPS[@]}"; do - if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then - CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" ) - fi - done - # re-assign the clean dependency array back to PIHOLE_WEB_DEPS - PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}") - unset CENTOS7_PIHOLE_WEB_DEPS - fi - # CentOS requires the EPEL repository to gain access to Fedora packages - EPEL_PKG="epel-release" - rpm -q ${EPEL_PKG} &> /dev/null || rc=$? - if [[ $rc -ne 0 ]]; then - printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}" - "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null - printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}" - fi + # If the host OS is Fedora, + if grep -qiE 'fedora|fedberry' /etc/redhat-release; then + # all required packages should be available by default with the latest fedora release + : # continue + # or if host OS is CentOS, + elif grep -qiE 'centos|scientific' /etc/redhat-release; then + # Pi-Hole currently supports CentOS 7+ with PHP7+ + SUPPORTED_CENTOS_VERSION=7 + SUPPORTED_CENTOS_PHP_VERSION=7 + # Check current CentOS major release version + CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release) + # Check if CentOS version is supported + if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then + printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}" + printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}" + # exit the installer + exit + fi + # php-json is not required on CentOS 7 as it is already compiled into php + # verifiy via `php -m | grep json` + if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then + # create a temporary array as arrays are not designed for use as mutable data structures + CENTOS7_PIHOLE_WEB_DEPS=() + for i in "${!PIHOLE_WEB_DEPS[@]}"; do + if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then + CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" ) + fi + done + # re-assign the clean dependency array back to PIHOLE_WEB_DEPS + PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}") + unset CENTOS7_PIHOLE_WEB_DEPS + fi + # CentOS requires the EPEL repository to gain access to Fedora packages + EPEL_PKG="epel-release" + rpm -q ${EPEL_PKG} &> /dev/null || rc=$? + if [[ $rc -ne 0 ]]; then + printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}" + "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null + printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}" + fi - # The default php on CentOS 7.x is 5.4 which is EOL - # Check if the version of PHP available via installed repositories is >= to PHP 7 - AVAILABLE_PHP_VERSION=$("${PKG_MANAGER}" info php | grep -i version | grep -o '[0-9]\+' | head -1) - if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then - # Since PHP 7 is available by default, install via default PHP package names - : # do nothing as PHP is current - else - REMI_PKG="remi-release" - REMI_REPO="remi-php72" - rpm -q ${REMI_PKG} &> /dev/null || rc=$? - if [[ $rc -ne 0 ]]; then - # The PHP version available via default repositories is older than version 7 - if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information" "${r}" "${c}"; then - # User decided to NOT update PHP from REMI, attempt to install the default available PHP version - printf " %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n" "${INFO}" - : # continue with unsupported php version + # The default php on CentOS 7.x is 5.4 which is EOL + # Check if the version of PHP available via installed repositories is >= to PHP 7 + AVAILABLE_PHP_VERSION=$("${PKG_MANAGER}" info php | grep -i version | grep -o '[0-9]\+' | head -1) + if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then + # Since PHP 7 is available by default, install via default PHP package names + : # do nothing as PHP is current else - printf " %b Enabling Remi's RPM repository (https://rpms.remirepo.net)\\n" "${INFO}" - "${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null - # enable the PHP 7 repository via yum-config-manager (provided by yum-utils) - "${PKG_INSTALL[@]}" "yum-utils" &> /dev/null - yum-config-manager --enable ${REMI_REPO} &> /dev/null - printf " %b Remi's RPM repository has been enabled for PHP7\\n" "${TICK}" - # trigger an install/update of PHP to ensure previous version of PHP is updated from REMI - if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then - printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}" + REMI_PKG="remi-release" + REMI_REPO="remi-php72" + rpm -q ${REMI_PKG} &> /dev/null || rc=$? + if [[ $rc -ne 0 ]]; then + # The PHP version available via default repositories is older than version 7 + if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information" "${r}" "${c}"; then + # User decided to NOT update PHP from REMI, attempt to install the default available PHP version + printf " %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n" "${INFO}" + : # continue with unsupported php version + else + printf " %b Enabling Remi's RPM repository (https://rpms.remirepo.net)\\n" "${INFO}" + "${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null + # enable the PHP 7 repository via yum-config-manager (provided by yum-utils) + "${PKG_INSTALL[@]}" "yum-utils" &> /dev/null + yum-config-manager --enable ${REMI_REPO} &> /dev/null + printf " %b Remi's RPM repository has been enabled for PHP7\\n" "${TICK}" + # trigger an install/update of PHP to ensure previous version of PHP is updated from REMI + if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then + printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}" + else + printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}" + exit 1 + fi + fi + fi # Warn user of unsupported version of Fedora or CentOS + if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+" "${r}" "${c}"; then + printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}" + exit else - printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}" - exit 1 + printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}" fi fi - fi # Warn user of unsupported version of Fedora or CentOS - if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+" "${r}" "${c}"; then - printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}" - exit - else - printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}" fi -fi -fi } # A function for checking if a directory is a git repository @@ -519,7 +519,7 @@ update_repo() { # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) curBranch=$(git rev-parse --abbrev-ref HEAD) if [[ "${curBranch}" == "master" ]]; then - git reset --hard "$(git describe --abbrev=0 --tags)" || return $? + git reset --hard "$(git describe --abbrev=0 --tags)" || return $? fi # Show a completion message printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" @@ -629,12 +629,12 @@ welcomeDialogs() { IMPORTANT: If you have not already done so, you must ensure that this device has a static IP. Either through DHCP reservation, or by manually assigning one. Depending on your operating system, there are many ways to achieve this. Choose yes to indicate that you have understood this message, and wish to continue" "${r}" "${c}"; then -#Nothing to do, continue - echo -else - printf " %b Installer exited at static IP message.\\n" "${INFO}" - exit 1 -fi + #Nothing to do, continue + echo + else + printf " %b Installer exited at static IP message.\\n" "${INFO}" + exit 1 + fi } # A function that lets the user pick an interface to use with Pi-hole @@ -759,8 +759,8 @@ collect_v4andv6_information() { printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" # if `dhcpcd` is used offer to set this as static IP for the device if [[ -f "/etc/dhcpcd.conf" ]]; then - # configure networking via dhcpcd - getStaticIPv4Settings + # configure networking via dhcpcd + getStaticIPv4Settings fi find_IPv6_information printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}" @@ -913,8 +913,8 @@ setDNS() { IFS=${OIFS} # In a whiptail dialog, show the options DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." "${r}" "${c}" 7 \ - "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \ - # Exit if the user selects "Cancel" + "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \ + # Exit if the user selects "Cancel" { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # Depending on the user's choice, set the GLOBAL variables to the IP of the respective provider @@ -1180,8 +1180,8 @@ version_check_dnsmasq() { install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}" printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}" else - # Otherwise, don't to anything - printf " it is not a Pi-hole file, leaving alone!\\n" + # Otherwise, don't to anything + printf " it is not a Pi-hole file, leaving alone!\\n" fi else # If a file cannot be found, @@ -1216,8 +1216,8 @@ version_check_dnsmasq() { sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}" fi - # Set the cache size - sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}" + # Set the cache size + sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}" sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}" @@ -1565,7 +1565,7 @@ install_dependent_packages() { # Install Fedora/CentOS packages for i in "$@"; do - # For each package, check if it's already installed (and if so, don't add it to the installArray) + # For each package, check if it's already installed (and if so, don't add it to the installArray) printf " %b Checking for %s..." "${INFO}" "${i}" if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" @@ -1735,18 +1735,18 @@ finalExports() { fi # echo the information to the user { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" - echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" - echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" - echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" - echo "CACHE_SIZE=${CACHE_SIZE}" - echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}" - echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}" + echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" + echo "IPV4_ADDRESS=${IPV4_ADDRESS}" + echo "IPV6_ADDRESS=${IPV6_ADDRESS}" + echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" + echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" + echo "QUERY_LOGGING=${QUERY_LOGGING}" + echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" + echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" + echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" + echo "CACHE_SIZE=${CACHE_SIZE}" + echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}" + echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}" }>> "${setupVars}" chmod 644 "${setupVars}" @@ -1921,7 +1921,7 @@ displayFinalMessage() { additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin Your Admin Webpage login password is ${pwstring}" - fi + fi # Final completion message to user whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: @@ -2456,7 +2456,7 @@ main() { #In case of RPM based distro, select the proper PHP version if [[ "$PKG_MANAGER" == "yum" || "$PKG_MANAGER" == "dnf" ]] ; then - select_rpm_php + select_rpm_php fi # Check if SELinux is Enforcing diff --git a/gravity.sh b/gravity.sh index dfaf4fea..bb4f4e4b 100755 --- a/gravity.sh +++ b/gravity.sh @@ -213,7 +213,7 @@ database_table_from_file() { # Move source file to backup directory, create directory if not existing mkdir -p "${backup_path}" mv "${source}" "${backup_file}" 2> /dev/null || \ - echo -e " ${CROSS} Unable to backup ${source} to ${backup_path}" + echo -e " ${CROSS} Unable to backup ${source} to ${backup_path}" # Delete tmpFile rm "${tmpFile}" > /dev/null 2>&1 || \ @@ -430,9 +430,9 @@ gravity_DownloadBlocklists() { compression="--compressed" echo -e " ${INFO} Using libz compression\n" else - compression="" - echo -e " ${INFO} Libz compression not available\n" - fi + compression="" + echo -e " ${INFO} Libz compression not available\n" + fi # Loop through $sources and download each one for ((i = 0; i < "${#sources[@]}"; i++)); do url="${sources[$i]}" @@ -462,9 +462,9 @@ gravity_DownloadBlocklists() { check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$url" )" if [[ "${check_url}" =~ ${regex} ]]; then - echo -e " ${CROSS} Invalid Target" + echo -e " ${CROSS} Invalid Target" else - gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}" "${compression}" + gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}" "${compression}" fi echo "" done @@ -583,28 +583,28 @@ gravity_DownloadBlocklistFromUrl() { blocked=false case $BLOCKINGMODE in "IP-NODATA-AAAA"|"IP") - # Get IP address of this domain - ip="$(dig "${domain}" +short)" - # Check if this IP matches any IP of the system - if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then - blocked=true - fi;; + # Get IP address of this domain + ip="$(dig "${domain}" +short)" + # Check if this IP matches any IP of the system + if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then + blocked=true + fi;; "NXDOMAIN") - if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then - blocked=true - fi;; + if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then + blocked=true + fi;; "NULL"|*) - if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then - blocked=true - fi;; - esac + if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then + blocked=true + fi;; + esac if [[ "${blocked}" == true ]]; then printf -v ip_addr "%s" "${PIHOLE_DNS_1%#*}" if [[ ${PIHOLE_DNS_1} != *"#"* ]]; then - port=53 + port=53 else - printf -v port "%s" "${PIHOLE_DNS_1#*#}" + printf -v port "%s" "${PIHOLE_DNS_1#*#}" fi ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}" | tail -1) if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then @@ -623,11 +623,11 @@ gravity_DownloadBlocklistFromUrl() { case $url in # Did we "download" a local file? "file"*) - if [[ -s "${patternBuffer}" ]]; then - echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true - else - echo -e "${OVER} ${CROSS} ${str} Not found / empty list" - fi;; + if [[ -s "${patternBuffer}" ]]; then + echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true + else + echo -e "${OVER} ${CROSS} ${str} Not found / empty list" + fi;; # Did we "download" a remote file? *) # Determine "Status:" output based on HTTP response From 14a379d4485328b85c08ff6cee30b137210b23d5 Mon Sep 17 00:00:00 2001 From: MichaIng Date: Tue, 30 Nov 2021 02:57:44 +0100 Subject: [PATCH 10/36] Fix generated /etc/os-release file in OS check test Signed-off-by: MichaIng --- test/test_automated_install.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 911407fc..7959e100 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -1085,9 +1085,9 @@ def test_os_check_fails(host): install_dependent_packages ${OS_CHECK_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]} cat < /etc/os-release - ID=UnsupportedOS - VERSION_ID="2" - EOT +ID=UnsupportedOS +VERSION_ID="2" +EOT ''') detectOS = host.run('''t source /opt/pihole/basic-install.sh From bfda52ed79c5648c8956d530f4c20393e7377b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 1 Dec 2021 11:07:17 +0100 Subject: [PATCH 11/36] Fix number of invalid domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: abesnier Signed-off-by: Christian König --- gravity.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index bb4f4e4b..d66b3b83 100755 --- a/gravity.sh +++ b/gravity.sh @@ -516,7 +516,7 @@ parseList() { # Find (up to) five domains containing invalid characters (see above) incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)" - local num_target_lines num_correct_lines num_invalid + local num_target_lines num_correct_lines # Get number of lines in source file num_lines="$(grep -c "^" "${src}")" # Get number of lines in destination file From e485a7b9bb3da8e0ab7ccb5b16caac07ba70782d Mon Sep 17 00:00:00 2001 From: Chiller Dragon Date: Thu, 2 Dec 2021 14:44:50 +0100 Subject: [PATCH 12/36] Some shellchecks in basic-install.sh (#4088) * Some shellchecks in basic-install.sh Signed-off-by: ChillerDragon * Use more explicit grep (thanks to @MichaIng) Signed-off-by: ChillerDragon --- automated install/basic-install.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index cbb8ebae..bd2bf4c3 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC1090 # Pi-hole: A black hole for Internet advertisements -# (c) 2017-2018 Pi-hole, LLC (https://pi-hole.net) +# (c) 2017-2021 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # # Installs and Updates Pi-hole @@ -172,7 +172,7 @@ os_check() { local remote_os_domain valid_os valid_version valid_response detected_os detected_version display_warning cmdResult digReturnCode response remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"} - detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"') + detected_os=$(grep '^ID=' /etc/os-release | cut -d '=' -f2 | tr -d '"') detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"') cmdResult="$(dig +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)" @@ -856,7 +856,7 @@ valid_ip() { # Regex matching an optional port (starting with '#') range of 1-65536 local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"; # Build a full IPv4 regex from the above subexpressions - local regex="^${ipv4elem}\.${ipv4elem}\.${ipv4elem}\.${ipv4elem}${portelem}$" + local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$" # Evaluate the regex, and return the result [[ $ip =~ ${regex} ]] @@ -1862,7 +1862,7 @@ checkSelinux() { local CURRENT_SELINUX local SELINUX_ENFORCING=0 # Check for SELinux configuration file and getenforce command - if [[ -f /etc/selinux/config ]] && command -v getenforce &> /dev/null; then + if [[ -f /etc/selinux/config ]] && is_command getenforce; then # Check the default SELinux mode DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) case "${DEFAULT_SELINUX,,}" in @@ -2094,7 +2094,6 @@ clone_or_update_repos() { # shellcheck disable=SC2120 FTLinstall() { # Local, named variables - local latesttag local str="Downloading and Installing FTL" printf " %b %s..." "${INFO}" "${str}" @@ -2165,7 +2164,7 @@ FTLinstall() { disable_dnsmasq() { # dnsmasq can now be stopped and disabled if it exists - if which dnsmasq &> /dev/null; then + if is_command dnsmasq; then if check_service_active "dnsmasq";then printf " %b FTL can now resolve DNS Queries without dnsmasq running separately\\n" "${INFO}" stop_service dnsmasq @@ -2295,7 +2294,7 @@ FTLcheckUpdate() { local localSha1 # if dnsmasq exists and is running at this point, force reinstall of FTL Binary - if which dnsmasq &> /dev/null; then + if is_command dnsmasq; then if check_service_active "dnsmasq";then return 0 fi From ba6d700e7e46dce79f44fea3339193ee80164f1a Mon Sep 17 00:00:00 2001 From: Subhaditya Nath <56714626+subnut@users.noreply.github.com> Date: Fri, 3 Dec 2021 02:16:11 +0530 Subject: [PATCH 13/36] Fix `pihole -v` output if WebAdmin not installed (#4370) * Fix https://github.com/pi-hole/pi-hole/issues/4279 Signed-off-by: Subhaditya Nath * Don't ignore exit code of version.sh If it exits with a non-zero return code, that means some error occurred, and so it shouldn't be ignored. Signed-off-by: Subhaditya Nath * Implement changes suggested by @Michalng Signed-off-by: Subhaditya Nath * Implement changes suggested by @PromoFaux Signed-off-by: Subhaditya Nath * Always source /etc/pihole/setupVars.conf https://github.com/pi-hole/pi-hole/pull/4370#issuecomment-978149567 Co-authored-by: Adam Warner --- advanced/Scripts/version.sh | 19 +++++++++++++------ pihole | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh index 2cb0a2c9..0b7b29dd 100755 --- a/advanced/Scripts/version.sh +++ b/advanced/Scripts/version.sh @@ -13,6 +13,10 @@ DEFAULT="-1" COREGITDIR="/etc/.pihole/" WEBGITDIR="/var/www/html/admin/" +# Source the setupvars config file +# shellcheck disable=SC1091 +source /etc/pihole/setupVars.conf + getLocalVersion() { # FTL requires a different method if [[ "$1" == "FTL" ]]; then @@ -91,10 +95,11 @@ getRemoteVersion(){ #If the above file exists, then we can read from that. Prevents overuse of GitHub API if [[ -f "$cachedVersions" ]]; then IFS=' ' read -r -a arrCache < "$cachedVersions" + case $daemon in "pi-hole" ) echo "${arrCache[0]}";; - "AdminLTE" ) echo "${arrCache[1]}";; - "FTL" ) echo "${arrCache[2]}";; + "AdminLTE" ) [[ "${INSTALL_WEB_INTERFACE}" == true ]] && echo "${arrCache[1]}";; + "FTL" ) [[ "${INSTALL_WEB_INTERFACE}" == true ]] && echo "${arrCache[2]}" || echo "${arrCache[1]}";; esac return 0 @@ -140,6 +145,11 @@ getLocalBranch(){ } versionOutput() { + if [[ "$1" == "AdminLTE" && "${INSTALL_WEB_INTERFACE}" != true ]]; then + echo " WebAdmin not installed" + return 1 + fi + [[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR [[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR [[ "$1" == "FTL" ]] && GITDIR="FTL" @@ -166,6 +176,7 @@ versionOutput() { output="Latest ${1^} hash is $latHash" else errorOutput + return 1 fi [[ -n "$output" ]] && echo " $output" @@ -177,10 +188,6 @@ errorOutput() { } defaultOutput() { - # Source the setupvars config file - # shellcheck disable=SC1091 - source /etc/pihole/setupVars.conf - versionOutput "pi-hole" "$@" if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then diff --git a/pihole b/pihole index 31356671..4f1b95eb 100755 --- a/pihole +++ b/pihole @@ -95,8 +95,7 @@ uninstallFunc() { versionFunc() { shift - "${PI_HOLE_SCRIPT_DIR}"/version.sh "$@" - exit 0 + exec "${PI_HOLE_SCRIPT_DIR}"/version.sh "$@" } # Get PID of main pihole-FTL process From 647ba6ec9d92bde1955cc8474aec565b433ca088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 2 Dec 2021 23:03:46 +0100 Subject: [PATCH 14/36] Rename variables to improve comprehensibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- gravity.sh | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/gravity.sh b/gravity.sh index d66b3b83..b2cd27b9 100755 --- a/gravity.sh +++ b/gravity.sh @@ -248,7 +248,7 @@ database_adlist_number() { return; fi - output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${num_lines}" "${num_invalid}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 ) + output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${num_source_lines}" "${num_invalid}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 ) status="$?" if [[ "${status}" -ne 0 ]]; then @@ -503,8 +503,9 @@ gravity_DownloadBlocklists() { gravity_Blackbody=true } -total_num=0 -num_lines=0 +# num_target_lines does increase for every correctly added domain in pareseList() +num_target_lines=0 +num_source_lines=0 num_invalid=0 parseList() { local adlistID="${1}" src="${2}" target="${3}" incorrect_lines @@ -516,18 +517,20 @@ parseList() { # Find (up to) five domains containing invalid characters (see above) incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)" - local num_target_lines num_correct_lines + local num_target_lines_new num_correct_lines # Get number of lines in source file - num_lines="$(grep -c "^" "${src}")" - # Get number of lines in destination file - num_target_lines="$(grep -c "^" "${target}")" - num_correct_lines="$(( num_target_lines-total_num ))" - total_num="$num_target_lines" - num_invalid="$(( num_lines-num_correct_lines ))" + num_source_lines="$(grep -c "^" "${src}")" + # Get the new number of lines in destination file + num_target_lines_new="$(grep -c "^" "${target}")" + # Number of new correctly added lines + num_correct_lines="$(( num_target_lines_new-num_target_lines ))" + # Upate number of lines in target file + num_target_lines="$num_target_lines_new" + num_invalid="$(( num_source_lines-num_correct_lines ))" if [[ "${num_invalid}" -eq 0 ]]; then - echo " ${INFO} Analyzed ${num_lines} domains" + echo " ${INFO} Analyzed ${num_source_lines} domains" else - echo " ${INFO} Analyzed ${num_lines} domains, ${num_invalid} domains invalid!" + echo " ${INFO} Analyzed ${num_source_lines} domains, ${num_invalid} domains invalid!" fi # Display sample of invalid lines if we found some @@ -686,7 +689,7 @@ gravity_DownloadBlocklistFromUrl() { else echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}" # Manually reset these two numbers because we do not call parseList here - num_lines=0 + num_source_lines=0 num_invalid=0 database_adlist_number "${adlistID}" database_adlist_status "${adlistID}" "4" From e243c562c21e1c3d03c87812b45ac07c6f7194c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 3 Dec 2021 09:17:19 +0100 Subject: [PATCH 15/36] Unblock adlist domain during gravity run in NODATA mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- gravity.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gravity.sh b/gravity.sh index b2cd27b9..2ac9028a 100755 --- a/gravity.sh +++ b/gravity.sh @@ -596,6 +596,10 @@ gravity_DownloadBlocklistFromUrl() { if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then blocked=true fi;; + "NODATA") + if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +noall +answer "${domain}" |awk '{print $5}') ]]; then + blocked=true + fi;; "NULL"|*) if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then blocked=true From ae39e338fef3054e95eb6910b15e7d585195d4e2 Mon Sep 17 00:00:00 2001 From: yubiuser Date: Sat, 4 Dec 2021 10:50:21 +0100 Subject: [PATCH 16/36] Use exec to run gravity script (#4449) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- pihole | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pihole b/pihole index 4f1b95eb..055bd702 100755 --- a/pihole +++ b/pihole @@ -71,8 +71,7 @@ reconfigurePiholeFunc() { } updateGravityFunc() { - "${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@" - exit $? + exec "${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@" } queryFunc() { From b52a3a021daa77f0605316c8e07f744514d2731d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Mon, 6 Dec 2021 20:27:13 +0100 Subject: [PATCH 17/36] Add comment help text to list function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/Scripts/list.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index bc254515..fda8603c 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -91,7 +91,8 @@ Options: -q, --quiet Make output less verbose -h, --help Show this help dialog -l, --list Display all your ${listname}listed domains - --nuke Removes all entries in a list" + --nuke Removes all entries in a list + --comment Add a comment to the domain. If adding multiple domains the same comment will be used for all" exit 0 } From 8d6ce78c655e9a5d0bbf57f2cbf92b594473ca90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 10 Dec 2021 07:09:42 +0100 Subject: [PATCH 18/36] Allow qr code iframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/lighttpd.conf.debian | 5 +++++ advanced/lighttpd.conf.fedora | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index a58b5a88..8966dc32 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -92,5 +92,10 @@ $HTTP["url"] =~ "/teleporter\.php$" { } } +# allow API qr code iframe on settings page +$HTTP["url"] =~ "/admin/settings\.php$" { + setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) +} + # Default expire header expire.url = ( "" => "access plus 0 seconds" ) diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index ad336a93..6bf9e683 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -100,5 +100,10 @@ $HTTP["url"] =~ "/teleporter\.php$" { } } +# allow API qr code iframe on settings page +$HTTP["url"] =~ "/admin/settings\.php$" { + setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) +} + # Default expire header expire.url = ( "" => "access plus 0 seconds" ) From 2eff53b2bbc19f899f206ccaf1cbf3d6acb6f57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 10 Dec 2021 07:17:13 +0100 Subject: [PATCH 19/36] Allow qr code iframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/lighttpd.conf.debian | 6 ++++-- advanced/lighttpd.conf.fedora | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index 8966dc32..37099ad7 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -93,8 +93,10 @@ $HTTP["url"] =~ "/teleporter\.php$" { } # allow API qr code iframe on settings page -$HTTP["url"] =~ "/admin/settings\.php$" { - setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) +$HTTP["url"] =~ "/api_token\.php$" { + $HTTP["referer"] =~ "/admin/settings\.php" { + setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) + } } # Default expire header diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index 6bf9e683..f4916422 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -101,8 +101,10 @@ $HTTP["url"] =~ "/teleporter\.php$" { } # allow API qr code iframe on settings page -$HTTP["url"] =~ "/admin/settings\.php$" { - setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) +$HTTP["url"] =~ "/api_token\.php$" { + $HTTP["referer"] =~ "/admin/settings\.php" { + setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) + } } # Default expire header From 28085cf7d8386608470d66ec59d3947b34c7970f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 17 Dec 2021 10:08:16 +0100 Subject: [PATCH 20/36] Merge iFrame exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- advanced/lighttpd.conf.debian | 11 ++--------- advanced/lighttpd.conf.fedora | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index 37099ad7..cf728e19 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -85,15 +85,8 @@ $HTTP["url"] =~ "^/admin/\.(.*)" { url.access-deny = ("") } -# allow teleporter iframe on settings page -$HTTP["url"] =~ "/teleporter\.php$" { - $HTTP["referer"] =~ "/admin/settings\.php" { - setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) - } -} - -# allow API qr code iframe on settings page -$HTTP["url"] =~ "/api_token\.php$" { +# allow teleporter and API qr code iframe on settings page +$HTTP["url"] =~ "/(teleporter|api_token)\.php$" { $HTTP["referer"] =~ "/admin/settings\.php" { setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) } diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index f4916422..626a3d8d 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -93,15 +93,8 @@ $HTTP["url"] =~ "^/admin/\.(.*)" { url.access-deny = ("") } -# allow teleporter iframe on settings page -$HTTP["url"] =~ "/teleporter\.php$" { - $HTTP["referer"] =~ "/admin/settings\.php" { - setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) - } -} - -# allow API qr code iframe on settings page -$HTTP["url"] =~ "/api_token\.php$" { +# allow teleporter and API qr code iframe on settings page +$HTTP["url"] =~ "/(teleporter|api_token)\.php$" { $HTTP["referer"] =~ "/admin/settings\.php" { setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) } From a780fc59e28b5cd9be7e60fa9c530db16fffff96 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Mon, 20 Dec 2021 10:56:42 -0800 Subject: [PATCH 21/36] Set DBFile permissions on creation. Signed-off-by: Dan Schaper --- gravity.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gravity.sh b/gravity.sh index b2cd27b9..81bca09d 100755 --- a/gravity.sh +++ b/gravity.sh @@ -75,7 +75,9 @@ fi # Generate new sqlite3 file from schema template generate_gravity_database() { - sqlite3 "${1}" < "${gravityDBschema}" + sqlite3 "${gravityDBFile}" < "${gravityDBschema}" + chown pihole:pihole "${gravityDBfile}" + chmod g+w "${piholeDir}" "${gravityDBfile}" } # Copy data from old to new database file and swap them @@ -279,7 +281,7 @@ migrate_to_database() { if [ ! -e "${gravityDBfile}" ]; then # Create new database file - note that this will be created in version 1 echo -e " ${INFO} Creating new gravity database" - generate_gravity_database "${gravityDBfile}" + generate_gravity_database # Check if gravity database needs to be updated upgrade_gravityDB "${gravityDBfile}" "${piholeDir}" From 76ae75689c9c2a20266cd4dc4d2ec098a08d215a Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Mon, 20 Dec 2021 11:09:11 -0800 Subject: [PATCH 22/36] Check for DNS before run. Signed-off-by: Dan Schaper --- gravity.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index 81bca09d..f3754a9f 100755 --- a/gravity.sh +++ b/gravity.sh @@ -898,7 +898,11 @@ if [[ "${forceDelete:-}" == true ]]; then fi # Gravity downloads blocklists next -gravity_CheckDNSResolutionAvailable +if [[ ! gravity_CheckDNSResolutionAvailable ]]; then + echo -e " ${CROSS} Can not complete gravity update, no DNS is available. Please contact support." + exit 1 +fi + gravity_DownloadBlocklists # Create local.list From 533a77d6d5a4fad51f243f844c5c82b7b50b4476 Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Mon, 20 Dec 2021 11:36:55 -0800 Subject: [PATCH 23/36] Add database function failure guards. Signed-off-by: Dan Schaper --- gravity.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/gravity.sh b/gravity.sh index f3754a9f..ec6e149a 100755 --- a/gravity.sh +++ b/gravity.sh @@ -75,7 +75,10 @@ fi # Generate new sqlite3 file from schema template generate_gravity_database() { - sqlite3 "${gravityDBFile}" < "${gravityDBschema}" + if ! sqlite3 "${gravityDBfile}" < "${gravityDBschema}"; then + echo -e " ${CROSS} Unable to create ${gravityDBfile}" + return 1 + fi chown pihole:pihole "${gravityDBfile}" chmod g+w "${piholeDir}" "${gravityDBfile}" } @@ -281,7 +284,10 @@ migrate_to_database() { if [ ! -e "${gravityDBfile}" ]; then # Create new database file - note that this will be created in version 1 echo -e " ${INFO} Creating new gravity database" - generate_gravity_database + if ! generate_gravity_database; then + echo -e " ${CROSS} Error creating new gravity database. Please contact support." + return 1 + fi # Check if gravity database needs to be updated upgrade_gravityDB "${gravityDBfile}" "${piholeDir}" @@ -887,7 +893,10 @@ if [[ "${recreate_database:-}" == true ]]; then fi # Move possibly existing legacy files to the gravity database -migrate_to_database +if ! migrate_to_database; then + echo -e " ${CROSS} Unable to migrate to database. Please contact support." + exit 1 +fi if [[ "${forceDelete:-}" == true ]]; then str="Deleting existing list cache" @@ -898,7 +907,7 @@ if [[ "${forceDelete:-}" == true ]]; then fi # Gravity downloads blocklists next -if [[ ! gravity_CheckDNSResolutionAvailable ]]; then +if ! gravity_CheckDNSResolutionAvailable; then echo -e " ${CROSS} Can not complete gravity update, no DNS is available. Please contact support." exit 1 fi @@ -909,7 +918,10 @@ gravity_DownloadBlocklists gravity_generateLocalList # Migrate rest of the data from old to new database -gravity_swap_databases +if ! gravity_swap_databases; then + echo -e " ${CROSS} Unable to create database. Please contact support." + exit 1 +fi # Update gravity timestamp update_gravity_timestamp From c0f454ddfa7230944e894e8ddab01d15704caa6e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Dec 2021 21:36:19 +0100 Subject: [PATCH 24/36] Add new interface listening option "bind" (#4476) Signed-off-by: DL6ER --- advanced/Scripts/webpage.sh | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index ec9b2cac..4f44eca8 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -199,6 +199,8 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423 # Setup interface listening behavior of dnsmasq delete_dnsmasq_setting "interface" delete_dnsmasq_setting "local-service" + delete_dnsmasq_setting "except-interface" + delete_dnsmasq_setting "bind-interfaces" if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then # Listen on all interfaces, permit all origins @@ -207,6 +209,7 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423 # Listen only on all interfaces, but only local subnets add_dnsmasq_setting "local-service" else + # Options "bind" and "single" # Listen only on one interface # Use eth0 as fallback interface if interface is missing in setupVars.conf if [ -z "${PIHOLE_INTERFACE}" ]; then @@ -214,6 +217,11 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423 fi add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}" + + if [[ "${DNSMASQ_LISTENING}" == "bind" ]]; then + # Really bind to interface + add_dnsmasq_setting "bind-interfaces" + fi fi if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then @@ -600,9 +608,10 @@ Example: 'pihole -a -i local' Specify dnsmasq's network interface listening behavior Interfaces: - local Listen on all interfaces, but only allow queries from - devices that are at most one hop away (local devices) - single Listen only on ${PIHOLE_INTERFACE} interface + local Only respond to queries from devices that + are at most one hop away (local devices) + single Respond only on interface ${PIHOLE_INTERFACE} + bind Bind only on interface ${PIHOLE_INTERFACE} all Listen on all interfaces, permit all origins" exit 0 fi @@ -613,6 +622,9 @@ Interfaces: elif [[ "${args[2]}" == "local" ]]; then echo -e " ${INFO} Listening on all interfaces, permitting origins from one hop away (LAN)" change_setting "DNSMASQ_LISTENING" "local" + elif [[ "${args[2]}" == "bind" ]]; then + echo -e " ${INFO} Binding on interface ${PIHOLE_INTERFACE}" + change_setting "DNSMASQ_LISTENING" "bind" else echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}" change_setting "DNSMASQ_LISTENING" "single" From 669f1b0f4ab1f27ed5c7b29e363ef1beb395f4fb Mon Sep 17 00:00:00 2001 From: yubiuser Date: Tue, 21 Dec 2021 12:58:39 +0100 Subject: [PATCH 25/36] Address reviewer's comment Co-authored-by: DL6ER --- advanced/Scripts/list.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index fda8603c..ffde075a 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -92,7 +92,7 @@ Options: -h, --help Show this help dialog -l, --list Display all your ${listname}listed domains --nuke Removes all entries in a list - --comment Add a comment to the domain. If adding multiple domains the same comment will be used for all" + --comment "text" Add a comment to the domain. If adding multiple domains the same comment will be used for all" exit 0 } From 190ab796063a53ac9843fd97cc8f978d10bd4a56 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 20 Aug 2021 20:48:57 +0200 Subject: [PATCH 26/36] Implement fully-automated gravity database recovery method. Signed-off-by: DL6ER --- gravity.sh | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/gravity.sh b/gravity.sh index b2cd27b9..911242f7 100755 --- a/gravity.sh +++ b/gravity.sh @@ -848,6 +848,42 @@ gravity_Cleanup() { fi } +database_recovery() { + str="Checking integrity of existing gravity database" + echo -ne " ${INFO} ${str}..." + if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA integrity_check" 2>&1)"; then + echo -e "${OVER} ${TICK} ${str} - no errors found" + else + echo -e "${OVER} ${CROSS} ${str} - errors found:" + while IFS= read -r line ; do echo " - $line"; done <<< "$result" + fi + + str="Checking foreign keys of existing gravity database" + echo -ne " ${INFO} ${str}..." + if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then + echo -e "${OVER} ${TICK} ${str} - no errors found" + else + echo -e "${OVER} ${CROSS} ${str} - errors found:" + while IFS= read -r line ; do echo " - $line"; done <<< "$result" + fi + + str="Trying to recover existing gravity database" + echo -ne " ${INFO} ${str}..." + # We have to remove any possibly existing recovery database or this will fail + rm -f "${gravityDBfile}.recovered" > /dev/null 2>&1 + if result="$(pihole-FTL sqlite3 "${gravityDBfile}" ".recover" | pihole-FTL sqlite3 "${gravityDBfile}.recovered" 2>&1)"; then + echo -e "${OVER} ${TICK} ${str} - success" + mv "${gravityDBfile}" "${gravityDBfile}.old" + mv "${gravityDBfile}.recovered" "${gravityDBfile}" + else + echo -e "${OVER} ${CROSS} ${str} - the following errors happened:" + while IFS= read -r line ; do echo " - $line"; done <<< "$result" + echo -e " ${CROSS} Recovery failed. Try \"pihole -r recreate\" instead." + exit 1 + fi + echo "" +} + helpFunc() { echo "Usage: pihole -g Update domains from blocklists specified in adlists.list @@ -858,10 +894,30 @@ Options: exit 0 } +repairSelector() { + case "$1" in + "recover") recover_database=true;; + "recreate") recreate_database=true;; + *) echo "Usage: pihole -g -r {recover,recreate} +Attempt to repair gravity database + +Available options: + pihole -g -r recover Try to recover a damaged gravity database file. + Pi-hole tries to restore as much as possible + from a corrupted gravity database. + pihole -g -r recreate Create a new gravity database file from scratch. + This will remove your existing gravity database + and create a new file from scratch. If you still + have the migration backup created when migrating + to Pi-hole v5.0, Pi-hole will import these files." + exit 0;; + esac +} + for var in "$@"; do case "${var}" in "-f" | "--force" ) forceDelete=true;; - "-r" | "--recreate" ) recreate_database=true;; + "-r" | "--repair" ) repairSelector "$3";; "-h" | "--help" ) helpFunc;; esac done @@ -875,7 +931,7 @@ fi gravity_Trap if [[ "${recreate_database:-}" == true ]]; then - str="Restoring from migration backup" + str="Recreating gravity database from migration backup" echo -ne "${INFO} ${str}..." rm "${gravityDBfile}" pushd "${piholeDir}" > /dev/null || exit @@ -884,6 +940,10 @@ if [[ "${recreate_database:-}" == true ]]; then echo -e "${OVER} ${TICK} ${str}" fi +if [[ "${recover_database:-}" == true ]]; then + database_recovery +fi + # Move possibly existing legacy files to the gravity database migrate_to_database From 469c179b328f93130fb7ccb5334585b15b94dc55 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 11 Sep 2021 21:54:42 +0200 Subject: [PATCH 27/36] Return early from recovery routine when integrity checks didn't show any database errors. Signed-off-by: DL6ER --- gravity.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gravity.sh b/gravity.sh index 911242f7..2d9b5dee 100755 --- a/gravity.sh +++ b/gravity.sh @@ -853,15 +853,16 @@ database_recovery() { echo -ne " ${INFO} ${str}..." if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA integrity_check" 2>&1)"; then echo -e "${OVER} ${TICK} ${str} - no errors found" - else - echo -e "${OVER} ${CROSS} ${str} - errors found:" - while IFS= read -r line ; do echo " - $line"; done <<< "$result" - fi - str="Checking foreign keys of existing gravity database" - echo -ne " ${INFO} ${str}..." - if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then - echo -e "${OVER} ${TICK} ${str} - no errors found" + str="Checking foreign keys of existing gravity database" + echo -ne " ${INFO} ${str}..." + if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then + echo -e "${OVER} ${TICK} ${str} - no errors found" + return + else + echo -e "${OVER} ${CROSS} ${str} - errors found:" + while IFS= read -r line ; do echo " - $line"; done <<< "$result" + fi else echo -e "${OVER} ${CROSS} ${str} - errors found:" while IFS= read -r line ; do echo " - $line"; done <<< "$result" From ab4bce4787093428305647ad547ed61c8d4809fd Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 11 Sep 2021 21:56:44 +0200 Subject: [PATCH 28/36] Allow users to force recovery even when checks are okay using "pihole -g -r recover force" Signed-off-by: DL6ER --- gravity.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gravity.sh b/gravity.sh index 2d9b5dee..c2033fcf 100755 --- a/gravity.sh +++ b/gravity.sh @@ -858,7 +858,9 @@ database_recovery() { echo -ne " ${INFO} ${str}..." if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then echo -e "${OVER} ${TICK} ${str} - no errors found" - return + if [[ $1 != "force" ]]; then + return + fi else echo -e "${OVER} ${CROSS} ${str} - errors found:" while IFS= read -r line ; do echo " - $line"; done <<< "$result" @@ -942,7 +944,7 @@ if [[ "${recreate_database:-}" == true ]]; then fi if [[ "${recover_database:-}" == true ]]; then - database_recovery + database_recovery "$4" fi # Move possibly existing legacy files to the gravity database From b06efb6ab7c3400b99cd7e4f8509cf52092d3671 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 21 Dec 2021 14:00:46 +0100 Subject: [PATCH 29/36] Declare variables local Signed-off-by: DL6ER --- gravity.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index c2033fcf..bd2ae909 100755 --- a/gravity.sh +++ b/gravity.sh @@ -849,7 +849,8 @@ gravity_Cleanup() { } database_recovery() { - str="Checking integrity of existing gravity database" + local result + local str="Checking integrity of existing gravity database" echo -ne " ${INFO} ${str}..." if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA integrity_check" 2>&1)"; then echo -e "${OVER} ${TICK} ${str} - no errors found" From 39a66b608b70e25e8f8218ce68fc15bd009a3ffa Mon Sep 17 00:00:00 2001 From: yubiuser Date: Tue, 21 Dec 2021 14:00:47 +0100 Subject: [PATCH 30/36] Replace Contributing Guide by link to docs.pi-hole.net (#4433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- CONTRIBUTING.md | 107 +----------------------------------------------- 1 file changed, 1 insertion(+), 106 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0dd22b42..018b8c5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,111 +2,6 @@ Please read and understand the contribution guide before creating an issue or pull request. -## Etiquette +The guide can be found here: [https://docs.pi-hole.net/guides/github/contributing/](https://docs.pi-hole.net/guides/github/contributing/) -- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature. -- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that. -- Please be considerate towards the developers and other users when raising issues or presenting pull requests. -- Respect our decision(s), and do not be upset or abusive if your submission is not used. -## Viability - -When requesting or submitting new features, first consider whether it might be useful to others. Open source projects are used by many people, who may have entirely different needs to your own. Think about whether or not your feature is likely to be used by other users of the project. - -## Procedure - -**Before filing an issue:** - -- Attempt to replicate and **document** the problem, to ensure that it wasn't a coincidental incident. -- Check to make sure your feature suggestion isn't already present within the project. -- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. -- Check the pull requests tab to ensure that the feature isn't already in progress. - -**Before submitting a pull request:** - -- Check the codebase to ensure that your feature doesn't already exist. -- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. -- Read and understand the [DCO guidelines](https://docs.pi-hole.net/guides/github/contributing/) for the project. - -## Technical Requirements - -- Submit Pull Requests to the **development branch only**. -- Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!) -- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles. -- Commit Unix line endings. -- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen) -- (Optional fun) keep to the theme of Star Trek/black holes/gravity. - -## Forking and Cloning from GitHub to GitHub - -1. Fork to a repo under a namespace you control, or have permission to use, for example: `https://github.com///`. You can do this from the github.com website. -2. Clone `https://github.com///` with the tool of you choice. -3. To keep your fork in sync with our repo, add an upstream remote for pi-hole/pi-hole to your repo. - - ```bash - git remote add upstream https://github.com/pi-hole/pi-hole.git - ``` - -4. Checkout the `development` branch from your fork `https://github.com///`. -5. Create a topic/branch, based on the `development` branch code. *Bonus fun to keep to the theme of Star Trek/black holes/gravity.* -6. Make your changes and commit to your topic branch in your repo. -7. Rebase your commits and squash any insignificant commits. See the notes below for an example. -8. Merge `development` your branch and fix any conflicts. -9. Open a Pull Request to merge your topic branch into our repo's `development` branch. - -- Keep in mind the technical requirements from above. - -## Forking and Cloning from GitHub to other code hosting sites - -- Forking is a GitHub concept and cannot be done from GitHub to other git-based code hosting sites. However, those sites may be able to mirror a GitHub repo. - -1. To contribute from another code hosting site, you must first complete the steps above to fork our repo to a GitHub namespace you have permission to use, for example: `https://github.com///`. -2. Create a repo in your code hosting site, for example: `https://gitlab.com///` -3. Follow the instructions from your code hosting site to create a mirror between `https://github.com///` and `https://gitlab.com///`. -4. When you are ready to create a Pull Request (PR), follow the steps `(starting at step #6)` from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) and create the PR from `https://github.com///`. - -## Notes for squashing commits with rebase - -- To rebase your commits and squash previous commits, you can use: - - ```bash - git rebase -i your_topic_branch~(number of commits to combine) - ``` - -- For more details visit [gitready.com](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) - -1. The following would combine the last four commits in the branch `mytopic`. - - ```bash - git rebase -i mytopic~4 - ``` - -2. An editor window opens with the most recent commits indicated: (edit the commands to the left of the commit ID) - - ```gitattributes - pick 9dff55b2 existing commit comments - squash ebb1a730 existing commit comments - squash 07cc5b50 existing commit comments - reword 9dff55b2 existing commit comments - ``` - -3. Save and close the editor. The next editor window opens: (edit the new commit message). *If you select reword for a commit, an additional editor window will open for you to edit the comment.* - - ```bash - new commit comments - Signed-off-by: yourname - ``` - -4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following: - - ```bash - Successfully rebased and updated refs/heads/mytopic. - ``` - -5. Once you have a successful rebase, and before you sync your local clone, you have to force push origin to update your repo: - - ```bash - git push -f origin - ``` - -6. Continue on from step #7 from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) From 7167e6d5e42869f8ab370565ae4517cef3b6821e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 21 Dec 2021 16:20:02 +0100 Subject: [PATCH 31/36] Apply suggestions from code review Co-authored-by: Dan Schaper --- gravity.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index bd2ae909..b1ecdc08 100755 --- a/gravity.sh +++ b/gravity.sh @@ -851,6 +851,7 @@ gravity_Cleanup() { database_recovery() { local result local str="Checking integrity of existing gravity database" + local option="${1}" echo -ne " ${INFO} ${str}..." if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA integrity_check" 2>&1)"; then echo -e "${OVER} ${TICK} ${str} - no errors found" @@ -859,7 +860,7 @@ database_recovery() { echo -ne " ${INFO} ${str}..." if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then echo -e "${OVER} ${TICK} ${str} - no errors found" - if [[ $1 != "force" ]]; then + if [[ "${option}" != "force" ]]; then return fi else From db5e94b14a4e5a0208cd3515cfc4e25356335120 Mon Sep 17 00:00:00 2001 From: yubiuser Date: Tue, 21 Dec 2021 22:01:34 +0100 Subject: [PATCH 32/36] use +short and omit obsolet awk Co-authored-by: DL6ER --- gravity.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gravity.sh b/gravity.sh index 2ac9028a..8fd3a47b 100755 --- a/gravity.sh +++ b/gravity.sh @@ -597,7 +597,7 @@ gravity_DownloadBlocklistFromUrl() { blocked=true fi;; "NODATA") - if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +noall +answer "${domain}" |awk '{print $5}') ]]; then + if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +short "${domain}") ]]; then blocked=true fi;; "NULL"|*) From ff4487ff749dcaa7eaf2b72ecaeb068209831fcd Mon Sep 17 00:00:00 2001 From: yubiuser Date: Tue, 21 Dec 2021 22:10:56 +0100 Subject: [PATCH 33/36] Escape quotes Co-authored-by: DL6ER --- advanced/Scripts/list.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index ffde075a..8945047e 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -92,7 +92,7 @@ Options: -h, --help Show this help dialog -l, --list Display all your ${listname}listed domains --nuke Removes all entries in a list - --comment "text" Add a comment to the domain. If adding multiple domains the same comment will be used for all" + --comment \"text\" Add a comment to the domain. If adding multiple domains the same comment will be used for all" exit 0 } From 920cf6de144b02950277b3b509a71c0adbad93a0 Mon Sep 17 00:00:00 2001 From: yubiuser Date: Wed, 22 Dec 2021 19:21:44 +0100 Subject: [PATCH 34/36] Check for updates on master based on tags not commits (#4475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Check for updates on master based on tags not commits Signed-off-by: Christian König * Fix stickler Signed-off-by: Christian König * Address reviewer's comments Signed-off-by: Christian König * Fix stickler again Signed-off-by: Christian König * Use local git instead of relying on github Signed-off-by: Christian König * Add --tags Co-authored-by: DL6ER Co-authored-by: DL6ER --- advanced/Scripts/update.sh | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 3fab9a95..d18d2e78 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -35,25 +35,37 @@ source "/opt/pihole/COL_TABLE" GitCheckUpdateAvail() { local directory + local curBranch directory="${1}" curdir=$PWD cd "${directory}" || return # Fetch latest changes in this repo - git fetch --quiet origin + git fetch --tags --quiet origin - # @ alone is a shortcut for HEAD. Older versions of git - # need @{0} - LOCAL="$(git rev-parse "@{0}")" + # Check current branch. If it is master, then check for the latest available tag instead of latest commit. + curBranch=$(git rev-parse --abbrev-ref HEAD) + if [[ "${curBranch}" == "master" ]]; then + # get the latest local tag + LOCAL=$(git describe --abbrev=0 --tags master) + # get the latest tag from remote + REMOTE=$(git describe --abbrev=0 --tags origin/master) + + else + # @ alone is a shortcut for HEAD. Older versions of git + # need @{0} + LOCAL="$(git rev-parse "@{0}")" + + # The suffix @{upstream} to a branchname + # (short form @{u}) refers + # to the branch that the branch specified + # by branchname is set to build on top of# + # (configured with branch..remote and + # branch..merge). A missing branchname + # defaults to the current one. + REMOTE="$(git rev-parse "@{upstream}")" + fi - # The suffix @{upstream} to a branchname - # (short form @{u}) refers - # to the branch that the branch specified - # by branchname is set to build on top of# - # (configured with branch..remote and - # branch..merge). A missing branchname - # defaults to the current one. - REMOTE="$(git rev-parse "@{upstream}")" if [[ "${#LOCAL}" == 0 ]]; then echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support" From 2e1ce7fc87a5934c9316db1ce1824ac0e3636dcd Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Dec 2021 19:52:08 +0100 Subject: [PATCH 35/36] Apply suggestions from code review Co-authored-by: yubiuser --- gravity.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gravity.sh b/gravity.sh index b1ecdc08..385ec3de 100755 --- a/gravity.sh +++ b/gravity.sh @@ -880,6 +880,8 @@ database_recovery() { echo -e "${OVER} ${TICK} ${str} - success" mv "${gravityDBfile}" "${gravityDBfile}.old" mv "${gravityDBfile}.recovered" "${gravityDBfile}" + echo -ne " ${INFO} ${gravityDBfile} has been recovered" + echo -ne " ${INFO} The old ${gravityDBfile} has been moved to ${gravityDBfile}.old" else echo -e "${OVER} ${CROSS} ${str} - the following errors happened:" while IFS= read -r line ; do echo " - $line"; done <<< "$result" From d2a98ae95498d6f72d0b1ceee3d46ec2e7ecfe4c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Dec 2021 19:53:52 +0100 Subject: [PATCH 36/36] Document -r recover force case Signed-off-by: DL6ER --- gravity.sh | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/gravity.sh b/gravity.sh index 385ec3de..4fcc281a 100755 --- a/gravity.sh +++ b/gravity.sh @@ -909,14 +909,21 @@ repairSelector() { Attempt to repair gravity database Available options: - pihole -g -r recover Try to recover a damaged gravity database file. - Pi-hole tries to restore as much as possible - from a corrupted gravity database. - pihole -g -r recreate Create a new gravity database file from scratch. - This will remove your existing gravity database - and create a new file from scratch. If you still - have the migration backup created when migrating - to Pi-hole v5.0, Pi-hole will import these files." + pihole -g -r recover Try to recover a damaged gravity database file. + Pi-hole tries to restore as much as possible + from a corrupted gravity database. + + pihole -g -r recover force Pi-hole will run the recovery process even when + no damage is detected. This option is meant to be + a last resort. Recovery is a fragile task + consuming a lot of resources and shouldn't be + performed unnecessarily. + + pihole -g -r recreate Create a new gravity database file from scratch. + This will remove your existing gravity database + and create a new file from scratch. If you still + have the migration backup created when migrating + to Pi-hole v5.0, Pi-hole will import these files." exit 0;; esac }