Pull request 2065: 6369-ratelimit-settings-ui
Closes #6369.
Co-authored-by: IldarKamalov <ik@adguard.com>
Squashed commit of the following:
commit efc824667a88765d5a16984fd17ecda2559f2b1e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Nov 15 19:10:47 2023 +0300
all: imp docs
commit 9ec59b59000f005006ea231071329a586d9889ac
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Nov 15 17:21:03 2023 +0300
dnsforward: imp err msg
commit d9710dfc1dcf74d5ee8386b053d7180316f21bce
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Nov 15 15:33:59 2023 +0300
all: upd chlog
commit 29e868b93b15cfce5faed4d0c07b16decbce52f9
Merge: 1c3aec9f1 ebb06a583
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Wed Nov 15 15:26:32 2023 +0300
Merge branch 'master' into 6369-ratelimit-settings-ui
commit 1c3aec9f1478f71afa4d0aa9ba1c454e9d98b8db
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Tue Nov 14 21:21:22 2023 +0300
dnsforward: imp docs
commit 486bf86e5a2b51b6014a231386337a2d1e945c23
Author: Ildar Kamalov <ik@adguard.com>
Date: Mon Nov 13 09:57:21 2023 +0300
fix linter
commit aec088f233737fdfa0e7086148ceb79df0d2e39a
Author: Ildar Kamalov <ik@adguard.com>
Date: Sun Nov 12 16:13:46 2023 +0300
client: validate rate limit subnets
commit d4ca4d3a604295cdfaae54e6e461981233eabf3e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Fri Nov 10 20:08:44 2023 +0300
dnsforward: imp code
commit 5c11a1ef5c6fcc786d8496b14b9b16d1de1708cd
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date: Fri Nov 10 15:07:56 2023 +0300
all: ratelimit settings
This commit is contained in:
@@ -23,12 +23,17 @@ See also the [v0.107.42 GitHub milestone][ms-v0.107.42].
|
|||||||
NOTE: Add new changes BELOW THIS COMMENT.
|
NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Ability to specify rate limiting settings in the Web UI ([#6369]).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Pre-filling the New static lease window with data ([#6402]).
|
- Pre-filling the New static lease window with data ([#6402]).
|
||||||
- Protection pause timer synchronization ([#5759]).
|
- Protection pause timer synchronization ([#5759]).
|
||||||
|
|
||||||
[#5759]: https://github.com/AdguardTeam/AdGuardHome/issues/5759
|
[#5759]: https://github.com/AdguardTeam/AdGuardHome/issues/5759
|
||||||
|
[#6369]: https://github.com/AdguardTeam/AdGuardHome/issues/6369
|
||||||
[#6402]: https://github.com/AdguardTeam/AdGuardHome/issues/6402
|
[#6402]: https://github.com/AdguardTeam/AdGuardHome/issues/6402
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
@@ -310,6 +310,16 @@
|
|||||||
"edns_use_custom_ip": "Use custom IP for EDNS",
|
"edns_use_custom_ip": "Use custom IP for EDNS",
|
||||||
"edns_use_custom_ip_desc": "Allow to use custom IP for EDNS",
|
"edns_use_custom_ip_desc": "Allow to use custom IP for EDNS",
|
||||||
"rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.",
|
"rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.",
|
||||||
|
"rate_limit_subnet_len_ipv4": "Subnet prefix length for IPv4 addresses",
|
||||||
|
"rate_limit_subnet_len_ipv4_desc": "Subnet prefix length for IPv4 addresses used for rate limiting. The default is 24",
|
||||||
|
"rate_limit_subnet_len_ipv4_error": "The IPv4 subnet prefix length should be between 0 and 32",
|
||||||
|
"rate_limit_subnet_len_ipv6": "Subnet prefix length for IPv6 addresses",
|
||||||
|
"rate_limit_subnet_len_ipv6_desc": "Subnet prefix length for IPv6 addresses used for rate limiting. The default is 56",
|
||||||
|
"rate_limit_subnet_len_ipv6_error": "The IPv6 subnet prefix length should be between 0 and 128",
|
||||||
|
"form_enter_rate_limit_subnet_len": "Enter subnet prefix length for rate limiting",
|
||||||
|
"rate_limit_whitelist": "Rate limiting allowlist",
|
||||||
|
"rate_limit_whitelist_desc": "IP addresses excluded from rate limiting",
|
||||||
|
"rate_limit_whitelist_placeholder": "Enter one IP address per line",
|
||||||
"blocking_ipv4_desc": "IP address to be returned for a blocked A request",
|
"blocking_ipv4_desc": "IP address to be returned for a blocked A request",
|
||||||
"blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request",
|
"blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request",
|
||||||
"blocking_mode_default": "Default: Respond with zero IP address (0.0.0.0 for A; :: for AAAA) when blocked by Adblock-style rule; respond with the IP address specified in the rule when blocked by /etc/hosts-style rule",
|
"blocking_mode_default": "Default: Respond with zero IP address (0.0.0.0 for A; :: for AAAA) when blocked by Adblock-style rule; respond with the IP address specified in the rule when blocked by /etc/hosts-style rule",
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
|||||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||||
hasDnsSettings = true;
|
hasDnsSettings = true;
|
||||||
}
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'ratelimit_whitelist')) {
|
||||||
|
data.ratelimit_whitelist = splitByNewLine(config.ratelimit_whitelist);
|
||||||
|
hasDnsSettings = true;
|
||||||
|
}
|
||||||
|
|
||||||
await apiClient.setDnsConfig(data);
|
await apiClient.setDnsConfig(data);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||||||
import {
|
import {
|
||||||
renderInputField,
|
renderInputField,
|
||||||
renderRadioField,
|
renderRadioField,
|
||||||
|
renderTextareaField,
|
||||||
CheckboxField,
|
CheckboxField,
|
||||||
toNumber,
|
toNumber,
|
||||||
} from '../../../../helpers/form';
|
} from '../../../../helpers/form';
|
||||||
@@ -14,7 +15,10 @@ import {
|
|||||||
validateIpv6,
|
validateIpv6,
|
||||||
validateRequiredValue,
|
validateRequiredValue,
|
||||||
validateIp,
|
validateIp,
|
||||||
|
validateIPv4Subnet,
|
||||||
|
validateIPv6Subnet,
|
||||||
} from '../../../../helpers/validators';
|
} from '../../../../helpers/validators';
|
||||||
|
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
|
import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
|
||||||
|
|
||||||
const checkboxes = [
|
const checkboxes = [
|
||||||
@@ -90,6 +94,69 @@ const Form = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-12 col-md-7">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="ratelimit_subnet_len_ipv4"
|
||||||
|
className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv4</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv4_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="ratelimit_subnet_len_ipv4"
|
||||||
|
type="number"
|
||||||
|
component={renderInputField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_rate_limit_subnet_len')}
|
||||||
|
normalize={toNumber}
|
||||||
|
validate={[validateRequiredValue, validateIPv4Subnet]}
|
||||||
|
min={0}
|
||||||
|
max={32}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-7">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="ratelimit_subnet_len_ipv6"
|
||||||
|
className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv6</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv6_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="ratelimit_subnet_len_ipv6"
|
||||||
|
type="number"
|
||||||
|
component={renderInputField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_rate_limit_subnet_len')}
|
||||||
|
normalize={toNumber}
|
||||||
|
validate={[validateRequiredValue, validateIPv6Subnet]}
|
||||||
|
min={0}
|
||||||
|
max={128}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-7">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="ratelimit_whitelist"
|
||||||
|
className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_whitelist</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_whitelist_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="ratelimit_whitelist"
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('rate_limit_whitelist_placeholder')}
|
||||||
|
normalizeOnBlur={removeEmptyLines}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Field
|
<Field
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ const Config = () => {
|
|||||||
const {
|
const {
|
||||||
blocking_mode,
|
blocking_mode,
|
||||||
ratelimit,
|
ratelimit,
|
||||||
|
ratelimit_subnet_len_ipv4,
|
||||||
|
ratelimit_subnet_len_ipv6,
|
||||||
|
ratelimit_whitelist,
|
||||||
blocking_ipv4,
|
blocking_ipv4,
|
||||||
blocking_ipv6,
|
blocking_ipv6,
|
||||||
blocked_response_ttl,
|
blocked_response_ttl,
|
||||||
@@ -36,6 +39,9 @@ const Config = () => {
|
|||||||
<Form
|
<Form
|
||||||
initialValues={{
|
initialValues={{
|
||||||
ratelimit,
|
ratelimit,
|
||||||
|
ratelimit_subnet_len_ipv4,
|
||||||
|
ratelimit_subnet_len_ipv6,
|
||||||
|
ratelimit_whitelist,
|
||||||
blocking_mode,
|
blocking_mode,
|
||||||
blocking_ipv4,
|
blocking_ipv4,
|
||||||
blocking_ipv6,
|
blocking_ipv6,
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ export const R_WIN_ABSOLUTE_PATH = /^([a-zA-Z]:)?(\\|\/)(?:[^\\/:*?"<>|\x00]+\\)
|
|||||||
|
|
||||||
export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/;
|
export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/;
|
||||||
|
|
||||||
|
export const R_IPV4_SUBNET = /^([0-9]|[1-2][0-9]|3[0-2])?$/;
|
||||||
|
|
||||||
|
export const R_IPV6_SUBNET = /^([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])?$/;
|
||||||
|
|
||||||
export const MIN_PASSWORD_LENGTH = 8;
|
export const MIN_PASSWORD_LENGTH = 8;
|
||||||
export const MAX_PASSWORD_LENGTH = 72;
|
export const MAX_PASSWORD_LENGTH = 72;
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
R_DOMAIN,
|
R_DOMAIN,
|
||||||
MAX_PASSWORD_LENGTH,
|
MAX_PASSWORD_LENGTH,
|
||||||
MIN_PASSWORD_LENGTH,
|
MIN_PASSWORD_LENGTH,
|
||||||
|
R_IPV4_SUBNET,
|
||||||
|
R_IPV6_SUBNET,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { ip4ToInt, isValidAbsolutePath } from './form';
|
import { ip4ToInt, isValidAbsolutePath } from './form';
|
||||||
import { isIpInCidr, parseSubnetMask } from './helpers';
|
import { isIpInCidr, parseSubnetMask } from './helpers';
|
||||||
@@ -365,3 +367,25 @@ export const validateIpGateway = (value, allValues) => {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value {string}
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export const validateIPv4Subnet = (value) => {
|
||||||
|
if (!R_IPV4_SUBNET.test(value)) {
|
||||||
|
return i18next.t('rate_limit_subnet_len_ipv4_error');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value {string}
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export const validateIPv6Subnet = (value) => {
|
||||||
|
if (!R_IPV6_SUBNET.test(value)) {
|
||||||
|
return i18next.t('rate_limit_subnet_len_ipv6_error');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const dnsConfig = handleActions(
|
|||||||
fallback_dns,
|
fallback_dns,
|
||||||
bootstrap_dns,
|
bootstrap_dns,
|
||||||
local_ptr_upstreams,
|
local_ptr_upstreams,
|
||||||
|
ratelimit_whitelist,
|
||||||
...values
|
...values
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ const dnsConfig = handleActions(
|
|||||||
fallback_dns: (fallback_dns && fallback_dns.join('\n')) || '',
|
fallback_dns: (fallback_dns && fallback_dns.join('\n')) || '',
|
||||||
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
||||||
local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '',
|
local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '',
|
||||||
|
ratelimit_whitelist: (ratelimit_whitelist && ratelimit_whitelist.join('\n')) || '',
|
||||||
processingGetConfig: false,
|
processingGetConfig: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -45,8 +45,19 @@ type jsonDNSConfig struct {
|
|||||||
// ProtectionEnabled defines if protection is enabled.
|
// ProtectionEnabled defines if protection is enabled.
|
||||||
ProtectionEnabled *bool `json:"protection_enabled"`
|
ProtectionEnabled *bool `json:"protection_enabled"`
|
||||||
|
|
||||||
// RateLimit is the number of requests per second allowed per client.
|
// Ratelimit is the number of requests per second allowed per client.
|
||||||
RateLimit *uint32 `json:"ratelimit"`
|
Ratelimit *uint32 `json:"ratelimit"`
|
||||||
|
|
||||||
|
// RatelimitSubnetLenIPv4 is a subnet length for IPv4 addresses used for
|
||||||
|
// rate limiting requests.
|
||||||
|
RatelimitSubnetLenIPv4 *int `json:"ratelimit_subnet_len_ipv4"`
|
||||||
|
|
||||||
|
// RatelimitSubnetLenIPv6 is a subnet length for IPv6 addresses used for
|
||||||
|
// rate limiting requests.
|
||||||
|
RatelimitSubnetLenIPv6 *int `json:"ratelimit_subnet_len_ipv6"`
|
||||||
|
|
||||||
|
// RatelimitWhitelist is a list of IP addresses excluded from rate limiting.
|
||||||
|
RatelimitWhitelist *[]string `json:"ratelimit_whitelist"`
|
||||||
|
|
||||||
// BlockingMode defines the way blocked responses are constructed.
|
// BlockingMode defines the way blocked responses are constructed.
|
||||||
BlockingMode *filtering.BlockingMode `json:"blocking_mode"`
|
BlockingMode *filtering.BlockingMode `json:"blocking_mode"`
|
||||||
@@ -121,6 +132,9 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
|
|||||||
blockingMode, blockingIPv4, blockingIPv6 := s.dnsFilter.BlockingMode()
|
blockingMode, blockingIPv4, blockingIPv6 := s.dnsFilter.BlockingMode()
|
||||||
blockedResponseTTL := s.dnsFilter.BlockedResponseTTL()
|
blockedResponseTTL := s.dnsFilter.BlockedResponseTTL()
|
||||||
ratelimit := s.conf.Ratelimit
|
ratelimit := s.conf.Ratelimit
|
||||||
|
ratelimitSubnetLenIPv4 := s.conf.RatelimitSubnetLenIPv4
|
||||||
|
ratelimitSubnetLenIPv6 := s.conf.RatelimitSubnetLenIPv6
|
||||||
|
ratelimitWhitelist := stringutil.CloneSliceOrEmpty(s.conf.RatelimitWhitelist)
|
||||||
|
|
||||||
customIP := s.conf.EDNSClientSubnet.CustomIP
|
customIP := s.conf.EDNSClientSubnet.CustomIP
|
||||||
enableEDNSClientSubnet := s.conf.EDNSClientSubnet.Enabled
|
enableEDNSClientSubnet := s.conf.EDNSClientSubnet.Enabled
|
||||||
@@ -157,7 +171,10 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
|
|||||||
BlockingMode: &blockingMode,
|
BlockingMode: &blockingMode,
|
||||||
BlockingIPv4: blockingIPv4,
|
BlockingIPv4: blockingIPv4,
|
||||||
BlockingIPv6: blockingIPv6,
|
BlockingIPv6: blockingIPv6,
|
||||||
RateLimit: &ratelimit,
|
Ratelimit: &ratelimit,
|
||||||
|
RatelimitSubnetLenIPv4: &ratelimitSubnetLenIPv4,
|
||||||
|
RatelimitSubnetLenIPv6: &ratelimitSubnetLenIPv6,
|
||||||
|
RatelimitWhitelist: &ratelimitWhitelist,
|
||||||
EDNSCSCustomIP: customIP,
|
EDNSCSCustomIP: customIP,
|
||||||
EDNSCSEnabled: &enableEDNSClientSubnet,
|
EDNSCSEnabled: &enableEDNSClientSubnet,
|
||||||
EDNSCSUseCustom: &useCustom,
|
EDNSCSUseCustom: &useCustom,
|
||||||
@@ -201,6 +218,7 @@ func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkBlockingMode returns an error if blocking mode is invalid.
|
||||||
func (req *jsonDNSConfig) checkBlockingMode() (err error) {
|
func (req *jsonDNSConfig) checkBlockingMode() (err error) {
|
||||||
if req.BlockingMode == nil {
|
if req.BlockingMode == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -209,12 +227,21 @@ func (req *jsonDNSConfig) checkBlockingMode() (err error) {
|
|||||||
return validateBlockingMode(*req.BlockingMode, req.BlockingIPv4, req.BlockingIPv6)
|
return validateBlockingMode(*req.BlockingMode, req.BlockingIPv4, req.BlockingIPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req *jsonDNSConfig) checkUpstreamsMode() bool {
|
// checkUpstreamsMode returns an error if the upstream mode is invalid.
|
||||||
valid := []string{"", "fastest_addr", "parallel"}
|
func (req *jsonDNSConfig) checkUpstreamsMode() (err error) {
|
||||||
|
if req.UpstreamMode == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return req.UpstreamMode == nil || stringutil.InSlice(valid, *req.UpstreamMode)
|
mode := *req.UpstreamMode
|
||||||
|
if ok := slices.Contains([]string{"", "fastest_addr", "parallel"}, mode); !ok {
|
||||||
|
return fmt.Errorf("upstream_mode: incorrect value %q", mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkBootstrap returns an error if any bootstrap address is invalid.
|
||||||
func (req *jsonDNSConfig) checkBootstrap() (err error) {
|
func (req *jsonDNSConfig) checkBootstrap() (err error) {
|
||||||
if req.Bootstraps == nil {
|
if req.Bootstraps == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -229,6 +256,7 @@ func (req *jsonDNSConfig) checkBootstrap() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err = upstream.NewUpstreamResolver(b, nil); err != nil {
|
if _, err = upstream.NewUpstreamResolver(b, nil); err != nil {
|
||||||
|
// Don't wrap the error because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,67 +272,157 @@ func (req *jsonDNSConfig) checkFallbacks() (err error) {
|
|||||||
|
|
||||||
err = ValidateUpstreams(*req.Fallbacks)
|
err = ValidateUpstreams(*req.Fallbacks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating fallback servers: %w", err)
|
return fmt.Errorf("fallback servers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate returns an error if any field of req is invalid.
|
// validate returns an error if any field of req is invalid.
|
||||||
|
//
|
||||||
|
// TODO(s.chzhen): Parse, don't validate.
|
||||||
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
|
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
|
||||||
|
defer func() { err = errors.Annotate(err, "validating dns config: %w") }()
|
||||||
|
|
||||||
|
err = req.validateUpstreamDNSServers(privateNets)
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = req.checkRatelimitSubnetMaskLen()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = req.checkRatelimitWhitelist()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = req.checkBlockingMode()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = req.checkUpstreamsMode()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = req.checkCacheTTL()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateUpstreamDNSServers returns an error if any field of req is invalid.
|
||||||
|
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
|
||||||
if req.Upstreams != nil {
|
if req.Upstreams != nil {
|
||||||
err = ValidateUpstreams(*req.Upstreams)
|
err = ValidateUpstreams(*req.Upstreams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating upstream servers: %w", err)
|
return fmt.Errorf("upstream servers: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.LocalPTRUpstreams != nil {
|
if req.LocalPTRUpstreams != nil {
|
||||||
err = ValidateUpstreamsPrivate(*req.LocalPTRUpstreams, privateNets)
|
err = ValidateUpstreamsPrivate(*req.LocalPTRUpstreams, privateNets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating private upstream servers: %w", err)
|
return fmt.Errorf("private upstream servers: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = req.checkBootstrap()
|
err = req.checkBootstrap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = req.checkFallbacks()
|
err = req.checkFallbacks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = req.checkBlockingMode()
|
return nil
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case !req.checkUpstreamsMode():
|
|
||||||
return errors.Error("upstream_mode: incorrect value")
|
|
||||||
case !req.checkCacheTTL():
|
|
||||||
return errors.Error("cache_ttl_min must be less or equal than cache_ttl_max")
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req *jsonDNSConfig) checkCacheTTL() bool {
|
// checkCacheTTL returns an error if the configuration of the cache TTL is
|
||||||
|
// invalid.
|
||||||
|
func (req *jsonDNSConfig) checkCacheTTL() (err error) {
|
||||||
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
|
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var min, max uint32
|
var minTTL, maxTTL uint32
|
||||||
if req.CacheMinTTL != nil {
|
if req.CacheMinTTL != nil {
|
||||||
min = *req.CacheMinTTL
|
minTTL = *req.CacheMinTTL
|
||||||
}
|
}
|
||||||
if req.CacheMaxTTL != nil {
|
if req.CacheMaxTTL != nil {
|
||||||
max = *req.CacheMaxTTL
|
maxTTL = *req.CacheMaxTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
return min <= max
|
if minTTL <= maxTTL {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Error("cache_ttl_min must be less or equal than cache_ttl_max")
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRatelimitSubnetMaskLen returns an error if the length of the subnet mask
|
||||||
|
// for IPv4 or IPv6 addresses is invalid.
|
||||||
|
func (req *jsonDNSConfig) checkRatelimitSubnetMaskLen() (err error) {
|
||||||
|
err = checkInclusion(req.RatelimitSubnetLenIPv4, 0, netutil.IPv4BitLen)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ratelimit_subnet_len_ipv4 is invalid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkInclusion(req.RatelimitSubnetLenIPv6, 0, netutil.IPv6BitLen)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ratelimit_subnet_len_ipv6 is invalid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkInclusion returns an error if a ptr is not nil and points to value,
|
||||||
|
// that not in the inclusive range between minN and maxN.
|
||||||
|
func checkInclusion(ptr *int, minN, maxN int) (err error) {
|
||||||
|
if ptr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := *ptr
|
||||||
|
switch {
|
||||||
|
case n < minN:
|
||||||
|
return fmt.Errorf("value %d less than min %d", n, minN)
|
||||||
|
case n > maxN:
|
||||||
|
return fmt.Errorf("value %d greater than max %d", n, maxN)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRatelimitWhitelist returns an error if any of IP addresses is invalid.
|
||||||
|
func (req *jsonDNSConfig) checkRatelimitWhitelist() (err error) {
|
||||||
|
if req.RatelimitWhitelist == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, ipStr := range *req.RatelimitWhitelist {
|
||||||
|
if _, err = netip.ParseAddr(ipStr); err != nil {
|
||||||
|
return fmt.Errorf("ratelimit whitelist: at index %d: %w", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSetConfig handles requests to the POST /control/dns_config endpoint.
|
// handleSetConfig handles requests to the POST /control/dns_config endpoint.
|
||||||
@@ -401,6 +519,9 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
|||||||
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
|
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
|
||||||
setIfNotNil(&s.conf.AddrProcConf.UseRDNS, dc.ResolveClients),
|
setIfNotNil(&s.conf.AddrProcConf.UseRDNS, dc.ResolveClients),
|
||||||
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS),
|
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS),
|
||||||
|
setIfNotNil(&s.conf.RatelimitSubnetLenIPv4, dc.RatelimitSubnetLenIPv4),
|
||||||
|
setIfNotNil(&s.conf.RatelimitSubnetLenIPv6, dc.RatelimitSubnetLenIPv6),
|
||||||
|
setIfNotNil(&s.conf.RatelimitWhitelist, dc.RatelimitWhitelist),
|
||||||
} {
|
} {
|
||||||
shouldRestart = shouldRestart || hasSet
|
shouldRestart = shouldRestart || hasSet
|
||||||
if shouldRestart {
|
if shouldRestart {
|
||||||
@@ -408,8 +529,8 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dc.RateLimit != nil && s.conf.Ratelimit != *dc.RateLimit {
|
if dc.Ratelimit != nil && s.conf.Ratelimit != *dc.Ratelimit {
|
||||||
s.conf.Ratelimit = *dc.RateLimit
|
s.conf.Ratelimit = *dc.Ratelimit
|
||||||
shouldRestart = true
|
shouldRestart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
|
|||||||
UDPListenAddrs: []*net.UDPAddr{},
|
UDPListenAddrs: []*net.UDPAddr{},
|
||||||
TCPListenAddrs: []*net.TCPAddr{},
|
TCPListenAddrs: []*net.TCPAddr{},
|
||||||
Config: Config{
|
Config: Config{
|
||||||
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
||||||
FallbackDNS: []string{"9.9.9.10"},
|
FallbackDNS: []string{"9.9.9.10"},
|
||||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
RatelimitSubnetLenIPv4: 24,
|
||||||
|
RatelimitSubnetLenIPv6: 56,
|
||||||
|
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||||
},
|
},
|
||||||
ConfigModified: func() {},
|
ConfigModified: func() {},
|
||||||
}
|
}
|
||||||
@@ -150,8 +152,10 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
|||||||
UDPListenAddrs: []*net.UDPAddr{},
|
UDPListenAddrs: []*net.UDPAddr{},
|
||||||
TCPListenAddrs: []*net.TCPAddr{},
|
TCPListenAddrs: []*net.TCPAddr{},
|
||||||
Config: Config{
|
Config: Config{
|
||||||
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
||||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
RatelimitSubnetLenIPv4: 24,
|
||||||
|
RatelimitSubnetLenIPv6: 56,
|
||||||
|
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||||
},
|
},
|
||||||
ConfigModified: func() {},
|
ConfigModified: func() {},
|
||||||
}
|
}
|
||||||
@@ -179,11 +183,19 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
|||||||
name: "blocking_mode_good",
|
name: "blocking_mode_good",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_bad",
|
name: "blocking_mode_bad",
|
||||||
wantSet: "blocking_ipv4 must be valid ipv4 on custom_ip blocking_mode",
|
wantSet: "validating dns config: " +
|
||||||
|
"blocking_ipv4 must be valid ipv4 on custom_ip blocking_mode",
|
||||||
}, {
|
}, {
|
||||||
name: "ratelimit",
|
name: "ratelimit",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
|
}, {
|
||||||
|
name: "ratelimit_subnet_len",
|
||||||
|
wantSet: "",
|
||||||
|
}, {
|
||||||
|
name: "ratelimit_whitelist_not_ip",
|
||||||
|
wantSet: `validating dns config: ratelimit whitelist: at index 1: ParseAddr("not.ip"): ` +
|
||||||
|
`unexpected character (at "not.ip")`,
|
||||||
}, {
|
}, {
|
||||||
name: "edns_cs_enabled",
|
name: "edns_cs_enabled",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
@@ -206,24 +218,26 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
|||||||
name: "upstream_mode_fastest_addr",
|
name: "upstream_mode_fastest_addr",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_dns_bad",
|
name: "upstream_dns_bad",
|
||||||
wantSet: `validating upstream servers: validating upstream "!!!": not an ip:port`,
|
wantSet: `validating dns config: ` +
|
||||||
|
`upstream servers: validating upstream "!!!": not an ip:port`,
|
||||||
}, {
|
}, {
|
||||||
name: "bootstraps_bad",
|
name: "bootstraps_bad",
|
||||||
wantSet: `checking bootstrap a: invalid address: bootstrap a:53: ` +
|
wantSet: `validating dns config: checking bootstrap a: invalid address: bootstrap a:53: ` +
|
||||||
`ParseAddr("a"): unable to parse IP`,
|
`ParseAddr("a"): unable to parse IP`,
|
||||||
}, {
|
}, {
|
||||||
name: "cache_bad_ttl",
|
name: "cache_bad_ttl",
|
||||||
wantSet: `cache_ttl_min must be less or equal than cache_ttl_max`,
|
wantSet: `validating dns config: cache_ttl_min must be less or equal than cache_ttl_max`,
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_bad",
|
name: "upstream_mode_bad",
|
||||||
wantSet: `upstream_mode: incorrect value`,
|
wantSet: `validating dns config: upstream_mode: incorrect value "somethingelse"`,
|
||||||
}, {
|
}, {
|
||||||
name: "local_ptr_upstreams_good",
|
name: "local_ptr_upstreams_good",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
}, {
|
}, {
|
||||||
name: "local_ptr_upstreams_bad",
|
name: "local_ptr_upstreams_bad",
|
||||||
wantSet: `validating private upstream servers: checking domain-specific upstreams: ` +
|
wantSet: `validating dns config: ` +
|
||||||
|
`private upstream servers: checking domain-specific upstreams: ` +
|
||||||
`bad arpa domain name "non.arpa.": not a reversed ip network`,
|
`bad arpa domain name "non.arpa.": not a reversed ip network`,
|
||||||
}, {
|
}, {
|
||||||
name: "local_ptr_upstreams_null",
|
name: "local_ptr_upstreams_null",
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -53,6 +56,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -89,6 +95,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -60,6 +63,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -99,6 +105,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "refused",
|
"blocking_mode": "refused",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -138,6 +147,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -177,6 +189,98 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 6,
|
"ratelimit": 6,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
|
"blocking_mode": "default",
|
||||||
|
"blocking_ipv4": "",
|
||||||
|
"blocking_ipv6": "",
|
||||||
|
"blocked_response_ttl": 10,
|
||||||
|
"edns_cs_enabled": false,
|
||||||
|
"dnssec_enabled": false,
|
||||||
|
"disable_ipv6": false,
|
||||||
|
"upstream_mode": "",
|
||||||
|
"cache_size": 0,
|
||||||
|
"cache_ttl_min": 0,
|
||||||
|
"cache_ttl_max": 0,
|
||||||
|
"cache_optimistic": false,
|
||||||
|
"resolve_clients": false,
|
||||||
|
"use_private_ptr_resolvers": false,
|
||||||
|
"local_ptr_upstreams": [],
|
||||||
|
"edns_cs_use_custom": false,
|
||||||
|
"edns_cs_custom_ip": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ratelimit_subnet_len": {
|
||||||
|
"req": {
|
||||||
|
"ratelimit": 12,
|
||||||
|
"ratelimit_subnet_len_ipv4": 32,
|
||||||
|
"ratelimit_subnet_len_ipv6": 128
|
||||||
|
},
|
||||||
|
"want": {
|
||||||
|
"upstream_dns": [
|
||||||
|
"8.8.8.8:53",
|
||||||
|
"8.8.4.4:53"
|
||||||
|
],
|
||||||
|
"upstream_dns_file": "",
|
||||||
|
"bootstrap_dns": [
|
||||||
|
"9.9.9.10",
|
||||||
|
"149.112.112.10",
|
||||||
|
"2620:fe::10",
|
||||||
|
"2620:fe::fe:10"
|
||||||
|
],
|
||||||
|
"fallback_dns": [],
|
||||||
|
"protection_enabled": true,
|
||||||
|
"protection_disabled_until": null,
|
||||||
|
"ratelimit": 12,
|
||||||
|
"ratelimit_subnet_len_ipv4": 32,
|
||||||
|
"ratelimit_subnet_len_ipv6": 128,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
|
"blocking_mode": "default",
|
||||||
|
"blocking_ipv4": "",
|
||||||
|
"blocking_ipv6": "",
|
||||||
|
"blocked_response_ttl": 10,
|
||||||
|
"edns_cs_enabled": false,
|
||||||
|
"dnssec_enabled": false,
|
||||||
|
"disable_ipv6": false,
|
||||||
|
"upstream_mode": "",
|
||||||
|
"cache_size": 0,
|
||||||
|
"cache_ttl_min": 0,
|
||||||
|
"cache_ttl_max": 0,
|
||||||
|
"cache_optimistic": false,
|
||||||
|
"resolve_clients": false,
|
||||||
|
"use_private_ptr_resolvers": false,
|
||||||
|
"local_ptr_upstreams": [],
|
||||||
|
"edns_cs_use_custom": false,
|
||||||
|
"edns_cs_custom_ip": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ratelimit_whitelist_not_ip": {
|
||||||
|
"req": {
|
||||||
|
"ratelimit_whitelist": [
|
||||||
|
"1.2.3.4",
|
||||||
|
"not.ip"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"want": {
|
||||||
|
"upstream_dns": [
|
||||||
|
"8.8.8.8:53",
|
||||||
|
"8.8.4.4:53"
|
||||||
|
],
|
||||||
|
"upstream_dns_file": "",
|
||||||
|
"bootstrap_dns": [
|
||||||
|
"9.9.9.10",
|
||||||
|
"149.112.112.10",
|
||||||
|
"2620:fe::10",
|
||||||
|
"2620:fe::fe:10"
|
||||||
|
],
|
||||||
|
"fallback_dns": [],
|
||||||
|
"protection_enabled": true,
|
||||||
|
"protection_disabled_until": null,
|
||||||
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -216,6 +320,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -257,6 +364,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -298,6 +408,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -337,6 +450,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -376,6 +492,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -415,6 +534,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -454,6 +576,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -495,6 +620,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -536,6 +664,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -576,6 +707,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -615,6 +749,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -656,6 +793,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -700,6 +840,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -739,6 +882,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -782,6 +928,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -821,6 +970,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
@@ -863,6 +1015,9 @@
|
|||||||
"protection_enabled": true,
|
"protection_enabled": true,
|
||||||
"protection_disabled_until": null,
|
"protection_disabled_until": null,
|
||||||
"ratelimit": 0,
|
"ratelimit": 0,
|
||||||
|
"ratelimit_subnet_len_ipv4": 24,
|
||||||
|
"ratelimit_subnet_len_ipv6": 56,
|
||||||
|
"ratelimit_whitelist": [],
|
||||||
"blocking_mode": "default",
|
"blocking_mode": "default",
|
||||||
"blocking_ipv4": "",
|
"blocking_ipv4": "",
|
||||||
"blocking_ipv6": "",
|
"blocking_ipv6": "",
|
||||||
|
|||||||
@@ -4,6 +4,25 @@
|
|||||||
|
|
||||||
## v0.108.0: API changes
|
## v0.108.0: API changes
|
||||||
|
|
||||||
|
## v0.107.42: API changes
|
||||||
|
|
||||||
|
### The new field `"ratelimit_subnet_len_ipv4"` in `DNSConfig` object
|
||||||
|
|
||||||
|
* The new field `"ratelimit_subnet_len_ipv4"` in `GET /control/dns_info` and
|
||||||
|
`POST /control/dns_config` is the length of the subnet mask for IPv4
|
||||||
|
addresses.
|
||||||
|
|
||||||
|
### The new field `"ratelimit_subnet_len_ipv6"` in `DNSConfig` object
|
||||||
|
|
||||||
|
* The new field `"ratelimit_subnet_len_ipv6"` in `GET /control/dns_info` and
|
||||||
|
`POST /control/dns_config` is the length of the subnet mask for IPv6
|
||||||
|
addresses.
|
||||||
|
|
||||||
|
### The new field `"ratelimit_whitelist"` in `DNSConfig` object
|
||||||
|
|
||||||
|
* The new field `"blocked_response_ttl"` in `GET /control/dns_info` and `POST
|
||||||
|
/control/dns_config` is the list of IP addresses excluded from rate limiting.
|
||||||
|
|
||||||
## v0.107.39: API changes
|
## v0.107.39: API changes
|
||||||
|
|
||||||
### New HTTP API 'POST /control/dhcp/update_static_lease'
|
### New HTTP API 'POST /control/dhcp/update_static_lease'
|
||||||
|
|||||||
@@ -1468,6 +1468,23 @@
|
|||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'ratelimit':
|
'ratelimit':
|
||||||
'type': 'integer'
|
'type': 'integer'
|
||||||
|
'ratelimit_subnet_subnet_len_ipv4':
|
||||||
|
'description': 'Length of the subnet mask for IPv4 addresses.'
|
||||||
|
'type': 'integer'
|
||||||
|
'default': 24
|
||||||
|
'minimum': 0
|
||||||
|
'maximum': 32
|
||||||
|
'ratelimit_subnet_subnet_len_ipv6':
|
||||||
|
'description': 'Length of the subnet mask for IPv6 addresses.'
|
||||||
|
'type': 'integer'
|
||||||
|
'default': 56
|
||||||
|
'minimum': 0
|
||||||
|
'maximum': 128
|
||||||
|
'ratelimit_whitelist':
|
||||||
|
'type': 'array'
|
||||||
|
'description': 'List of IP addresses excluded from rate limiting.'
|
||||||
|
'items':
|
||||||
|
'type': 'string'
|
||||||
'blocking_mode':
|
'blocking_mode':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'enum':
|
'enum':
|
||||||
|
|||||||
Reference in New Issue
Block a user