diff --git a/actions/monkeysphere b/actions/monkeysphere deleted file mode 100755 index e9c892a7e..000000000 --- a/actions/monkeysphere +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/python3 -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Configuration helper for monkeysphere. -""" - -import argparse -import contextlib -import copy -import json -import os -import re -import shutil -import signal -import subprocess -import tempfile - -import augeas -import psutil - - -def parse_arguments(): - """Return parsed command line arguments as dictionary.""" - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - - host_show_keys = subparsers.add_parser( - 'host-show-keys', help='Show imported/importable keys') - host_show_keys.add_argument('key_id', nargs='?', - help='Optional KEYID to retrieve details for') - - host_import_key = subparsers.add_parser( - 'host-import-key', help='Import a key into monkeysphere') - host_import_key.add_argument('ssh_fingerprint', - help='SSH fingerprint of the key to import') - host_import_key.add_argument('domains', nargs='*', - help='List of available domains') - - host_publish_key = subparsers.add_parser('host-publish-key', - help='Push host key to keyserver') - host_publish_key.add_argument('key_ids', nargs='*', - help='Optional list of KEYIDs') - - host_cancel_publish = subparsers.add_parser( - 'host-cancel-publish', help='Cancel a running publish operation') - host_cancel_publish.add_argument('pid', help='PID of the publish process') - - subparsers.required = True - return parser.parse_args() - - -def get_ssh_keys(fingerprint_hash): - """Return all SSH keys.""" - keys = {} - - key_files = ['/etc/ssh/ssh_host_rsa_key'] - for key_file in key_files: - output = subprocess.check_output( - ['ssh-keygen', '-l', '-E', fingerprint_hash, '-f', key_file]) - fingerprint = output.decode().split()[1] - keys[fingerprint] = { - 'ssh_fingerprint': fingerprint, - 'service': 'ssh', - 'key_file': key_file, - 'available_domains': ['*'] - } - - return keys - - -def get_pem_ssh_fingerprint(pem_file, fingerprint_hash): - """Return the SSH fingerprint of a PEM file.""" - public_key = subprocess.check_output( - ['openssl', 'rsa', '-in', pem_file, '-pubout'], - stderr=subprocess.DEVNULL) - ssh_public_key = subprocess.check_output( - ['ssh-keygen', '-i', '-m', 'PKCS8', '-f', '/dev/stdin'], - input=public_key) - fingerprint = subprocess.check_output( - ['ssh-keygen', '-l', '-E', fingerprint_hash, '-f', '/dev/stdin'], - input=ssh_public_key) - - return fingerprint.decode().split()[1] - - -def get_https_keys(fingerprint_hash): - """Return all HTTPS keys.""" - aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + - augeas.Augeas.NO_MODL_AUTOLOAD) - aug.set('/augeas/load/Httpd/lens', 'Httpd.lns') - aug.set('/augeas/load/Httpd/incl[last() + 1]', - '/etc/apache2/sites-available/*') - aug.set('/augeas/load/Httpd/incl[last() + 1]', - '/etc/apache2/conf-available/*') - aug.load() - - # Read from default-tls.conf and default-ssl.conf - keys = {} - path = '/files/etc/apache2/sites-available//VirtualHost' - for match in aug.match(path): - host = {'available_domains': ['*'], 'service': 'https'} - for directive in aug.match(match + '/directive'): - name = aug.get(directive) - if name == 'ServerName': - host['available_domains'] = [aug.get(directive + '/arg')] - elif name in ('GnuTLSKeyFile', 'SSLCertificateKeyFile'): - host['key_file'] = aug.get(directive + '/arg') - - if 'key_file' in host: - host['ssh_fingerprint'] = get_pem_ssh_fingerprint( - host['key_file'], fingerprint_hash) - keys[host['ssh_fingerprint']] = host - - # Read from FreedomBox configured domains with proper SSL certs. - path = "/files/etc/apache2/sites-available//" \ - "directive[. = 'Use'][arg[1] = 'FreedomBoxTLSSiteMacro']" - key_file = ("/files/etc/apache2//Macro[arg[1] = 'FreedomBoxTLSSiteMacro']" - "//VirtualHost/directive[. = 'GnuTLSKeyFile']/arg") - key_file = aug.get(key_file) - for match in aug.match(path): - domain = aug.get(match + '/arg[2]') - host = { - 'available_domains': [domain], - 'service': 'https', - 'key_file': key_file.replace('$domain', domain) - } - host['ssh_fingerprint'] = get_pem_ssh_fingerprint( - host['key_file'], fingerprint_hash) - keys[host['ssh_fingerprint']] = host - - return keys - - -def get_monkeysphere_keys(key_id=None): - """Return the list of keys imported into monkeysphere.""" - try: - key_ids = [] if not key_id else [key_id] - output = subprocess.check_output(['monkeysphere-host', 'show-keys'] + - key_ids, stderr=subprocess.DEVNULL) - except subprocess.CalledProcessError: - # no keys available - return {} - - # parse output - default_dict = {'imported_domains': [], 'available_domains': []} - keys = [copy.deepcopy(default_dict)] - lines = output.decode().strip().split('\n') - for line in lines: - if line.startswith('pub'): - data = line.lstrip('pub').split() - keys[-1]['pub'] = data[0] - keys[-1]['date'] = data[1] - elif line.startswith('uid'): - uid = line.lstrip('uid').strip() - if uid.startswith('['): - uid = uid[uid.index(']') + 1:].strip() - - keys[-1].setdefault('uids', []).append(uid) - matches = re.match(r'([a-zA-Z]*)://(.*)(:\d*)?', uid) - keys[-1]['service'] = matches.group(1) - keys[-1]['imported_domains'].append(matches.group(2)) - elif line.startswith('OpenPGP fingerprint:'): - keys[-1]['openpgp_fingerprint'] = \ - line.lstrip('OpenPGP fingerprint:') - elif line.startswith('ssh fingerprint:'): - data = line.lstrip('ssh fingerprint:').split() - keys[-1]['ssh_key_size'] = data[0] - keys[-1]['ssh_fingerprint'] = data[1] - keys[-1]['ssh_key_type'] = data[2].strip('()') - elif line == '': - keys.append(copy.deepcopy(default_dict)) - - return {key['ssh_fingerprint']: key for key in keys} - - -def get_merged_keys(key_id=None): - """Return merged list of system and monkeysphere keys.""" - keys = get_monkeysphere_keys(key_id) - - # Monkeysphere used use MD5 for fingerprint hash and recently - # changed to SHA256. In case of SHA256 the string 'SHA256:' is - # being prepended to the fingerprint. Hoping that such a prefix - # will be available in all future changes, extract it from one key - # (assuming all the others will be the same) and use it. - fingerprint_hash = 'SHA256' - if keys: - fingerprint_hash = list(keys.keys())[0].split(':')[0] - - system_keys = list(get_ssh_keys(fingerprint_hash).items()) + \ - list(get_https_keys(fingerprint_hash).items()) - for ssh_fingerprint, key in system_keys: - if key_id and ssh_fingerprint not in keys: - continue - - if ssh_fingerprint in keys: - keys[ssh_fingerprint].update({ - 'available_domains': key['available_domains'], - 'key_file': key['key_file'] - }) - else: - keys[ssh_fingerprint] = key - - return keys - - -@contextlib.contextmanager -def _get_ssh_key_file_for_import(original_key_file, service): - """Return an SSH key file that can be imported into monkeysphere. - - If the key file is in PEM format, the key file can be used as it is. - Otherwise, if the file is in the OpenSSH key format, which is default since - 7.8, then convert it to PEM format. - - """ - if service != 'ssh': - yield original_key_file - return - - first_line = open(original_key_file, 'r').readline() - if '--BEGIN OPENSSH PRIVATE KEY--' not in first_line: - yield original_key_file - return - - with tempfile.TemporaryDirectory() as temp_directory: - key_file = os.path.join(temp_directory, 'ssh_key_file') - shutil.copy2(original_key_file, key_file) - # Convert OpenSSH format to PEM - subprocess.run( - ['ssh-keygen', '-p', '-N', '', '-m', 'PEM', '-f', key_file], - check=True) - yield key_file - - -def subcommand_host_show_keys(arguments): - """Show host key fingerprints.""" - print(json.dumps({'keys': get_merged_keys(arguments.key_id)})) - - -def subcommand_host_import_key(arguments, second_run=False): - """Import host SSH key.""" - keys = get_merged_keys() - if arguments.ssh_fingerprint not in keys: - raise Exception('Unknown SSH fingerprint') - - key = keys[arguments.ssh_fingerprint] - if '*' in key['available_domains']: - key['available_domains'] = arguments.domains - - if 'openpgp_fingerprint' not in key and not second_run: - env = dict(os.environ, MONKEYSPHERE_PROMPT='false') - with _get_ssh_key_file_for_import(key['key_file'], - key['service']) as key_file: - subprocess.check_call([ - 'monkeysphere-host', 'import-key', key_file, - key['service'] + '://' + key['available_domains'][0] - ], env=env) - - subcommand_host_import_key(arguments, second_run=True) - else: - for domain in key['available_domains']: - if domain in key['imported_domains']: - continue - - env = dict(os.environ, MONKEYSPHERE_PROMPT='false') - subprocess.check_call([ - 'monkeysphere-host', 'add-servicename', - key['service'] + '://' + domain, key['openpgp_fingerprint'] - ], env=env) - - -def subcommand_host_publish_key(arguments): - """Push host key to keyserver.""" - # setting TMPDIR as workaround for Debian bug #656750 - proc = subprocess.Popen( - ['monkeysphere-host', 'publish-keys'] + arguments.key_ids, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=dict(os.environ, - TMPDIR='/var/lib/monkeysphere/authentication/tmp/', - MONKEYSPHERE_PROMPT='false')) - output, error = proc.communicate() - output, error = output.decode(), error.decode() - if proc.returncode != 0: - raise Exception(output, error) - - print(output) - - -def subcommand_host_cancel_publish(arguments): - """Kill a running publish process.""" - process = psutil.Process(int(arguments.pid)) - - # Perform tight checks on the process before killing for security. - arguments = process.cmdline() - if not arguments: - # Process already completed - return - - # Remove the sudo prefix if present - while arguments[0] == 'sudo' or arguments[0].startswith('-'): - arguments = arguments[1:] - - if len(arguments) >= 2 and \ - arguments[0].split('/')[-1] == 'monkeysphere' and \ - arguments[1] == 'host-publish-key': - process.send_signal(signal.SIGTERM) - - -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/debian/freedombox.maintscript b/debian/freedombox.maintscript index d9b3a7b5e..943a06034 100644 --- a/debian/freedombox.maintscript +++ b/debian/freedombox.maintscript @@ -13,3 +13,4 @@ rm_conffile /etc/apt/preferences.d/50freedombox3.pref 20.5~ rm_conffile /etc/plinth/plinth.config 20.12~ rm_conffile /etc/plinth/custom-shortcuts.json 20.12~ rm_conffile /etc/plinth/modules-enabled/coquelicot 20.14~ +rm_conffile /etc/plinth/modules-enabled/monkeysphere 21.16~ diff --git a/plinth/modules/help/tests/test_views.py b/plinth/modules/help/tests/test_views.py index 3ab4c53ab..331ab07d9 100644 --- a/plinth/modules/help/tests/test_views.py +++ b/plinth/modules/help/tests/test_views.py @@ -109,17 +109,16 @@ MANUAL_PAGES = ('Apache_userdir', 'APU', 'Backups', 'BananaPro', 'BeagleBone', 'freedombox-manual', 'GettingHelp', 'GitWeb', 'Hardware', 'I2P', 'Ikiwiki', 'Infinoted', 'Introduction', 'JSXC', 'LetsEncrypt', 'Maker', 'MatrixSynapse', 'MediaWiki', - 'Minetest', 'MiniDLNA', 'MLDonkey', 'Monkeysphere', 'Mumble', - 'NameServices', 'Networks', 'OpenVPN', 'OrangePiZero', - 'PageKite', 'pcDuino3', 'Performance', 'PineA64+', - 'PioneerEdition', 'Plinth', 'Power', 'Privoxy', 'Quassel', - 'QuickStart', 'Radicale', 'RaspberryPi2', 'RaspberryPi3B+', - 'RaspberryPi3B', 'RaspberryPi4B', 'ReleaseNotes', 'Rock64', - 'RockPro64', 'Roundcube', 'Samba', 'Searx', 'SecureShell', - 'Security', 'ServiceDiscovery', 'Shadowsocks', 'Sharing', - 'Snapshots', 'Storage', 'Syncthing', 'TinyTinyRSS', 'Tor', - 'Transmission', 'Upgrades', 'USBWiFi', 'Users', 'VirtualBox', - 'WireGuard') + 'Minetest', 'MiniDLNA', 'MLDonkey', 'Mumble', 'NameServices', + 'Networks', 'OpenVPN', 'OrangePiZero', 'PageKite', 'pcDuino3', + 'Performance', 'PineA64+', 'PioneerEdition', 'Plinth', 'Power', + 'Privoxy', 'Quassel', 'QuickStart', 'Radicale', 'RaspberryPi2', + 'RaspberryPi3B+', 'RaspberryPi3B', 'RaspberryPi4B', + 'ReleaseNotes', 'Rock64', 'RockPro64', 'Roundcube', 'Samba', + 'Searx', 'SecureShell', 'Security', 'ServiceDiscovery', + 'Shadowsocks', 'Sharing', 'Snapshots', 'Storage', 'Syncthing', + 'TinyTinyRSS', 'Tor', 'Transmission', 'Upgrades', 'USBWiFi', + 'Users', 'VirtualBox', 'WireGuard') _restricted_reason = ('Needs installed manual. ' 'CI speed-optimized workspace does not provide it.') not_restricted_environment = pytest.mark.skipif(not canary.exists(), diff --git a/plinth/modules/monkeysphere/__init__.py b/plinth/modules/monkeysphere/__init__.py deleted file mode 100644 index c6aea2286..000000000 --- a/plinth/modules/monkeysphere/__init__.py +++ /dev/null @@ -1,74 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -FreedomBox app for monkeysphere. -""" - -from django.utils.translation import gettext_lazy as _ - -from plinth import app as app_module -from plinth import menu -from plinth.modules.backups.components import BackupRestore -from plinth.modules.users.components import UsersAndGroups -from plinth.package import Packages - -from . import manifest - -_description = [ - _('With Monkeysphere, an OpenPGP key can be generated for each configured ' - 'domain serving SSH. The OpenPGP public key can then be uploaded to the ' - 'OpenPGP keyservers. Users connecting to this machine through SSH can ' - 'verify that they are connecting to the correct host. For users to ' - 'trust the key, at least one person (usually the machine owner) must ' - 'sign the key using the regular OpenPGP key signing process. See the ' - ' ' - 'Monkeysphere SSH documentation for more details.'), - _('Monkeysphere can also generate an OpenPGP key for each Secure Web ' - 'Server (HTTPS) certificate installed on this machine. The OpenPGP ' - 'public key can then be uploaded to the OpenPGP keyservers. Users ' - 'accessing the web server through HTTPS can verify that they are ' - 'connecting to the correct host. To validate the certificate, the user ' - 'will need to install some software that is available on the ' - ' Monkeysphere ' - 'website.') -] - -app = None - - -class MonkeysphereApp(app_module.App): - """FreedomBox app for Monkeysphere.""" - - app_id = 'monkeysphere' - - _version = 1 - - def __init__(self): - """Create components for the app.""" - super().__init__() - - info = app_module.Info(app_id=self.app_id, version=self._version, - name=_('Monkeysphere'), icon='fa-certificate', - description=_description, - manual_page='Monkeysphere') - self.add(info) - - menu_item = menu.Menu('menu-monkeysphere', info.name, None, info.icon, - 'monkeysphere:index', parent_url_name='system', - advanced=True) - self.add(menu_item) - - packages = Packages('packages-monkeysphere', ['monkeysphere']) - self.add(packages) - - users_and_groups = UsersAndGroups('users-and-groups-monkeysphere', - reserved_usernames=['monkeysphere']) - self.add(users_and_groups) - - backup_restore = BackupRestore('backup-restore-monkeysphere', - **manifest.backup) - self.add(backup_restore) - - -def setup(helper, old_version=None): - """Install and configure the module.""" - app.setup(old_version) diff --git a/plinth/modules/monkeysphere/data/etc/plinth/modules-enabled/monkeysphere b/plinth/modules/monkeysphere/data/etc/plinth/modules-enabled/monkeysphere deleted file mode 100644 index 2de664397..000000000 --- a/plinth/modules/monkeysphere/data/etc/plinth/modules-enabled/monkeysphere +++ /dev/null @@ -1 +0,0 @@ -plinth.modules.monkeysphere diff --git a/plinth/modules/monkeysphere/manifest.py b/plinth/modules/monkeysphere/manifest.py deleted file mode 100644 index 0f5708de6..000000000 --- a/plinth/modules/monkeysphere/manifest.py +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Application manfiest for monkeysphere. -""" - -backup = { - 'config': { - 'directories': ['/etc/monkeysphere/'] - }, - 'secrets': { - 'directories': ['/var/lib/monkeysphere/'] - } -} diff --git a/plinth/modules/monkeysphere/static/monkeysphere.css b/plinth/modules/monkeysphere/static/monkeysphere.css deleted file mode 100644 index 323f40788..000000000 --- a/plinth/modules/monkeysphere/static/monkeysphere.css +++ /dev/null @@ -1,20 +0,0 @@ -/* -# SPDX-License-Identifier: AGPL-3.0-or-later -*/ - -td li { - list-style: none; - line-height: 2rem; -} - -td ul { - padding: 0; -} - -.form-action.pull-right { - margin-right: 0.625rem; -} - -.form-action button { - width: 7.875rem; -} diff --git a/plinth/modules/monkeysphere/templates/monkeysphere.html b/plinth/modules/monkeysphere/templates/monkeysphere.html deleted file mode 100644 index f74d2f132..000000000 --- a/plinth/modules/monkeysphere/templates/monkeysphere.html +++ /dev/null @@ -1,136 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# SPDX-License-Identifier: AGPL-3.0-or-later -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} -{% load static %} - -{% block page_head %} - -{% endblock %} - -{% block configuration %} - - {% if running %} -
- - {% trans "Publishing key to keyserver..." %} - -
- - {% endif %} - -| {% trans "Service" %} | -{% trans "Domains" %} | -{% trans "OpenPGP Fingerprint" %} | -- |
|---|---|---|---|
| - {% if key.service == 'ssh' %} - {% trans "Secure Shell" %} - {% elif key.service == 'https' %} - {% trans "Web Server" %} - {% else %} - {% trans "Other" %} - {% endif %} - | -
-
|
- - {% if key.openpgp_fingerprint %} - - {{ key.openpgp_fingerprint }} - - {% else %} - {% trans "-" %} - {% endif %} - | -- {% if not key.openpgp_fingerprint %} - - {% else %} - {% if not running %} - - {% endif %} - {% if key.importable_domains %} - - {% endif %} - {% endif %} - | -
| {% trans "OpenPGP Fingerprint" %} | -{{ key.openpgp_fingerprint }} | -
| {% trans "OpenPGP User IDs" %} | -{{ key.uids|join:', ' }} | -
| {% trans "Key Import Date" %} | -{{ key.date }} | -
| {% trans "SSH Key Type" %} | -{{ key.ssh_key_type }} | -
| {% trans "SSH Key Size" %} | -{{ key.ssh_key_size }} | -
| {% trans "SSH Fingerprint" %} | -{{ key.ssh_fingerprint }} | -
| {% trans "Service" %} | -- {% if key.service == 'ssh' %} - {% trans "Secure Shell" %} - {% elif key.service == 'https' %} - {% trans "Web Server" %} - {% else %} - {% trans "Other" %} - {% endif %} - | -
| {% trans "Key File" %} | -{{ key.key_file }} | -
| {% trans "Available Domains" %} | -{{ key.available_domains|join:', ' }} | -
| {% trans "Added Domains" %} | -{{ key.imported_domains|join:', ' }} | -
- {% blocktrans trimmed %} - After this key is published to the keyservers, it can be signed using - GnuPG with the following commands: - {% endblocktrans %} -
-
-# {% trans "Download the key" %}
-gpg --recv-key {{ key.openpgp_fingerprint }}
-
-# {% trans "Sign the key" %}
-gpg --sign-key {{ key.openpgp_fingerprint }}
-
-# {% trans "Send the key back to the keyservers" %}
-gpg --send-key {{ key.openpgp_fingerprint }}
-
-
-{% endblock %}
diff --git a/plinth/modules/monkeysphere/tests/__init__.py b/plinth/modules/monkeysphere/tests/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/plinth/modules/monkeysphere/tests/test_functional.py b/plinth/modules/monkeysphere/tests/test_functional.py
deleted file mode 100644
index bd59df726..000000000
--- a/plinth/modules/monkeysphere/tests/test_functional.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# SPDX-License-Identifier: AGPL-3.0-or-later
-"""
-Functional, browser based tests for monkeysphere app.
-"""
-
-import pytest
-from plinth.tests import functional
-
-pytestmark = [pytest.mark.system, pytest.mark.monkeysphere]
-
-
-@pytest.fixture(scope='module', autouse=True)
-def fixture_background(session_browser):
- """Login and install the app."""
- functional.login(session_browser)
- functional.set_advanced_mode(session_browser, True)
- functional.install(session_browser, 'monkeysphere')
- functional.set_domain_name(session_browser, 'mydomain.example')
- yield
-
-
-def test_import_ssh_keys(session_browser):
- """Test importing SSH keys."""
- _import_key(session_browser, 'ssh', 'mydomain.example')
- _assert_imported_key(session_browser, 'ssh', 'mydomain.example')
-
-
-def test_import_https_keys(session_browser):
- """Test importing HTTPS keys."""
- _import_key(session_browser, 'https', 'mydomain.example')
- _assert_imported_key(session_browser, 'https', 'mydomain.example')
-
-
-def test_publish_ssh_keys(session_browser):
- """Test publishing SSH keys."""
- _import_key(session_browser, 'ssh', 'mydomain.example')
- _publish_key(session_browser, 'ssh', 'mydomain.example')
-
-
-def test_publish_https_keys(session_browser):
- """Test publishing HTTPS keys."""
- _import_key(session_browser, 'https', 'mydomain.example')
- _publish_key(session_browser, 'https', 'mydomain.example')
-
-
-def test_backup_restore(session_browser):
- """Test backup and restore of keys"""
- _import_key(session_browser, 'ssh', 'mydomain.example')
- _import_key(session_browser, 'https', 'mydomain.example')
- functional.backup_create(session_browser, 'monkeysphere',
- 'test_monkeysphere')
-
- functional.backup_restore(session_browser, 'monkeysphere',
- 'test_monkeysphere')
- _assert_imported_key(session_browser, 'ssh', 'mydomain.example')
- _assert_imported_key(session_browser, 'https', 'mydomain.example')
-
-
-def _find_domain(browser, key_type, domain_type, domain):
- """Iterate every domain of a given type which given key type."""
- keys_of_type = browser.find_by_css(
- '.monkeysphere-service-{}'.format(key_type))
- for key_of_type in keys_of_type:
- search_domains = key_of_type.find_by_css(
- '.monkeysphere-{}-domain'.format(domain_type))
- for search_domain in search_domains:
- if search_domain.text == domain:
- return key_of_type, search_domain
-
- raise IndexError('Domain not found')
-
-
-def _import_key(browser, key_type, domain):
- """Import a key of specified type for given domain into monkeysphere."""
- try:
- _assert_imported_key(browser, key_type, domain)
- except IndexError:
- pass
- else:
- return
-
- key, _ = _find_domain(browser, key_type, 'importable', domain)
- with functional.wait_for_page_update(browser):
- key.find_by_css('.button-import').click()
-
-
-def _assert_imported_key(browser, key_type, domain):
- """Assert that a key of specified type for given domain was imported.."""
- functional.nav_to_module(browser, 'monkeysphere')
- return _find_domain(browser, key_type, 'imported', domain)
-
-
-def _publish_key(browser, key_type, domain):
- """Publish a key of specified type for given domain from monkeysphere."""
- functional.nav_to_module(browser, 'monkeysphere')
- key, _ = _find_domain(browser, key_type, 'imported', domain)
- with functional.wait_for_page_update(browser):
- key.find_by_css('.button-publish').click()
-
- functional.wait_for_config_update(browser, 'monkeysphere')
diff --git a/plinth/modules/monkeysphere/urls.py b/plinth/modules/monkeysphere/urls.py
deleted file mode 100644
index 68e1cb215..000000000
--- a/plinth/modules/monkeysphere/urls.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# SPDX-License-Identifier: AGPL-3.0-or-later
-"""
-URLs for the monkeysphere module.
-"""
-
-from django.urls import re_path
-
-from . import views
-
-urlpatterns = [
- re_path(r'^sys/monkeysphere/$', views.index, name='index'),
- re_path(r'^sys/monkeysphere/(?P