Merge: + DNS: new settings (ratelimit, blocking mode, edns_client_subnet)
Close #1091 Close #1154 Close #1022 * commit '97e77cab643d6784067ce97c0f03ec3e4612c2c9': + client: handle EDNS Client Subnet setting + dns: add "edns_client_subnet" setting + client: handle DNS config * DNS: remove /enable_protection and /disable_protection + openapi: /dns_info, /dns_config * /control/set_upstreams_config: allow empty upstream list + dns: support blocking_mode=custom_ip + DNS: Get/Set DNS general settings
This commit is contained in:
commit
2b14a043a9
|
@ -29,6 +29,9 @@ Contents:
|
||||||
* Static IP check/set
|
* Static IP check/set
|
||||||
* Add a static lease
|
* Add a static lease
|
||||||
* API: Reset DHCP configuration
|
* API: Reset DHCP configuration
|
||||||
|
* DNS general settings
|
||||||
|
* API: Get DNS general settings
|
||||||
|
* API: Set DNS general settings
|
||||||
* DNS access settings
|
* DNS access settings
|
||||||
* List access settings
|
* List access settings
|
||||||
* Set access settings
|
* Set access settings
|
||||||
|
@ -801,6 +804,50 @@ Response:
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
## DNS general settings
|
||||||
|
|
||||||
|
### API: Get DNS general settings
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
GET /control/dns_info
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
200 OK
|
||||||
|
|
||||||
|
{
|
||||||
|
"protection_enabled": true | false,
|
||||||
|
"ratelimit": 1234,
|
||||||
|
"blocking_mode": "nxdomain" | "null_ip" | "custom_ip",
|
||||||
|
"blocking_ipv4": "1.2.3.4",
|
||||||
|
"blocking_ipv6": "1:2:3::4",
|
||||||
|
"edns_cs_enabled": true | false,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### API: Set DNS general settings
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
POST /control/dns_config
|
||||||
|
|
||||||
|
{
|
||||||
|
"protection_enabled": true | false,
|
||||||
|
"ratelimit": 1234,
|
||||||
|
"blocking_mode": "nxdomain" | "null_ip" | "custom_ip",
|
||||||
|
"blocking_ipv4": "1.2.3.4",
|
||||||
|
"blocking_ipv6": "1:2:3::4",
|
||||||
|
"edns_cs_enabled": true | false,
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
200 OK
|
||||||
|
|
||||||
|
`blocking_ipv4` and `blocking_ipv6` values are active when `blocking_mode` is set to `custom_ip`.
|
||||||
|
|
||||||
|
|
||||||
## DNS access settings
|
## DNS access settings
|
||||||
|
|
||||||
There are low-level settings that can block undesired DNS requests. "Blocking" means not responding to request.
|
There are low-level settings that can block undesired DNS requests. "Blocking" means not responding to request.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"form_error_ip_format": "Invalid IP format",
|
"form_error_ip_format": "Invalid IP format",
|
||||||
"form_error_mac_format": "Invalid MAC format",
|
"form_error_mac_format": "Invalid MAC format",
|
||||||
"form_error_positive": "Must be greater than 0",
|
"form_error_positive": "Must be greater than 0",
|
||||||
|
"form_error_negative": "Must be equal to 0 or greater",
|
||||||
"dhcp_form_gateway_input": "Gateway IP",
|
"dhcp_form_gateway_input": "Gateway IP",
|
||||||
"dhcp_form_subnet_input": "Subnet mask",
|
"dhcp_form_subnet_input": "Subnet mask",
|
||||||
"dhcp_form_range_title": "Range of IP addresses",
|
"dhcp_form_range_title": "Range of IP addresses",
|
||||||
|
@ -187,6 +188,22 @@
|
||||||
"query_log_disabled": "The query log is disabled and can be configured in the <0>settings</0>",
|
"query_log_disabled": "The query log is disabled and can be configured in the <0>settings</0>",
|
||||||
"query_log_strict_search": "Use double quotes for strict search",
|
"query_log_strict_search": "Use double quotes for strict search",
|
||||||
"query_log_retention_confirm": "Are you sure you want to change query log retention? If you decrease the interval value, some data will be lost",
|
"query_log_retention_confirm": "Are you sure you want to change query log retention? If you decrease the interval value, some data will be lost",
|
||||||
|
"dns_config": "DNS server configuration",
|
||||||
|
"blocking_mode": "Blocking mode",
|
||||||
|
"nxdomain": "NXDOMAIN",
|
||||||
|
"null_ip": "Null IP",
|
||||||
|
"custom_ip": "Custom IP",
|
||||||
|
"blocking_ipv4": "Blocking IPv4",
|
||||||
|
"blocking_ipv6": "Blocking IPv6",
|
||||||
|
"form_enter_rate_limit": "Enter rate limit",
|
||||||
|
"rate_limit": "Rate limit",
|
||||||
|
"edns_enable": "Enable EDNS Client Subnet",
|
||||||
|
"edns_cs_desc": "If enabled, AdGuard Home will be sending clients' subnets to the DNS servers.",
|
||||||
|
"rate_limit_desc": "The number of requests per second that a single client is allowed to make (0: unlimited)",
|
||||||
|
"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_mode_desc": "<0>NXDOMAIN – Respond with NXDOMAIN code;</0> <0>Null IP – Respond with zero IP address (0.0.0.0 for A; :: for AAAA);</0> <0>Custom IP - Respond with a manually set IP address.</0>",
|
||||||
|
"upstream_dns_client_desc": "If you keep this field empty, AdGuard Home will use the servers configured in the <0>DNS settings</0>.",
|
||||||
"source_label": "Source",
|
"source_label": "Source",
|
||||||
"found_in_known_domain_db": "Found in the known domains database.",
|
"found_in_known_domain_db": "Found in the known domains database.",
|
||||||
"category_label": "Category",
|
"category_label": "Category",
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { createAction } from 'redux-actions';
|
||||||
|
|
||||||
|
import apiClient from '../api/Api';
|
||||||
|
import { addErrorToast, addSuccessToast } from './index';
|
||||||
|
|
||||||
|
export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST');
|
||||||
|
export const getDnsConfigFailure = createAction('GET_DNS_CONFIG_FAILURE');
|
||||||
|
export const getDnsConfigSuccess = createAction('GET_DNS_CONFIG_SUCCESS');
|
||||||
|
|
||||||
|
export const getDnsConfig = () => async (dispatch) => {
|
||||||
|
dispatch(getDnsConfigRequest());
|
||||||
|
try {
|
||||||
|
const data = await apiClient.getDnsConfig();
|
||||||
|
dispatch(getDnsConfigSuccess(data));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(getDnsConfigFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setDnsConfigRequest = createAction('SET_DNS_CONFIG_REQUEST');
|
||||||
|
export const setDnsConfigFailure = createAction('SET_DNS_CONFIG_FAILURE');
|
||||||
|
export const setDnsConfigSuccess = createAction('SET_DNS_CONFIG_SUCCESS');
|
||||||
|
|
||||||
|
export const setDnsConfig = config => async (dispatch) => {
|
||||||
|
dispatch(setDnsConfigRequest());
|
||||||
|
try {
|
||||||
|
await apiClient.setDnsConfig(config);
|
||||||
|
dispatch(addSuccessToast('config_successfully_saved'));
|
||||||
|
dispatch(setDnsConfigSuccess(config));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(setDnsConfigFailure());
|
||||||
|
}
|
||||||
|
};
|
|
@ -91,17 +91,9 @@ export const toggleProtectionSuccess = createAction('TOGGLE_PROTECTION_SUCCESS')
|
||||||
|
|
||||||
export const toggleProtection = status => async (dispatch) => {
|
export const toggleProtection = status => async (dispatch) => {
|
||||||
dispatch(toggleProtectionRequest());
|
dispatch(toggleProtectionRequest());
|
||||||
let successMessage = '';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (status) {
|
const successMessage = status ? 'disabled_protection' : 'enabled_protection';
|
||||||
successMessage = 'disabled_protection';
|
await apiClient.setDnsConfig({ protection_enabled: !status });
|
||||||
await apiClient.disableGlobalProtection();
|
|
||||||
} else {
|
|
||||||
successMessage = 'enabled_protection';
|
|
||||||
await apiClient.enableGlobalProtection();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(addSuccessToast(successMessage));
|
dispatch(addSuccessToast(successMessage));
|
||||||
dispatch(toggleProtectionSuccess());
|
dispatch(toggleProtectionSuccess());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -30,8 +30,6 @@ class Api {
|
||||||
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' };
|
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' };
|
||||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
||||||
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
||||||
GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' };
|
|
||||||
GLOBAL_DISABLE_PROTECTION = { path: 'disable_protection', method: 'POST' };
|
|
||||||
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
||||||
|
|
||||||
startGlobalFiltering() {
|
startGlobalFiltering() {
|
||||||
|
@ -76,16 +74,6 @@ class Api {
|
||||||
return this.makeRequest(path, method, config);
|
return this.makeRequest(path, method, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
enableGlobalProtection() {
|
|
||||||
const { path, method } = this.GLOBAL_ENABLE_PROTECTION;
|
|
||||||
return this.makeRequest(path, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
disableGlobalProtection() {
|
|
||||||
const { path, method } = this.GLOBAL_DISABLE_PROTECTION;
|
|
||||||
return this.makeRequest(path, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUpdate() {
|
getUpdate() {
|
||||||
const { path, method } = this.GLOBAL_UPDATE;
|
const { path, method } = this.GLOBAL_UPDATE;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
|
@ -546,6 +534,24 @@ class Api {
|
||||||
const { path, method } = this.GET_PROFILE;
|
const { path, method } = this.GET_PROFILE;
|
||||||
return this.makeRequest(path, method);
|
return this.makeRequest(path, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNS config
|
||||||
|
GET_DNS_CONFIG = { path: 'dns_info', method: 'GET' };
|
||||||
|
SET_DNS_CONFIG = { path: 'dns_config', method: 'POST' };
|
||||||
|
|
||||||
|
getDnsConfig() {
|
||||||
|
const { path, method } = this.GET_DNS_CONFIG;
|
||||||
|
return this.makeRequest(path, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDnsConfig(data) {
|
||||||
|
const { path, method } = this.SET_DNS_CONFIG;
|
||||||
|
const config = {
|
||||||
|
data,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
};
|
||||||
|
return this.makeRequest(path, method, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiClient = new Api();
|
const apiClient = new Api();
|
||||||
|
|
|
@ -225,6 +225,11 @@ let Form = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div label="upstream" title={props.t('upstream_dns')}>
|
<div label="upstream" title={props.t('upstream_dns')}>
|
||||||
|
<div className="form__desc mb-3">
|
||||||
|
<Trans components={[<a href="#dns" key="0">link</a>]}>
|
||||||
|
upstream_dns_client_desc
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
<Field
|
<Field
|
||||||
id="upstreams"
|
id="upstreams"
|
||||||
name="upstreams"
|
name="upstreams"
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||||
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
renderField,
|
||||||
|
renderRadioField,
|
||||||
|
renderSelectField,
|
||||||
|
required,
|
||||||
|
ipv4,
|
||||||
|
ipv6,
|
||||||
|
biggerOrEqualZero,
|
||||||
|
toNumber,
|
||||||
|
} from '../../../../helpers/form';
|
||||||
|
import { BLOCKING_MODES } from '../../../../helpers/constants';
|
||||||
|
|
||||||
|
const getFields = (processing, t) => Object.values(BLOCKING_MODES).map(mode => (
|
||||||
|
<Field
|
||||||
|
key={mode}
|
||||||
|
name="blocking_mode"
|
||||||
|
type="radio"
|
||||||
|
component={renderRadioField}
|
||||||
|
value={mode}
|
||||||
|
placeholder={t(mode)}
|
||||||
|
disabled={processing}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
let Form = ({
|
||||||
|
handleSubmit, submitting, invalid, processing, blockingMode, t,
|
||||||
|
}) => (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12 col-sm-6">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="ratelimit" className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="ratelimit"
|
||||||
|
type="number"
|
||||||
|
component={renderField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_rate_limit')}
|
||||||
|
normalize={toNumber}
|
||||||
|
validate={[required, biggerOrEqualZero]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name="edns_cs_enabled"
|
||||||
|
type="checkbox"
|
||||||
|
component={renderSelectField}
|
||||||
|
placeholder={t('edns_enable')}
|
||||||
|
disabled={processing}
|
||||||
|
subtitle={t('edns_cs_desc')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings mb-4">
|
||||||
|
<label className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocking_mode</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans components={[<div key="0">text</div>]}>blocking_mode_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<div className="custom-controls-stacked">
|
||||||
|
{getFields(processing, t)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{blockingMode === BLOCKING_MODES.custom_ip && (
|
||||||
|
<Fragment>
|
||||||
|
<div className="col-12 col-sm-6">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="blocking_ipv4" className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocking_ipv4</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>blocking_ipv4_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="blocking_ipv4"
|
||||||
|
component={renderField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_ip')}
|
||||||
|
validate={[ipv4, required]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-sm-6">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="ip_address" className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocking_ipv6</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>blocking_ipv6_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
name="blocking_ipv6"
|
||||||
|
component={renderField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_enter_ip')}
|
||||||
|
validate={[ipv6, required]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard btn-large"
|
||||||
|
disabled={submitting || invalid || processing}
|
||||||
|
>
|
||||||
|
<Trans>save_btn</Trans>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
blockingMode: PropTypes.string.isRequired,
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
submitting: PropTypes.bool.isRequired,
|
||||||
|
invalid: PropTypes.bool.isRequired,
|
||||||
|
processing: PropTypes.bool.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector('blockingModeForm');
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const blockingMode = selector(state, 'blocking_mode');
|
||||||
|
return {
|
||||||
|
blockingMode,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withNamespaces(),
|
||||||
|
reduxForm({
|
||||||
|
form: 'blockingModeForm',
|
||||||
|
}),
|
||||||
|
])(Form);
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
import Card from '../../../ui/Card';
|
||||||
|
import Form from './Form';
|
||||||
|
|
||||||
|
const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
setDnsConfig(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
blocking_mode,
|
||||||
|
ratelimit,
|
||||||
|
blocking_ipv4,
|
||||||
|
blocking_ipv6,
|
||||||
|
edns_cs_enabled,
|
||||||
|
processingSetConfig,
|
||||||
|
} = dnsConfig;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t('dns_config')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
id="dns-config"
|
||||||
|
>
|
||||||
|
<div className="form">
|
||||||
|
<Form
|
||||||
|
initialValues={{
|
||||||
|
ratelimit,
|
||||||
|
blocking_mode,
|
||||||
|
blocking_ipv4,
|
||||||
|
blocking_ipv6,
|
||||||
|
edns_cs_enabled,
|
||||||
|
}}
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
processing={processingSetConfig}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Config.propTypes = {
|
||||||
|
dnsConfig: PropTypes.object.isRequired,
|
||||||
|
setDnsConfig: PropTypes.func.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Config);
|
|
@ -5,6 +5,7 @@ import { withNamespaces } from 'react-i18next';
|
||||||
import Upstream from './Upstream';
|
import Upstream from './Upstream';
|
||||||
import Access from './Access';
|
import Access from './Access';
|
||||||
import Rewrites from './Rewrites';
|
import Rewrites from './Rewrites';
|
||||||
|
import Config from './Config';
|
||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ class Dns extends Component {
|
||||||
this.props.getDnsSettings();
|
this.props.getDnsSettings();
|
||||||
this.props.getAccessList();
|
this.props.getAccessList();
|
||||||
this.props.getRewritesList();
|
this.props.getRewritesList();
|
||||||
|
this.props.getDnsConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -29,12 +31,18 @@ class Dns extends Component {
|
||||||
addRewrite,
|
addRewrite,
|
||||||
deleteRewrite,
|
deleteRewrite,
|
||||||
toggleRewritesModal,
|
toggleRewritesModal,
|
||||||
|
dnsConfig,
|
||||||
|
setDnsConfig,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isDataLoading =
|
const isDataLoading = dashboard.processingDnsSettings
|
||||||
dashboard.processingDnsSettings || access.processing || rewrites.processing;
|
|| access.processing
|
||||||
const isDataReady =
|
|| rewrites.processing
|
||||||
!dashboard.processingDnsSettings && !access.processing && !rewrites.processing;
|
|| dnsConfig.processingGetConfig;
|
||||||
|
const isDataReady = !dashboard.processingDnsSettings
|
||||||
|
&& !access.processing
|
||||||
|
&& !rewrites.processing
|
||||||
|
&& !dnsConfig.processingGetConfig;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -42,6 +50,10 @@ class Dns extends Component {
|
||||||
{isDataLoading && <Loading />}
|
{isDataLoading && <Loading />}
|
||||||
{isDataReady && (
|
{isDataReady && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
<Config
|
||||||
|
dnsConfig={dnsConfig}
|
||||||
|
setDnsConfig={setDnsConfig}
|
||||||
|
/>
|
||||||
<Upstream
|
<Upstream
|
||||||
upstreamDns={dashboard.upstreamDns}
|
upstreamDns={dashboard.upstreamDns}
|
||||||
bootstrapDns={dashboard.bootstrapDns}
|
bootstrapDns={dashboard.bootstrapDns}
|
||||||
|
@ -80,6 +92,9 @@ Dns.propTypes = {
|
||||||
deleteRewrite: PropTypes.func.isRequired,
|
deleteRewrite: PropTypes.func.isRequired,
|
||||||
toggleRewritesModal: PropTypes.func.isRequired,
|
toggleRewritesModal: PropTypes.func.isRequired,
|
||||||
getDnsSettings: PropTypes.func.isRequired,
|
getDnsSettings: PropTypes.func.isRequired,
|
||||||
|
dnsConfig: PropTypes.object.isRequired,
|
||||||
|
setDnsConfig: PropTypes.func.isRequired,
|
||||||
|
getDnsConfig: PropTypes.func.isRequired,
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Services from './Services';
|
||||||
import StatsConfig from './StatsConfig';
|
import StatsConfig from './StatsConfig';
|
||||||
import LogsConfig from './LogsConfig';
|
import LogsConfig from './LogsConfig';
|
||||||
import FiltersConfig from './FiltersConfig';
|
import FiltersConfig from './FiltersConfig';
|
||||||
|
|
||||||
import Checkbox from '../ui/Checkbox';
|
import Checkbox from '../ui/Checkbox';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import PageTitle from '../ui/PageTitle';
|
import PageTitle from '../ui/PageTitle';
|
||||||
|
|
|
@ -7,17 +7,19 @@ import {
|
||||||
deleteRewrite,
|
deleteRewrite,
|
||||||
toggleRewritesModal,
|
toggleRewritesModal,
|
||||||
} from '../actions/rewrites';
|
} from '../actions/rewrites';
|
||||||
|
import { getDnsConfig, setDnsConfig } from '../actions/dnsConfig';
|
||||||
import Dns from '../components/Settings/Dns';
|
import Dns from '../components/Settings/Dns';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const {
|
const {
|
||||||
dashboard, settings, access, rewrites,
|
dashboard, settings, access, rewrites, dnsConfig,
|
||||||
} = state;
|
} = state;
|
||||||
const props = {
|
const props = {
|
||||||
dashboard,
|
dashboard,
|
||||||
settings,
|
settings,
|
||||||
access,
|
access,
|
||||||
rewrites,
|
rewrites,
|
||||||
|
dnsConfig,
|
||||||
};
|
};
|
||||||
return props;
|
return props;
|
||||||
};
|
};
|
||||||
|
@ -33,6 +35,8 @@ const mapDispatchToProps = {
|
||||||
deleteRewrite,
|
deleteRewrite,
|
||||||
toggleRewritesModal,
|
toggleRewritesModal,
|
||||||
getDnsSettings,
|
getDnsSettings,
|
||||||
|
getDnsConfig,
|
||||||
|
setDnsConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -341,6 +341,12 @@ export const QUERY_LOG_INTERVALS_DAYS = [1, 7, 30, 90];
|
||||||
|
|
||||||
export const FILTERS_INTERVALS_HOURS = [0, 1, 12, 24, 72, 168];
|
export const FILTERS_INTERVALS_HOURS = [0, 1, 12, 24, 72, 168];
|
||||||
|
|
||||||
|
export const BLOCKING_MODES = {
|
||||||
|
nxdomain: 'nxdomain',
|
||||||
|
null_ip: 'null_ip',
|
||||||
|
custom_ip: 'custom_ip',
|
||||||
|
};
|
||||||
|
|
||||||
export const WHOIS_ICONS = {
|
export const WHOIS_ICONS = {
|
||||||
location: 'location',
|
location: 'location',
|
||||||
orgname: 'network',
|
orgname: 'network',
|
||||||
|
|
|
@ -189,6 +189,13 @@ export const isPositive = (value) => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const biggerOrEqualZero = (value) => {
|
||||||
|
if (value < 0) {
|
||||||
|
return <Trans>form_error_negative</Trans>;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export const port = (value) => {
|
export const port = (value) => {
|
||||||
if ((value || value === 0) && (value < 80 || value > 65535)) {
|
if ((value || value === 0) && (value < 80 || value > 65535)) {
|
||||||
return <Trans>form_error_port_range</Trans>;
|
return <Trans>form_error_port_range</Trans>;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { handleActions } from 'redux-actions';
|
||||||
|
|
||||||
|
import * as actions from '../actions/dnsConfig';
|
||||||
|
import { BLOCKING_MODES } from '../helpers/constants';
|
||||||
|
|
||||||
|
const DEFAULT_BLOCKING_IPV4 = '0.0.0.0';
|
||||||
|
const DEFAULT_BLOCKING_IPV6 = '::';
|
||||||
|
|
||||||
|
const dnsConfig = handleActions(
|
||||||
|
{
|
||||||
|
[actions.getDnsConfigRequest]: state => ({ ...state, processingGetConfig: true }),
|
||||||
|
[actions.getDnsConfigFailure]: state =>
|
||||||
|
({ ...state, processingGetConfig: false }),
|
||||||
|
[actions.getDnsConfigSuccess]: (state, { payload }) => {
|
||||||
|
const {
|
||||||
|
blocking_ipv4,
|
||||||
|
blocking_ipv6,
|
||||||
|
...values
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...values,
|
||||||
|
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
||||||
|
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
||||||
|
processingGetConfig: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[actions.setDnsConfigRequest]: state => ({ ...state, processingSetConfig: true }),
|
||||||
|
[actions.setDnsConfigFailure]: state =>
|
||||||
|
({ ...state, processingSetConfig: false }),
|
||||||
|
[actions.setDnsConfigSuccess]: (state, { payload }) => ({
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
processingSetConfig: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processingGetConfig: false,
|
||||||
|
processingSetConfig: false,
|
||||||
|
blocking_mode: BLOCKING_MODES.nxdomain,
|
||||||
|
ratelimit: 20,
|
||||||
|
blocking_ipv4: DEFAULT_BLOCKING_IPV4,
|
||||||
|
blocking_ipv6: DEFAULT_BLOCKING_IPV6,
|
||||||
|
edns_cs_enabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default dnsConfig;
|
|
@ -13,6 +13,7 @@ import rewrites from './rewrites';
|
||||||
import services from './services';
|
import services from './services';
|
||||||
import stats from './stats';
|
import stats from './stats';
|
||||||
import queryLogs from './queryLogs';
|
import queryLogs from './queryLogs';
|
||||||
|
import dnsConfig from './dnsConfig';
|
||||||
import filtering from './filtering';
|
import filtering from './filtering';
|
||||||
|
|
||||||
const settings = handleActions(
|
const settings = handleActions(
|
||||||
|
@ -369,6 +370,7 @@ export default combineReducers({
|
||||||
rewrites,
|
rewrites,
|
||||||
services,
|
services,
|
||||||
stats,
|
stats,
|
||||||
|
dnsConfig,
|
||||||
loadingBar: loadingBarReducer,
|
loadingBar: loadingBarReducer,
|
||||||
form: formReducer,
|
form: formReducer,
|
||||||
});
|
});
|
||||||
|
|
|
@ -99,14 +99,21 @@ type FilteringConfig struct {
|
||||||
|
|
||||||
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features
|
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features
|
||||||
|
|
||||||
BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests
|
BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests
|
||||||
|
BlockingIPv4 string `yaml:"blocking_ipv4"` // IP address to be returned for a blocked A request
|
||||||
|
BlockingIPv6 string `yaml:"blocking_ipv6"` // IP address to be returned for a blocked AAAA request
|
||||||
|
BlockingIPAddrv4 net.IP `yaml:"-"`
|
||||||
|
BlockingIPAddrv6 net.IP `yaml:"-"`
|
||||||
|
|
||||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
||||||
Ratelimit int `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable)
|
Ratelimit uint32 `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable)
|
||||||
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` // a list of whitelisted client IP addresses
|
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` // a list of whitelisted client IP addresses
|
||||||
RefuseAny bool `yaml:"refuse_any"` // if true, refuse ANY requests
|
RefuseAny bool `yaml:"refuse_any"` // if true, refuse ANY requests
|
||||||
BootstrapDNS []string `yaml:"bootstrap_dns"` // a list of bootstrap DNS for DoH and DoT (plain DNS only)
|
BootstrapDNS []string `yaml:"bootstrap_dns"` // a list of bootstrap DNS for DoH and DoT (plain DNS only)
|
||||||
AllServers bool `yaml:"all_servers"` // if true, parallel queries to all configured upstream servers are enabled
|
AllServers bool `yaml:"all_servers"` // if true, parallel queries to all configured upstream servers are enabled
|
||||||
|
|
||||||
|
EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option
|
||||||
|
|
||||||
AllowedClients []string `yaml:"allowed_clients"` // IP addresses of whitelist clients
|
AllowedClients []string `yaml:"allowed_clients"` // IP addresses of whitelist clients
|
||||||
DisallowedClients []string `yaml:"disallowed_clients"` // IP addresses of clients that should be blocked
|
DisallowedClients []string `yaml:"disallowed_clients"` // IP addresses of clients that should be blocked
|
||||||
BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
|
BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
|
||||||
|
@ -214,7 +221,7 @@ func (s *Server) prepare(config *ServerConfig) error {
|
||||||
proxyConfig := proxy.Config{
|
proxyConfig := proxy.Config{
|
||||||
UDPListenAddr: s.conf.UDPListenAddr,
|
UDPListenAddr: s.conf.UDPListenAddr,
|
||||||
TCPListenAddr: s.conf.TCPListenAddr,
|
TCPListenAddr: s.conf.TCPListenAddr,
|
||||||
Ratelimit: s.conf.Ratelimit,
|
Ratelimit: int(s.conf.Ratelimit),
|
||||||
RatelimitWhitelist: s.conf.RatelimitWhitelist,
|
RatelimitWhitelist: s.conf.RatelimitWhitelist,
|
||||||
RefuseAny: s.conf.RefuseAny,
|
RefuseAny: s.conf.RefuseAny,
|
||||||
CacheEnabled: true,
|
CacheEnabled: true,
|
||||||
|
@ -224,6 +231,7 @@ func (s *Server) prepare(config *ServerConfig) error {
|
||||||
BeforeRequestHandler: s.beforeRequestHandler,
|
BeforeRequestHandler: s.beforeRequestHandler,
|
||||||
RequestHandler: s.handleDNSRequest,
|
RequestHandler: s.handleDNSRequest,
|
||||||
AllServers: s.conf.AllServers,
|
AllServers: s.conf.AllServers,
|
||||||
|
EnableEDNSClientSubnet: s.conf.EnableEDNSClientSubnet,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.access = &accessCtx{}
|
s.access = &accessCtx{}
|
||||||
|
@ -657,6 +665,14 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
||||||
case dns.TypeAAAA:
|
case dns.TypeAAAA:
|
||||||
return s.genAAAARecord(m, net.IPv6zero)
|
return s.genAAAARecord(m, net.IPv6zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if s.conf.BlockingMode == "custom_ip" {
|
||||||
|
switch m.Question[0].Qtype {
|
||||||
|
case dns.TypeA:
|
||||||
|
return s.genARecord(m, s.conf.BlockingIPAddrv4)
|
||||||
|
case dns.TypeAAAA:
|
||||||
|
return s.genAAAARecord(m, s.conf.BlockingIPAddrv6)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.genNXDomain(m)
|
return s.genNXDomain(m)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||||
|
"github.com/AdguardTeam/golibs/jsonutil"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/golibs/utils"
|
"github.com/AdguardTeam/golibs/utils"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
@ -20,14 +21,112 @@ func httpError(r *http.Request, w http.ResponseWriter, code int, format string,
|
||||||
http.Error(w, text, code)
|
http.Error(w, text, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleProtectionEnable(w http.ResponseWriter, r *http.Request) {
|
type dnsConfigJSON struct {
|
||||||
s.conf.ProtectionEnabled = true
|
ProtectionEnabled bool `json:"protection_enabled"`
|
||||||
s.conf.ConfigModified()
|
RateLimit uint32 `json:"ratelimit"`
|
||||||
|
BlockingMode string `json:"blocking_mode"`
|
||||||
|
BlockingIPv4 string `json:"blocking_ipv4"`
|
||||||
|
BlockingIPv6 string `json:"blocking_ipv6"`
|
||||||
|
EDNSCSEnabled bool `json:"edns_cs_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleProtectionDisable(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
s.conf.ProtectionEnabled = false
|
resp := dnsConfigJSON{}
|
||||||
|
s.RLock()
|
||||||
|
resp.ProtectionEnabled = s.conf.ProtectionEnabled
|
||||||
|
resp.BlockingMode = s.conf.BlockingMode
|
||||||
|
resp.BlockingIPv4 = s.conf.BlockingIPv4
|
||||||
|
resp.BlockingIPv6 = s.conf.BlockingIPv6
|
||||||
|
resp.RateLimit = s.conf.Ratelimit
|
||||||
|
resp.EDNSCSEnabled = s.conf.EnableEDNSClientSubnet
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
|
js, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
httpError(r, w, http.StatusInternalServerError, "json.Marshal: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write(js)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBlockingMode(req dnsConfigJSON) bool {
|
||||||
|
bm := req.BlockingMode
|
||||||
|
if !(bm == "nxdomain" || bm == "null_ip" || bm == "custom_ip") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bm == "custom_ip" {
|
||||||
|
ip := net.ParseIP(req.BlockingIPv4)
|
||||||
|
if ip == nil || ip.To4() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = net.ParseIP(req.BlockingIPv6)
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := dnsConfigJSON{}
|
||||||
|
js, err := jsonutil.DecodeObject(&req, r.Body)
|
||||||
|
if err != nil {
|
||||||
|
httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("blocking_mode") && !checkBlockingMode(req) {
|
||||||
|
httpError(r, w, http.StatusBadRequest, "blocking_mode: incorrect value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
restart := false
|
||||||
|
s.Lock()
|
||||||
|
|
||||||
|
if js.Exists("protection_enabled") {
|
||||||
|
s.conf.ProtectionEnabled = req.ProtectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("blocking_mode") {
|
||||||
|
s.conf.BlockingMode = req.BlockingMode
|
||||||
|
if req.BlockingMode == "custom_ip" {
|
||||||
|
if js.Exists("blocking_ipv4") {
|
||||||
|
s.conf.BlockingIPv4 = req.BlockingIPv4
|
||||||
|
s.conf.BlockingIPAddrv4 = net.ParseIP(req.BlockingIPv4)
|
||||||
|
}
|
||||||
|
if js.Exists("blocking_ipv6") {
|
||||||
|
s.conf.BlockingIPv6 = req.BlockingIPv6
|
||||||
|
s.conf.BlockingIPAddrv6 = net.ParseIP(req.BlockingIPv6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("ratelimit") {
|
||||||
|
if s.conf.Ratelimit != req.RateLimit {
|
||||||
|
restart = true
|
||||||
|
}
|
||||||
|
s.conf.Ratelimit = req.RateLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Exists("edns_cs_enabled") {
|
||||||
|
s.conf.EnableEDNSClientSubnet = req.EDNSCSEnabled
|
||||||
|
restart = true
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Unlock()
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
|
|
||||||
|
if restart {
|
||||||
|
err = s.Restart()
|
||||||
|
if err != nil {
|
||||||
|
httpError(r, w, http.StatusInternalServerError, "%s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type upstreamJSON struct {
|
type upstreamJSON struct {
|
||||||
|
@ -44,10 +143,12 @@ func (s *Server) handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ValidateUpstreams(req.Upstreams)
|
if len(req.Upstreams) != 0 {
|
||||||
if err != nil {
|
err = ValidateUpstreams(req.Upstreams)
|
||||||
httpError(r, w, http.StatusBadRequest, "wrong upstreams specification: %s", err)
|
if err != nil {
|
||||||
return
|
httpError(r, w, http.StatusBadRequest, "wrong upstreams specification: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newconf := FilteringConfig{}
|
newconf := FilteringConfig{}
|
||||||
|
@ -270,12 +371,11 @@ func checkDNS(input string, bootstrap []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) registerHandlers() {
|
func (s *Server) registerHandlers() {
|
||||||
s.conf.HTTPRegister("POST", "/control/enable_protection", s.handleProtectionEnable)
|
s.conf.HTTPRegister("GET", "/control/dns_info", s.handleGetConfig)
|
||||||
s.conf.HTTPRegister("POST", "/control/disable_protection", s.handleProtectionDisable)
|
s.conf.HTTPRegister("POST", "/control/dns_config", s.handleSetConfig)
|
||||||
s.conf.HTTPRegister("POST", "/control/set_upstreams_config", s.handleSetUpstreamConfig)
|
s.conf.HTTPRegister("POST", "/control/set_upstreams_config", s.handleSetUpstreamConfig)
|
||||||
s.conf.HTTPRegister("POST", "/control/test_upstream_dns", s.handleTestUpstreamDNS)
|
s.conf.HTTPRegister("POST", "/control/test_upstream_dns", s.handleTestUpstreamDNS)
|
||||||
|
|
||||||
s.conf.HTTPRegister("GET", "/control/access/list", s.handleAccessList)
|
s.conf.HTTPRegister("GET", "/control/access/list", s.handleAccessList)
|
||||||
s.conf.HTTPRegister("POST", "/control/access/set", s.handleAccessSet)
|
s.conf.HTTPRegister("POST", "/control/access/set", s.handleAccessSet)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,30 @@ Response:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### Set DNS general settings: POST /control/dns_config
|
||||||
|
|
||||||
|
Replaces these API methods:
|
||||||
|
|
||||||
|
POST /control/enable_protection
|
||||||
|
POST /control/disable_protection
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
POST /control/dns_config
|
||||||
|
|
||||||
|
{
|
||||||
|
"protection_enabled": true | false,
|
||||||
|
"ratelimit": 1234,
|
||||||
|
"blocking_mode": "nxdomain" | "null_ip" | "custom_ip",
|
||||||
|
"blocking_ipv4": "1.2.3.4",
|
||||||
|
"blocking_ipv6": "1:2:3::4",
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
200 OK
|
||||||
|
|
||||||
|
|
||||||
## v0.99: incompatible API changes
|
## v0.99: incompatible API changes
|
||||||
|
|
||||||
* A note about web user authentication
|
* A note about web user authentication
|
||||||
|
|
|
@ -70,22 +70,31 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/ServerStatus"
|
$ref: "#/definitions/ServerStatus"
|
||||||
|
|
||||||
/enable_protection:
|
/dns_info:
|
||||||
post:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- global
|
- global
|
||||||
operationId: enableProtection
|
operationId: dnsInfo
|
||||||
summary: "Enable protection (turns on dnsfilter module in coredns)"
|
summary: 'Get general DNS parameters'
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/DNSConfig"
|
||||||
|
|
||||||
/disable_protection:
|
/dns_config:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- global
|
- global
|
||||||
operationId: disableProtection
|
operationId: dnsConfig
|
||||||
summary: "Disable protection (turns off filtering, sb, parental, safesearch temporarily by disabling dnsfilter module in coredns)"
|
summary: "Set general DNS parameters"
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: "body"
|
||||||
|
name: "body"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/DNSConfig"
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -1054,6 +1063,28 @@ definitions:
|
||||||
language:
|
language:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "en"
|
example: "en"
|
||||||
|
|
||||||
|
DNSConfig:
|
||||||
|
type: "object"
|
||||||
|
description: "Query log configuration"
|
||||||
|
properties:
|
||||||
|
protection_enabled:
|
||||||
|
type: "boolean"
|
||||||
|
ratelimit:
|
||||||
|
type: "integer"
|
||||||
|
blocking_mode:
|
||||||
|
type: "string"
|
||||||
|
enum:
|
||||||
|
- "nxdomain"
|
||||||
|
- "null_ip"
|
||||||
|
- "custom_ip"
|
||||||
|
blocking_ipv4:
|
||||||
|
type: "string"
|
||||||
|
blocking_ipv6:
|
||||||
|
type: "string"
|
||||||
|
edns_cs_enabled:
|
||||||
|
type: "boolean"
|
||||||
|
|
||||||
UpstreamsConfig:
|
UpstreamsConfig:
|
||||||
type: "object"
|
type: "object"
|
||||||
description: "Upstreams configuration"
|
description: "Upstreams configuration"
|
||||||
|
|
Loading…
Reference in New Issue