diff --git a/README.md b/README.md index 7eb1fb3a..fddd3d2d 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the Some notable features include: -- [Whitelisting, Blacklisting, and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex) +- [Allowlisting, Denylisting (fka Whitelisting, Blacklisting), and Regex](https://docs.pi-hole.net/core/pihole-command/#allowlisting-denylisting-and-regex) - [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) - [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity) diff --git a/advanced/Scripts/api.sh b/advanced/Scripts/api.sh index c9c2dd49..b8c7fe99 100755 --- a/advanced/Scripts/api.sh +++ b/advanced/Scripts/api.sh @@ -34,6 +34,12 @@ TestAPIAvailability() { exit 1 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 while [ -n "${chaos_api_list}" ]; do # Get the first URL diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 9ea63b4c..6c6167c0 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -218,7 +218,7 @@ main() { fi if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then - ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \ + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --repair --unattended || \ echo -e "${basicError}" && exit 1 fi diff --git a/advanced/Templates/pihole-FTL-prestart.sh b/advanced/Templates/pihole-FTL-prestart.sh index c604d5d4..49b0f0d4 100755 --- a/advanced/Templates/pihole-FTL-prestart.sh +++ b/advanced/Templates/pihole-FTL-prestart.sh @@ -10,22 +10,17 @@ utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" FTL_PID_FILE="$(getFTLConfigValue files.pid)" # Ensure that permissions are set so that pihole-FTL can edit all necessary files -# shellcheck disable=SC2174 -mkdir -pm 0640 /var/log/pihole +mkdir -p /var/log/pihole chown -R pihole:pihole /etc/pihole /var/log/pihole -chmod -R 0640 /var/log/pihole -chmod -R 0660 /etc/pihole - -# Logrotate config file need to be owned by root and must not be writable by group and others -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 {} \; +find /etc/pihole/ /var/log/pihole/ -type d -exec chmod 0755 {} + +# Set all files (except TLS-related ones) to u+rw g+r +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 +chown root:root /etc/pihole/logrotate # 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}" diff --git a/advanced/bash-completion/pihole b/advanced/bash-completion/pihole index 4343cf92..cf99ab73 100644 --- a/advanced/bash-completion/pihole +++ b/advanced/bash-completion/pihole @@ -7,7 +7,7 @@ _pihole() { case "${prev}" in "pihole") - 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" + 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" COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) ;; "allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild") diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 4a1df70c..e69256ff 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -81,9 +81,7 @@ PI_HOLE_INSTALL_DIR="/opt/pihole" PI_HOLE_CONFIG_DIR="/etc/pihole" PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_V6_CONFIG="${PI_HOLE_CONFIG_DIR}/pihole.toml" -if [ -z "$useUpdateVars" ]; then - useUpdateVars=false -fi +fresh_install=true 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 @@ -91,9 +89,7 @@ IPV4_ADDRESS=${IPV4_ADDRESS} IPV6_ADDRESS=${IPV6_ADDRESS} # Give settings their default values. These may be changed by prompts later in the script. QUERY_LOGGING= -WEBPORT= PRIVACY_LEVEL= -v5_to_v6_update=false # Where old configs go to if a v6 migration is performed V6_CONF_MIGRATION_DIR="/etc/pihole/migration_backup_v6" @@ -143,12 +139,12 @@ EOM ######## Undocumented Flags. Shhh ######## # These are undocumented flags; some of which we can use when repairing an installation # The runUnattended flag is one example of this -reconfigure=false +repair=false runUnattended=false # Check arguments for the undocumented flags for var in "$@"; do case "$var" in - "--reconfigure") reconfigure=true ;; + "--repair") repair=true ;; "--unattended") runUnattended=true ;; esac done @@ -1112,7 +1108,7 @@ setPrivacyLevel() { # A function to display a list of example blocklists for users to select chooseBlocklists() { - # Back up any existing adlist file, on the off chance that it exists. Useful in case of a reconfigure. + # Back up any existing adlist file, on the off chance that it exists. if [[ -f "${adlistFile}" ]]; then mv "${adlistFile}" "${adlistFile}.old" fi @@ -1355,9 +1351,9 @@ stop_service() { local str="Stopping ${1} service" printf " %b %s..." "${INFO}" "${str}" if is_command systemctl; then - systemctl stop "${1}" &>/dev/null || true + systemctl -q stop "${1}" || true else - service "${1}" stop &>/dev/null || true + service "${1}" stop >/dev/null || true fi printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } @@ -1370,10 +1366,10 @@ restart_service() { # If systemctl exists, if is_command systemctl; then # use that to restart the service - systemctl restart "${1}" &>/dev/null + systemctl -q restart "${1}" else # Otherwise, fall back to the service command - service "${1}" restart &>/dev/null + service "${1}" restart >/dev/null fi printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } @@ -1386,10 +1382,10 @@ enable_service() { # If systemctl exists, if is_command systemctl; then # use that to enable the service - systemctl enable "${1}" &>/dev/null + systemctl -q enable "${1}" else # Otherwise, use update-rc.d to accomplish this - update-rc.d "${1}" defaults &>/dev/null + update-rc.d "${1}" defaults >/dev/null fi printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } @@ -1402,10 +1398,10 @@ disable_service() { # If systemctl exists, if is_command systemctl; then # use that to disable the service - systemctl disable "${1}" &>/dev/null + systemctl -q disable "${1}" else # Otherwise, use update-rc.d to accomplish this - update-rc.d "${1}" disable &>/dev/null + update-rc.d "${1}" disable >/dev/null fi printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } @@ -1414,7 +1410,7 @@ check_service_active() { # If systemctl exists, if is_command systemctl; then # use that to check the status of the service - systemctl is-enabled "${1}" &>/dev/null + systemctl -q is-enabled "${1}" 2>/dev/null else # Otherwise, fall back to service command service "${1}" status &>/dev/null @@ -1473,16 +1469,11 @@ notify_package_updates_available() { # Store the list of packages in a variable updatesToInstall=$(eval "${PKG_COUNT}") - if [[ -d "/lib/modules/$(uname -r)" ]]; then - 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 + if [[ "${updatesToInstall}" -eq 0 ]]; then + printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}" else - printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" - printf " Kernel update detected. If the install fails, please reboot and try again\\n" + 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 } @@ -1757,83 +1748,6 @@ checkSelinux() { 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 [[ -n $(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 if the download exists and we can reach the server local status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) @@ -1920,10 +1834,10 @@ checkout_pull_branch() { return 0 } -clone_or_update_repos() { - # If the user wants to reconfigure, - if [[ "${reconfigure}" == true ]]; then - printf " %b Performing reconfiguration, skipping download of local repos\\n" "${INFO}" +clone_or_reset_repos() { + # If the user wants to repair/update, + if [[ "${repair}" == true ]]; then + printf " %b Resetting local repos\\n" "${INFO}" # Reset the Core repo resetRepo ${PI_HOLE_LOCAL_REPO} || { @@ -1936,7 +1850,7 @@ clone_or_update_repos() { printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}" exit 1 } - # Otherwise, a repair is happening + # Otherwise, a fresh installation is happening else # so get git files for Core getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || @@ -1999,7 +1913,7 @@ FTLinstall() { curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true # 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 -T -m 0755 "${binary}" /usr/bin/pihole-FTL @@ -2339,8 +2253,6 @@ migrate_dnsmasq_configs() { # Print a blank line for separation printf "\\n" - - v5_to_v6_update=true } # Check for availability of either the "service" or "systemctl" commands @@ -2430,22 +2342,19 @@ main() { exit 1 fi - # in case of an update (can be a v5 -> v6 or v6 -> v6 update) + # in case of an update (can be a v5 -> v6 or v6 -> v6 update) or repair if [[ -f "${PI_HOLE_V6_CONFIG}" ]] || [[ -f "/etc/pihole/setupVars.conf" ]]; then + # retain settings + fresh_install=false # if it's running unattended, if [[ "${runUnattended}" == true ]]; then printf " %b Performing unattended setup, no dialogs will be displayed\\n" "${INFO}" - # Use the setup variables - useUpdateVars=true # also disable debconf-apt-progress dialogs export DEBIAN_FRONTEND="noninteractive" - else - # If running attended, show the available options (repair/reconfigure) - update_dialogs fi fi - if [[ "${useUpdateVars}" == false ]]; then + if [[ "${fresh_install}" == true ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage (/etc/pihole/) @@ -2468,9 +2377,8 @@ main() { # Setup adlist file if not exists installDefaultBlocklists fi - # Download or update the scripts by updating the appropriate git repos - clone_or_update_repos - + # Download or reset the appropriate git repos depending on the 'repair' flag + clone_or_reset_repos # Create the pihole user create_pihole_user @@ -2500,11 +2408,9 @@ main() { # Copy the temp log file into final log location for storage copy_to_install_log - # Migrate existing install to v6.0 migrate_dnsmasq_configs - # Check for and disable systemd-resolved-DNSStubListener before reloading resolved # DNSStubListener needs to remain in place for installer to download needed files, # so this change needs to be made after installation is complete, @@ -2527,28 +2433,25 @@ main() { restart_service pihole-FTL - # Add password to web UI if there is none - pw="" - # If this is a fresh installation and no password is set, - if [[ ${v5_to_v6_update} = false && -z $(getFTLConfigValue webserver.api.pwhash) ]]; then - # generate a random password - pw=$(tr -dc _A-Z-a-z-0-9 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 [[ "${fresh_install}" == true ]]; then - if [[ "${useUpdateVars}" == false ]]; then # 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') - # Display the completion dialog - displayFinalMessage "${pw}" - - # 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}" + # If this is a fresh install, we will set a random password. + # Users can change this password after installation if they wish + pw=$(tr -dc _A-Z-a-z-0-9 /dev/null # Explain to the user how to use Pi-hole as their DNS server - printf " %b You may now configure your devices to use the Pi-hole as their DNS server\\n" "${INFO}" + printf "\\n %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 "${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 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" else INSTALL_TYPE="Update" diff --git a/gravity.sh b/gravity.sh index 0a34599b..3a7db5ae 100755 --- a/gravity.sh +++ b/gravity.sh @@ -541,14 +541,16 @@ gravity_DownloadBlocklists() { # it (in case it doesn't exist) # First, check if the directory is writable directory="$(dirname -- "${saveLocation}")" - if [ ! -w "${directory}" ]; then + directory_permissions=$(stat -c %a ${directory}) + if [ $directory_permissions -lt 700 ]; then echo -e " ${CROSS} Unable to write to ${directory}" echo " Please run pihole -g as root" echo "" continue fi # Then, check if the file is writable (if it exists) - if [ -e "${saveLocation}" ] && [ ! -w "${saveLocation}" ]; then + saveLocation_permissions=$(stat -c %a ${saveLocation}) + if [ -e "${saveLocation}" ] && [ ${saveLocation_permissions} -lt 600 ]; then echo -e " ${CROSS} Unable to write to ${saveLocation}" echo " Please run pihole -g as root" echo "" @@ -1082,7 +1084,7 @@ migrate_to_listsCache_dir() { fi # Update the list's paths in the corresponding .sha1 files to the new location - sed -i "s|${piholeDir}/|${listsCacheDir}/|g" "${listsCacheDir}"/*.sha1 + sed -i "s|${piholeDir}/|${listsCacheDir}/|g" "${listsCacheDir}"/*.sha1 2>/dev/null } helpFunc() { diff --git a/pihole b/pihole index b5fae016..bf662a82 100755 --- a/pihole +++ b/pihole @@ -107,11 +107,11 @@ updatePiholeFunc() { fi } -reconfigurePiholeFunc() { +repairPiholeFunc() { if [ -n "${DOCKER_VERSION}" ]; then unsupportedFunc else - /etc/.pihole/automated\ install/basic-install.sh --reconfigure + /etc/.pihole/automated\ install/basic-install.sh --repair exit 0; fi } @@ -479,7 +479,7 @@ Debugging Options: 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 -f, flush Flush the Pi-hole log - -r, reconfigure Reconfigure or Repair Pi-hole subsystems + -r, repair Repair Pi-hole subsystems -t, tail [arg] View the live output of the Pi-hole log. Add an optional argument to filter the log (regular expressions are supported) @@ -536,7 +536,7 @@ case "${1}" in "--allow-wild" | "allow-wild" ) need_root=0;; "-f" | "flush" ) ;; "-up" | "updatePihole" ) ;; - "-r" | "reconfigure" ) ;; + "-r" | "repair" ) ;; "-l" | "logging" ) ;; "uninstall" ) ;; "enable" ) need_root=0;; @@ -579,7 +579,7 @@ case "${1}" in "-d" | "debug" ) debugFunc "$@";; "-f" | "flush" ) flushFunc "$@";; "-up" | "updatePihole" ) updatePiholeFunc "$@";; - "-r" | "reconfigure" ) reconfigurePiholeFunc;; + "-r" | "repair" ) repairPiholeFunc;; "-g" | "updateGravity" ) updateGravityFunc "$@";; "-l" | "logging" ) piholeLogging "$@";; "uninstall" ) uninstallFunc;; diff --git a/test/test_any_automated_install.py b/test/test_any_automated_install.py index 73da7eef..5fa0f065 100644 --- a/test/test_any_automated_install.py +++ b/test/test_any_automated_install.py @@ -89,10 +89,10 @@ def test_installPihole_fresh_install_readableFiles(host): export DEBIAN_FRONTEND=noninteractive umask 0027 runUnattended=true - useUpdateVars=true + fresh_install=false source /opt/pihole/basic-install.sh > /dev/null runUnattended=true - useUpdateVars=true + fresh_install=false main /opt/pihole/pihole-FTL-prestart.sh """ @@ -127,10 +127,6 @@ def test_installPihole_fresh_install_readableFiles(host): check_localversion = test_cmd.format("r", "/etc/pihole/versions", piholeuser) actual_rc = host.run(check_localversion).rc assert exit_status_success == actual_rc - # readable logrotate - check_logrotate = test_cmd.format("r", "/etc/pihole/logrotate", piholeuser) - actual_rc = host.run(check_logrotate).rc - assert exit_status_success == actual_rc # readable macvendor.db check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser) actual_rc = host.run(check_macvendor).rc