mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-06-03 10:50:20 +00:00
monkeysphere: Proper domain handling
Action: - Don't use const for HOST_TOOL, it is unlikely to be ever changed. - Don't pass multiple key ids as single string to monkeysphere-host. - Use JSON for data transfer with action instead of custom format and parsing. - Minor styling fixes. Template: - More consistent indentation. - Improve the description. - Add headers to the table. - List domains instead of domain types. URLs: - Take domain as argument for key generation. - Narrow down fingerprint matching regex. Views: - Take domain as argument for key generation. Verify that domain is valid. - Minor grammer fix to cancel message. - Use JSON format for getting key status. - List domains instead of domain types.
This commit is contained in:
parent
70d85cbd6f
commit
6afe350fe5
@ -21,42 +21,39 @@ Configuration helper for monkeysphere.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
HOST_TOOL = 'monkeysphere-host'
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Return parsed command line arguments as dictionary."""
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
host_show_key = subparsers.add_parser('host-show-key',
|
||||
help='Show host key fingerprint')
|
||||
host_show_key.add_argument('keyid', nargs='*',
|
||||
help='Optional list of KEYIDs')
|
||||
host_show_keys = subparsers.add_parser(
|
||||
'host-show-keys', help='Show host key fingerprints')
|
||||
host_show_keys.add_argument(
|
||||
'key_ids', nargs='*', help='Optional list of KEYIDs')
|
||||
|
||||
host_import_ssh_key = subparsers.add_parser('host-import-ssh-key',
|
||||
help='Import host SSH key')
|
||||
host_import_ssh_key.add_argument('hostname',
|
||||
help='Fully-qualified hostname')
|
||||
host_import_ssh_key = subparsers.add_parser(
|
||||
'host-import-ssh-key', help='Import host SSH key')
|
||||
host_import_ssh_key.add_argument(
|
||||
'hostname', help='Fully-qualified hostname')
|
||||
|
||||
host_publish_key = subparsers.add_parser(
|
||||
'host-publish-key',
|
||||
help='Push host key to keyserver')
|
||||
'host-publish-key', help='Push host key to keyserver')
|
||||
host_publish_key.add_argument(
|
||||
'keyid', nargs='*',
|
||||
help='Optional list of KEYIDs')
|
||||
'key_ids', nargs='*', help='Optional list of KEYIDs')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_host_show_key(arguments):
|
||||
"""Show host key fingerprint."""
|
||||
keyid = ' '.join(arguments.keyid)
|
||||
def subcommand_host_show_keys(arguments):
|
||||
"""Show host key fingerprints."""
|
||||
try:
|
||||
output = subprocess.check_output([HOST_TOOL, 'show-key', keyid])
|
||||
output = subprocess.check_output(
|
||||
['monkeysphere-host', 'show-keys'] + arguments.key_ids)
|
||||
except subprocess.CalledProcessError:
|
||||
# no keys available
|
||||
return
|
||||
@ -75,32 +72,29 @@ def subcommand_host_show_key(arguments):
|
||||
keys[-1]['pgp_fingerprint'] = line.lstrip('Open PGP fingerprint:')
|
||||
elif line.startswith('ssh fingerprint:'):
|
||||
data = line.lstrip('ssh fingerprint:').split()
|
||||
keys[-1]['ssh_keysize'] = data[0]
|
||||
keys[-1]['ssh_key_size'] = data[0]
|
||||
keys[-1]['ssh_fingerprint'] = data[1]
|
||||
keys[-1]['ssh_keytype'] = data[2].strip('()')
|
||||
keys[-1]['ssh_key_type'] = data[2].strip('()')
|
||||
elif line == '':
|
||||
keys.append(dict())
|
||||
|
||||
for key in keys:
|
||||
print(key['pub'], key['date'], key['uid'], key['pgp_fingerprint'],
|
||||
key['ssh_keysize'], key['ssh_fingerprint'], key['ssh_keytype'])
|
||||
print(json.dumps({'keys': keys}))
|
||||
|
||||
|
||||
def subcommand_host_import_ssh_key(arguments):
|
||||
"""Import host SSH key."""
|
||||
output = subprocess.check_output(
|
||||
[HOST_TOOL, 'import-key',
|
||||
['monkeysphere-host', 'import-key',
|
||||
'/etc/ssh/ssh_host_rsa_key', arguments.hostname])
|
||||
print(output.decode())
|
||||
|
||||
|
||||
def subcommand_host_publish_key(arguments):
|
||||
"""Push host key to keyserver."""
|
||||
keyid = ' '.join(arguments.keyid)
|
||||
# setting TMPDIR as workaround for Debian bug #656750
|
||||
proc = subprocess.Popen(
|
||||
[HOST_TOOL, 'publish-keys', keyid],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False,
|
||||
['monkeysphere-host', 'publish-keys'] + arguments.key_ids,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
env=dict(
|
||||
os.environ,
|
||||
TMPDIR='/var/lib/monkeysphere/authentication/tmp/',
|
||||
|
||||
@ -32,77 +32,81 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Monkeysphere" %}</h2>
|
||||
<h2>{% trans "Monkeysphere" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed with box_name=cfg.box_name %}
|
||||
With Monkeysphere, a PGP key can be generated for each domain
|
||||
serving SSH on this {{ box_name }}. The PGP public key can then be
|
||||
uploaded to the PGP keyservers. Users connecting to this {{ box_name }}
|
||||
through SSH can verify that they are connecting to the correct
|
||||
host. See the
|
||||
<a href="http://web.monkeysphere.info/getting-started-ssh/">
|
||||
Monkeysphere SSH documentation</a> for more details.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if running %}
|
||||
<p class="running-status-parent">
|
||||
<span class="running-status active"></span>
|
||||
{% trans "Publishing key to keyserver..." %}
|
||||
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:cancel' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<button type="submit" class="btn btn-warning btn-sm">
|
||||
{% trans "Cancel" %}</button>
|
||||
</form>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
With Monkeysphere, a PGP key can be generated for each configured domain
|
||||
serving SSH. The PGP public key can then be uploaded to the PGP
|
||||
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 PGP key signing process. See the
|
||||
<a href="http://web.monkeysphere.info/getting-started-ssh/">
|
||||
Monkeysphere SSH documentation</a> for more details.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<table class="table table-bordered table-condensed table-striped">
|
||||
{% for name_service in status.name_services %}
|
||||
<tr>
|
||||
<td>
|
||||
<b>{{ name_service.type }}</b><br>
|
||||
<i>{{ name_service.name }}</i>
|
||||
</td>
|
||||
<td>
|
||||
{% if name_service.key %}
|
||||
{{ name_service.key.pgp_fingerprint }}
|
||||
{% else %}
|
||||
{% trans "Not Available" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if name_service.available %}
|
||||
{% if not name_service.key %}
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:generate' name_service.short_type %}">
|
||||
{% csrf_token %}
|
||||
{% if running %}
|
||||
<p class="running-status-parent">
|
||||
<span class="running-status active"></span>
|
||||
{% trans "Publishing key to keyserver..." %}
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-sm pull-right">
|
||||
{% trans "Generate PGP Key" %}</button>
|
||||
</form>
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:cancel' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
{% elif not running %}
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:publish' name_service.key.pgp_fingerprint %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-warning btn-sm">
|
||||
{% trans "Cancel" %}</button>
|
||||
</form>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" class="btn btn-warning btn-sm pull-right">
|
||||
{% trans "Publish Key" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<table class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Domain" %}</th>
|
||||
<th>{% trans "GPG Fingerprint" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in status.domains %}
|
||||
<tr>
|
||||
<td>{{ domain.name }}</td>
|
||||
<td>
|
||||
{% if domain.key %}
|
||||
{{ domain.key.pgp_fingerprint }}
|
||||
{% else %}
|
||||
{% trans "Not Available" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if not domain.key %}
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:generate' domain.name %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-sm pull-right">
|
||||
{% trans "Generate PGP Key" %}</button>
|
||||
</form>
|
||||
{% elif not running %}
|
||||
<form class="form" method="post"
|
||||
action="{% url 'monkeysphere:publish' domain.key.pgp_fingerprint %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<button type="submit" class="btn btn-warning btn-sm pull-right">
|
||||
{% trans "Publish Key" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -26,9 +26,9 @@ from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/monkeysphere/$', views.index, name='index'),
|
||||
url(r'^sys/monkeysphere/(?P<service>[\w]+)/generate/$',
|
||||
url(r'^sys/monkeysphere/(?P<domain>[^/]+)/generate/$',
|
||||
views.generate, name='generate'),
|
||||
url(r'^sys/monkeysphere/(?P<fingerprint>[\w]+)/publish/$',
|
||||
url(r'^sys/monkeysphere/(?P<fingerprint>[0-9A-Fa-f]+)/publish/$',
|
||||
views.publish, name='publish'),
|
||||
url(r'^sys/monkeysphere/cancel/$', views.cancel, name='cancel'),
|
||||
]
|
||||
|
||||
@ -25,6 +25,7 @@ from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_POST
|
||||
import json
|
||||
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
@ -46,19 +47,17 @@ def index(request):
|
||||
|
||||
|
||||
@require_POST
|
||||
def generate(request, service):
|
||||
def generate(request, domain):
|
||||
"""Generate PGP key for SSH service."""
|
||||
for domain_type in sorted(names.get_domain_types()):
|
||||
if domain_type == service:
|
||||
domain = names.get_domain(domain_type)
|
||||
|
||||
try:
|
||||
actions.superuser_run(
|
||||
'monkeysphere',
|
||||
['host-import-ssh-key', 'ssh://' + domain])
|
||||
messages.success(request, _('Generated PGP key'))
|
||||
except actions.ActionError as exception:
|
||||
messages.error(request, str(exception))
|
||||
valid_domain = any((domain in domains
|
||||
for domains in names.domains.values()))
|
||||
if valid_domain:
|
||||
try:
|
||||
actions.superuser_run(
|
||||
'monkeysphere', ['host-import-ssh-key', 'ssh://' + domain])
|
||||
messages.success(request, _('Generated PGP key.'))
|
||||
except actions.ActionError as exception:
|
||||
messages.error(request, str(exception))
|
||||
|
||||
return redirect(reverse_lazy('monkeysphere:index'))
|
||||
|
||||
@ -81,47 +80,28 @@ def cancel(request):
|
||||
if publish_process:
|
||||
publish_process.terminate()
|
||||
publish_process = None
|
||||
messages.info(request, _('Cancelled publish key.'))
|
||||
messages.info(request, _('Cancelled key publishing.'))
|
||||
|
||||
return redirect(reverse_lazy('monkeysphere:index'))
|
||||
|
||||
|
||||
def get_status():
|
||||
"""Get the current status."""
|
||||
output = actions.superuser_run('monkeysphere', ['host-show-key'])
|
||||
keys = []
|
||||
for line in output.split('\n'):
|
||||
data = line.strip().split()
|
||||
if data and len(data) == 7:
|
||||
keys.append(dict())
|
||||
keys[-1]['pub'] = data[0]
|
||||
keys[-1]['date'] = data[1]
|
||||
keys[-1]['uid'] = data[2]
|
||||
keys[-1]['name'] = data[2].replace('ssh://', '')
|
||||
keys[-1]['pgp_fingerprint'] = data[3]
|
||||
keys[-1]['ssh_keysize'] = data[4]
|
||||
keys[-1]['ssh_fingerprint'] = data[5]
|
||||
keys[-1]['ssh_keytype'] = data[6]
|
||||
output = actions.superuser_run('monkeysphere', ['host-show-keys'])
|
||||
keys = {}
|
||||
for key in json.loads(output)['keys']:
|
||||
key['name'] = key['uid'].replace('ssh://', '')
|
||||
keys[key['name']] = key
|
||||
|
||||
name_services = []
|
||||
for domain_type in sorted(names.get_domain_types()):
|
||||
domain = names.get_domain(domain_type)
|
||||
name_services.append({
|
||||
'type': names.get_description(domain_type),
|
||||
'short_type': domain_type,
|
||||
'name': domain or _('Not Available'),
|
||||
'available': bool(domain),
|
||||
'key': None,
|
||||
})
|
||||
domains = []
|
||||
for domains_of_a_type in names.domains.values():
|
||||
for domain in domains_of_a_type:
|
||||
domains.append({
|
||||
'name': domain,
|
||||
'key': keys.get(domain),
|
||||
})
|
||||
|
||||
# match up keys with name services
|
||||
for key in keys:
|
||||
for name_service in name_services:
|
||||
if key['name'] == name_service['name']:
|
||||
name_service['key'] = key
|
||||
continue
|
||||
|
||||
return {'name_services': name_services}
|
||||
return {'domains': domains}
|
||||
|
||||
|
||||
def _collect_publish_result(request):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user