local-unbound-setup.sh revision 295691
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 295691 2016-02-17 11:38:43Z 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 " insecure-lan-zones: yes" 214} 215 216# 217# Generate control.conf 218# 219gen_control_conf() { 220 do_not_edit 221 echo "remote-control:" 222 echo " control-enable: yes" 223 echo " control-interface: ${control_socket}" 224 echo " control-use-cert: no" 225} 226 227# 228# Generate unbound.conf 229# 230gen_unbound_conf() { 231 do_not_edit 232 echo "server:" 233 echo " username: ${user}" 234 echo " directory: ${workdir}" 235 echo " chroot: ${chrootdir}" 236 echo " pidfile: ${pidfile}" 237 echo " auto-trust-anchor-file: ${anchor}" 238 echo "" 239 if [ -f "${forward_conf}" ] ; then 240 echo "include: ${forward_conf}" 241 fi 242 if [ -f "${lanzones_conf}" ] ; then 243 echo "include: ${lanzones_conf}" 244 fi 245 if [ -f "${control_conf}" ] ; then 246 echo "include: ${control_conf}" 247 fi 248 if [ -d "${confdir}" ] ; then 249 echo "include: ${confdir}/*.conf" 250 fi 251} 252 253# 254# Replace one file with another, making a backup copy of the first, 255# but only if the new file is different from the old. 256# 257replace() { 258 local file="$1" 259 local newfile="$2" 260 if [ ! -f "${file}" ] ; then 261 echo "${file} created" 262 mv "${newfile}" "${file}" 263 elif ! cmp -s "${file}" "${newfile}" ; then 264 local oldfile="${file}.${bkext}" 265 echo "original ${file} saved as ${oldfile}" 266 mv "${file}" "${oldfile}" 267 mv "${newfile}" "${file}" 268 else 269 echo "${file} not modified" 270 rm "${newfile}" 271 fi 272} 273 274# 275# Print usage message and exit 276# 277usage() { 278 exec >&2 279 echo "usage: $self [options] [forwarder ...]" 280 echo "options:" 281 echo " -n do not start unbound" 282 echo " -a path full path to trust anchor file" 283 echo " -C path full path to additional configuration directory" 284 echo " -c path full path to unbound configuration file" 285 echo " -f path full path to forwarding configuration" 286 echo " -O path full path to remote control socket" 287 echo " -o path full path to remote control configuration" 288 echo " -p path full path to pid file" 289 echo " -R path full path to resolvconf.conf" 290 echo " -r path full path to resolv.conf" 291 echo " -s service name of unbound service" 292 echo " -u user user to run unbound as" 293 echo " -w path full path to working directory" 294 exit 1 295} 296 297# 298# Main 299# 300main() { 301 umask 022 302 303 # 304 # Parse and validate command-line options 305 # 306 while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do 307 case $option in 308 a) 309 anchor="$OPTARG" 310 ;; 311 C) 312 confdir="$OPTARG" 313 ;; 314 c) 315 unbound_conf="$OPTARG" 316 ;; 317 f) 318 forward_conf="$OPTARG" 319 ;; 320 n) 321 start_unbound="no" 322 ;; 323 O) 324 control_socket="$OPTARG" 325 ;; 326 o) 327 control_conf="$OPTARG" 328 ;; 329 p) 330 pidfile="$OPTARG" 331 ;; 332 R) 333 resolvconf_conf="$OPTARG" 334 ;; 335 r) 336 resolv_conf="$OPTARG" 337 ;; 338 s) 339 service="$OPTARG" 340 ;; 341 u) 342 user="$OPTARG" 343 ;; 344 w) 345 workdir="$OPTARG" 346 ;; 347 *) 348 usage 349 ;; 350 esac 351 done 352 shift $((OPTIND-1)) 353 set_defaults 354 355 # 356 # Get the list of forwarders, either from the command line or 357 # from resolv.conf. 358 # 359 forwarders="$@" 360 if [ -z "$forwarders" ] ; then 361 echo "Extracting forwarders from ${resolv_conf}." 362 forwarders=$(get_nameservers <"${resolv_conf}") 363 style=dynamic 364 else 365 style=static 366 fi 367 368 # 369 # Generate forward.conf. 370 # 371 if [ -z "${forwarders}" ] ; then 372 echo -n "No forwarders found in ${resolv_conf##*/}, " 373 if [ -f "${forward_conf}" ] ; then 374 echo "using existing ${forward_conf##*/}." 375 else 376 echo "unbound will recurse." 377 fi 378 else 379 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") 380 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}" 381 replace "${forward_conf}" "${tmp_forward_conf}" 382 fi 383 384 # 385 # Generate lan-zones.conf. 386 # 387 local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX") 388 gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}" 389 replace "${lanzones_conf}" "${tmp_lanzones_conf}" 390 391 # 392 # Generate control.conf. 393 # 394 local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX") 395 gen_control_conf | unexpand >"${tmp_control_conf}" 396 replace "${control_conf}" "${tmp_control_conf}" 397 398 # 399 # Generate unbound.conf. 400 # 401 local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") 402 set_chrootdir 403 gen_unbound_conf | unexpand >"${tmp_unbound_conf}" 404 replace "${unbound_conf}" "${tmp_unbound_conf}" 405 406 # 407 # Start unbound, unless requested not to. Stop immediately if 408 # it is not enabled so we don't end up with a resolv.conf that 409 # points into nothingness. We could "onestart" it, but it 410 # wouldn't stick. 411 # 412 if [ "${start_unbound}" = "no" ] ; then 413 # skip 414 elif ! service "${service}" enabled ; then 415 echo "Please enable $service in rc.conf(5) and try again." 416 return 1 417 elif ! service "${service}" restart ; then 418 echo "Failed to start $service." 419 return 1 420 fi 421 422 # 423 # Rewrite resolvconf.conf so resolvconf updates forward.conf 424 # instead of resolv.conf. 425 # 426 local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") 427 gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}" 428 replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" 429 430 # 431 # Finally, rewrite resolv.conf. 432 # 433 local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") 434 gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}" 435 replace "${resolv_conf}" "${tmp_resolv_conf}" 436} 437 438main "$@" 439