diff --git a/actions/cockpit b/actions/cockpit index 3abc8770f..e3ff78d41 100755 --- a/actions/cockpit +++ b/actions/cockpit @@ -6,8 +6,11 @@ Configuration helper for Cockpit. import argparse +import augeas + from plinth import action_utils -from plinth.modules.cockpit import utils + +CONFIG_FILE = '/etc/cockpit/cockpit.conf' def parse_arguments(): @@ -15,65 +18,31 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparser = subparsers.add_parser('setup', - help='Setup Cockpit configuration') - subparser.add_argument('domain_names', nargs='*', - help='Domain names to be allowed') - subparser = subparsers.add_parser( - 'add-domain', - help='Allow a new domain to be origin for Cockpit\'s WebSocket') - subparser.add_argument('domain_name', help='Domain name to be allowed') - subparser = subparsers.add_parser( - 'remove-domain', - help='Disallow a new domain from being origin for Cockpit\'s ' - 'WebSocket') - subparser.add_argument('domain_name', help='Domain name to be removed') + subparsers.add_parser('setup', help='Setup Cockpit configuration') subparsers.required = True return parser.parse_args() +def _load_augeas(): + """Initialize Augeas.""" + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.set('/augeas/load/inifile/lens', 'Puppet.lns') + aug.set('/augeas/load/inifile/incl[last() + 1]', CONFIG_FILE) + aug.load() + return aug + + def subcommand_setup(arguments): """Setup Cockpit configuration.""" - aug = utils.load_augeas() - origins = [ - utils.get_origin_from_domain(domain) - for domain in arguments.domain_names - ] - origins += ['https://localhost', 'https://localhost:4430'] - _set_origin_domains(aug, origins) - aug.set('/files' + utils.CONFIG_FILE + '/WebService/UrlRoot', '/_cockpit/') + aug = _load_augeas() + aug.set('/files' + CONFIG_FILE + '/WebService/UrlRoot', '/_cockpit/') + aug.remove('/files' + CONFIG_FILE + '/WebService/Origins') aug.save() - action_utils.service_restart('cockpit.socket') - - -def _set_origin_domains(aug, origins): - """Set the list of allowed origin domains.""" - aug.set('/files' + utils.CONFIG_FILE + '/WebService/Origins', - ' '.join(origins)) - - -def subcommand_add_domain(arguments): - """Allow a new domain to be origin for Cockpit's WebSocket.""" - aug = utils.load_augeas() - origins = utils.get_origin_domains(aug) - origins.add(utils.get_origin_from_domain(arguments.domain_name)) - _set_origin_domains(aug, origins) - aug.save() - - -def subcommand_remove_domain(arguments): - """Disallow a domain from being origin for Cockpit's WebSocket.""" - aug = utils.load_augeas() - origins = utils.get_origin_domains(aug) - try: - origins.remove(utils.get_origin_from_domain(arguments.domain_name)) - except KeyError: - pass - else: - _set_origin_domains(aug, origins) - aug.save() + # Accommodate changes in Apache configuration file from v1 to v2. + action_utils.service_reload('apache2') def main(): diff --git a/plinth/modules/cockpit/__init__.py b/plinth/modules/cockpit/__init__.py index 5e7abb6b8..f565a8a8c 100644 --- a/plinth/modules/cockpit/__init__.py +++ b/plinth/modules/cockpit/__init__.py @@ -10,15 +10,13 @@ from plinth import actions from plinth import app as app_module from plinth import cfg, frontpage, menu from plinth.daemon import Daemon -from plinth.modules import names from plinth.modules.apache.components import Webserver from plinth.modules.backups.components import BackupRestore from plinth.modules.firewall.components import Firewall from plinth.package import Packages -from plinth.signals import domain_added, domain_removed from plinth.utils import format_lazy -from . import manifest, utils +from . import manifest _description = [ format_lazy( @@ -36,10 +34,6 @@ _description = [ _('It can be accessed by any user on ' '{box_name} belonging to the admin group.'), box_name=_(cfg.box_name), users_url=reverse_lazy('users:index')), - format_lazy( - _('Cockpit requires that you access it through a domain name. ' - 'It will not work when accessed using an IP address as part' - ' of the URL.')), ] app = None @@ -50,9 +44,7 @@ class CockpitApp(app_module.App): app_id = 'cockpit' - _version = 1 - - DAEMON = 'cockpit.socket' + _version = 2 def __init__(self): """Create components for the app.""" @@ -91,43 +83,17 @@ class CockpitApp(app_module.App): urls=['https://{host}/_cockpit/']) self.add(webserver) - daemon = Daemon('daemon-cockpit', self.DAEMON) + daemon = Daemon('daemon-cockpit', 'cockpit.socket') self.add(daemon) backup_restore = BackupRestore('backup-restore-cockpit', **manifest.backup) self.add(backup_restore) - @staticmethod - def post_init(): - """Perform post initialization operations.""" - domain_added.connect(on_domain_added) - domain_removed.connect(on_domain_removed) - def setup(helper, old_version=None): """Install and configure the module.""" app.setup(old_version) - domains = names.components.DomainName.list_names('https') - helper.call('post', actions.superuser_run, 'cockpit', - ['setup'] + list(domains)) - helper.call('post', app.enable) - - -def on_domain_added(sender, domain_type, name='', description='', - services=None, **kwargs): - """Handle addition of a new domain.""" - if name and not app.needs_setup(): - if name not in utils.get_domains(): - actions.superuser_run('cockpit', ['add-domain', name]) - actions.superuser_run('service', - ['try-restart', CockpitApp.DAEMON]) - - -def on_domain_removed(sender, domain_type, name='', **kwargs): - """Handle removal of a domain.""" - if name and not app.needs_setup(): - if name in utils.get_domains(): - actions.superuser_run('cockpit', ['remove-domain', name]) - actions.superuser_run('service', - ['try-restart', CockpitApp.DAEMON]) + helper.call('post', actions.superuser_run, 'cockpit', ['setup']) + if not old_version: + helper.call('post', app.enable) diff --git a/plinth/modules/cockpit/data/etc/apache2/conf-available/cockpit-freedombox.conf b/plinth/modules/cockpit/data/etc/apache2/conf-available/cockpit-freedombox.conf index 495037bbe..db203d231 100644 --- a/plinth/modules/cockpit/data/etc/apache2/conf-available/cockpit-freedombox.conf +++ b/plinth/modules/cockpit/data/etc/apache2/conf-available/cockpit-freedombox.conf @@ -14,9 +14,17 @@ ReWriteCond %{HTTPS} !=on RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L] - ProxyPass http://localhost:9090/_cockpit/ + ProxyPass https://localhost:9090/_cockpit/ + ProxyPreserveHost On - ProxyPass ws://localhost:9090/_cockpit/socket + ProxyPass wss://localhost:9090/_cockpit/socket + ProxyPreserveHost On + + + SSLProxyEngine on + SSLProxyVerify none + SSLProxyCheckPeerName off + diff --git a/plinth/modules/cockpit/templates/cockpit.html b/plinth/modules/cockpit/templates/cockpit.html deleted file mode 100644 index f962db2e9..000000000 --- a/plinth/modules/cockpit/templates/cockpit.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# SPDX-License-Identifier: AGPL-3.0-or-later -{% endcomment %} -{% load bootstrap %} -{% load i18n %} - -{% block status %} - {{ block.super }} - -

{% trans "Access" %}

- -

- {% blocktrans trimmed %} - Cockpit will only work when accessed using the following URLs. - {% endblocktrans %} -

- -
    - {% for url_ in urls %} -
  1. {{ url_ }}
  2. - {% endfor %} -
-{% endblock %} diff --git a/plinth/modules/cockpit/urls.py b/plinth/modules/cockpit/urls.py index 041de1146..0b888270a 100644 --- a/plinth/modules/cockpit/urls.py +++ b/plinth/modules/cockpit/urls.py @@ -5,8 +5,9 @@ URLs for Cockpit module. from django.urls import re_path -from plinth.modules.cockpit.views import CockpitAppView +from plinth.views import AppView urlpatterns = [ - re_path(r'^sys/cockpit/$', CockpitAppView.as_view(), name='index'), + re_path(r'^sys/cockpit/$', AppView.as_view(app_id='cockpit'), + name='index'), ] diff --git a/plinth/modules/cockpit/utils.py b/plinth/modules/cockpit/utils.py deleted file mode 100644 index 385642174..000000000 --- a/plinth/modules/cockpit/utils.py +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Minor utility methods for Cockpit. -""" - -import urllib.parse - -import augeas - -CONFIG_FILE = '/etc/cockpit/cockpit.conf' - - -def load_augeas(): - """Initialize Augeas.""" - aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + - augeas.Augeas.NO_MODL_AUTOLOAD) - aug.set('/augeas/load/inifile/lens', 'Puppet.lns') - aug.set('/augeas/load/inifile/incl[last() + 1]', CONFIG_FILE) - aug.load() - return aug - - -def get_origin_domains(aug): - """Return the list of allowed origin domains.""" - origins = aug.get('/files' + CONFIG_FILE + '/WebService/Origins') - return set(origins.split()) if origins else set() - - -def get_origin_from_domain(domain): - """Return the origin that should be allowed for a domain.""" - return 'https://{domain}'.format(domain=domain) - - -def _get_domain_from_origin(origin): - """Return the domain from an origin URL.""" - return urllib.parse.urlparse(origin).netloc - - -def get_domains(): - """Return the domain name in origin URL.""" - aug = load_augeas() - origins = get_origin_domains(aug) - return [_get_domain_from_origin(origin) for origin in origins] diff --git a/plinth/modules/cockpit/views.py b/plinth/modules/cockpit/views.py deleted file mode 100644 index 231e6c67d..000000000 --- a/plinth/modules/cockpit/views.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Views for the Cockpit module -""" -from plinth.modules.cockpit.utils import get_origin_domains, load_augeas -from plinth.views import AppView - - -class CockpitAppView(AppView): - app_id = 'cockpit' - template_name = 'cockpit.html' - - def get_context_data(self, *args, **kwargs): - context = super().get_context_data(**kwargs) - urls = get_origin_domains(load_augeas()) - context['urls'] = [url for url in urls if 'localhost' not in url] - - return context