Project

General

Profile

Download (24.8 KB) Statistics
| Branch: | Tag: | Revision:
1 f20afeb6 Warren Baker
<?php
2
/*
3 ac24dc24 Renato Botelho
 * unbound.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 c5d81585 Renato Botelho
 * Copyright (c) 2015 Warren Baker <warren@percol8.co.za>
7 81299b5c Renato Botelho
 * Copyright (c) 2015-2016 Rubicon Communications, LLC (Netgate)
8 ac24dc24 Renato Botelho
 * All rights reserved.
9
 *
10
 * originally part of m0n0wall (http://m0n0.ch/wall)
11 c5d81585 Renato Botelho
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
12 ac24dc24 Renato Botelho
 * All rights reserved.
13
 *
14 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
15
 * you may not use this file except in compliance with the License.
16
 * You may obtain a copy of the License at
17 ac24dc24 Renato Botelho
 *
18 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
19 ac24dc24 Renato Botelho
 *
20 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
21
 * distributed under the License is distributed on an "AS IS" BASIS,
22
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
 * See the License for the specific language governing permissions and
24
 * limitations under the License.
25 ac24dc24 Renato Botelho
 */
26 f20afeb6 Warren Baker
27
/* include all configuration functions */
28
require_once("config.inc");
29
require_once("functions.inc");
30 3bdf2a70 Chris Buechler
require_once("filter.inc");
31
require_once("shaper.inc");
32 f20afeb6 Warren Baker
33 1548bd35 Phil Davis
function create_unbound_chroot_path($cfgsubdir = "") {
34 e318d592 Phil Davis
	global $config, $g;
35
36
	// Configure chroot
37
	if (!is_dir($g['unbound_chroot_path'])) {
38
		mkdir($g['unbound_chroot_path']);
39
		chown($g['unbound_chroot_path'], "unbound");
40
		chgrp($g['unbound_chroot_path'], "unbound");
41
	}
42
43 1548bd35 Phil Davis
	if ($cfgsubdir != "") {
44
		$cfgdir = $g['unbound_chroot_path'] . $cfgsubdir;
45
		if (!is_dir($cfgdir)) {
46
			mkdir($cfgdir);
47
			chown($cfgdir, "unbound");
48
			chgrp($cfgdir, "unbound");
49
		}
50
	}
51 e318d592 Phil Davis
}
52
53 f20afeb6 Warren Baker
/* Optimize Unbound for environment */
54
function unbound_optimization() {
55 fe9d4894 Renato Botelho
	global $config;
56
57
	$optimization_settings = array();
58
59 751533a2 Phil Davis
	/*
60 fe9d4894 Renato Botelho
	 * Set the number of threads equal to number of CPUs.
61
	 * Use 1 to disable threading, if for some reason this sysctl fails.
62
	 */
63
	$numprocs = intval(get_single_sysctl('kern.smp.cpus'));
64 46762efe Warren Baker
	if ($numprocs > 1) {
65 fe9d4894 Renato Botelho
		$optimization['number_threads'] = "num-threads: {$numprocs}";
66 086cf944 Phil Davis
		$optimize_num = pow(2, floor(log($numprocs, 2)));
67 46762efe Warren Baker
	} else {
68 fe9d4894 Renato Botelho
		$optimization['number_threads'] = "num-threads: 1";
69 46762efe Warren Baker
		$optimize_num = 4;
70
	}
71 fe9d4894 Renato Botelho
72
	// Slabs to help reduce lock contention.
73 46762efe Warren Baker
	$optimization['msg_cache_slabs'] = "msg-cache-slabs: {$optimize_num}";
74
	$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: {$optimize_num}";
75
	$optimization['infra_cache_slabs'] = "infra-cache-slabs: {$optimize_num}";
76
	$optimization['key_cache_slabs'] = "key-cache-slabs: {$optimize_num}";
77 fe9d4894 Renato Botelho
78
	/*
79
	 * Larger socket buffer for busy servers
80
	 * Check that it is set to 4MB (by default the OS has it configured to 4MB)
81
	 */
82 d87fcac9 Ermal
	if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) {
83
		foreach ($config['sysctl']['item'] as $tunable) {
84
			if ($tunable['tunable'] == 'kern.ipc.maxsockbuf') {
85 ad04bbbf Warren Baker
				$so = floor(($tunable['value']/1024/1024)-4);
86 d87fcac9 Ermal
				// Check to ensure that the number is not a negative
87 ad04bbbf Warren Baker
				if ($so >= 4) {
88
					// Limit to 32MB, users might set maxsockbuf very high for other reasons.
89 4708c6f0 Phil Davis
					// We do not want unbound to fail because of that.
90 ad04bbbf Warren Baker
					$so = min($so, 32);
91 d87fcac9 Ermal
					$optimization['so_rcvbuf'] = "so-rcvbuf: {$so}m";
92 ad04bbbf Warren Baker
				} else {
93 d87fcac9 Ermal
					unset($optimization['so_rcvbuf']);
94 ad04bbbf Warren Baker
				}
95 d87fcac9 Ermal
			}
96 fe9d4894 Renato Botelho
		}
97
	}
98
	// Safety check in case kern.ipc.maxsockbuf is not available.
99 751533a2 Phil Davis
	if (!isset($optimization['so_rcvbuf'])) {
100 fe9d4894 Renato Botelho
		$optimization['so_rcvbuf'] = "#so-rcvbuf: 4m";
101 751533a2 Phil Davis
	}
102 fe9d4894 Renato Botelho
103
	return $optimization;
104 f20afeb6 Warren Baker
105
}
106
107 932711c7 Matt Smith
function test_unbound_config($unboundcfg, &$output) {
108
	global $g;
109
110 1548bd35 Phil Davis
	$cfgsubdir = "/test";
111
	unbound_generate_config($unboundcfg, $cfgsubdir);
112
	unbound_remote_control_setup($cfgsubdir);
113
	do_as_unbound_user("unbound-anchor", $cfgsubdir);
114
115
	$cfgdir = "{$g['unbound_chroot_path']}{$cfgsubdir}";
116 932711c7 Matt Smith
117
	$rv = 0;
118 1548bd35 Phil Davis
	exec("/usr/local/sbin/unbound-checkconf {$cfgdir}/unbound.conf 2>&1", $output, $rv);
119
	rmdir_recursive($cfgdir);
120 932711c7 Matt Smith
121
	return $rv;
122
}
123
124
125 1548bd35 Phil Davis
function unbound_generate_config($unboundcfg = NULL, $cfgsubdir = "") {
126 a569071d Phil Davis
	global $g;
127 932711c7 Matt Smith
128 1548bd35 Phil Davis
	$unboundcfgtxt = unbound_generate_config_text($unboundcfg, $cfgsubdir);
129 932711c7 Matt Smith
130
	// Configure static Host entries
131 1548bd35 Phil Davis
	unbound_add_host_entries($cfgsubdir);
132 932711c7 Matt Smith
133
	// Configure Domain Overrides
134 1548bd35 Phil Davis
	unbound_add_domain_overrides("", $cfgsubdir);
135 932711c7 Matt Smith
136
	// Configure Unbound access-lists
137 1548bd35 Phil Davis
	unbound_acls_config($cfgsubdir);
138 932711c7 Matt Smith
139 1548bd35 Phil Davis
	create_unbound_chroot_path($cfgsubdir);
140
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/unbound.conf", $unboundcfgtxt);
141 932711c7 Matt Smith
}
142
143
144 1548bd35 Phil Davis
function unbound_generate_config_text($unboundcfg = NULL, $cfgsubdir = "") {
145 932711c7 Matt Smith
146 fe9d4894 Renato Botelho
	global $config, $g;
147 932711c7 Matt Smith
	if (is_null($unboundcfg)) {
148
		$unboundcfg = $config['unbound'];
149
	}
150 fe9d4894 Renato Botelho
151
	// Setup optimization
152
	$optimization = unbound_optimization();
153
154
	// Setup DNSSEC support
155 932711c7 Matt Smith
	if (isset($unboundcfg['dnssec'])) {
156 fe9d4894 Renato Botelho
		$module_config = "validator iterator";
157 1548bd35 Phil Davis
		$anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}{$cfgsubdir}/root.key";
158 751533a2 Phil Davis
	} else {
159 fe9d4894 Renato Botelho
		$module_config = "iterator";
160 751533a2 Phil Davis
	}
161 fe9d4894 Renato Botelho
162
	// Setup DNS Rebinding
163
	if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
164
		// Private-addresses for DNS Rebinding
165
		$private_addr = <<<EOF
166 f20afeb6 Warren Baker
# For DNS Rebinding prevention
167
private-address: 10.0.0.0/8
168
private-address: 172.16.0.0/12
169 c683f627 Chris Buechler
private-address: 169.254.0.0/16
170 f20afeb6 Warren Baker
private-address: 192.168.0.0/16
171
private-address: fd00::/8
172
private-address: fe80::/10
173
EOF;
174 fe9d4894 Renato Botelho
	}
175
176
	// Determine interfaces to run on
177
	$bindints = "";
178 932711c7 Matt Smith
	if (!empty($unboundcfg['active_interface'])) {
179
		$active_interfaces = explode(",", $unboundcfg['active_interface']);
180 b7960673 Chris Buechler
		if (in_array("all", $active_interfaces, true)) {
181
			$bindints .= "interface: 0.0.0.0\n";
182
			$bindints .= "interface: ::0\n";
183 f358fe3f Chris Buechler
			$bindints .= "interface-automatic: yes\n";
184 b7960673 Chris Buechler
		} else {
185 751533a2 Phil Davis
			foreach ($active_interfaces as $ubif) {
186 a0e9e17d Chris Buechler
				if (is_ipaddr($ubif)) {
187 6cffaccc Chris Buechler
					$bindints .= "interface: $ubif\n";
188 a0e9e17d Chris Buechler
				} else {
189
					$intip = get_interface_ip($ubif);
190 751533a2 Phil Davis
					if (is_ipaddrv4($intip)) {
191 a0e9e17d Chris Buechler
						$bindints .= "interface: $intip\n";
192 751533a2 Phil Davis
					}
193 a0e9e17d Chris Buechler
					$intip = get_interface_ipv6($ubif);
194 751533a2 Phil Davis
					if (is_ipaddrv6($intip)) {
195 7cc0a76b Luiz Otavio O Souza
						$bindints .= "interface: $intip\n";
196 751533a2 Phil Davis
					}
197 a0e9e17d Chris Buechler
				}
198 751533a2 Phil Davis
			}
199 fe9d4894 Renato Botelho
		}
200
	} else {
201
		$bindints .= "interface: 0.0.0.0\n";
202
		$bindints .= "interface: ::0\n";
203 a2cbbb74 jim-p
		/* If the active interface array is empty, treat it the same as "All" as is done above. Otherwise it breaks CARP with a default config. */
204
		$bindints .= "interface-automatic: yes\n";
205 fe9d4894 Renato Botelho
	}
206
207
	// Determine interfaces to run on
208
	$outgoingints = "";
209 932711c7 Matt Smith
	if (!empty($unboundcfg['outgoing_interface'])) {
210 fe9d4894 Renato Botelho
		$outgoingints = "# Outgoing interfaces to be used\n";
211 932711c7 Matt Smith
		$outgoing_interfaces = explode(",", $unboundcfg['outgoing_interface']);
212 751533a2 Phil Davis
		foreach ($outgoing_interfaces as $outif) {
213 fe9d4894 Renato Botelho
			$outip = get_interface_ip($outif);
214 c37ffea8 Chris Buechler
			if (is_ipaddr($outip)) {
215 fe9d4894 Renato Botelho
				$outgoingints .= "outgoing-interface: $outip\n";
216 751533a2 Phil Davis
			}
217 fe9d4894 Renato Botelho
			$outip = get_interface_ipv6($outif);
218 c37ffea8 Chris Buechler
			if (is_ipaddrv6($outip)) {
219 fe9d4894 Renato Botelho
				$outgoingints .= "outgoing-interface: $outip\n";
220 751533a2 Phil Davis
			}
221 fe9d4894 Renato Botelho
		}
222
	}
223
224
	// Allow DNS Rebind for forwarded domains
225 932711c7 Matt Smith
	if (isset($unboundcfg['domainoverrides']) && is_array($unboundcfg['domainoverrides'])) {
226 984abd66 Phil Davis
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
227
			$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
228
			$private_domains .= unbound_add_domain_overrides("private");
229
		}
230
		$reverse_zones .= unbound_add_domain_overrides("reverse");
231 fe9d4894 Renato Botelho
	}
232
233 ab8f10f2 Phil Davis
	// Configure Unbound statistics
234
	$statistics = unbound_statistics();
235
236 fe9d4894 Renato Botelho
	// Add custom Unbound options
237 932711c7 Matt Smith
	if ($unboundcfg['custom_options']) {
238
		$custom_options_source = explode("\n", base64_decode($unboundcfg['custom_options']));
239 b9608ab6 Phil Davis
		$custom_options = "# Unbound custom options\n";
240 751533a2 Phil Davis
		foreach ($custom_options_source as $ent) {
241 b9608ab6 Phil Davis
			$custom_options .= $ent."\n";
242 751533a2 Phil Davis
		}
243 fe9d4894 Renato Botelho
	}
244
245
	// Server configuration variables
246 932711c7 Matt Smith
	$port = (is_port($unboundcfg['port'])) ? $unboundcfg['port'] : "53";
247
	$hide_identity = isset($unboundcfg['hideidentity']) ? "yes" : "no";
248
	$hide_version = isset($unboundcfg['hideversion']) ? "yes" : "no";
249
	$harden_dnssec_stripped = isset($unboundcfg['dnssecstripped']) ? "yes" : "no";
250
	$prefetch = isset($unboundcfg['prefetch']) ? "yes" : "no";
251
	$prefetch_key = isset($unboundcfg['prefetchkey']) ? "yes" : "no";
252 84ec58b7 marjohn56
	$dns_record_cache = isset($unboundcfg['dnsrecordcache']) ? "yes" : "no";
253 f8a475f5 lukehamburg
	$outgoing_num_tcp = isset($unboundcfg['outgoing_num_tcp']) ? $unboundcfg['outgoing_num_tcp'] : "10";
254
	$incoming_num_tcp = isset($unboundcfg['incoming_num_tcp']) ? $unboundcfg['incoming_num_tcp'] : "10";
255 932711c7 Matt Smith
	$edns_buffer_size = (!empty($unboundcfg['edns_buffer_size'])) ? $unboundcfg['edns_buffer_size'] : "4096";
256
	$num_queries_per_thread = (!empty($unboundcfg['num_queries_per_thread'])) ? $unboundcfg['num_queries_per_thread'] : "4096";
257
	$jostle_timeout = (!empty($unboundcfg['jostle_timeout'])) ? $unboundcfg['jostle_timeout'] : "200";
258
	$cache_max_ttl = (!empty($unboundcfg['cache_max_ttl'])) ? $unboundcfg['cache_max_ttl'] : "86400";
259
	$cache_min_ttl = (!empty($unboundcfg['cache_min_ttl'])) ? $unboundcfg['cache_min_ttl'] : "0";
260
	$infra_host_ttl = (!empty($unboundcfg['infra_host_ttl'])) ? $unboundcfg['infra_host_ttl'] : "900";
261
	$infra_cache_numhosts = (!empty($unboundcfg['infra_cache_numhosts'])) ? $unboundcfg['infra_cache_numhosts'] : "10000";
262
	$unwanted_reply_threshold = (!empty($unboundcfg['unwanted_reply_threshold'])) ? $unboundcfg['unwanted_reply_threshold'] : "0";
263 751533a2 Phil Davis
	if ($unwanted_reply_threshold == "disabled") {
264 b9608ab6 Phil Davis
		$unwanted_reply_threshold = "0";
265 751533a2 Phil Davis
	}
266 932711c7 Matt Smith
	$msg_cache_size = (!empty($unboundcfg['msgcachesize'])) ? $unboundcfg['msgcachesize'] : "4";
267
	$verbosity = isset($unboundcfg['log_verbosity']) ? $unboundcfg['log_verbosity'] : 1;
268
	$use_caps = isset($unboundcfg['use_caps']) ? "yes" : "no";
269 fe9d4894 Renato Botelho
270 a110a0cb Phil Davis
	// Set up forwarding if it is configured
271 932711c7 Matt Smith
	if (isset($unboundcfg['forwarding'])) {
272 fe9d4894 Renato Botelho
		$dnsservers = array();
273
		if (isset($config['system']['dnsallowoverride'])) {
274
			$ns = array_unique(get_nameservers());
275 751533a2 Phil Davis
			foreach ($ns as $nameserver) {
276
				if ($nameserver) {
277 fe9d4894 Renato Botelho
					$dnsservers[] = $nameserver;
278 751533a2 Phil Davis
				}
279 fe9d4894 Renato Botelho
			}
280 9b837c5d Phil Davis
		} else {
281
			$ns = array();
282 a110a0cb Phil Davis
		}
283
		$sys_dnsservers = array_unique(get_dns_servers());
284
		foreach ($sys_dnsservers as $sys_dnsserver) {
285
			if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) {
286
				$dnsservers[] = $sys_dnsserver;
287 fe9d4894 Renato Botelho
			}
288
		}
289
290
		if (!empty($dnsservers)) {
291
			$forward_conf .=<<<EOD
292 75e6d1b2 Warren Baker
# Forwarding
293 be5aa310 Warren Baker
forward-zone:
294 fe9d4894 Renato Botelho
	name: "."
295 be5aa310 Warren Baker
296
EOD;
297 751533a2 Phil Davis
			foreach ($dnsservers as $dnsserver) {
298 84588e00 Chris Buechler
				if (is_ipaddr($dnsserver) && !ip_in_subnet($dnsserver, "127.0.0.0/8")) {
299
					$forward_conf .= "\tforward-addr: $dnsserver\n";
300
				}
301 751533a2 Phil Davis
			}
302 fe9d4894 Renato Botelho
		}
303 751533a2 Phil Davis
	} else {
304 fe9d4894 Renato Botelho
		$forward_conf = "";
305 751533a2 Phil Davis
	}
306 be5aa310 Warren Baker
307 2597415b Chris Buechler
	// Size of the RRset cache == 2 * msg-cache-size per Unbound's recommendations
308
	$rrset_cache_size = $msg_cache_size * 2;
309
310 fe9d4894 Renato Botelho
	$unboundconf = <<<EOD
311 f20afeb6 Warren Baker
##########################
312
# Unbound Configuration
313
##########################
314
315
##
316
# Server configuration
317
##
318
server:
319 984abd66 Phil Davis
{$reverse_zones}
320 f20afeb6 Warren Baker
chroot: {$g['unbound_chroot_path']}
321
username: "unbound"
322 56a87b19 Warren Baker
directory: "{$g['unbound_chroot_path']}"
323 f20afeb6 Warren Baker
pidfile: "/var/run/unbound.pid"
324
use-syslog: yes
325 d12889b0 Warren Baker
port: {$port}
326 56a87b19 Warren Baker
verbosity: {$verbosity}
327 b9608ab6 Phil Davis
hide-identity: {$hide_identity}
328
hide-version: {$hide_version}
329 5c7c369f Chris Buechler
harden-glue: yes
330 f20afeb6 Warren Baker
do-ip4: yes
331
do-ip6: yes
332
do-udp: yes
333
do-tcp: yes
334
do-daemonize: yes
335
module-config: "{$module_config}"
336 b9608ab6 Phil Davis
unwanted-reply-threshold: {$unwanted_reply_threshold}
337
num-queries-per-thread: {$num_queries_per_thread}
338
jostle-timeout: {$jostle_timeout}
339
infra-host-ttl: {$infra_host_ttl}
340
infra-cache-numhosts: {$infra_cache_numhosts}
341
outgoing-num-tcp: {$outgoing_num_tcp}
342
incoming-num-tcp: {$incoming_num_tcp}
343
edns-buffer-size: {$edns_buffer_size}
344 56a87b19 Warren Baker
cache-max-ttl: {$cache_max_ttl}
345
cache-min-ttl: {$cache_min_ttl}
346 b9608ab6 Phil Davis
harden-dnssec-stripped: {$harden_dnssec_stripped}
347
msg-cache-size: {$msg_cache_size}m
348 2597415b Chris Buechler
rrset-cache-size: {$rrset_cache_size}m
349
350 f20afeb6 Warren Baker
{$optimization['number_threads']}
351
{$optimization['msg_cache_slabs']}
352
{$optimization['rrset_cache_slabs']}
353
{$optimization['infra_cache_slabs']}
354
{$optimization['key_cache_slabs']}
355 2cbcc256 Warren Baker
outgoing-range: 4096
356 f20afeb6 Warren Baker
{$optimization['so_rcvbuf']}
357
{$anchor_file}
358 56a87b19 Warren Baker
prefetch: {$prefetch}
359
prefetch-key: {$prefetch_key}
360 a771a6ae Warren Baker
use-caps-for-id: {$use_caps}
361 84ec58b7 marjohn56
serve-expired: {$dns_record_cache}
362 f20afeb6 Warren Baker
# Statistics
363
{$statistics}
364
# Interface IP(s) to bind to
365 16a3108f Warren Baker
{$bindints}
366 6374fb57 Warren Baker
{$outgoingints}
367 f20afeb6 Warren Baker
368
# DNS Rebinding
369
{$private_addr}
370
{$private_domains}
371
372 4e8e8cc8 Warren Baker
# Access lists
373 1548bd35 Phil Davis
include: {$g['unbound_chroot_path']}{$cfgsubdir}/access_lists.conf
374 4e8e8cc8 Warren Baker
375 f20afeb6 Warren Baker
# Static host entries
376 1548bd35 Phil Davis
include: {$g['unbound_chroot_path']}{$cfgsubdir}/host_entries.conf
377 f20afeb6 Warren Baker
378 b3977493 Renato Botelho
# dhcp lease entries
379 1548bd35 Phil Davis
include: {$g['unbound_chroot_path']}{$cfgsubdir}/dhcpleases_entries.conf
380 b3977493 Renato Botelho
381 f20afeb6 Warren Baker
# Domain overrides
382 1548bd35 Phil Davis
include: {$g['unbound_chroot_path']}{$cfgsubdir}/domainoverrides.conf
383 be5aa310 Warren Baker
{$forward_conf}
384
385 f20afeb6 Warren Baker
{$custom_options}
386
387
###
388
# Remote Control Config
389
###
390 1548bd35 Phil Davis
include: {$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf
391 f20afeb6 Warren Baker
392
EOD;
393
394 932711c7 Matt Smith
	return $unboundconf;
395 f20afeb6 Warren Baker
}
396
397 1548bd35 Phil Davis
function unbound_remote_control_setup($cfgsubdir = "") {
398 fe9d4894 Renato Botelho
	global $g;
399 f20afeb6 Warren Baker
400 1548bd35 Phil Davis
	if (!file_exists("{$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.key")) {
401 fe9d4894 Renato Botelho
		$remotcfg = <<<EOF
402 f20afeb6 Warren Baker
remote-control:
403 fe9d4894 Renato Botelho
	control-enable: yes
404
	control-interface: 127.0.0.1
405
	control-port: 953
406 1548bd35 Phil Davis
	server-key-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_server.key"
407
	server-cert-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_server.pem"
408
	control-key-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.key"
409
	control-cert-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.pem"
410 56a87b19 Warren Baker
411 f20afeb6 Warren Baker
EOF;
412
413 1548bd35 Phil Davis
		create_unbound_chroot_path($cfgsubdir);
414
		file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf", $remotcfg);
415 f20afeb6 Warren Baker
416 fe9d4894 Renato Botelho
		// Generate our keys
417 1548bd35 Phil Davis
		do_as_unbound_user("unbound-control-setup", $cfgsubdir);
418 f20afeb6 Warren Baker
419 fe9d4894 Renato Botelho
	}
420 56a87b19 Warren Baker
}
421 f20afeb6 Warren Baker
422
function sync_unbound_service() {
423 fe9d4894 Renato Botelho
	global $config, $g;
424
425 e318d592 Phil Davis
	create_unbound_chroot_path();
426 fe9d4894 Renato Botelho
427
	// Configure our Unbound service
428
	do_as_unbound_user("unbound-anchor");
429
	unbound_remote_control_setup();
430
	unbound_generate_config();
431
	do_as_unbound_user("start");
432
	require_once("service-utils.inc");
433 751533a2 Phil Davis
	if (is_service_running("unbound")) {
434 fe9d4894 Renato Botelho
		do_as_unbound_user("restore_cache");
435 751533a2 Phil Davis
	}
436 f20afeb6 Warren Baker
437
}
438
439
function unbound_acl_id_used($id) {
440 fe9d4894 Renato Botelho
	global $config;
441 f20afeb6 Warren Baker
442 751533a2 Phil Davis
	if (is_array($config['unbound']['acls'])) {
443
		foreach ($config['unbound']['acls'] as & $acls) {
444
			if ($id == $acls['aclid']) {
445 fe9d4894 Renato Botelho
				return true;
446 751533a2 Phil Davis
			}
447
		}
448
	}
449 f20afeb6 Warren Baker
450 fe9d4894 Renato Botelho
	return false;
451 f20afeb6 Warren Baker
}
452
453
function unbound_get_next_id() {
454 fe9d4894 Renato Botelho
	$aclid = 0;
455 751533a2 Phil Davis
	while (unbound_acl_id_used($aclid)) {
456 fe9d4894 Renato Botelho
		$aclid++;
457 751533a2 Phil Davis
	}
458 fe9d4894 Renato Botelho
	return $aclid;
459 f20afeb6 Warren Baker
}
460
461
// Execute commands as the user unbound
462 1548bd35 Phil Davis
function do_as_unbound_user($cmd, $param1 = "") {
463 fe9d4894 Renato Botelho
	global $g;
464
465
	switch ($cmd) {
466 751533a2 Phil Davis
		case "start":
467
			mwexec("/usr/local/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf");
468
			break;
469
		case "stop":
470 8a0aa42c jim-p
			mwexec("echo '/usr/local/sbin/unbound-control -c {$g['unbound_chroot_path']}/unbound.conf stop' | /usr/bin/su -m unbound", true);
471 751533a2 Phil Davis
			break;
472
		case "reload":
473 8a0aa42c jim-p
			mwexec("echo '/usr/local/sbin/unbound-control -c {$g['unbound_chroot_path']}/unbound.conf reload' | /usr/bin/su -m unbound", true);
474 751533a2 Phil Davis
			break;
475
		case "unbound-anchor":
476 1548bd35 Phil Davis
			$root_key_file = "{$g['unbound_chroot_path']}{$param1}/root.key";
477 4eeb2809 Chris Buechler
			// sanity check root.key because unbound-anchor will fail without manual removal otherwise. redmine #5334
478 1548bd35 Phil Davis
			if (file_exists($root_key_file)) {
479
				$rootkeycheck = mwexec("/usr/bin/grep 'autotrust trust anchor file' {$root_key_file}", true);
480 4eeb2809 Chris Buechler
				if ($rootkeycheck != "0") {
481 1548bd35 Phil Davis
					log_error("Unbound {$root_key_file} file is corrupt, removing and recreating.");
482
					unlink_if_exists($root_key_file);
483 4eeb2809 Chris Buechler
				}
484
			}
485 1548bd35 Phil Davis
			mwexec("echo '/usr/local/sbin/unbound-anchor -a {$root_key_file}' | /usr/bin/su -m unbound", true);
486
			// Only sync the file if this is the real (default) one, not a test one.
487
			if ($param1 == "") {
488
				pfSense_fsync($root_key_file);
489
			}
490 751533a2 Phil Davis
			break;
491
		case "unbound-control-setup":
492 1548bd35 Phil Davis
			mwexec("echo '/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}{$param1}' | /usr/bin/su -m unbound", true);
493 751533a2 Phil Davis
			break;
494
		default:
495
			break;
496 fe9d4894 Renato Botelho
	}
497 f20afeb6 Warren Baker
}
498
499 1548bd35 Phil Davis
function unbound_add_domain_overrides($pvt_rev="", $cfgsubdir = "") {
500 fe9d4894 Renato Botelho
	global $config, $g;
501
502
	$domains = $config['unbound']['domainoverrides'];
503
504
	$sorted_domains = msort($domains, "domain");
505
	$result = array();
506 751533a2 Phil Davis
	foreach ($sorted_domains as $domain) {
507 fe9d4894 Renato Botelho
		$domain_key = current($domain);
508 751533a2 Phil Davis
		if (!isset($result[$domain_key])) {
509 fe9d4894 Renato Botelho
			$result[$domain_key] = array();
510 751533a2 Phil Davis
		}
511 fe9d4894 Renato Botelho
		$result[$domain_key][] = $domain['ip'];
512
	}
513
514
	// Domain overrides that have multiple entries need multiple stub-addr: added
515
	$domain_entries = "";
516 751533a2 Phil Davis
	foreach ($result as $domain=>$ips) {
517 984abd66 Phil Davis
		if ($pvt_rev == "private") {
518 fe9d4894 Renato Botelho
			$domain_entries .= "private-domain: \"$domain\"\n";
519
			$domain_entries .= "domain-insecure: \"$domain\"\n";
520 984abd66 Phil Davis
		} else if ($pvt_rev == "reverse") {
521 086cf944 Phil Davis
			if ((substr($domain, -14) == ".in-addr.arpa.") || (substr($domain, -13) == ".in-addr.arpa")) {
522 984abd66 Phil Davis
				$domain_entries .= "local-zone: \"$domain\" typetransparent\n";
523
			}
524 fe9d4894 Renato Botelho
		} else {
525 0bde07b7 Chris Buechler
			$domain_entries .= "forward-zone:\n";
526 fe9d4894 Renato Botelho
			$domain_entries .= "\tname: \"$domain\"\n";
527 751533a2 Phil Davis
			foreach ($ips as $ip) {
528 0bde07b7 Chris Buechler
				$domain_entries .= "\tforward-addr: $ip\n";
529 751533a2 Phil Davis
			}
530 fe9d4894 Renato Botelho
		}
531
	}
532
533 751533a2 Phil Davis
	if ($pvt_rev != "") {
534 fe9d4894 Renato Botelho
		return $domain_entries;
535 751533a2 Phil Davis
	} else {
536 1548bd35 Phil Davis
		create_unbound_chroot_path($cfgsubdir);
537
		file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/domainoverrides.conf", $domain_entries);
538 e318d592 Phil Davis
	}
539 f20afeb6 Warren Baker
}
540
541 2c3b9ac5 jim-p
function unbound_generate_zone_data($domain, $hosts, &$added_ptr, $zone_type = "transparent", $write_domain_zone_declaration = false, $always_add_short_names = false) {
542 4541f84d jim-p
	global $config;
543 2c3b9ac5 jim-p
	if ($write_domain_zone_declaration) {
544 4541f84d jim-p
		$zone_data = "local-zone: \"{$domain}.\" {$zone_type}\n";
545
	} else {
546
		$zone_data = "";
547 49d9b45f Robbert Rijkse
	}
548
	foreach ($hosts as $host) {
549
		if (is_ipaddrv4($host['ipaddr'])) {
550
			$type = 'A';
551
		} else if (is_ipaddrv6($host['ipaddr'])) {
552
			$type = 'AAAA';
553
		} else {
554
			continue;
555
		}
556
		if (!$added_ptr[$host['ipaddr']]) {
557 4541f84d jim-p
			$zone_data .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
558 49d9b45f Robbert Rijkse
			$added_ptr[$host['ipaddr']] = true;
559 06266c34 Lorenz Schori
		}
560 4541f84d jim-p
		/* For the system localhost entry, write an entry for just the hostname. */
561
		if ((($host['name'] == "localhost") && ($domain == $config['system']['domain'])) || $always_add_short_names) {
562
			$zone_data .= "local-data: \"{$host['name']}. {$type} {$host['ipaddr']}\"\n";
563
		}
564
		/* Redirect zones must have a zone declaration that matches the
565
		 * local-data record exactly, it cannot have entries "under" the
566
		 * domain.
567
		 */
568
		if ($zone_type == "redirect") {
569
			$zone_data .= "local-zone: \"{$host['fqdn']}.\" {$zone_type}\n";;
570
		}
571
		$zone_data .= "local-data: \"{$host['fqdn']}. {$type} {$host['ipaddr']}\"\n";
572
	}
573
	return $zone_data;
574
}
575
576
function unbound_add_host_entries($cfgsubdir = "") {
577
	global $config, $g;
578
579
	$hosts = system_hosts_entries($config['unbound']);
580
581
	/* Pass 1: Build domain list and hosts inside domains */
582
	$hosts_by_domain = array();
583
	foreach ($hosts as $host) {
584
		if (!array_key_exists($host['domain'], $hosts_by_domain)) {
585
			$hosts_by_domain[$host['domain']] = array();
586
		}
587
		$hosts_by_domain[$host['domain']][] = $host;
588
	}
589
590
	$added_ptr = array();
591
	/* Build local zone data */
592
	// Check if auto add host entries is not set
593
	$system_domain_local_zone_type = "transparent";
594
	if (!isset($config['unbound']['disable_auto_added_host_entries'])) {
595
		// Make sure the config setting is a valid unbound local zone type.  If not use "transparent".
596
		if (array_key_exists($config['unbound']['system_domain_local_zone_type'], unbound_local_zone_types())) {
597
			$system_domain_local_zone_type = $config['unbound']['system_domain_local_zone_type'];
598
		}
599
	}
600
	/* Add entries for the system domain before all others */
601
	if (array_key_exists($config['system']['domain'], $hosts_by_domain)) {
602
		$unbound_entries .= unbound_generate_zone_data($config['system']['domain'],
603
					$hosts_by_domain[$config['system']['domain']],
604
					$added_ptr,
605
					$system_domain_local_zone_type,
606
					true);
607
		/* Unset this so it isn't processed again by the loop below. */
608
		unset($hosts_by_domain[$config['system']['domain']]);
609
	}
610
611
	/* Build zone data for other domain */
612
	foreach ($hosts_by_domain as $domain => $hosts) {
613
		$unbound_entries .= unbound_generate_zone_data($domain,
614
					$hosts,
615
					$added_ptr,
616
					"transparent",
617
					false,
618
					isset($config['unbound']['always_add_short_names']));
619 fe9d4894 Renato Botelho
	}
620
621
	// Write out entries
622 1548bd35 Phil Davis
	create_unbound_chroot_path($cfgsubdir);
623
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/host_entries.conf", $unbound_entries);
624 b3977493 Renato Botelho
625
	/* dhcpleases will write to this config file, make sure it exists */
626 1548bd35 Phil Davis
	@touch("{$g['unbound_chroot_path']}{$cfgsubdir}/dhcpleases_entries.conf");
627 f20afeb6 Warren Baker
}
628
629 56a87b19 Warren Baker
function unbound_control($action) {
630 fe9d4894 Renato Botelho
	global $config, $g;
631
632
	$cache_dumpfile = "/var/tmp/unbound_cache";
633
634
	switch ($action) {
635
	case "start":
636
		// Start Unbound
637
		if ($config['unbound']['enable'] == "on") {
638 751533a2 Phil Davis
			if (!is_service_running("unbound")) {
639 fe9d4894 Renato Botelho
				do_as_unbound_user("start");
640 751533a2 Phil Davis
			}
641 fe9d4894 Renato Botelho
		}
642
		break;
643
	case "stop":
644 751533a2 Phil Davis
		if ($config['unbound']['enable'] == "on") {
645 fe9d4894 Renato Botelho
			do_as_unbound_user("stop");
646 751533a2 Phil Davis
		}
647 fe9d4894 Renato Botelho
		break;
648
	case "reload":
649 751533a2 Phil Davis
		if ($config['unbound']['enable'] == "on") {
650 fe9d4894 Renato Botelho
			do_as_unbound_user("reload");
651 751533a2 Phil Davis
		}
652 fe9d4894 Renato Botelho
		break;
653
	case "dump_cache":
654
		// Dump Unbound's Cache
655 751533a2 Phil Davis
		if ($config['unbound']['dumpcache'] == "on") {
656 fe9d4894 Renato Botelho
			do_as_unbound_user("dump_cache");
657 751533a2 Phil Davis
		}
658 fe9d4894 Renato Botelho
		break;
659
	case "restore_cache":
660
		// Restore Unbound's Cache
661
		if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) {
662 751533a2 Phil Davis
			if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) {
663 fe9d4894 Renato Botelho
				do_as_unbound_user("load_cache < /var/tmp/unbound_cache");
664 751533a2 Phil Davis
			}
665 fe9d4894 Renato Botelho
		}
666
		break;
667
	default:
668
		break;
669
670
	}
671 56a87b19 Warren Baker
}
672
673
// Generation of Unbound statistics
674
function unbound_statistics() {
675 fe9d4894 Renato Botelho
	global $config;
676
677
	if ($config['stats'] == "on") {
678
		$stats_interval = $config['unbound']['stats_interval'];
679
		$cumulative_stats = $config['cumulative_stats'];
680 751533a2 Phil Davis
		if ($config['extended_stats'] == "on") {
681 fe9d4894 Renato Botelho
			$extended_stats = "yes";
682 751533a2 Phil Davis
		} else {
683 fe9d4894 Renato Botelho
			$extended_stats = "no";
684 751533a2 Phil Davis
		}
685 fe9d4894 Renato Botelho
	} else {
686
		$stats_interval = "0";
687
		$cumulative_stats = "no";
688
		$extended_stats = "no";
689
	}
690
	/* XXX To do - add RRD graphs */
691
	$stats = <<<EOF
692 56a87b19 Warren Baker
# Unbound Statistics
693
statistics-interval: {$stats_interval}
694
extended-statistics: yes
695
statistics-cumulative: yes
696
697
EOF;
698
699 fe9d4894 Renato Botelho
	return $stats;
700 56a87b19 Warren Baker
}
701
702 8fccab67 Warren Baker
// Unbound Access lists
703 1548bd35 Phil Davis
function unbound_acls_config($cfgsubdir = "") {
704 fe9d4894 Renato Botelho
	global $g, $config;
705
706 f8f5ba1a Chris Buechler
	if (!isset($config['unbound']['disable_auto_added_access_control'])) {
707 0266efa6 doktornotor
		$aclcfg = "access-control: 127.0.0.1/32 allow_snoop\n";
708
		$aclcfg .= "access-control: ::1 allow_snoop\n";
709 f8f5ba1a Chris Buechler
		// Add our networks for active interfaces including localhost
710 8c2a5a73 Phil Davis
		if (!empty($config['unbound']['active_interface'])) {
711 f8f5ba1a Chris Buechler
			$active_interfaces = array_flip(explode(",", $config['unbound']['active_interface']));
712 751533a2 Phil Davis
			if (in_array("all", $active_interfaces)) {
713 80075b9e Chris Buechler
				$active_interfaces = get_configured_interface_with_descr();
714 751533a2 Phil Davis
			}
715
		} else {
716 f8f5ba1a Chris Buechler
			$active_interfaces = get_configured_interface_with_descr();
717 751533a2 Phil Davis
		}
718
719 f8f5ba1a Chris Buechler
		$bindints = "";
720 751533a2 Phil Davis
		foreach ($active_interfaces as $ubif => $ifdesc) {
721 f8f5ba1a Chris Buechler
			$ifip = get_interface_ip($ubif);
722
			if (is_ipaddrv4($ifip)) {
723
				// IPv4 is handled via NAT networks below
724
			}
725
			$ifip = get_interface_ipv6($ubif);
726
			if (is_ipaddrv6($ifip)) {
727 f302a333 Jean Cyr
				if (!is_linklocal($ifip)) {
728
					$subnet_bits = get_interface_subnetv6($ubif);
729
					$subnet_ip = gen_subnetv6($ifip, $subnet_bits);
730 751533a2 Phil Davis
					// only add LAN-type interfaces
731
					if (!interface_has_gateway($ubif)) {
732 f302a333 Jean Cyr
						$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
733 751533a2 Phil Davis
					}
734 f302a333 Jean Cyr
				}
735 f8f5ba1a Chris Buechler
				// add for IPv6 static routes to local networks
736
				// for safety, we include only routes reachable on an interface with no
737 751533a2 Phil Davis
				// gateway specified - read: not an Internet connection.
738 cf08b49e Phil Davis
				$static_routes = get_staticroutes(false, false, true); // Parameter 3 returnenabledroutesonly
739 f8f5ba1a Chris Buechler
				foreach ($static_routes as $route) {
740
					if ((lookup_gateway_interface_by_name($route['gateway']) == $ubif) && !interface_has_gateway($ubif)) {
741
						// route is on this interface, interface doesn't have gateway, add it
742
						$aclcfg .= "access-control: {$route['network']} allow\n";
743
					}
744 e3045c51 Chris Buechler
				}
745
			}
746 fe9d4894 Renato Botelho
		}
747 751533a2 Phil Davis
748 f8f5ba1a Chris Buechler
		// Generate IPv4 access-control entries using the same logic as automatic outbound NAT
749
		if (empty($FilterIflist)) {
750
			filter_generate_optcfg_array();
751
		}
752
		$natnetworks_array = array();
753
		$natnetworks_array = filter_nat_rules_automatic_tonathosts();
754
		foreach ($natnetworks_array as $allowednet) {
755 751533a2 Phil Davis
			$aclcfg .= "access-control: $allowednet allow \n";
756
		}
757 3bdf2a70 Chris Buechler
	}
758 fe9d4894 Renato Botelho
759
	// Configure the custom ACLs
760
	if (is_array($config['unbound']['acls'])) {
761 751533a2 Phil Davis
		foreach ($config['unbound']['acls'] as $unbound_acl) {
762 fe9d4894 Renato Botelho
			$aclcfg .= "#{$unbound_acl['aclname']}\n";
763 751533a2 Phil Davis
			foreach ($unbound_acl['row'] as $network) {
764
				if ($unbound_acl['aclaction'] == "allow snoop") {
765 fe9d4894 Renato Botelho
					$unbound_acl['aclaction'] = "allow_snoop";
766 6a827f69 doktornotor
				} elseif ($unbound_acl['aclaction'] == "deny nonlocal") {
767
					$unbound_acl['aclaction'] = "deny_non_local";
768
				} elseif ($unbound_acl['aclaction'] == "refuse nonlocal") {
769
					$unbound_acl['aclaction'] = "refuse_non_local";
770 751533a2 Phil Davis
				}
771 fe9d4894 Renato Botelho
				$aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n";
772
			}
773
		}
774
	}
775
	// Write out Access list
776 1548bd35 Phil Davis
	create_unbound_chroot_path($cfgsubdir);
777
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/access_lists.conf", $aclcfg);
778 58d00e65 Warren Baker
779 8fccab67 Warren Baker
}
780
781 f6248774 Warren Baker
// Generate hosts and reload services
782
function unbound_hosts_generate() {
783 fe9d4894 Renato Botelho
	// Generate our hosts file
784
	unbound_add_host_entries();
785 f6248774 Warren Baker
786 fe9d4894 Renato Botelho
	// Reload our service to read the updates
787
	unbound_control("reload");
788 f6248774 Warren Baker
}
789
790 9a83872f NOYB
// Array of valid unbound local zone types
791
function unbound_local_zone_types() {
792
	return array(
793
		"deny" => gettext("Deny"),
794
		"refuse" => gettext("Refuse"),
795
		"static" => gettext("Static"),
796
		"transparent" => gettext("Transparent"),
797
		"typetransparent" => gettext("Type Transparent"),
798
		"redirect" => gettext("Redirect"),
799
		"inform" => gettext("Inform"),
800
		"inform_deny" => gettext("Inform Deny"),
801
		"nodefault" => gettext("No Default")
802
	);
803
}
804
805 3f0c20c3 Renato Botelho
?>