dns/bind: Rate-limiting feature expansion

Expands the rate-limiting feature set:

1. Adds all other BIND v9.20.11 rate-limting options
2. Updates General MVC model to v1.0.13

Note: Not all option arguments are implemented.

Signed-off-by: benyamin-codez <115509179+benyamin-codez@users.noreply.github.com>
This commit is contained in:
benyamin-codez 2025-08-17 13:17:45 +10:00
parent e3ab911da7
commit 5a375b91c9
3 changed files with 207 additions and 8 deletions

View file

@ -153,28 +153,124 @@
<advanced>true</advanced>
<help>This will disable prefetching of domains before they time out.</help>
</field>
<field>
<type>header</type>
<label>Rate Limiting</label>
<advanced>true</advanced>
</field>
<field>
<id>general.enableratelimiting</id>
<label>Enable Rate Limiting</label>
<type>checkbox</type>
<advanced>true</advanced>
<help>This will enable rate-limiting for DNS replies.</help>
<help>This will enable rate-limiting for DNS responses.</help>
</field>
<field>
<id>general.ratelimitcount</id>
<label>Rate Limit Replies</label>
<label>Responses Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many replies per second are allowed.</help>
<help>Set how many non-empty responses are allowed per second for valid domain names and record types. The default is 0 or no limit.</help>
</field>
<field>
<id>general.ratelimitwindow</id>
<label>Window</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the number of second during which responses are tracked. The default is 15 seconds.</help>
</field>
<field>
<id>general.ratelimitexcept</id>
<label>Rate Limit Exceptions</label>
<label>Exempt Clients</label>
<style>tokenize</style>
<type>select_multiple</type>
<allownew>true</allownew>
<advanced>true</advanced>
<help>Except a list of IPs from rate-limiting like ::1</help>
<help>Exempt a list of IPs from rate-limiting, e.g. ::1</help>
</field>
<field>
<id>general.ratelimitipv4prefixlength</id>
<label>IPv4 Prefix Length</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the number of bits of the address block. Used to distinquish clients into a rate-limited group. The default is 24.</help>
</field>
<field>
<id>general.ratelimitipv6prefixlength</id>
<label>IPv6 Prefix Length</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the number of bits of the address block. Used to distinquish clients into a rate-limited group. The default is 56.</help>
</field>
<field>
<id>general.ratelimitnodataps</id>
<label>NODATA Responses Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many empty (NODATA) responses are allowed per second for valid domain names. The default is equal to the Responses Per Second value.</help>
</field>
<field>
<id>general.ratelimitnxdomsps</id>
<label>NXDOMAIN Responses Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many NXDOMAIN errors are allowed per second for undefined subdomains for valid domain names. The default is equal to the Responses Per Second value.</help>
</field>
<field>
<id>general.ratelimitrefsps</id>
<label>Referrals Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many referrals or delegations are allowed per second to a server for a given domain. The default is equal to the Responses Per Second value.</help>
</field>
<field>
<id>general.ratelimiterrsps</id>
<label>Errors Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many errors are allowed per second for valid domain names and record types. The default is equal to the Responses Per Second value.</help>
</field>
<field>
<id>general.ratelimitallps</id>
<label>All Per Second</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how many UDP responses of all types are allowed per second. If used, this should be set to 4 times the size of other per second limits.</help>
</field>
<field>
<id>general.ratelimitslip</id>
<label>Slip</label>
<type>text</type>
<advanced>true</advanced>
<help>Set how often to "slip" responses, reducing the use of forged source addresses in attacks. The default is 2, i.e. every second response. Use a value of 0 to disable slipped responses. Use a value of 1 with caution.</help>
</field>
<field>
<id>general.ratelimitscale</id>
<label>QPS Scale</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the ratio by which to scale back the Responses Per Second value during attacks. The formula used to produce the new value is ("QPS Scale"/Total Query Rate)*"Responses Per Second".</help>
</field>
<field>
<id>general.ratelimitmaxtbl</id>
<label>Maximum Table Size</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the maximum number of table entries used to track requests and rate-limit responses. The default is 20,000.</help>
</field>
<field>
<id>general.ratelimitmintbl</id>
<label>Minimum Table Size</label>
<type>text</type>
<advanced>true</advanced>
<help>Set the minimum number of table entries used to track requests and rate-limit responses. The default is 500.</help>
</field>
<field>
<id>general.ratelimittry</id>
<label>Trial Rate Limiting</label>
<type>checkbox</type>
<advanced>true</advanced>
<help>Enable to test rate-limiting parameters without actually dropping any requests.</help>
</field>
<field>
<type>header</type>

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>
@ -146,11 +146,75 @@
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitcount>
<ratelimitwindow type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>3600</MaximumValue>
<ValidationMessage>Choose a value between 1 and 3600.</ValidationMessage>
</ratelimitwindow>
<ratelimitexcept type="NetworkField">
<Default>0.0.0.0,::</Default>
<Required>Y</Required>
<AsList>Y</AsList>
</ratelimitexcept>
<ratelimitipv4prefixlength type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>32</MaximumValue>
<ValidationMessage>Choose a value between 1 and 32.</ValidationMessage>
</ratelimitipv4prefixlength>
<ratelimitipv6prefixlength type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>128</MaximumValue>
<ValidationMessage>Choose a value between 1 and 128.</ValidationMessage>
</ratelimitipv6prefixlength>
<ratelimitnodataps type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitnodataps>
<ratelimitnxdomsps type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitnxdomsps>
<ratelimitrefsps type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitrefsps>
<ratelimiterrsps type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimiterrsps>
<ratelimitallps type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitallps>
<ratelimitslip type="IntegerField">
<MinimumValue>0</MinimumValue>
<MaximumValue>10</MaximumValue>
<ValidationMessage>Choose a value between 0 and 10.</ValidationMessage>
</ratelimitslip>
<ratelimitscale type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>1000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 1000.</ValidationMessage>
</ratelimitscale>
<ratelimitmaxtbl type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>100000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 100,000.</ValidationMessage>
</ratelimitmaxtbl>
<ratelimitmintbl type="IntegerField">
<MinimumValue>1</MinimumValue>
<MaximumValue>100000</MaximumValue>
<ValidationMessage>Choose a value between 1 and 100,000.</ValidationMessage>
</ratelimitmintbl>
<ratelimittry type="BooleanField">
<Default>0</Default>
<Required>N</Required>
</ratelimittry>
<rndcalgo type="OptionField">
<Required>Y</Required>
<Default>hmac-sha256</Default>

View file

@ -92,9 +92,48 @@ options {
{% if helpers.exists('OPNsense.bind.general.enableratelimiting') and OPNsense.bind.general.enableratelimiting == '1' %}
{% if helpers.exists('OPNsense.bind.general.ratelimitcount') and OPNsense.bind.general.ratelimitcount != '' %}
rate-limit {
responses-per-second {{ OPNsense.bind.general.ratelimitcount }};
responses-per-second {{ OPNsense.bind.general.ratelimitcount }};
{% if helpers.exists('OPNsense.bind.general.ratelimitwindow') and OPNsense.bind.general.ratelimitwindow != '' %}
window {{ OPNsense.bind.general.ratelimitwindow }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitipv4prefixlength') and OPNsense.bind.general.ratelimitipv4prefixlength != '' %}
ipv4-prefix-length {{ OPNsense.bind.general.ratelimitipv4prefixlength }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitipv6prefixlength') and OPNsense.bind.general.ratelimitipv6prefixlength != '' %}
ipv6-prefix-length {{ OPNsense.bind.general.ratelimitipv6prefixlength }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitnodataps') and OPNsense.bind.general.ratelimitnodataps != '' %}
nodata-per-second {{ OPNsense.bind.general.ratelimitnodataps }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitnxdomsps') and OPNsense.bind.general.ratelimitnxdomsps != '' %}
nxdomains-per-second {{ OPNsense.bind.general.ratelimitnxdomsps }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitrefsps') and OPNsense.bind.general.ratelimitrefsps != '' %}
referrals-per-second {{ OPNsense.bind.general.ratelimitrefsps }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimiterrsps') and OPNsense.bind.general.ratelimiterrsps != '' %}
errors-per-second {{ OPNsense.bind.general.ratelimiterrsps }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitallps') and OPNsense.bind.general.ratelimitallps != '' %}
all-per-second {{ OPNsense.bind.general.ratelimitallps }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitslip') and OPNsense.bind.general.ratelimitslip != '' %}
slip {{ OPNsense.bind.general.ratelimitslip }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitscale') and OPNsense.bind.general.ratelimitscale != '' %}
qps-scale {{ OPNsense.bind.general.ratelimitscale }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitmaxtbl') and OPNsense.bind.general.ratelimitmaxtbl != '' %}
max-table-size {{ OPNsense.bind.general.ratelimitmaxtbl }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitmintbl') and OPNsense.bind.general.ratelimitmintbl != '' %}
min-table-size {{ OPNsense.bind.general.ratelimitmintbl }};
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimitexcept') and OPNsense.bind.general.ratelimitexcept != '' %}
exempt-clients { {{ OPNsense.bind.general.ratelimitexcept.replace(',', '; ') }}; };
exempt-clients { {{ OPNsense.bind.general.ratelimitexcept.replace(',', '; ') }}; };
{% endif %}
{% if helpers.exists('OPNsense.bind.general.ratelimittry') and OPNsense.bind.general.ratelimittry != '' %}
log-only {{ OPNsense.bind.general.ratelimittry }};
{% endif %}
};
{% endif %}