This commit is too big, but it works

* Tests are passing, hopefully consistently
* FTL pulling from official releases
  * thanks @DL6ER for the musl-libc build
* Thanks middleagedman for the IPv6 fixes
* Thanks everyone for patience while I get this release working!
This commit is contained in:
diginc 2017-05-13 17:03:24 -05:00
parent 8d0ea48ecd
commit b9095bc123
14 changed files with 91 additions and 53 deletions

View File

@ -17,8 +17,10 @@ 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
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

View File

@ -20,7 +20,6 @@ ENTRYPOINT [ "/init" ]
ADD s6/alpine-root /
COPY s6/service /usr/local/bin/service
COPY pihole-FTL /usr/bin/pihole-FTL
# Things installer did and fix alpine+nginx differences
ENV WEBLOGDIR /var/log/nginx
@ -32,6 +31,7 @@ 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 && \

View File

@ -2,6 +2,7 @@
. /opt/pihole/webpage.sh
setupVars="$setupVars"
ServerIP="$ServerIP"
ServerIPv6="$ServerIPv6"
IPv6="$IPv6"
prepare_setup_vars() {
@ -148,7 +149,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"
@ -176,7 +177,7 @@ test_configs_alpine() {
echo -n '::: Testing DNSmasq config: '
dnsmasq --test -7 /etc/dnsmasq.d
echo -n '::: Testing PHP-FPM config: '
php-fpm -t
php-fpm5 -t
echo -n '::: Testing NGINX config: '
nginx -t
set +e
@ -184,5 +185,9 @@ 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
}

View File

@ -1,8 +1,8 @@
#!/bin/bash -x
#!/bin/bash -ex
mkdir -p /etc/pihole/
export CORE_TAG='v3.0.1'
export WEB_TAG='v3.0.1'
export FTL_TAG='v2.6.2'
export FTL_TAG='v2.7'
# Make pihole scripts fail searching for `systemctl`,
# which fails pretty miserably in docker compared to `service`
@ -25,6 +25,8 @@ if [[ "$IMAGE" == 'alpine' ]] ; then
# 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
@ -45,7 +47,7 @@ elif [[ "$IMAGE" == 'alpine' ]] ; then
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
@ -75,4 +77,4 @@ mv "${tmpLog}" "${instalLogLoc}"
# Fix dnsmasq in docker
grep -q '^user=root' || echo -e '\nuser=root' >> /etc/dnsmasq.conf
echo 'done'
echo 'Docker install successful'

View File

@ -1,4 +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

View File

@ -12,13 +12,15 @@ 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;
location ~ /(pihole|admin)/*.php$ {
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;

View File

@ -0,0 +1,4 @@
#!/usr/bin/with-contenv bash
s6-echo "Starting pihole-FTL"
pihole-FTL no-daemon

View File

@ -1,4 +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

View File

@ -0,0 +1,4 @@
#!/usr/bin/with-contenv bash
s6-echo "Starting pihole-FTL"
pihole-FTL no-daemon

View File

@ -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"
# s6's init takes care of running services now, no more main start services function

View File

@ -10,9 +10,9 @@ 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 += " -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():
@ -44,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)
@ -54,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):
@ -70,27 +70,27 @@ def image(request, tag):
@pytest.fixture()
def cmd(request):
return ''
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 ''
return 'tail -f /dev/null'
@pytest.fixture
def Slow():
@ -98,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:
@ -112,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 '''

View File

@ -1,28 +1,30 @@
import pytest
import re
DEFAULTARGS = '-e ServerIP="127.0.0.1" '
# Override these docker command pieces to minimize parameter repititon
@pytest.fixture()
def cmd(request):
return 'tail -f /dev/null'
@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 +55,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 +82,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

View File

@ -33,8 +33,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 '''

View File

@ -23,7 +23,7 @@ def test_ServerIP_missing_triggers_start_error(Docker):
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 +37,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