1255809Sdes#!/bin/sh 2255809Sdes#- 3255809Sdes# Copyright (c) 2013 Dag-Erling Sm��rgrav 4255809Sdes# All rights reserved. 5255809Sdes# 6255809Sdes# Redistribution and use in source and binary forms, with or without 7255809Sdes# modification, are permitted provided that the following conditions 8255809Sdes# are met: 9255809Sdes# 1. Redistributions of source code must retain the above copyright 10255809Sdes# notice, this list of conditions and the following disclaimer. 11255809Sdes# 2. Redistributions in binary form must reproduce the above copyright 12255809Sdes# notice, this list of conditions and the following disclaimer in the 13255809Sdes# documentation and/or other materials provided with the distribution. 14255809Sdes# 15255809Sdes# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16255809Sdes# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17255809Sdes# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18255809Sdes# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19255809Sdes# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20255809Sdes# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21255809Sdes# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22255809Sdes# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23255809Sdes# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24255809Sdes# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25255809Sdes# SUCH DAMAGE. 26255809Sdes# 27255809Sdes# $FreeBSD$ 28255809Sdes# 29255809Sdes 30255809Sdes# 31255809Sdes# Configuration variables 32255809Sdes# 33255809Sdesuser="" 34255809Sdesunbound_conf="" 35255809Sdesforward_conf="" 36269257Sdeslanzones_conf="" 37255809Sdesworkdir="" 38269257Sdesconfdir="" 39255809Sdeschrootdir="" 40255809Sdesanchor="" 41255809Sdespidfile="" 42255809Sdesresolv_conf="" 43255809Sdesresolvconf_conf="" 44255809Sdesservice="" 45255809Sdesstart_unbound="" 46255809Sdesforwarders="" 47255809Sdes 48255809Sdes# 49255809Sdes# Global variables 50255809Sdes# 51255809Sdesself=$(basename $(realpath "$0")) 52255809Sdesbkext=$(date "+%Y%m%d.%H%M%S") 53255809Sdes 54255809Sdes# 55255809Sdes# Set default values for unset configuration variables. 56255809Sdes# 57255809Sdesset_defaults() { 58255809Sdes : ${user:=unbound} 59255809Sdes : ${workdir:=/var/unbound} 60269257Sdes : ${confdir:=${workdir}/conf.d} 61255809Sdes : ${unbound_conf:=${workdir}/unbound.conf} 62255809Sdes : ${forward_conf:=${workdir}/forward.conf} 63269257Sdes : ${lanzones_conf:=${workdir}/lan-zones.conf} 64255809Sdes : ${anchor:=${workdir}/root.key} 65255809Sdes : ${pidfile:=/var/run/local_unbound.pid} 66255809Sdes : ${resolv_conf:=/etc/resolv.conf} 67255809Sdes : ${resolvconf_conf:=/etc/resolvconf.conf} 68255809Sdes : ${service:=local_unbound} 69255809Sdes : ${start_unbound:=yes} 70255809Sdes} 71255809Sdes 72255809Sdes# 73255809Sdes# Verify that the configuration files are inside the working 74255809Sdes# directory, and if so, set the chroot directory accordingly. 75255809Sdes# 76255809Sdesset_chrootdir() { 77255809Sdes chrootdir="${workdir}" 78269257Sdes for file in "${unbound_conf}" "${forward_conf}" \ 79269257Sdes "${lanzones_conf}" "${anchor}" ; do 80255809Sdes if [ "${file#${workdir%/}/}" = "${file}" ] ; then 81255809Sdes echo "warning: ${file} is outside ${workdir}" >&2 82255809Sdes chrootdir="" 83255809Sdes fi 84255809Sdes done 85255809Sdes if [ -z "${chrootdir}" ] ; then 86255809Sdes echo "warning: disabling chroot" >&2 87255809Sdes fi 88255809Sdes} 89255809Sdes 90255809Sdes# 91255809Sdes# Scan through /etc/resolv.conf looking for uncommented nameserver 92255809Sdes# lines that don't point to localhost and return their values. 93255809Sdes# 94255809Sdesget_nameservers() { 95255809Sdes while read line ; do 96255809Sdes local bareline=${line%%\#*} 97255809Sdes local key=${bareline%% *} 98255809Sdes local value=${bareline#* } 99255809Sdes case ${key} in 100255809Sdes nameserver) 101255809Sdes case ${value} in 102255809Sdes 127.0.0.1|::1|localhost|localhost.*) 103255809Sdes ;; 104255809Sdes *) 105255809Sdes echo "${value}" 106255809Sdes ;; 107255809Sdes esac 108255809Sdes ;; 109255809Sdes esac 110255809Sdes done 111255809Sdes} 112255809Sdes 113255809Sdes# 114255809Sdes# Scan through /etc/resolv.conf looking for uncommented nameserver 115255809Sdes# lines. Comment out any that don't point to localhost. Finally, 116255809Sdes# append a nameserver line that points to localhost, if there wasn't 117255809Sdes# one already, and enable the edns0 option. 118255809Sdes# 119255809Sdesgen_resolv_conf() { 120255809Sdes local localhost=no 121255809Sdes local edns0=no 122255809Sdes while read line ; do 123255809Sdes local bareline=${line%%\#*} 124255809Sdes local key=${bareline%% *} 125255809Sdes local value=${bareline#* } 126255809Sdes case ${key} in 127255809Sdes nameserver) 128255809Sdes case ${value} in 129255809Sdes 127.0.0.1|::1|localhost|localhost.*) 130255809Sdes localhost=yes 131255809Sdes ;; 132255809Sdes *) 133255809Sdes echo -n "# " 134255809Sdes ;; 135255809Sdes esac 136255809Sdes ;; 137255809Sdes options) 138255809Sdes case ${value} in 139255809Sdes *edns0*) 140255809Sdes edns0=yes 141255809Sdes ;; 142255809Sdes esac 143255809Sdes ;; 144255809Sdes esac 145255809Sdes echo "${line}" 146255809Sdes done 147255809Sdes if [ "${localhost}" = "no" ] ; then 148255809Sdes echo "nameserver 127.0.0.1" 149255809Sdes fi 150255809Sdes if [ "${edns0}" = "no" ] ; then 151255809Sdes echo "options edns0" 152255809Sdes fi 153255809Sdes} 154255809Sdes 155255809Sdes# 156255809Sdes# Generate resolvconf.conf so it updates forward.conf in addition to 157255809Sdes# resolv.conf. Note "in addition to" rather than "instead of", 158255809Sdes# because we still want it to update the domain name and search path 159255809Sdes# if they change. Setting name_servers to "127.0.0.1" ensures that 160255809Sdes# the libc resolver will try unbound first. 161255809Sdes# 162255809Sdesgen_resolvconf_conf() { 163255809Sdes echo "# Generated by $self" 164255826Sdes echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" 165255809Sdes echo "unbound_conf=\"${forward_conf}\"" 166255809Sdes echo "unbound_pid=\"${pidfile}\"" 167255809Sdes echo "unbound_service=\"${service}\"" 168255826Sdes # resolvconf(8) likes to restart rather than reload 169255826Sdes echo "unbound_restart=\"service ${service} reload\"" 170255809Sdes} 171255809Sdes 172255809Sdes# 173255809Sdes# Generate forward.conf 174255809Sdes# 175255809Sdesgen_forward_conf() { 176255809Sdes echo "# Generated by $self" 177269257Sdes echo "# Do not edit this file." 178255809Sdes echo "forward-zone:" 179255809Sdes echo " name: ." 180255809Sdes for forwarder ; do 181271760Sdes if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then 182255809Sdes echo " forward-addr: ${forwarder}" 183255809Sdes else 184255809Sdes echo " forward-host: ${forwarder}" 185255809Sdes fi 186255809Sdes done 187255809Sdes} 188255809Sdes 189255809Sdes# 190269257Sdes# Generate lan-zones.conf 191269257Sdes# 192269257Sdesgen_lanzones_conf() { 193269257Sdes echo "# Generated by $self" 194269257Sdes echo "# Do not edit this file." 195269257Sdes echo "server:" 196269257Sdes echo " # Unblock reverse lookups for LAN addresses" 197269257Sdes echo " unblock-lan-zones: yes" 198269257Sdes echo " domain-insecure: 10.in-addr.arpa." 199269257Sdes echo " domain-insecure: 127.in-addr.arpa." 200269257Sdes echo " domain-insecure: 16.172.in-addr.arpa." 201269257Sdes echo " domain-insecure: 17.172.in-addr.arpa." 202269257Sdes echo " domain-insecure: 18.172.in-addr.arpa." 203269257Sdes echo " domain-insecure: 19.172.in-addr.arpa." 204269257Sdes echo " domain-insecure: 20.172.in-addr.arpa." 205269257Sdes echo " domain-insecure: 21.172.in-addr.arpa." 206269257Sdes echo " domain-insecure: 22.172.in-addr.arpa." 207269257Sdes echo " domain-insecure: 23.172.in-addr.arpa." 208269257Sdes echo " domain-insecure: 24.172.in-addr.arpa." 209269257Sdes echo " domain-insecure: 25.172.in-addr.arpa." 210269257Sdes echo " domain-insecure: 26.172.in-addr.arpa." 211269257Sdes echo " domain-insecure: 27.172.in-addr.arpa." 212269257Sdes echo " domain-insecure: 28.172.in-addr.arpa." 213269257Sdes echo " domain-insecure: 29.172.in-addr.arpa." 214269257Sdes echo " domain-insecure: 30.172.in-addr.arpa." 215269257Sdes echo " domain-insecure: 31.172.in-addr.arpa." 216269257Sdes echo " domain-insecure: 168.192.in-addr.arpa." 217269257Sdes echo " domain-insecure: 254.169.in-addr.arpa." 218269257Sdes echo " domain-insecure: d.f.ip6.arpa." 219269257Sdes echo " domain-insecure: 8.e.ip6.arpa." 220269257Sdes echo " domain-insecure: 9.e.ip6.arpa." 221269257Sdes echo " domain-insecure: a.e.ip6.arpa." 222269257Sdes echo " domain-insecure: b.e.ip6.arpa." 223269257Sdes} 224269257Sdes 225269257Sdes# 226255809Sdes# Generate unbound.conf 227255809Sdes# 228255809Sdesgen_unbound_conf() { 229255809Sdes echo "# Generated by $self" 230255809Sdes echo "server:" 231255809Sdes echo " username: ${user}" 232255809Sdes echo " directory: ${workdir}" 233255809Sdes echo " chroot: ${chrootdir}" 234255809Sdes echo " pidfile: ${pidfile}" 235255809Sdes echo " auto-trust-anchor-file: ${anchor}" 236255809Sdes echo "" 237255809Sdes if [ -f "${forward_conf}" ] ; then 238255809Sdes echo "include: ${forward_conf}" 239255809Sdes fi 240269257Sdes if [ -f "${lanzones_conf}" ] ; then 241269257Sdes echo "include: ${lanzones_conf}" 242269257Sdes fi 243269257Sdes if [ -d "${confdir}" ] ; then 244269257Sdes echo "include: ${confdir}/*.conf" 245269257Sdes fi 246255809Sdes} 247255809Sdes 248255809Sdes# 249255809Sdes# Replace one file with another, making a backup copy of the first, 250255809Sdes# but only if the new file is different from the old. 251255809Sdes# 252255809Sdesreplace() { 253255809Sdes local file="$1" 254255809Sdes local newfile="$2" 255255809Sdes if [ ! -f "${file}" ] ; then 256255809Sdes echo "${file} created" 257255809Sdes mv "${newfile}" "${file}" 258255809Sdes elif ! cmp -s "${file}" "${newfile}" ; then 259255809Sdes local oldfile="${file}.${bkext}" 260255809Sdes echo "original ${file} saved as ${oldfile}" 261255809Sdes mv "${file}" "${oldfile}" 262255809Sdes mv "${newfile}" "${file}" 263255809Sdes else 264255809Sdes echo "${file} not modified" 265255809Sdes rm "${newfile}" 266255809Sdes fi 267255809Sdes} 268255809Sdes 269255809Sdes# 270255809Sdes# Print usage message and exit 271255809Sdes# 272255809Sdesusage() { 273255809Sdes exec >&2 274255809Sdes echo "usage: $self [options] [forwarder ...]" 275255809Sdes echo "options:" 276255809Sdes echo " -n do not start unbound" 277255809Sdes echo " -a path full path to trust anchor file" 278269257Sdes echo " -C path full path to additional configuration directory" 279269257Sdes echo " -c path full path to unbound configuration file" 280255809Sdes echo " -f path full path to forwarding configuration" 281255809Sdes echo " -p path full path to pid file" 282255809Sdes echo " -R path full path to resolvconf.conf" 283255809Sdes echo " -r path full path to resolv.conf" 284255809Sdes echo " -s service name of unbound service" 285255809Sdes echo " -u user user to run unbound as" 286255809Sdes echo " -w path full path to working directory" 287255809Sdes exit 1 288255809Sdes} 289255809Sdes 290255809Sdes# 291255809Sdes# Main 292255809Sdes# 293255809Sdesmain() { 294255809Sdes umask 022 295255809Sdes 296255809Sdes # 297255809Sdes # Parse and validate command-line options 298255809Sdes # 299269257Sdes while getopts "a:C:c:f:np:R:r:s:u:w:" option ; do 300255809Sdes case $option in 301255809Sdes a) 302255809Sdes anchor="$OPTARG" 303255809Sdes ;; 304269257Sdes C) 305269257Sdes confdir="$OPTARG" 306269257Sdes ;; 307255809Sdes c) 308255809Sdes unbound_conf="$OPTARG" 309255809Sdes ;; 310255809Sdes f) 311255809Sdes forward_conf="$OPTARG" 312255809Sdes ;; 313255809Sdes n) 314255809Sdes start_unbound="no" 315255809Sdes ;; 316255809Sdes p) 317255809Sdes pidfile="$OPTARG" 318255809Sdes ;; 319255809Sdes R) 320255809Sdes resolvconf_conf="$OPTARG" 321255809Sdes ;; 322255809Sdes r) 323255809Sdes resolv_conf="$OPTARG" 324255809Sdes ;; 325255809Sdes s) 326255809Sdes service="$OPTARG" 327255809Sdes ;; 328255809Sdes u) 329255809Sdes user="$OPTARG" 330255809Sdes ;; 331255809Sdes w) 332255809Sdes workdir="$OPTARG" 333255809Sdes ;; 334255809Sdes *) 335255809Sdes usage 336255809Sdes ;; 337255809Sdes esac 338255809Sdes done 339255809Sdes shift $((OPTIND-1)) 340255809Sdes set_defaults 341255809Sdes 342255809Sdes # 343255809Sdes # Get the list of forwarders, either from the command line or 344255809Sdes # from resolv.conf. 345255809Sdes # 346255809Sdes forwarders="$@" 347255809Sdes if [ -z "$forwarders" ] ; then 348255809Sdes echo "Extracting forwarders from ${resolv_conf}." 349255809Sdes forwarders=$(get_nameservers <"${resolv_conf}") 350255809Sdes fi 351255809Sdes 352255809Sdes # 353255809Sdes # Generate forward.conf. 354255809Sdes # 355255809Sdes if [ -z "${forwarders}" ] ; then 356255809Sdes echo -n "No forwarders found in ${resolv_conf##*/}, " 357255809Sdes if [ -f "${forward_conf}" ] ; then 358255809Sdes echo "using existing ${forward_conf##*/}." 359255809Sdes else 360255809Sdes echo "unbound will recurse." 361255809Sdes fi 362255809Sdes else 363255809Sdes local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") 364255809Sdes gen_forward_conf ${forwarders} >"${tmp_forward_conf}" 365255809Sdes replace "${forward_conf}" "${tmp_forward_conf}" 366255809Sdes fi 367255809Sdes 368255809Sdes # 369269257Sdes # Generate lan-zones.conf. 370269257Sdes # 371269257Sdes local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX") 372269257Sdes gen_lanzones_conf >"${tmp_lanzones_conf}" 373269257Sdes replace "${lanzones_conf}" "${tmp_lanzones_conf}" 374269257Sdes 375269257Sdes # 376255809Sdes # Generate unbound.conf. 377255809Sdes # 378255809Sdes local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") 379255809Sdes set_chrootdir 380255809Sdes gen_unbound_conf >"${tmp_unbound_conf}" 381255809Sdes replace "${unbound_conf}" "${tmp_unbound_conf}" 382255809Sdes 383255809Sdes # 384255809Sdes # Start unbound, unless requested not to. Stop immediately if 385255809Sdes # it is not enabled so we don't end up with a resolv.conf that 386255809Sdes # points into nothingness. We could "onestart" it, but it 387255809Sdes # wouldn't stick. 388255809Sdes # 389255809Sdes if [ "${start_unbound}" = "no" ] ; then 390255809Sdes # skip 391255809Sdes elif ! service "${service}" enabled ; then 392255809Sdes echo "Please enable $service in rc.conf(5) and try again." 393255809Sdes return 1 394255809Sdes elif ! service "${service}" restart ; then 395255809Sdes echo "Failed to start $service." 396255809Sdes return 1 397255809Sdes fi 398255809Sdes 399255809Sdes # 400255809Sdes # Rewrite resolvconf.conf so resolvconf updates forward.conf 401255809Sdes # instead of resolv.conf. 402255809Sdes # 403255809Sdes local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") 404255809Sdes gen_resolvconf_conf >"${tmp_resolvconf_conf}" 405255809Sdes replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" 406255809Sdes 407255809Sdes # 408255809Sdes # Finally, rewrite resolv.conf. 409255809Sdes # 410255809Sdes local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") 411255809Sdes gen_resolv_conf <"${resolv_conf}" >"${tmp_resolv_conf}" 412255809Sdes replace "${resolv_conf}" "${tmp_resolv_conf}" 413255809Sdes} 414255809Sdes 415255809Sdesmain "$@" 416