From 4f21f677758d62c20b4a9f9165e043c2a50713d2 Mon Sep 17 00:00:00 2001 From: John Crisp Date: Mon, 16 Sep 2019 14:46:09 +0200 Subject: [PATCH 01/34] Update pihole Fix spelling typos --- pihole | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pihole b/pihole index 971595d5..4a358443 100755 --- a/pihole +++ b/pihole @@ -11,8 +11,8 @@ readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" -# setupVars and PI_HOLE_BIN_DIR are not readonly here because in some funcitons (checkout), -# it might get set again when the installer is sourced. This causes an +# setupVars and PI_HOLE_BIN_DIR are not readonly here because in some functions (checkout), +# they might get set again when the installer is sourced. This causes an # error due to modifying a readonly variable. setupVars="/etc/pihole/setupVars.conf" PI_HOLE_BIN_DIR="/usr/local/bin" From fc0899b2ad75cf42f85668fddd4646b651a9429a Mon Sep 17 00:00:00 2001 From: bcambl Date: Sun, 13 Oct 2019 14:35:38 -0600 Subject: [PATCH 02/34] fix fedora dependency check/install stdout Signed-off-by: bcambl --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index cc78afbf..11b78fcd 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1645,9 +1645,9 @@ install_dependent_packages() { for i in "$@"; do printf " %b Checking for %s..." "${INFO}" "${i}" if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then - printf "%b %b Checking for %s" "${OVER}" "${TICK}" "${i}" + printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" else - printf "%b %b Checking for %s (will be installed)" "${OVER}" "${INFO}" "${i}" + echo -e "${OVER} ${INFO} Checking for $i (will be installed)" installArray+=("${i}") fi done From 81ca78e7f40e395a38502ae55f84969796e65b13 Mon Sep 17 00:00:00 2001 From: bcambl Date: Mon, 14 Oct 2019 12:14:45 -0600 Subject: [PATCH 03/34] exit installer if SELinux is enforcing The Pi-hole project does not ship a custom SELinux policy as the required policy would lower the overall system security. Users who require SELinux to be enforcing are encouraged to create an custom policy on a case-by-case basis. Signed-off-by: bcambl --- automated install/basic-install.sh | 50 +++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index cc78afbf..091c543a 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1959,20 +1959,42 @@ installPihole() { # SELinux checkSelinux() { - # If the getenforce command exists, - if is_command getenforce ; then - # Store the current mode in a variable - enforceMode=$(getenforce) - printf "\\n %b SELinux mode detected: %s\\n" "${INFO}" "${enforceMode}" - - # If it's enforcing, - if [[ "${enforceMode}" == "Enforcing" ]]; then - # Explain Pi-hole does not support it yet - whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?" "${r}" "${c}" || \ - { printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } - printf " %b Continuing installation with SELinux Enforcing\\n" "${INFO}" - printf " %b Please refer to official SELinux documentation to create a custom policy\\n" "${INFO}" - fi + local DEFAULT_SELINUX + local CURRENT_SELINUX + local SELINUX_ENFORCING=0 + # Check if a SELinux configuration file exists + if [[ -f /etc/selinux/config ]]; then + # If a SELinux configuration file was found, check the default SELinux mode. + DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) + case "${DEFAULT_SELINUX,,}" in + enforcing) + echo -e "${CROSS} ${COL_RED}Default SELinux: $DEFAULT_SELINUX${COL_NC}" + SELINUX_ENFORCING=1 + ;; + *) # 'permissive' and 'disabled' + echo -e "${TICK} ${COL_GREEN}Default SELinux: $DEFAULT_SELINUX${COL_NC}"; + ;; + esac + # Check the current state of SELinux + CURRENT_SELINUX=$(getenforce) + case "${CURRENT_SELINUX,,}" in + enforcing) + echo -e "${CROSS} ${COL_RED}Current SELinux: $CURRENT_SELINUX${COL_NC}" + SELINUX_ENFORCING=1 + ;; + *) # 'permissive' and 'disabled' + echo -e "${TICK} ${COL_GREEN}Current SELinux: $CURRENT_SELINUX${COL_NC}"; + ;; + esac + else + echo -e "${INFO} ${COL_GREEN}SELinux not detected${COL_NC}"; + fi + # Exit the installer if any SELinux checks toggled the flag + if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then + echo -e "Pi-hole does not provide an SELinux policy as the required changes modify the security of your system." + echo -e "Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment." + printf "\\n%bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; + exit 1; fi } From cd9b1fcb8c55c11bb0ff8220f0a1de4b29da6b42 Mon Sep 17 00:00:00 2001 From: bcambl Date: Mon, 14 Oct 2019 12:26:39 -0600 Subject: [PATCH 04/34] update tests for SELinux changes Signed-off-by: bcambl --- test/test_automated_install.py | 65 ++---------------------------- test/test_centos_fedora_support.py | 62 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/test/test_automated_install.py b/test/test_automated_install.py index e8a4dede..aeaac3dc 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -254,73 +254,16 @@ def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole): assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 2 -def test_selinux_enforcing_default_exit(Pihole): +def test_selinux_not_detected(Pihole): ''' - confirms installer prompts to exit when SELinux is Enforcing by default + confirms installer continues when SELinux configuration file does not exist ''' - # getenforce returns the running state of SELinux - mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole) - # Whiptail dialog returns Cancel for user prompt - mock_command('whiptail', {'*': ('', '1')}, Pihole) - check_selinux = Pihole.run(''' - source /opt/pihole/basic-install.sh - checkSelinux - ''') - expected_stdout = info_box + ' SELinux mode detected: Enforcing' - assert expected_stdout in check_selinux.stdout - expected_stdout = 'SELinux Enforcing detected, exiting installer' - assert expected_stdout in check_selinux.stdout - assert check_selinux.rc == 1 - - -def test_selinux_enforcing_continue(Pihole): - ''' - confirms installer prompts to continue with custom policy warning - ''' - # getenforce returns the running state of SELinux - mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole) - # Whiptail dialog returns Continue for user prompt - mock_command('whiptail', {'*': ('', '0')}, Pihole) - check_selinux = Pihole.run(''' - source /opt/pihole/basic-install.sh - checkSelinux - ''') - expected_stdout = info_box + ' SELinux mode detected: Enforcing' - assert expected_stdout in check_selinux.stdout - expected_stdout = info_box + (' Continuing installation with SELinux ' - 'Enforcing') - assert expected_stdout in check_selinux.stdout - expected_stdout = info_box + (' Please refer to official SELinux ' - 'documentation to create a custom policy') - assert expected_stdout in check_selinux.stdout - assert check_selinux.rc == 0 - - -def test_selinux_permissive(Pihole): - ''' - confirms installer continues when SELinux is Permissive - ''' - # getenforce returns the running state of SELinux - mock_command('getenforce', {'*': ('Permissive', '0')}, Pihole) - check_selinux = Pihole.run(''' - source /opt/pihole/basic-install.sh - checkSelinux - ''') - expected_stdout = info_box + ' SELinux mode detected: Permissive' - assert expected_stdout in check_selinux.stdout - assert check_selinux.rc == 0 - - -def test_selinux_disabled(Pihole): - ''' - confirms installer continues when SELinux is Disabled - ''' - mock_command('getenforce', {'*': ('Disabled', '0')}, Pihole) check_selinux = Pihole.run(''' + rm -f /etc/selinux/config source /opt/pihole/basic-install.sh checkSelinux ''') - expected_stdout = info_box + ' SELinux mode detected: Disabled' + expected_stdout = info_box + ' SELinux not detected' assert expected_stdout in check_selinux.stdout assert check_selinux.rc == 0 diff --git a/test/test_centos_fedora_support.py b/test/test_centos_fedora_support.py index df53d73f..78910b99 100644 --- a/test/test_centos_fedora_support.py +++ b/test/test_centos_fedora_support.py @@ -7,6 +7,68 @@ from conftest import ( mock_command_2, ) +def mock_selinux_config(state, Pihole): + ''' + Creates a mock SELinux config file with expected content + ''' + # validate state string + valid_states = ['enforcing', 'permissive', 'disabled'] + assert state in valid_states + # getenforce returns the running state of SELinux + mock_command('getenforce', {'*': (state.capitalize(), '0')}, Pihole) + # create mock configuration with desired content + Pihole.run(''' + mkdir /etc/selinux + echo "SELINUX={state}" > /etc/selinux/config + '''.format(state=state.lower())) + + +@pytest.mark.parametrize("tag", [('centos'), ('fedora'), ]) +def test_selinux_enforcing_exit(Pihole): + ''' + confirms installer prompts to exit when SELinux is Enforcing by default + ''' + mock_selinux_config("enforcing", Pihole) + check_selinux = Pihole.run(''' + source /opt/pihole/basic-install.sh + checkSelinux + ''') + expected_stdout = cross_box + ' Current SELinux: Enforcing' + assert expected_stdout in check_selinux.stdout + expected_stdout = 'SELinux Enforcing detected, exiting installer' + assert expected_stdout in check_selinux.stdout + assert check_selinux.rc == 1 + + +@pytest.mark.parametrize("tag", [('centos'), ('fedora'), ]) +def test_selinux_permissive(Pihole): + ''' + confirms installer continues when SELinux is Permissive + ''' + mock_selinux_config("permissive", Pihole) + check_selinux = Pihole.run(''' + source /opt/pihole/basic-install.sh + checkSelinux + ''') + expected_stdout = tick_box + ' Current SELinux: Permissive' + assert expected_stdout in check_selinux.stdout + assert check_selinux.rc == 0 + + +@pytest.mark.parametrize("tag", [('centos'), ('fedora'), ]) +def test_selinux_disabled(Pihole): + ''' + confirms installer continues when SELinux is Disabled + ''' + mock_selinux_config("disabled", Pihole) + check_selinux = Pihole.run(''' + source /opt/pihole/basic-install.sh + checkSelinux + ''') + expected_stdout = tick_box + ' Current SELinux: Disabled' + assert expected_stdout in check_selinux.stdout + assert check_selinux.rc == 0 + @pytest.mark.parametrize("tag", [('fedora'), ]) def test_epel_and_remi_not_installed_fedora(Pihole): From cf2b02150207288c268cc4660f288d146ddc78b4 Mon Sep 17 00:00:00 2001 From: bcambl Date: Mon, 14 Oct 2019 13:29:43 -0600 Subject: [PATCH 05/34] linting: E302 expected 2 blank lines, found 1 Signed-off-by: bcambl --- test/test_centos_fedora_support.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_centos_fedora_support.py b/test/test_centos_fedora_support.py index 78910b99..aee16212 100644 --- a/test/test_centos_fedora_support.py +++ b/test/test_centos_fedora_support.py @@ -7,6 +7,7 @@ from conftest import ( mock_command_2, ) + def mock_selinux_config(state, Pihole): ''' Creates a mock SELinux config file with expected content From 5bac1ad58b2b2b179f308da2e997f69467d9037f Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Mon, 14 Oct 2019 22:59:58 +0100 Subject: [PATCH 06/34] backend changes to allow comment when adding new adlist Signed-off-by: Adam Warner --- advanced/Scripts/webpage.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 411cc1f6..ce404f31 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -404,13 +404,15 @@ SetWebUILayout() { CustomizeAdLists() { local address address="${args[3]}" + local comment + comment="${args[4]}" if [[ "${args[2]}" == "enable" ]]; then sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 1 WHERE address = '${address}'" elif [[ "${args[2]}" == "disable" ]]; then sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 0 WHERE address = '${address}'" elif [[ "${args[2]}" == "add" ]]; then - sqlite3 "${gravityDBfile}" "INSERT OR IGNORE INTO adlist (address) VALUES ('${address}')" + sqlite3 "${gravityDBfile}" "INSERT OR IGNORE INTO adlist (address, comment) VALUES ('${address}', '${comment}')" elif [[ "${args[2]}" == "del" ]]; then sqlite3 "${gravityDBfile}" "DELETE FROM adlist WHERE address = '${address}'" else From a86f5781391727a37f17081349b514ff53e2ab95 Mon Sep 17 00:00:00 2001 From: bcambl Date: Mon, 14 Oct 2019 20:06:23 -0600 Subject: [PATCH 07/34] replace echo with printf in checkSelinux() Signed-off-by: bcambl --- automated install/basic-install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 091c543a..0b00d968 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1968,22 +1968,22 @@ checkSelinux() { DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) case "${DEFAULT_SELINUX,,}" in enforcing) - echo -e "${CROSS} ${COL_RED}Default SELinux: $DEFAULT_SELINUX${COL_NC}" + printf "%b %bDefault SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${DEFAULT_SELINUX}" "${COL_NC}" SELINUX_ENFORCING=1 ;; *) # 'permissive' and 'disabled' - echo -e "${TICK} ${COL_GREEN}Default SELinux: $DEFAULT_SELINUX${COL_NC}"; + printf "%b %bDefault SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${DEFAULT_SELINUX}" "${COL_NC}" ;; esac # Check the current state of SELinux CURRENT_SELINUX=$(getenforce) case "${CURRENT_SELINUX,,}" in enforcing) - echo -e "${CROSS} ${COL_RED}Current SELinux: $CURRENT_SELINUX${COL_NC}" + printf "%b %bCurrent SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${CURRENT_SELINUX}" "${COL_NC}" SELINUX_ENFORCING=1 ;; *) # 'permissive' and 'disabled' - echo -e "${TICK} ${COL_GREEN}Current SELinux: $CURRENT_SELINUX${COL_NC}"; + printf "%b %bCurrent SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${CURRENT_SELINUX}" "${COL_NC}" ;; esac else @@ -1991,8 +1991,8 @@ checkSelinux() { fi # Exit the installer if any SELinux checks toggled the flag if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then - echo -e "Pi-hole does not provide an SELinux policy as the required changes modify the security of your system." - echo -e "Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment." + printf "Pi-hole does not provide an SELinux policy as the required changes modify the security of your system.\\n" + printf "Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment.\\n" printf "\\n%bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; fi From 612d408034cc5e80d6678ebd48e24767140d402d Mon Sep 17 00:00:00 2001 From: bcambl Date: Mon, 14 Oct 2019 20:16:40 -0600 Subject: [PATCH 08/34] replace echo with printf in install_dependent_packages() Signed-off-by: bcambl --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 11b78fcd..c887a6c6 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1628,7 +1628,7 @@ install_dependent_packages() { if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &> /dev/null; then printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" else - echo -e "${OVER} ${INFO} Checking for $i (will be installed)" + printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}" installArray+=("${i}") fi done @@ -1647,7 +1647,7 @@ install_dependent_packages() { if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" else - echo -e "${OVER} ${INFO} Checking for $i (will be installed)" + printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}" installArray+=("${i}") fi done From c6f9fe3af2b351b484ff14de426dde268d56441c Mon Sep 17 00:00:00 2001 From: John Krull Date: Tue, 15 Oct 2019 21:29:55 -0500 Subject: [PATCH 09/34] Fix spelling of the word "permitting" Signed-off-by: John Krull --- advanced/Scripts/webpage.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 411cc1f6..8aa3fa08 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -524,10 +524,10 @@ Interfaces: fi if [[ "${args[2]}" == "all" ]]; then - echo -e " ${INFO} Listening on all interfaces, permiting all origins. Please use a firewall!" + echo -e " ${INFO} Listening on all interfaces, permitting all origins. Please use a firewall!" change_setting "DNSMASQ_LISTENING" "all" elif [[ "${args[2]}" == "local" ]]; then - echo -e " ${INFO} Listening on all interfaces, permiting origins from one hop away (LAN)" + echo -e " ${INFO} Listening on all interfaces, permitting origins from one hop away (LAN)" change_setting "DNSMASQ_LISTENING" "local" else echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}" From f9d16c2b1525a9dde136be5968583a56723b3a7d Mon Sep 17 00:00:00 2001 From: Pierre Ghiot Date: Sun, 27 Oct 2019 02:07:08 +0200 Subject: [PATCH 10/34] Update webpage.sh Signed-off-by: Mograine --- advanced/Scripts/webpage.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 600a45a5..e990cc22 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -17,6 +17,7 @@ readonly FTLconf="/etc/pihole/pihole-FTL.conf" # 03 -> wildcards readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" readonly PI_HOLE_BIN_DIR="/usr/local/bin" +readonly dnscustomfile="/etc/pihole/custom.list" coltable="/opt/pihole/COL_TABLE" if [[ -f ${coltable} ]]; then @@ -564,6 +565,17 @@ SetPrivacyLevel() { fi } +AddCustomDNSAddress() { + ip="${args[2]}" + host="${args[3]}" + echo "${ip} ${host}" >> "${dnscustomfile}" +} + +RemoveCustomDNSAddress() { + host="${args[2]}" + sed -i "/.*${host}/d" "${dnscustomfile}" +} + main() { args=("$@") @@ -595,6 +607,8 @@ main() { "audit" ) addAudit "$@";; "clearaudit" ) clearAudit;; "-l" | "privacylevel" ) SetPrivacyLevel;; + "addcustomdns" ) AddCustomDNSAddress;; + "removecustomdns" ) RemoveCustomDNSAddress;; * ) helpFunc;; esac From bb8dbe9da5d703d8a15328ee33c0bda01959e5c4 Mon Sep 17 00:00:00 2001 From: Pierre Ghiot Date: Sun, 27 Oct 2019 02:09:29 +0200 Subject: [PATCH 11/34] Update 01-pihole.conf Signed-off-by: Mograine --- advanced/01-pihole.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 38d2c0b5..5919034d 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -21,6 +21,7 @@ addn-hosts=/etc/pihole/gravity.list addn-hosts=/etc/pihole/black.list addn-hosts=/etc/pihole/local.list +addn-hosts=/etc/pihole/custom.list domain-needed From 193ff38ab3d6fd49a9912c5e07cd8e098267bf3a Mon Sep 17 00:00:00 2001 From: Mograine Date: Mon, 28 Oct 2019 13:21:05 +0100 Subject: [PATCH 12/34] Allow more precise deletion by passing ip as parameter Signed-off-by: Mograine --- advanced/Scripts/webpage.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index e990cc22..dc2c83af 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -572,8 +572,9 @@ AddCustomDNSAddress() { } RemoveCustomDNSAddress() { - host="${args[2]}" - sed -i "/.*${host}/d" "${dnscustomfile}" + ip="${args[2]}" + host="${args[3]}" + sed -i "/${ip} ${host}/d" "${dnscustomfile}" } main() { From 3fbb0ac8dde14b8edc1982ae3a2a021f3cf68477 Mon Sep 17 00:00:00 2001 From: Adam Hill Date: Tue, 29 Oct 2019 22:26:46 -0500 Subject: [PATCH 13/34] Adding docker+arm detection & FTL download Signed-off-by: Adam Hill --- automated install/basic-install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c887a6c6..bbc8b2ac 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -2305,9 +2305,15 @@ get_binary_name() { binary="pihole-FTL-arm-linux-gnueabi" fi else - printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}" - # set the binary to be used - binary="pihole-FTL-arm-linux-gnueabi" + if [[ -f "/.dockerenv" ]]; then + printf "%b %b Detected ARM architecture in docker\\n" "${OVER}" "${TICK}" + # set the binary to be used + binary="pihole-FTL-armel-native" + else + printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}" + # set the binary to be used + binary="pihole-FTL-arm-linux-gnueabi" + fi fi elif [[ "${machine}" == "x86_64" ]]; then # This gives the architecture of packages dpkg installs (for example, "i386") From 476975540a6286eef126c14654765dcb856216d2 Mon Sep 17 00:00:00 2001 From: chrunchyjesus Date: Tue, 5 Nov 2019 22:11:47 +0100 Subject: [PATCH 14/34] make some shebangs comply to posix standard --- advanced/Scripts/wildcard_regex_converter.sh | 2 +- advanced/Templates/pihole-FTL.service | 2 +- pihole | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/advanced/Scripts/wildcard_regex_converter.sh b/advanced/Scripts/wildcard_regex_converter.sh index 8c9578a3..b4b6b4a1 100644 --- a/advanced/Scripts/wildcard_regex_converter.sh +++ b/advanced/Scripts/wildcard_regex_converter.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service index 8a4c7ce6..5dbf080e 100644 --- a/advanced/Templates/pihole-FTL.service +++ b/advanced/Templates/pihole-FTL.service @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ### BEGIN INIT INFO # Provides: pihole-FTL # Required-Start: $remote_fs $syslog diff --git a/pihole b/pihole index 4a358443..f0195843 100755 --- a/pihole +++ b/pihole @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Pi-hole: A black hole for Internet advertisements # (c) 2017 Pi-hole, LLC (https://pi-hole.net) From ea67c828cde17764242fdf846eb374d5943bbd12 Mon Sep 17 00:00:00 2001 From: MichaIng Date: Thu, 7 Nov 2019 13:59:44 +0100 Subject: [PATCH 15/34] Minor installer output enhancements + Print restart hint after setting IPv4 address on a separate line with [i] prefix to not break text alignment + Print final upstream DNS choice as a single printf call and by this fix missing info and linebreak on "Custom" choices. + Minor if/then/else code alignment Signed-off-by: MichaIng --- automated install/basic-install.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c887a6c6..c2f8ced5 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -838,7 +838,8 @@ setDHCPCD() { # Then use the ip command to immediately set the new address ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" # Also give a warning that the user may need to reboot their system - printf " %b Set IP address to %s \\n You may need to restart after the install is complete\\n" "${TICK}" "${IPV4_ADDRESS%/*}" + printf " %b Set IP address to %s\\n" "${TICK}" "${IPV4_ADDRESS%/*}" + printf " %b You may need to restart after the install is complete\\n" "${INFO}" fi } @@ -984,8 +985,6 @@ setDNS() { # exit if Cancel is selected { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } - # Display the selection - printf " %b Using " "${INFO}" # Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider if [[ "${DNSchoices}" == "Custom" ]] then @@ -1037,14 +1036,14 @@ setDNS() { if [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi - # Since the settings will not work, stay in the loop - DNSSettingsCorrect=False + # Since the settings will not work, stay in the loop + DNSSettingsCorrect=False # Otherwise, else # Show the settings if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" "${r}" "${c}"); then - # and break from the loop since the servers are valid - DNSSettingsCorrect=True + # and break from the loop since the servers are valid + DNSSettingsCorrect=True # Otherwise, else # If the settings are wrong, the loop continues @@ -1052,7 +1051,7 @@ setDNS() { fi fi done - else + else # Save the old Internal Field Separator in a variable OIFS=$IFS # and set the new one to newline @@ -1062,7 +1061,6 @@ setDNS() { DNSName="$(cut -d';' -f1 <<< "${DNSServer}")" if [[ "${DNSchoices}" == "${DNSName}" ]] then - printf "%s\\n" "${DNSName}" PIHOLE_DNS_1="$(cut -d';' -f2 <<< "${DNSServer}")" PIHOLE_DNS_2="$(cut -d';' -f3 <<< "${DNSServer}")" break @@ -1071,6 +1069,9 @@ setDNS() { # Restore the IFS to what it was IFS=${OIFS} fi + + # Display final selection + printf " %b Using upstream DNS: %s %s\\n" "${INFO}" "${PIHOLE_DNS_1}" "${PIHOLE_DNS_2}" } # Allow the user to enable/disable logging From d457d40e0b98d8b42a2b17e99d97d05debdb9800 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 12 Nov 2019 20:49:46 +0100 Subject: [PATCH 16/34] Add php-xml package as new dependency. Signed-off-by: DL6ER --- automated install/basic-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c887a6c6..744b8b4f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -250,7 +250,7 @@ if is_command apt-get ; then PIHOLE_DEPS=(cron curl dnsutils iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data resolvconf libcap2) # The Web dashboard has some that also need to be installed # It's useful to separate the two since our repos are also setup as "Core" code and "Web" code - PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-${phpSqlite}") + PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-${phpSqlite}" "${phpVer}-xml") # The Web server user, LIGHTTPD_USER="www-data" # group, @@ -290,7 +290,7 @@ elif is_command rpm ; then PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" INSTALLER_DEPS=(dialog git iproute newt procps-ng which chkconfig) PIHOLE_DEPS=(bind-utils cronie curl findutils nmap-ncat sudo unzip wget libidn2 psmisc sqlite libcap) - PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo) + PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml) LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" From 4840bdb03158410d474cd7be88d77b41299cdd3f Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Thu, 14 Nov 2019 19:06:23 +0000 Subject: [PATCH 17/34] add a double space to the beginning of some outputs Signed-off-by: Adam Warner --- automated install/basic-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 2d06f526..a92a35a7 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1968,32 +1968,32 @@ checkSelinux() { DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) case "${DEFAULT_SELINUX,,}" in enforcing) - printf "%b %bDefault SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${DEFAULT_SELINUX}" "${COL_NC}" + printf " %b %bDefault SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${DEFAULT_SELINUX}" "${COL_NC}" SELINUX_ENFORCING=1 ;; *) # 'permissive' and 'disabled' - printf "%b %bDefault SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${DEFAULT_SELINUX}" "${COL_NC}" + printf " %b %bDefault SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${DEFAULT_SELINUX}" "${COL_NC}" ;; esac # Check the current state of SELinux CURRENT_SELINUX=$(getenforce) case "${CURRENT_SELINUX,,}" in enforcing) - printf "%b %bCurrent SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${CURRENT_SELINUX}" "${COL_NC}" + printf " %b %bCurrent SELinux: %s%b\\n" "${CROSS}" "${COL_RED}" "${CURRENT_SELINUX}" "${COL_NC}" SELINUX_ENFORCING=1 ;; *) # 'permissive' and 'disabled' - printf "%b %bCurrent SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${CURRENT_SELINUX}" "${COL_NC}" + printf " %b %bCurrent SELinux: %s%b\\n" "${TICK}" "${COL_GREEN}" "${CURRENT_SELINUX}" "${COL_NC}" ;; esac else - echo -e "${INFO} ${COL_GREEN}SELinux not detected${COL_NC}"; + echo -e " ${INFO} ${COL_GREEN}SELinux not detected${COL_NC}"; fi # Exit the installer if any SELinux checks toggled the flag if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then - printf "Pi-hole does not provide an SELinux policy as the required changes modify the security of your system.\\n" - printf "Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment.\\n" - printf "\\n%bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; + printf " Pi-hole does not provide an SELinux policy as the required changes modify the security of your system.\\n" + printf " Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment.\\n" + printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; fi } From 12817c09bb22afc02eaeb635071206bf454f9848 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Thu, 14 Nov 2019 18:52:07 +0000 Subject: [PATCH 18/34] (Squashed commits) Always ensure we have the correct machine arch by storing to/reading from a file rather than depending on global variable that for some reason is not always populated... Signed-off-by: Adam Warner no need for global variable Signed-off-by: Adam Warner Use a file in the temporary FTL download directory Signed-off-by: Dan Schaper Local binary variable named to l_binary. Disambiguate from global binary. Allow 'binary' to be shadowed for testing. Use ./ftlbinary in all operations. Signed-off-by: Dan Schaper Revert shadow ability on binary variable. Signed-off-by: Dan Schaper Remove unused tests, binary variable can not be overridden. Signed-off-by: Dan Schaper This should work here, too Signed-off-by: Adam Warner binary name is passed through from pihole checkout Signed-off-by: Adam Warner Add comments Signed-off-by: Adam Warner OK, let's try it this way again Signed-off-by: Adam Warner we might be getting somewhere.. squash after this I think! Signed-off-by: Adam Warner This is a test to see if it fixes the aarch64 test (we are definitely squashing these commits Signed-off-by: Adam Warner fix the rest of the tests Signed-off-by: Adam Warner Remove trailing whitespace in the files we've touched here Signed-off-by: Adam Warner --- advanced/Scripts/piholeCheckout.sh | 9 ++-- advanced/Scripts/update.sh | 1 - automated install/basic-install.sh | 47 +++++++++++------ test/test_automated_install.py | 82 +++++++++--------------------- 4 files changed, 63 insertions(+), 76 deletions(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 673ded0b..31009dd9 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -46,6 +46,12 @@ checkout() { local corebranches local webbranches + # Check if FTL is installed - do this early on as FTL is a hard dependency for Pi-hole + local funcOutput + funcOutput=$(get_binary_name) #Store output of get_binary_name here + local binary + binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL) + # Avoid globbing set -f @@ -86,7 +92,6 @@ checkout() { fi #echo -e " ${TICK} Pi-hole Core" - get_binary_name local path path="development/${binary}" echo "development" > /etc/pihole/ftlbranch @@ -101,7 +106,6 @@ checkout() { fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; } fi #echo -e " ${TICK} Web Interface" - get_binary_name local path path="master/${binary}" echo "master" > /etc/pihole/ftlbranch @@ -161,7 +165,6 @@ checkout() { fi checkout_pull_branch "${webInterfaceDir}" "${2}" elif [[ "${1}" == "ftl" ]] ; then - get_binary_name local path path="${2}/${binary}" diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 4d352777..443dfb1f 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -31,7 +31,6 @@ source "/opt/pihole/COL_TABLE" # make_repo() sourced from basic-install.sh # update_repo() source from basic-install.sh # getGitFiles() sourced from basic-install.sh -# get_binary_name() sourced from basic-install.sh # FTLcheckUpdate() sourced from basic-install.sh GitCheckUpdateAvail() { diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 2d06f526..e99d2b9a 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -138,9 +138,6 @@ else OVER="\\r\\033[K" fi -# Define global binary variable -binary="tbd" - # 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() { @@ -2189,7 +2186,10 @@ clone_or_update_repos() { } # Download FTL binary to random temp directory and install FTL binary +# Disable directive for SC2120 a value _can_ be passed to this function, but it is passed from an external script that sources this one +# shellcheck disable=SC2120 FTLinstall() { + # Local, named variables local latesttag local str="Downloading and Installing FTL" @@ -2219,6 +2219,9 @@ FTLinstall() { ftlBranch="master" fi + local binary + binary="${1}" + # Determine which version of FTL to download if [[ "${ftlBranch}" == "master" ]];then url="https://github.com/pi-hole/FTL/releases/download/${latesttag%$'\r'}" @@ -2297,6 +2300,8 @@ get_binary_name() { local machine machine=$(uname -m) + local l_binary + local str="Detecting architecture" printf " %b %s..." "${INFO}" "${str}" # If the machine is arm or aarch @@ -2312,24 +2317,24 @@ get_binary_name() { if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then printf "%b %b Detected ARM-aarch64 architecture\\n" "${OVER}" "${TICK}" # set the binary to be used - binary="pihole-FTL-aarch64-linux-gnu" + l_binary="pihole-FTL-aarch64-linux-gnu" # elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then # if [[ "${rev}" -gt 6 ]]; then printf "%b %b Detected ARM-hf architecture (armv7+)\\n" "${OVER}" "${TICK}" # set the binary to be used - binary="pihole-FTL-arm-linux-gnueabihf" + l_binary="pihole-FTL-arm-linux-gnueabihf" # Otherwise, else printf "%b %b Detected ARM-hf architecture (armv6 or lower) Using ARM binary\\n" "${OVER}" "${TICK}" # set the binary to be used - binary="pihole-FTL-arm-linux-gnueabi" + l_binary="pihole-FTL-arm-linux-gnueabi" fi else printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}" # set the binary to be used - binary="pihole-FTL-arm-linux-gnueabi" + l_binary="pihole-FTL-arm-linux-gnueabi" fi elif [[ "${machine}" == "x86_64" ]]; then # This gives the architecture of packages dpkg installs (for example, "i386") @@ -2342,12 +2347,12 @@ get_binary_name() { # in the past (see https://github.com/pi-hole/pi-hole/pull/2004) if [[ "${dpkgarch}" == "i386" ]]; then printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" - binary="pihole-FTL-linux-x86_32" + l_binary="pihole-FTL-linux-x86_32" else # 64bit printf "%b %b Detected x86_64 architecture\\n" "${OVER}" "${TICK}" # set the binary to be used - binary="pihole-FTL-linux-x86_64" + l_binary="pihole-FTL-linux-x86_64" fi else # Something else - we try to use 32bit executable and warn the user @@ -2358,13 +2363,13 @@ get_binary_name() { else printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" fi - binary="pihole-FTL-linux-x86_32" + l_binary="pihole-FTL-linux-x86_32" fi + + echo ${l_binary} } FTLcheckUpdate() { - get_binary_name - #In the next section we check to see if FTL is already installed (in case of pihole -r). #If the installed version matches the latest version, then check the installed sha1sum of the binary vs the remote sha1sum. If they do not match, then download printf " %b Checking for existing FTL binary...\\n" "${INFO}" @@ -2380,6 +2385,9 @@ FTLcheckUpdate() { ftlBranch="master" fi + local binary + binary="${1}" + local remoteSha1 local localSha1 @@ -2458,8 +2466,10 @@ FTLcheckUpdate() { FTLdetect() { printf "\\n %b FTL Checks...\\n\\n" "${INFO}" - if FTLcheckUpdate ; then - FTLinstall || return 1 + printf " %b" "${2}" + + if FTLcheckUpdate "${1}"; then + FTLinstall "${1}" || return 1 fi } @@ -2622,8 +2632,15 @@ main() { fi # Create the pihole user create_pihole_user + # Check if FTL is installed - do this early on as FTL is a hard dependency for Pi-hole - if ! FTLdetect; then + local funcOutput + funcOutput=$(get_binary_name) #Store output of get_binary_name here + local binary + binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL) + local theRest + theRest="${funcOutput%pihole-FTL*}" # Print the rest of get_binary_name's output to display (cut out from first instance of "pihole-FTL") + if ! FTLdetect "${binary}" "${theRest}"; then printf " %b FTL Engine not installed\\n" "${CROSS}" exit 1 fi diff --git a/test/test_automated_install.py b/test/test_automated_install.py index aeaac3dc..567ea241 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -342,7 +342,10 @@ def test_FTL_detect_aarch64_no_errors(Pihole): detectPlatform = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" ''') expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout @@ -363,7 +366,10 @@ def test_FTL_detect_armv6l_no_errors(Pihole): detectPlatform = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" ''') expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout @@ -385,7 +391,10 @@ def test_FTL_detect_armv7l_no_errors(Pihole): detectPlatform = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" ''') expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout @@ -402,7 +411,10 @@ def test_FTL_detect_x86_64_no_errors(Pihole): detectPlatform = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" ''') expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout @@ -419,7 +431,10 @@ def test_FTL_detect_unknown_no_errors(Pihole): detectPlatform = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" ''') expected_stdout = 'Not able to detect architecture (unknown: mips)' assert expected_stdout in detectPlatform.stdout @@ -438,64 +453,14 @@ def test_FTL_download_aarch64_no_errors(Pihole): ''') download_binary = Pihole.run(''' source /opt/pihole/basic-install.sh - binary="pihole-FTL-aarch64-linux-gnu" create_pihole_user - FTLinstall + FTLinstall "pihole-FTL-aarch64-linux-gnu" ''') expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in download_binary.stdout assert 'error' not in download_binary.stdout.lower() -def test_FTL_download_unknown_fails_no_errors(Pihole): - ''' - confirms unknown binary is not downloaded for FTL engine - ''' - # mock whiptail answers and ensure installer dependencies - mock_command('whiptail', {'*': ('', '0')}, Pihole) - Pihole.run(''' - source /opt/pihole/basic-install.sh - distro_check - install_dependent_packages ${INSTALLER_DEPS[@]} - ''') - download_binary = Pihole.run(''' - source /opt/pihole/basic-install.sh - binary="pihole-FTL-mips" - create_pihole_user - FTLinstall - ''') - expected_stdout = cross_box + ' Downloading and Installing FTL' - assert expected_stdout in download_binary.stdout - error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/' - assert error1 in download_binary.stdout - error2 = 'not found' - assert error2 in download_binary.stdout - - -def test_FTL_download_binary_unset_no_errors(Pihole): - ''' - confirms unset binary variable does not download FTL engine - ''' - # mock whiptail answers and ensure installer dependencies - mock_command('whiptail', {'*': ('', '0')}, Pihole) - Pihole.run(''' - source /opt/pihole/basic-install.sh - distro_check - install_dependent_packages ${INSTALLER_DEPS[@]} - ''') - download_binary = Pihole.run(''' - source /opt/pihole/basic-install.sh - create_pihole_user - FTLinstall - ''') - expected_stdout = cross_box + ' Downloading and Installing FTL' - assert expected_stdout in download_binary.stdout - error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/' - assert error1 in download_binary.stdout - error2 = 'not found' - assert error2 in download_binary.stdout - - def test_FTL_binary_installed_and_responsive_no_errors(Pihole): ''' confirms FTL binary is copied and functional in installed location @@ -503,7 +468,10 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole): installed_binary = Pihole.run(''' source /opt/pihole/basic-install.sh create_pihole_user - FTLdetect + funcOutput=$(get_binary_name) + binary="pihole-FTL${funcOutput##*pihole-FTL}" + theRest="${funcOutput%pihole-FTL*}" + FTLdetect "${binary}" "${theRest}" pihole-FTL version ''') expected_stdout = 'v' From c809c34024ac302e11ba90761526d2c57a006f45 Mon Sep 17 00:00:00 2001 From: Mograine Date: Wed, 27 Nov 2019 00:27:57 +0100 Subject: [PATCH 19/34] Add user feedback Signed-off-by: Mograine --- advanced/Scripts/webpage.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 7568d119..88ee00f8 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -599,15 +599,25 @@ SetPrivacyLevel() { } AddCustomDNSAddress() { + echo -e " ${TICK} Adding custom DNS entry..." + ip="${args[2]}" host="${args[3]}" echo "${ip} ${host}" >> "${dnscustomfile}" + + # Restart dnsmasq to load new custom DNS entries + RestartDNS } RemoveCustomDNSAddress() { + echo -e " ${TICK} Removing custom DNS entry..." + ip="${args[2]}" host="${args[3]}" sed -i "/${ip} ${host}/d" "${dnscustomfile}" + + # Restart dnsmasq to update removed custom DNS entries + RestartDNS } main() { From 185319d560829d9304f38db568c61e8e6955bc90 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 30 Nov 2019 12:33:16 +0000 Subject: [PATCH 20/34] Unite four domain tables into a single domainlist table. Signed-off-by: DL6ER --- .../Scripts/database_migration/gravity-db.sh | 7 ++ .../database_migration/gravity/3_to_4.sql | 96 +++++++++++++++++++ advanced/Scripts/list.sh | 34 +++++-- 3 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 advanced/Scripts/database_migration/gravity/3_to_4.sql diff --git a/advanced/Scripts/database_migration/gravity-db.sh b/advanced/Scripts/database_migration/gravity-db.sh index 0fe90d8a..d7de0ec5 100644 --- a/advanced/Scripts/database_migration/gravity-db.sh +++ b/advanced/Scripts/database_migration/gravity-db.sh @@ -41,4 +41,11 @@ upgrade_gravityDB(){ sqlite3 "${database}" < "/etc/.pihole/advanced/Scripts/database_migration/gravity/2_to_3.sql" version=3 fi + if [[ "$version" == "3" ]]; then + # This migration script unifies the formally separated domain + # lists into a single table with a UNIQUE domain constraint + echo -e " ${INFO} Upgrading gravity database from version 3 to 4" + sqlite3 "${database}" < "${scriptPath}/3_to_4.sql" + version=6 + fi } diff --git a/advanced/Scripts/database_migration/gravity/3_to_4.sql b/advanced/Scripts/database_migration/gravity/3_to_4.sql new file mode 100644 index 00000000..8d1c1d26 --- /dev/null +++ b/advanced/Scripts/database_migration/gravity/3_to_4.sql @@ -0,0 +1,96 @@ +.timeout 30000 + +PRAGMA FOREIGN_KEYS=OFF; + +BEGIN TRANSACTION; + +CREATE TABLE domainlist +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + type INTEGER NOT NULL DEFAULT 0, + domain TEXT UNIQUE NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT 1, + date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), + date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), + comment TEXT +); + +ALTER TABLE whitelist ADD COLUMN type INTEGER; +UPDATE whitelist SET type = 0; +INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment) + SELECT type,domain,enabled,date_added,date_modified,comment FROM whitelist; + +ALTER TABLE blacklist ADD COLUMN type INTEGER; +UPDATE blacklist SET type = 1; +INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment) + SELECT type,domain,enabled,date_added,date_modified,comment FROM blacklist; + +ALTER TABLE regex_whitelist ADD COLUMN type INTEGER; +UPDATE regex_whitelist SET type = 2; +INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment) + SELECT type,domain,enabled,date_added,date_modified,comment FROM regex_whitelist; + +ALTER TABLE regex_blacklist ADD COLUMN type INTEGER; +UPDATE regex_blacklist SET type = 3; +INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment) + SELECT type,domain,enabled,date_added,date_modified,comment FROM regex_blacklist; + +DROP TABLE whitelist_by_group; +DROP TABLE blacklist_by_group; +DROP TABLE regex_whitelist_by_group; +DROP TABLE regex_blacklist_by_group; +CREATE TABLE domainlist_by_group +( + domainlist_id INTEGER NOT NULL REFERENCES domainlist (id), + group_id INTEGER NOT NULL REFERENCES "group" (id), + PRIMARY KEY (domainlist_id, group_id) +); + +DROP TRIGGER tr_whitelist_update; +DROP TRIGGER tr_blacklist_update; +DROP TRIGGER tr_regex_whitelist_update; +DROP TRIGGER tr_regex_blacklist_update; +CREATE TRIGGER tr_domainlist_update AFTER UPDATE ON domainlist + BEGIN + UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain; + END; + +DROP VIEW vw_whitelist; +CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id + FROM domainlist + LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id + LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id + WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1) + AND domainlist.type = 0 + ORDER BY domainlist.id; + +DROP VIEW vw_blacklist; +CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id + FROM domainlist + LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id + LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id + WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1) + AND domainlist.type = 1 + ORDER BY domainlist.id; + +DROP VIEW vw_regex_whitelist; +CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id + FROM domainlist + LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id + LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id + WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1) + AND domainlist.type = 2 + ORDER BY domainlist.id; + +DROP VIEW vw_regex_blacklist; +CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id + FROM domainlist + LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id + LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id + WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1) + AND domainlist.type = 3 + ORDER BY domainlist.id; + +UPDATE info SET value = 6 WHERE property = 'version'; + +COMMIT; diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 6a606665..483b7153 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -27,6 +27,17 @@ listname="" colfile="/opt/pihole/COL_TABLE" source ${colfile} +getTypeID() { + if [[ "$1" == "whitelist" ]]; then + echo "0" + elif [[ "$1" == "blacklist" ]]; then + echo "1" + elif [[ "$1" == "regex_whitelist" ]]; then + echo "2" + elif [[ "$1" == "regex_blacklist" ]]; then + echo "3" + fi +} helpFunc() { if [[ "${listType}" == "whitelist" ]]; then @@ -129,13 +140,14 @@ ProcessDomainList() { } AddDomain() { - local domain list num + local domain list num typeID # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" list="$2" + typeID="$(getTypeID "${list}")" # Is the domain in the list we want to add it to? - num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM ${list} WHERE domain = '${domain}';")" + num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};")" if [[ "${num}" -ne 0 ]]; then if [[ "${verbose}" == true ]]; then @@ -151,17 +163,18 @@ AddDomain() { reload=true # Insert only the domain here. The enabled and date_added fields will be filled # with their default values (enabled = true, date_added = current timestamp) - sqlite3 "${gravityDBfile}" "INSERT INTO ${list} (domain) VALUES ('${domain}');" + sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeID});" } RemoveDomain() { - local domain list num + local domain list num typeID # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" list="$2" + typeID="$(getTypeID "${list}")" # Is the domain in the list we want to remove it from? - num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM ${list} WHERE domain = '${domain}';")" + num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};")" if [[ "${num}" -eq 0 ]]; then if [[ "${verbose}" == true ]]; then @@ -176,14 +189,15 @@ RemoveDomain() { fi reload=true # Remove it from the current list - sqlite3 "${gravityDBfile}" "DELETE FROM ${list} WHERE domain = '${domain}';" + sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};" } Displaylist() { - local list listname count num_pipes domain enabled status nicedate + local list listname count num_pipes domain enabled status nicedate typeID listname="${listType}" - data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM ${listType};" 2> /dev/null)" + typeID="$(getTypeID "${listType}")" + data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeID};" 2> /dev/null)" if [[ -z $data ]]; then echo -e "Not showing empty list" @@ -221,7 +235,9 @@ Displaylist() { } NukeList() { - sqlite3 "${gravityDBfile}" "DELETE FROM ${listType};" + local typeID + typeID=$(getTypeID "${list}") + sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeID};" } for var in "$@"; do From a1f120b2ff5fd7801202a481723c12871a8ab902 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 30 Nov 2019 12:43:07 +0000 Subject: [PATCH 21/34] Address stickler's complaint Signed-off-by: DL6ER --- advanced/Scripts/database_migration/gravity-db.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/database_migration/gravity-db.sh b/advanced/Scripts/database_migration/gravity-db.sh index d7de0ec5..a12be864 100644 --- a/advanced/Scripts/database_migration/gravity-db.sh +++ b/advanced/Scripts/database_migration/gravity-db.sh @@ -10,6 +10,8 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. +scriptPath="/etc/.pihole/advanced/Scripts/database_migration/gravity" + upgrade_gravityDB(){ local database piholeDir auditFile version database="${1}" @@ -23,7 +25,7 @@ upgrade_gravityDB(){ # This migration script upgrades the gravity.db file by # adding the domain_audit table echo -e " ${INFO} Upgrading gravity database from version 1 to 2" - sqlite3 "${database}" < "/etc/.pihole/advanced/Scripts/database_migration/gravity/1_to_2.sql" + sqlite3 "${database}" < "${scriptPath}/1_to_2.sql" version=2 # Store audit domains in database table @@ -38,7 +40,7 @@ upgrade_gravityDB(){ # renaming the regex table to regex_blacklist, and # creating a new regex_whitelist table + corresponding linking table and views echo -e " ${INFO} Upgrading gravity database from version 2 to 3" - sqlite3 "${database}" < "/etc/.pihole/advanced/Scripts/database_migration/gravity/2_to_3.sql" + sqlite3 "${database}" < "${scriptPath}/2_to_3.sql" version=3 fi if [[ "$version" == "3" ]]; then From d0de5fda3086614b1f8266a9a4ed12f0b6ef9c97 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 13:13:26 +0000 Subject: [PATCH 22/34] Simplify removal of domain from one list when it is requested for another Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 483b7153..55272cde 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -39,6 +39,18 @@ getTypeID() { fi } +getListnameFromType() { + if [[ "$1" == "0" ]]; then + echo "whitelist" + elif [[ "$1" == "1" ]]; then + echo "blacklist" + elif [[ "$1" == "2" ]]; then + echo "regex_whitelist" + elif [[ "$1" == "3" ]]; then + echo "regex_blacklist" + fi +} + helpFunc() { if [[ "${listType}" == "whitelist" ]]; then param="w" @@ -105,19 +117,15 @@ HandleOther() { } ProcessDomainList() { - local is_regexlist if [[ "${listType}" == "regex_blacklist" ]]; then # Regex black filter list listname="regex blacklist filters" - is_regexlist=true elif [[ "${listType}" == "regex_whitelist" ]]; then # Regex white filter list listname="regex whitelist filters" - is_regexlist=true else # Whitelist / Blacklist listname="${listType}" - is_regexlist=false fi for dom in "${domList[@]}"; do @@ -130,9 +138,6 @@ ProcessDomainList() { # if delmode then remove from desired list but do not add to the other if ${addmode}; then AddDomain "${dom}" "${listType}" - if ! ${is_regexlist}; then - RemoveDomain "${dom}" "${listAlt}" - fi else RemoveDomain "${dom}" "${listType}" fi @@ -140,18 +145,27 @@ ProcessDomainList() { } AddDomain() { - local domain list num typeID + local domain list num currTypeID currListName typeID # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" list="$2" typeID="$(getTypeID "${list}")" # Is the domain in the list we want to add it to? - num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};")" + num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}';")" if [[ "${num}" -ne 0 ]]; then - if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!" + currTypeID="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" + if [[ "${currTypeID}" == "${typeID}" ]]; then + if [[ "${verbose}" == true ]]; then + echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!" + fi + else + currListName="$(getListnameFromType "${currTypeID}")" + sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeID} WHERE domain='${domain}';" + if [[ "${verbose}" == true ]]; then + echo -e " ${INFO} ${1} already exists in ${currListName}, it has been updated to the requested list type." + fi fi return fi From 6a881545b0d99952af2e26b50e9e859415fe7254 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 13:19:58 +0000 Subject: [PATCH 23/34] tweak wording Signed-off-by: Adam Warner --- 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 55272cde..596a6fb7 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -164,7 +164,7 @@ AddDomain() { currListName="$(getListnameFromType "${currTypeID}")" sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeID} WHERE domain='${domain}';" if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} already exists in ${currListName}, it has been updated to the requested list type." + echo -e " ${INFO} ${1} already exists in ${currListName}, it has been moved to ${listname}" fi fi return From 77bfb3fb671a1049119ae3d075c450277b668187 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 14:18:12 +0000 Subject: [PATCH 24/34] tidy up variable usage in list.sh Remove some that are redundant Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 104 ++++++++++++++------------------------- 1 file changed, 38 insertions(+), 66 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 596a6fb7..43f84008 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -21,25 +21,12 @@ web=false domList=() -listType="" -listname="" +typeId="" colfile="/opt/pihole/COL_TABLE" source ${colfile} -getTypeID() { - if [[ "$1" == "whitelist" ]]; then - echo "0" - elif [[ "$1" == "blacklist" ]]; then - echo "1" - elif [[ "$1" == "regex_whitelist" ]]; then - echo "2" - elif [[ "$1" == "regex_blacklist" ]]; then - echo "3" - fi -} - -getListnameFromType() { +getListnameFromTypeId() { if [[ "$1" == "0" ]]; then echo "whitelist" elif [[ "$1" == "1" ]]; then @@ -52,19 +39,19 @@ getListnameFromType() { } helpFunc() { - if [[ "${listType}" == "whitelist" ]]; then + if [[ "${typeId}" == "0" ]]; then param="w" type="whitelist" - elif [[ "${listType}" == "regex_blacklist" && "${wildcard}" == true ]]; then + elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then param="-wild" type="wildcard blacklist" - elif [[ "${listType}" == "regex_blacklist" ]]; then + elif [[ "${typeId}" == "3" ]]; then param="-regex" type="regex blacklist filter" - elif [[ "${listType}" == "regex_whitelist" && "${wildcard}" == true ]]; then + elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then param="-white-wild" type="wildcard whitelist" - elif [[ "${listType}" == "regex_whitelist" ]]; then + elif [[ "${typeId}" == "2" ]]; then param="-white-regex" type="regex whitelist filter" else @@ -101,7 +88,7 @@ HandleOther() { # Check validity of domain (don't check for regex entries) if [[ "${#domain}" -le 253 ]]; then - if [[ ( "${listType}" == "regex_blacklist" || "${listType}" == "regex_whitelist" ) && "${wildcard}" == false ]]; then + if [[ ( "${typeId}" == "3" || "${typeId}" == "2" ) && "${wildcard}" == false ]]; then validDomain="${domain}" else validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check @@ -117,17 +104,6 @@ HandleOther() { } ProcessDomainList() { - if [[ "${listType}" == "regex_blacklist" ]]; then - # Regex black filter list - listname="regex blacklist filters" - elif [[ "${listType}" == "regex_whitelist" ]]; then - # Regex white filter list - listname="regex whitelist filters" - else - # Whitelist / Blacklist - listname="${listType}" - fi - for dom in "${domList[@]}"; do # Format domain into regex filter if requested if [[ "${wildcard}" == true ]]; then @@ -137,34 +113,33 @@ ProcessDomainList() { # Logic: If addmode then add to desired list and remove from the other; # if delmode then remove from desired list but do not add to the other if ${addmode}; then - AddDomain "${dom}" "${listType}" + AddDomain "${dom}" else - RemoveDomain "${dom}" "${listType}" + RemoveDomain "${dom}" fi done } AddDomain() { - local domain list num currTypeID currListName typeID + local domain num requestedListname existingTypeId existingListname # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" - list="$2" - typeID="$(getTypeID "${list}")" # Is the domain in the list we want to add it to? num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}';")" + requestedListname="$(getListnameFromTypeId "${typeId}")" if [[ "${num}" -ne 0 ]]; then - currTypeID="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" - if [[ "${currTypeID}" == "${typeID}" ]]; 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 ${listname}, no need to add!" + echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!" fi else - currListName="$(getListnameFromType "${currTypeID}")" - sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeID} WHERE domain='${domain}';" + existingListname="$(getListnameFromTypeId "${existingTypeId}")" + sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';" if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} already exists in ${currListName}, it has been moved to ${listname}" + echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!" fi fi return @@ -172,51 +147,50 @@ AddDomain() { # Domain not found in the table, add it! if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} Adding ${1} to the ${listname}..." + echo -e " ${INFO} Adding ${domain} to the ${requestedListname}..." fi reload=true # Insert only the domain here. The enabled and date_added fields will be filled # with their default values (enabled = true, date_added = current timestamp) - sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeID});" + sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeId});" } RemoveDomain() { - local domain list num typeID + local domain num requestedListname # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" - list="$2" - typeID="$(getTypeID "${list}")" # Is the domain in the list we want to remove it from? - num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};")" + num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};")" + + requestedListname="$(getListnameFromTypeId "${typeId}")" if [[ "${num}" -eq 0 ]]; then if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} ${1} does not exist in ${list}, no need to remove!" + echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!" fi return fi # Domain found in the table, remove it! if [[ "${verbose}" == true ]]; then - echo -e " ${INFO} Removing ${1} from the ${listname}..." + echo -e " ${INFO} Removing ${domain} from the ${requestedListname}..." fi reload=true # Remove it from the current list - sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE domain = '${domain}' AND type = ${typeID};" + sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};" } Displaylist() { - local list listname count num_pipes domain enabled status nicedate typeID + local count num_pipes domain enabled status nicedate requestedListname - listname="${listType}" - typeID="$(getTypeID "${listType}")" - data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeID};" 2> /dev/null)" + requestedListname="$(getListnameFromTypeId "${typeId}")" + data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeId};" 2> /dev/null)" if [[ -z $data ]]; then echo -e "Not showing empty list" else - echo -e "Displaying ${listname}:" + echo -e "Displaying ${requestedListname}:" count=1 while IFS= read -r line do @@ -249,19 +223,17 @@ Displaylist() { } NukeList() { - local typeID - typeID=$(getTypeID "${list}") - sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeID};" + sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};" } for var in "$@"; do case "${var}" in - "-w" | "whitelist" ) listType="whitelist"; listAlt="blacklist";; - "-b" | "blacklist" ) listType="blacklist"; listAlt="whitelist";; - "--wild" | "wildcard" ) listType="regex_blacklist"; wildcard=true;; - "--regex" | "regex" ) listType="regex_blacklist";; - "--white-regex" | "white-regex" ) listType="regex_whitelist";; - "--white-wild" | "white-wild" ) listType="regex_whitelist"; wildcard=true;; + "-w" | "whitelist" ) typeId=0;; + "-b" | "blacklist" ) typeId=1;; + "--white-regex" | "white-regex" ) typeId=2;; + "--white-wild" | "white-wild" ) typeId=2; wildcard=true;; + "--wild" | "wildcard" ) typeId=3; wildcard=true;; + "--regex" | "regex" ) typeId=3;; "-nr"| "--noreload" ) reload=false;; "-d" | "--delmode" ) addmode=false;; "-q" | "--quiet" ) verbose=false;; From edaee4e9626223574b4432a03bc7d86f99baa205 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 16:02:50 +0000 Subject: [PATCH 25/34] remove redundant function and comments Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 43f84008..7707ceea 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -74,14 +74,6 @@ Options: exit 0 } -EscapeRegexp() { - # This way we may safely insert an arbitrary - # string in our regular expressions - # This sed is intentionally executed in three steps to ease maintainability - # The first sed removes any amount of leading dots - echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g" -} - HandleOther() { # Convert to lowercase domain="${1,,}" @@ -122,7 +114,6 @@ ProcessDomainList() { AddDomain() { local domain num requestedListname existingTypeId existingListname - # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" # Is the domain in the list we want to add it to? @@ -157,7 +148,6 @@ AddDomain() { RemoveDomain() { local domain num requestedListname - # Use printf to escape domain. %q prints the argument in a form that can be reused as shell input domain="$1" # Is the domain in the list we want to remove it from? From 4b8a72fda71bae5cc0ed2cfb6ba5bbb70a623b89 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 16:26:26 +0000 Subject: [PATCH 26/34] functionise parameter discovery Rename HandleOther to ValidateDomain Capital letters on the new functions Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 59 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 7707ceea..5fbe831f 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -26,7 +26,7 @@ typeId="" colfile="/opt/pihole/COL_TABLE" source ${colfile} -getListnameFromTypeId() { +GetListnameFromTypeId() { if [[ "$1" == "0" ]]; then echo "whitelist" elif [[ "$1" == "1" ]]; then @@ -38,43 +38,44 @@ getListnameFromTypeId() { fi } -helpFunc() { +GetListParamFromTypeId() { if [[ "${typeId}" == "0" ]]; then - param="w" - type="whitelist" - elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then - param="-wild" - type="wildcard blacklist" - elif [[ "${typeId}" == "3" ]]; then - param="-regex" - type="regex blacklist filter" - elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then - param="-white-wild" - type="wildcard whitelist" - elif [[ "${typeId}" == "2" ]]; then - param="-white-regex" - type="regex whitelist filter" - else - param="b" - type="blacklist" + echo "w" + elif [[ "${typeId}" == "1" ]]; then + echo "b" + elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then + echo "-white-wild" + elif [[ "${typeId}" == "2" ]]; then + echo "regex_blacklist" + elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then + echo "-regex" + elif [[ "${typeId}" == "3" ]]; then + echo "-wild" fi +} + +helpFunc() { + local listname param + + listname="$(GetListnameFromTypeId "${typeId}")" + param="$(GetListParamFromTypeId)" echo "Usage: pihole -${param} [options] Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com' -${type^} one or more domains +${listname^} one or more domains Options: - -d, --delmode Remove domain(s) from the ${type} - -nr, --noreload Update ${type} without reloading the DNS server + -d, --delmode Remove domain(s) from the ${listname} + -nr, --noreload Update ${listname} without reloading the DNS server -q, --quiet Make output less verbose -h, --help Show this help dialog - -l, --list Display all your ${type}listed domains + -l, --list Display all your ${listname}listed domains --nuke Removes all entries in a list" exit 0 } -HandleOther() { +ValidateDomain() { # Convert to lowercase domain="${1,,}" @@ -118,7 +119,7 @@ AddDomain() { # Is the domain in the list we want to add it to? num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}';")" - requestedListname="$(getListnameFromTypeId "${typeId}")" + requestedListname="$(GetListnameFromTypeId "${typeId}")" if [[ "${num}" -ne 0 ]]; then existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" @@ -127,7 +128,7 @@ AddDomain() { echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!" fi else - existingListname="$(getListnameFromTypeId "${existingTypeId}")" + 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}!" @@ -153,7 +154,7 @@ RemoveDomain() { # Is the domain in the list we want to remove it from? num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};")" - requestedListname="$(getListnameFromTypeId "${typeId}")" + requestedListname="$(GetListnameFromTypeId "${typeId}")" if [[ "${num}" -eq 0 ]]; then if [[ "${verbose}" == true ]]; then @@ -174,7 +175,7 @@ RemoveDomain() { Displaylist() { local count num_pipes domain enabled status nicedate requestedListname - requestedListname="$(getListnameFromTypeId "${typeId}")" + requestedListname="$(GetListnameFromTypeId "${typeId}")" data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeId};" 2> /dev/null)" if [[ -z $data ]]; then @@ -231,7 +232,7 @@ for var in "$@"; do "-l" | "--list" ) Displaylist;; "--nuke" ) NukeList;; "--web" ) web=true;; - * ) HandleOther "${var}";; + * ) ValidateDomain "${var}";; esac done From 76460f01e9c70905b7ac82d9a294855c6bfd19bc Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sat, 30 Nov 2019 17:45:07 +0000 Subject: [PATCH 27/34] Change the regex used for domain validation Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 5fbe831f..50541872 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -84,8 +84,8 @@ ValidateDomain() { if [[ ( "${typeId}" == "3" || "${typeId}" == "2" ) && "${wildcard}" == false ]]; then validDomain="${domain}" else - validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check - validDomain=$(grep -P "^[^\\.]{1,63}(\\.[^\\.]{1,63})*$" <<< "${validDomain}") # Length of each label + # Use regex to check the validity of the passed domain. see https://regexr.com/3abjr + validDomain=$(grep -P "^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$" <<< "${domain}") fi fi From 44e1455b12e0dafba4ba9cfca7708bdb9aa2d188 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 1 Dec 2019 12:44:48 +0000 Subject: [PATCH 28/34] Update advanced/Scripts/list.sh 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 50541872..40fabd54 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -46,7 +46,7 @@ GetListParamFromTypeId() { elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then echo "-white-wild" elif [[ "${typeId}" == "2" ]]; then - echo "regex_blacklist" + echo "-white-regex" elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then echo "-regex" elif [[ "${typeId}" == "3" ]]; then From 0251117c779f9f3ebb2056862e1009b8a0e88e28 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 1 Dec 2019 12:45:06 +0000 Subject: [PATCH 29/34] Update advanced/Scripts/list.sh 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 40fabd54..064181f0 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -50,7 +50,7 @@ GetListParamFromTypeId() { elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then echo "-regex" elif [[ "${typeId}" == "3" ]]; then - echo "-wild" + echo "-regex" fi } From 63e407cfdc9f7fd1ffddd6c49b3bf6b2dfd63c63 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 1 Dec 2019 12:45:22 +0000 Subject: [PATCH 30/34] Update advanced/Scripts/list.sh 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 064181f0..a5c84661 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -48,7 +48,7 @@ GetListParamFromTypeId() { elif [[ "${typeId}" == "2" ]]; then echo "-white-regex" elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then - echo "-regex" + echo "-wild" elif [[ "${typeId}" == "3" ]]; then echo "-regex" fi From 869473172c2d8620e180aa037fb9cde12cef1fb3 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 1 Dec 2019 12:50:24 +0000 Subject: [PATCH 31/34] remove _ from regex descibers Signed-off-by: Adam Warner --- advanced/Scripts/list.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index a5c84661..a7d12657 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -32,9 +32,9 @@ GetListnameFromTypeId() { elif [[ "$1" == "1" ]]; then echo "blacklist" elif [[ "$1" == "2" ]]; then - echo "regex_whitelist" + echo "regex whitelist" elif [[ "$1" == "3" ]]; then - echo "regex_blacklist" + echo "regex blacklist" fi } From b6cd7b8e3d36a5d7208136a671e65913076aef79 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 2 Dec 2019 17:27:32 +0000 Subject: [PATCH 32/34] Use more descriptive names instead of directly using the IDs in list.sh Signed-off-by: DL6ER --- advanced/Scripts/list.sh | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index a7d12657..c5bf5b2a 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -26,30 +26,37 @@ typeId="" colfile="/opt/pihole/COL_TABLE" source ${colfile} +# IDs are hard-wired to domain interpretation in the gravity database scheme +# Clients (including FTL) will read them through the corresponding views +readonly whitelist="0" +readonly blacklist="1" +readonly regex_whitelist="2" +readonly regex_blacklist="3" + GetListnameFromTypeId() { - if [[ "$1" == "0" ]]; then + if [[ "$1" == "${whitelist}" ]]; then echo "whitelist" - elif [[ "$1" == "1" ]]; then + elif [[ "$1" == "${blacklist}" ]]; then echo "blacklist" - elif [[ "$1" == "2" ]]; then + elif [[ "$1" == "${regex_whitelist}" ]]; then echo "regex whitelist" - elif [[ "$1" == "3" ]]; then + elif [[ "$1" == "${regex_blacklist}" ]]; then echo "regex blacklist" fi } GetListParamFromTypeId() { - if [[ "${typeId}" == "0" ]]; then + if [[ "${typeId}" == "${whitelist}" ]]; then echo "w" - elif [[ "${typeId}" == "1" ]]; then + elif [[ "${typeId}" == "${blacklist}" ]]; then echo "b" - elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then + elif [[ "${typeId}" == "${regex_whitelist}" && "${wildcard}" == true ]]; then echo "-white-wild" - elif [[ "${typeId}" == "2" ]]; then + elif [[ "${typeId}" == "${regex_whitelist}" ]]; then echo "-white-regex" - elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then + elif [[ "${typeId}" == "${regex_blacklist}" && "${wildcard}" == true ]]; then echo "-wild" - elif [[ "${typeId}" == "3" ]]; then + elif [[ "${typeId}" == "${regex_blacklist}" ]]; then echo "-regex" fi } @@ -81,7 +88,7 @@ ValidateDomain() { # Check validity of domain (don't check for regex entries) if [[ "${#domain}" -le 253 ]]; then - if [[ ( "${typeId}" == "3" || "${typeId}" == "2" ) && "${wildcard}" == false ]]; then + if [[ ( "${typeId}" == "${regex_blacklist}" || "${typeId}" == "${regex_whitelist}" ) && "${wildcard}" == false ]]; then validDomain="${domain}" else # Use regex to check the validity of the passed domain. see https://regexr.com/3abjr From 85673b8273f5b27de92ae9bbde942569453fdbdf Mon Sep 17 00:00:00 2001 From: MichaIng Date: Wed, 4 Dec 2019 18:59:25 +0100 Subject: [PATCH 33/34] Print name of chosen upstream DNS as well Signed-off-by: MichaIng --- automated install/basic-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c2f8ced5..59a55001 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1071,7 +1071,9 @@ setDNS() { fi # Display final selection - printf " %b Using upstream DNS: %s %s\\n" "${INFO}" "${PIHOLE_DNS_1}" "${PIHOLE_DNS_2}" + local DNSIP=${PIHOLE_DNS_1} + [[ -z ${PIHOLE_DNS_2} ]] || DNSIP+=", ${PIHOLE_DNS_2}" + printf " %b Using upstream DNS: %s (%s)\\n" "${INFO}" "${DNSchoices}" "${DNSIP}" } # Allow the user to enable/disable logging From eaf1244932ecb3e3eece033649453241df513b69 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Wed, 4 Dec 2019 20:09:34 +0000 Subject: [PATCH 34/34] :dominik: Detect binary name before calling FTLcheckUpdate in update.sh Signed-off-by: Adam Warner --- advanced/Scripts/update.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 443dfb1f..e45be5cf 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -128,7 +128,12 @@ main() { fi fi - if FTLcheckUpdate > /dev/null; then + local funcOutput + funcOutput=$(get_binary_name) #Store output of get_binary_name here + local binary + binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL) + + if FTLcheckUpdate "${binary}" > /dev/null; then FTL_update=true echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}" else