Merge pull request #130 from diginc/v3_final_touches

V3 Final touches for documentation & validating Server IP env vars
This commit is contained in:
Adam Hill 2017-05-19 11:36:53 -05:00 committed by GitHub
commit edad7e57f3
10 changed files with 93 additions and 28 deletions

View File

@ -20,15 +20,21 @@ IP_LOOKUP="$(ip route get 8.8.8.8 | awk '{ print $NF; exit }')" # May not work
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
docker run -p 53:53/tcp -p 53:53/udp -p 80:80 --cap-add=NET_ADMIN -e ServerIP="$IP" -e ServerIPv6="$IPv6" --restart=always --name pihole -d $IMAGE
# 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 create \
--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
@ -36,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
@ -54,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).
@ -105,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.

View File

@ -7,7 +7,7 @@ ENV PATH /opt/pihole:${PATH}
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.18.1.5/s6-overlay-amd64.tar.gz
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-amd64.tar.gz
RUN apk upgrade --update && \
apk add bind-tools wget curl bash libcap && \
@ -55,5 +55,6 @@ EXPOSE 80
ENV S6_LOGGING 0
ENV S6_KEEP_ENV 1
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
SHELL ["/bin/bash", "-c"]

View File

@ -14,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() {

View File

@ -9,7 +9,7 @@ ENV PATH /opt/pihole:${PATH}
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.18.1.5/s6-overlay-armhf.tar.gz
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-armhf.tar.gz
RUN apt-get update && \
apt-get install -y wget curl net-tools cron && \
@ -38,5 +38,8 @@ EXPOSE 80
ENV S6_LOGGING 0
ENV S6_KEEP_ENV 1
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
SHELL ["/bin/bash", "-c"]
RUN [ "cross-build-end" ]

View File

@ -7,7 +7,7 @@ ENV PATH /opt/pihole:${PATH}
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.18.1.5/s6-overlay-amd64.tar.gz
ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-amd64.tar.gz
RUN apt-get update && \
apt-get install -y wget curl net-tools cron && \
@ -36,5 +36,6 @@ EXPOSE 80
ENV S6_LOGGING 0
ENV S6_KEEP_ENV 1
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2
SHELL ["/bin/bash", "-c"]

View File

@ -1,12 +1,23 @@
#!/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 create \
--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 start pihole
sleep 2
docker logs pihole 2> /dev/null | grep 'password:'

View File

@ -42,6 +42,8 @@ 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 \
@ -72,6 +74,10 @@ 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}"

View File

@ -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"

View File

@ -13,8 +13,6 @@ def RunningPiHole(DockerPersist, Slow, persist_webserver, persist_tag, start_cmd
Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0)
Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver)).rc == 0)
oldpid = DockerPersist.run('pidof dnsmasq')
for service in [ 'dnsmasq', 'nginx', ]:
print DockerPersist.run('service {} status'.format(service))
cmd = DockerPersist.run('pihole {}'.format(start_cmd))
Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0)
newpid = DockerPersist.run('pidof dnsmasq')

View File

@ -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,6 +21,18 @@ 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', '127.0.0.1'),
('google-public-dns-a.google.com', '8.8.8.8'),