2017-03-04 06:13:11 +01:00
#!/bin/bash
2019-02-17 07:18:28 +01:00
# Some of the bash_functions use variables these core pi-hole/web scripts
. /opt/pihole/webpage.sh
2017-01-08 17:42:39 +01:00
2019-02-06 11:02:30 +01:00
fix_capabilities( ) {
2022-04-02 11:50:55 +02:00
# Testing on Docker 20.10.14 with no caps set shows the following caps available to the container:
# Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
# FTL can also use CAP_NET_ADMIN and CAP_SYS_NICE. If we try to set them when they haven't been explicitly enabled, FTL will not start. Test for them first:
2022-05-05 17:02:23 +02:00
/sbin/capsh --has-p= cap_chown && CAP_STR += ',CAP_CHOWN'
/sbin/capsh --has-p= cap_net_bind_service && CAP_STR += ',CAP_NET_BIND_SERVICE'
/sbin/capsh --has-p= cap_net_raw && CAP_STR += ',CAP_NET_RAW'
/sbin/capsh --has-p= cap_net_admin && CAP_STR += ',CAP_NET_ADMIN' || DHCP_READY = 'false'
/sbin/capsh --has-p= cap_sys_nice && CAP_STR += ',CAP_SYS_NICE'
2022-04-02 12:08:20 +02:00
if [ [ ${ CAP_STR } ] ] ; then
# We have the (some of) the above caps available to us - apply them to pihole-FTL
setcap ${ CAP_STR : 1 } +ep $( which pihole-FTL) || ret = $?
2022-04-02 20:58:42 +02:00
if [ [ $DHCP_READY = = false ] ] && [ [ $DHCP_ACTIVE = = true ] ] ; then
# DHCP is requested but NET_ADMIN is not available.
echo "ERROR: DHCP requested but NET_ADMIN is not available. DHCP will not be started."
echo " Please add cap_net_admin to the container's capabilities or disable DHCP."
DHCP_ACTIVE = 'false'
change_setting "DHCP_ACTIVE" "false"
fi
2022-04-02 12:08:20 +02:00
if [ [ $ret -ne 0 && " ${ DNSMASQ_USER :- pihole } " != "root" ] ] ; then
echo "ERROR: Unable to set capabilities for pihole-FTL. Cannot run as non-root."
echo " If you are seeing this error, please set the environment variable 'DNSMASQ_USER' to the value 'root'"
exit 1
fi
2022-04-02 20:58:42 +02:00
else
echo "WARNING: Unable to set capabilities for pihole-FTL."
echo " Please ensure that the container has the required capabilities."
exit 1
2019-02-06 11:02:30 +01:00
fi
}
2018-08-03 05:49:55 +02:00
prepare_configs( ) {
2018-08-06 01:20:55 +02:00
# Done in /start.sh, don't do twice
2021-04-20 00:06:00 +02:00
PH_TEST = true . " ${ PIHOLE_INSTALL } "
2021-01-24 14:54:19 +01:00
# Set Debian webserver variables for installConfigs
LIGHTTPD_USER = "www-data"
LIGHTTPD_GROUP = "www-data"
LIGHTTPD_CFG = "lighttpd.conf.debian"
2018-08-03 05:49:55 +02:00
installConfigs
2022-03-31 20:49:35 +02:00
if [ ! -f " ${ setupVars } " ] ; then
2022-04-04 21:59:07 +02:00
install -m 644 /dev/null " ${ setupVars } "
2022-03-31 20:49:35 +02:00
echo " Creating empty ${ setupVars } file. "
fi
2018-08-03 05:49:55 +02:00
set +e
mkdir -p /var/run/pihole /var/log/pihole
2022-01-18 00:28:02 +01:00
2018-08-03 05:49:55 +02:00
chown pihole:root /etc/lighttpd
2022-01-18 00:28:02 +01:00
2022-04-22 18:41:29 +02:00
# In case of `pihole` UID being changed, re-chown the pihole scripts and pihole command
2022-01-20 18:10:19 +01:00
chown -R pihole:root " ${ PI_HOLE_INSTALL_DIR } "
chown pihole:root " ${ PI_HOLE_BIN_DIR } /pihole "
2018-08-03 05:49:55 +02:00
set -e
2018-08-06 01:20:55 +02:00
# Update version numbers
pihole updatechecker
2018-08-03 05:49:55 +02:00
# Re-write all of the setupVars to ensure required ones are present (like QUERY_LOGGING)
2020-05-04 23:15:35 +02:00
2018-08-03 05:49:55 +02:00
# If the setup variable file exists,
if [ [ -e " ${ setupVars } " ] ] ; then
2020-05-04 23:15:35 +02:00
cp -f " ${ setupVars } " " ${ setupVars } .update.bak "
2018-08-03 05:49:55 +02:00
fi
2017-01-21 22:02:08 +01:00
}
2016-10-07 02:46:20 +02:00
validate_env( ) {
2019-02-08 02:51:15 +01:00
# Optional ServerIP is a valid IP
2018-01-27 04:32:39 +01:00
# nc won't throw any text based errors when it times out connecting to a valid IP, otherwise it complains about the DNS name being garbage
# if nc doesn't behave as we expect on a valid IP the routing table should be able to look it up and return a 0 retcode
2020-06-04 23:03:48 +02:00
if [ [ " $( nc -4 -w1 -z " $ServerIP " 53 2>& 1) " != "" ] ] && ! ip route get " $ServerIP " > /dev/null ; then
2017-05-19 07:36:53 +02:00
echo " ERROR: ServerIP Environment variable ( $ServerIP ) doesn't appear to be a valid IPv4 address "
exit 1
fi
# Optional IPv6 is a valid address
if [ [ -n " $ServerIPv6 " ] ] ; then
if [ [ " $ServerIPv6 " = = 'kernel' ] ] ; then
2022-04-22 18:41:29 +02:00
echo "ERROR: You passed in IPv6 with a value of 'kernel', this maybe because you do not have IPv6 enabled on your network"
2017-05-19 07:36:53 +02:00
unset ServerIPv6
2018-01-05 05:30:40 +01:00
exit 1
2017-05-19 07:36:53 +02:00
fi
2020-06-04 23:03:48 +02:00
if [ [ " $( nc -6 -w1 -z " $ServerIPv6 " 53 2>& 1) " != "" ] ] && ! ip route get " $ServerIPv6 " > /dev/null ; then
2017-05-19 07:36:53 +02:00
echo " ERROR: ServerIPv6 Environment variable ( $ServerIPv6 ) doesn't appear to be a valid IPv6 address "
echo " TIP: If your server is not IPv6 enabled just remove '-e ServerIPv6' from your docker container"
exit 1
fi
fi ;
2016-10-07 02:46:20 +02:00
}
2017-03-05 00:31:09 +01:00
setup_dnsmasq_interface( ) {
2018-07-30 01:14:24 +02:00
local interface = " ${ 1 :- eth0 } "
2017-03-05 00:31:09 +01:00
local interfaceType = 'default'
2018-07-30 01:14:24 +02:00
if [ " $interface " != 'eth0' ] ; then
2017-03-05 00:31:09 +01:00
interfaceType = 'custom'
fi ;
2018-07-30 01:14:24 +02:00
echo " DNSMasq binding to $interfaceType interface: $interface "
[ -n " $interface " ] && change_setting "PIHOLE_INTERFACE" " ${ interface } "
2017-03-05 00:31:09 +01:00
}
2018-09-29 22:17:21 +02:00
setup_dnsmasq_listening_behaviour( ) {
local dnsmasq_listening_behaviour = " ${ 1 } "
if [ -n " $dnsmasq_listening_behaviour " ] ; then
change_setting "DNSMASQ_LISTENING" " ${ dnsmasq_listening_behaviour } "
fi ;
}
2017-05-23 16:22:16 +02:00
setup_dnsmasq_config_if_missing( ) {
# When fresh empty directory volumes are used we miss this file
if [ ! -f /etc/dnsmasq.d/01-pihole.conf ] ; then
cp /etc/.pihole/advanced/01-pihole.conf /etc/dnsmasq.d/
fi ;
}
2017-03-05 00:31:09 +01:00
setup_dnsmasq( ) {
2020-12-16 23:19:20 +01:00
local interface = " $1 "
local dnsmasq_listening_behaviour = " $2 "
2020-05-04 23:15:35 +02:00
# Coordinates
2017-05-23 16:22:16 +02:00
setup_dnsmasq_config_if_missing
2018-07-30 01:14:24 +02:00
setup_dnsmasq_interface " $interface "
2018-09-29 22:17:21 +02:00
setup_dnsmasq_listening_behaviour " $dnsmasq_listening_behaviour "
2019-02-08 02:51:15 +01:00
setup_dnsmasq_user " ${ DNSMASQ_USER } "
2020-10-11 02:01:18 +02:00
setup_cache_size " ${ CUSTOM_CACHE_SIZE } "
2017-03-05 00:31:09 +01:00
ProcessDNSSettings
2016-10-07 02:46:20 +02:00
}
2019-02-03 19:47:37 +01:00
setup_dnsmasq_user( ) {
2019-02-08 02:51:15 +01:00
local DNSMASQ_USER = " ${ 1 } "
2019-02-04 01:30:42 +01:00
2019-02-04 01:37:03 +01:00
# Run DNSMASQ as root user to avoid SHM permission issues
2019-02-04 01:30:42 +01:00
if grep -r -q '^\s*user=' /etc/dnsmasq.* ; then
# Change user that had been set previously to root
2019-02-05 07:17:54 +01:00
for f in $( grep -r -l '^\s*user=' /etc/dnsmasq.*) ; do
2019-02-04 01:37:03 +01:00
sed -i " /^\s*user=/ c\user= ${ DNSMASQ_USER } " " ${ f } "
2019-02-04 01:30:42 +01:00
done
else
2019-02-04 01:37:03 +01:00
echo -e " \nuser= ${ DNSMASQ_USER } " >> /etc/dnsmasq.conf
2019-02-04 01:30:42 +01:00
fi
2019-02-03 19:47:37 +01:00
}
2016-10-30 18:13:52 +01:00
setup_dnsmasq_hostnames( ) {
# largely borrowed from automated install/basic-install.sh
2017-01-08 17:42:39 +01:00
local IPV4_ADDRESS = " ${ 1 } "
local IPV6_ADDRESS = " ${ 2 } "
2016-10-30 18:13:52 +01:00
local hostname = " ${ 3 } "
2016-10-30 18:25:08 +01:00
local dnsmasq_pihole_01_location = "/etc/dnsmasq.d/01-pihole.conf"
2016-10-30 18:13:52 +01:00
if [ -z " $hostname " ] ; then
2016-10-30 18:25:08 +01:00
if [ [ -f /etc/hostname ] ] ; then
hostname = $( </etc/hostname)
elif [ -x " $( command -v hostname) " ] ; then
hostname = $( hostname -f)
fi
2016-10-30 18:13:52 +01:00
fi ;
2017-01-08 17:42:39 +01:00
if [ [ " ${ IPV4_ADDRESS } " != "" ] ] ; then
tmp = ${ IPV4_ADDRESS %/* }
sed -i " s/@IPV4@/ $tmp / " ${ dnsmasq_pihole_01_location }
2016-10-30 18:25:08 +01:00
else
2017-01-08 17:42:39 +01:00
sed -i '/^address=\/pi.hole\/@IPV4@/d' ${ dnsmasq_pihole_01_location }
sed -i '/^address=\/@HOSTNAME@\/@IPV4@/d' ${ dnsmasq_pihole_01_location }
2016-10-30 18:25:08 +01:00
fi
2017-01-08 17:42:39 +01:00
if [ [ " ${ IPV6_ADDRESS } " != "" ] ] ; then
sed -i " s/@IPv6@/ $IPV6_ADDRESS / " ${ dnsmasq_pihole_01_location }
2016-10-30 18:25:08 +01:00
else
sed -i '/^address=\/pi.hole\/@IPv6@/d' ${ dnsmasq_pihole_01_location }
sed -i '/^address=\/@HOSTNAME@\/@IPv6@/d' ${ dnsmasq_pihole_01_location }
fi
if [ [ " ${ hostname } " != "" ] ] ; then
sed -i " s/@HOSTNAME@/ $hostname / " ${ dnsmasq_pihole_01_location }
else
sed -i '/^address=\/@HOSTNAME@*/d' ${ dnsmasq_pihole_01_location }
fi
2016-10-30 18:13:52 +01:00
}
2020-10-11 02:01:18 +02:00
setup_cache_size( ) {
local warning = "WARNING: CUSTOM_CACHE_SIZE not used"
local dnsmasq_pihole_01_location = "/etc/dnsmasq.d/01-pihole.conf"
# Quietly exit early for empty or default
if [ [ -z " ${ 1 } " || " ${ 1 } " = = '10000' ] ] ; then return ; fi
if [ [ " ${ DNSSEC } " = = "true" ] ] ; then
echo " $warning - Cannot change cache size if DNSSEC is enabled "
return
fi
if ! echo $1 | grep -q '^[0-9]*$' ; then
echo " $warning - $1 is not an integer "
return
fi
local -i custom_cache_size = " $1 "
if ( ( $custom_cache_size < 0 ) ) ; then
echo " $warning - $custom_cache_size is not a positive integer or zero "
return
fi
echo " Custom CUSTOM_CACHE_SIZE set to $custom_cache_size "
sed -i " s/^cache-size=\s*[0-9]*/cache-size= $custom_cache_size / " ${ dnsmasq_pihole_01_location }
}
2017-07-26 21:36:32 +02:00
setup_lighttpd_bind( ) {
2018-07-30 01:14:24 +02:00
local serverip = " $1 "
2018-01-13 16:23:12 +01:00
# if using '--net=host' only bind lighttpd on $ServerIP and localhost
2020-05-30 06:56:32 +02:00
if grep -q "docker" /proc/net/dev && [ [ $serverip != 0.0.0.0 ] ] ; then #docker (docker0 by default) should only be present on the host system
2018-07-30 01:14:24 +02:00
if ! grep -q "server.bind" /etc/lighttpd/lighttpd.conf ; then # if the declaration is already there, don't add it again
sed -i -E " s/server\.port\s+\=\s+([0-9]+)/server.bind\t\t = \" ${ serverip } \"\nserver.port\t\t = \1\n " \$ SERVER"\[\"socket\"\] == \"127\.0\.0\.1:\1\" \{\}/" /etc/lighttpd/lighttpd.conf
2017-07-26 23:50:23 +02:00
fi
fi
2017-07-26 21:36:32 +02:00
}
2016-10-07 02:46:20 +02:00
setup_php_env( ) {
if [ -z " $VIRTUAL_HOST " ] ; then
VIRTUAL_HOST = " $ServerIP "
fi ;
2021-12-28 23:20:32 +01:00
2022-02-06 21:38:15 +01:00
for config_var in "VIRTUAL_HOST" "CORS_HOSTS" "ServerIP" "PHP_ERROR_LOG" "PIHOLE_DOCKER_TAG" "TZ" ; do
2021-12-28 23:20:32 +01:00
local beginning_of_line = " \t\t\t\" ${ config_var } \" => "
if grep -qP " $beginning_of_line " " $PHP_ENV_CONFIG " ; then
# replace line if already present
sed -i " / ${ beginning_of_line } /c\\ ${ beginning_of_line } \" ${ !config_var } \", " " $PHP_ENV_CONFIG "
else
# add line otherwise
sed -i " /bin-environment/ a\\ ${ beginning_of_line } \" ${ !config_var } \", " " $PHP_ENV_CONFIG "
fi
done
2016-10-07 02:46:20 +02:00
echo "Added ENV to php:"
2022-02-06 21:38:15 +01:00
grep -E '(VIRTUAL_HOST|CORS_HOSTS|ServerIP|PHP_ERROR_LOG|PIHOLE_DOCKER_TAG|TZ)' " $PHP_ENV_CONFIG "
2016-10-07 02:46:20 +02:00
}
2017-11-15 05:33:05 +01:00
setup_web_port( ) {
local warning = "WARNING: Custom WEB_PORT not used"
# Quietly exit early for empty or default
if [ [ -z " ${ 1 } " || " ${ 1 } " = = '80' ] ] ; then return ; fi
2020-05-04 23:15:35 +02:00
if ! echo $1 | grep -q '^[0-9][0-9]*$' ; then
2017-11-15 05:33:05 +01:00
echo " $warning - $1 is not an integer "
return
fi
local -i web_port = " $1 "
if ( ( $web_port < 1 || $web_port > 65535 ) ) ; then
echo " $warning - $web_port is not within valid port range of 1-65535 "
return
fi
echo " Custom WEB_PORT set to $web_port "
echo " INFO: Without proper router DNAT forwarding to $ServerIP : $web_port , you may not get any blocked websites on ads "
2018-02-23 06:11:53 +01:00
2020-01-14 15:37:44 +01:00
# Update lighttpd's port
sed -i '/server.port\s*=\s*80\s*$/ s/80/' $WEB_PORT '/g' /etc/lighttpd/lighttpd.conf
2017-11-15 05:33:05 +01:00
}
2020-02-26 18:27:42 +01:00
load_web_password_secret( ) {
# If WEBPASSWORD is not set at all, attempt to read password from WEBPASSWORD_FILE,
# allowing secrets to be passed via docker secrets
if [ -z " ${ WEBPASSWORD +x } " ] && [ -n " ${ WEBPASSWORD_FILE } " ] && [ -r " ${ WEBPASSWORD_FILE } " ] ; then
WEBPASSWORD = $( <" ${ WEBPASSWORD_FILE } " )
fi ;
}
2018-09-18 07:11:26 +02:00
generate_password( ) {
2018-08-04 23:19:11 +02:00
if [ -z " ${ WEBPASSWORD +x } " ] ; then
2018-08-04 22:58:05 +02:00
# Not set at all, give the user a random pass
WEBPASSWORD = $( tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
echo " Assigning random password: $WEBPASSWORD "
fi ;
2018-09-18 07:11:26 +02:00
}
setup_web_password( ) {
2019-02-17 07:18:28 +01:00
setup_var_exists "WEBPASSWORD" && return
2018-09-18 07:11:26 +02:00
PASS = " $1 "
2021-04-14 18:13:01 +02:00
# Explicitly turn off bash printing when working with secrets
{ set +x; } 2>/dev/null
2018-09-18 07:11:26 +02:00
if [ [ " $PASS " = = "" ] ] ; then
2018-08-04 22:58:05 +02:00
echo "" | pihole -a -p
else
2018-09-18 07:11:26 +02:00
pihole -a -p " $PASS " " $PASS "
2018-08-04 22:58:05 +02:00
fi
2018-10-17 02:55:52 +02:00
# To avoid printing this if conditional in bash debug, turn off debug above..
# then re-enable debug if necessary (more code but cleaner printed output)
if [ " ${ PH_VERBOSE :- 0 } " -gt 0 ] ; then
set -x
2018-08-04 22:58:05 +02:00
fi
2017-02-09 05:46:46 +01:00
}
2017-10-14 14:53:29 +02:00
2016-10-21 05:36:55 +02:00
setup_ipv4_ipv6( ) {
local ip_versions = "IPv4 and IPv6"
2021-04-09 20:14:39 +02:00
if [ " ${ IPv6 ,, } " != "true" ] ; then
2016-10-21 05:36:55 +02:00
ip_versions = "IPv4"
2018-07-30 01:14:24 +02:00
sed -i '/use-ipv6.pl/ d' /etc/lighttpd/lighttpd.conf
2016-10-21 05:36:55 +02:00
fi ;
echo " Using $ip_versions "
}
2016-10-07 02:46:20 +02:00
test_configs( ) {
2016-10-07 19:14:12 +02:00
set -e
echo -n '::: Testing lighttpd config: '
2018-01-25 19:45:05 +01:00
lighttpd -t -f /etc/lighttpd/lighttpd.conf || exit 1
2016-10-07 19:14:12 +02:00
set +e
2018-07-30 01:14:24 +02:00
echo "::: All config checks passed, cleared for startup ..."
2016-10-07 02:46:20 +02:00
}
2018-07-30 01:14:24 +02:00
setup_blocklists( ) {
2020-05-04 23:15:35 +02:00
local blocklists = " $1 "
2018-07-30 01:14:24 +02:00
# Exit/return early without setting up adlists with defaults for any of the following conditions:
2019-02-17 07:18:28 +01:00
# 1. skip_setup_blocklists env is set
2018-07-30 01:14:24 +02:00
exit_string = " (exiting ${ FUNCNAME [0] } early) "
2019-02-17 07:18:28 +01:00
if [ -n " ${ skip_setup_blocklists } " ] ; then
echo " ::: skip_setup_blocklists requested ( $exit_string ) "
2018-07-30 01:14:24 +02:00
return
fi
# 2. The adlist file exists already (restarted container or volume mounted list)
if [ -f " ${ adlistFile } " ] ; then
echo " ::: Preexisting ad list ${ adlistFile } detected ( $exit_string ) "
cat " ${ adlistFile } "
return
fi
echo " ::: ${ FUNCNAME [0] } now setting default blocklists up: "
echo " ::: TIP: Use a docker volume for ${ adlistFile } if you want to customize for first boot "
2019-01-22 05:43:38 +01:00
installDefaultBlocklists
2018-07-30 01:14:24 +02:00
echo " ::: Blocklists ( ${ adlistFile } ) now set to: "
cat " ${ adlistFile } "
2016-10-07 02:46:20 +02:00
}
2019-02-17 07:18:28 +01:00
setup_var_exists( ) {
local KEY = " $1 "
if [ -n " $2 " ] ; then
local REQUIRED_VALUE = "[^\n]+"
fi
if grep -Pq " ^ ${ KEY } = ${ REQUIRED_VALUE } " " $setupVars " ; then
echo " ::: Pre existing ${ KEY } found "
true
else
false
fi
}
2020-05-04 23:15:35 +02:00
setup_temp_unit( ) {
local UNIT = " $1 "
# check if var is empty
if [ [ " $UNIT " != "" ] ] ; then
# check if we have valid units
if [ [ " $UNIT " = = "c" || " $UNIT " = = "k" || $UNIT = = "f" ] ] ; then
pihole -a -${ UNIT }
fi
fi
}
setup_ui_layout( ) {
local LO = $1
# check if var is empty
if [ [ " $LO " != "" ] ] ; then
# check if we have valid types boxed | traditional
if [ [ " $LO " = = "traditional" || " $LO " = = "boxed" ] ] ; then
change_setting "WEBUIBOXEDLAYOUT" " $WEBUIBOXEDLAYOUT "
fi
fi
}
setup_admin_email( ) {
local EMAIL = $1
# check if var is empty
if [ [ " $EMAIL " != "" ] ] ; then
pihole -a -e " $EMAIL "
fi
}
2021-01-23 17:20:47 +01:00
setup_dhcp( ) {
if [ -z " ${ DHCP_START } " ] || [ -z " ${ DHCP_END } " ] || [ -z " ${ DHCP_ROUTER } " ] ; then
echo "ERROR: Won't enable DHCP server because mandatory Environment variables are missing: DHCP_START, DHCP_END and/or DHCP_ROUTER"
change_setting "DHCP_ACTIVE" "false"
else
change_setting "DHCP_ACTIVE" " ${ DHCP_ACTIVE } "
change_setting "DHCP_START" " ${ DHCP_START } "
change_setting "DHCP_END" " ${ DHCP_END } "
change_setting "DHCP_ROUTER" " ${ DHCP_ROUTER } "
change_setting "DHCP_LEASETIME" " ${ DHCP_LEASETIME } "
change_setting "PIHOLE_DOMAIN" " ${ PIHOLE_DOMAIN } "
change_setting "DHCP_IPv6" " ${ DHCP_IPv6 } "
change_setting "DHCP_rapid_commit" " ${ DHCP_rapid_commit } "
fi
}