FreedomBox/plinth/modules/names/privileged.py
Sunil Mohan Adapa 7e8819d7d5
names: Try to install systemd-resolved during app setup
- If installing systemd-resolved for the first time, set fallback DNS setting to
True irrespective of the app version.

Tests:

- Ensure that systemd-resolved is not installed. On a fresh systemd without
first setup done, run service.

- Names app setup is run and systemd-resolved is installed if internet
connection is available. Setup succeeds. Fallback DNS setting is true in privacy
app. systemd-resolved has been restarted and current DNS known to Network
Manager has been populated in it. Name resolution works.

- If Internet connection is not available, setup still succeeds but
systemd-resolved package is not installed.

- Rerun setup without internet connectivity. Setup succeeds without installing
systemd-resolved.

- Rerun setup with internet connectivity. Setup succeeds and installs
systemd-resolved. Fallback DNS setting is true in privacy app. systemd-resolved
has been restarted and current DNS known to Network Manager has been populated
in it. Name resolution works.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
2024-10-07 01:34:37 +03:00

135 lines
4.2 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configure Names App."""
import pathlib
import subprocess
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_hostname(hostname: str):
"""Set system hostname using hostnamectl."""
subprocess.run(
['hostnamectl', 'set-hostname', '--transient', '--static', hostname],
check=True)
action_utils.service_restart('avahi-daemon')
@privileged
def set_domain_name(domain_name: str | None = None):
"""Set system's static domain name in /etc/hosts."""
hostname = subprocess.check_output(['hostname']).decode().strip()
hosts_path = pathlib.Path('/etc/hosts')
if domain_name:
insert_line = f'127.0.1.1 {hostname}.{domain_name} {hostname}\n'
else:
insert_line = f'127.0.1.1 {hostname}\n'
lines = hosts_path.read_text(encoding='utf-8').splitlines(keepends=True)
new_lines = []
found = False
for line in lines:
if '127.0.1.1' in line:
new_lines.append(insert_line)
found = True
else:
new_lines.append(line)
if not found:
new_lines.append(insert_line)
hosts_path.write_text(''.join(new_lines), encoding='utf-8')
@privileged
def install_resolved():
"""Install systemd-resolved related packages."""
packages = ['systemd-resolved', 'libnss-resolve']
subprocess.run(['dpkg', '--configure', '-a'], check=False)
with action_utils.apt_hold_freedombox():
action_utils.run_apt_command(['--fix-broken', 'install'])
returncode = action_utils.run_apt_command(['install'] + packages)
if returncode:
raise RuntimeError(
f'Apt command failed with return code: {returncode}')
@privileged
def set_resolved_configuration(dns_fallback: bool | None = None,
dns_over_tls: str | None = None,
dnssec: 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 or dnssec is not None:
_set_resolved_configuration(dns_over_tls, dnssec)
# Workaround buggy reload that does not apply DNS-over-TLS changes
# properly.
action_utils.service_try_restart('systemd-resolved')
def get_resolved_configuration() -> dict[str, bool]:
"""Return systemd-resolved configuration."""
configuration = _get_resolved_configuration()
configuration['dns_fallback'] = fallback_conf.exists()
return configuration
def _set_enable_dns_fallback(dns_fallback: bool):
"""Update whether to use DNS fallback servers."""
if dns_fallback:
if not fallback_conf.exists():
fallback_conf.parent.mkdir(parents=True, exist_ok=True)
fallback_conf.symlink_to(source_fallback_conf)
else:
fallback_conf.unlink(missing_ok=True)
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()
# Default value for DNSSEC upstream is 'allow-downgrade', but in Debian it
# is 'no'.
return {
'dns_over_tls': aug.get('Resolve/DNSOverTLS/value') or 'no',
'dnssec': aug.get('Resolve/DNSSEC/value') or 'no'
}
def _set_resolved_configuration(dns_over_tls: str | None = None,
dnssec: 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)
if dnssec is not None:
aug.set('Resolve/DNSSEC/value', dnssec)
aug.save()