From a8a5da996e4176e84bbe51839811bd71aef4a22b Mon Sep 17 00:00:00 2001 From: nsaikiran Date: Fri, 14 Jul 2017 18:32:19 +0530 Subject: [PATCH] [#759] Add fail2ban to Plinth UI Run sudo-required action via actions.superuser_run Actions related to services those require `sudo` permissions need to be executed via actions.superuser_run. NOTE: If plinth service is started via `sudo ./run --debug` (in dev mode) all actions will be executed silently. But plinth in user machines won't be executed with sudo permissions. --- actions/service | 6 ++++++ plinth/action_utils.py | 5 +++++ plinth/modules/security/__init__.py | 16 +++++++++++++++- plinth/modules/security/forms.py | 6 ++++++ plinth/modules/security/views.py | 11 ++++++++++- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/actions/service b/actions/service index 6decc050d..0205af9b0 100755 --- a/actions/service +++ b/actions/service @@ -36,6 +36,7 @@ def add_service_action(subparsers, action, help): parser = subparsers.add_parser(action, help=help) parser.add_argument('service', help='name of the service') + def parse_arguments(): """Return parsed command line arguments as dictionary.""" parser = argparse.ArgumentParser() @@ -48,6 +49,7 @@ def parse_arguments(): add_service_action(subparsers, 'reload', 'reload a service') add_service_action(subparsers, 'is-running', 'status of a service') add_service_action(subparsers, 'is-enabled', 'status a service') + add_service_action(subparsers, 'unmask', 'unmask a service') subparsers.add_parser('list', help='List of running system services') @@ -75,6 +77,10 @@ def subcommand_reload(arguments): action_utils.service_reload(arguments.service) +def subcommand_unmask(arguments): + action_utils.service_unmask(arguments.service) + + def subcommand_is_enabled(arguments): print(action_utils.service_is_enabled(arguments.service)) diff --git a/plinth/action_utils.py b/plinth/action_utils.py index c43e96f86..019366b96 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -88,6 +88,11 @@ def service_disable(service_name): subprocess.call(['systemctl', 'disable', service_name]) +def service_unmask(service_name): + """Unmask a service""" + subprocess.call(['systemctl', 'unmask', service_name]) + + def service_start(service_name): """Start a service with systemd or sysvinit.""" if is_systemd_running(): diff --git a/plinth/modules/security/__init__.py b/plinth/modules/security/__init__.py index fd6f84eaa..420f1b6e0 100644 --- a/plinth/modules/security/__init__.py +++ b/plinth/modules/security/__init__.py @@ -25,12 +25,15 @@ from plinth import actions from plinth.menu import main_menu -version = 1 +version = 2 is_essential = True title = _('Security') +managed_packages = ['fail2ban'] + +managed_services = ['fail2ban'] ACCESS_CONF_FILE = '/etc/security/access.conf' ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx (admin) (sudo):ALL' @@ -42,6 +45,17 @@ def init(): menu.add_urlname(title, 'glyphicon-lock', 'security:index') +def setup(helper, old_version=None): + """Install the required packages""" + helper.install(managed_packages) + setup_fail2ban() + + +def setup_fail2ban(): + 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, 'r') as conffile: diff --git a/plinth/modules/security/forms.py b/plinth/modules/security/forms.py index 58a6105e1..343b4aff4 100644 --- a/plinth/modules/security/forms.py +++ b/plinth/modules/security/forms.py @@ -31,3 +31,9 @@ class SecurityForm(forms.Form): 'group will be able to log in to console or via SSH. ' 'Console users may be able to access some services ' 'without further authorization.')) + fail2ban_enabled = forms.BooleanField( + label=_('Fail2ban (recommended)'), required=False, + help_text=_('When this option is enabled, fail2ban will limit brute force ' + 'break-in attempts to the SSH server and other password protected ' + 'internet-services which are enabled.' + )) diff --git a/plinth/modules/security/views.py b/plinth/modules/security/views.py index 8deb3792f..150ebb6bb 100644 --- a/plinth/modules/security/views.py +++ b/plinth/modules/security/views.py @@ -25,6 +25,8 @@ from django.utils.translation import ugettext as _ from .forms import SecurityForm from plinth.modules import security +from plinth import actions +from plinth import action_utils def index(request): @@ -49,7 +51,8 @@ def index(request): def get_status(request): """Return the current status""" - return {'restricted_access': security.get_restricted_access_enabled()} + return {'restricted_access': security.get_restricted_access_enabled(), + 'fail2ban_enabled': action_utils.service_is_enabled('fail2ban')} def _apply_changes(request, old_status, new_status): @@ -64,3 +67,9 @@ def _apply_changes(request, old_status, new_status): .format(exception=exception)) else: messages.success(request, _('Updated security configuration')) + + if old_status['fail2ban_enabled'] != new_status['fail2ban_enabled']: + if new_status['fail2ban_enabled']: + actions.superuser_run('service',['enable','fail2ban']) + else: + actions.superuser_run('service',['disable','fail2ban'])