This commit is contained in:
mbedworth 2026-05-25 09:42:12 +08:00 committed by GitHub
commit 1c8aefa1fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 128 additions and 3 deletions

View file

@ -195,4 +195,26 @@
<advanced>true</advanced>
<help>The base64-encoded RNDC key. This requires a restart of the Bind Service.</help>
</field>
<field>
<type>header</type>
<label>DNS over TLS (Incoming)</label>
</field>
<field>
<id>general.dotenable</id>
<label>Enable DNS over TLS</label>
<type>checkbox</type>
<help>Accept incoming DNS-over-TLS (port 853) connections. Requires BIND 9.18+ and a valid certificate. The same listen addresses used for plain DNS are also used for DoT.</help>
</field>
<field>
<id>general.dotport</id>
<label>DNS over TLS Port</label>
<type>text</type>
<help>Port to listen on for DNS-over-TLS connections. Default is 853.</help>
</field>
<field>
<id>general.dotcertificate</id>
<label>Certificate</label>
<type>dropdown</type>
<help>TLS certificate to present to DNS-over-TLS clients. Select a certificate from System &gt; Trust &gt; Certificates.</help>
</field>
</form>

View file

@ -1,7 +1,7 @@
<model>
<mount>//OPNsense/bind/general</mount>
<description>BIND configuration</description>
<version>1.0.12</version>
<version>1.0.13</version>
<items>
<enabled type="BooleanField">
<Default>0</Default>
@ -167,5 +167,17 @@
<Required>Y</Required>
<Default>VxtIzJevSQXqnr7h2qerrcwjnZlMWSGGFBndKeNIDfw=</Default>
</rndcsecret>
<dotenable type="BooleanField">
<Default>0</Default>
<Required>Y</Required>
</dotenable>
<dotport type="PortField">
<Default>853</Default>
<Required>Y</Required>
</dotport>
<dotcertificate type="CertificateField">
<Required>N</Required>
<ValidationMessage>Please select a valid certificate for DNS over TLS.</ValidationMessage>
</dotcertificate>
</items>
</model>

View file

@ -0,0 +1,69 @@
#!/usr/local/bin/php
<?php
/*
* Copyright (C) 2026 opnsense.org community
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("script/load_phalcon.php");
use OPNsense\Core\Config;
use OPNsense\Core\File;
use OPNsense\Trust\Store;
$targetdir = "/var/etc/named";
$certfile = "{$targetdir}/dot.crt";
$keyfile = "{$targetdir}/dot.key";
if (!is_dir($targetdir)) {
mkdir($targetdir, 0750, true);
chown($targetdir, 'bind');
chgrp($targetdir, 'bind');
}
$cfg = Config::getInstance()->object();
$general = $cfg->OPNsense->bind->general ?? new \stdClass();
$dotenable = (string)($general->dotenable ?? '0');
$certref = (string)($general->dotcertificate ?? '');
if ($dotenable === '1' && $certref !== '') {
$cert = Store::getCertificate($certref);
if ($cert && isset($cert['prv'])) {
File::file_update_contents($certfile, $cert['crt'], 0640);
File::file_update_contents($keyfile, $cert['prv'], 0640);
chown($certfile, 'bind');
chgrp($certfile, 'bind');
chown($keyfile, 'bind');
chgrp($keyfile, 'bind');
exit(0);
}
}
foreach ([$certfile, $keyfile] as $f) {
if (file_exists($f)) {
unlink($f);
}
}

View file

@ -1,5 +1,12 @@
[certsetup]
command:/usr/local/bin/php /usr/local/opnsense/scripts/OPNsense/Bind/generate_certs.php
parameters:
type:script
message:setting up BIND TLS certificates
description: Write BIND DoT certificate files from OPNsense trust store
[start]
command:/usr/local/etc/rc.d/named start
command:/usr/local/bin/php /usr/local/opnsense/scripts/OPNsense/Bind/generate_certs.php && /usr/local/etc/rc.d/named start
parameters:
type:script
message:starting BIND
@ -11,7 +18,7 @@ type:script
message:stopping BIND
[restart]
command:/usr/local/etc/rc.d/named restart
command:/usr/local/bin/php /usr/local/opnsense/scripts/OPNsense/Bind/generate_certs.php && /usr/local/etc/rc.d/named restart
parameters:
type:script
message:restarting BIND

View file

@ -8,6 +8,13 @@ acl "{{ acl_list.name }}" { {{ acl_list.networks.replace(',', '; ') }}; };
{% endfor %}
{% endif %}
{% if helpers.exists('OPNsense.bind.general.dotenable') and OPNsense.bind.general.dotenable == '1' %}
tls dot-tls {
cert-file "/var/etc/named/dot.crt";
key-file "/var/etc/named/dot.key";
};
{% endif %}
options {
directory "/usr/local/etc/namedb/working";
@ -21,6 +28,14 @@ options {
{% for listenv6 in OPNsense.bind.general.listenv6.split(',') %}
listen-on-v6 port {{ OPNsense.bind.general.port }} { {% if listenv6 == '::' %}any{% else %}{{ listenv6 }}{% endif %}; };
{% endfor %}
{% if helpers.exists('OPNsense.bind.general.dotenable') and OPNsense.bind.general.dotenable == '1' %}
{% for listenv4 in OPNsense.bind.general.listenv4.split(',') %}
listen-on port {{ OPNsense.bind.general.dotport }} tls dot-tls { {% if listenv4 == '0.0.0.0' %}any{% else %}{{ listenv4 }}{% endif %}; };
{% endfor %}
{% for listenv6 in OPNsense.bind.general.listenv6.split(',') %}
listen-on-v6 port {{ OPNsense.bind.general.dotport }} tls dot-tls { {% if listenv6 == '::' %}any{% else %}{{ listenv6 }}{% endif %}; };
{% endfor %}
{% endif %}
{% if helpers.exists('OPNsense.bind.general.querysource') and OPNsense.bind.general.querysource != '' %}
query-source {{ OPNsense.bind.general.querysource }};