local-unbound-setup.sh revision 266863
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: head/usr.sbin/unbound/local-setup/local-unbound-setup.sh 266863 2014-05-29 22:34:04Z des $ 28# 29 30# 31# Configuration variables 32# 33user="" 34unbound_conf="" 35forward_conf="" 36workdir="" 37confdir="" 38chrootdir="" 39anchor="" 40pidfile="" 41resolv_conf="" 42resolvconf_conf="" 43service="" 44start_unbound="" 45forwarders="" 46 47# 48# Global variables 49# 50self=$(basename $(realpath "$0")) 51bkext=$(date "+%Y%m%d.%H%M%S") 52 53# 54# Set default values for unset configuration variables. 55# 56set_defaults() { 57 : ${user:=unbound} 58 : ${workdir:=/var/unbound} 59 : ${confdir:=${workdir}/conf.d} 60 : ${unbound_conf:=${workdir}/unbound.conf} 61 : ${forward_conf:=${workdir}/forward.conf} 62 : ${anchor:=${workdir}/root.key} 63 : ${pidfile:=/var/run/local_unbound.pid} 64 : ${resolv_conf:=/etc/resolv.conf} 65 : ${resolvconf_conf:=/etc/resolvconf.conf} 66 : ${service:=local_unbound} 67 : ${start_unbound:=yes} 68} 69 70# 71# Verify that the configuration files are inside the working 72# directory, and if so, set the chroot directory accordingly. 73# 74set_chrootdir() { 75 chrootdir="${workdir}" 76 for file in "${unbound_conf}" "${forward_conf}" "${anchor}" ; do 77 if [ "${file#${workdir%/}/}" = "${file}" ] ; then 78 echo "warning: ${file} is outside ${workdir}" >&2 79 chrootdir="" 80 fi 81 done 82 if [ -z "${chrootdir}" ] ; then 83 echo "warning: disabling chroot" >&2 84 fi 85} 86 87# 88# Scan through /etc/resolv.conf looking for uncommented nameserver 89# lines that don't point to localhost and return their values. 90# 91get_nameservers() { 92 while read line ; do 93 local bareline=${line%%\#*} 94 local key=${bareline%% *} 95 local value=${bareline#* } 96 case ${key} in 97 nameserver) 98 case ${value} in 99 127.0.0.1|::1|localhost|localhost.*) 100 ;; 101 *) 102 echo "${value}" 103 ;; 104 esac 105 ;; 106 esac 107 done 108} 109 110# 111# Scan through /etc/resolv.conf looking for uncommented nameserver 112# lines. Comment out any that don't point to localhost. Finally, 113# append a nameserver line that points to localhost, if there wasn't 114# one already, and enable the edns0 option. 115# 116gen_resolv_conf() { 117 local localhost=no 118 local edns0=no 119 while read line ; do 120 local bareline=${line%%\#*} 121 local key=${bareline%% *} 122 local value=${bareline#* } 123 case ${key} in 124 nameserver) 125 case ${value} in 126 127.0.0.1|::1|localhost|localhost.*) 127 localhost=yes 128 ;; 129 *) 130 echo -n "# " 131 ;; 132 esac 133 ;; 134 options) 135 case ${value} in 136 *edns0*) 137 edns0=yes 138 ;; 139 esac 140 ;; 141 esac 142 echo "${line}" 143 done 144 if [ "${localhost}" = "no" ] ; then 145 echo "nameserver 127.0.0.1" 146 fi 147 if [ "${edns0}" = "no" ] ; then 148 echo "options edns0" 149 fi 150} 151 152# 153# Generate resolvconf.conf so it updates forward.conf in addition to 154# resolv.conf. Note "in addition to" rather than "instead of", 155# because we still want it to update the domain name and search path 156# if they change. Setting name_servers to "127.0.0.1" ensures that 157# the libc resolver will try unbound first. 158# 159gen_resolvconf_conf() { 160 echo "# Generated by $self" 161 echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" 162 echo "unbound_conf=\"${forward_conf}\"" 163 echo "unbound_pid=\"${pidfile}\"" 164 echo "unbound_service=\"${service}\"" 165 # resolvconf(8) likes to restart rather than reload 166 echo "unbound_restart=\"service ${service} reload\"" 167} 168 169# 170# Generate forward.conf 171# 172gen_forward_conf() { 173 echo "# Generated by $self" 174 echo "forward-zone:" 175 echo " name: ." 176 for forwarder ; do 177 if expr "${forwarder}" : "^[0-9:.]\{1,\}$" >/dev/null ; then 178 echo " forward-addr: ${forwarder}" 179 else 180 echo " forward-host: ${forwarder}" 181 fi 182 done 183} 184 185# 186# Generate unbound.conf 187# 188gen_unbound_conf() { 189 echo "# Generated by $self" 190 echo "server:" 191 echo " username: ${user}" 192 echo " directory: ${workdir}" 193 echo " chroot: ${chrootdir}" 194 echo " pidfile: ${pidfile}" 195 echo " auto-trust-anchor-file: ${anchor}" 196 echo "" 197 if [ -f "${forward_conf}" ] ; then 198 echo "include: ${forward_conf}" 199 fi 200 if [ -d "${confdir}" ] ; then 201 echo "include: ${confdir}/*.conf" 202 fi 203} 204 205# 206# Replace one file with another, making a backup copy of the first, 207# but only if the new file is different from the old. 208# 209replace() { 210 local file="$1" 211 local newfile="$2" 212 if [ ! -f "${file}" ] ; then 213 echo "${file} created" 214 mv "${newfile}" "${file}" 215 elif ! cmp -s "${file}" "${newfile}" ; then 216 local oldfile="${file}.${bkext}" 217 echo "original ${file} saved as ${oldfile}" 218 mv "${file}" "${oldfile}" 219 mv "${newfile}" "${file}" 220 else 221 echo "${file} not modified" 222 rm "${newfile}" 223 fi 224} 225 226# 227# Print usage message and exit 228# 229usage() { 230 exec >&2 231 echo "usage: $self [options] [forwarder ...]" 232 echo "options:" 233 echo " -n do not start unbound" 234 echo " -a path full path to trust anchor file" 235 echo " -C path full path to additional configuration directory" 236 echo " -c path full path to unbound configuration file" 237 echo " -f path full path to forwarding configuration" 238 echo " -p path full path to pid file" 239 echo " -R path full path to resolvconf.conf" 240 echo " -r path full path to resolv.conf" 241 echo " -s service name of unbound service" 242 echo " -u user user to run unbound as" 243 echo " -w path full path to working directory" 244 exit 1 245} 246 247# 248# Main 249# 250main() { 251 umask 022 252 253 # 254 # Parse and validate command-line options 255 # 256 while getopts "a:C:c:f:np:R:r:s:u:w:" option ; do 257 case $option in 258 a) 259 anchor="$OPTARG" 260 ;; 261 C) 262 confdir="$OPTARG" 263 ;; 264 c) 265 unbound_conf="$OPTARG" 266 ;; 267 f) 268 forward_conf="$OPTARG" 269 ;; 270 n) 271 start_unbound="no" 272 ;; 273 p) 274 pidfile="$OPTARG" 275 ;; 276 R) 277 resolvconf_conf="$OPTARG" 278 ;; 279 r) 280 resolv_conf="$OPTARG" 281 ;; 282 s) 283 service="$OPTARG" 284 ;; 285 u) 286 user="$OPTARG" 287 ;; 288 w) 289 workdir="$OPTARG" 290 ;; 291 *) 292 usage 293 ;; 294 esac 295 done 296 shift $((OPTIND-1)) 297 set_defaults 298 299 # 300 # Get the list of forwarders, either from the command line or 301 # from resolv.conf. 302 # 303 forwarders="$@" 304 if [ -z "$forwarders" ] ; then 305 echo "Extracting forwarders from ${resolv_conf}." 306 forwarders=$(get_nameservers <"${resolv_conf}") 307 fi 308 309 # 310 # Generate forward.conf. 311 # 312 if [ -z "${forwarders}" ] ; then 313 echo -n "No forwarders found in ${resolv_conf##*/}, " 314 if [ -f "${forward_conf}" ] ; then 315 echo "using existing ${forward_conf##*/}." 316 else 317 echo "unbound will recurse." 318 fi 319 else 320 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") 321 gen_forward_conf ${forwarders} >"${tmp_forward_conf}" 322 replace "${forward_conf}" "${tmp_forward_conf}" 323 fi 324 325 # 326 # Generate unbound.conf. 327 # 328 local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") 329 set_chrootdir 330 gen_unbound_conf >"${tmp_unbound_conf}" 331 replace "${unbound_conf}" "${tmp_unbound_conf}" 332 333 # 334 # Start unbound, unless requested not to. Stop immediately if 335 # it is not enabled so we don't end up with a resolv.conf that 336 # points into nothingness. We could "onestart" it, but it 337 # wouldn't stick. 338 # 339 if [ "${start_unbound}" = "no" ] ; then 340 # skip 341 elif ! service "${service}" enabled ; then 342 echo "Please enable $service in rc.conf(5) and try again." 343 return 1 344 elif ! service "${service}" restart ; then 345 echo "Failed to start $service." 346 return 1 347 fi 348 349 # 350 # Rewrite resolvconf.conf so resolvconf updates forward.conf 351 # instead of resolv.conf. 352 # 353 local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") 354 gen_resolvconf_conf >"${tmp_resolvconf_conf}" 355 replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" 356 357 # 358 # Finally, rewrite resolv.conf. 359 # 360 local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") 361 gen_resolv_conf <"${resolv_conf}" >"${tmp_resolv_conf}" 362 replace "${resolv_conf}" "${tmp_resolv_conf}" 363} 364 365main "$@" 366