Skip to content

Commit cd170f3

Browse files
committed
unbound: Add support for DoH and DoT
Added additional values to the unbound statistics page which allow to monitor DoH and DoT queries.
1 parent c1784ad commit cd170f3

File tree

4 files changed

+152
-7
lines changed

4 files changed

+152
-7
lines changed

src/etc/inc/plugins.inc.d/unbound.inc

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@
3030
* POSSIBILITY OF SUCH DAMAGE.
3131
*/
3232

33+
require_once('certs.inc');
34+
35+
function export_pem_file($filename, $data, $post_append = null)
36+
{
37+
$pem_content = trim(str_replace("\n\n", "\n", str_replace(
38+
"\r",
39+
"",
40+
base64_decode((string)$data)
41+
) . ($post_append == null ? '' : "\n" . $post_append)));
42+
file_put_contents($filename, $pem_content);
43+
chmod($filename, 0640);
44+
}
45+
3346
function unbound_enabled()
3447
{
3548
$mdl = new \OPNsense\Unbound\Unbound();
@@ -176,6 +189,10 @@ function unbound_generate_config()
176189
}
177190
}
178191

192+
$port = $general['port'];
193+
$port_doh = $general['port_doh'];
194+
$port_dot = $general['port_dot'];
195+
179196
$bindints = '';
180197
if (!empty($general['active_interface'])) {
181198
$active_interfaces = explode(',', $general['active_interface']);
@@ -196,12 +213,25 @@ function unbound_generate_config()
196213
}
197214

198215
foreach ($addresses as $address) {
199-
$bindints .= "interface: $address\n";
216+
$bindints .= "interface: $address@$port\n";
217+
if (!empty($general['enable_dot'])) {
218+
$bindints .= "interface: $address@$port_dot\n";
219+
}
220+
if (!empty($general['enable_doh'])) {
221+
$bindints .= "interface: $address@$port_doh\n";
222+
}
200223
}
201224
} else {
202-
$bindints .= "interface: 0.0.0.0\n";
203-
$bindints .= "interface: ::\n";
204-
$bindints .= "interface-automatic: yes\n";
225+
$bindints .= "interface: 0.0.0.0@$port\n";
226+
$bindints .= "interface: ::@$port\n";
227+
if (!empty($general['enable_dot'])) {
228+
$bindints .= "interface: 0.0.0.0@$port_dot\n";
229+
$bindints .= "interface: ::@$port_dot\n";
230+
}
231+
if (!empty($general['enable_doh'])) {
232+
$bindints .= "interface: 0.0.0.0@$port_doh\n";
233+
$bindints .= "interface: ::@$port_doh\n";
234+
}
205235
}
206236

207237
$outgoingints = '';
@@ -223,7 +253,7 @@ function unbound_generate_config()
223253

224254
unbound_add_host_entries($ifconfig_details);
225255

226-
$port = $general['port'];
256+
227257

228258
/* do not touch prefer-ip6 as it is defaulting to 'no' anyway */
229259
$do_ip6 = isset($config['system']['ipv6allow']) ? 'yes' : 'no';
@@ -270,6 +300,38 @@ EOD;
270300

271301
$so_reuseport = empty(system_sysctl_get()['net.inet.rss.enabled']) ? 'yes' : 'no';
272302

303+
$dohdot_settings = '';
304+
if (!empty($general['enable_doh']) || !empty($general['enable_dot'])) {
305+
$cert =& lookup_cert($general['dohdot_cert']);
306+
$chain = [];
307+
$ca_chain = ca_chain_array($cert);
308+
if (is_array($ca_chain)) {
309+
foreach ($ca_chain as $entry) {
310+
$chain[] = base64_decode($entry['crt']);
311+
}
312+
}
313+
if (isset($cert)) {
314+
export_pem_file(
315+
'/var/unbound/dohdot.pem',
316+
$cert['crt'],
317+
implode("\n", $chain)
318+
);
319+
export_pem_file(
320+
'/var/unbound/dohdot.key',
321+
$cert['prv']
322+
);
323+
$dohdot_settings .= "# DoH and DoT\n";
324+
if (!empty($general['enable_dot'])) {
325+
$dohdot_settings .= "tls-port: $port_dot\n";
326+
}
327+
if (!empty($general['enable_doh'])) {
328+
$dohdot_settings .= "https-port: $port_doh\n";
329+
}
330+
$dohdot_settings .= "tls-service-key: /var/unbound/dohdot.key\n";
331+
$dohdot_settings .= "tls-service-pem: /var/unbound/dohdot.pem\n";
332+
}
333+
}
334+
273335
$unboundconf = <<<EOD
274336
##########################
275337
# Unbound Configuration
@@ -303,6 +365,7 @@ module-config: "{$module_config}"
303365
{$anchor_file}
304366
{$forward_local}
305367
{$dns64_config}
368+
{$dohdot_settings}
306369
307370
# Interface IP(s) to bind to
308371
{$bindints}

src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/general.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,35 @@
1010
<type>text</type>
1111
<help>The TCP/UDP port used for responding to DNS queries.</help>
1212
</field>
13+
<field>
14+
<id>unbound.general.enable_doh</id>
15+
<label>Enable DoH</label>
16+
<type>checkbox</type>
17+
<help>Only enable this on port 443 when the web-interface is not listening on the same port!</help>
18+
</field>
19+
<field>
20+
<id>unbound.general.port_doh</id>
21+
<label>Listen Port for DoH</label>
22+
<type>text</type>
23+
<help>The TCP/UDP port used for responding to DoH queries.</help>
24+
</field>
25+
<field>
26+
<id>unbound.general.enable_dot</id>
27+
<label>Enable DoT</label>
28+
<type>checkbox</type>
29+
</field>
30+
<field>
31+
<id>unbound.general.port_dot</id>
32+
<label>Listen Port for DoT</label>
33+
<type>text</type>
34+
<help>The TCP/UDP port used for responding to DoT queries.</help>
35+
</field>
36+
<field>
37+
<id>unbound.general.dohdot_cert</id>
38+
<label>Certificate</label>
39+
<type>dropdown</type>
40+
<help>DoH and DoT Certificate to use</help>
41+
</field>
1342
<field>
1443
<id>unbound.general.active_interface</id>
1544
<label>Network Interfaces</label>

src/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<model>
22
<mount>//OPNsense/unboundplus</mount>
33
<description>Unbound configuration</description>
4-
<version>1.0.6</version>
4+
<version>1.0.7</version>
55
<items>
66
<general>
77
<enabled type="BooleanField">
@@ -12,6 +12,26 @@
1212
<default>53</default>
1313
<Required>Y</Required>
1414
</port>
15+
<enable_doh type="BooleanField">
16+
<default>0</default>
17+
<Required>Y</Required>
18+
</enable_doh>
19+
<port_doh type="PortField">
20+
<default>443</default>
21+
<Required>Y</Required>
22+
</port_doh>
23+
<enable_dot type="BooleanField">
24+
<default>0</default>
25+
<Required>Y</Required>
26+
</enable_dot>
27+
<port_dot type="PortField">
28+
<default>853</default>
29+
<Required>Y</Required>
30+
</port_dot>
31+
<dohdot_cert type="CertificateField">
32+
<Required>N</Required>
33+
<ValidationMessage>Please select a valid certificate from the list</ValidationMessage>
34+
</dohdot_cert>
1535
<stats type="BooleanField">
1636
<default>0</default>
1737
<Required>N</Required>

src/opnsense/mvc/app/views/OPNsense/Unbound/stats.volt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,26 @@
6262
'elapsed': "{{ lang._('Elapsed') }}"
6363
};
6464

65+
const descriptionNumQuery = {
66+
'query': {
67+
'type': {
68+
'A': "{{ lang._('A') }}",
69+
'AAAA': "{{ lang._('AAAA') }}",
70+
'CNAME': "{{ lang._('CNAME') }}",
71+
'SOA': "{{ lang._('SOA') }}",
72+
'PTR': "{{ lang._('PTR') }}",
73+
'TXT': "{{ lang._('TXT') }}",
74+
'SRV': "{{ lang._('SRV') }}",
75+
'NAPTR': "{{ lang._('NAPTR') }}",
76+
},
77+
'tls': {
78+
'__value__': "{{ lang._('DoT') }}"
79+
},
80+
'https': "{{ lang._('DoH') }}",
81+
'ipv6': "{{ lang._('IPv6') }}"
82+
}
83+
};
84+
6585
function writeDescs(parent, data, descriptions) {
6686
$.each(descriptions, function(descKey, descValue) {
6787
if (typeof descValue !== 'object') {
@@ -136,6 +156,19 @@
136156
writeDescs(tbody, value, descriptionMapTime);
137157
table.append(tbody);
138158
statsView.append(table);
159+
} else if (key === "num") {
160+
let title = document.createElement("h2");
161+
title.innerHTML = "Query Types";
162+
statsView.append(title);
163+
164+
let table = document.createElement('table');
165+
table.classList.add('table');
166+
table.classList.add('table-striped');
167+
table.style.width = 'auto';
168+
let tbody = document.createElement('tbody');
169+
writeDescs(tbody, value, descriptionNumQuery);
170+
table.append(tbody);
171+
statsView.append(table);
139172
}
140173
});
141174
}
@@ -155,7 +188,7 @@
155188
// initial fetch
156189
updateStats();
157190

158-
updateServiceControlUI('unbound');
191+
updateServiceControlUI('unbound');
159192
});
160193
</script>
161194

0 commit comments

Comments
 (0)