local-unbound-setup.sh revision 291767
1#!/bin/sh 2#- 3# Copyright (c) 2013 Dag-Erling Sm��rgrav 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD: stable/10/usr.sbin/unbound/local-setup/local-unbound-setup.sh 291767 2015-12-04 13:26:12Z des $ 28# 29 30# 31# Configuration variables 32# 33user="" 34unbound_conf="" 35forward_conf="" 36lanzones_conf="" 37control_conf="" 38control_socket="" 39workdir="" 40confdir="" 41chrootdir="" 42anchor="" 43pidfile="" 44resolv_conf="" 45resolvconf_conf="" 46service="" 47start_unbound="" 48forwarders="" 49 50# 51# Global variables 52# 53self=$(basename $(realpath "$0")) 54bkext=$(date "+%Y%m%d.%H%M%S") 55 56# 57# Set default values for unset configuration variables. 58# 59set_defaults() { 60 : ${user:=unbound} 61 : ${workdir:=/var/unbound} 62 : ${confdir:=${workdir}/conf.d} 63 : ${unbound_conf:=${workdir}/unbound.conf} 64 : ${forward_conf:=${workdir}/forward.conf} 65 : ${lanzones_conf:=${workdir}/lan-zones.conf} 66 : ${control_conf:=${workdir}/control.conf} 67 : ${control_socket:=/var/run/local_unbound.ctl} 68 : ${anchor:=${workdir}/root.key} 69 : ${pidfile:=/var/run/local_unbound.pid} 70 : ${resolv_conf:=/etc/resolv.conf} 71 : ${resolvconf_conf:=/etc/resolvconf.conf} 72 : ${service:=local_unbound} 73 : ${start_unbound:=yes} 74} 75 76# 77# Verify that the configuration files are inside the working 78# directory, and if so, set the chroot directory accordingly. 79# 80set_chrootdir() { 81 chrootdir="${workdir}" 82 for file in "${unbound_conf}" "${forward_conf}" \ 83 "${lanzones_conf}" "${control_conf}" "${anchor}" ; do 84 if [ "${file#${workdir%/}/}" = "${file}" ] ; then 85 echo "warning: ${file} is outside ${workdir}" >&2 86 chrootdir="" 87 fi 88 done 89 if [ -z "${chrootdir}" ] ; then 90 echo "warning: disabling chroot" >&2 91 fi 92} 93 94# 95# Scan through /etc/resolv.conf looking for uncommented nameserver 96# lines that don't point to localhost and return their values. 97# 98get_nameservers() { 99 while read line ; do 100 local bareline=${line%%\#*} 101 local key=${bareline%% *} 102 local value=${bareline#* } 103 case ${key} in 104 nameserver) 105 case ${value} in 106 127.0.0.1|::1|localhost|localhost.*) 107 ;; 108 *) 109 echo "${value}" 110 ;; 111 esac 112 ;; 113 esac 114 done 115} 116 117# 118# Scan through /etc/resolv.conf looking for uncommented nameserver 119# lines. Comment out any that don't point to localhost. Finally, 120# append a nameserver line that points to localhost, if there wasn't 121# one already, and enable the edns0 option. 122# 123gen_resolv_conf() { 124 local localhost=no 125 local edns0=no 126 while read line ; do 127 local bareline=${line%%\#*} 128 local key=${bareline%% *} 129 local value=${bareline#* } 130 case ${key} in 131 nameserver) 132 case ${value} in 133 127.0.0.1|::1|localhost|localhost.*) 134 localhost=yes 135 ;; 136 *) 137 echo -n "# " 138 ;; 139 esac 140 ;; 141 options) 142 case ${value} in 143 *edns0*) 144 edns0=yes 145 ;; 146 esac 147 ;; 148 esac 149 echo "${line}" 150 done 151 if [ "${localhost}" = "no" ] ; then 152 echo "nameserver 127.0.0.1" 153 fi 154 if [ "${edns0}" = "no" ] ; then 155 echo "options edns0" 156 fi 157} 158 159# 160# Boilerplate 161# 162do_not_edit() { 163 echo "# This file was generated by $self." 164 echo "# Modifications will be overwritten." 165} 166 167# 168# Generate resolvconf.conf so it updates forward.conf in addition to 169# resolv.conf. Note "in addition to" rather than "instead of", 170# because we still want it to update the domain name and search path 171# if they change. Setting name_servers to "127.0.0.1" ensures that 172# the libc resolver will try unbound first. 173# 174gen_resolvconf_conf() { 175 local style="$1" 176 do_not_edit 177 echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" 178 if [ "${style}" = "dynamic" ] ; then 179 echo "unbound_conf=\"${forward_conf}\"" 180 echo "unbound_pid=\"${pidfile}\"" 181 echo "unbound_service=\"${service}\"" 182 # resolvconf(8) likes to restart rather than reload 183 echo "unbound_restart=\"service ${service} reload\"" 184 else 185 echo "# Static DNS configuration" 186 fi 187} 188 189# 190# Generate forward.conf 191# 192gen_forward_conf() { 193 do_not_edit 194 echo "forward-zone:" 195 echo " name: ." 196 for forwarder ; do 197 if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then 198 echo " forward-addr: ${forwarder}" 199 else 200 echo " forward-host: ${forwarder}" 201 fi 202 done 203} 204 205# 206# Generate lan-zones.conf 207# 208gen_lanzones_conf() { 209 do_not_edit 210 echo "server:" 211 echo " # Unblock reverse lookups for LAN addresses" 212 echo " unblock-lan-zones: yes" 213 echo " domain-insecure: 10.in-addr.arpa." 214 echo " domain-insecure: 127.in-addr.arpa." 215 echo " domain-insecure: 16.172.in-addr.arpa." 216 echo " domain-insecure: 17.172.in-addr.arpa." 217 echo " domain-insecure: 18.172.in-addr.arpa." 218 echo " domain-insecure: 19.172.in-addr.arpa." 219 echo " domain-insecure: 20.172.in-addr.arpa." 220 echo " domain-insecure: 21.172.in-addr.arpa." 221 echo " domain-insecure: 22.172.in-addr.arpa." 222 echo " domain-insecure: 23.172.in-addr.arpa." 223 echo " domain-insecure: 24.172.in-addr.arpa." 224 echo " domain-insecure: 25.172.in-addr.arpa." 225 echo " domain-insecure: 26.172.in-addr.arpa." 226 echo " domain-insecure: 27.172.in-addr.arpa." 227 echo " domain-insecure: 28.172.in-addr.arpa." 228 echo " domain-insecure: 29.172.in-addr.arpa." 229 echo " domain-insecure: 30.172.in-addr.arpa." 230 echo " domain-insecure: 31.172.in-addr.arpa." 231 echo " domain-insecure: 168.192.in-addr.arpa." 232 echo " domain-insecure: 254.169.in-addr.arpa." 233 echo " domain-insecure: d.f.ip6.arpa." 234 echo " domain-insecure: 8.e.ip6.arpa." 235 echo " domain-insecure: 9.e.ip6.arpa." 236 echo " domain-insecure: a.e.ip6.arpa." 237 echo " domain-insecure: b.e.ip6.arpa." 238} 239 240# 241# Generate control.conf 242# 243gen_control_conf() { 244 do_not_edit 245 echo "remote-control:" 246 echo " control-enable: yes" 247 echo " control-interface: ${control_socket}" 248 echo " control-use-cert: no" 249} 250 251# 252# Generate unbound.conf 253# 254gen_unbound_conf() { 255 do_not_edit 256 echo "server:" 257 echo " username: ${user}" 258 echo " directory: ${workdir}" 259 echo " chroot: ${chrootdir}" 260 echo " pidfile: ${pidfile}" 261 echo " auto-trust-anchor-file: ${anchor}" 262 echo "" 263 if [ -f "${forward_conf}" ] ; then 264 echo "include: ${forward_conf}" 265 fi 266 if [ -f "${lanzones_conf}" ] ; then 267 echo "include: ${lanzones_conf}" 268 fi 269 if [ -f "${control_conf}" ] ; then 270 echo "include: ${control_conf}" 271 fi 272 if [ -d "${confdir}" ] ; then 273 echo "include: ${confdir}/*.conf" 274 fi 275} 276 277# 278# Replace one file with another, making a backup copy of the first, 279# but only if the new file is different from the old. 280# 281replace() { 282 local file="$1" 283 local newfile="$2" 284 if [ ! -f "${file}" ] ; then 285 echo "${file} created" 286 mv "${newfile}" "${file}" 287 elif ! cmp -s "${file}" "${newfile}" ; then 288 local oldfile="${file}.${bkext}" 289 echo "original ${file} saved as ${oldfile}" 290 mv "${file}" "${oldfile}" 291 mv "${newfile}" "${file}" 292 else 293 echo "${file} not modified" 294 rm "${newfile}" 295 fi 296} 297 298# 299# Print usage message and exit 300# 301usage() { 302 exec >&2 303 echo "usage: $self [options] [forwarder ...]" 304 echo "options:" 305 echo " -n do not start unbound" 306 echo " -a path full path to trust anchor file" 307 echo " -C path full path to additional configuration directory" 308 echo " -c path full path to unbound configuration file" 309 echo " -f path full path to forwarding configuration" 310 echo " -O path full path to remote control socket" 311 echo " -o path full path to remote control configuration" 312 echo " -p path full path to pid file" 313 echo " -R path full path to resolvconf.conf" 314 echo " -r path full path to resolv.conf" 315 echo " -s service name of unbound service" 316 echo " -u user user to run unbound as" 317 echo " -w path full path to working directory" 318 exit 1 319} 320 321# 322# Main 323# 324main() { 325 umask 022 326 327 # 328 # Parse and validate command-line options 329 # 330 while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do 331 case $option in 332 a) 333 anchor="$OPTARG" 334 ;; 335 C) 336 confdir="$OPTARG" 337 ;; 338 c) 339 unbound_conf="$OPTARG" 340 ;; 341 f) 342 forward_conf="$OPTARG" 343 ;; 344 n) 345 start_unbound="no" 346 ;; 347 O) 348 control_socket="$OPTARG" 349 ;; 350 o) 351 control_conf="$OPTARG" 352 ;; 353 p) 354 pidfile="$OPTARG" 355 ;; 356 R) 357 resolvconf_conf="$OPTARG" 358 ;; 359 r) 360 resolv_conf="$OPTARG" 361 ;; 362 s) 363 service="$OPTARG" 364 ;; 365 u) 366 user="$OPTARG" 367 ;; 368 w) 369 workdir="$OPTARG" 370 ;; 371 *) 372 usage 373 ;; 374 esac 375 done 376 shift $((OPTIND-1)) 377 set_defaults 378 379 # 380 # Get the list of forwarders, either from the command line or 381 # from resolv.conf. 382 # 383 forwarders="$@" 384 if [ -z "$forwarders" ] ; then 385 echo "Extracting forwarders from ${resolv_conf}." 386 forwarders=$(get_nameservers <"${resolv_conf}") 387 style=dynamic 388 else 389 style=static 390 fi 391 392 # 393 # Generate forward.conf. 394 # 395 if [ -z "${forwarders}" ] ; then 396 echo -n "No forwarders found in ${resolv_conf##*/}, " 397 if [ -f "${forward_conf}" ] ; then 398 echo "using existing ${forward_conf##*/}." 399 else 400 echo "unbound will recurse." 401 fi 402 else 403 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") 404 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}" 405 replace "${forward_conf}" "${tmp_forward_conf}" 406 fi 407 408 # 409 # Generate lan-zones.conf. 410 # 411 local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX") 412 gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}" 413 replace "${lanzones_conf}" "${tmp_lanzones_conf}" 414 415 # 416 # Generate control.conf. 417 # 418 local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX") 419 gen_control_conf | unexpand >"${tmp_control_conf}" 420 replace "${control_conf}" "${tmp_control_conf}" 421 422 # 423 # Generate unbound.conf. 424 # 425 local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") 426 set_chrootdir 427 gen_unbound_conf | unexpand >"${tmp_unbound_conf}" 428 replace "${unbound_conf}" "${tmp_unbound_conf}" 429 430 # 431 # Start unbound, unless requested not to. Stop immediately if 432 # it is not enabled so we don't end up with a resolv.conf that 433 # points into nothingness. We could "onestart" it, but it 434 # wouldn't stick. 435 # 436 if [ "${start_unbound}" = "no" ] ; then 437 # skip 438 elif ! service "${service}" enabled ; then 439 echo "Please enable $service in rc.conf(5) and try again." 440 return 1 441 elif ! service "${service}" restart ; then 442 echo "Failed to start $service." 443 return 1 444 fi 445 446 # 447 # Rewrite resolvconf.conf so resolvconf updates forward.conf 448 # instead of resolv.conf. 449 # 450 local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") 451 gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}" 452 replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" 453 454 # 455 # Finally, rewrite resolv.conf. 456 # 457 local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") 458 gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}" 459 replace "${resolv_conf}" "${tmp_resolv_conf}" 460} 461 462main "$@" 463