|
75 | 75 | if [ -f /etc/unbound/unbound.conf.d/pi-hole.conf ] |
76 | 76 | then |
77 | 77 | rm -f /etc/unbound/unbound.conf.d/pi-hole.conf |
| 78 | + # Remove the daily restart cron job |
| 79 | + crontab -u root -l 2>/dev/null | grep -v "restart unbound" | crontab -u root - |
78 | 80 | rm -f /etc/systemd/system/unbound.service.d/ncvm-pihole.conf |
79 | 81 | rmdir /etc/systemd/system/unbound.service.d &>/dev/null |
80 | 82 | systemctl daemon-reload |
81 | | - if is_this_installed unbound |
| 83 | + if is_this_installed unbound || is_this_installed unbound-anchor |
82 | 84 | then |
83 | | - apt-get purge unbound -y |
| 85 | + apt-get purge unbound unbound-anchor -y |
84 | 86 | apt-get autoremove -y |
85 | 87 | fi |
86 | 88 | fi |
@@ -372,11 +374,36 @@ ufw allow "$PIHOLE_PROXY_PORT"/tcp comment 'Pi-hole Web' &>/dev/null |
372 | 374 | if [ "$UNBOUND" = "yes" ] |
373 | 375 | then |
374 | 376 | # Install unbound. We do not use install_if_not here, since it installs |
375 | | - # with RUNLEVEL=1, which skips the postinst that creates the DNSSEC anchor. |
376 | | - if ! is_this_installed unbound |
| 377 | + # with RUNLEVEL=1, which skips parts of the package setup. |
| 378 | + if ! is_this_installed unbound || ! is_this_installed unbound-anchor |
377 | 379 | then |
378 | 380 | apt-get update -q4 & spinner_loading |
379 | | - check_command apt-get install unbound -y |
| 381 | + check_command apt-get install unbound unbound-anchor -y |
| 382 | + fi |
| 383 | + |
| 384 | + # Ubuntu makes unbound listen on 127.0.0.1:53 via resolvconf, which |
| 385 | + # conflicts with port 53 that the Pi-hole container publishes |
| 386 | + systemctl disable --now unbound-resolvconf.service &>/dev/null |
| 387 | + rm -f /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf |
| 388 | + |
| 389 | + # The DNSSEC root trust anchor is not always created by the package, |
| 390 | + # but unbound refuses to start without it |
| 391 | + if ! [ -f /var/lib/unbound/root.key ] |
| 392 | + then |
| 393 | + print_text_in_color "$ICyan" "Creating the DNSSEC root trust anchor..." |
| 394 | + mkdir -p /var/lib/unbound |
| 395 | + # It returns 1 when it had to bootstrap the key from its built-in |
| 396 | + # copy, which is the expected case on a fresh installation |
| 397 | + unbound-anchor -a /var/lib/unbound/root.key || true |
| 398 | + chown unbound:unbound /var/lib/unbound/root.key &>/dev/null |
| 399 | + if ! [ -f /var/lib/unbound/root.key ] |
| 400 | + then |
| 401 | + msg_box "Could not create the DNSSEC root trust anchor in \ |
| 402 | +'/var/lib/unbound/root.key', which means that unbound cannot start. |
| 403 | +
|
| 404 | +Please report this to $ISSUES" |
| 405 | + exit 1 |
| 406 | + fi |
380 | 407 | fi |
381 | 408 |
|
382 | 409 | # unbound listens on the docker bridge gateway so that the container can |
@@ -438,25 +465,61 @@ After=docker.service |
438 | 465 | UNBOUND_SERVICE |
439 | 466 | systemctl daemon-reload |
440 | 467 |
|
441 | | - # Restart unbound |
| 468 | + # Allow the container to reach unbound on the docker bridge |
| 469 | + ufw allow in on docker0 to "$DOCKER_GATEWAY" port 5335 comment 'Pi-hole unbound' &>/dev/null |
| 470 | + |
| 471 | + # Restart unbound. A former failed start can latch the unit into a failed |
| 472 | + # state with 'start request repeated too quickly', which we clear first |
442 | 473 | print_text_in_color "$ICyan" "Restarting unbound..." |
443 | | - check_command systemctl restart unbound |
444 | | - countdown "Waiting for unbound to start... " 10 |
| 474 | + systemctl reset-failed unbound &>/dev/null |
| 475 | + systemctl restart unbound &>/dev/null |
| 476 | + |
| 477 | + # Wait for unbound to actually answer instead of guessing a delay, since |
| 478 | + # a restart can still end in a failed unit or a not yet ready resolver |
| 479 | + UNBOUND_READY=no |
| 480 | + for _ in $(seq 1 30) |
| 481 | + do |
| 482 | + if docker exec pihole dig +time=2 +tries=1 @"$DOCKER_GATEWAY" -p 5335 \ |
| 483 | +nextcloud.com &>/dev/null |
| 484 | + then |
| 485 | + UNBOUND_READY=yes |
| 486 | + break |
| 487 | + fi |
| 488 | + sleep 1 |
| 489 | + done |
| 490 | + if [ "$UNBOUND_READY" != "yes" ] |
| 491 | + then |
| 492 | + msg_box "unbound did not start correctly and does not answer queries. |
| 493 | +
|
| 494 | +Please report this to $ISSUES" |
| 495 | + exit 1 |
| 496 | + fi |
445 | 497 |
|
446 | | - # Testing DNSSEC |
447 | | - install_if_not dnsutils |
448 | | - if ! dig sigfail.verteiltesysteme.net @"$DOCKER_GATEWAY" -p 5335 | grep -q "SERVFAIL" |
| 498 | + # Testing DNSSEC from inside the container, since unbound refuses queries |
| 499 | + # from the host. A validated answer carries the 'ad' flag. |
| 500 | + if ! docker exec pihole dig +time=10 +tries=1 @"$DOCKER_GATEWAY" -p 5335 \ |
| 501 | +sigok.verteiltesysteme.net | grep -q "flags:.* ad[;,]" |
449 | 502 | then |
450 | | - msg_box "Something went wrong while testing SERVFAIL. |
| 503 | + msg_box "Something went wrong while testing DNSSEC validation. |
| 504 | +unbound did not return an authenticated answer for a signed domain. |
| 505 | +
|
451 | 506 | Please report this to $ISSUES" |
452 | | - elif ! dig sigok.verteiltesysteme.net @"$DOCKER_GATEWAY" -p 5335 | grep -q "NOERROR" |
| 507 | + # A domain with a broken signature must not resolve. unbound either answers |
| 508 | + # with SERVFAIL or doesn't answer at all while it retries the nameservers |
| 509 | + elif docker exec pihole dig +time=10 +tries=1 @"$DOCKER_GATEWAY" -p 5335 \ |
| 510 | +sigfail.verteiltesysteme.net | grep -q "flags:.* ad[;,]" |
453 | 511 | then |
454 | | - msg_box "Something went wrong while testing NOERROR. |
| 512 | + msg_box "Something went wrong while testing DNSSEC validation. |
| 513 | +unbound validated a domain with a broken signature. |
| 514 | +
|
455 | 515 | Please report this to $ISSUES" |
456 | 516 | fi |
457 | 517 |
|
458 | | - # Allow the container to reach unbound on the docker bridge |
459 | | - ufw allow in on docker0 to "$DOCKER_GATEWAY" port 5335 comment 'Pi-hole unbound' &>/dev/null |
| 518 | + # Restart unbound daily, since a failed start at boot latches the unit and |
| 519 | + # would leave the Pi-hole without its upstream DNS server until fixed by hand |
| 520 | + crontab -u root -l 2>/dev/null | grep -v "restart unbound" | crontab -u root - |
| 521 | + crontab -u root -l 2>/dev/null | { cat; echo "0 4 * * * systemctl reset-failed \ |
| 522 | +unbound && systemctl restart unbound"; } | crontab -u root - |
460 | 523 |
|
461 | 524 | # Configure Pi-hole to use unbound as its upstream DNS server |
462 | 525 | print_text_in_color "$ICyan" "Configuring Pi-hole to use unbound..." |
@@ -526,13 +589,4 @@ which will generate and show you a new password while keeping all your settings. |
526 | 589 | Please also note that the DHCP functionality of Pi-hole is not enabled since the \ |
527 | 590 | container doesn't run in the host network." |
528 | 591 |
|
529 | | -# Inform about updates |
530 | | -msg_box "Concerning updates: |
531 | | -Pi-hole runs in a Docker container, which means that you can update it \ |
532 | | -by running the following commands: |
533 | | -'sudo docker pull pihole/pihole:latest' |
534 | | -and afterwards running this script again and choosing 'Reinstall'. |
535 | | -
|
536 | | -Your settings and statistics in '$PIHOLE_DIR' will be kept in that process." |
537 | | - |
538 | 592 | exit |
0 commit comments