diff --git a/plist b/plist
index 67804bd83..8fd44f341 100644
--- a/plist
+++ b/plist
@@ -150,6 +150,7 @@
/usr/local/etc/ssl/opnsense.cnf
/usr/local/etc/strongswan.opnsense.d/README
/usr/local/etc/unbound.opnsense.d/README
+/usr/local/etc/unbound.opnsense.d/miscellaneous.conf
/usr/local/libexec/opnsense-auth
/usr/local/opnsense/contrib/IXR/IXR_Library.php
/usr/local/opnsense/contrib/base32/Base32.php
@@ -1064,7 +1065,6 @@
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/blocklists.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/domainoverrides.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/dot.conf
-/usr/local/opnsense/service/templates/OPNsense/Unbound/core/miscellaneous.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/private_domains.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/root.min.hints
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/unbound_dhcpd.conf
diff --git a/src/etc/unbound.opnsense.d/miscellaneous.conf b/src/etc/unbound.opnsense.d/miscellaneous.conf
new file mode 100644
index 000000000..ab346e45e
--- /dev/null
+++ b/src/etc/unbound.opnsense.d/miscellaneous.conf
@@ -0,0 +1 @@
+# obsolete include file, last used in 22.7
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/advanced.xml b/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/advanced.xml
index 9bb6f14c0..e040125f2 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/advanced.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/advanced.xml
@@ -43,8 +43,52 @@
unbound.advanced.serveexpiredcheckbox
+
Serve expired responses from the cache with a TTL of 0 without waiting for the actual resolution to finish.
+ The TTL can be modified with "Expired Record Reply TTL value"
+
+
+
+ unbound.advanced.serveexpiredreplyttl
+
+ text
+
+
+ TTL value to use when replying with expired data. If "Client Expired Response Timeout" is also used
+ then it is recommended to use 30 as the value as per RFC 8767.
+
+
+
+ unbound.advanced.serveexpiredttl
+
+ text
+
+
+ Limits the serving of expired responses to the configured amount of seconds after expiration.
+ A value of 0 disables the limit. A suggested value per RFC 8767 is between 86400 (1 day) and 259200 (3 days).
+
+
+
+ unbound.advanced.serveexpiredttlreset
+
+ checkbox
+
+
+ Set the TTL of expired records to the "TTL for Expired Responses" value after a failed attempt to
+ retrieve the record from an upstream server. This makes sure that the expired records will be served as long
+ as there are queries for it.
+
+
+
+ unbound.advanced.serveexpiredclienttimeout
+
+ text
+
+
+ Time in milliseconds before replying to the client with expired data. This essentially enables the serve-
+ stable behavior as specified in RFC 8767 that first tries to resolve before immediately responding with expired
+ data. A recommended value per RF 8767 is 1800. Setting this to 0 will disable this behavior.
@@ -71,7 +115,7 @@
If enabled, prints one line per query to the log, with the log timestamp and IP address, name, type and class.
Note that it takes time to print these lines, which makes the server (significantly) slower. Odd
- (nonprintable) characters in names are printed as '?'.
+ (non-printable) characters in names are printed as '?'.
@@ -82,7 +126,7 @@
If enabled, prints one line per reply to the log, with the log timestamp and IP address, name, type,
class, return code, time to resolve, whether the reply is from the cache and the response size.
Note that it takes time to print these lines, which makes the server (significantly) slower. Odd
- (nonprintable) characters in names are printed as '?'.
+ (non-printable) characters in names are printed as '?'.
@@ -104,19 +148,49 @@
Level 4 gives algorithm level information. Level 5 logs client identification for cache misses.
+
+ unbound.advanced.privatedomain
+
+ select_multiple
+
+ true
+
+ List of domains to mark as private. These domains and all its subdomains are allowed to contain
+ private addresses.
+
+
+
+ unbound.advanced.insecuredomain
+
+ select_multiple
+
+ true
+ List of domains to mark as insecure. DNSSEC chain of trust is ignored towards the domain name.
+ unbound.advanced.msgcachesize
- dropdown
+ text
Size of the message cache. The message cache stores DNS rcodes and validation statuses.
The RRSet cache will automatically be set to twice this amount. The RRSet cache contains the actual RR data.
+ Valid input is plain bytes, optionally appended with 'k', 'm', or 'g' for kilobytes, megabytes
+ or gigabytes respectively.
+
+
+
+ unbound.advanced.rrsetcachesize
+
+ text
+
+ Size of the RRset cache. Contains the actual RR data. Valid input is plain bytes, optionally appended
+ with 'k', 'm', or 'g' for kilobytes, megabytes or gigabytes respectively.
unbound.advanced.outgoingnumtcp
- dropdown
+ text
The number of outgoing TCP buffers to allocate per thread.
If 0 is selected then no TCP queries, to authoritative servers, are done.
@@ -125,7 +199,7 @@
unbound.advanced.incomingnumtcp
- dropdown
+ text
The number of incoming TCP buffers to allocate per thread.
If 0 is selected then no TCP queries, from clients, are accepted.
@@ -134,18 +208,33 @@
unbound.advanced.numqueriesperthread
- dropdown
+ text
The number of queries that every thread will service simultaneously. If more queries arrive that
- need to be serviced, and no queries can be jostled, then these queries are dropped.
+ need to be serviced, and no queries can be jostled out (see "Jostle Timeout"),
+ then these queries are dropped. This forces the client to resend after a timeout, allowing the
+ server time to work on the existing queries.
+
+
+
+ unbound.advanced.outgoingrange
+
+ text
+
+ The number of ports to open. This number of file descriptors can be opened per thread. Larger numbers
+ need extra resources from the operating system. For performance a very large value is best.
+ For reference, usually double the amount of queries per thread is used.
unbound.advanced.jostletimeout
- dropdown
+ text
- This timeout is used for when the server is very busy. This protects against denial of service by
+ This timeout is used for when the server is very busy. Set to a value that usually results in one
+ round-trip to the authority servers. If too many queries arrive, then 50% of the queries are allowed
+ to run to completion, and the other 50% are replaced with the new incoming query if they have
+ already spent more than their allowed time. This protects against denial of service by
slow queries or high query rates.
@@ -154,8 +243,8 @@
text
- Configure a maximum Time to live for RRsets and messages in the cache.
- The default is 86400 seconds (1 day). When the internal TTL expires the cache item is expired.
+ Configure a maximum Time to live in seconds for RRsets and messages in the cache.
+ When the internal TTL expires the cache item is expired.
This can be configured to force the resolver to query for data more often and
not trust (very large) TTL values.
@@ -165,9 +254,9 @@
text
- Configure a minimum Time to live for RRsets and messages in the cache.
- The default is 0 seconds. If the minimum value kicks in, the data is cached for longer than
- the domain owner intended, and thus less queries are made to look up the data.
+ Configure a minimum Time to live in seconds for RRsets and messages in the cache.
+ If the minimum value kicks in, the data is cached for longer than
+ the domain owner intended, and thus fewer queries are made to look up the data.
The 0 value ensures the data in the cache is as the domain owner intended.
High values can lead to trouble as the data in the cache might not match up with the actual data anymore.
@@ -175,16 +264,16 @@
unbound.advanced.infrahostttl
- dropdown
+ text
- Time to live for entries in the host cache. The host cache contains roundtrip timing and EDNS
- support information.
+ Time to live in seconds for entries in the host cache. The host cache contains round-trip timing, lameness
+ and EDNS support information.
unbound.advanced.infracachenumhosts
- dropdown
+ text
Number of hosts for which information is cached.
@@ -192,12 +281,11 @@
unbound.advanced.unwantedreplythreshold
- dropdown
+ text
If enabled, a total number of unwanted replies is kept track of in every thread.
When it reaches the threshold, a defensive action is taken and a warning is printed to the log file.
This defensive action is to clear the RRSet and message caches, hopefully flushing away any poison.
- The default is disabled, but if enabled a value of 10 million is suggested.
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dnsbl.xml b/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dnsbl.xml
index c55b3d6bd..11bdc6a17 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dnsbl.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dnsbl.xml
@@ -35,20 +35,4 @@
trueDestination ip address for entries in the blocklist (leave empty to use default: 0.0.0.0)
-
- unbound.miscellaneous.privatedomain
-
- select_multiple
-
- true
- List of domains to mark as private. You only need this for some DNSBL lists which resolve to private addresses.
-
-
- unbound.miscellaneous.insecuredomain
-
- select_multiple
-
- true
- List of domains to mark as insecure. DNSSEC chain of trust is ignored towards the domain name.
-
diff --git a/src/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_3.php b/src/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_3.php
index c71987a19..15f2764ca 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_3.php
+++ b/src/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_3.php
@@ -33,6 +33,29 @@ use OPNsense\Core\Config;
class M1_0_3 extends BaseModelMigration
{
+ private $legacy_format = array(
+ 'outgoing_num_tcp',
+ 'incoming_num_tcp',
+ 'num_queries_per_thread',
+ 'jostle_timeout',
+ 'cache_max_ttl',
+ 'cache_min_ttl',
+ 'infra_host_ttl',
+ 'infra_cache_numhosts',
+ 'unwanted_reply_threshold',
+ 'log_verbosity',
+ 'extended_statistics',
+ 'log_queries',
+ 'hideidentity',
+ 'hideversion',
+ 'prefetch',
+ 'prefetchkey',
+ 'dnssecstripped',
+ 'serveexpired',
+ 'qnameminstrict',
+ 'msgcachesize'
+ );
+
/**
* Migrate older models into shared model
* @param $model
@@ -41,34 +64,44 @@ class M1_0_3 extends BaseModelMigration
{
$config = Config::getInstance()->object();
- $legacy_format = array(
- 'outgoing_num_tcp',
- 'incoming_num_tcp',
- 'num_queries_per_thread',
- 'jostle_timeout',
- 'cache_max_ttl',
- 'cache_min_ttl',
- 'infra_host_ttl',
- 'infra_cache_numhosts',
- 'unwanted_reply_threshold',
- 'log_verbosity',
- 'extended_statistics',
- 'log_queries'
- );
-
$legacy_config = array();
foreach ($config->unbound->children() as $key => $value) {
- if (in_array($key, $legacy_format) && !empty((string)$value)) {
- /* handle differing keys */
+ if (in_array($key, $this->legacy_format) && !empty((string)$value)) {
+ if ($key == 'msgcachesize') {
+ $legacy_config[$key] = (string)$value . 'm';
+ /* Mimic legacy behaviour for the msg cache size value (if applied) */
+ $legacy_config['rrsetcachesize'] = ($value * 2) . 'm';
+ continue;
+ }
+
+ if ($key == 'num_queries_per_thread') {
+ $legacy_config['outgoingrange'] = $value * 2;
+ }
+
+ /* handle differing keys, underscore got removed in model transition */
$legacy_config[str_replace('_', '', $key)] = (string)$value;
}
+ }
- if (array_key_exists($key, $model->advanced->getNodes()) && !empty((string)$value)) {
- /* handle matching keys */
- $legacy_config[$key] = (string)$value;
+ foreach (['privatedomain', 'insecuredomain'] as $misc_node) {
+ $node_value = (string)$config->OPNsense->unboundplus->miscellaneous->$misc_node;
+ if (!empty($node_value)) {
+ $legacy_config[$misc_node] = $node_value;
}
}
$model->advanced->setNodes($legacy_config);
}
+
+ /**
+ * cleanup old config after config save
+ * @param $model
+ */
+ public function post($model)
+ {
+ $config = Config::getInstance()->object();
+ foreach ($this->legacy_format as $node) {
+ unset($config->unbound->$node);
+ }
+ }
}
diff --git a/src/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml b/src/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml
index dfd538fb2..d9bb735e4 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml
+++ b/src/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml
@@ -25,6 +25,15 @@
0
+
+
+
+
+
+ 0
+
+
+ 0
@@ -52,103 +61,39 @@
Level 5
-
- 4
- Y
-
- 4 MB (Default)
- 10 MB
- 20 MB
- 50 MB
- 100 MB
- 250 MB
- 512 MB
-
+
+ N
+
+
+ N
+
+
+ /[0-9]+[kmg]?/i
+ The cache size should be numeric, optionally appended with 'k', 'm', or 'g'
-
- 10
- Y
-
- 0
- 10 (Default)
- 20
- 30
- 40
- 50
-
+
+ /[0-9]+[kmg]?/i
+ The cache size should be numeric, optionally appended with 'k', 'm', or 'g'
+
+
-
- 10
- Y
-
- 0
- 10 (Default)
- 20
- 30
- 40
- 50
-
+
-
- 4096
- Y
-
- 512
- 1024
- 2048
- 4096 (Default)
- 8192
-
+
-
- 200
- Y
-
- 100
- 200 (Default)
- 500
- 1000
-
+
+
+
- 86400
- 0
-
- 900
- Y
-
- 1 minute
- 2 minutes
- 5 minutes
- 10 minutes
- 15 minutes (Default)
-
+
-
- 10000
- Y
-
- 1000
- 5000
- 10000 (Default)
- 20000
- 50000
-
+
-
- 0
- Y
-
- Disabled (Default)
- 5 million
- 10 million
- 20 million
- 40 million
- 50 million
-
+
@@ -391,13 +336,5 @@
-
-
- N
-
-
- N
-
-
diff --git a/src/opnsense/mvc/app/views/OPNsense/Unbound/advanced.volt b/src/opnsense/mvc/app/views/OPNsense/Unbound/advanced.volt
index dbac3554d..2df4bc589 100644
--- a/src/opnsense/mvc/app/views/OPNsense/Unbound/advanced.volt
+++ b/src/opnsense/mvc/app/views/OPNsense/Unbound/advanced.volt
@@ -26,10 +26,18 @@
diff --git a/src/opnsense/service/templates/OPNsense/Unbound/core/+TARGETS b/src/opnsense/service/templates/OPNsense/Unbound/core/+TARGETS
index 8ed7bc632..8eb414f4b 100644
--- a/src/opnsense/service/templates/OPNsense/Unbound/core/+TARGETS
+++ b/src/opnsense/service/templates/OPNsense/Unbound/core/+TARGETS
@@ -3,6 +3,5 @@ blocklists.conf:/tmp/unbound-blocklists.conf
dot.conf:/usr/local/etc/unbound.opnsense.d/dot.conf
private_domains.conf:/var/unbound/private_domains.conf
domainoverrides.conf:/usr/local/etc/unbound.opnsense.d/domainoverrides.conf
-miscellaneous.conf:/usr/local/etc/unbound.opnsense.d/miscellaneous.conf
root.min.hints:/var/unbound/root.hints
unbound_dhcpd.conf:/usr/local/etc/unbound_dhcpd.conf
diff --git a/src/opnsense/service/templates/OPNsense/Unbound/core/advanced.conf b/src/opnsense/service/templates/OPNsense/Unbound/core/advanced.conf
index e7d7d908d..c0fe6ac8a 100644
--- a/src/opnsense/service/templates/OPNsense/Unbound/core/advanced.conf
+++ b/src/opnsense/service/templates/OPNsense/Unbound/core/advanced.conf
@@ -1,6 +1,11 @@
{% macro set_boolean(name) -%}
{% if name == '1' %}yes{%else%}no{%endif%}
{%- endmacro %}
+{% macro set_numeric_value(key, value) -%}
+ {% if value is defined and not empty %}
+{{ key }}: {{ value }}
+ {% endif %}
+{%- endmacro %}
{% if helpers.exists('OPNsense.unboundplus.advanced') %}
hide-identity: {{ set_boolean(OPNsense.unboundplus.advanced.hideidentity) }}
hide-version: {{ set_boolean(OPNsense.unboundplus.advanced.hideversion) }}
@@ -13,17 +18,27 @@ extended-statistics: {{ set_boolean(OPNsense.unboundplus.advanced.extendedstatis
log-queries: {{ set_boolean(OPNsense.unboundplus.advanced.logqueries) }}
log-replies: {{ set_boolean(OPNsense.unboundplus.advanced.logreplies) }}
log-tag-queryreply: {{ set_boolean(OPNsense.unboundplus.advanced.logtagqueryreply) }}
-verbosity: {{ OPNsense.unboundplus.advanced.logverbosity }}
-msg-cache-size: {{ OPNsense.unboundplus.advanced.msgcachesize }}m
-rrset-cache-size: {{ OPNsense.unboundplus.advanced.msgcachesize|int() * 2 }}m
-outgoing-num-tcp: {{ OPNsense.unboundplus.advanced.outgoingnumtcp }}
-incoming-num-tcp: {{ OPNsense.unboundplus.advanced.incomingnumtcp }}
-num-queries-per-thread: {{ OPNsense.unboundplus.advanced.numqueriesperthread }}
-outgoing-range: {{ OPNsense.unboundplus.advanced.numqueriesperthread|int() * 2 }}
-jostle-timeout: {{ OPNsense.unboundplus.advanced.jostletimeout }}
-cache-max-ttl: {{ OPNsense.unboundplus.advanced.cachemaxttl }}
-cache-min-ttl: {{ OPNsense.unboundplus.advanced.cacheminttl }}
-infra-host-ttl: {{ OPNsense.unboundplus.advanced.infrahostttl }}
-infra-cache-numhosts: {{ OPNsense.unboundplus.advanced.infracachenumhosts }}
-unwanted-reply-threshold: {{ OPNsense.unboundplus.advanced.unwantedreplythreshold }}
+{{ set_numeric_value('verbosity', OPNsense.unboundplus.advanced.logverbosity) }}
+{{ set_numeric_value('msg-cache-size', OPNsense.unboundplus.advanced.msgcachesize) }}
+{{ set_numeric_value('rrset-cache-size', OPNsense.unboundplus.advanced.rrsetcachesize) }}
+{{ set_numeric_value('outgoing-num-tcp', OPNsense.unboundplus.advanced.outgoingnumtcp) }}
+{{ set_numeric_value('incoming-num-tcp', OPNsense.unboundplus.advanced.incomingnumtcp) }}
+{{ set_numeric_value('num-queries-per-thread', OPNsense.unboundplus.advanced.numqueriesperthread) }}
+{{ set_numeric_value('outgoing-range', OPNsense.unboundplus.advanced.outgoingrange) }}
+{{ set_numeric_value('jostle-timeout', OPNsense.unboundplus.advanced.jostletimeout) }}
+{{ set_numeric_value('cache-max-ttl', OPNsense.unboundplus.advanced.cachemaxttl) }}
+{{ set_numeric_value('cache-min-ttl', OPNsense.unboundplus.advanced.cacheminttl) }}
+{{ set_numeric_value('infra-host-ttl', OPNsense.unboundplus.advanced.infrahostttl) }}
+{{ set_numeric_value('infra-cache-numhosts', OPNsense.unboundplus.advanced.infracachenumhosts) }}
+{{ set_numeric_value('unwanted-reply-threshold', OPNsense.unboundplus.advanced.unwantedreplythreshold) }}
+{% if not helpers.empty('OPNsense.unboundplus.advanced.privatedomain') %}
+{% for privatedomain in OPNsense.unboundplus.advanced.privatedomain.split(',') %}
+private-domain: {{ privatedomain }}
+{% endfor %}
+{% endif %}
+{% if not helpers.empty('OPNsense.unboundplus.advanced.insecuredomain') %}
+{% for insecuredomain in OPNsense.unboundplus.advanced.insecuredomain.split(',') %}
+domain-insecure: {{ insecuredomain }}
+{% endfor %}
+{% endif %}
{% endif %}
diff --git a/src/opnsense/service/templates/OPNsense/Unbound/core/miscellaneous.conf b/src/opnsense/service/templates/OPNsense/Unbound/core/miscellaneous.conf
deleted file mode 100644
index 2417cff58..000000000
--- a/src/opnsense/service/templates/OPNsense/Unbound/core/miscellaneous.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-server:
-{% if not helpers.empty('OPNsense.unboundplus.miscellaneous.privatedomain') %}
-{% for privatedomain in OPNsense.unboundplus.miscellaneous.privatedomain.split(',') %}
-private-domain: {{ privatedomain }}
-{% endfor %}
-{% endif %}
-{% if not helpers.empty('OPNsense.unboundplus.miscellaneous.insecuredomain') %}
-{% for insecuredomain in OPNsense.unboundplus.miscellaneous.insecuredomain.split(',') %}
-domain-insecure: {{ insecuredomain }}
-{% endfor %}
-{% endif %}