diff --git a/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/dialogEditBindPrimaryDomain.xml b/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/dialogEditBindPrimaryDomain.xml
index 8439403d18..f992d5c9fc 100644
--- a/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/dialogEditBindPrimaryDomain.xml
+++ b/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/dialogEditBindPrimaryDomain.xml
@@ -35,6 +35,12 @@
checkbox
Allow updates via the RDNC key named "rndc-key". The key is shown in the general tab.
+
+ domain.allowddnsupdate
+
+ checkbox
+ Generates an update-policy grant for the configured DDNS TSIG key (e.g. Kea). If all update options are disabled, no update-policy will be generated (use named.conf.d drop-ins).
+
domain.ttl
diff --git a/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/general.xml b/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/general.xml
index 23e9c92026..559c27b560 100644
--- a/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/general.xml
+++ b/dns/bind/src/opnsense/mvc/app/controllers/OPNsense/Bind/forms/general.xml
@@ -195,4 +195,32 @@
true
The base64-encoded RNDC key. This requires a restart of the Bind Service.
+
+ header
+
+ true
+
+
+ general.ddnskeyname
+
+ text
+ true
+ TSIG key name used for dynamic DNS updates (e.g. from Kea).
+
+
+
+ general.ddnskeyalgo
+
+ dropdown
+ true
+ TSIG algorithm for the DDNS key.
+
+
+
+ general.ddnskeysecret
+
+ text
+ true
+ Base64-encoded TSIG secret for the DDNS key. This requires a restart of the Bind Service.
+
diff --git a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/Domain.xml b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/Domain.xml
index 6743b66ae4..63533faddd 100644
--- a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/Domain.xml
+++ b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/Domain.xml
@@ -67,6 +67,10 @@
1
Y
+
+ 0
+
+
86400
diff --git a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml
index 238c9dc248..a3d8777ab3 100644
--- a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml
+++ b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml
@@ -167,5 +167,21 @@
Y
VxtIzJevSQXqnr7h2qerrcwjnZlMWSGGFBndKeNIDfw=
+
+
+ hmac-sha256
+
+ HMAC-SHA512
+ HMAC-SHA384
+ HMAC-SHA256
+ HMAC-SHA224
+ HMAC-SHA1
+ HMAC-MD5
+
+
+
+ false
+
+
diff --git a/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf b/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf
index 9196b5de3e..0b9c2d9a1f 100644
--- a/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf
+++ b/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf
@@ -111,6 +111,13 @@ controls {
allow { 127.0.0.1; } keys { "rndc-key"; };
};
{% endif %}
+{% if helpers.exists('OPNsense.bind.general.ddnskeyname') and OPNsense.bind.general.ddnskeyname != ''
+ and helpers.exists('OPNsense.bind.general.ddnskeysecret') and OPNsense.bind.general.ddnskeysecret != '' %}
+key "{{ OPNsense.bind.general.ddnskeyname }}" {
+ algorithm "{{ OPNsense.bind.general.ddnskeyalgo }}";
+ secret "{{ OPNsense.bind.general.ddnskeysecret }}";
+};
+{% endif %}
include "/usr/local/etc/namedb/named.conf.d/*.conf";
@@ -189,11 +196,29 @@ zone "{{ domain.domainname }}" {
{% endfor %}
};
{% endif %}
-{% if domain.allowrndcupdate is defined and domain.allowrndcupdate == "1" and domain.type == 'primary' %}
+{% set ddns_key_ok = (helpers.exists('OPNsense.bind.general.ddnskeyname')
+ and OPNsense.bind.general.ddnskeyname != ''
+ and helpers.exists('OPNsense.bind.general.ddnskeysecret')
+ and OPNsense.bind.general.ddnskeysecret != '') %}
+
+{% set ddns_ok = (domain.type == 'primary'
+ and domain.allowddnsupdate is defined and domain.allowddnsupdate == "1"
+ and ddns_key_ok) %}
+
+{% set rndc_ok = (domain.type == 'primary'
+ and domain.allowrndcupdate is defined and domain.allowrndcupdate == "1") %}
+
+{% if ddns_ok or rndc_ok %}
update-policy {
- grant rndc-key zonesub ANY;
+{% if ddns_ok %}
+ grant {{ OPNsense.bind.general.ddnskeyname }} zonesub ANY;
+{% endif %}
+{% if rndc_ok %}
+ grant rndc-key zonesub ANY;
+{% endif %}
};
{% endif %}
+
};
{% if domain.type == 'secondary' and domain.transferkey is defined and not(domain.transferkeyname in usedkeys) %}
{% do usedkeys.append(domain.transferkeyname) %}