Merge pull request #133 from diginc/dev
Development version of v3.0.1 release
This commit is contained in:
commit
4f8d7f4720
|
@ -0,0 +1 @@
|
|||
**/*.sw*
|
40
README.md
40
README.md
|
@ -17,16 +17,24 @@ One crucial thing to know before starting is the docker-pi-hole container needs
|
|||
```
|
||||
IMAGE='diginc/pi-hole'
|
||||
IP_LOOKUP="$(ip route get 8.8.8.8 | awk '{ print $NF; exit }')" # May not work for VPN / tun0
|
||||
IPv6_LOOKUP="$(ip -6 route get 2001:4860:4860::8888 | awk '{ print $10; exit }')" # May not work for VPN / tun0
|
||||
IP="${IP:-$IP_LOOKUP}" # use $IP, if set, otherwise IP_LOOKUP
|
||||
docker run -p 53:53/tcp -p 53:53/udp -p 80:80 --cap-add=NET_ADMIN -e ServerIP="$IP" --restart=always --name pihole -d $IMAGE
|
||||
IPv6="${IPv6:-$IPv6_LOOKUP}" # use $IPv6, if set, otherwise IP_LOOKUP
|
||||
|
||||
# Recommended auto ad list updates & log rotation:
|
||||
wget -O- https://raw.githubusercontent.com/diginc/docker-pi-hole/master/docker-pi-hole.cron | sudo tee /etc/cron.d/docker-pi-hole
|
||||
docker run -d \
|
||||
--name pihole \
|
||||
-p 53:53/tcp -p 53:53/udp -p 80:80 \
|
||||
-v </path/to/store/pihole>:/etc/pihole \
|
||||
-v </path/to/store/dnsmasq.d>:/etc/dnsmasq.d \
|
||||
-e ServerIP="${IP:-$(ip route get 8.8.8.8 | awk '{ print $NF; exit }')}" \
|
||||
-e ServerIPv6="${IPv6:-$(ip -6 route get 2001:4860:4860::8888 | awk '{ print $10; exit }')}" \
|
||||
--restart=always \
|
||||
diginc/pi-hole
|
||||
```
|
||||
|
||||
This is just an example and might need changing. As mentioned on line 2, the auto IP_LOOKUP variable may not work for VPN tunnel interfaces.
|
||||
Volumes aren't required but are recommended for persisting data across docker re-creations for updating images. This is just an example and might need changing. As mentioned on line 2, the auto IP_LOOKUP variable may not work for VPN tunnel interfaces.
|
||||
|
||||
**Automatic Ad List Updates** - [docker-pi-hole.cron](https://github.com/diginc/docker-pi-hole/blob/master/docker-pi-hole.cron) is a modified version of upstream pi-hole's crontab entries using `docker exec` to run the same update scripts inside the docker container. The cron automatically updates pi-hole ad lists and cleans up pi-hole logs nightly. If you're not using the `docker run` with `--name pihole` from default container run command be sure to fill in your container's DOCKER_NAME into the variable in the cron file.
|
||||
**Automatic Ad List Updates** - since 3.0+ release cron is baked into the container and will grab the newest versions of your lists and flush your logs. **Set TZ** environment variable to make sure the midnight log rotation syncs up with your timezone's midnight.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
@ -34,12 +42,14 @@ In addition to the required environment variable you saw above (`-e ServerIP="$I
|
|||
|
||||
| Env Variable | Default | Description |
|
||||
| ------------ | ------- | ----------- |
|
||||
| ServerIP | REQUIRED! | Set to your server's external IP in order to override what Pi-Hole users. Pi-Hole auto discovers the unusable internal docker IP otherwise |
|
||||
| WEBPASSWORD | <random> | Set this to your desired password or on first boot we'll randomly set one. `docker logs pihole` can tell you what it got set to. To change it check out the tips below |
|
||||
| ServerIP | REQUIRED! | Set to your server's external IP in order to override what Pi-Hole users requests get sent to. This container is designed to fail when not set since it is pretty useless without it. |
|
||||
| ServerIPv6 | optional | Where IPv6 ad requests end up, not required if you're not running ipv6 network |
|
||||
| WEBPASSWORD | <random> | Recommended! Set this to your desired password or on first boot we'll randomly set one. `docker logs pihole` can tell you what it got set to. To change it check out the tips below |
|
||||
| DNS1 | 8.8.8.8 | Primary upstream DNS for Pi-Hole's DNSMasq to use, defaults to google |
|
||||
| DNS2 | 8.8.4.4 | Secondary upstream DNS for Pi-Hole's DNSMasq to use, defaults to google |
|
||||
| VIRTUAL_HOST | Server_IP | What your web server 'virtual host' is, accessing admin through this Hostname/IP allows you to make changes to the whitelist / blacklists in addition to the default 'http://pi.hole/admin/' address |
|
||||
| IPv6 | True | Allows forced disabling of IPv6 for docker setups that can't support it (like unraid) |
|
||||
| TZ | UCT | Customize your container's timezone, useful if you use the cron for pihole updates |
|
||||
|
||||
*OPTIONAL Advanced* Environment Variables
|
||||
|
||||
|
@ -52,19 +62,19 @@ In addition to the required environment variable you saw above (`-e ServerIP="$I
|
|||
|
||||
* A good way to test things are working right is by loading this page: [http://pi.hole/admin/](http://pi.hole/admin/)
|
||||
* [How do I set or reset the Web interface Password?](https://discourse.pi-hole.net/t/how-do-i-set-or-reset-the-web-interface-password/1328)
|
||||
* `docker exec pihole_container_name pihole -a -p supersecurepassword`
|
||||
* `docker exec pihole_container_name pihole -a -p supersecurepassword`
|
||||
* Port conflicts? Stop your server's existing DNS / Web services.
|
||||
* Ubuntu users especially may need to shutoff dnsmasq on your docker server so it can run in the container on port 53
|
||||
* Don't forget to stop your services from auto-starting again after you reboot
|
||||
* Ubuntu users especially may need to shutoff dnsmasq on your docker server so it can run in the container on port 53
|
||||
* Don't forget to stop your services from auto-starting again after you reboot
|
||||
* Port 80 is highly recommended because if you have another site/service using port 80 by default then the ads may not transform into blank ads correctly. To make sure docker-pi-hole plays nicely with an existing webserver you run you'll probably need a reverse proxy webserver config if you don't have one already. Pi-Hole has to be the default web app on said proxy e.g. if you goto your host by IP instead of domain then pi-hole is served out instead of any other sites hosted by the proxy. This is the '[default_server](http://nginx.org/en/docs/http/ngx_http_core_module.html#listen)' in nginx or ['_default_' virtual host](https://httpd.apache.org/docs/2.4/vhosts/examples.html#default) in Apache and is taken advantage of so any undefined ad domain can be directed to your webserver and get a 'blocked' response instead of ads.
|
||||
* You can still map other ports to pi-hole port 80 using docker's port forwarding like this `-p 8080:80`, but again the ads won't render propertly. Changing the inner port 80 shouldn't be required unless you run docker host networking mode.
|
||||
* [Here is an example of running with jwilder/proxy](https://github.com/diginc/docker-pi-hole/blob/master/jwilder-proxy-example-doco.yml) (an nginx auto-configuring docker reverse proxy for docker) on my port 80 with pihole on another port. Pi-hole needs to be `DEFAULT_HOST` env in jwilder/proxy and you need to set the matching `VIRTUAL_HOST` for the pihole's container. Please read jwilder/proxy readme for more info if you have trouble. I tested this basic example which is based off what I run.
|
||||
* You can still map other ports to pi-hole port 80 using docker's port forwarding like this `-p 8080:80`, but again the ads won't render propertly. Changing the inner port 80 shouldn't be required unless you run docker host networking mode.
|
||||
* [Here is an example of running with jwilder/proxy](https://github.com/diginc/docker-pi-hole/blob/master/jwilder-proxy-example-doco.yml) (an nginx auto-configuring docker reverse proxy for docker) on my port 80 with pihole on another port. Pi-hole needs to be `DEFAULT_HOST` env in jwilder/proxy and you need to set the matching `VIRTUAL_HOST` for the pihole's container. Please read jwilder/proxy readme for more info if you have trouble. I tested this basic example which is based off what I run.
|
||||
|
||||
## Volume Mounts
|
||||
Here are some useful volume mount options to persist your history of stats in the admin interface, or add custom whitelists/blacklists. **Create these files on the docker host first or you'll get errors**:
|
||||
|
||||
* `docker run -v /var/log/pihole.log:/var/log/pihole.log ...` (plus all of the minimum options added)
|
||||
* `touch /var/log/pihole.log` on your docker server first or you will end up with a directory there (silly docker!)
|
||||
* `touch /var/log/pihole.log` on your docker server first or you will end up with a directory there (silly docker!)
|
||||
* `docker run -v /etc/pihole/:/etc/pihole/ ...` (plus all of the minimum options added)
|
||||
|
||||
All of these options get really long when strung together in one command, which is why I'm not showing all the full `docker run` commands variations here. This is where [docker-compose](https://docs.docker.com/compose/install/) yml files come in handy for representing [really long docker commands in a readable file format](https://github.com/diginc/docker-pi-hole/blob/master/doco-example.yml).
|
||||
|
@ -103,8 +113,8 @@ The standard pi-hole customization abilities apply to this docker, but with dock
|
|||
|
||||
1. Download the latest version of the image: `docker pull diginc/pi-hole`
|
||||
2. Throw away your container: `docker rm -f pihole`
|
||||
* **Warning** When removing your pihole container you may be stuck without DNS until step 3 - **docker pull** before you **docker rm -f** to avoid DNS inturruption **OR** always have a fallback DNS server configured in DHCP to avoid this problem all together.
|
||||
* If you care about your data (logs/customizations), make sure you have it volume mapped or it will be deleted in this step
|
||||
* **Warning** When removing your pihole container you may be stuck without DNS until step 3 - **docker pull** before you **docker rm -f** to avoid DNS inturruption **OR** always have a fallback DNS server configured in DHCP to avoid this problem all together.
|
||||
* If you care about your data (logs/customizations), make sure you have it volume mapped or it will be deleted in this step
|
||||
3. Start your container with the newer base image: `docker run <args> diginc/pi-hole` (`<args>` being your preferred run volumes and env vars)
|
||||
|
||||
Why is this style of upgrading good? A couple reasons: Everyone is starting from the same base image which has been tested to know it works. No worrying about upgrading from A to B, B to C, or A to C is required when rolling out updates, it reducing complexity, and simply allows a 'fresh start' every time while preserving customizations with volumes. Basically I'm encouraging [phoenix servers](https://www.google.com/?q=phoenix+servers) principles for your containers.
|
||||
|
|
|
@ -4,26 +4,22 @@ MAINTAINER adam@diginc.us <adam@diginc.us>
|
|||
ENV IMAGE alpine
|
||||
ENV PATH /opt/pihole:${PATH}
|
||||
|
||||
COPY install.sh /install.sh
|
||||
COPY ./alpine/service /usr/local/bin/service
|
||||
COPY install.sh /usr/local/bin/docker-install.sh
|
||||
ENV setupVars /etc/pihole/setupVars.conf
|
||||
ENV PIHOLE_INSTALL /tmp/ph_install.sh
|
||||
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-amd64.tar.gz
|
||||
|
||||
ENV TINI_VERSION v0.13.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static.asc /tini.asc
|
||||
|
||||
# Tini and package requirements
|
||||
RUN apk add --update 'gnupg<2.1.17-r0' && \
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0527A9B7 && \
|
||||
gpg --verify /tini.asc && \
|
||||
chmod +x /tini && \
|
||||
apk add wget bash && \
|
||||
/install.sh && \
|
||||
RUN apk upgrade --update && \
|
||||
apk add bind-tools wget curl bash libcap && \
|
||||
curl -L -s $S6OVERLAY_RELEASE \
|
||||
| tar xvzf - -C / && \
|
||||
docker-install.sh && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Customized from submodules
|
||||
COPY ./alpine/nginx.conf /etc/nginx/nginx.conf
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
||||
ADD s6/alpine-root /
|
||||
COPY s6/service /usr/local/bin/service
|
||||
|
||||
# Things installer did and fix alpine+nginx differences
|
||||
ENV WEBLOGDIR /var/log/nginx
|
||||
|
@ -35,29 +31,30 @@ RUN mkdir -p /etc/pihole/ && \
|
|||
touch ${WEBLOGDIR}/access.log ${WEBLOGDIR}/error.log && \
|
||||
chown -R nginx:nginx ${WEBLOGDIR} && \
|
||||
sed -i 's|^user\s*=.*$|user = nginx|' $PHP_CONFIG && \
|
||||
sed -i '/^;pid/ s|^;||' $PHP_CONFIG && \
|
||||
chmod 775 /var/www/html && \
|
||||
touch /var/log/pihole.log && \
|
||||
chmod 644 /var/log/pihole.log && \
|
||||
chown dnsmasq:root /var/log/pihole.log && \
|
||||
sed -i "s/@INT@/eth0/" /etc/dnsmasq.d/01-pihole.conf && \
|
||||
setcap CAP_NET_BIND_SERVICE=+eip `which dnsmasq` && \
|
||||
cp -f /usr/bin/list.sh /opt/pihole/list.sh && \
|
||||
echo 'Done!'
|
||||
|
||||
#sed -i 's|"cd /etc/.pihole/ && git describe --tags --abbrev=0"|"cat /etc/pi-hole_version.txt"|g' /var/www/html/admin/footer.php && \
|
||||
#sed -i 's|"git describe --tags --abbrev=0"|"cat /etc/AdminLTE_version.txt"|g' /var/www/html/admin/footer.php && \
|
||||
#sed -i 's|www-data|nginx|g' /etc/sudoers.d/pihole && \
|
||||
|
||||
# php config start passes special ENVs into
|
||||
ENV PHP_ENV_CONFIG '/etc/php5/fpm.d/envs.conf'
|
||||
ENV PHP_ERROR_LOG '/var/log/nginx/error.log'
|
||||
COPY ./start.sh /
|
||||
COPY ./bash_functions.sh /
|
||||
|
||||
# IPV6 disable flag for networks/devices that do not support it
|
||||
# IPv6 disable flag for networks/devices that do not support it
|
||||
ENV IPv6 True
|
||||
|
||||
EXPOSE 53 53/udp
|
||||
EXPOSE 80
|
||||
|
||||
ENV S6_LOGGING 0
|
||||
ENV S6_KEEP_ENV 1
|
||||
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
CMD [ "/start.sh" ]
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#!/bin/bash
|
||||
# lazy cheap service script patch for alpine
|
||||
dnsmasq_start() {
|
||||
dnsmasq -7 /etc/dnsmasq.d
|
||||
}
|
||||
|
||||
dnsmasq_stop() {
|
||||
kill -9 $(pidof dnsmasq)
|
||||
}
|
||||
|
||||
dnsmasq_restart() {
|
||||
dnsmasq_stop; dnsmasq_start;
|
||||
}
|
||||
|
||||
status() {
|
||||
if pidof $service 2&>1 > /dev/null ; then
|
||||
echo "$service running"
|
||||
else
|
||||
echo "$service not running"
|
||||
fi;
|
||||
}
|
||||
|
||||
nginx_start() {
|
||||
nginx -t && \
|
||||
nginx
|
||||
}
|
||||
nginx_stop() {
|
||||
nginx -t && \
|
||||
kill -9 $(pidof nginx)
|
||||
}
|
||||
nginx_restart() {
|
||||
nginx_stop
|
||||
nginx_start
|
||||
}
|
||||
dnsmasq_status() {
|
||||
status
|
||||
}
|
||||
nginx_status() {
|
||||
status
|
||||
}
|
||||
|
||||
service="$1"
|
||||
command="$2"
|
||||
if [[ "$service" == 'lighttpd' ]] ; then
|
||||
echo -e "Lighttpd replaced by nginx in diginc/pi-hole:alpine\nrunning service nginx $command instead";
|
||||
service='nginx'
|
||||
fi;
|
||||
|
||||
|
||||
if [[ "$service" == 'dnsmasq' ]] || [[ "$service" == 'nginx' ]] ; then
|
||||
${service}_${command} || echo "Unknown option $command"
|
||||
else
|
||||
echo "$service service wrapper not patched into alpine container"
|
||||
exit 1
|
||||
fi
|
|
@ -2,6 +2,7 @@
|
|||
. /opt/pihole/webpage.sh
|
||||
setupVars="$setupVars"
|
||||
ServerIP="$ServerIP"
|
||||
ServerIPv6="$ServerIPv6"
|
||||
IPv6="$IPv6"
|
||||
|
||||
prepare_setup_vars() {
|
||||
|
@ -13,6 +14,32 @@ validate_env() {
|
|||
echo "ERROR: To function correctly you must pass an environment variables of 'ServerIP' into the docker container with the IP of your docker host from which you are passing web (80) and dns (53) ports from"
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
# Debian
|
||||
nc_error='Name or service not known'
|
||||
if [[ "$IMAGE" == 'alpine' ]] ; then
|
||||
nc_error='bad address'
|
||||
fi;
|
||||
|
||||
# Required ServerIP is a valid IP
|
||||
if nc -w1 -z "$ServerIP" 53 2>&1 | grep -q "$nc_error" ; then
|
||||
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
|
||||
echo "WARNING: You passed in IPv6 with a value of 'kernel', this maybe beacuse you do not have IPv6 enabled on your network"
|
||||
unset ServerIPv6
|
||||
return
|
||||
fi
|
||||
if nc -w 1 -z "$ServerIPv6" 53 2>&1 | grep -q "$nc_error" || ! ip route get "$ServerIPv6" ; then
|
||||
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;
|
||||
}
|
||||
|
||||
setup_dnsmasq_dns() {
|
||||
|
@ -134,7 +161,13 @@ setup_web_password() {
|
|||
WEBPASSWORD=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
|
||||
echo "Assigning random password: $WEBPASSWORD"
|
||||
fi;
|
||||
pihole -a -p "$WEBPASSWORD"
|
||||
set -x
|
||||
if [[ "$WEBPASSWORD" == "" ]] ; then
|
||||
echo "" | pihole -a -p
|
||||
else
|
||||
pihole -a -p "$WEBPASSWORD" "$WEBPASSWORD"
|
||||
fi
|
||||
{ set +x; } 2>/dev/null
|
||||
}
|
||||
setup_ipv4_ipv6() {
|
||||
local ip_versions="IPv4 and IPv6"
|
||||
|
@ -142,7 +175,7 @@ setup_ipv4_ipv6() {
|
|||
ip_versions="IPv4"
|
||||
case $IMAGE in
|
||||
"debian") sed -i '/use-ipv6.pl/ d' /etc/lighttpd/lighttpd.conf ;;
|
||||
"alpine") sed -i '/listen \[::\]:80;/ d' /etc/nginx/nginx.conf ;;
|
||||
"alpine") sed -i '/listen \[::\]:80/ d' /etc/nginx/nginx.conf ;;
|
||||
esac
|
||||
fi;
|
||||
echo "Using $ip_versions"
|
||||
|
@ -178,7 +211,11 @@ test_configs_alpine() {
|
|||
}
|
||||
|
||||
test_framework_stubbing() {
|
||||
if [ -n "$PYTEST" ] ; then sed -i 's/^gravity_spinup$/#gravity_spinup # DISABLED FOR PYTEST/g' "$(which gravity.sh)"; fi;
|
||||
if [ -n "$PYTEST" ] ; then
|
||||
echo ":::::: Tests are being ran - stub out ad list fetching and add a fake ad block"
|
||||
sed -i 's/^gravity_spinup$/#gravity_spinup # DISABLED FOR PYTEST/g' "$(which gravity.sh)"
|
||||
echo 'testblock.pi-hole.local' >> /etc/pihole/blacklist.txt
|
||||
fi
|
||||
}
|
||||
|
||||
docker_main() {
|
||||
|
|
|
@ -6,22 +6,23 @@ RUN [ "cross-build-start" ]
|
|||
ENV IMAGE debian
|
||||
ENV PATH /opt/pihole:${PATH}
|
||||
|
||||
COPY install.sh /install.sh
|
||||
COPY install.sh /usr/local/bin/docker-install.sh
|
||||
ENV setupVars /etc/pihole/setupVars.conf
|
||||
ENV PIHOLE_INSTALL /tmp/ph_install.sh
|
||||
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-armhf.tar.gz
|
||||
|
||||
ENV TINI_VERSION v0.13.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-armhf /tini
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-armhf.asc /tini.asc
|
||||
|
||||
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0527A9B7 && \
|
||||
gpg --verify /tini.asc && \
|
||||
chmod +x /tini && \
|
||||
apt-get -q update && \
|
||||
apt-get install -y wget net-tools && \
|
||||
/install.sh && \
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget curl net-tools cron && \
|
||||
curl -L -s $S6OVERLAY_RELEASE \
|
||||
| tar xvzf - -C / && \
|
||||
docker-install.sh && \
|
||||
rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
|
||||
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
||||
ADD s6/debian-root /
|
||||
COPY s6/service /usr/local/bin/service
|
||||
|
||||
# php config start passes special ENVs into
|
||||
ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf'
|
||||
ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log'
|
||||
|
@ -35,8 +36,10 @@ ENV IPv6 True
|
|||
EXPOSE 53 53/udp
|
||||
EXPOSE 80
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
CMD [ "/start.sh" ]
|
||||
ENV S6_LOGGING 0
|
||||
ENV S6_KEEP_ENV 1
|
||||
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
|
||||
|
||||
RUN [ "cross-build-end" ]
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN [ "cross-build-end" ]
|
||||
|
|
|
@ -4,22 +4,23 @@ MAINTAINER adam@diginc.us <adam@diginc.us>
|
|||
ENV IMAGE debian
|
||||
ENV PATH /opt/pihole:${PATH}
|
||||
|
||||
COPY install.sh /install.sh
|
||||
COPY install.sh /usr/local/bin/docker-install.sh
|
||||
ENV setupVars /etc/pihole/setupVars.conf
|
||||
ENV PIHOLE_INSTALL /tmp/ph_install.sh
|
||||
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-amd64.tar.gz
|
||||
|
||||
ENV TINI_VERSION v0.13.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini.asc /tini.asc
|
||||
|
||||
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0527A9B7 && \
|
||||
gpg --verify /tini.asc && \
|
||||
chmod +x /tini && \
|
||||
apt-get -q update && \
|
||||
apt-get install -y wget net-tools && \
|
||||
/install.sh && \
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget curl net-tools cron && \
|
||||
curl -L -s $S6OVERLAY_RELEASE \
|
||||
| tar xvzf - -C / && \
|
||||
docker-install.sh && \
|
||||
rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
|
||||
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
||||
ADD s6/debian-root /
|
||||
COPY s6/service /usr/local/bin/service
|
||||
|
||||
# php config start passes special ENVs into
|
||||
ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf'
|
||||
ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log'
|
||||
|
@ -33,6 +34,8 @@ ENV IPv6 True
|
|||
EXPOSE 53 53/udp
|
||||
EXPOSE 80
|
||||
|
||||
ENV S6_LOGGING 0
|
||||
ENV S6_KEEP_ENV 1
|
||||
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
CMD [ "/start.sh" ]
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
#!/bin/bash
|
||||
IMAGE=${1:-'diginc/pi-hole:alpine'}
|
||||
IP_LOOKUP="$(ip route get 8.8.8.8 | awk '{ print $NF; exit }')" # May not work for VPN / tun0
|
||||
IPv6_LOOKUP="$(ip -6 route get 2001:4860:4860::8888 | awk '{ print $10; exit }')" # May not work for VPN / tun0
|
||||
IP="${IP:-$IP_LOOKUP}" # use $IP, if set, otherwise IP_LOOKUP
|
||||
IPv6="${IPv6:-$IPv6_LOOKUP}" # use $IPv6, if set, otherwise IP_LOOKUP
|
||||
|
||||
echo "IP: ${IP} - IPv6: ${IPv6}"
|
||||
|
||||
# Default ports + daemonized docker container
|
||||
docker run -p 53:53/tcp -p 53:53/udp -p 80:80 \
|
||||
--cap-add=NET_ADMIN \
|
||||
-e ServerIP="$IP" \
|
||||
--name pihole \
|
||||
--restart=always \
|
||||
-d "$IMAGE"
|
||||
docker run -d \
|
||||
--name pihole \
|
||||
-p 53:53/tcp -p 53:53/udp -p 80:80 \
|
||||
-v /etc/volumes/pihole:/etc/pihole \
|
||||
-v /etc/volumes/dnsmasq.d:/etc/dnsmasq.d \
|
||||
-e ServerIP="${IP:-$(ip route get 8.8.8.8 | awk '{ print $NF; exit }')}" \
|
||||
-e ServerIPv6="${IPv6:-$(ip -6 route get 2001:4860:4860::8888 | awk '{ print $10; exit }')}" \
|
||||
--restart=always \
|
||||
diginc/pi-hole
|
||||
|
||||
docker logs pihole 2> /dev/null | grep 'password:'
|
||||
|
|
24
install.sh
24
install.sh
|
@ -1,7 +1,8 @@
|
|||
#!/bin/bash -x
|
||||
#!/bin/bash -ex
|
||||
mkdir -p /etc/pihole/
|
||||
export CORE_TAG='v2.13.2'
|
||||
export WEB_TAG='v2.5.2'
|
||||
export CORE_TAG='v3.0.1'
|
||||
export WEB_TAG='v3.0.1'
|
||||
export FTL_TAG='v2.7'
|
||||
|
||||
# Make pihole scripts fail searching for `systemctl`,
|
||||
# which fails pretty miserably in docker compared to `service`
|
||||
|
@ -21,6 +22,11 @@ if [[ "$IMAGE" == 'alpine' ]] ; then
|
|||
sed -i 's/www-data/nginx/g' "$PIHOLE_INSTALL"
|
||||
sed -i '/LIGHTTPD_CFG/d' "${PIHOLE_INSTALL}"
|
||||
sed -i '/etc\/cron.d\//d' "${PIHOLE_INSTALL}"
|
||||
# For new FTL install lines
|
||||
sed -i 's/sha1sum --status --quiet/sha1sum -s/g' "${PIHOLE_INSTALL}"
|
||||
sed -i 's/install -T/install /g' "${PIHOLE_INSTALL}"
|
||||
# shellcheck disable=SC2016
|
||||
sed -i '/FTLinstall/ s/${binary}/pihole-FTL-musl-linux-x86_64/g' "${PIHOLE_INSTALL}"
|
||||
LIGHTTPD_USER="nginx" # shellcheck disable=SC2034
|
||||
LIGHTTPD_GROUP="nginx" # shellcheck disable=SC2034
|
||||
LIGHTTPD_CFG="lighttpd.conf.debian" # shellcheck disable=SC2034
|
||||
|
@ -36,13 +42,17 @@ if [[ "$IMAGE" == 'debian' ]] ; then
|
|||
install_dependent_packages PIHOLE_DEPS[@]
|
||||
install_dependent_packages PIHOLE_WEB_DEPS[@]
|
||||
sed -i "/sleep 2/ d" /etc/init.d/dnsmasq # SLOW
|
||||
# IPv6 support for nc openbsd better than traditional
|
||||
apt-get install -y --force-yes netcat-openbsd
|
||||
elif [[ "$IMAGE" == 'alpine' ]] ; then
|
||||
apk add \
|
||||
dnsmasq \
|
||||
nginx \
|
||||
ca-certificates \
|
||||
php5-fpm php5-json php5-openssl php5-zip libxml2 \
|
||||
php5-fpm php5-json php5-openssl php5-zip php5-sockets libxml2 \
|
||||
bc bash curl perl sudo git
|
||||
# S6 service like to be blocking/foreground
|
||||
sed -i 's|^;daemonize = yes|daemonize = no|' /etc/php5/php-fpm.conf
|
||||
fi
|
||||
|
||||
piholeGitUrl="${piholeGitUrl}"
|
||||
|
@ -64,9 +74,13 @@ tmpLog="${tmpLog}"
|
|||
instalLogLoc="${instalLogLoc}"
|
||||
installPihole | tee "${tmpLog}"
|
||||
sed -i 's/readonly //g' /opt/pihole/webpage.sh
|
||||
if [[ "$IMAGE" == 'alpine' ]] ; then
|
||||
cp /etc/.pihole/advanced/pihole.cron /etc/crontabs/pihole
|
||||
fi
|
||||
|
||||
|
||||
mv "${tmpLog}" "${instalLogLoc}"
|
||||
|
||||
# Fix dnsmasq in docker
|
||||
grep -q '^user=root' || echo -e '\nuser=root' >> /etc/dnsmasq.conf
|
||||
echo 'done'
|
||||
echo 'Docker install successful'
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# Early DNS Startup for the gravity list process to use
|
||||
dnsmasq -7 /etc/dnsmasq.d
|
||||
|
||||
/start.sh
|
||||
gravity.sh
|
||||
|
||||
# Done with DNS, let s6 services start up properly configured dns now
|
||||
killall -9 dnsmasq
|
|
@ -0,0 +1 @@
|
|||
/etc/resolv.conf false doesntexist,0:1000 0664 0664
|
|
@ -12,8 +12,10 @@ http {
|
|||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
# PHP SERVER_NAME is empty unless set, which can bug out ipv6less setups
|
||||
server_name dockerpi.hole;
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
root /var/www/html;
|
||||
index pihole/index.php index.php;
|
||||
error_page 404 =200 /pihole/index.php;
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
s6-echo "Starting crond"
|
||||
|
||||
exec -c
|
||||
fdmove -c 2 1 /usr/sbin/crond -f -L /var/log/cron -l 0 -c /etc/crontabs
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
kill -9 $(pgrep dnsmasq)
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting dnsmasq"
|
||||
|
||||
s6-setuidgid dnsmasq dnsmasq -7 /etc/dnsmasq.d --no-daemon
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-svwait -u -t 5000 /var/run/s6/services/php-fpm
|
||||
s6-echo "Starting nginx"
|
||||
|
||||
nginx -g "daemon off;"
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting php-fpm"
|
||||
|
||||
php-fpm5 -d daemonize=no
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting pihole-FTL"
|
||||
pihole-FTL no-daemon
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/with-contenv sh
|
||||
|
||||
#
|
||||
# This script will determine the network IP of the container.
|
||||
#
|
||||
# Return format should be a single IP address.
|
||||
#
|
||||
|
||||
# Default to using the value of the $HOSTNAME ENV variable.
|
||||
getent hosts ${1:-$HOSTNAME} | awk '{print $1}'
|
|
@ -0,0 +1,235 @@
|
|||
#!/usr/bin/env bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Whitelists and blacklists domains
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
|
||||
#globals
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
whitelist=${piholeDir}/whitelist.txt
|
||||
blacklist=${piholeDir}/blacklist.txt
|
||||
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
reload=false
|
||||
addmode=true
|
||||
verbose=true
|
||||
|
||||
domList=()
|
||||
domToRemoveList=()
|
||||
|
||||
listMain=""
|
||||
listAlt=""
|
||||
|
||||
helpFunc() {
|
||||
|
||||
if [[ ${listMain} == ${whitelist} ]]; then
|
||||
letter="w"
|
||||
word="white"
|
||||
else
|
||||
letter="b"
|
||||
word="black"
|
||||
fi
|
||||
|
||||
cat << EOM
|
||||
::: Immediately ${word}lists one or more domains in the hosts file
|
||||
:::
|
||||
::: Usage: pihole -${letter} domain1 [domain2 ...]
|
||||
:::
|
||||
::: Options:
|
||||
::: -d, --delmode Remove domains from the ${word}list
|
||||
::: -nr, --noreload Update ${word}list without refreshing dnsmasq
|
||||
::: -q, --quiet Output is less verbose
|
||||
::: -h, --help Show this help dialog
|
||||
::: -l, --list Display your ${word}listed domains
|
||||
EOM
|
||||
if [[ "${letter}" == "b" ]]; then
|
||||
echo "::: -wild, --wildcard Add wildcard entry (only blacklist)"
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
EscapeRegexp() {
|
||||
# This way we may safely insert an arbitrary
|
||||
# string in our regular expressions
|
||||
# Also remove leading "." if present
|
||||
echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
|
||||
}
|
||||
|
||||
HandleOther(){
|
||||
# First, convert everything to lowercase
|
||||
domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1")
|
||||
|
||||
#check validity of domain
|
||||
validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/')
|
||||
if [ -z "${validDomain}" ]; then
|
||||
echo "::: $1 is not a valid argument or domain name"
|
||||
else
|
||||
domList=("${domList[@]}" ${validDomain})
|
||||
fi
|
||||
}
|
||||
|
||||
PoplistFile() {
|
||||
#check whitelist file exists, and if not, create it
|
||||
if [[ ! -f ${whitelist} ]]; then
|
||||
touch ${whitelist}
|
||||
fi
|
||||
for dom in "${domList[@]}"; do
|
||||
# Logic : If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other
|
||||
if ${addmode}; then
|
||||
AddDomain "${dom}" "${listMain}"
|
||||
RemoveDomain "${dom}" "${listAlt}"
|
||||
if [[ "${listMain}" == "${whitelist}" || "${listMain}" == "${blacklist}" ]]; then
|
||||
RemoveDomain "${dom}" "${wildcardlist}"
|
||||
fi
|
||||
else
|
||||
RemoveDomain "${dom}" "${listMain}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
AddDomain() {
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
|
||||
bool=true
|
||||
#Is the domain in the list we want to add it to?
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
#domain not found in the whitelist file, add it!
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: Adding $1 to $list..."
|
||||
fi
|
||||
reload=true
|
||||
# Add it to the list we want to add it to
|
||||
echo "$1" >> "${list}"
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} already exists in ${list}, no need to add!"
|
||||
fi
|
||||
fi
|
||||
|
||||
elif [[ "${list}" == "${wildcardlist}" ]]; then
|
||||
|
||||
source "${piholeDir}/setupVars.conf"
|
||||
#Remove the /* from the end of the IPv4addr.
|
||||
IPV4_ADDRESS=${IPV4_ADDRESS%/*}
|
||||
IPV6_ADDRESS=${IPV6_ADDRESS}
|
||||
|
||||
bool=true
|
||||
#Is the domain in the list?
|
||||
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: Adding $1 to wildcard blacklist..."
|
||||
fi
|
||||
reload=true
|
||||
echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}"
|
||||
if [[ ${#IPV6_ADDRESS} > 0 ]] ; then
|
||||
echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}"
|
||||
fi
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} already exists in wildcard blacklist, no need to add!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveDomain() {
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
|
||||
bool=true
|
||||
#Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo "::: Removing $1 from $list..."
|
||||
# Busybox sed compatible case-insensitive domain removal
|
||||
sed -i "$(grep -in "^${domain}$" ${list} | awk -F':' '{print $1}' | tr '\n' ',' | sed 's/,$/\n/')d" ${list}
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} does not exist in ${list}, no need to remove!"
|
||||
fi
|
||||
fi
|
||||
|
||||
elif [[ "${list}" == "${wildcardlist}" ]]; then
|
||||
|
||||
bool=true
|
||||
#Is it in the list?
|
||||
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo "::: Removing $1 from $list..."
|
||||
# Busybox sed compatible case-insensitive domain removal
|
||||
sed -i "$(grep -in "/${domain}/" ${list} | awk -F':' '{print $1}' | tr '\n' ',' | sed 's/,$/\n/')d" ${list}
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} does not exist in ${list}, no need to remove!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Reload() {
|
||||
# Reload hosts file
|
||||
pihole -g -sd
|
||||
}
|
||||
|
||||
Displaylist() {
|
||||
if [[ ${listMain} == ${whitelist} ]]; then
|
||||
string="gravity resistant domains"
|
||||
else
|
||||
string="domains caught in the sinkhole"
|
||||
fi
|
||||
verbose=false
|
||||
echo -e " Displaying $string \n"
|
||||
count=1
|
||||
while IFS= read -r RD; do
|
||||
echo "${count}: ${RD}"
|
||||
count=$((count+1))
|
||||
done < "${listMain}"
|
||||
exit 0;
|
||||
}
|
||||
|
||||
for var in "$@"; do
|
||||
case "${var}" in
|
||||
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
|
||||
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
|
||||
"-wild" | "wildcard" ) listMain="${wildcardlist}";;
|
||||
"-nr"| "--noreload" ) reload=false;;
|
||||
"-d" | "--delmode" ) addmode=false;;
|
||||
"-f" | "--force" ) force=true;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) Displaylist;;
|
||||
* ) HandleOther "${var}";;
|
||||
esac
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
PoplistFile
|
||||
|
||||
if ${reload}; then
|
||||
Reload
|
||||
fi
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/execlineb -S0
|
||||
|
||||
if { s6-test $# -eq 2 }
|
||||
|
||||
backtick -in FILENAME {
|
||||
pipeline { s6-echo "${1}" }
|
||||
tr "a-z" "A-Z"
|
||||
}
|
||||
import -u FILENAME
|
||||
|
||||
redirfd -w 1 /var/run/s6/container_environment/${FILENAME}
|
||||
s6-echo -n -- ${2}
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# Early DNS Startup for the gravity list process to use
|
||||
dnsmasq -7 /etc/dnsmasq.d
|
||||
|
||||
/start.sh
|
||||
gravity.sh
|
||||
|
||||
# Done with DNS, let s6 services start up properly configured dns now
|
||||
killall -9 dnsmasq
|
|
@ -0,0 +1 @@
|
|||
/etc/resolv.conf false doesntexist,0:1000 0664 0664
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
s6-echo "Starting crond"
|
||||
|
||||
exec -c
|
||||
fdmove -c 2 1 /usr/sbin/cron -f
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
kill -9 $(pgrep dnsmasq)
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting dnsmasq"
|
||||
|
||||
s6-setuidgid root dnsmasq -7 /etc/dnsmasq.d --no-daemon
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting lighttpd"
|
||||
|
||||
lighttpd -D -f /etc/lighttpd/lighttpd.conf
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/with-contenv bash
|
||||
|
||||
s6-echo "Starting pihole-FTL"
|
||||
pihole-FTL no-daemon
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/with-contenv sh
|
||||
|
||||
#
|
||||
# This script will determine the network IP of the container.
|
||||
#
|
||||
# Return format should be a single IP address.
|
||||
#
|
||||
|
||||
# Default to using the value of the $HOSTNAME ENV variable.
|
||||
getent hosts ${1:-$HOSTNAME} | awk '{print $1}'
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/execlineb -S0
|
||||
|
||||
if { s6-test $# -eq 2 }
|
||||
|
||||
backtick -in FILENAME {
|
||||
pipeline { s6-echo "${1}" }
|
||||
tr "a-z" "A-Z"
|
||||
}
|
||||
import -u FILENAME
|
||||
|
||||
redirfd -w 1 /var/run/s6/container_environment/${FILENAME}
|
||||
s6-echo -n -- ${2}
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
# This script patches all service commands into the appropriate s6- commands
|
||||
# pi-hole upstream scripts need a 'service' interface. why not systemd? docker said so.
|
||||
start() {
|
||||
s6-svc -wU -u -T2500 /var/run/s6/services/$service
|
||||
}
|
||||
|
||||
stop() {
|
||||
s6-svc -wD -d -T2500 /var/run/s6/services/$service
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
#s6-svc -t -wR -T5000 /var/run/s6/services/$service
|
||||
}
|
||||
|
||||
status() {
|
||||
s6-svstat /var/run/s6/services/$service
|
||||
}
|
||||
|
||||
service="$1"
|
||||
command="$2"
|
||||
|
||||
if [[ ! -d "/var/run/s6/services/$service" ]] ; then
|
||||
echo "s6 service not found for $service, exiting..."
|
||||
exit
|
||||
fi;
|
||||
|
||||
${command} "${service}"
|
5
start.sh
5
start.sh
|
@ -16,7 +16,7 @@ export IPv6
|
|||
. /bash_functions.sh
|
||||
|
||||
echo " ::: Starting docker specific setup for docker diginc/pi-hole"
|
||||
validate_env
|
||||
validate_env || exit 1
|
||||
prepare_setup_vars
|
||||
change_setting "IPV4_ADDRESS" "$ServerIP"
|
||||
change_setting "IPV6_ADDRESS" "$ServerIPv6"
|
||||
|
@ -27,5 +27,6 @@ setup_dnsmasq_hostnames "$ServerIP" "$ServerIPv6" "$HOSTNAME"
|
|||
setup_ipv4_ipv6
|
||||
test_configs
|
||||
test_framework_stubbing
|
||||
echo "::: Docker start setup complete - beginning s6 services"
|
||||
|
||||
docker_main "$IMAGE"
|
||||
# s6's init takes care of running services now, no more main start services function
|
||||
|
|
|
@ -10,12 +10,14 @@ check_output = testinfra.get_backend(
|
|||
def DockerGeneric(request, args, image, cmd):
|
||||
assert 'docker' in check_output('id'), "Are you in the docker group?"
|
||||
if 'diginc/pi-hole' in image:
|
||||
args += " -v /dev/null:/etc/pihole/adlists.default -e PYTEST=\"True\""
|
||||
args += " --dns 127.0.0.1 -v /dev/null:/etc/.pihole/adlists.default -e PYTEST=\"True\""
|
||||
docker_run = "docker run -d {} {} {}".format(args, image, cmd)
|
||||
print docker_run
|
||||
docker_id = check_output(docker_run)
|
||||
|
||||
def teardown():
|
||||
check_output("docker rm -f %s", docker_id)
|
||||
check_output("docker logs {}".format(docker_id))
|
||||
check_output("docker rm -f {}".format(docker_id))
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
docker_container = testinfra.get_backend("docker://" + docker_id)
|
||||
|
@ -42,7 +44,7 @@ def Docker(request, args, image, cmd):
|
|||
''' One-off Docker container run '''
|
||||
return DockerGeneric(request, args, image, cmd)
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def DockerPersist(request, persist_args, persist_image, persist_cmd, Dig):
|
||||
''' Persistent Docker container for multiple tests '''
|
||||
persistent_container = DockerGeneric(request, persist_args, persist_image, persist_cmd)
|
||||
|
@ -52,7 +54,7 @@ def DockerPersist(request, persist_args, persist_image, persist_cmd, Dig):
|
|||
|
||||
@pytest.fixture()
|
||||
def args(request):
|
||||
return '-e ServerIP="192.168.100.2"'
|
||||
return '-e ServerIP="127.0.0.1" -e ServerIPv6="::1"'
|
||||
|
||||
@pytest.fixture(params=['alpine', 'debian'])
|
||||
def tag(request):
|
||||
|
@ -68,27 +70,27 @@ def image(request, tag):
|
|||
|
||||
@pytest.fixture()
|
||||
def cmd(request):
|
||||
return '/start.sh'
|
||||
return 'tail -f /dev/null'
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def persist_args(request):
|
||||
return '-e ServerIP="192.168.100.2"'
|
||||
return '-e ServerIP="127.0.0.1" -e ServerIPv6="::1"'
|
||||
|
||||
@pytest.fixture(scope='session', params=['alpine', 'debian'])
|
||||
@pytest.fixture(scope='module', params=['alpine', 'debian'])
|
||||
def persist_tag(request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def persist_webserver(request, persist_tag):
|
||||
return WEB_SERVER[persist_tag]
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def persist_image(request, persist_tag):
|
||||
return 'diginc/pi-hole:{}'.format(persist_tag)
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def persist_cmd(request):
|
||||
return '/start.sh'
|
||||
return 'tail -f /dev/null'
|
||||
|
||||
@pytest.fixture
|
||||
def Slow():
|
||||
|
@ -96,7 +98,7 @@ def Slow():
|
|||
Run a slow check, check if the state is correct for `timeout` seconds.
|
||||
"""
|
||||
import time
|
||||
def slow(check, timeout=5):
|
||||
def slow(check, timeout=20):
|
||||
timeout_at = time.time() + timeout
|
||||
while True:
|
||||
try:
|
||||
|
@ -110,7 +112,7 @@ def Slow():
|
|||
return
|
||||
return slow
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='module')
|
||||
def Dig(request):
|
||||
''' separate container to link to pi-hole and perform lookups '''
|
||||
''' a docker pull is faster than running an install of dnsutils '''
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
import pytest
|
||||
import re
|
||||
|
||||
# Override these docker command pieces to minimize parameter repititon
|
||||
@pytest.fixture()
|
||||
def cmd(request):
|
||||
return 'tail -f /dev/null'
|
||||
DEFAULTARGS = '-e ServerIP="127.0.0.1" '
|
||||
|
||||
@pytest.mark.parametrize('args,expected_ipv6,expected_stdout', [
|
||||
('-e ServerIP="1.2.3.4"', True, 'IPv4 and IPv6'),
|
||||
('-e ServerIP="1.2.3.4" -e "IPv6=True"', True, 'IPv4 and IPv6'),
|
||||
('-e ServerIP="1.2.3.4" -e "IPv6=False"', False, 'IPv4'),
|
||||
('-e ServerIP="1.2.3.4" -e "IPv6=foobar"', False, 'IPv4'),
|
||||
(DEFAULTARGS, True, 'IPv4 and IPv6'),
|
||||
(DEFAULTARGS + '-e "IPv6=True"', True, 'IPv4 and IPv6'),
|
||||
(DEFAULTARGS + '-e "IPv6=False"', False, 'IPv4'),
|
||||
(DEFAULTARGS + '-e "IPv6=foobar"', False, 'IPv4'),
|
||||
])
|
||||
def test_IPv6_not_True_removes_ipv6(Docker, tag, args, expected_ipv6, expected_stdout):
|
||||
''' When a user overrides IPv6=True they only get IPv4 listening webservers '''
|
||||
IPV6_LINE = { 'alpine': 'listen \[::\]:80',
|
||||
IPV6_LINE = { 'alpine': 'listen [::]:80 default_server',
|
||||
'debian': 'use-ipv6.pl' }
|
||||
WEB_CONFIG = { 'alpine': '/etc/nginx/nginx.conf',
|
||||
'debian': '/etc/lighttpd/lighttpd.conf' }
|
||||
|
||||
function = Docker.run('. /bash_functions.sh ; setup_ipv4_ipv6')
|
||||
assert "Using {}".format(expected_stdout) in function.stdout
|
||||
ipv6 = Docker.run('grep -q \'{}\' {}'.format(IPV6_LINE[tag], WEB_CONFIG[tag])).rc == 0
|
||||
assert ipv6 == expected_ipv6
|
||||
config = Docker.run('cat {}'.format( WEB_CONFIG[tag])).stdout
|
||||
assert (IPV6_LINE[tag] in config) == expected_ipv6
|
||||
|
||||
@pytest.mark.parametrize('args, expected_stdout, dns1, dns2', [
|
||||
('-e ServerIP="1.2.3.4"', 'default DNS', '8.8.8.8', '8.8.4.4' ),
|
||||
|
@ -53,8 +50,8 @@ def test_DNS_interface_override_defaults(Docker, args, expected_stdout, expected
|
|||
assert expected_config_line + '\n' == docker_dns_interface
|
||||
|
||||
expected_debian_lines = [
|
||||
'"VIRTUAL_HOST" => "192.168.100.2"',
|
||||
'"ServerIP" => "192.168.100.2"',
|
||||
'"VIRTUAL_HOST" => "127.0.0.1"',
|
||||
'"ServerIP" => "127.0.0.1"',
|
||||
'"PHP_ERROR_LOG" => "/var/log/lighttpd/error.log"'
|
||||
]
|
||||
@pytest.mark.parametrize('tag,expected_lines,repeat_function', [
|
||||
|
@ -80,13 +77,13 @@ def test_webPassword_env_assigns_password_to_file(Docker, args, secure, setupVar
|
|||
''' When a user sets webPassword env the admin password gets set to that '''
|
||||
function = Docker.run('. /bash_functions.sh ; eval `grep setup_web_password /start.sh`')
|
||||
if secure and 'WEBPASSWORD' not in args:
|
||||
assert 'Assigning random password' in function.stdout
|
||||
assert 'assigning random password' in function.stdout.lower()
|
||||
else:
|
||||
assert 'Assigning random password' not in function.stdout
|
||||
assert 'assigning random password' not in function.stdout.lower()
|
||||
|
||||
if secure:
|
||||
assert 'New password set' in function.stdout
|
||||
assert 'new password set' in function.stdout.lower()
|
||||
assert Docker.run('grep -q \'{}\' {}'.format(setupVarsHash, '/etc/pihole/setupVars.conf')).rc == 0
|
||||
else:
|
||||
assert 'Password removed' in function.stdout
|
||||
assert 'password removed' in function.stdout.lower()
|
||||
assert Docker.run('grep -q \'^WEBPASSWORD=$\' /etc/pihole/setupVars.conf').rc == 0
|
||||
|
|
|
@ -5,23 +5,20 @@ def start_cmd():
|
|||
''' broken by default, required override '''
|
||||
return None
|
||||
|
||||
START_DNS_STDOUT = {
|
||||
'alpine': '',
|
||||
'debian': 'Restarting DNS forwarder and DHCP server: dnsmasq.\n'
|
||||
}
|
||||
@pytest.fixture
|
||||
def RunningPiHole(DockerPersist, Slow, persist_webserver, persist_tag, start_cmd):
|
||||
''' Override the RunningPiHole to run and check for success of a
|
||||
dnsmasq start based `pihole` script command '''
|
||||
#print DockerPersist.run('ps -ef').stdout
|
||||
Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0)
|
||||
Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver) ).rc == 0)
|
||||
Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver)).rc == 0)
|
||||
oldpid = DockerPersist.run('pidof dnsmasq')
|
||||
cmd = DockerPersist.run('pihole {}'.format(start_cmd))
|
||||
Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0)
|
||||
newpid = DockerPersist.run('pidof dnsmasq')
|
||||
for pid in [oldpid, newpid]:
|
||||
assert pid != ''
|
||||
# ensure a new pid for dnsmasq appeared
|
||||
# ensure a new pid for dnsmasq appeared due to service restart
|
||||
assert oldpid != newpid
|
||||
assert cmd.rc == 0
|
||||
# Save out cmd result to check different stdout of start/enable/disable
|
||||
|
@ -34,8 +31,8 @@ def test_pihole_start_cmd(RunningPiHole, start_cmd, persist_tag):
|
|||
assert RunningPiHole.cmd.stdout == START_DNS_STDOUT[persist_tag]
|
||||
|
||||
@pytest.mark.parametrize('start_cmd,hostname,expected_ip', [
|
||||
('enable', 'pi.hole', '192.168.100.2'),
|
||||
('disable', 'pi.hole', '192.168.100.2'),
|
||||
('enable', 'pi.hole', '127.0.0.1'),
|
||||
('disable', 'pi.hole', '127.0.0.1'),
|
||||
])
|
||||
def test_pihole_start_cmd(RunningPiHole, Dig, persist_tag, start_cmd, hostname, expected_ip):
|
||||
''' the start_cmd tests are all built into the RunningPiHole fixture in this file '''
|
||||
|
@ -43,5 +40,5 @@ def test_pihole_start_cmd(RunningPiHole, Dig, persist_tag, start_cmd, hostname,
|
|||
lookup = RunningPiHole.dig.run(dig_cmd).stdout.rstrip('\n')
|
||||
assert lookup == expected_ip
|
||||
|
||||
stdout = "::: Blocking has been {}d!\n{}".format(start_cmd, START_DNS_STDOUT[persist_tag])
|
||||
stdout = "::: Blocking has been {}d!\n".format(start_cmd)
|
||||
assert RunningPiHole.cmd.stdout == stdout
|
||||
|
|
|
@ -7,7 +7,7 @@ run_local = testinfra.get_backend(
|
|||
|
||||
def test_scripts_pass_shellcheck():
|
||||
''' Make sure shellcheck does not find anything wrong with our shell scripts '''
|
||||
shellcheck = "find . -name '*.sh' | while read file; do shellcheck $file; done;"
|
||||
shellcheck = "find . -name '*.sh' -a ! ( -name 'list.sh' ) | while read file; do shellcheck -e SC1008 $file; done;"
|
||||
results = run_local(shellcheck)
|
||||
print results.stdout
|
||||
assert '' == results.stdout
|
||||
|
|
|
@ -14,7 +14,6 @@ def test_pihole_default_run_command(Docker, tag):
|
|||
assert False, '{}: Couldn\'t find proc {}'.format(tag, expected_proc)
|
||||
|
||||
@pytest.mark.parametrize('args', [ '' ])
|
||||
@pytest.mark.parametrize('cmd', [ 'tail -f /dev/null' ])
|
||||
def test_ServerIP_missing_triggers_start_error(Docker):
|
||||
''' When args to docker are empty start.sh exits saying ServerIP is required '''
|
||||
start = Docker.run('/start.sh')
|
||||
|
@ -22,8 +21,20 @@ def test_ServerIP_missing_triggers_start_error(Docker):
|
|||
assert start.rc == 1
|
||||
assert error_msg in start.stdout
|
||||
|
||||
@pytest.mark.parametrize('args,error_msg,expect_rc', [
|
||||
('-e ServerIP="1.2.3.z"', "ServerIP Environment variable (1.2.3.z) doesn't appear to be a valid IPv4 address",1),
|
||||
('-e ServerIP="1.2.3.4" -e ServerIPv6="1234:1234:1234:ZZZZ"', "Environment variable (1234:1234:1234:ZZZZ) doesn't appear to be a valid IPv6 address",1),
|
||||
('-e ServerIP="1.2.3.4" -e ServerIPv6="kernel"', "WARNING: You passed in IPv6 with a value of 'kernel'",0),
|
||||
])
|
||||
def test_ServerIP_invalid_IPs_triggers_exit_error(Docker, error_msg, expect_rc):
|
||||
''' When args to docker are empty start.sh exits saying ServerIP is required '''
|
||||
start = Docker.run('/start.sh')
|
||||
assert start.rc == expect_rc
|
||||
assert 'ERROR' in start.stdout
|
||||
assert error_msg in start.stdout
|
||||
|
||||
@pytest.mark.parametrize('hostname,expected_ip', [
|
||||
('pi.hole', '192.168.100.2'),
|
||||
('pi.hole', '127.0.0.1'),
|
||||
('google-public-dns-a.google.com', '8.8.8.8'),
|
||||
('b.resolvers.Level3.net', '4.2.2.2')
|
||||
])
|
||||
|
@ -37,28 +48,32 @@ def test_indecies_are_present(RunningPiHole):
|
|||
File('/var/www/html/pihole/index.html').exists
|
||||
File('/var/www/html/pihole/index.js').exists
|
||||
|
||||
@pytest.mark.parametrize('ip', [ 'localhost', '[::]' ])
|
||||
@pytest.mark.parametrize('addr', [ 'testblock.pi-hole.local' ])
|
||||
@pytest.mark.parametrize('url', [ '/', '/index.html', '/any.html' ] )
|
||||
def test_html_index_requests_load_as_expected(RunningPiHole, ip, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(ip, url)
|
||||
def test_html_index_requests_load_as_expected(RunningPiHole, Slow, addr, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(addr, url)
|
||||
http_rc = RunningPiHole.run(command)
|
||||
assert RunningPiHole.run('grep -q "Access to the following site has been blocked" /tmp/curled_file ').rc == 0
|
||||
assert http_rc.rc == 0
|
||||
assert int(http_rc.stdout) == 200
|
||||
page_contents = RunningPiHole.run('cat /tmp/curled_file ').stdout
|
||||
assert 'blocked' in page_contents
|
||||
|
||||
@pytest.mark.parametrize('ip', [ '127.0.0.1', '[::]' ] )
|
||||
@pytest.mark.parametrize('addr', [ 'testblock.pi-hole.local' ])
|
||||
@pytest.mark.parametrize('url', [ '/index.js', '/any.js'] )
|
||||
def test_javascript_requests_load_as_expected(RunningPiHole, ip, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(ip, url)
|
||||
def test_javascript_requests_load_as_expected(RunningPiHole, addr, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(addr, url)
|
||||
http_rc = RunningPiHole.run(command)
|
||||
assert RunningPiHole.run('md5sum /tmp/curled_file /var/www/html/pihole/index.js').rc == 0
|
||||
assert http_rc.rc == 0
|
||||
assert int(http_rc.stdout) == 200
|
||||
assert RunningPiHole.run('md5sum /tmp/curled_file /var/www/html/pihole/index.js').rc == 0
|
||||
|
||||
# IPv6 checks aren't passing CORS, removed :(
|
||||
@pytest.mark.parametrize('ip', [ 'localhost' ] )
|
||||
@pytest.mark.parametrize('addr', [ 'localhost' ] )
|
||||
@pytest.mark.parametrize('url', [ '/admin/', '/admin/index.php' ] )
|
||||
def test_admin_requests_load_as_expected(RunningPiHole, ip, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(ip, url)
|
||||
def test_admin_requests_load_as_expected(RunningPiHole, addr, url):
|
||||
command = 'curl -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(addr, url)
|
||||
http_rc = RunningPiHole.run(command)
|
||||
assert http_rc.rc == 0
|
||||
assert int(http_rc.stdout) == 200
|
||||
assert RunningPiHole.run('wc -l /tmp/curled_file ') > 10
|
||||
assert RunningPiHole.run('grep -q "Content-Security-Policy" /tmp/curled_file ').rc == 0
|
||||
|
|
Loading…
Reference in New Issue