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