mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
networks: Add support for DNS-over-TLS for individual connections
- Expose Network Manager per-connection setting for DNS-over-TLS. Support all four values: default, no, opportunistic, and yes. - Create a new collapsible section all 'Privacy' for this setting the connection create/edit form. Strictly speaking this is related to security and censorship resistance too. - Don't show the DoT field for PPPoE connection types are DNS servers are not relevant. - Show the status of DoT for a connection in the connection status page. Tests: - In all Add New Connection forms except PPPoE form, the privacy section shows up as expected. - For each value for DoT, create a new connection and set the value for DoT to the desired value and observe that the connection status page shows DoT to the set value. - For each value for DoT, edit an existing connection and set the value for the DoT to the desired value and observe that the connection status page shows DoT to the set value. - Connection status page shows the values for DoT as expected. - Update the primary Internet connection for the machine. Set the value to 'yes' and notice that DNS resolutions fail. Set the value to 'opportunistic' or 'no' and the DNS resolutions pass. In each case, 'resolvectl' shows the correct DoT value for the connection. When 1.1.1.1 is set as DNS server, all values of DoT in the connection succeed. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
parent
01968d7d7e
commit
2abf2dc88c
@ -30,6 +30,37 @@ class ConnectionForm(forms.Form):
|
||||
help_text=_('The firewall zone will control which services are '
|
||||
'available over this interfaces. Select Internal only '
|
||||
'for trusted networks.'), choices=network.ZONES)
|
||||
dns_over_tls = forms.ChoiceField(
|
||||
label=_('Use DNS-over-TLS'), widget=forms.RadioSelect, choices=[
|
||||
('default',
|
||||
format_lazy(
|
||||
'Default. Unspecified for this connection. <p '
|
||||
'class="help-block">Use the global preference.</p>',
|
||||
allow_markup=True)),
|
||||
('yes',
|
||||
format_lazy(
|
||||
'Yes. Encrypt connections to the DNS server. <p '
|
||||
'class="help-block">This improves privacy as domain name '
|
||||
'queries will not be made as plain text over the network. It '
|
||||
'also improves security as responses from the server cannot '
|
||||
'be manipulated. If the configured DNS servers do not '
|
||||
'support DNS-over-TLS, all name resolutions will fail. If '
|
||||
'your DNS provider (likely your ISP) does not support '
|
||||
'DNS-over-TLS or blocks some domains, you can configure a '
|
||||
'well-known public DNS server below.</p>',
|
||||
allow_markup=True)),
|
||||
('opportunistic',
|
||||
format_lazy(
|
||||
'Opportunistic. <p class="help-block">Encrypt connections to '
|
||||
'the DNS server if the server supports DNS-over-TLS. '
|
||||
'Otherwise, use unencrypted connections. There is no '
|
||||
'protection against response manipulation.</p>',
|
||||
allow_markup=True)),
|
||||
('no',
|
||||
format_lazy(
|
||||
'No. <p class="help-block">Do not encrypt domain name '
|
||||
'resolutions for this connection.</p>', allow_markup=True)),
|
||||
], initial='default')
|
||||
ipv4_method = forms.ChoiceField(
|
||||
label=_('IPv4 Addressing Method'), widget=forms.RadioSelect, choices=[
|
||||
('auto',
|
||||
@ -127,6 +158,7 @@ class ConnectionForm(forms.Form):
|
||||
'name': self.cleaned_data['name'],
|
||||
'interface': self.cleaned_data['interface'],
|
||||
'zone': self.cleaned_data['zone'],
|
||||
'dns_over_tls': self.cleaned_data['dns_over_tls'],
|
||||
}
|
||||
settings['ipv4'] = self.get_ipv4_settings()
|
||||
settings['ipv6'] = self.get_ipv6_settings()
|
||||
@ -191,6 +223,7 @@ class EthernetForm(ConnectionForm):
|
||||
|
||||
class PPPoEForm(EthernetForm):
|
||||
"""Form to create a new PPPoE connection."""
|
||||
dns_over_tls = None
|
||||
ipv4_method = None
|
||||
ipv4_address = None
|
||||
ipv4_netmask = None
|
||||
|
||||
@ -256,6 +256,15 @@
|
||||
<p>{% trans "This connection is not active." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<h3>{% trans "Privacy" %}</h3>
|
||||
|
||||
<div class="list-group list-group-two-column">
|
||||
<div class="list-group-item">
|
||||
<span class="primary">{% trans "DNS-over-TLS" %}</span>
|
||||
<span class="secondary">{{ connection.dns_over_tls_string }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{% trans "Security" %}</h3>
|
||||
|
||||
{% if connection.zone == "internal" %}
|
||||
|
||||
@ -37,6 +37,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.dns_over_tls %}
|
||||
{% include "connections_fields_privacy.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if form.ssid %}
|
||||
{% include "connections_fields_wifi.html" %}
|
||||
{% endif %}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class='field-group'>
|
||||
<div class="card-header" id="header-privacy">
|
||||
<h4>
|
||||
<button class="btn btn-block text-left dropdown-toggle {{ form.errors|yesno:",collapsed" }}"
|
||||
type="button" data-toggle="collapse"
|
||||
data-target="#collapse-privacy"
|
||||
aria-expanded="{{ form.errors|yesno:"true,false" }}"
|
||||
aria-controls="collapse-privacy">
|
||||
{% trans "Privacy" %}
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div id="collapse-privacy" class="collapse {{ form.errors|yesno:"show," }}"
|
||||
aria-labelledby="header-privacy"
|
||||
data-parent="#accordion-connections-fields">
|
||||
<div class="card-body">
|
||||
{{ form.dns_over_tls|bootstrap }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -114,6 +114,16 @@ WIRELESS_MODE_STRINGS = {
|
||||
'mesh': gettext_lazy('mesh point'),
|
||||
}
|
||||
|
||||
# i18n for connection.dns_over_tls
|
||||
# https://networkmanager.dev/docs/libnm/latest/NMSettingConnection.html#
|
||||
# NMSettingConnectionDnsOverTls
|
||||
DNS_OVER_TLS_STRINGS = {
|
||||
'default': gettext_lazy('default'),
|
||||
'no': gettext_lazy('no'),
|
||||
'opportunistic': gettext_lazy('opportunistic'),
|
||||
'yes': gettext_lazy('yes'),
|
||||
}
|
||||
|
||||
|
||||
class NetworksAppView(AppView):
|
||||
"""Show networks app main page."""
|
||||
@ -149,6 +159,8 @@ def show(request, uuid):
|
||||
connection_status = network.get_status_from_connection(connection)
|
||||
connection_status['zone_string'] = dict(network.ZONES).get(
|
||||
connection_status['zone'], connection_status['zone'])
|
||||
connection_status['dns_over_tls_string'] = DNS_OVER_TLS_STRINGS.get(
|
||||
connection_status['dns_over_tls'], connection_status['dns_over_tls'])
|
||||
connection_status['ipv4']['method_string'] = CONNECTION_METHOD_STRINGS.get(
|
||||
connection_status['ipv4']['method'],
|
||||
connection_status['ipv4']['method'])
|
||||
@ -248,6 +260,9 @@ def edit(request, uuid):
|
||||
form_data['zone'] = 'external'
|
||||
|
||||
if settings_connection.get_connection_type() != 'pppoe':
|
||||
form_data['dns_over_tls'] = \
|
||||
settings_connection.get_dns_over_tls().value_nick
|
||||
|
||||
settings_ipv4 = connection.get_setting_ip4_config()
|
||||
form_data['ipv4_method'] = settings_ipv4.get_method()
|
||||
if settings_ipv4.get_num_addresses():
|
||||
|
||||
@ -101,6 +101,8 @@ def get_status_from_connection(connection):
|
||||
status['uuid'] = connection.get_uuid()
|
||||
status['type'] = connection.get_connection_type()
|
||||
status['zone'] = connection.get_setting_connection().get_zone()
|
||||
status['dns_over_tls'] = \
|
||||
connection.get_setting_connection().get_dns_over_tls().value_nick
|
||||
status['interface_name'] = connection.get_interface_name()
|
||||
status['primary'] = _is_primary(connection)
|
||||
|
||||
@ -333,6 +335,16 @@ def _update_common_settings(connection, connection_uuid, common):
|
||||
if 'zone' in common:
|
||||
settings.set_property(nm.SETTING_CONNECTION_ZONE, common['zone'])
|
||||
|
||||
if 'dns_over_tls' in common:
|
||||
values = {
|
||||
'default': nm.SettingConnectionDnsOverTls.DEFAULT,
|
||||
'no': nm.SettingConnectionDnsOverTls.NO,
|
||||
'opportunistic': nm.SettingConnectionDnsOverTls.OPPORTUNISTIC,
|
||||
'yes': nm.SettingConnectionDnsOverTls.YES
|
||||
}
|
||||
settings.set_property(nm.SETTING_CONNECTION_DNS_OVER_TLS,
|
||||
values[common['dns_over_tls']])
|
||||
|
||||
if 'autoconnect' in common:
|
||||
settings.set_property(nm.SETTING_CONNECTION_AUTOCONNECT,
|
||||
common['autoconnect'])
|
||||
|
||||
@ -17,6 +17,7 @@ ethernet_settings = {
|
||||
'name': 'plinth_test_eth',
|
||||
'interface': 'eth0',
|
||||
'zone': 'internal',
|
||||
'dns_over_tls': 'opportunistic',
|
||||
},
|
||||
'ipv4': {
|
||||
'method': 'auto',
|
||||
@ -36,6 +37,7 @@ wifi_settings = {
|
||||
'name': 'plinth_test_wifi',
|
||||
'interface': 'wlan0',
|
||||
'zone': 'external',
|
||||
'dns_over_tls': 'yes',
|
||||
},
|
||||
'ipv4': {
|
||||
'method': 'auto',
|
||||
@ -161,6 +163,7 @@ def test_edit_ethernet_connection(network, ethernet_uuid):
|
||||
ethernet_settings2['common']['name'] = 'plinth_test_eth_new'
|
||||
ethernet_settings2['common']['interface'] = 'eth1'
|
||||
ethernet_settings2['common']['zone'] = 'external'
|
||||
ethernet_settings2['common']['dns_over_tls'] = 'no'
|
||||
ethernet_settings2['common']['autoconnect'] = False
|
||||
ethernet_settings2['ipv4']['method'] = 'auto'
|
||||
network.edit_connection(connection, ethernet_settings2)
|
||||
@ -171,6 +174,7 @@ def test_edit_ethernet_connection(network, ethernet_uuid):
|
||||
settings_connection = connection.get_setting_connection()
|
||||
assert settings_connection.get_interface_name() == 'eth1'
|
||||
assert settings_connection.get_zone() == 'external'
|
||||
assert settings_connection.get_dns_over_tls().value_nick == 'no'
|
||||
assert not settings_connection.get_autoconnect()
|
||||
|
||||
settings_ipv4 = connection.get_setting_ip4_config()
|
||||
@ -214,6 +218,7 @@ def test_edit_wifi_connection(network, wifi_uuid):
|
||||
wifi_settings2['common']['name'] = 'plinth_test_wifi_new'
|
||||
wifi_settings2['common']['interface'] = 'wlan1'
|
||||
wifi_settings2['common']['zone'] = 'external'
|
||||
wifi_settings2['common']['dns_over_tls'] = 'opportunistic'
|
||||
wifi_settings2['common']['autoconnect'] = False
|
||||
wifi_settings2['ipv4']['method'] = 'auto'
|
||||
wifi_settings2['wireless']['ssid'] = 'plinthtestwifi2'
|
||||
@ -229,6 +234,7 @@ def test_edit_wifi_connection(network, wifi_uuid):
|
||||
settings_connection = connection.get_setting_connection()
|
||||
assert settings_connection.get_interface_name() == 'wlan1'
|
||||
assert settings_connection.get_zone() == 'external'
|
||||
assert settings_connection.get_dns_over_tls().value_nick == 'opportunistic'
|
||||
assert not settings_connection.get_autoconnect()
|
||||
|
||||
settings_wireless = connection.get_setting_wireless()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user