diff --git a/actions/cockpit b/actions/cockpit
new file mode 100755
index 000000000..5e2e79015
--- /dev/null
+++ b/actions/cockpit
@@ -0,0 +1,139 @@
+#!/usr/bin/python3
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+"""
+Configuration helper for Cockpit.
+"""
+
+import argparse
+import augeas
+
+from plinth import action_utils
+
+CONFIG_FILE = '/etc/cockpit/cockpit.conf'
+
+
+def parse_arguments():
+ """Return parsed command line arguments as dictionary."""
+ 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')
+ subparsers.add_parser('enable', help='Enable Cockpit')
+ subparsers.add_parser('disable', help='Disable Cockpit')
+ 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.required = True
+ return parser.parse_args()
+
+
+def subcommand_setup(arguments):
+ """Setup Cockpit configuration."""
+ aug = load_augeas()
+ origins = [
+ _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' + CONFIG_FILE + '/WebService/UrlRoot', '/_cockpit/')
+ aug.save()
+
+ with action_utils.WebserverChange() as webserver_change:
+ webserver_change.enable('proxy_wstunnel', kind='module')
+ webserver_change.enable('cockpit-freedombox')
+
+ action_utils.service_restart('cockpit.socket')
+
+
+def subcommand_enable(_):
+ """Enable web configuration and reload."""
+ action_utils.service_enable('cockpit.socket')
+ action_utils.webserver_enable('cockpit-freedombox')
+
+
+def subcommand_disable(_):
+ """Disable web configuration and reload."""
+ action_utils.webserver_disable('cockpit-freedombox')
+ action_utils.service_disable('cockpit.socket')
+
+
+def _get_origin_domains(aug):
+ """Return the list of allowed origin domains."""
+ origins = aug.get('/files' + CONFIG_FILE + '/WebService/Origins')
+ return set(origins.split())
+
+
+def _set_origin_domains(aug, origins):
+ """Set the list of allowed origin domains."""
+ aug.set('/files' + CONFIG_FILE + '/WebService/Origins', ' '.join(origins))
+
+
+def _get_origin_from_domain(domain):
+ """Return the origin that should be allowed for a domain."""
+ return 'https://{domain}'.format(domain=domain)
+
+
+def subcommand_add_domain(arguments):
+ """Allow a new domain to be origin for Cockpit's WebSocket."""
+ aug = load_augeas()
+ origins = _get_origin_domains(aug)
+ origins.add(_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 = load_augeas()
+ origins = _get_origin_domains(aug)
+ origins.remove(_get_origin_from_domain(arguments.domain_name))
+ _set_origin_domains(aug, origins)
+ aug.save()
+
+
+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 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/actions/cockpit.socket b/actions/cockpit.socket
deleted file mode 100755
index 7fa660d6d..000000000
--- a/actions/cockpit.socket
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/python3
-# -*- mode: python -*-
-#
-# This file is part of Plinth.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-
-"""
-Configuration helper for Cockpit.
-"""
-
-import argparse
-import augeas
-import subprocess
-
-from plinth import action_utils
-
-CONFIG_FILE = '/etc/cockpit/cockpit.conf'
-
-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('pre-setup', help='Perform pre-setup operations')
- subparsers.add_parser('setup', help='Setup Cockpit configuration')
- subparsers.add_parser('enable', help='Enable Cockpit site')
- subparsers.add_parser('disable', help='Disable Cockpit site')
-
- subparsers.required = True
- return parser.parse_args()
-
-
-def subcommand_pre_setup(_):
- """Preseed debconf values before packages are installed."""
- """subprocess.check_output(
- ['debconf-set-selections'],
- input=b'cockpit tt-rss/database-type string pgsql')"""
- pass
-
-def subcommand_setup(_):
- """Setup Cockpit configuration."""
-
- open(CONFIG_FILE, 'a').close()
-
- aug = load_augeas()
- SECTION = CONFIG_FILE+'/WebService'
- aug.set('/files'+SECTION, '')
- origins = "https://localhost:9090 https://localhost:4430"
- aug.set('/files'+SECTION+'/Origins', origins)
- aug.save()
-
- action_utils.webserver_enable('proxy_wstunnel', kind='module')
- action_utils.service_restart('cockpit.socket')
- action_utils.webserver_enable('cockpit-plinth')
- action_utils.service_restart('apache2')
-
-def subcommand_enable(_):
- """Enable web configuration and reload."""
- action_utils.service_enable('cockpit.socket')
-# action_utils.webserver_enable('cockpit-plinth')
-
-
-def subcommand_disable(_):
- """Disable web configuration and reload."""
-# action_utils.webserver_disable('cockpit-plinth')
- action_utils.service_disable('cockpit.socket')
-
-def subcommand_add_domain(_):
- aug = load_augeas()
-
-def subcommand_remove_domain(_):
- aug = load_augeas()
-
-def subcommand_change_domain(_):
- aug = load_augeas()
-
-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 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/data/etc/apache2/conf-available/cockpit-freedombox.conf b/data/etc/apache2/conf-available/cockpit-freedombox.conf
new file mode 100644
index 000000000..001c2537f
--- /dev/null
+++ b/data/etc/apache2/conf-available/cockpit-freedombox.conf
@@ -0,0 +1,16 @@
+##
+## On all sites, provide cockpit on the path: /_cockpit/
+##
+## Requires the following Apache modules to be enabled:
+## mod_headers
+## mod_proxy
+## mod_proxy_http
+## mod_proxy_wstunnel
+##
+
+ ProxyPass http://localhost:9090/_cockpit/
+
+
+
+ ProxyPass ws://localhost:9090/_cockpit/socket
+
diff --git a/data/etc/apache2/conf-available/cockpit-plinth.conf b/data/etc/apache2/conf-available/cockpit-plinth.conf
deleted file mode 100644
index 9509a0133..000000000
--- a/data/etc/apache2/conf-available/cockpit-plinth.conf
+++ /dev/null
@@ -1,21 +0,0 @@
-##
-## On all sites, provide cockpit on a defaut path: /cockpit
-##
-## Requires the following Apache modules to be enabled:
-## mod_headers
-## mod_proxy
-## mod_proxy_http
-## mod_proxy_wstunnel
-##
-
-
- proxypass http://localhost:9090/
-
-
-
- proxypass http://localhost:9090/cockpit/
-
-
-
- proxypass ws://localhost:9090/cockpit/socket
-
diff --git a/plinth/modules/cockpit/__init__.py b/plinth/modules/cockpit/__init__.py
index dd44b8760..b1b32896f 100644
--- a/plinth/modules/cockpit/__init__.py
+++ b/plinth/modules/cockpit/__init__.py
@@ -28,27 +28,34 @@ from plinth import cfg
from plinth import frontpage
from plinth import service as service_module
from plinth.menu import main_menu
+from plinth.modules import names
from plinth.signals import domain_added, domain_removed, domainname_change
-
version = 1
managed_services = ['cockpit.socket']
managed_packages = ['cockpit']
-title = _('Dashboard of Servers \n (Cockpit)')
+name = _('Cockpit')
+
+short_description = _('Server Administration')
description = [
- _('Cockpit is an interactive server admin interface. It is easy to use '
- 'and very light weight. Cockpit interacts directly with the operating '
- 'system from a real linux session in a browser'),
-
format_lazy(
- _('When enabled, Cockpit will be available from '
- '/cockpit path on the web server. It can be accessed by '
- 'any user with a {box_name} login.'),
- box_name=_(cfg.box_name))
+ _('Cockpit is a server manager that makes it easy to administer '
+ 'GNU/Linux servers via a web browser. On a {box_name}, controls '
+ 'are available for many advanced functions that are not usually '
+ 'required. A web based terminal for console operations is also '
+ 'available.'), box_name=_(cfg.box_name)),
+ format_lazy(
+ _('When enabled, Cockpit will be available from '
+ '/_cockpit/ path on the web server. It can be accessed by '
+ 'any user with a {box_name} login. '
+ 'Sensitive information and system altering abilities are limited to '
+ 'users belonging to admin group.'),
+ box_name=_(cfg.box_name)),
+ _('Currently only limited functionality is available.'),
]
service = None
@@ -56,30 +63,37 @@ service = None
def init():
"""Intialize the module."""
- menu = main_menu.get('apps')
- menu.add_urlname(title, 'glyphicon-dashboard', 'cockpit:index')
+ menu = main_menu.get('system')
+ menu.add_urlname(name, 'glyphicon-wrench', 'cockpit:index',
+ short_description)
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
service = service_module.Service(
- managed_services[0], title, ports=['http', 'https'],
+ managed_services[0], name, ports=['http', 'https'],
is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled():
add_shortcut()
+ domain_added.connect(on_domain_added)
+ domain_removed.connect(on_domain_removed)
+ domainname_change.connect(on_domainname_change)
+
def setup(helper, old_version=None):
"""Install and configure the module."""
- helper.call('pre', actions.superuser_run, 'cockpit', ['pre-setup'])
helper.install(managed_packages)
- helper.call('post', actions.superuser_run, 'cockpit', ['setup'])
+ domains = [domain
+ for domains_of_a_type in names.domains.values()
+ for domain in domains_of_a_type]
+ helper.call('post', actions.superuser_run, 'cockpit', ['setup'] + domains)
global service
if service is None:
service = service_module.Service(
- managed_services[0], title, ports=['http', 'https'],
+ managed_services[0], name, ports=['http', 'https'],
is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
@@ -87,25 +101,26 @@ def setup(helper, old_version=None):
def add_shortcut():
- frontpage.add_shortcut('cockpit', title, url='/cockpit',
+ """Add a shortcut the frontpage."""
+ frontpage.add_shortcut('cockpit', name, url='/_cockpit/',
login_required=True)
def is_enabled():
"""Return whether the module is enabled."""
- return (action_utils.webserver_is_enabled('cockpit-plinth') and
+ return (action_utils.webserver_is_enabled('cockpit-freedombox') and
action_utils.service_is_running('cockpit.socket'))
def enable():
"""Enable the module."""
- actions.superuser_run('cockpit.socket', ['enable'])
+ actions.superuser_run('cockpit', ['enable'])
add_shortcut()
def disable():
"""Disable the module."""
- actions.superuser_run('cockpit.socket', ['disable'])
+ actions.superuser_run('cockpit', ['disable'])
frontpage.remove_shortcut('cockpit')
@@ -114,20 +129,23 @@ def diagnose():
results = []
results.extend(action_utils.diagnose_url_on_all(
- 'https://{host}/cockpit', check_certificate=False))
+ 'https://{host}/_cockpit/', check_certificate=False))
return results
-def on_domain_added():
- actions.superuser_run('cockpit.socket', ['add_domain'])
-def on_domain_removed():
- actions.superuser_run('cockpit.socket', ['remove_domain'])
+def on_domain_added(sender, domain_type, name, description='', services=None,
+ **kwargs):
+ """Handle addition of a new domain."""
+ actions.superuser_run('cockpit', ['add-domain', name])
+
+
+def on_domain_removed(sender, domain_type, name, **kwargs):
+ """Handle removal of a domain."""
+ actions.superuser_run('cockpit', ['remove-domain', name])
+
def on_domainname_change(sender, old_domainname, new_domainname, **kwargs):
- del sender
- del kwargs
- actions.superuser_run('cockpit.socket',
- ['change_domain',
- '--domainname', new_domainname],
- async=True)
+ """Handle change of a domain."""
+ actions.superuser_run('cockpit', ['remove-domain', old_domainname])
+ actions.superuser_run('cockpit', ['add-domain', new_domainname])
diff --git a/plinth/modules/cockpit/urls.py b/plinth/modules/cockpit/urls.py
index 72a9e8096..34a2b5cc7 100644
--- a/plinth/modules/cockpit/urls.py
+++ b/plinth/modules/cockpit/urls.py
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
-
"""
URLs for Cockpit module.
"""
@@ -24,12 +23,12 @@ from django.conf.urls import url
from plinth.views import ServiceView
from plinth.modules import cockpit
-
urlpatterns = [
- url(r'^apps/cockpit/$', ServiceView.as_view(
- service_id=cockpit.managed_services[0],
- diagnostics_module_name="cockpit",
- description=cockpit.description,
- show_status_block=True
- ), name='index'),
+ url(r'^sys/cockpit/$',
+ ServiceView.as_view(
+ service_id=cockpit.managed_services[0],
+ diagnostics_module_name='cockpit',
+ description=cockpit.description,
+ show_status_block=True),
+ name='index'),
]