]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/unbound/local-setup/local-unbound-setup.sh
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 workdir=""
37 chrootdir=""
38 anchor=""
39 pidfile=""
40 resolv_conf=""
41 resolvconf_conf=""
42 service=""
43 start_unbound=""
44 forwarders=""
45
46 #
47 # Global variables
48 #
49 self=$(basename $(realpath "$0"))
50 bkext=$(date "+%Y%m%d.%H%M%S")
51
52 #
53 # Set default values for unset configuration variables.
54 #
55 set_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 #
72 set_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 #
89 get_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 #
114 gen_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 #
157 gen_resolvconf_conf() {
158         echo "# Generated by $self"
159         echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
160         echo "unbound_conf=\"${forward_conf}\""
161         echo "unbound_pid=\"${pidfile}\""
162         echo "unbound_service=\"${service}\""
163         # resolvconf(8) likes to restart rather than reload
164         echo "unbound_restart=\"service ${service} reload\""
165 }
166
167 #
168 # Generate forward.conf
169 #
170 gen_forward_conf() {
171         echo "# Generated by $self"
172         echo "forward-zone:"
173         echo "        name: ."
174         for forwarder ; do
175                 if expr "${forwarder}" : "^[0-9:.]\{1,\}$" >/dev/null ; then
176                         echo "        forward-addr: ${forwarder}"
177                 else
178                         echo "        forward-host: ${forwarder}"
179                 fi
180         done
181 }
182
183 #
184 # Generate unbound.conf
185 #
186 gen_unbound_conf() {
187         echo "# Generated by $self"
188         echo "server:"
189         echo "        username: ${user}"
190         echo "        directory: ${workdir}"
191         echo "        chroot: ${chrootdir}"
192         echo "        pidfile: ${pidfile}"
193         echo "        auto-trust-anchor-file: ${anchor}"
194         echo ""
195         if [ -f "${forward_conf}" ] ; then
196                 echo "include: ${forward_conf}"
197         fi
198 }
199
200 #
201 # Replace one file with another, making a backup copy of the first,
202 # but only if the new file is different from the old.
203 #
204 replace() {
205         local file="$1"
206         local newfile="$2"
207         if [ ! -f "${file}" ] ; then
208                 echo "${file} created"
209                 mv "${newfile}" "${file}"
210         elif ! cmp -s "${file}" "${newfile}" ; then
211                 local oldfile="${file}.${bkext}"
212                 echo "original ${file} saved as ${oldfile}"
213                 mv "${file}" "${oldfile}"
214                 mv "${newfile}" "${file}"
215         else
216                 echo "${file} not modified"
217                 rm "${newfile}"
218         fi
219 }
220
221 #
222 # Print usage message and exit
223 #
224 usage() {
225         exec >&2
226         echo "usage: $self [options] [forwarder ...]"
227         echo "options:"
228         echo "    -n          do not start unbound"
229         echo "    -a path     full path to trust anchor file"
230         echo "    -c path     full path to unbound configuration"
231         echo "    -f path     full path to forwarding configuration"
232         echo "    -p path     full path to pid file"
233         echo "    -R path     full path to resolvconf.conf"
234         echo "    -r path     full path to resolv.conf"
235         echo "    -s service  name of unbound service"
236         echo "    -u user     user to run unbound as"
237         echo "    -w path     full path to working directory"
238         exit 1
239 }
240
241 #
242 # Main
243 #
244 main() {
245         umask 022
246
247         #
248         # Parse and validate command-line options
249         #
250         while getopts "a:c:f:np:R:r:s:u:w:" option ; do
251                 case $option in
252                 a)
253                         anchor="$OPTARG"
254                         ;;
255                 c)
256                         unbound_conf="$OPTARG"
257                         ;;
258                 f)
259                         forward_conf="$OPTARG"
260                         ;;
261                 n)
262                         start_unbound="no"
263                         ;;
264                 p)
265                         pidfile="$OPTARG"
266                         ;;
267                 R)
268                         resolvconf_conf="$OPTARG"
269                         ;;
270                 r)
271                         resolv_conf="$OPTARG"
272                         ;;
273                 s)
274                         service="$OPTARG"
275                         ;;
276                 u)
277                         user="$OPTARG"
278                         ;;
279                 w)
280                         workdir="$OPTARG"
281                         ;;
282                 *)
283                         usage
284                         ;;
285                 esac
286         done
287         shift $((OPTIND-1))
288         set_defaults
289
290         #
291         # Get the list of forwarders, either from the command line or
292         # from resolv.conf.
293         #
294         forwarders="$@"
295         if [ -z "$forwarders" ] ; then
296                 echo "Extracting forwarders from ${resolv_conf}."
297                 forwarders=$(get_nameservers <"${resolv_conf}")
298         fi
299
300         #
301         # Generate forward.conf.
302         #
303         if [ -z "${forwarders}" ] ; then
304                 echo -n "No forwarders found in ${resolv_conf##*/}, "
305                 if [ -f "${forward_conf}" ] ; then
306                         echo "using existing ${forward_conf##*/}."
307                 else
308                         echo "unbound will recurse."
309                 fi
310         else
311                 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX")
312                 gen_forward_conf ${forwarders} >"${tmp_forward_conf}"
313                 replace "${forward_conf}" "${tmp_forward_conf}"
314         fi
315
316         #
317         # Generate unbound.conf.
318         #
319         local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX")
320         set_chrootdir
321         gen_unbound_conf >"${tmp_unbound_conf}"
322         replace "${unbound_conf}" "${tmp_unbound_conf}"
323
324         #
325         # Start unbound, unless requested not to.  Stop immediately if
326         # it is not enabled so we don't end up with a resolv.conf that
327         # points into nothingness.  We could "onestart" it, but it
328         # wouldn't stick.
329         #
330         if [ "${start_unbound}" = "no" ] ; then
331                 # skip
332         elif ! service "${service}" enabled ; then
333                 echo "Please enable $service in rc.conf(5) and try again."
334                 return 1
335         elif ! service "${service}" restart ; then
336                 echo "Failed to start $service."
337                 return 1
338         fi
339
340         #
341         # Rewrite resolvconf.conf so resolvconf updates forward.conf
342         # instead of resolv.conf.
343         #
344         local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX")
345         gen_resolvconf_conf >"${tmp_resolvconf_conf}"
346         replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
347
348         #
349         # Finally, rewrite resolv.conf.
350         #
351         local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX")
352         gen_resolv_conf <"${resolv_conf}" >"${tmp_resolv_conf}"
353         replace "${resolv_conf}" "${tmp_resolv_conf}"
354 }
355
356 main "$@"