From fcb582679e6cb1a913267f57efd0507807b915f9 Mon Sep 17 00:00:00 2001 From: Reinaldo de Souza Jr Date: Sat, 5 Dec 2020 15:42:53 +0100 Subject: [PATCH] Create UI for DNS rebinding protection --- client/src/__locales/en.json | 8 +- client/src/actions/dnsConfig.js | 3 + .../components/Settings/Dns/Rebinding/Form.js | 91 +++++++++++++++++++ .../Settings/Dns/Rebinding/index.js | 36 ++++++++ client/src/components/Settings/Dns/index.js | 2 + client/src/helpers/constants.js | 1 + client/src/reducers/dnsConfig.js | 2 + 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 client/src/components/Settings/Dns/Rebinding/Form.js create mode 100644 client/src/components/Settings/Dns/Rebinding/index.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 388691b0..efe77161 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -587,5 +587,11 @@ "port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction on how to resolve this.", "adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.", "client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.", - "experimental": "Experimental" + "experimental": "Experimental", + "rebinding_title": "DNS Rebinding Protection", + "rebinding_desc": "Here you can configure protection against DNS rebinding attacks", + "rebinding_protection_enabled": "Enable protection from DNS rebinding attacks", + "rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.", + "rebinding_allowed_hosts_title": "Allowed domains", + "rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains." } diff --git a/client/src/actions/dnsConfig.js b/client/src/actions/dnsConfig.js index c599ca8d..f7809de6 100644 --- a/client/src/actions/dnsConfig.js +++ b/client/src/actions/dnsConfig.js @@ -37,6 +37,9 @@ export const setDnsConfig = (config) => async (dispatch) => { data.upstream_dns = splitByNewLine(config.upstream_dns); hasDnsSettings = true; } + if (Object.prototype.hasOwnProperty.call(data, 'rebinding_allowed_hosts')) { + data.rebinding_allowed_hosts = splitByNewLine(config.rebinding_allowed_hosts); + } await apiClient.setDnsConfig(data); diff --git a/client/src/components/Settings/Dns/Rebinding/Form.js b/client/src/components/Settings/Dns/Rebinding/Form.js new file mode 100644 index 00000000..69b59abf --- /dev/null +++ b/client/src/components/Settings/Dns/Rebinding/Form.js @@ -0,0 +1,91 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Field, reduxForm } from 'redux-form'; +import { Trans, useTranslation } from 'react-i18next'; +import { shallowEqual, useSelector } from 'react-redux'; +import { renderTextareaField, CheckboxField } from '../../../../helpers/form'; +import { removeEmptyLines } from '../../../../helpers/helpers'; +import { FORM_NAME } from '../../../../helpers/constants'; + +const fields = [ + { + id: 'rebinding_allowed_hosts', + title: 'rebinding_allowed_hosts_title', + subtitle: 'rebinding_allowed_hosts_desc', + normalizeOnBlur: removeEmptyLines, + }, +]; + +const Form = ({ + handleSubmit, submitting, invalid, +}) => { + const { t } = useTranslation(); + const { processingSetConfig } = useSelector((state) => state.dnsConfig, shallowEqual); + + const renderField = ({ + id, title, subtitle, disabled = processingSetConfig, normalizeOnBlur, + }) =>
+ +
+ {subtitle} +
+ +
; + + renderField.propTypes = { + id: PropTypes.string, + title: PropTypes.string, + subtitle: PropTypes.string, + disabled: PropTypes.bool, + normalizeOnBlur: PropTypes.func, + }; + + return ( +
+
+
+ +
+
+ + {fields.map(renderField)} + +
+
+ +
+
+
+ ); +}; + +Form.propTypes = { + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + invalid: PropTypes.bool.isRequired, +}; + +export default reduxForm({ form: FORM_NAME.REBINDING })(Form); diff --git a/client/src/components/Settings/Dns/Rebinding/index.js b/client/src/components/Settings/Dns/Rebinding/index.js new file mode 100644 index 00000000..c1e349fc --- /dev/null +++ b/client/src/components/Settings/Dns/Rebinding/index.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; +import Form from './Form'; +import Card from '../../../ui/Card'; +import { setDnsConfig } from '../../../../actions/dnsConfig'; + +const RebindingConfig = () => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const { + rebinding_protection_enabled, rebinding_allowed_hosts, + } = useSelector((state) => state.dnsConfig, shallowEqual); + + const handleFormSubmit = (values) => { + dispatch(setDnsConfig(values)); + }; + + return ( + +
+ + ); +}; + +export default RebindingConfig; diff --git a/client/src/components/Settings/Dns/index.js b/client/src/components/Settings/Dns/index.js index 06c68f21..226585d3 100644 --- a/client/src/components/Settings/Dns/index.js +++ b/client/src/components/Settings/Dns/index.js @@ -8,6 +8,7 @@ import Config from './Config'; import PageTitle from '../../ui/PageTitle'; import Loading from '../../ui/Loading'; import CacheConfig from './Cache'; +import RebindingConfig from './Rebinding'; import { getDnsConfig } from '../../../actions/dnsConfig'; import { getAccessList } from '../../../actions/access'; @@ -33,6 +34,7 @@ const Dns = () => { + } ; }; diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index 7bce8387..f8d567ce 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -509,6 +509,7 @@ export const FORM_NAME = { INSTALL: 'install', LOGIN: 'login', CACHE: 'cache', + REBINDING: 'rebinding', ...DHCP_FORM_NAMES, }; diff --git a/client/src/reducers/dnsConfig.js b/client/src/reducers/dnsConfig.js index bbe4ad2f..b05d9991 100644 --- a/client/src/reducers/dnsConfig.js +++ b/client/src/reducers/dnsConfig.js @@ -16,6 +16,7 @@ const dnsConfig = handleActions( blocking_ipv6, upstream_dns, bootstrap_dns, + rebinding_allowed_hosts, ...values } = payload; @@ -26,6 +27,7 @@ const dnsConfig = handleActions( blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6, upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '', bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '', + rebinding_allowed_hosts: (rebinding_allowed_hosts && rebinding_allowed_hosts.join('\n')) || '', processingGetConfig: false, }; },