]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/unbound/local-setup/local-unbound-setup.sh
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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         do_not_edit
176         echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
177         echo "unbound_conf=\"${forward_conf}\""
178         echo "unbound_pid=\"${pidfile}\""
179         echo "unbound_service=\"${service}\""
180         # resolvconf(8) likes to restart rather than reload
181         echo "unbound_restart=\"service ${service} reload\""
182 }
183
184 #
185 # Generate forward.conf
186 #
187 gen_forward_conf() {
188         do_not_edit
189         echo "forward-zone:"
190         echo "        name: ."
191         for forwarder ; do
192                 if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then
193                         echo "        forward-addr: ${forwarder}"
194                 else
195                         echo "        forward-host: ${forwarder}"
196                 fi
197         done
198 }
199
200 #
201 # Generate lan-zones.conf
202 #
203 gen_lanzones_conf() {
204         do_not_edit
205         echo "server:"
206         echo "        # Unblock reverse lookups for LAN addresses"
207         echo "        unblock-lan-zones: yes"
208         echo "        domain-insecure: 10.in-addr.arpa."
209         echo "        domain-insecure: 127.in-addr.arpa."
210         echo "        domain-insecure: 16.172.in-addr.arpa."
211         echo "        domain-insecure: 17.172.in-addr.arpa."
212         echo "        domain-insecure: 18.172.in-addr.arpa."
213         echo "        domain-insecure: 19.172.in-addr.arpa."
214         echo "        domain-insecure: 20.172.in-addr.arpa."
215         echo "        domain-insecure: 21.172.in-addr.arpa."
216         echo "        domain-insecure: 22.172.in-addr.arpa."
217         echo "        domain-insecure: 23.172.in-addr.arpa."
218         echo "        domain-insecure: 24.172.in-addr.arpa."
219         echo "        domain-insecure: 25.172.in-addr.arpa."
220         echo "        domain-insecure: 26.172.in-addr.arpa."
221         echo "        domain-insecure: 27.172.in-addr.arpa."
222         echo "        domain-insecure: 28.172.in-addr.arpa."
223         echo "        domain-insecure: 29.172.in-addr.arpa."
224         echo "        domain-insecure: 30.172.in-addr.arpa."
225         echo "        domain-insecure: 31.172.in-addr.arpa."
226         echo "        domain-insecure: 168.192.in-addr.arpa."
227         echo "        domain-insecure: 254.169.in-addr.arpa."
228         echo "        domain-insecure: d.f.ip6.arpa."
229         echo "        domain-insecure: 8.e.ip6.arpa."
230         echo "        domain-insecure: 9.e.ip6.arpa."
231         echo "        domain-insecure: a.e.ip6.arpa."
232         echo "        domain-insecure: b.e.ip6.arpa."
233 }
234
235 #
236 # Generate control.conf
237 #
238 gen_control_conf() {
239         do_not_edit
240         echo "remote-control:"
241         echo "        control-enable: yes"
242         echo "        control-interface: ${control_socket}"
243         echo "        control-use-cert: no"
244 }
245
246 #
247 # Generate unbound.conf
248 #
249 gen_unbound_conf() {
250         do_not_edit
251         echo "server:"
252         echo "        username: ${user}"
253         echo "        directory: ${workdir}"
254         echo "        chroot: ${chrootdir}"
255         echo "        pidfile: ${pidfile}"
256         echo "        auto-trust-anchor-file: ${anchor}"
257         echo ""
258         if [ -f "${forward_conf}" ] ; then
259                 echo "include: ${forward_conf}"
260         fi
261         if [ -f "${lanzones_conf}" ] ; then
262                 echo "include: ${lanzones_conf}"
263         fi
264         if [ -f "${control_conf}" ] ; then
265                 echo "include: ${control_conf}"
266         fi
267         if [ -d "${confdir}" ] ; then
268                 echo "include: ${confdir}/*.conf"
269         fi
270 }
271
272 #
273 # Replace one file with another, making a backup copy of the first,
274 # but only if the new file is different from the old.
275 #
276 replace() {
277         local file="$1"
278         local newfile="$2"
279         if [ ! -f "${file}" ] ; then
280                 echo "${file} created"
281                 mv "${newfile}" "${file}"
282         elif ! cmp -s "${file}" "${newfile}" ; then
283                 local oldfile="${file}.${bkext}"
284                 echo "original ${file} saved as ${oldfile}"
285                 mv "${file}" "${oldfile}"
286                 mv "${newfile}" "${file}"
287         else
288                 echo "${file} not modified"
289                 rm "${newfile}"
290         fi
291 }
292
293 #
294 # Print usage message and exit
295 #
296 usage() {
297         exec >&2
298         echo "usage: $self [options] [forwarder ...]"
299         echo "options:"
300         echo "    -n          do not start unbound"
301         echo "    -a path     full path to trust anchor file"
302         echo "    -C path     full path to additional configuration directory"
303         echo "    -c path     full path to unbound configuration file"
304         echo "    -f path     full path to forwarding configuration"
305         echo "    -O path     full path to remote control socket"
306         echo "    -o path     full path to remote control configuration"
307         echo "    -p path     full path to pid file"
308         echo "    -R path     full path to resolvconf.conf"
309         echo "    -r path     full path to resolv.conf"
310         echo "    -s service  name of unbound service"
311         echo "    -u user     user to run unbound as"
312         echo "    -w path     full path to working directory"
313         exit 1
314 }
315
316 #
317 # Main
318 #
319 main() {
320         umask 022
321
322         #
323         # Parse and validate command-line options
324         #
325         while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do
326                 case $option in
327                 a)
328                         anchor="$OPTARG"
329                         ;;
330                 C)
331                         confdir="$OPTARG"
332                         ;;
333                 c)
334                         unbound_conf="$OPTARG"
335                         ;;
336                 f)
337                         forward_conf="$OPTARG"
338                         ;;
339                 n)
340                         start_unbound="no"
341                         ;;
342                 O)
343                         control_socket="$OPTARG"
344                         ;;
345                 o)
346                         control_conf="$OPTARG"
347                         ;;      
348                 p)
349                         pidfile="$OPTARG"
350                         ;;
351                 R)
352                         resolvconf_conf="$OPTARG"
353                         ;;
354                 r)
355                         resolv_conf="$OPTARG"
356                         ;;
357                 s)
358                         service="$OPTARG"
359                         ;;
360                 u)
361                         user="$OPTARG"
362                         ;;
363                 w)
364                         workdir="$OPTARG"
365                         ;;
366                 *)
367                         usage
368                         ;;
369                 esac
370         done
371         shift $((OPTIND-1))
372         set_defaults
373
374         #
375         # Get the list of forwarders, either from the command line or
376         # from resolv.conf.
377         #
378         forwarders="$@"
379         if [ -z "$forwarders" ] ; then
380                 echo "Extracting forwarders from ${resolv_conf}."
381                 forwarders=$(get_nameservers <"${resolv_conf}")
382         fi
383
384         #
385         # Generate forward.conf.
386         #
387         if [ -z "${forwarders}" ] ; then
388                 echo -n "No forwarders found in ${resolv_conf##*/}, "
389                 if [ -f "${forward_conf}" ] ; then
390                         echo "using existing ${forward_conf##*/}."
391                 else
392                         echo "unbound will recurse."
393                 fi
394         else
395                 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX")
396                 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}"
397                 replace "${forward_conf}" "${tmp_forward_conf}"
398         fi
399
400         #
401         # Generate lan-zones.conf.
402         #
403         local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX")
404         gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}"
405         replace "${lanzones_conf}" "${tmp_lanzones_conf}"
406
407         #
408         # Generate control.conf.
409         #
410         local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX")
411         gen_control_conf | unexpand >"${tmp_control_conf}"
412         replace "${control_conf}" "${tmp_control_conf}"
413
414         #
415         # Generate unbound.conf.
416         #
417         local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX")
418         set_chrootdir
419         gen_unbound_conf | unexpand >"${tmp_unbound_conf}"
420         replace "${unbound_conf}" "${tmp_unbound_conf}"
421
422         #
423         # Start unbound, unless requested not to.  Stop immediately if
424         # it is not enabled so we don't end up with a resolv.conf that
425         # points into nothingness.  We could "onestart" it, but it
426         # wouldn't stick.
427         #
428         if [ "${start_unbound}" = "no" ] ; then
429                 # skip
430         elif ! service "${service}" enabled ; then
431                 echo "Please enable $service in rc.conf(5) and try again."
432                 return 1
433         elif ! service "${service}" restart ; then
434                 echo "Failed to start $service."
435                 return 1
436         fi
437
438         #
439         # Rewrite resolvconf.conf so resolvconf updates forward.conf
440         # instead of resolv.conf.
441         #
442         local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX")
443         gen_resolvconf_conf | unexpand >"${tmp_resolvconf_conf}"
444         replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
445
446         #
447         # Finally, rewrite resolv.conf.
448         #
449         local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX")
450         gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}"
451         replace "${resolv_conf}" "${tmp_resolv_conf}"
452 }
453
454 main "$@"