Compare commits

..

No commits in common. "master" and "v6.0" have entirely different histories.
master ... v6.0

13 changed files with 260 additions and 202 deletions

View File

@ -77,7 +77,7 @@ jobs:
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Set up Python 3.10 - name: Set up Python 3.10
uses: actions/setup-python@v5.5.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.10" python-version: "3.10"

View File

@ -3,9 +3,13 @@
# #
<p align="center"> <p align="center">
<img src="https://raw.githubusercontent.com/pi-hole/graphics/refs/heads/master/Vortex/vortex_with_text.svg" alt="Pi-hole website" width="168" height="270"> <picture>
<br> <source media="(prefers-color-scheme: dark)" srcset="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_darkmode.png">
<strong>Network-wide ad blocking via your own Linux hardware</strong> <source media="(prefers-color-scheme: light)" srcset="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_lightmode.png">
<img src="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_lightmode.png" width="168" height="270" alt="Pi-hole website">
</picture>
<br>
<strong>Network-wide ad blocking via your own Linux hardware</strong>
</p> </p>
<!-- markdownlint-enable MD033 --> <!-- markdownlint-enable MD033 -->
@ -136,7 +140,7 @@ The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the
Some notable features include: Some notable features include:
- [Allowlisting, Denylisting (fka Whitelisting, Blacklisting), and Regex](https://docs.pi-hole.net/core/pihole-command/#allowlisting-denylisting-and-regex) - [Whitelisting, Blacklisting, and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex)
- [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger) - [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger)
- [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail) - [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail)
- [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity) - [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity)

View File

@ -1,6 +1,5 @@
#!/usr/bin/env sh
# Determine if terminal is capable of showing colors # Determine if terminal is capable of showing colors
if [ -t 1 ] && [ "$(tput colors)" -ge 8 ]; then if ([ -t 1 ] && [ $(tput colors) -ge 8 ]) || [ "${WEBCALL}" ]; then
# Bold and underline may not show up on all clients # Bold and underline may not show up on all clients
# If something MUST be emphasized, use both # If something MUST be emphasized, use both
COL_BOLD='' COL_BOLD=''

View File

@ -21,7 +21,7 @@
TestAPIAvailability() { TestAPIAvailability() {
# as we are running locally, we can get the port value from FTL directly # as we are running locally, we can get the port value from FTL directly
local chaos_api_list authResponse authStatus authData local chaos_api_list availabilityResponse
# Query the API URLs from FTL using CHAOS TXT local.api.ftl # Query the API URLs from FTL using CHAOS TXT local.api.ftl
# The result is a space-separated enumeration of full URLs # The result is a space-separated enumeration of full URLs
@ -34,12 +34,6 @@ TestAPIAvailability() {
exit 1 exit 1
fi fi
# If an error occurred, the variable starts with ;;
if [ "${chaos_api_list#;;}" != "${chaos_api_list}" ]; then
echo "Communication error. Is FTL running?"
exit 1
fi
# Iterate over space-separated list of URLs # Iterate over space-separated list of URLs
while [ -n "${chaos_api_list}" ]; do while [ -n "${chaos_api_list}" ]; do
# Get the first URL # Get the first URL
@ -49,29 +43,20 @@ TestAPIAvailability() {
API_URL="${API_URL#\"}" API_URL="${API_URL#\"}"
# Test if the API is available at this URL # Test if the API is available at this URL
authResponse=$(curl --connect-timeout 2 -skS -w "%{http_code}" "${API_URL}auth") availabilityResponse=$(curl -skS -o /dev/null -w "%{http_code}" "${API_URL}auth")
# authStatus are the last 3 characters
# not using ${authResponse#"${authResponse%???}"}" here because it's extremely slow on big responses
authStatus=$(printf "%s" "${authResponse}" | tail -c 3)
# data is everything from response without the last 3 characters
authData=$(printf %s "${authResponse%???}")
# Test if http status code was 200 (OK) or 401 (authentication required) # Test if http status code was 200 (OK) or 401 (authentication required)
if [ ! "${authStatus}" = 200 ] && [ ! "${authStatus}" = 401 ]; then if [ ! "${availabilityResponse}" = 200 ] && [ ! "${availabilityResponse}" = 401 ]; then
# API is not available at this port/protocol combination # API is not available at this port/protocol combination
API_PORT="" API_PORT=""
else else
# API is available at this URL combination # API is available at this URL combination
if [ "${authStatus}" = 200 ]; then if [ "${availabilityResponse}" = 200 ]; then
# API is available without authentication # API is available without authentication
needAuth=false needAuth=false
fi fi
# Check if 2FA is required
needTOTP=$(echo "${authData}"| jq --raw-output .session.totp 2>/dev/null)
break break
fi fi
@ -117,51 +102,22 @@ LoginAPI() {
echo "API Authentication: Trying to use CLI password" echo "API Authentication: Trying to use CLI password"
fi fi
# If we can read the CLI password, we can skip 2FA even when it's required otherwise # Try to authenticate using the CLI password
needTOTP=false Authentication "${1}"
elif [ "${1}" = "verbose" ]; then elif [ "${1}" = "verbose" ]; then
echo "API Authentication: CLI password not available" echo "API Authentication: CLI password not available"
fi fi
if [ -z "${password}" ]; then
# no password read from CLI file
echo "Please enter your password:"
# secretly read the password
secretRead; printf '\n'
fi
if [ "${needTOTP}" = true ]; then
# 2FA required
echo "Please enter the correct second factor."
echo "(Can be any number if you used the app password)"
read -r totp
fi
# Try to authenticate using the supplied password (CLI file or user input) and TOTP # If this did not work, ask the user for the password
Authentication "${1}" while [ "${validSession}" = false ] || [ -z "${validSession}" ] ; do
# Try to login again until the session is valid
while [ ! "${validSession}" = true ] ; do
echo "Authentication failed. Please enter your Pi-hole password" echo "Authentication failed. Please enter your Pi-hole password"
# Print the error message if there is one
if [ ! "${sessionError}" = "null" ] && [ "${1}" = "verbose" ]; then
echo "Error: ${sessionError}"
fi
# Print the session message if there is one
if [ ! "${sessionMessage}" = "null" ] && [ "${1}" = "verbose" ]; then
echo "Error: ${sessionMessage}"
fi
# secretly read the password # secretly read the password
secretRead; printf '\n' secretRead; printf '\n'
if [ "${needTOTP}" = true ]; then
echo "Please enter the correct second factor:"
echo "(Can be any number if you used the app password)"
read -r totp
fi
# Try to authenticate again # Try to authenticate again
Authentication "${1}" Authentication "${1}"
done done
@ -169,27 +125,23 @@ LoginAPI() {
} }
Authentication() { Authentication() {
sessionResponse="$(curl --connect-timeout 2 -skS -X POST "${API_URL}auth" --user-agent "Pi-hole cli" --data "{\"password\":\"${password}\", \"totp\":${totp:-null}}" )" sessionResponse="$(curl -skS -X POST "${API_URL}auth" --user-agent "Pi-hole cli " --data "{\"password\":\"${password}\"}" )"
if [ -z "${sessionResponse}" ]; then if [ -z "${sessionResponse}" ]; then
echo "No response from FTL server. Please check connectivity" echo "No response from FTL server. Please check connectivity"
exit 1 exit 1
fi fi
# obtain validity, session ID and sessionMessage from session response # obtain validity and session ID from session response
validSession=$(echo "${sessionResponse}"| jq .session.valid 2>/dev/null) validSession=$(echo "${sessionResponse}"| jq .session.valid 2>/dev/null)
SID=$(echo "${sessionResponse}"| jq --raw-output .session.sid 2>/dev/null) SID=$(echo "${sessionResponse}"| jq --raw-output .session.sid 2>/dev/null)
sessionMessage=$(echo "${sessionResponse}"| jq --raw-output .session.message 2>/dev/null)
if [ "${1}" = "verbose" ]; then
# obtain the error message from the session response if [ "${validSession}" = true ]; then
sessionError=$(echo "${sessionResponse}"| jq --raw-output .error.message 2>/dev/null) echo "API Authentication: ${COL_GREEN}Success${COL_NC}"
else
if [ "${1}" = "verbose" ]; then echo "API Authentication: ${COL_RED}Failed${COL_NC}"
if [ "${validSession}" = true ]; then
echo "API Authentication: ${COL_GREEN}Success${COL_NC}"
else
echo "API Authentication: ${COL_RED}Failed${COL_NC}"
fi
fi fi
fi
} }
LogoutAPI() { LogoutAPI() {

View File

@ -218,7 +218,7 @@ main() {
fi fi
if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --repair --unattended || \ ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
echo -e "${basicError}" && exit 1 echo -e "${basicError}" && exit 1
fi fi

View File

@ -12,7 +12,7 @@
# shellcheck disable=SC3043 # shellcheck disable=SC3043
# https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions # https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
# Source the versions file populated by updatechecker.sh # Source the versions file poupulated by updatechecker.sh
cachedVersions="/etc/pihole/versions" cachedVersions="/etc/pihole/versions"
if [ -f ${cachedVersions} ]; then if [ -f ${cachedVersions} ]; then

View File

@ -10,21 +10,25 @@ utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
FTL_PID_FILE="$(getFTLConfigValue files.pid)" FTL_PID_FILE="$(getFTLConfigValue files.pid)"
# Ensure that permissions are set so that pihole-FTL can edit all necessary files # Ensure that permissions are set so that pihole-FTL can edit all necessary files
mkdir -p /var/log/pihole # shellcheck disable=SC2174
chown -R pihole:pihole /etc/pihole/ /var/log/pihole/ mkdir -pm 0640 /var/log/pihole
# allow pihole to access subdirs in /etc/pihole (sets execution bit on dirs) chown -R pihole:pihole /etc/pihole /var/log/pihole
find /etc/pihole/ /var/log/pihole/ -type d -exec chmod 0755 {} + chmod -R 0640 /var/log/pihole
# Set all files (except TLS-related ones) to u+rw g+r chmod -R 0660 /etc/pihole
find /etc/pihole/ /var/log/pihole/ -type f ! \( -name '*.pem' -o -name '*.crt' \) -exec chmod 0640 {} +
# Set TLS-related files to a more restrictive u+rw *only* (they may contain private keys)
find /etc/pihole/ -type f \( -name '*.pem' -o -name '*.crt' \) -exec chmod 0600 {} +
# Logrotate config file need to be owned by root # Logrotate config file need to be owned by root and must not be writable by group and others
chown root:root /etc/pihole/logrotate chown root:root /etc/pihole/logrotate
chmod 0644 /etc/pihole/logrotate
# allow all users to enter the directories
chmod 0755 /etc/pihole /var/log/pihole
# allow pihole to access subdirs in /etc/pihole (sets execution bit on dirs)
# credits https://stackoverflow.com/a/11512211
find /etc/pihole -type d -exec chmod 0755 {} \;
# 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)
[ -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 /var/log/pihole/FTL.log ] || install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/FTL.log [ -f /var/log/pihole/FTL.log ] || install -m 640 -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 /var/log/pihole/webserver.log ] || install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/webserver.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

View File

@ -7,7 +7,7 @@ _pihole() {
case "${prev}" in case "${prev}" in
"pihole") "pihole")
opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query repair regex reloaddns reloadlists status tail uninstall updateGravity updatePihole version wildcard arpflush api" opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query reconfigure regex reloaddns reloadlists status tail uninstall updateGravity updatePihole version wildcard arpflush api"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
;; ;;
"allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild") "allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild")

View File

@ -81,7 +81,9 @@ 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_V6_CONFIG="${PI_HOLE_CONFIG_DIR}/pihole.toml" PI_HOLE_V6_CONFIG="${PI_HOLE_CONFIG_DIR}/pihole.toml"
fresh_install=true if [ -z "$useUpdateVars" ]; then
useUpdateVars=false
fi
adlistFile="/etc/pihole/adlists.list" adlistFile="/etc/pihole/adlists.list"
# Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until this script can run # Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until this script can run
@ -89,6 +91,7 @@ IPV4_ADDRESS=${IPV4_ADDRESS}
IPV6_ADDRESS=${IPV6_ADDRESS} IPV6_ADDRESS=${IPV6_ADDRESS}
# Give settings their default values. These may be changed by prompts later in the script. # Give settings their default values. These may be changed by prompts later in the script.
QUERY_LOGGING= QUERY_LOGGING=
WEBPORT=
PRIVACY_LEVEL= PRIVACY_LEVEL=
# Where old configs go to if a v6 migration is performed # Where old configs go to if a v6 migration is performed
@ -139,12 +142,12 @@ EOM
######## Undocumented Flags. Shhh ######## ######## Undocumented Flags. Shhh ########
# These are undocumented flags; some of which we can use when repairing an installation # These are undocumented flags; some of which we can use when repairing an installation
# The runUnattended flag is one example of this # The runUnattended flag is one example of this
repair=false reconfigure=false
runUnattended=false runUnattended=false
# Check arguments for the undocumented flags # Check arguments for the undocumented flags
for var in "$@"; do for var in "$@"; do
case "$var" in case "$var" in
"--repair") repair=true ;; "--reconfigure") reconfigure=true ;;
"--unattended") runUnattended=true ;; "--unattended") runUnattended=true ;;
esac esac
done done
@ -1108,7 +1111,7 @@ setPrivacyLevel() {
# A function to display a list of example blocklists for users to select # A function to display a list of example blocklists for users to select
chooseBlocklists() { chooseBlocklists() {
# Back up any existing adlist file, on the off chance that it exists. # Back up any existing adlist file, on the off chance that it exists. Useful in case of a reconfigure.
if [[ -f "${adlistFile}" ]]; then if [[ -f "${adlistFile}" ]]; then
mv "${adlistFile}" "${adlistFile}.old" mv "${adlistFile}" "${adlistFile}.old"
fi fi
@ -1157,23 +1160,27 @@ installDefaultBlocklists() {
echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >>"${adlistFile}" echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >>"${adlistFile}"
} }
move_old_dnsmasq_ftl_configs() { remove_old_dnsmasq_ftl_configs() {
# Create migration directory /etc/pihole/migration_backup_v6 # Local, named variables
# and make it owned by pihole:pihole
mkdir -p "${V6_CONF_MIGRATION_DIR}"
chown pihole:pihole "${V6_CONF_MIGRATION_DIR}"
# Move all conf files originally created by Pi-hole into this directory
# - 01-pihole.conf
# - 02-pihole-dhcp.conf
# - 04-pihole-static-dhcp.conf
# - 05-pihole-custom-cname.conf
# - 06-rfc6761.conf
mv /etc/dnsmasq.d/0{1,2,4,5}-pihole*.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
mv /etc/dnsmasq.d/06-rfc6761.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
# If the dnsmasq main config file exists
local dnsmasq_conf="/etc/dnsmasq.conf" local dnsmasq_conf="/etc/dnsmasq.conf"
local pihole_01="/etc/dnsmasq.d/01-pihole.conf"
local rfc6761_06="/etc/dnsmasq.d/06-rfc6761.conf"
local pihole_dhcp_02="/etc/dnsmasq.d/02-pihole-dhcp.conf"
# pihole-FTL does some fancy stuff with config these days, and so we can remove some old config files
if [[ -f "${pihole_01}" ]]; then
rm "${pihole_01}"
fi
if [[ -f "${rfc6761_06}" ]]; then
rm "${rfc6761_06}"
fi
if [[ -f "${pihole_dhcp_02}" ]]; then
rm "${pihole_dhcp_02}"
fi
# If the dnsmasq config file exists
if [[ -f "${dnsmasq_conf}" ]]; then if [[ -f "${dnsmasq_conf}" ]]; then
# There should not be anything custom in here for Pi-hole users # There should not be anything custom in here for Pi-hole users
# It is no longer needed, but we'll back it up instead of deleting it just in case # It is no longer needed, but we'll back it up instead of deleting it just in case
@ -1204,13 +1211,13 @@ remove_old_pihole_lighttpd_configs() {
lighty-disable-mod pihole-admin >/dev/null || true lighty-disable-mod pihole-admin >/dev/null || true
fi fi
if [[ -f "${confenabled}" || -L "${confenabled}" ]]; then
rm "${confenabled}"
fi
if [[ -f "${confavailable}" ]]; then if [[ -f "${confavailable}" ]]; then
rm "${confavailable}" rm "${confavailable}"
fi fi
if [[ -f "${confenabled}" ]]; then
rm "${confenabled}"
fi
} }
# Clean an existing installation to prepare for upgrade/reinstall # Clean an existing installation to prepare for upgrade/reinstall
@ -1351,9 +1358,9 @@ stop_service() {
local str="Stopping ${1} service" local str="Stopping ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
if is_command systemctl; then if is_command systemctl; then
systemctl -q stop "${1}" || true systemctl stop "${1}" &>/dev/null || true
else else
service "${1}" stop >/dev/null || true service "${1}" stop &>/dev/null || true
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1366,10 +1373,10 @@ restart_service() {
# If systemctl exists, # If systemctl exists,
if is_command systemctl; then if is_command systemctl; then
# use that to restart the service # use that to restart the service
systemctl -q restart "${1}" systemctl restart "${1}" &>/dev/null
else else
# Otherwise, fall back to the service command # Otherwise, fall back to the service command
service "${1}" restart >/dev/null service "${1}" restart &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1382,10 +1389,10 @@ enable_service() {
# If systemctl exists, # If systemctl exists,
if is_command systemctl; then if is_command systemctl; then
# use that to enable the service # use that to enable the service
systemctl -q enable "${1}" systemctl enable "${1}" &>/dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" defaults >/dev/null update-rc.d "${1}" defaults &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1398,10 +1405,10 @@ disable_service() {
# If systemctl exists, # If systemctl exists,
if is_command systemctl; then if is_command systemctl; then
# use that to disable the service # use that to disable the service
systemctl -q disable "${1}" systemctl disable "${1}" &>/dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" disable >/dev/null update-rc.d "${1}" disable &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1410,7 +1417,7 @@ check_service_active() {
# If systemctl exists, # If systemctl exists,
if is_command systemctl; then if is_command systemctl; then
# use that to check the status of the service # use that to check the status of the service
systemctl -q is-enabled "${1}" 2>/dev/null systemctl is-enabled "${1}" &>/dev/null
else else
# Otherwise, fall back to service command # Otherwise, fall back to service command
service "${1}" status &>/dev/null service "${1}" status &>/dev/null
@ -1469,11 +1476,16 @@ notify_package_updates_available() {
# Store the list of packages in a variable # Store the list of packages in a variable
updatesToInstall=$(eval "${PKG_COUNT}") updatesToInstall=$(eval "${PKG_COUNT}")
if [[ "${updatesToInstall}" -eq 0 ]]; then if [[ -d "/lib/modules/$(uname -r)" ]]; then
printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}" if [[ "${updatesToInstall}" -eq 0 ]]; then
printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}"
else
printf "%b %b %s... %s updates available\\n" "${OVER}" "${TICK}" "${str}" "${updatesToInstall}"
printf " %b %bIt is recommended to update your OS after installing the Pi-hole!%b\\n\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}"
fi
else else
printf "%b %b %s... %s updates available\\n" "${OVER}" "${TICK}" "${str}" "${updatesToInstall}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b %bIt is recommended to update your OS after installing the Pi-hole!%b\\n\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}" printf " Kernel update detected. If the install fails, please reboot and try again\\n"
fi fi
} }
@ -1682,8 +1694,7 @@ installPihole() {
exit 1 exit 1
fi fi
# Move old dnsmasq files to $V6_CONF_MIGRATION_DIR for later migration via migrate_dnsmasq_configs() remove_old_dnsmasq_ftl_configs
move_old_dnsmasq_ftl_configs
remove_old_pihole_lighttpd_configs remove_old_pihole_lighttpd_configs
# Install config files # Install config files
@ -1748,6 +1759,83 @@ checkSelinux() {
fi fi
} }
# Installation complete message with instructions for the user
displayFinalMessage() {
# TODO: COME BACK TO THIS, WHAT IS GOING ON?
# If the number of arguments is > 0,
if [[ "${#1}" -gt 0 ]]; then
# set the password to the first argument.
pwstring="$1"
elif [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]]; then
# Else if the password exists from previous setup, we'll load it later
pwstring="unchanged"
else
# Else, inform the user that there is no set password.
pwstring="NOT SET"
fi
# Store a message in a variable and display it
additional="View the web interface at http://pi.hole/admin:${WEBPORT} or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pwstring}"
# Final completion message to user
dialog --no-shadow --keep-tite \
--title "Installation Complete!" \
--msgbox "Configure your devices to use the Pi-hole as their DNS server using:\
\\n\\nIPv4: ${IPV4_ADDRESS%/*}\
\\nIPv6: ${IPV6_ADDRESS:-"Not Configured"}\
\\nIf you have not done so already, the above IP should be set to static.\
\\n${additional}" "${r}" "${c}"
}
update_dialogs() {
# If pihole -r "reconfigure" option was selected,
if [[ "${reconfigure}" = true ]]; then
# set some variables that will be used
opt1a="Repair"
opt1b="This will retain existing settings"
strAdd="You will remain on the same version"
else
# Otherwise, set some variables with different values
opt1a="Update"
opt1b="This will retain existing settings."
strAdd="You will be updated to the latest version."
fi
opt2a="Reconfigure"
opt2b="Resets Pi-hole and allows re-selecting settings."
# Display the information to the user
UpdateCmd=$(dialog --no-shadow --keep-tite --output-fd 1 \
--cancel-label Exit \
--title "Existing Install Detected!" \
--menu "\\n\\nWe have detected an existing install.\
\\n\\nPlease choose from the following options:\
\\n($strAdd)" \
"${r}" "${c}" 2 \
"${opt1a}" "${opt1b}" \
"${opt2a}" "${opt2b}") || result=$?
case ${result} in
"${DIALOG_CANCEL}" | "${DIALOG_ESC}")
printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
exit 1
;;
esac
# Set the variable based on if the user chooses
case ${UpdateCmd} in
# repair, or
"${opt1a}")
printf " %b %s option selected\\n" "${INFO}" "${opt1a}"
useUpdateVars=true
;;
# reconfigure,
"${opt2a}")
printf " %b %s option selected\\n" "${INFO}" "${opt2a}"
useUpdateVars=false
;;
esac
}
check_download_exists() { check_download_exists() {
# Check if the download exists and we can reach the server # Check if the download exists and we can reach the server
local status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) local status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
@ -1834,10 +1922,10 @@ checkout_pull_branch() {
return 0 return 0
} }
clone_or_reset_repos() { clone_or_update_repos() {
# If the user wants to repair/update, # If the user wants to reconfigure,
if [[ "${repair}" == true ]]; then if [[ "${reconfigure}" == true ]]; then
printf " %b Resetting local repos\\n" "${INFO}" printf " %b Performing reconfiguration, skipping download of local repos\\n" "${INFO}"
# Reset the Core repo # Reset the Core repo
resetRepo ${PI_HOLE_LOCAL_REPO} || resetRepo ${PI_HOLE_LOCAL_REPO} ||
{ {
@ -1850,7 +1938,7 @@ clone_or_reset_repos() {
printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}" printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}"
exit 1 exit 1
} }
# Otherwise, a fresh installation is happening # Otherwise, a repair is happening
else else
# so get git files for Core # so get git files for Core
getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} ||
@ -1913,7 +2001,7 @@ FTLinstall() {
curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true
# Stop pihole-FTL service if available # Stop pihole-FTL service if available
stop_service pihole-FTL >/dev/null stop_service pihole-FTL &>/dev/null
# Install the new version with the correct permissions # Install the new version with the correct permissions
install -T -m 0755 "${binary}" /usr/bin/pihole-FTL install -T -m 0755 "${binary}" /usr/bin/pihole-FTL
@ -2034,7 +2122,7 @@ get_binary_name() {
else else
printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}"
fi fi
l_binary="pihole-FTL-386" l_binary="pihole-FTL-linux-386"
fi fi
# Returning a string value via echo # Returning a string value via echo
@ -2223,18 +2311,33 @@ migrate_dnsmasq_configs() {
# avoid conflicts with other services on this system # avoid conflicts with other services on this system
# Exit early if this is already Pi-hole v6.0 # Exit early if this is already Pi-hole v6.0
# We decide this on the non-existence of the file /etc/pihole/setupVars.conf (either moved by previous migration or fresh install) # We decide this on the presence of the file /etc/pihole/pihole.toml
if [[ ! -f "/etc/pihole/setupVars.conf" ]]; then if [[ -f "${PI_HOLE_V6_CONFIG}" ]]; then
return 0 return 0
fi fi
# Disable lighttpd server during v6 migration # Disable lighttpd server during v6 migration
disableLighttpd disableLighttpd
# move_old_dnsmasq_ftl_configs() moved everything is in place, # Create target directory /etc/pihole/migration_backup_v6
# so we can create the new config file /etc/pihole/pihole.toml # and make it owned by pihole:pihole
mkdir -p "${V6_CONF_MIGRATION_DIR}"
chown pihole:pihole "${V6_CONF_MIGRATION_DIR}"
# Move all conf files originally created by Pi-hole into this directory
# - 01-pihole.conf
# - 02-pihole-dhcp.conf
# - 04-pihole-static-dhcp.conf
# - 05-pihole-custom-cname.conf
# - 06-rfc6761.conf
mv /etc/dnsmasq.d/0{1,2,4,5}-pihole*.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
mv /etc/dnsmasq.d/06-rfc6761.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
# Finally, after everything is in place, we can create the new config file
# /etc/pihole/pihole.toml
# This file will be created with the default settings unless the user has # This file will be created with the default settings unless the user has
# changed settings via setupVars.conf or the other dnsmasq files moved before # changed settings via setupVars.conf or the other dnsmasq files moved above
# During migration, setupVars.conf is moved to /etc/pihole/migration_backup_v6 # During migration, setupVars.conf is moved to /etc/pihole/migration_backup_v6
str="Migrating Pi-hole configuration to version 6" str="Migrating Pi-hole configuration to version 6"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
@ -2342,19 +2445,22 @@ main() {
exit 1 exit 1
fi fi
# in case of an update (can be a v5 -> v6 or v6 -> v6 update) or repair # in case of an update (can be a v5 -> v6 or v6 -> v6 update)
if [[ -f "${PI_HOLE_V6_CONFIG}" ]] || [[ -f "/etc/pihole/setupVars.conf" ]]; then if [[ -f "${PI_HOLE_V6_CONFIG}" ]] || [[ -f "/etc/pihole/setupVars.conf" ]]; then
# retain settings
fresh_install=false
# if it's running unattended, # if it's running unattended,
if [[ "${runUnattended}" == true ]]; then if [[ "${runUnattended}" == true ]]; then
printf " %b Performing unattended setup, no dialogs will be displayed\\n" "${INFO}" printf " %b Performing unattended setup, no dialogs will be displayed\\n" "${INFO}"
# Use the setup variables
useUpdateVars=true
# also disable debconf-apt-progress dialogs # also disable debconf-apt-progress dialogs
export DEBIAN_FRONTEND="noninteractive" export DEBIAN_FRONTEND="noninteractive"
else
# If running attended, show the available options (repair/reconfigure)
update_dialogs
fi fi
fi fi
if [[ "${fresh_install}" == true ]]; then if [[ "${useUpdateVars}" == false ]]; then
# Display welcome dialogs # Display welcome dialogs
welcomeDialogs welcomeDialogs
# Create directory for Pi-hole storage (/etc/pihole/) # Create directory for Pi-hole storage (/etc/pihole/)
@ -2377,8 +2483,9 @@ main() {
# Setup adlist file if not exists # Setup adlist file if not exists
installDefaultBlocklists installDefaultBlocklists
fi fi
# Download or reset the appropriate git repos depending on the 'repair' flag # Download or update the scripts by updating the appropriate git repos
clone_or_reset_repos clone_or_update_repos
# Create the pihole user # Create the pihole user
create_pihole_user create_pihole_user
@ -2408,6 +2515,15 @@ main() {
# Copy the temp log file into final log location for storage # Copy the temp log file into final log location for storage
copy_to_install_log copy_to_install_log
# Add password to web UI if there is none
pw=""
# If no password is set,
if [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]]; then
# generate a random password
pw=$(tr -dc _A-Z-a-z-0-9 </dev/urandom | head -c 8)
pihole setpassword "${pw}"
fi
# Migrate existing install to v6.0 # Migrate existing install to v6.0
migrate_dnsmasq_configs migrate_dnsmasq_configs
@ -2433,25 +2549,14 @@ main() {
restart_service pihole-FTL restart_service pihole-FTL
if [[ "${fresh_install}" == true ]]; then # write privacy level and logging to pihole.toml
# apply settings to pihole.toml # needs to be done after FTL service has been started, otherwise pihole.toml does not exist
# needs to be done after FTL service has been started, otherwise pihole.toml does not exist # set on fresh installations by setPrivacyLevel() and setLogging(
# set on fresh installations by setDNS() and setPrivacyLevel() and setLogging() if [ -n "${QUERY_LOGGING}" ]; then
setFTLConfigValue "dns.queryLogging" "${QUERY_LOGGING}"
# Upstreams may be needed in order to run gravity.sh fi
if [ -n "${PIHOLE_DNS_1}" ]; then if [ -n "${PRIVACY_LEVEL}" ]; then
local string="\"${PIHOLE_DNS_1}\"" setFTLConfigValue "misc.privacylevel" "${PRIVACY_LEVEL}"
[ -n "${PIHOLE_DNS_2}" ] && string+=", \"${PIHOLE_DNS_2}\""
setFTLConfigValue "dns.upstreams" "[ $string ]"
fi
if [ -n "${QUERY_LOGGING}" ]; then
setFTLConfigValue "dns.queryLogging" "${QUERY_LOGGING}"
fi
if [ -n "${PRIVACY_LEVEL}" ]; then
setFTLConfigValue "misc.privacylevel" "${PRIVACY_LEVEL}"
fi
fi fi
# Download and compile the aggregated block list # Download and compile the aggregated block list
@ -2460,35 +2565,28 @@ main() {
# Update local and remote versions via updatechecker # Update local and remote versions via updatechecker
/opt/pihole/updatecheck.sh /opt/pihole/updatecheck.sh
if [[ "${fresh_install}" == true ]]; then # If there is a password
if ((${#pw} > 0)); then
# display the password
printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}"
printf " %b This can be changed using 'pihole setpassword'\\n\\n" "${INFO}"
fi
if [[ "${useUpdateVars}" == false ]]; then
# Get the Web interface port, return only the first port and strip all non-numeric characters # Get the Web interface port, return only the first port and strip all non-numeric characters
WEBPORT=$(getFTLConfigValue webserver.port|cut -d, -f1 | tr -cd '0-9') WEBPORT=$(getFTLConfigValue webserver.port|cut -d, -f1 | tr -cd '0-9')
# If this is a fresh install, we will set a random password. # Display the completion dialog
# Users can change this password after installation if they wish displayFinalMessage "${pw}"
pw=$(tr -dc _A-Z-a-z-0-9 </dev/urandom | head -c 8)
pihole setpassword "${pw}" > /dev/null # If the Web interface was installed,
printf " %b View the web interface at http://pi.hole:${WEBPORT}/admin or http://%s/admin\\n\\n" "${INFO}" "${IPV4_ADDRESS%/*}:${WEBPORT}"
# Explain to the user how to use Pi-hole as their DNS server # Explain to the user how to use Pi-hole as their DNS server
printf "\\n %b You may now configure your devices to use the Pi-hole as their DNS server\\n" "${INFO}" printf " %b You may now configure your devices to use the Pi-hole as their DNS server\\n" "${INFO}"
[[ -n "${IPV4_ADDRESS%/*}" ]] && printf " %b Pi-hole DNS (IPv4): %s\\n" "${INFO}" "${IPV4_ADDRESS%/*}" [[ -n "${IPV4_ADDRESS%/*}" ]] && printf " %b Pi-hole DNS (IPv4): %s\\n" "${INFO}" "${IPV4_ADDRESS%/*}"
[[ -n "${IPV6_ADDRESS}" ]] && printf " %b Pi-hole DNS (IPv6): %s\\n" "${INFO}" "${IPV6_ADDRESS}" [[ -n "${IPV6_ADDRESS}" ]] && printf " %b Pi-hole DNS (IPv6): %s\\n" "${INFO}" "${IPV6_ADDRESS}"
printf " %b If you have not done so already, the above IP should be set to static.\\n" "${INFO}" printf " %b If you have not done so already, the above IP should be set to static.\\n" "${INFO}"
printf " %b View the web interface at http://pi.hole:${WEBPORT}/admin or http://%s/admin\\n\\n" "${INFO}" "${IPV4_ADDRESS%/*}:${WEBPORT}"
printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}"
printf " %b This can be changed using 'pihole setpassword'\\n\\n" "${INFO}"
# Final dialog message to the user
dialog --no-shadow --keep-tite \
--title "Installation Complete!" \
--msgbox "Configure your devices to use the Pi-hole as their DNS server using:\
\\n\\nIPv4: ${IPV4_ADDRESS%/*}\
\\nIPv6: ${IPV6_ADDRESS:-"Not Configured"}\
\\nIf you have not done so already, the above IP should be set to static.\
\\nView the web interface at http://pi.hole/admin:${WEBPORT} or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pw}" "${r}" "${c}"
INSTALL_TYPE="Installation" INSTALL_TYPE="Installation"
else else
INSTALL_TYPE="Update" INSTALL_TYPE="Update"

View File

@ -1082,7 +1082,7 @@ migrate_to_listsCache_dir() {
fi fi
# Update the list's paths in the corresponding .sha1 files to the new location # Update the list's paths in the corresponding .sha1 files to the new location
sed -i "s|${piholeDir}/|${listsCacheDir}/|g" "${listsCacheDir}"/*.sha1 2>/dev/null sed -i "s|${piholeDir}/|${listsCacheDir}/|g" "${listsCacheDir}"/*.sha1
} }
helpFunc() { helpFunc() {

15
pihole
View File

@ -107,11 +107,11 @@ updatePiholeFunc() {
fi fi
} }
repairPiholeFunc() { reconfigurePiholeFunc() {
if [ -n "${DOCKER_VERSION}" ]; then if [ -n "${DOCKER_VERSION}" ]; then
unsupportedFunc unsupportedFunc
else else
/etc/.pihole/automated\ install/basic-install.sh --repair /etc/.pihole/automated\ install/basic-install.sh --reconfigure
exit 0; exit 0;
fi fi
} }
@ -398,10 +398,7 @@ tailFunc() {
piholeCheckoutFunc() { piholeCheckoutFunc() {
if [ -n "${DOCKER_VERSION}" ]; then if [ -n "${DOCKER_VERSION}" ]; then
echo -e "${CROSS} Function not supported in Docker images" unsupportedFunc
echo "Please build a custom image following the steps at"
echo "https://github.com/pi-hole/docker-pi-hole?tab=readme-ov-file#building-the-image-locally"
exit 0
else else
if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then
echo "Switch Pi-hole subsystems to a different GitHub branch echo "Switch Pi-hole subsystems to a different GitHub branch
@ -479,7 +476,7 @@ Debugging Options:
Add '-c' or '--check-database' to include a Pi-hole database integrity check Add '-c' or '--check-database' to include a Pi-hole database integrity check
Add '-a' to automatically upload the log to tricorder.pi-hole.net Add '-a' to automatically upload the log to tricorder.pi-hole.net
-f, flush Flush the Pi-hole log -f, flush Flush the Pi-hole log
-r, repair Repair Pi-hole subsystems -r, reconfigure Reconfigure or Repair Pi-hole subsystems
-t, tail [arg] View the live output of the Pi-hole log. -t, tail [arg] View the live output of the Pi-hole log.
Add an optional argument to filter the log Add an optional argument to filter the log
(regular expressions are supported) (regular expressions are supported)
@ -536,7 +533,7 @@ case "${1}" in
"--allow-wild" | "allow-wild" ) need_root=0;; "--allow-wild" | "allow-wild" ) need_root=0;;
"-f" | "flush" ) ;; "-f" | "flush" ) ;;
"-up" | "updatePihole" ) ;; "-up" | "updatePihole" ) ;;
"-r" | "repair" ) ;; "-r" | "reconfigure" ) ;;
"-l" | "logging" ) ;; "-l" | "logging" ) ;;
"uninstall" ) ;; "uninstall" ) ;;
"enable" ) need_root=0;; "enable" ) need_root=0;;
@ -579,7 +576,7 @@ case "${1}" in
"-d" | "debug" ) debugFunc "$@";; "-d" | "debug" ) debugFunc "$@";;
"-f" | "flush" ) flushFunc "$@";; "-f" | "flush" ) flushFunc "$@";;
"-up" | "updatePihole" ) updatePiholeFunc "$@";; "-up" | "updatePihole" ) updatePiholeFunc "$@";;
"-r" | "repair" ) repairPiholeFunc;; "-r" | "reconfigure" ) reconfigurePiholeFunc;;
"-g" | "updateGravity" ) updateGravityFunc "$@";; "-g" | "updateGravity" ) updateGravityFunc "$@";;
"-l" | "logging" ) piholeLogging "$@";; "-l" | "logging" ) piholeLogging "$@";;
"uninstall" ) uninstallFunc;; "uninstall" ) uninstallFunc;;

View File

@ -1,6 +1,6 @@
pyyaml == 6.0.2 pyyaml == 6.0.2
pytest == 8.3.5 pytest == 8.3.4
pytest-xdist == 3.6.1 pytest-xdist == 3.6.1
pytest-testinfra == 10.1.1 pytest-testinfra == 10.1.1
tox == 4.25.0 tox == 4.24.1
pytest-clarity == 1.0.1 pytest-clarity == 1.0.1

View File

@ -89,10 +89,10 @@ def test_installPihole_fresh_install_readableFiles(host):
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
umask 0027 umask 0027
runUnattended=true runUnattended=true
fresh_install=false useUpdateVars=true
source /opt/pihole/basic-install.sh > /dev/null source /opt/pihole/basic-install.sh > /dev/null
runUnattended=true runUnattended=true
fresh_install=false useUpdateVars=true
main main
/opt/pihole/pihole-FTL-prestart.sh /opt/pihole/pihole-FTL-prestart.sh
""" """
@ -127,6 +127,10 @@ def test_installPihole_fresh_install_readableFiles(host):
check_localversion = test_cmd.format("r", "/etc/pihole/versions", piholeuser) check_localversion = test_cmd.format("r", "/etc/pihole/versions", piholeuser)
actual_rc = host.run(check_localversion).rc actual_rc = host.run(check_localversion).rc
assert exit_status_success == actual_rc assert exit_status_success == actual_rc
# readable logrotate
check_logrotate = test_cmd.format("r", "/etc/pihole/logrotate", piholeuser)
actual_rc = host.run(check_logrotate).rc
assert exit_status_success == actual_rc
# readable macvendor.db # readable macvendor.db
check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser) check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser)
actual_rc = host.run(check_macvendor).rc actual_rc = host.run(check_macvendor).rc