firewall: Use privileged decorator, drop showing running status

- If a daemon is not-running, we already show an error message to the user. Use
that mechanism instead of the custom one.

Tests:

- Functional tests work.
- Initial setup for firewall on first boot works.
  - Default zone of the firewalld is set to external in /etc/firewalld.conf
- Status of various apps is shown properly in the app page
- If firewalld is not running, the app page is still displayed properly and
  message that firewalld is not running is shown.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2022-08-26 16:37:42 -07:00 committed by James Valleroy
parent 5389303e98
commit a62b7c7522
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
4 changed files with 88 additions and 168 deletions

View File

@ -1,14 +1,11 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """FreedomBox app to configure a firewall."""
FreedomBox app to configure a firewall.
"""
import contextlib import contextlib
import logging import logging
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import cfg, menu from plinth import cfg, menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
@ -16,7 +13,7 @@ from plinth.modules.backups.components import BackupRestore
from plinth.package import Packages, install from plinth.package import Packages, install
from plinth.utils import Version, format_lazy, import_from_gi from plinth.utils import Version, format_lazy, import_from_gi
from . import manifest from . import manifest, privileged
gio = import_from_gi('Gio', '2.0') gio = import_from_gi('Gio', '2.0')
glib = import_from_gi('GLib', '2.0') glib = import_from_gi('GLib', '2.0')
@ -98,7 +95,7 @@ class FirewallApp(app_module.App):
def _run_setup(): def _run_setup():
"""Run firewalld setup.""" """Run firewalld setup."""
_run(['setup'], superuser=True) privileged.setup()
add_service('http', 'external') add_service('http', 'external')
add_service('http', 'internal') add_service('http', 'internal')
add_service('https', 'external') add_service('https', 'external')
@ -171,17 +168,8 @@ def try_with_reload(operation):
operation() operation()
def get_enabled_status():
"""Return whether firewall is enabled"""
output = _run(['get-status'], superuser=True)
if not output:
return False
else:
return output.split()[0] == 'running'
def get_enabled_services(zone): def get_enabled_services(zone):
"""Return the status of various services currently enabled""" """Return the status of various services currently enabled."""
with ignore_dbus_error(dbus_error='ServiceUnknown'): with ignore_dbus_error(dbus_error='ServiceUnknown'):
zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE) zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE)
return zone_proxy.getServices('(s)', zone) return zone_proxy.getServices('(s)', zone)
@ -190,7 +178,7 @@ def get_enabled_services(zone):
def get_port_details(service_port): def get_port_details(service_port):
"""Return the port types and numbers for a service port""" """Return the port types and numbers for a service port."""
try: try:
return _port_details[service_port] return _port_details[service_port]
except KeyError: except KeyError:
@ -215,7 +203,7 @@ def get_interfaces(zone):
def add_service(port, zone): def add_service(port, zone):
"""Enable a service in firewall""" """Enable a service in firewall."""
with ignore_dbus_error(dbus_error='ServiceUnknown'): with ignore_dbus_error(dbus_error='ServiceUnknown'):
zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE) zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE)
with ignore_dbus_error(service_error='ALREADY_ENABLED'): with ignore_dbus_error(service_error='ALREADY_ENABLED'):
@ -229,7 +217,7 @@ def add_service(port, zone):
def remove_service(port, zone): def remove_service(port, zone):
"""Remove a service in firewall""" """Remove a service in firewall."""
with ignore_dbus_error(dbus_error='ServiceUnknown'): with ignore_dbus_error(dbus_error='ServiceUnknown'):
zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE) zone_proxy = _get_dbus_proxy(_FIREWALLD_OBJECT, _ZONE_INTERFACE)
with ignore_dbus_error(service_error='NOT_ENABLED'): with ignore_dbus_error(service_error='NOT_ENABLED'):
@ -240,13 +228,3 @@ def remove_service(port, zone):
config_zone = _get_dbus_proxy(zone_path, _CONFIG_ZONE_INTERFACE) config_zone = _get_dbus_proxy(zone_path, _CONFIG_ZONE_INTERFACE)
with ignore_dbus_error(service_error='NOT_ENABLED'): with ignore_dbus_error(service_error='NOT_ENABLED'):
config_zone.removeService('(s)', port) config_zone.removeService('(s)', port)
def _run(arguments, superuser=False):
"""Run an given command and raise exception if there was an error"""
command = 'firewall'
if superuser:
return actions.superuser_run(command, arguments)
else:
return actions.run(command, arguments)

View File

@ -1,31 +1,12 @@
#!/usr/bin/python3
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Configuration helper for FreedomBox firewall interface."""
Configuration helper for FreedomBox firewall interface.
"""
import argparse
import subprocess import subprocess
import augeas import augeas
from plinth import action_utils from plinth import action_utils
from plinth.actions import privileged
def parse_arguments():
"""Return parsed command line arguments as dictionary"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
# Setup
subparsers.add_parser('setup', help='Perform basic firewall setup')
# Get status
subparsers.add_parser('get-status',
help='Get whether firewalld is running')
subparsers.required = True
return parser.parse_args()
def _flush_iptables_rules(): def _flush_iptables_rules():
@ -81,26 +62,11 @@ def set_firewall_backend(backend):
action_utils.service_restart('firewalld') action_utils.service_restart('firewalld')
def subcommand_setup(_): @privileged
def setup():
"""Perform basic firewalld setup.""" """Perform basic firewalld setup."""
action_utils.service_enable('firewalld') action_utils.service_enable('firewalld')
subprocess.call(['firewall-cmd', '--set-default-zone=external']) subprocess.run(['firewall-cmd', '--set-default-zone=external'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True)
set_firewall_backend('nftables') set_firewall_backend('nftables')
def subcommand_get_status(_):
"""Print status of the firewalld service"""
subprocess.call(['firewall-cmd', '--state'])
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()

View File

@ -15,20 +15,6 @@
<h3>{% trans "Status" %}</h3> <h3>{% trans "Status" %}</h3>
{% if firewall_status == 'not_running' %}
<p>
{% blocktrans trimmed %}
Firewall daemon is not running. Please run it. Firewall comes
enabled by default on {{ box_name }}. On any Debian based
system (such as {{ box_name }}) you may run it using the
command 'service firewalld start' or in case of a system with
systemd 'systemctl start firewalld'.
{% endblocktrans %}
</p>
{% else %}
<div class="table-responsive"> <div class="table-responsive">
<table class='table table-autowidth'> <table class='table table-autowidth'>
<thead> <thead>
@ -106,6 +92,5 @@
by the <a href="/_cockpit/network/firewall">Cockpit</a> app. by the <a href="/_cockpit/network/firewall">Cockpit</a> app.
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% endif %}
{% endblock %} {% endblock %}

View File

@ -1,7 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """FreedomBox app to configure a firewall."""
FreedomBox app to configure a firewall.
"""
from plinth import views from plinth import views
from plinth.modules import firewall from plinth.modules import firewall
@ -11,23 +9,16 @@ from . import components
class FirewallAppView(views.AppView): class FirewallAppView(views.AppView):
"""Serve firewall index page.""" """Serve firewall index page."""
app_id = 'firewall' app_id = 'firewall'
template_name = 'firewall.html' template_name = 'firewall.html'
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
"""Add additional context data for the template.""" """Add additional context data for the template."""
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
status = 'running' if firewall.get_enabled_status() else 'not_running'
context['firewall_status'] = status
if status == 'running':
context['components'] = components.Firewall.list() context['components'] = components.Firewall.list()
internal_enabled_ports = firewall.get_enabled_services( internal_enabled_ports = firewall.get_enabled_services(zone='internal')
zone='internal') external_enabled_ports = firewall.get_enabled_services(zone='external')
external_enabled_ports = firewall.get_enabled_services(
zone='external')
context['internal_enabled_ports'] = internal_enabled_ports context['internal_enabled_ports'] = internal_enabled_ports
context['external_enabled_ports'] = external_enabled_ports context['external_enabled_ports'] = external_enabled_ports
return context return context