mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
names: Add option for setting global DNS-over-TLS preference
Tests:
- Visit the names app. New 'Domains' heading and configuration section appear.
- DNS-over-TLS configuration option is as expected.
- When the configuration file does not exist, the option selected is 'no'.
- When the configuration option is changed, 'resolvectl' shows the newly set
configuration. Using 'resolvectl query {domain}' does not work when DoT is on
and server does not support DoT. 'opportunistic' and 'no' work on those cases.
- When a DNS server supporting DoT (such as 1.1.1.1) is manually set, resolution
with all three settings works.
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
parent
64cfdc07b8
commit
a124681083
39
plinth/modules/names/forms.py
Normal file
39
plinth/modules/names/forms.py
Normal file
@ -0,0 +1,39 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Forms for the names app."""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
|
||||
class NamesConfigurationForm(forms.Form):
|
||||
"""Form to configure names app."""
|
||||
|
||||
dns_over_tls = forms.ChoiceField(
|
||||
label=_('Use DNS-over-TLS for resolving domains (global preference)'),
|
||||
widget=forms.RadioSelect, choices=[
|
||||
('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 '
|
||||
'well-known public DNS servers in individual network '
|
||||
'connection settings.</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.</p>', allow_markup=True)),
|
||||
], initial='no')
|
||||
@ -3,26 +3,37 @@
|
||||
|
||||
import pathlib
|
||||
|
||||
import augeas
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth.actions import privileged
|
||||
|
||||
fallback_conf = pathlib.Path(
|
||||
'/etc/systemd/resolved.conf.d/freedombox-fallback.conf')
|
||||
override_conf = pathlib.Path('/etc/systemd/resolved.conf.d/freedombox.conf')
|
||||
source_fallback_conf = pathlib.Path(
|
||||
'/usr/share/freedombox'
|
||||
'/etc/systemd/resolved.conf.d/freedombox-fallback.conf')
|
||||
|
||||
|
||||
@privileged
|
||||
def set_resolved_configuration(dns_fallback: bool | None = None):
|
||||
def set_resolved_configuration(dns_fallback: bool | None = None,
|
||||
dns_over_tls: str | None = None):
|
||||
"""Set systemd-resolved configuration options."""
|
||||
if dns_fallback is not None:
|
||||
_set_enable_dns_fallback(dns_fallback)
|
||||
|
||||
if dns_over_tls is not None:
|
||||
_set_resolved_configuration(dns_over_tls)
|
||||
|
||||
action_utils.service_reload('systemd-resolved')
|
||||
|
||||
|
||||
def get_resolved_configuration() -> dict[str, bool]:
|
||||
"""Return systemd-resolved configuration."""
|
||||
return {'dns_fallback': fallback_conf.exists()}
|
||||
configuration = _get_resolved_configuration()
|
||||
configuration['dns_fallback'] = fallback_conf.exists()
|
||||
return configuration
|
||||
|
||||
|
||||
def _set_enable_dns_fallback(dns_fallback: bool):
|
||||
@ -34,4 +45,28 @@ def _set_enable_dns_fallback(dns_fallback: bool):
|
||||
else:
|
||||
fallback_conf.unlink(missing_ok=True)
|
||||
|
||||
action_utils.service_reload('systemd-resolved')
|
||||
|
||||
def _load_augeas():
|
||||
"""Initialize Augeas."""
|
||||
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
||||
augeas.Augeas.NO_MODL_AUTOLOAD)
|
||||
aug.transform('Systemd', str(override_conf))
|
||||
aug.set('/augeas/context', '/files' + str(override_conf))
|
||||
aug.load()
|
||||
return aug
|
||||
|
||||
|
||||
def _get_resolved_configuration():
|
||||
"""Return overridden configuration for systemd-resolved."""
|
||||
aug = _load_augeas()
|
||||
return {'dns_over_tls': aug.get('Resolve/DNSOverTLS/value') or 'no'}
|
||||
|
||||
|
||||
def _set_resolved_configuration(dns_over_tls: str | None = None):
|
||||
"""Write configuration into a systemd-resolved override file."""
|
||||
aug = _load_augeas()
|
||||
|
||||
if dns_over_tls is not None:
|
||||
aug.set('Resolve/DNSOverTLS/value', dns_over_tls)
|
||||
|
||||
aug.save()
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block configuration %}
|
||||
{% block status %}
|
||||
{{ block.super }}
|
||||
|
||||
<h3>{% trans "Domains" %}</h3>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table names-table">
|
||||
|
||||
@ -3,9 +3,13 @@
|
||||
FreedomBox app for name services.
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import components
|
||||
from . import components, privileged
|
||||
from .forms import NamesConfigurationForm
|
||||
|
||||
|
||||
class NamesAppView(AppView):
|
||||
@ -13,6 +17,14 @@ class NamesAppView(AppView):
|
||||
|
||||
app_id = 'names'
|
||||
template_name = 'names.html'
|
||||
prefix = 'names'
|
||||
form_class = NamesConfigurationForm
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the values to fill in the form."""
|
||||
initial = super().get_initial()
|
||||
initial.update(privileged.get_resolved_configuration())
|
||||
return initial
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
"""Add additional context data for template."""
|
||||
@ -20,6 +32,18 @@ class NamesAppView(AppView):
|
||||
context['status'] = get_status()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Apply the changes submitted in the form."""
|
||||
old_data = form.initial
|
||||
form_data = form.cleaned_data
|
||||
|
||||
if old_data['dns_over_tls'] != form_data['dns_over_tls']:
|
||||
privileged.set_resolved_configuration(
|
||||
dns_over_tls=form_data['dns_over_tls'])
|
||||
messages.success(self.request, _('Configuration updated'))
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def get_status():
|
||||
"""Get configured services per name."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user