]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - usr.sbin/unbound/local-setup/local-unbound-setup.sh
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / usr.sbin / unbound / local-setup / local-unbound-setup.sh
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$
28 #
29
30 #
31 # Configuration variables
32 #
33 user=""
34 unbound_conf=""
35 forward_conf=""
36 lanzones_conf=""
37 control_conf=""
38 control_socket=""
39 workdir=""
40 confdir=""
41 chrootdir=""
42 anchor=""
43 pidfile=""
44 resolv_conf=""
45 resolvconf_conf=""
46 service=""
47 start_unbound=""
48 forwarders=""
49
50 #
51 # Global variables
52 #
53 self=$(basename $(realpath "$0"))
54 bkext=$(date "+%Y%m%d.%H%M%S")
55
56 #
57 # Set default values for unset configuration variables.
58 #
59 set_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 #
80 set_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 #
98 get_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 #
123 gen_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 #
162 do_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 #
174 gen_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 #
192 gen_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 #
208 gen_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 #
219 gen_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 #
230 gen_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 #
257 replace() {
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 #
277 usage() {
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 #
300 main() {
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
438 main "$@"