diff --git a/actions/security b/actions/security deleted file mode 100755 index f61775dcb..000000000 --- a/actions/security +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python3 -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Helper for security configuration -""" - -import argparse -import os - -from plinth.modules.security import (ACCESS_CONF_FILE, ACCESS_CONF_FILE_OLD, - ACCESS_CONF_SNIPPET, ACCESS_CONF_SNIPPETS) - - -def parse_arguments(): - """Return parsed command line arguments as dictionary""" - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - - subparsers.add_parser( - 'enable-restricted-access', - help='Restrict console login to users in admin or sudo group') - subparsers.add_parser( - 'disable-restricted-access', - help='Don\'t restrict console login to users in admin or sudo group') - - subparsers.required = True - return parser.parse_args() - - -def subcommand_enable_restricted_access(_): - """Restrict console login to users in admin or sudo group.""" - try: - os.mkdir(os.path.dirname(ACCESS_CONF_FILE)) - except FileExistsError: - pass - - with open(ACCESS_CONF_FILE, 'w', encoding='utf-8') as conffile: - conffile.write(ACCESS_CONF_SNIPPET + '\n') - - -def subcommand_disable_restricted_access(_): - """Don't restrict console login to users in admin or sudo group.""" - with open(ACCESS_CONF_FILE_OLD, 'r', encoding='utf-8') as conffile: - lines = conffile.readlines() - - with open(ACCESS_CONF_FILE_OLD, 'w', encoding='utf-8') as conffile: - for line in lines: - if line.strip() not in ACCESS_CONF_SNIPPETS: - conffile.write(line) - - try: - os.remove(ACCESS_CONF_FILE) - except OSError: - pass - - -def main(): - """Parse arguments and perform all duties""" - arguments = parse_arguments() - - subcommand = arguments.subcommand.replace('-', '_') - subcommand_method = globals()['subcommand_' + subcommand] - subcommand_method(arguments) - - -if __name__ == '__main__': - main() diff --git a/plinth/modules/security/__init__.py b/plinth/modules/security/__init__.py index 8f46d2990..d43a0bf00 100644 --- a/plinth/modules/security/__init__.py +++ b/plinth/modules/security/__init__.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -""" -FreedomBox app for security configuration. -""" +"""FreedomBox app for security configuration.""" import re import subprocess @@ -16,13 +14,7 @@ from plinth.daemon import Daemon, RelatedDaemon from plinth.modules.backups.components import BackupRestore from plinth.package import Packages -from . import manifest - -ACCESS_CONF_FILE = '/etc/security/access.d/50freedombox.conf' -ACCESS_CONF_FILE_OLD = '/etc/security/access.conf' -ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx plinth (admin) (sudo):ALL' -OLD_ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx (admin) (sudo):ALL' -ACCESS_CONF_SNIPPETS = [OLD_ACCESS_CONF_SNIPPET, ACCESS_CONF_SNIPPET] +from . import manifest, privileged class SecurityApp(app_module.App): @@ -66,43 +58,28 @@ class SecurityApp(app_module.App): actions.superuser_run('service', ['reload', 'fail2ban']) # Migrate to new config file. - enabled = get_restricted_access_enabled() + enabled = privileged.get_restricted_access_enabled() set_restricted_access(False) if enabled: set_restricted_access(True) def enable_fail2ban(): + """Unmask, enable and run the fail2ban service.""" actions.superuser_run('service', ['unmask', 'fail2ban']) actions.superuser_run('service', ['enable', 'fail2ban']) -def get_restricted_access_enabled(): - """Return whether restricted access is enabled""" - with open(ACCESS_CONF_FILE_OLD, 'r', encoding='utf-8') as conffile: - if any(line.strip() in ACCESS_CONF_SNIPPETS - for line in conffile.readlines()): - return True - - try: - with open(ACCESS_CONF_FILE, 'r', encoding='utf-8') as conffile: - return any(line.strip() in ACCESS_CONF_SNIPPETS - for line in conffile.readlines()) - except FileNotFoundError: - return False - - def set_restricted_access(enabled): - """Enable or disable restricted access""" - action = 'disable-restricted-access' + """Enable or disable restricted access.""" if enabled: - action = 'enable-restricted-access' - - actions.superuser_run('security', [action]) + privileged.enable_restricted_access() + else: + privileged.disable_restricted_access() def get_apps_report(): - """Return a security report for each app""" + """Return a security report for each app.""" lines = subprocess.check_output(['debsecan']).decode().split('\n') cves = defaultdict(set) for line in lines: diff --git a/plinth/modules/security/privileged.py b/plinth/modules/security/privileged.py new file mode 100644 index 000000000..b59a8f6fb --- /dev/null +++ b/plinth/modules/security/privileged.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Helper for security configuration.""" + +import os + +from plinth.actions import privileged + +ACCESS_CONF_FILE = '/etc/security/access.d/50freedombox.conf' +ACCESS_CONF_FILE_OLD = '/etc/security/access.conf' +ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx plinth (admin) (sudo):ALL' +OLD_ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx (admin) (sudo):ALL' +ACCESS_CONF_SNIPPETS = [OLD_ACCESS_CONF_SNIPPET, ACCESS_CONF_SNIPPET] + + +@privileged +def enable_restricted_access(): + """Restrict console login to users in admin or sudo group.""" + try: + os.mkdir(os.path.dirname(ACCESS_CONF_FILE)) + except FileExistsError: + pass + + with open(ACCESS_CONF_FILE, 'w', encoding='utf-8') as conffile: + conffile.write(ACCESS_CONF_SNIPPET + '\n') + + +@privileged +def disable_restricted_access(): + """Don't restrict console login to users in admin or sudo group.""" + with open(ACCESS_CONF_FILE_OLD, 'r', encoding='utf-8') as conffile: + lines = conffile.readlines() + + with open(ACCESS_CONF_FILE_OLD, 'w', encoding='utf-8') as conffile: + for line in lines: + if line.strip() not in ACCESS_CONF_SNIPPETS: + conffile.write(line) + + try: + os.remove(ACCESS_CONF_FILE) + except OSError: + pass + + +def get_restricted_access_enabled(): + """Return whether restricted access is enabled.""" + with open(ACCESS_CONF_FILE_OLD, 'r', encoding='utf-8') as conffile: + if any(line.strip() in ACCESS_CONF_SNIPPETS + for line in conffile.readlines()): + return True + + try: + with open(ACCESS_CONF_FILE, 'r', encoding='utf-8') as conffile: + return any(line.strip() in ACCESS_CONF_SNIPPETS + for line in conffile.readlines()) + except FileNotFoundError: + return False diff --git a/plinth/modules/security/views.py b/plinth/modules/security/views.py index 0831698d2..8d35c60f1 100644 --- a/plinth/modules/security/views.py +++ b/plinth/modules/security/views.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -""" -Views for security module -""" +"""Views for security module.""" from django.contrib import messages from django.template.response import TemplateResponse @@ -12,6 +10,7 @@ from plinth.modules import security from plinth.modules.upgrades import is_backports_requested from plinth.views import AppView +from . import privileged from .forms import SecurityForm @@ -42,15 +41,15 @@ class SecurityAppView(AppView): def get_status(request): - """Return the current status""" + """Return the current status.""" return { - 'restricted_access': security.get_restricted_access_enabled(), + 'restricted_access': privileged.get_restricted_access_enabled(), 'fail2ban_enabled': action_utils.service_is_enabled('fail2ban') } def _apply_changes(request, old_status, new_status): - """Apply the form changes""" + """Apply the form changes.""" if old_status['restricted_access'] != new_status['restricted_access']: try: security.set_restricted_access(new_status['restricted_access']) @@ -70,7 +69,7 @@ def _apply_changes(request, old_status, new_status): def report(request): - """Serve the security report page""" + """Serve the security report page.""" apps_report = security.get_apps_report() return TemplateResponse( request, 'security_report.html', {