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