Pi-hole v5.13 (#4960)

pull/4970/head v5.13
Adam Warner 2 years ago committed by GitHub
commit 17779bad94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,7 +25,7 @@ jobs:
steps: steps:
- -
name: Checkout repository name: Checkout repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- -
name: Initialize CodeQL name: Initialize CodeQL

@ -13,7 +13,7 @@ jobs:
issues: write issues: write
steps: steps:
- uses: actions/stale@v5.2.0 - uses: actions/stale@v6.0.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30 days-before-stale: 30

@ -11,7 +11,7 @@ jobs:
name: Syncing branches name: Syncing branches
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Opening pull request - name: Opening pull request
id: pull id: pull
uses: tretuna/sync-branches@1.4.0 uses: tretuna/sync-branches@1.4.0

@ -12,29 +12,33 @@ jobs:
if: github.event.pull_request.draft == false if: github.event.pull_request.draft == false
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- - name: Checkout repository
name: Checkout repository uses: actions/checkout@v3.1.0
uses: actions/checkout@v3.0.2
-
name: Check scripts in repository are executable
run: |
IFS=$'\n';
for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
unset IFS;
# If FAIL is 1 then we fail.
[[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!"
-
name: Spell-Checking
uses: codespell-project/actions-codespell@master
with:
ignore_words_file: .codespellignore
-
name: Get editorconfig-checker
uses: editorconfig-checker/action-editorconfig-checker@main # tag v1.0.0 is really out of date
-
name: Run editorconfig-checker
run: editorconfig-checker
- name: Check scripts in repository are executable
run: |
IFS=$'\n';
for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
unset IFS;
# If FAIL is 1 then we fail.
[[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!"
- name: Spell-Checking
uses: codespell-project/actions-codespell@master
with:
ignore_words_file: .codespellignore
- name: Get editorconfig-checker
uses: editorconfig-checker/action-editorconfig-checker@main # tag v1.0.0 is really out of date
- name: Run editorconfig-checker
run: editorconfig-checker
- name: Check python code formatting with black
uses: psf/black@stable
with:
src: "./test"
options: "--check --diff --color"
distro-test: distro-test:
if: github.event.pull_request.draft == false if: github.event.pull_request.draft == false
@ -43,21 +47,29 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
distro: [debian_10, debian_11, ubuntu_20, ubuntu_22, centos_8, fedora_34] distro:
[
debian_10,
debian_11,
ubuntu_20,
ubuntu_22,
centos_8,
fedora_35,
fedora_36,
]
env: env:
DISTRO: ${{matrix.distro}} DISTRO: ${{matrix.distro}}
steps: steps:
- - name: Checkout repository
name: Checkout repository uses: actions/checkout@v3.1.0
uses: actions/checkout@v3.0.2
- - name: Set up Python 3.10
name: Set up Python 3.10 uses: actions/setup-python@v4.2.0
uses: actions/setup-python@v4.2.0 with:
with: python-version: "3.10"
python-version: '3.10'
- - name: Install dependencies
name: Install dependencies run: pip install -r test/requirements.txt
run: pip install -r test/requirements.txt
- - name: Test with tox
name: Test with tox run: tox -c test/tox.${DISTRO}.ini
run: tox -c test/tox.${DISTRO}.ini

@ -29,13 +29,6 @@ bogus-priv
no-resolv no-resolv
server=@DNS1@
server=@DNS2@
interface=@INT@
cache-size=@CACHE_SIZE@
log-queries log-queries
log-facility=/var/log/pihole/pihole.log log-facility=/var/log/pihole/pihole.log

@ -14,7 +14,9 @@ LC_NUMERIC=C
# Retrieve stats from FTL engine # Retrieve stats from FTL engine
pihole-FTL() { pihole-FTL() {
local ftl_port LINE local ftl_port LINE
ftl_port=$(cat /run/pihole-FTL.port 2> /dev/null) # shellcheck disable=SC1091
. /opt/pihole/utils.sh
ftl_port=$(getFTLAPIPort)
if [[ -n "$ftl_port" ]]; then if [[ -n "$ftl_port" ]]; then
# Open connection to FTL # Open connection to FTL
exec 3<>"/dev/tcp/127.0.0.1/$ftl_port" exec 3<>"/dev/tcp/127.0.0.1/$ftl_port"
@ -503,11 +505,11 @@ chronoFunc() {
fi fi
printFunc " Pi-hole: " "$ph_status" "$ph_info" printFunc " Pi-hole: " "$ph_status" "$ph_info"
printFunc " Ads Today: " "$ads_percentage_today%" "$ads_info" printFunc " Blocked: " "$ads_percentage_today%" "$ads_info"
printFunc "Local Qrys: " "$queries_cached_percentage%" "$dns_info" printFunc "Local Qrys: " "$queries_cached_percentage%" "$dns_info"
printFunc " Blocked: " "$recent_blocked" printFunc "Last Block: " "$recent_blocked"
printFunc "Top Advert: " "$top_ad" printFunc " Top Block: " "$top_ad"
# Provide more stats on screens with more lines # Provide more stats on screens with more lines
if [[ "$scr_lines" -eq 17 ]]; then if [[ "$scr_lines" -eq 17 ]]; then

@ -42,6 +42,11 @@ warning1() {
esac esac
} }
updateCheckFunc() {
/opt/pihole/updatecheck.sh
/opt/pihole/updatecheck.sh x remote
}
checkout() { checkout() {
local corebranches local corebranches
local webbranches local webbranches
@ -164,6 +169,8 @@ checkout() {
exit 1 exit 1
fi fi
checkout_pull_branch "${webInterfaceDir}" "${2}" checkout_pull_branch "${webInterfaceDir}" "${2}"
# Force an update of the updatechecker
updateCheckFunc
elif [[ "${1}" == "ftl" ]] ; then elif [[ "${1}" == "ftl" ]] ; then
local path local path
local oldbranch local oldbranch
@ -178,6 +185,8 @@ checkout() {
FTLinstall "${binary}" FTLinstall "${binary}"
restart_service pihole-FTL restart_service pihole-FTL
enable_service pihole-FTL enable_service pihole-FTL
# Force an update of the updatechecker
updateCheckFunc
else else
echo " ${CROSS} Requested branch \"${2}\" is not available" echo " ${CROSS} Requested branch \"${2}\" is not available"
ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') ) ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') )

@ -126,7 +126,6 @@ PIHOLE_COMMAND="${BIN_DIRECTORY}/pihole"
PIHOLE_COLTABLE_FILE="${BIN_DIRECTORY}/COL_TABLE" PIHOLE_COLTABLE_FILE="${BIN_DIRECTORY}/COL_TABLE"
FTL_PID="${RUN_DIRECTORY}/pihole-FTL.pid" FTL_PID="${RUN_DIRECTORY}/pihole-FTL.pid"
FTL_PORT="${RUN_DIRECTORY}/pihole-FTL.port"
PIHOLE_LOG="${LOG_DIRECTORY}/pihole.log" PIHOLE_LOG="${LOG_DIRECTORY}/pihole.log"
PIHOLE_LOG_GZIPS="${LOG_DIRECTORY}/pihole.log.[0-9].*" PIHOLE_LOG_GZIPS="${LOG_DIRECTORY}/pihole.log.[0-9].*"
@ -155,7 +154,6 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_COMMAND}" "${PIHOLE_COMMAND}"
"${PIHOLE_COLTABLE_FILE}" "${PIHOLE_COLTABLE_FILE}"
"${FTL_PID}" "${FTL_PID}"
"${FTL_PORT}"
"${PIHOLE_LOG}" "${PIHOLE_LOG}"
"${PIHOLE_LOG_GZIPS}" "${PIHOLE_LOG_GZIPS}"
"${PIHOLE_DEBUG_LOG}" "${PIHOLE_DEBUG_LOG}"
@ -678,15 +676,20 @@ ping_gateway() {
local protocol="${1}" local protocol="${1}"
ping_ipv4_or_ipv6 "${protocol}" ping_ipv4_or_ipv6 "${protocol}"
# Check if we are using IPv4 or IPv6 # Check if we are using IPv4 or IPv6
# Find the default gateway using IPv4 or IPv6 # Find the default gateways using IPv4 or IPv6
local gateway local gateway
gateway="$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3)"
# If the gateway variable has a value (meaning a gateway was found), log_write "${INFO} Default IPv${protocol} gateway(s):"
if [[ -n "${gateway}" ]]; then
log_write "${INFO} Default IPv${protocol} gateway: ${gateway}" while IFS= read -r gateway; do
log_write " ${gateway}"
done < <(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3)
gateway=$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3 | head -n 1)
# If there was at least one gateway
if [ -n "${gateway}" ]; then
# Let the user know we will ping the gateway for a response # Let the user know we will ping the gateway for a response
log_write " * Pinging ${gateway}..." log_write " * Pinging first gateway ${gateway}..."
# Try to quietly ping the gateway 3 times, with a timeout of 3 seconds, using numeric output only, # Try to quietly ping the gateway 3 times, with a timeout of 3 seconds, using numeric output only,
# on the pihole interface, and tail the last three lines of the output # on the pihole interface, and tail the last three lines of the output
# If pinging the gateway is not successful, # If pinging the gateway is not successful,

@ -38,6 +38,14 @@ VERSION_FILE="/etc/pihole/versions"
touch "${VERSION_FILE}" touch "${VERSION_FILE}"
chmod 644 "${VERSION_FILE}" chmod 644 "${VERSION_FILE}"
# if /pihole.docker.tag file exists, we will use it's value later in this script
DOCKER_TAG=$(cat /pihole.docker.tag 2>/dev/null)
regex='^([0-9]+\.){1,2}(\*|[0-9]+)(-.*)?$|(^nightly$)|(^dev.*$)'
if [[ ! "${DOCKER_TAG}" =~ $regex ]]; then
# DOCKER_TAG does not match the pattern (see https://regex101.com/r/RsENuz/1), so unset it.
unset DOCKER_TAG
fi
if [[ "$2" == "remote" ]]; then if [[ "$2" == "remote" ]]; then
if [[ "$3" == "reboot" ]]; then if [[ "$3" == "reboot" ]]; then
@ -55,7 +63,7 @@ if [[ "$2" == "remote" ]]; then
GITHUB_FTL_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null | jq --raw-output .tag_name)" GITHUB_FTL_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null | jq --raw-output .tag_name)"
addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_VERSION" "${GITHUB_FTL_VERSION}" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_VERSION" "${GITHUB_FTL_VERSION}"
if [[ "${PIHOLE_DOCKER_TAG}" ]]; then if [[ "${DOCKER_TAG}" ]]; then
GITHUB_DOCKER_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/docker-pi-hole/releases/latest' 2> /dev/null | jq --raw-output .tag_name)" GITHUB_DOCKER_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/docker-pi-hole/releases/latest' 2> /dev/null | jq --raw-output .tag_name)"
addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_DOCKER_VERSION" "${GITHUB_DOCKER_VERSION}" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_DOCKER_VERSION" "${GITHUB_DOCKER_VERSION}"
fi fi
@ -84,9 +92,8 @@ else
FTL_VERSION="$(pihole-FTL version)" FTL_VERSION="$(pihole-FTL version)"
addOrEditKeyValPair "${VERSION_FILE}" "FTL_VERSION" "${FTL_VERSION}" addOrEditKeyValPair "${VERSION_FILE}" "FTL_VERSION" "${FTL_VERSION}"
# PIHOLE_DOCKER_TAG is set as env variable only on docker installations if [[ "${DOCKER_TAG}" ]]; then
if [[ "${PIHOLE_DOCKER_TAG}" ]]; then addOrEditKeyValPair "${VERSION_FILE}" "DOCKER_VERSION" "${DOCKER_TAG}"
addOrEditKeyValPair "${VERSION_FILE}" "DOCKER_VERSION" "${PIHOLE_DOCKER_TAG}"
fi fi
fi fi

@ -32,8 +32,8 @@ addOrEditKeyValPair() {
local value="${3}" local value="${3}"
if grep -q "^${key}=" "${file}"; then if grep -q "^${key}=" "${file}"; then
# Key already exists in file, modify the value # Key already exists in file, modify the value
sed -i "/^${key}=/c\\${key}=${value}" "${file}" sed -i "/^${key}=/c\\${key}=${value}" "${file}"
else else
# Key does not already exist, add it and it's value # Key does not already exist, add it and it's value
echo "${key}=${value}" >> "${file}" echo "${key}=${value}" >> "${file}"
@ -52,8 +52,8 @@ addKey(){
local key="${2}" local key="${2}"
if ! grep -q "^${key}" "${file}"; then if ! grep -q "^${key}" "${file}"; then
# Key does not exist, add it. # Key does not exist, add it.
echo "${key}" >> "${file}" echo "${key}" >> "${file}"
fi fi
} }
@ -70,47 +70,27 @@ removeKey() {
sed -i "/^${key}/d" "${file}" sed -i "/^${key}/d" "${file}"
} }
#######################
# returns path of FTL's port file
#######################
getFTLAPIPortFile() {
local FTLCONFFILE="/etc/pihole/pihole-FTL.conf"
local DEFAULT_PORT_FILE="/run/pihole-FTL.port"
local FTL_APIPORT_FILE
if [ -s "${FTLCONFFILE}" ]; then
# if PORTFILE is not set in pihole-FTL.conf, use the default path
FTL_APIPORT_FILE="$({ grep '^PORTFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PORT_FILE}"; } | cut -d'=' -f2-)"
else
# if there is no pihole-FTL.conf, use the default path
FTL_APIPORT_FILE="${DEFAULT_PORT_FILE}"
fi
echo "${FTL_APIPORT_FILE}"
}
#######################
# returns FTL's current telnet API port based on the content of the pihole-FTL.port file
#
# Takes one argument: path to pihole-FTL.port
# Example getFTLAPIPort "/run/pihole-FTL.port"
####################### #######################
# returns FTL's current telnet API port based on the setting in /etc/pihole-FTL.conf
########################
getFTLAPIPort(){ getFTLAPIPort(){
local PORTFILE="${1}" local FTLCONFFILE="/etc/pihole/pihole-FTL.conf"
local DEFAULT_FTL_PORT=4711 local DEFAULT_FTL_PORT=4711
local ftl_api_port local ftl_api_port
if [ -s "$PORTFILE" ]; then if [ -s "$FTLCONFFILE" ]; then
# -s: FILE exists and has a size greater than zero # if FTLPORT is not set in pihole-FTL.conf, use the default port
ftl_api_port=$(cat "${PORTFILE}") ftl_api_port="$({ grep '^FTLPORT=' "${FTLCONFFILE}" || echo "${DEFAULT_FTL_PORT}"; } | cut -d'=' -f2-)"
# Exploit prevention: unset the variable if there is malicious content # Exploit prevention: set the port to the default port if there is malicious (non-numeric)
# Verify that the value read from the file is numeric # content set in pihole-FTL.conf
expr "$ftl_api_port" : "[^[:digit:]]" > /dev/null && unset ftl_api_port expr "${ftl_api_port}" : "[^[:digit:]]" > /dev/null && ftl_api_port="${DEFAULT_FTL_PORT}"
else
# if there is no pihole-FTL.conf, use the default port
ftl_api_port="${DEFAULT_FTL_PORT}"
fi fi
# echo the port found in the portfile or default to the default port echo "${ftl_api_port}"
echo "${ftl_api_port:=$DEFAULT_FTL_PORT}"
} }
####################### #######################

@ -393,13 +393,8 @@ ProcessDHCPSettings() {
if [[ "${DHCP_LEASETIME}" == "0" ]]; then if [[ "${DHCP_LEASETIME}" == "0" ]]; then
leasetime="infinite" leasetime="infinite"
elif [[ "${DHCP_LEASETIME}" == "" ]]; then elif [[ "${DHCP_LEASETIME}" == "" ]]; then
leasetime="24" leasetime="24h"
addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "${leasetime}" addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "24"
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"
addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "${leasetime}"
else else
leasetime="${DHCP_LEASETIME}h" leasetime="${DHCP_LEASETIME}h"
fi fi

@ -9,7 +9,7 @@
# Description: Enable service provided by pihole-FTL daemon # Description: Enable service provided by pihole-FTL daemon
### END INIT INFO ### END INIT INFO
#source utils.sh for getFTLPIDFile(), getFTLPID (), getFTLAPIPortFile() #source utils.sh for getFTLPIDFile(), getFTLPID ()
PI_HOLE_SCRIPT_DIR="/opt/pihole" PI_HOLE_SCRIPT_DIR="/opt/pihole"
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
. "${utilsfile}" . "${utilsfile}"
@ -31,7 +31,6 @@ start() {
# Touch files to ensure they exist (create if non-existing, preserve if existing) # Touch files to ensure they exist (create if non-existing, preserve if existing)
mkdir -pm 0755 /run/pihole /var/log/pihole mkdir -pm 0755 /run/pihole /var/log/pihole
[ ! -f "${FTL_PID_FILE}" ] && install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PID_FILE}" [ ! -f "${FTL_PID_FILE}" ] && install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PID_FILE}"
[ ! -f "${FTL_PORT_FILE}" ] && install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PORT_FILE}"
[ ! -f /var/log/pihole/FTL.log ] && install -m 644 -o pihole -g pihole /dev/null /var/log/pihole/FTL.log [ ! -f /var/log/pihole/FTL.log ] && install -m 644 -o pihole -g pihole /dev/null /var/log/pihole/FTL.log
[ ! -f /var/log/pihole/pihole.log ] && install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/pihole.log [ ! -f /var/log/pihole/pihole.log ] && install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/pihole.log
[ ! -f /etc/pihole/dhcp.leases ] && install -m 644 -o pihole -g pihole /dev/null /etc/pihole/dhcp.leases [ ! -f /etc/pihole/dhcp.leases ] && install -m 644 -o pihole -g pihole /dev/null /etc/pihole/dhcp.leases
@ -91,7 +90,7 @@ stop() {
echo "Not running" echo "Not running"
fi fi
# Cleanup # Cleanup
rm -f /run/pihole/FTL.sock /dev/shm/FTL-* "${FTL_PID_FILE}" "${FTL_PORT_FILE}" rm -f /run/pihole/FTL.sock /dev/shm/FTL-* "${FTL_PID_FILE}"
echo echo
} }
@ -111,7 +110,6 @@ status() {
# Get file paths # Get file paths
FTL_PID_FILE="$(getFTLPIDFile)" FTL_PID_FILE="$(getFTLPIDFile)"
FTL_PORT_FILE="$(getFTLAPIPortFile)"
# Get FTL's current PID # Get FTL's current PID
FTL_PID="$(getFTLPID ${FTL_PID_FILE})" FTL_PID="$(getFTLPID ${FTL_PID_FILE})"

@ -28,9 +28,6 @@
@reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate @reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate
# Pi-hole: Grab local version and branch every 10 minutes
*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local
# Pi-hole: Grab remote version every 24 hours # Pi-hole: Grab remote version every 24 hours
59 17 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote 59 17 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote
@reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote reboot @reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote reboot

@ -29,7 +29,8 @@ if (!empty($_SERVER["FQDN"])) {
if ($serverName === "pi.hole" if ($serverName === "pi.hole"
|| (!empty($_SERVER["VIRTUAL_HOST"]) && $serverName === $_SERVER["VIRTUAL_HOST"])) { || (!empty($_SERVER["VIRTUAL_HOST"]) && $serverName === $_SERVER["VIRTUAL_HOST"])) {
// Redirect to Web Interface // Redirect to Web Interface
exit(header("Location: /admin")); header("Location: /admin");
exit();
} elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) { } elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
// When directly browsing via IP or authorized hostname // When directly browsing via IP or authorized hostname
// Render splash/landing page based off presence of $landPage file // Render splash/landing page based off presence of $landPage file
@ -75,6 +76,6 @@ EOT;
exit($splashPage); exit($splashPage);
} }
exit(header("HTTP/1.1 404 Not Found")); header("HTTP/1.1 404 Not Found");
exit();
?> ?>

@ -83,6 +83,7 @@ PI_HOLE_INSTALL_DIR="/opt/pihole"
PI_HOLE_CONFIG_DIR="/etc/pihole" PI_HOLE_CONFIG_DIR="/etc/pihole"
PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_BIN_DIR="/usr/local/bin"
PI_HOLE_404_DIR="${webroot}/pihole" PI_HOLE_404_DIR="${webroot}/pihole"
FTL_CONFIG_FILE="${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf"
if [ -z "$useUpdateVars" ]; then if [ -z "$useUpdateVars" ]; then
useUpdateVars=false useUpdateVars=false
fi fi
@ -999,10 +1000,10 @@ If you want to specify a port other than 53, separate it with a hash.\
# and continue the loop. # and continue the loop.
DNSSettingsCorrect=False DNSSettingsCorrect=False
else else
dialog --no-shadow --keep-tite \ dialog --no-shadow --no-collapse --keep-tite \
--backtitle "Specify Upstream DNS Provider(s)" \ --backtitle "Specify Upstream DNS Provider(s)" \
--title "Upstream DNS Provider(s)" \ --title "Upstream DNS Provider(s)" \
--yesno "Are these settings correct?\\n\\tDNS Server 1:\\t${PIHOLE_DNS_1}\\n\\tDNS Server 2:\\t${PIHOLE_DNS_2}" \ --yesno "Are these settings correct?\\n"$'\t'"DNS Server 1:"$'\t'"${PIHOLE_DNS_1}\\n"$'\t'"DNS Server 2:"$'\t'"${PIHOLE_DNS_2}" \
"${r}" "${c}" && result=0 || result=$? "${r}" "${c}" && result=0 || result=$?
case ${result} in case ${result} in
@ -1264,35 +1265,30 @@ version_check_dnsmasq() {
# Copy the new Pi-hole DNS config file into the dnsmasq.d directory # Copy the new Pi-hole DNS config file into the dnsmasq.d directory
install -D -m 644 -T "${dnsmasq_pihole_01_source}" "${dnsmasq_pihole_01_target}" install -D -m 644 -T "${dnsmasq_pihole_01_source}" "${dnsmasq_pihole_01_target}"
printf "%b %b Installed %s\n" "${OVER}" "${TICK}" "${dnsmasq_pihole_01_target}" printf "%b %b Installed %s\n" "${OVER}" "${TICK}" "${dnsmasq_pihole_01_target}"
# Replace our placeholder values with the GLOBAL DNS variables that we populated earlier # Add settings with the GLOBAL DNS variables that we populated earlier
# First, swap in the interface to listen on, # First, set the interface to listen on
sed -i "s/@INT@/$PIHOLE_INTERFACE/" "${dnsmasq_pihole_01_target}" addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "interface" "$PIHOLE_INTERFACE"
if [[ "${PIHOLE_DNS_1}" != "" ]]; then if [[ "${PIHOLE_DNS_1}" != "" ]]; then
# then swap in the primary DNS server. # then add in the primary DNS server.
sed -i "s/@DNS1@/$PIHOLE_DNS_1/" "${dnsmasq_pihole_01_target}" addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "server" "$PIHOLE_DNS_1"
else
# Otherwise, remove the line which sets DNS1.
sed -i '/^server=@DNS1@/d' "${dnsmasq_pihole_01_target}"
fi fi
# Ditto if DNS2 is not empty # Ditto if DNS2 is not empty
if [[ "${PIHOLE_DNS_2}" != "" ]]; then if [[ "${PIHOLE_DNS_2}" != "" ]]; then
sed -i "s/@DNS2@/$PIHOLE_DNS_2/" "${dnsmasq_pihole_01_target}" addKey "${dnsmasq_pihole_01_target}" "server=$PIHOLE_DNS_2"
else
sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}"
fi fi
# Set the cache size # Set the cache size
sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}" addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "cache-size" "$CACHE_SIZE"
sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}" sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}"
# If the user does not want to enable logging, # If the user does not want to enable logging,
if [[ "${QUERY_LOGGING}" == false ]] ; then if [[ "${QUERY_LOGGING}" == false ]] ; then
# disable it by commenting out the directive in the DNS config file # remove itfrom the DNS config file
sed -i 's/^log-queries/#log-queries/' "${dnsmasq_pihole_01_target}" removeKey "${dnsmasq_pihole_01_target}" "log-queries"
else else
# Otherwise, enable it by uncommenting the directive in the DNS config file # Otherwise, enable it by adding the directive to the DNS config file
sed -i 's/^#log-queries/log-queries/' "${dnsmasq_pihole_01_target}" addKey "${dnsmasq_pihole_01_target}" "log-queries"
fi fi
printf " %b Installing %s..." "${INFO}" "${dnsmasq_rfc6761_06_source}" printf " %b Installing %s..." "${INFO}" "${dnsmasq_rfc6761_06_source}"
@ -1365,9 +1361,9 @@ installConfigs() {
chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf" chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
# Install template file if it does not exist # Install template file if it does not exist
if [[ ! -r "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then if [[ ! -r "${FTL_CONFIG_FILE}" ]]; then
install -d -m 0755 ${PI_HOLE_CONFIG_DIR} install -d -m 0755 ${PI_HOLE_CONFIG_DIR}
if ! install -T -o pihole -m 664 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.conf" "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" &>/dev/null; then if ! install -T -o pihole -m 664 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.conf" "${FTL_CONFIG_FILE}" &>/dev/null; then
printf " %b Error: Unable to initialize configuration file %s/pihole-FTL.conf\\n" "${COL_LIGHT_RED}" "${PI_HOLE_CONFIG_DIR}" printf " %b Error: Unable to initialize configuration file %s/pihole-FTL.conf\\n" "${COL_LIGHT_RED}" "${PI_HOLE_CONFIG_DIR}"
return 1 return 1
fi fi
@ -1784,30 +1780,24 @@ create_pihole_user() {
# This function saves any changes to the setup variables into the setupvars.conf file for future runs # This function saves any changes to the setup variables into the setupvars.conf file for future runs
finalExports() { finalExports() {
# If the setup variable file exists, # set or update the variables in the file
if [[ -e "${setupVars}" ]]; then
# update the variables in the file addOrEditKeyValPair "${setupVars}" "PIHOLE_INTERFACE" "${PIHOLE_INTERFACE}"
sed -i.update.bak '/PIHOLE_INTERFACE/d;/PIHOLE_DNS_1\b/d;/PIHOLE_DNS_2\b/d;/QUERY_LOGGING/d;/INSTALL_WEB_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;/CACHE_SIZE/d;/DNS_FQDN_REQUIRED/d;/DNS_BOGUS_PRIV/d;/DNSMASQ_LISTENING/d;' "${setupVars}" addOrEditKeyValPair "${setupVars}" "PIHOLE_DNS_1" "${PIHOLE_DNS_1}"
fi addOrEditKeyValPair "${setupVars}" "PIHOLE_DNS_2" "${PIHOLE_DNS_2}"
# echo the information to the user addOrEditKeyValPair "${setupVars}" "QUERY_LOGGING" "${QUERY_LOGGING}"
{ addOrEditKeyValPair "${setupVars}" "INSTALL_WEB_SERVER" "${INSTALL_WEB_SERVER}"
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" addOrEditKeyValPair "${setupVars}" "INSTALL_WEB_INTERFACE" "${INSTALL_WEB_INTERFACE}"
echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" addOrEditKeyValPair "${setupVars}" "LIGHTTPD_ENABLED" "${LIGHTTPD_ENABLED}"
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" addOrEditKeyValPair "${setupVars}" "CACHE_SIZE" "${CACHE_SIZE}"
echo "QUERY_LOGGING=${QUERY_LOGGING}" addOrEditKeyValPair "${setupVars}" "DNS_FQDN_REQUIRED" "${DNS_FQDN_REQUIRED:-true}"
echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" addOrEditKeyValPair "${setupVars}" "DNS_BOGUS_PRIV" "${DNS_BOGUS_PRIV:-true}"
echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" addOrEditKeyValPair "${setupVars}" "DNSMASQ_LISTENING" "${DNSMASQ_LISTENING:-local}"
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 "DNSMASQ_LISTENING=${DNSMASQ_LISTENING:-local}"
}>> "${setupVars}"
chmod 644 "${setupVars}" chmod 644 "${setupVars}"
# Set the privacy level # Set the privacy level
sed -i '/PRIVACYLEVEL/d' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" addOrEditKeyValPair "${FTL_CONFIG_FILE}" "PRIVACYLEVEL" "${PRIVACY_LEVEL}"
echo "PRIVACYLEVEL=${PRIVACY_LEVEL}" >> "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf"
# Bring in the current settings and the functions to manipulate them # Bring in the current settings and the functions to manipulate them
source "${setupVars}" source "${setupVars}"
@ -1895,6 +1885,16 @@ installPihole() {
printf " %b Failure in dependent script copy function.\\n" "${CROSS}" printf " %b Failure in dependent script copy function.\\n" "${CROSS}"
exit 1 exit 1
fi fi
# /opt/pihole/utils.sh should be installed by installScripts now, so we can use it
if [ -f "${PI_HOLE_INSTALL_DIR}/utils.sh" ]; then
# shellcheck disable=SC1091
source "${PI_HOLE_INSTALL_DIR}/utils.sh"
else
printf " %b Failure: /opt/pihole/utils.sh does not exist .\\n" "${CROSS}"
exit 1
fi
# Install config files # Install config files
if ! installConfigs; then if ! installConfigs; then
printf " %b Failure in dependent config copy function.\\n" "${CROSS}" printf " %b Failure in dependent config copy function.\\n" "${CROSS}"
@ -2022,9 +2022,8 @@ update_dialogs() {
\\n($strAdd)"\ \\n($strAdd)"\
"${r}" "${c}" 2 \ "${r}" "${c}" 2 \
"${opt1a}" "${opt1b}" \ "${opt1a}" "${opt1b}" \
"${opt2a}" "${opt2b}" || true) "${opt2a}" "${opt2b}") || result=$?
result=$?
case ${result} in case ${result} in
"${DIALOG_CANCEL}" | "${DIALOG_ESC}") "${DIALOG_CANCEL}" | "${DIALOG_ESC}")
printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
@ -2569,8 +2568,8 @@ main() {
source "${setupVars}" source "${setupVars}"
# Get the privacy level if it exists (default is 0) # Get the privacy level if it exists (default is 0)
if [[ -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then if [[ -f "${FTL_CONFIG_FILE}" ]]; then
PRIVACY_LEVEL=$(sed -ne 's/PRIVACYLEVEL=\(.*\)/\1/p' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf") PRIVACY_LEVEL=$(sed -ne 's/PRIVACYLEVEL=\(.*\)/\1/p' "${FTL_CONFIG_FILE}")
# If no setting was found, default to 0 # If no setting was found, default to 0
PRIVACY_LEVEL="${PRIVACY_LEVEL:-0}" PRIVACY_LEVEL="${PRIVACY_LEVEL:-0}"

@ -40,6 +40,7 @@ gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql"
gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql" gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql"
domainsExtension="domains" domainsExtension="domains"
curl_connect_timeout=10
# Source setupVars from install script # Source setupVars from install script
setupVars="${piholeDir}/setupVars.conf" setupVars="${piholeDir}/setupVars.conf"
@ -641,7 +642,7 @@ gravity_DownloadBlocklistFromUrl() {
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
httpCode=$(curl -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null) httpCode=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null)
case $url in case $url in
# Did we "download" a local file? # Did we "download" a local file?

@ -303,14 +303,13 @@ analyze_ports() {
statusFunc() { statusFunc() {
# Determine if there is pihole-FTL service is listening # Determine if there is pihole-FTL service is listening
local pid port ftl_api_port ftl_pid_file ftl_apiport_file local pid port ftl_api_port ftl_pid_file
ftl_pid_file="$(getFTLPIDFile)" ftl_pid_file="$(getFTLPIDFile)"
pid="$(getFTLPID ${ftl_pid_file})" pid="$(getFTLPID ${ftl_pid_file})"
ftl_apiport_file="${getFTLAPIPortFile}" ftl_api_port="$(getFTLAPIPort)"
ftl_api_port="$(getFTLAPIPort ${ftl_apiport_file})"
if [[ "$pid" -eq "-1" ]]; then if [[ "$pid" -eq "-1" ]]; then
case "${1}" in case "${1}" in
"web") echo "-1";; "web") echo "-1";;

@ -1,5 +1,5 @@
FROM quay.io/centos/centos:stream8 FROM quay.io/centos/centos:stream8
RUN yum install -y git RUN yum install -y git initscripts
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -1,5 +1,5 @@
FROM fedora:34 FROM fedora:35
RUN dnf install -y git RUN dnf install -y git initscripts
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -0,0 +1,18 @@
FROM fedora:36
RUN dnf install -y git initscripts
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV SKIP_INSTALL true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

@ -6,12 +6,12 @@ from textwrap import dedent
SETUPVARS = { SETUPVARS = {
'PIHOLE_INTERFACE': 'eth99', "PIHOLE_INTERFACE": "eth99",
'PIHOLE_DNS_1': '4.2.2.1', "PIHOLE_DNS_1": "4.2.2.1",
'PIHOLE_DNS_2': '4.2.2.2' "PIHOLE_DNS_2": "4.2.2.2",
} }
IMAGE = 'pytest_pihole:test_container' IMAGE = "pytest_pihole:test_container"
tick_box = "[\x1b[1;32m\u2713\x1b[0m]" tick_box = "[\x1b[1;32m\u2713\x1b[0m]"
cross_box = "[\x1b[1;31m\u2717\x1b[0m]" cross_box = "[\x1b[1;31m\u2717\x1b[0m]"
@ -38,132 +38,187 @@ testinfra.backend.docker.DockerBackend.run = run_bash
@pytest.fixture @pytest.fixture
def host(): def host():
# run a container # run a container
docker_id = subprocess.check_output( docker_id = (
['docker', 'run', '-t', '-d', '--cap-add=ALL', IMAGE]).decode().strip() subprocess.check_output(["docker", "run", "-t", "-d", "--cap-add=ALL", IMAGE])
.decode()
.strip()
)
# return a testinfra connection to the container # return a testinfra connection to the container
docker_host = testinfra.get_host("docker://" + docker_id) docker_host = testinfra.get_host("docker://" + docker_id)
yield docker_host yield docker_host
# at the end of the test suite, destroy the container # at the end of the test suite, destroy the container
subprocess.check_call(['docker', 'rm', '-f', docker_id]) subprocess.check_call(["docker", "rm", "-f", docker_id])
# Helper functions # Helper functions
def mock_command(script, args, container): def mock_command(script, args, container):
''' """
Allows for setup of commands we don't really want to have to run for real Allows for setup of commands we don't really want to have to run for real
in unit tests in unit tests
''' """
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = "/usr/local/bin/{}".format(script)
mock_script = dedent(r'''\ mock_script = dedent(
r"""\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script)) case "\$1" in""".format(
script=script
)
)
for k, v in args.items(): for k, v in args.items():
case = dedent(''' case = dedent(
"""
{arg}) {arg})
echo {res} echo {res}
exit {retcode} exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1])) ;;""".format(
arg=k, res=v[0], retcode=v[1]
)
)
mock_script += case mock_script += case
mock_script += dedent(''' mock_script += dedent(
esac''') """
container.run(''' esac"""
)
container.run(
"""
cat <<EOF> {script}\n{content}\nEOF cat <<EOF> {script}\n{content}\nEOF
chmod +x {script} chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, rm -f /var/log/{scriptlog}""".format(
content=mock_script, script=full_script_path, content=mock_script, scriptlog=script
scriptlog=script)) )
)
def mock_command_passthrough(script, args, container): def mock_command_passthrough(script, args, container):
''' """
Per other mock_command* functions, allows intercepting of commands we don't want to run for real 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 in unit tests, however also allows only specific arguments to be mocked. Anything not defined will
be passed through to the actual command. be passed through to the actual command.
Example use-case: mocking `git pull` but still allowing `git clone` to work as intended Example use-case: mocking `git pull` but still allowing `git clone` to work as intended
''' """
orig_script_path = container.check_output('command -v {}'.format(script)) orig_script_path = container.check_output("command -v {}".format(script))
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = "/usr/local/bin/{}".format(script)
mock_script = dedent(r'''\ mock_script = dedent(
r"""\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script)) case "\$1" in""".format(
script=script
)
)
for k, v in args.items(): for k, v in args.items():
case = dedent(''' case = dedent(
"""
{arg}) {arg})
echo {res} echo {res}
exit {retcode} exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1])) ;;""".format(
arg=k, res=v[0], retcode=v[1]
)
)
mock_script += case mock_script += case
mock_script += dedent(r''' mock_script += dedent(
r"""
*) *)
{orig_script_path} "\$@" {orig_script_path} "\$@"
;;'''.format(orig_script_path=orig_script_path)) ;;""".format(
mock_script += dedent(''' orig_script_path=orig_script_path
esac''') )
container.run(''' )
mock_script += dedent(
"""
esac"""
)
container.run(
"""
cat <<EOF> {script}\n{content}\nEOF cat <<EOF> {script}\n{content}\nEOF
chmod +x {script} chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, rm -f /var/log/{scriptlog}""".format(
content=mock_script, script=full_script_path, content=mock_script, scriptlog=script
scriptlog=script)) )
)
def mock_command_run(script, args, container): def mock_command_run(script, args, container):
''' """
Allows for setup of commands we don't really want to have to run for real Allows for setup of commands we don't really want to have to run for real
in unit tests in unit tests
''' """
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = "/usr/local/bin/{}".format(script)
mock_script = dedent(r'''\ mock_script = dedent(
r"""\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script)) case "\$1 \$2" in""".format(
script=script
)
)
for k, v in args.items(): for k, v in args.items():
case = dedent(''' case = dedent(
"""
\"{arg}\") \"{arg}\")
echo {res} echo {res}
exit {retcode} exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1])) ;;""".format(
arg=k, res=v[0], retcode=v[1]
)
)
mock_script += case mock_script += case
mock_script += dedent(''' mock_script += dedent(
esac''') """
container.run(''' esac"""
)
container.run(
"""
cat <<EOF> {script}\n{content}\nEOF cat <<EOF> {script}\n{content}\nEOF
chmod +x {script} chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, rm -f /var/log/{scriptlog}""".format(
content=mock_script, script=full_script_path, content=mock_script, scriptlog=script
scriptlog=script)) )
)
def mock_command_2(script, args, container): def mock_command_2(script, args, container):
''' """
Allows for setup of commands we don't really want to have to run for real Allows for setup of commands we don't really want to have to run for real
in unit tests in unit tests
''' """
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = "/usr/local/bin/{}".format(script)
mock_script = dedent(r'''\ mock_script = dedent(
r"""\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script)) case "\$1 \$2" in""".format(
script=script
)
)
for k, v in args.items(): for k, v in args.items():
case = dedent(''' case = dedent(
"""
\"{arg}\") \"{arg}\")
echo \"{res}\" echo \"{res}\"
exit {retcode} exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1])) ;;""".format(
arg=k, res=v[0], retcode=v[1]
)
)
mock_script += case mock_script += case
mock_script += dedent(''' mock_script += dedent(
esac''') """
container.run(''' esac"""
)
container.run(
"""
cat <<EOF> {script}\n{content}\nEOF cat <<EOF> {script}\n{content}\nEOF
chmod +x {script} chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, rm -f /var/log/{scriptlog}""".format(
content=mock_script, script=full_script_path, content=mock_script, scriptlog=script
scriptlog=script)) )
)
def run_script(Pihole, script): def run_script(Pihole, script):

@ -1,6 +1,5 @@
docker-compose docker-compose
pytest pytest
pytest-xdist pytest-xdist
pytest-cov
pytest-testinfra pytest-testinfra
tox tox

@ -2,6 +2,6 @@ from setuptools import setup
setup( setup(
py_modules=[], py_modules=[],
setup_requires=['pytest-runner'], setup_requires=["pytest-runner"],
tests_require=['pytest'], tests_require=["pytest"],
) )

File diff suppressed because it is too large Load Diff

@ -1,22 +1,27 @@
def test_key_val_replacement_works(host): def test_key_val_replacement_works(host):
''' Confirms addOrEditKeyValPair either adds or replaces a key value pair in a given file ''' """Confirms addOrEditKeyValPair either adds or replaces a key value pair in a given file"""
host.run(''' host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1" addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1"
addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2" addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2"
addOrEditKeyValPair "./testoutput" "KEY_ONE" "value3" addOrEditKeyValPair "./testoutput" "KEY_ONE" "value3"
addOrEditKeyValPair "./testoutput" "KEY_FOUR" "value4" addOrEditKeyValPair "./testoutput" "KEY_FOUR" "value4"
''') """
output = host.run(''' )
output = host.run(
"""
cat ./testoutput cat ./testoutput
''') """
expected_stdout = 'KEY_ONE=value3\nKEY_TWO=value2\nKEY_FOUR=value4\n' )
expected_stdout = "KEY_ONE=value3\nKEY_TWO=value2\nKEY_FOUR=value4\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_key_addition_works(host): def test_key_addition_works(host):
''' Confirms addKey adds a key (no value) to a file without duplicating it ''' """Confirms addKey adds a key (no value) to a file without duplicating it"""
host.run(''' host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
addKey "./testoutput" "KEY_ONE" addKey "./testoutput" "KEY_ONE"
addKey "./testoutput" "KEY_ONE" addKey "./testoutput" "KEY_ONE"
@ -24,17 +29,21 @@ def test_key_addition_works(host):
addKey "./testoutput" "KEY_TWO" addKey "./testoutput" "KEY_TWO"
addKey "./testoutput" "KEY_THREE" addKey "./testoutput" "KEY_THREE"
addKey "./testoutput" "KEY_THREE" addKey "./testoutput" "KEY_THREE"
''') """
output = host.run(''' )
output = host.run(
"""
cat ./testoutput cat ./testoutput
''') """
expected_stdout = 'KEY_ONE\nKEY_TWO\nKEY_THREE\n' )
expected_stdout = "KEY_ONE\nKEY_TWO\nKEY_THREE\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_key_removal_works(host): def test_key_removal_works(host):
''' Confirms removeKey removes a key or key/value pair ''' """Confirms removeKey removes a key or key/value pair"""
host.run(''' host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1" addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1"
addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2" addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2"
@ -42,81 +51,102 @@ def test_key_removal_works(host):
addKey "./testoutput" "KEY_FOUR" addKey "./testoutput" "KEY_FOUR"
removeKey "./testoutput" "KEY_TWO" removeKey "./testoutput" "KEY_TWO"
removeKey "./testoutput" "KEY_FOUR" removeKey "./testoutput" "KEY_FOUR"
''') """
output = host.run(''' )
output = host.run(
"""
cat ./testoutput cat ./testoutput
''') """
expected_stdout = 'KEY_ONE=value1\nKEY_THREE=value3\n' )
expected_stdout = "KEY_ONE=value1\nKEY_THREE=value3\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLAPIPortFile_default(host): def test_getFTLAPIPort_default(host):
''' Confirms getFTLAPIPortFile returns the default API port file path ''' """Confirms getFTLAPIPort returns the default API port"""
output = host.run(''' output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
getFTLAPIPortFile getFTLAPIPort
''') """
expected_stdout = '/run/pihole-FTL.port\n' )
expected_stdout = "4711\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLAPIPort_default(host): def test_getFTLAPIPort_custom(host):
''' Confirms getFTLAPIPort returns the default API port ''' """Confirms getFTLAPIPort returns a custom API port"""
output = host.run(''' host.run(
"""
echo "FTLPORT=1234" > /etc/pihole/pihole-FTL.conf
"""
)
output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
getFTLAPIPort "/run/pihole-FTL.port" getFTLAPIPort
''') """
expected_stdout = '4711\n' )
expected_stdout = "1234\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLAPIPortFile_and_getFTLAPIPort_custom(host): def test_getFTLAPIPort_malicious(host):
''' Confirms getFTLAPIPort returns a custom API port in a custom PORTFILE location ''' """Confirms getFTLAPIPort returns 4711 if the setting in pihole-FTL.conf contains non-digits"""
host.run(''' host.run(
tmpfile=$(mktemp) """
echo "PORTFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf echo "FTLPORT=*$ssdfsd" > /etc/pihole/pihole-FTL.conf
echo "1234" > ${tmpfile} """
''') )
output = host.run(''' output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
FTL_API_PORT_FILE=$(getFTLAPIPortFile) getFTLAPIPort
getFTLAPIPort "${FTL_API_PORT_FILE}" """
''') )
expected_stdout = '1234\n' expected_stdout = "4711\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLPIDFile_default(host): def test_getFTLPIDFile_default(host):
''' Confirms getFTLPIDFile returns the default PID file path ''' """Confirms getFTLPIDFile returns the default PID file path"""
output = host.run(''' output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
getFTLPIDFile getFTLPIDFile
''') """
expected_stdout = '/run/pihole-FTL.pid\n' )
expected_stdout = "/run/pihole-FTL.pid\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLPID_default(host): def test_getFTLPID_default(host):
''' Confirms getFTLPID returns the default value if FTL is not running ''' """Confirms getFTLPID returns the default value if FTL is not running"""
output = host.run(''' output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
getFTLPID getFTLPID
''') """
expected_stdout = '-1\n' )
expected_stdout = "-1\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout
def test_getFTLPIDFile_and_getFTLPID_custom(host): def test_getFTLPIDFile_and_getFTLPID_custom(host):
''' Confirms getFTLPIDFile returns a custom PID file path ''' """Confirms getFTLPIDFile returns a custom PID file path"""
host.run(''' host.run(
"""
tmpfile=$(mktemp) tmpfile=$(mktemp)
echo "PIDFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf echo "PIDFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf
echo "1234" > ${tmpfile} echo "1234" > ${tmpfile}
''') """
output = host.run(''' )
output = host.run(
"""
source /opt/pihole/utils.sh source /opt/pihole/utils.sh
FTL_PID_FILE=$(getFTLPIDFile) FTL_PID_FILE=$(getFTLPIDFile)
getFTLPID "${FTL_PID_FILE}" getFTLPID "${FTL_PID_FILE}"
''') """
expected_stdout = '1234\n' )
expected_stdout = "1234\n"
assert expected_stdout == output.stdout assert expected_stdout == output.stdout

@ -8,17 +8,20 @@ from .conftest import (
def test_enable_epel_repository_centos(host): def test_enable_epel_repository_centos(host):
''' """
confirms the EPEL package repository is enabled when installed on CentOS confirms the EPEL package repository is enabled when installed on CentOS
''' """
package_manager_detect = host.run(''' package_manager_detect = host.run(
"""
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
''') """
expected_stdout = info_box + (' Enabling EPEL package repository ' )
'(https://fedoraproject.org/wiki/EPEL)') expected_stdout = info_box + (
" Enabling EPEL package repository " "(https://fedoraproject.org/wiki/EPEL)"
)
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + ' Installed' expected_stdout = tick_box + " Installed"
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
epel_package = host.package('epel-release') epel_package = host.package("epel-release")
assert epel_package.is_installed assert epel_package.is_installed

@ -6,60 +6,70 @@ from .conftest import (
def mock_selinux_config(state, host): def mock_selinux_config(state, host):
''' """
Creates a mock SELinux config file with expected content Creates a mock SELinux config file with expected content
''' """
# validate state string # validate state string
valid_states = ['enforcing', 'permissive', 'disabled'] valid_states = ["enforcing", "permissive", "disabled"]
assert state in valid_states assert state in valid_states
# getenforce returns the running state of SELinux # getenforce returns the running state of SELinux
mock_command('getenforce', {'*': (state.capitalize(), '0')}, host) mock_command("getenforce", {"*": (state.capitalize(), "0")}, host)
# create mock configuration with desired content # create mock configuration with desired content
host.run(''' host.run(
"""
mkdir /etc/selinux mkdir /etc/selinux
echo "SELINUX={state}" > /etc/selinux/config echo "SELINUX={state}" > /etc/selinux/config
'''.format(state=state.lower())) """.format(
state=state.lower()
)
)
def test_selinux_enforcing_exit(host): def test_selinux_enforcing_exit(host):
''' """
confirms installer prompts to exit when SELinux is Enforcing by default confirms installer prompts to exit when SELinux is Enforcing by default
''' """
mock_selinux_config("enforcing", host) mock_selinux_config("enforcing", host)
check_selinux = host.run(''' check_selinux = host.run(
"""
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') """
expected_stdout = cross_box + ' Current SELinux: enforcing' )
expected_stdout = cross_box + " Current SELinux: enforcing"
assert expected_stdout in check_selinux.stdout assert expected_stdout in check_selinux.stdout
expected_stdout = 'SELinux Enforcing detected, exiting installer' expected_stdout = "SELinux Enforcing detected, exiting installer"
assert expected_stdout in check_selinux.stdout assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 1 assert check_selinux.rc == 1
def test_selinux_permissive(host): def test_selinux_permissive(host):
''' """
confirms installer continues when SELinux is Permissive confirms installer continues when SELinux is Permissive
''' """
mock_selinux_config("permissive", host) mock_selinux_config("permissive", host)
check_selinux = host.run(''' check_selinux = host.run(
"""
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') """
expected_stdout = tick_box + ' Current SELinux: permissive' )
expected_stdout = tick_box + " Current SELinux: permissive"
assert expected_stdout in check_selinux.stdout assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0 assert check_selinux.rc == 0
def test_selinux_disabled(host): def test_selinux_disabled(host):
''' """
confirms installer continues when SELinux is Disabled confirms installer continues when SELinux is Disabled
''' """
mock_selinux_config("disabled", host) mock_selinux_config("disabled", host)
check_selinux = host.run(''' check_selinux = host.run(
"""
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') """
expected_stdout = tick_box + ' Current SELinux: disabled' )
expected_stdout = tick_box + " Current SELinux: disabled"
assert expected_stdout in check_selinux.stdout assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0 assert check_selinux.rc == 0

@ -1,13 +1,15 @@
def test_epel_and_remi_not_installed_fedora(host): def test_epel_and_remi_not_installed_fedora(host):
''' """
confirms installer does not attempt to install EPEL/REMI repositories confirms installer does not attempt to install EPEL/REMI repositories
on Fedora on Fedora
''' """
package_manager_detect = host.run(''' package_manager_detect = host.run(
"""
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
''') """
assert package_manager_detect.stdout == '' )
assert package_manager_detect.stdout == ""
epel_package = host.package('epel-release') epel_package = host.package("epel-release")
assert not epel_package.is_installed assert not epel_package.is_installed

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _centos_8.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _centos_8.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_centos_common_support.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_centos_common_support.py

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _debian_10.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _debian_10.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _fedora_34.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _fedora_35.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_fedora_support.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

@ -0,0 +1,8 @@
[tox]
envlist = py3
[testenv]
allowlist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _fedora_36.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _ubuntu_20.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _ubuntu_20.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

@ -2,7 +2,7 @@
envlist = py3 envlist = py3
[testenv] [testenv]
whitelist_externals = docker allowlist_externals = docker
deps = -rrequirements.txt deps = -rrequirements.txt
commands = docker build -f _ubuntu_22.Dockerfile -t pytest_pihole:test_container ../ commands = docker build -f _ubuntu_22.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

Loading…
Cancel
Save