]> git.ipfire.org Git - ipfire-2.x.git/blob - src/initscripts/system/unbound
unbound: use nic carrier instead of /var/ipfire/red/active
[ipfire-2.x.git] / src / initscripts / system / unbound
1 #!/bin/sh
2 # Begin $rc_base/init.d/unbound
3
4 # Description : Unbound DNS resolver boot script for IPfire
5 # Author : Marcel Lorenz <marcel.lorenz@ipfire.org>
6
7 . /etc/sysconfig/rc
8 . ${rc_functions}
9
10 TEST_DOMAIN="ipfire.org"
11
12 # This domain will never validate
13 TEST_DOMAIN_FAIL="dnssec-failed.org"
14
15 INSECURE_ZONES=
16 USE_FORWARDERS=1
17 ENABLE_SAFE_SEARCH=off
18
19 # Cache any local zones for 60 seconds
20 LOCAL_TTL=60
21
22 # EDNS buffer size
23 EDNS_DEFAULT_BUFFER_SIZE=4096
24
25 # Load optional configuration
26 [ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
27
28 ip_address_revptr() {
29 local addr=${1}
30
31 local a1 a2 a3 a4
32 IFS=. read -r a1 a2 a3 a4 <<< ${addr}
33
34 echo "${a4}.${a3}.${a2}.${a1}.in-addr.arpa"
35 }
36
37 read_name_servers() {
38 local i
39 for i in 1 2; do
40 echo "$(</var/ipfire/red/dns${i})"
41 done 2>/dev/null | xargs echo
42 }
43
44 config_header() {
45 echo "# This file is automatically generated and any changes"
46 echo "# will be overwritten. DO NOT EDIT!"
47 echo
48 }
49
50 update_forwarders() {
51 if [ "${USE_FORWARDERS}" = "1" -a "$(</sys/class/net/$(</var/ipfire/red/iface)/carrier)" = "1" ]; then
52 local forwarders
53 local broken_forwarders
54
55 local ns
56 for ns in $(read_name_servers); do
57 test_name_server ${ns} &>/dev/null
58 case "$?" in
59 # Only use DNSSEC-validating or DNSSEC-aware name servers
60 0|2)
61 forwarders="${forwarders} ${ns}"
62 ;;
63 *)
64 broken_forwarders="${broken_forwarders} ${ns}"
65 ;;
66 esac
67 done
68
69 # Determine EDNS buffer size
70 local new_edns_buffer_size=${EDNS_DEFAULT_BUFFER_SIZE}
71
72 for ns in ${forwarders}; do
73 local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
74 if [ -n "${edns_buffer_size}" ]; then
75 if [ ${edns_buffer_size} -lt ${new_edns_buffer_size} ]; then
76 new_edns_buffer_size=${edns_buffer_size}
77 fi
78 fi
79 done
80
81 if [ ${new_edns_buffer_size} -lt ${EDNS_DEFAULT_BUFFER_SIZE} ]; then
82 boot_mesg "EDNS buffer size reduced to ${new_edns_buffer_size}" ${WARNING}
83 echo_warning
84
85 unbound-control -q set_option edns-buffer-size: ${new_edns_buffer_size}
86 fi
87
88 # Show warning for any broken upstream name servers
89 if [ -n "${broken_forwarders}" ]; then
90 boot_mesg "Ignoring broken upstream name server(s): ${broken_forwarders:1}" ${WARNING}
91 echo_warning
92 fi
93
94 if [ -n "${forwarders}" ]; then
95 boot_mesg "Configuring upstream name server(s): ${forwarders:1}" ${INFO}
96 echo_ok
97
98 # Make sure DNSSEC is activated
99 enable_dnssec
100
101 echo "${forwarders}" > /var/ipfire/red/dns
102 unbound-control -q forward ${forwarders}
103 return 0
104
105 # In case we have found no working forwarders
106 else
107 # Test if the recursor mode is available
108 if can_resolve_root +bufsize=${new_edns_buffer_size}; then
109 # Make sure DNSSEC is activated
110 enable_dnssec
111
112 boot_mesg "Falling back to recursor mode" ${WARNING}
113 echo_warning
114
115 # If not, we set DNSSEC in permissive mode and allow using all recursors
116 elif [ -n "${broken_forwarders}" ]; then
117 disable_dnssec
118
119 boot_mesg "DNSSEC has been set to permissive mode" ${FAILURE}
120 echo_failure
121
122 echo "${broken_forwarders}" > /var/ipfire/red/dns
123 unbound-control -q forward ${broken_forwarders}
124 return 0
125 fi
126 fi
127 fi
128
129 # If forwarders cannot be used we run in recursor mode
130 echo "local recursor" > /var/ipfire/red/dns
131 unbound-control -q forward off
132 }
133
134 own_hostname() {
135 local hostname=$(hostname -f)
136 # 1.1.1.1 is reserved for unused green, skip this
137 if [ -n "${GREEN_ADDRESS}" -a "${GREEN_ADDRESS}" != "1.1.1.1" ]; then
138 unbound-control -q local_data "${hostname} ${LOCAL_TTL} IN A ${GREEN_ADDRESS}"
139 fi
140
141 local address
142 for address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do
143 [ -n "${address}" ] || continue
144 [ "${address}" = "1.1.1.1" ] && continue
145
146 address=$(ip_address_revptr ${address})
147 unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${hostname}"
148 done
149 }
150
151 update_hosts() {
152 local enabled address hostname domainname generateptr
153
154 while IFS="," read -r enabled address hostname domainname generateptr; do
155 [ "${enabled}" = "on" ] || continue
156
157 # Build FQDN
158 local fqdn="${hostname}.${domainname}"
159
160 unbound-control -q local_data "${fqdn} ${LOCAL_TTL} IN A ${address}"
161
162 # Skip reverse resolution if the address equals the GREEN address
163 [ "${address}" = "${GREEN_ADDRESS}" ] && continue
164
165 # Skip reverse resolution if user requested not to do so
166 [ "${generateptr}" = "off" ] && continue
167
168 # Add RDNS
169 address=$(ip_address_revptr ${address})
170 unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${fqdn}"
171 done < /var/ipfire/main/hosts
172 }
173
174 write_forward_conf() {
175 (
176 config_header
177
178 local insecure_zones="${INSECURE_ZONES}"
179
180 local enabled zone server servers remark disable_dnssec rest
181 while IFS="," read -r enabled zone servers remark disable_dnssec rest; do
182 # Line must be enabled.
183 [ "${enabled}" = "on" ] || continue
184
185 # Zones that end with .local are commonly used for internal
186 # zones and therefore not signed
187 case "${zone}" in
188 *.local)
189 insecure_zones="${insecure_zones} ${zone}"
190 ;;
191 *)
192 if [ "${disable_dnssec}" = "on" ]; then
193 insecure_zones="${insecure_zones} ${zone}"
194 fi
195 ;;
196 esac
197
198 # Reverse-lookup zones must be stubs
199 case "${zone}" in
200 *.in-addr.arpa)
201 echo "stub-zone:"
202 echo " name: ${zone}"
203 for server in ${servers//|/ }; do
204 if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
205 echo " stub-addr: ${server}"
206 else
207 echo " stub-host: ${server}"
208 fi
209 done
210 echo
211 echo "server:"
212 echo " local-zone: \"${zone}\" transparent"
213 echo
214 ;;
215 *)
216 echo "forward-zone:"
217 echo " name: ${zone}"
218 for server in ${servers//|/ }; do
219 if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
220 echo " forward-addr: ${server}"
221 else
222 echo " forward-host: ${server}"
223 fi
224 done
225 echo
226 ;;
227 esac
228 done < /var/ipfire/dnsforward/config
229
230 if [ -n "${insecure_zones}" ]; then
231 echo "server:"
232
233 for zone in ${insecure_zones}; do
234 echo " domain-insecure: ${zone}"
235 done
236 fi
237 ) > /etc/unbound/forward.conf
238 }
239
240 write_tuning_conf() {
241 # https://www.unbound.net/documentation/howto_optimise.html
242
243 # Determine number of online processors
244 local processors=$(getconf _NPROCESSORS_ONLN)
245
246 # Determine number of slabs
247 local slabs=1
248 while [ ${slabs} -lt ${processors} ]; do
249 slabs=$(( ${slabs} * 2 ))
250 done
251
252 # Determine amount of system memory
253 local mem=$(get_memory_amount)
254
255 # In the worst case scenario, unbound can use double the
256 # amount of memory allocated to a cache due to malloc overhead
257
258 # Even larger systems with more than 8GB of RAM
259 if [ ${mem} -ge 8192 ]; then
260 mem=1024
261
262 # Extra large systems with more than 4GB of RAM
263 elif [ ${mem} -ge 4096 ]; then
264 mem=512
265
266 # Large systems with more than 2GB of RAM
267 elif [ ${mem} -ge 2048 ]; then
268 mem=256
269
270 # Medium systems with more than 1GB of RAM
271 elif [ ${mem} -ge 1024 ]; then
272 mem=128
273
274 # Small systems with less than 256MB of RAM
275 elif [ ${mem} -le 256 ]; then
276 mem=16
277
278 # Everything else
279 else
280 mem=64
281 fi
282
283 (
284 config_header
285
286 # We run one thread per processor
287 echo "num-threads: ${processors}"
288 echo "so-reuseport: yes"
289
290 # Adjust number of slabs
291 echo "infra-cache-slabs: ${slabs}"
292 echo "key-cache-slabs: ${slabs}"
293 echo "msg-cache-slabs: ${slabs}"
294 echo "rrset-cache-slabs: ${slabs}"
295
296 # Slice up the cache
297 echo "rrset-cache-size: $(( ${mem} / 2 ))m"
298 echo "msg-cache-size: $(( ${mem} / 4 ))m"
299 echo "key-cache-size: $(( ${mem} / 4 ))m"
300
301 # Increase parallel queries
302 echo "outgoing-range: 8192"
303 echo "num-queries-per-thread: 4096"
304
305 # Use larger send/receive buffers
306 echo "so-sndbuf: 4m"
307 echo "so-rcvbuf: 4m"
308 ) > /etc/unbound/tuning.conf
309 }
310
311 get_memory_amount() {
312 local key val unit
313
314 while read -r key val unit; do
315 case "${key}" in
316 MemTotal:*)
317 # Convert to MB
318 echo "$(( ${val} / 1024 ))"
319 break
320 ;;
321 esac
322 done < /proc/meminfo
323 }
324
325 test_name_server() {
326 local ns=${1}
327 local args
328
329 # Return codes:
330 # 0 DNSSEC validating
331 # 1 Error: unreachable, etc.
332 # 2 DNSSEC aware
333 # 3 NOT DNSSEC-aware
334
335 # Exit when the server is not reachable
336 ns_is_online ${ns} || return 1
337
338 # Determine the maximum edns buffer size that works
339 local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
340 if [ -n "${edns_buffer_size}" ]; then
341 args="${args} +bufsize=${edns_buffer_size}"
342 fi
343
344 local errors
345 for rr in DNSKEY DS RRSIG; do
346 if ! ns_forwards_${rr} ${ns} ${args}; then
347 errors="${errors} ${rr}"
348 fi
349 done
350
351 if [ -n "${errors}" ]; then
352 echo >&2 "Unable to retrieve the following resource records from ${ns}: ${errors:1}"
353 return 3
354 fi
355
356 if ns_is_validating ${ns} ${args}; then
357 # Return 0 if validating
358 return 0
359 else
360 # Is DNSSEC-aware
361 return 2
362 fi
363 }
364
365 # Sends an A query to the nameserver w/o DNSSEC
366 ns_is_online() {
367 local ns=${1}
368 shift
369
370 dig @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
371 }
372
373 # Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
374 ns_is_validating() {
375 local ns=${1}
376 shift
377
378 if ! dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
379 return 1
380 else
381 # Determine if NS replies with "ad" data flag if DNSSEC enabled
382 dig @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
383 fi
384 }
385
386 # Checks if we can retrieve the DNSKEY for this domain.
387 # dig will print the SOA if nothing was found
388 ns_forwards_DNSKEY() {
389 local ns=${1}
390 shift
391
392 dig @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
393 }
394
395 ns_forwards_DS() {
396 local ns=${1}
397 shift
398
399 dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
400 }
401
402 ns_forwards_RRSIG() {
403 local ns=${1}
404 shift
405
406 dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
407 }
408
409 ns_supports_tcp() {
410 local ns=${1}
411 shift
412
413 dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
414 }
415
416 ns_determine_edns_buffer_size() {
417 local ns=${1}
418 shift
419
420 local b
421 for b in 4096 2048 1500 1480 1464 1400 1280 512; do
422 if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
423 echo "${b}"
424 return 0
425 fi
426 done
427
428 return 1
429 }
430
431 get_root_nameservers() {
432 while read -r hostname ttl record address; do
433 # Searching for A records
434 [ "${record}" = "A" ] || continue
435
436 echo "${address}"
437 done < /etc/unbound/root.hints
438 }
439
440 can_resolve_root() {
441 local ns
442 for ns in $(get_root_nameservers); do
443 if dig @${ns} +dnssec SOA . $@ >/dev/null; then
444 return 0
445 fi
446 done
447
448 # none of the servers was reachable
449 return 1
450 }
451
452 enable_dnssec() {
453 local status=$(unbound-control get_option val-permissive-mode)
454
455 # Log DNSSEC status
456 echo "on" > /var/ipfire/red/dnssec-status
457
458 # Don't do anything if DNSSEC is already activated
459 [ "${status}" = "no" ] && return 0
460
461 # Activate DNSSEC and flush cache with any stale and unvalidated data
462 unbound-control -q set_option val-permissive-mode: no
463 unbound-control -q flush_zone .
464 }
465
466 disable_dnssec() {
467 # Log DNSSEC status
468 echo "off" > /var/ipfire/red/dnssec-status
469
470 unbound-control -q set_option val-permissive-mode: yes
471 }
472
473 fix_time_if_dns_fail() {
474 # If DNS still not work try to init ntp with
475 # hardcoded ntp.ipfire.org (81.3.27.46)
476 if [ "$(</sys/class/net/$(</var/ipfire/red/iface)/carrier)" = "1" ]; then
477 host 0.ipfire.pool.ntp.org > /dev/null 2>&1
478 if [ "${?}" != "0" ]; then
479 boot_mesg "DNS still not functioning... Trying to sync time with ntp.ipfire.org (81.3.27.46)..."
480 loadproc /usr/local/bin/settime 81.3.27.46
481 fi
482 fi
483 }
484
485 resolve() {
486 local hostname="${1}"
487
488 local found=0
489 local ns
490 for ns in $(read_name_servers); do
491 local answer
492 for answer in $(dig +short "@${ns}" A "${hostname}"); do
493 found=1
494
495 # Filter out non-IP addresses
496 if [[ ! "${answer}" =~ \.$ ]]; then
497 echo "${answer}"
498 fi
499 done
500
501 # End loop when we have got something
502 [ ${found} -eq 1 ] && break
503 done
504 }
505
506 # Sets up Safe Search for various search engines
507 write_safe_search_conf() {
508 local google_tlds=(
509 google.ad
510 google.ae
511 google.al
512 google.am
513 google.as
514 google.at
515 google.az
516 google.ba
517 google.be
518 google.bf
519 google.bg
520 google.bi
521 google.bj
522 google.bs
523 google.bt
524 google.by
525 google.ca
526 google.cat
527 google.cd
528 google.cf
529 google.cg
530 google.ch
531 google.ci
532 google.cl
533 google.cm
534 google.cn
535 google.co.ao
536 google.co.bw
537 google.co.ck
538 google.co.cr
539 google.co.id
540 google.co.il
541 google.co.in
542 google.co.jp
543 google.co.ke
544 google.co.kr
545 google.co.ls
546 google.com
547 google.co.ma
548 google.com.af
549 google.com.ag
550 google.com.ai
551 google.com.ar
552 google.com.au
553 google.com.bd
554 google.com.bh
555 google.com.bn
556 google.com.bo
557 google.com.br
558 google.com.bz
559 google.com.co
560 google.com.cu
561 google.com.cy
562 google.com.do
563 google.com.ec
564 google.com.eg
565 google.com.et
566 google.com.fj
567 google.com.gh
568 google.com.gi
569 google.com.gt
570 google.com.hk
571 google.com.jm
572 google.com.kh
573 google.com.kw
574 google.com.lb
575 google.com.ly
576 google.com.mm
577 google.com.mt
578 google.com.mx
579 google.com.my
580 google.com.na
581 google.com.nf
582 google.com.ng
583 google.com.ni
584 google.com.np
585 google.com.om
586 google.com.pa
587 google.com.pe
588 google.com.pg
589 google.com.ph
590 google.com.pk
591 google.com.pr
592 google.com.py
593 google.com.qa
594 google.com.sa
595 google.com.sb
596 google.com.sg
597 google.com.sl
598 google.com.sv
599 google.com.tj
600 google.com.tr
601 google.com.tw
602 google.com.ua
603 google.com.uy
604 google.com.vc
605 google.com.vn
606 google.co.mz
607 google.co.nz
608 google.co.th
609 google.co.tz
610 google.co.ug
611 google.co.uk
612 google.co.uz
613 google.co.ve
614 google.co.vi
615 google.co.za
616 google.co.zm
617 google.co.zw
618 google.cv
619 google.cz
620 google.de
621 google.dj
622 google.dk
623 google.dm
624 google.dz
625 google.ee
626 google.es
627 google.fi
628 google.fm
629 google.fr
630 google.ga
631 google.ge
632 google.gg
633 google.gl
634 google.gm
635 google.gp
636 google.gr
637 google.gy
638 google.hn
639 google.hr
640 google.ht
641 google.hu
642 google.ie
643 google.im
644 google.iq
645 google.is
646 google.it
647 google.je
648 google.jo
649 google.kg
650 google.ki
651 google.kz
652 google.la
653 google.li
654 google.lk
655 google.lt
656 google.lu
657 google.lv
658 google.md
659 google.me
660 google.mg
661 google.mk
662 google.ml
663 google.mn
664 google.ms
665 google.mu
666 google.mv
667 google.mw
668 google.ne
669 google.nl
670 google.no
671 google.nr
672 google.nu
673 google.pl
674 google.pn
675 google.ps
676 google.pt
677 google.ro
678 google.rs
679 google.ru
680 google.rw
681 google.sc
682 google.se
683 google.sh
684 google.si
685 google.sk
686 google.sm
687 google.sn
688 google.so
689 google.sr
690 google.st
691 google.td
692 google.tg
693 google.tk
694 google.tl
695 google.tm
696 google.tn
697 google.to
698 google.tt
699 google.vg
700 google.vu
701 google.ws
702 )
703
704 (
705 # Nothing to do if safe search is not enabled
706 if [ "${ENABLE_SAFE_SEARCH}" != "on" ]; then
707 exit 0
708 fi
709
710 # This all belongs into the server: section
711 echo "server:"
712
713 # Bing
714 echo " local-zone: bing.com transparent"
715 for address in $(resolve "strict.bing.com"); do
716 echo " local-data: \"www.bing.com ${LOCAL_TTL} IN A ${address}\""
717 done
718
719 # DuckDuckGo
720 echo " local-zone: duckduckgo.com typetransparent"
721 for address in $(resolve "safe.duckduckgo.com"); do
722 echo " local-data: \"duckduckgo.com ${LOCAL_TTL} IN A ${address}\""
723 done
724
725 # Google
726 addresses="$(resolve "forcesafesearch.google.com")"
727 local domain
728 for domain in ${google_tlds[@]}; do
729 echo " local-zone: ${domain} transparent"
730 for address in ${addresses}; do
731 echo " local-data: \"www.${domain} ${LOCAL_TTL} IN A ${address}\""
732 done
733 done
734
735 # Yandex
736 for domain in yandex.com yandex.ru; do
737 echo " local-zone: ${domain} typetransparent"
738 for address in $(resolve "familysearch.${domain}"); do
739 echo " local-data: \"${domain} ${LOCAL_TTL} IN A ${address}\""
740 done
741 done
742
743 # YouTube
744 echo " local-zone: youtube.com transparent"
745 for address in $(resolve "restrictmoderate.youtube.com"); do
746 echo " local-data: \"www.youtube.com ${LOCAL_TTL} IN A ${address}\""
747 done
748 ) > /etc/unbound/safe-search.conf
749 }
750
751 case "$1" in
752 start)
753 # Print a nicer messagen when unbound is already running
754 if pidofproc -s unbound; then
755 statusproc /usr/sbin/unbound
756 exit 0
757 fi
758
759 eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
760
761 # Update configuration files
762 write_tuning_conf
763 write_forward_conf
764 write_safe_search_conf
765
766 boot_mesg "Starting Unbound DNS Proxy..."
767 loadproc /usr/sbin/unbound || exit $?
768
769 # Make own hostname resolveable
770 own_hostname
771
772 # Update any known forwarding name servers
773 update_forwarders
774
775 # Update hosts
776 update_hosts
777
778 fix_time_if_dns_fail
779 ;;
780
781 stop)
782 boot_mesg "Stopping Unbound DNS Proxy..."
783 killproc /usr/sbin/unbound
784 ;;
785
786 restart)
787 $0 stop
788 sleep 1
789 $0 start
790 ;;
791
792 status)
793 statusproc /usr/sbin/unbound
794 ;;
795
796 update-forwarders)
797 # Do not try updating forwarders when unbound is not running
798 if ! pgrep unbound &>/dev/null; then
799 exit 0
800 fi
801
802 update_forwarders
803
804 unbound-control flush_negative > /dev/null
805 unbound-control flush_bogus > /dev/null
806
807 fix_time_if_dns_fail
808 ;;
809
810 test-name-server)
811 ns=${2}
812
813 test_name_server ${ns}
814 ret=${?}
815
816 case "${ret}" in
817 0)
818 echo "${ns} is validating"
819 ;;
820 2)
821 echo "${ns} is DNSSEC-aware"
822 ;;
823 3)
824 echo "${ns} is NOT DNSSEC-aware"
825 ;;
826 *)
827 echo "Test failed for an unknown reason"
828 exit ${ret}
829 ;;
830 esac
831
832 if ns_supports_tcp ${ns}; then
833 echo "${ns} supports TCP fallback"
834 else
835 echo "${ns} does not support TCP fallback"
836 fi
837
838 edns_buffer_size=$(ns_determine_edns_buffer_size ${ns})
839 if [ -n "${edns_buffer_size}" ]; then
840 echo "EDNS buffer size for ${ns}: ${edns_buffer_size}"
841 fi
842
843 exit ${ret}
844 ;;
845
846 resolve)
847 resolve "${2}"
848 ;;
849
850 *)
851 echo "Usage: $0 {start|stop|restart|status|update-forwarders|test-name-server|resolve}"
852 exit 1
853 ;;
854 esac
855
856 # End $rc_base/init.d/unbound