From 136c8d1ff0aa79e9ccccaf091da020121c50bed5 Mon Sep 17 00:00:00 2001 From: Frank Sauerburger <frank@sauerburger.com> Date: Fri, 19 Feb 2021 23:51:08 +0100 Subject: [PATCH] Add SSH key list --- keys/settings.py | 3 +- keys/templates/keys/base.html | 17 +++++++++++ keys/urls.py | 1 + ssh/__init__.py | 0 ssh/admin.py | 4 +++ ssh/apps.py | 5 ++++ ssh/migrations/0001_initial.py | 25 ++++++++++++++++ ssh/migrations/0002_auto_20210219_1432.py | 18 ++++++++++++ .../0003_sshpublickey_client_cert.py | 18 ++++++++++++ ssh/migrations/__init__.py | 0 ssh/models.py | 29 +++++++++++++++++++ ssh/templates/ssh/sshpublickey_list.html | 17 +++++++++++ ssh/tests.py | 3 ++ ssh/urls.py | 9 ++++++ ssh/views.py | 14 +++++++++ 15 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 keys/templates/keys/base.html create mode 100644 ssh/__init__.py create mode 100644 ssh/admin.py create mode 100644 ssh/apps.py create mode 100644 ssh/migrations/0001_initial.py create mode 100644 ssh/migrations/0002_auto_20210219_1432.py create mode 100644 ssh/migrations/0003_sshpublickey_client_cert.py create mode 100644 ssh/migrations/__init__.py create mode 100644 ssh/models.py create mode 100644 ssh/templates/ssh/sshpublickey_list.html create mode 100644 ssh/tests.py create mode 100644 ssh/urls.py create mode 100644 ssh/views.py diff --git a/keys/settings.py b/keys/settings.py index e8e8cc5..e2be4f2 100644 --- a/keys/settings.py +++ b/keys/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'owlca', 'pgp', + 'ssh', 'guardian', ] @@ -63,7 +64,7 @@ ROOT_URLCONF = 'keys.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': ["keys/templates"], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/keys/templates/keys/base.html b/keys/templates/keys/base.html new file mode 100644 index 0000000..e96dcda --- /dev/null +++ b/keys/templates/keys/base.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<head> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta charset="utf-8" /> + <title>Sauerburger Keys</title> +</head> +<body> +<nav> +Nav +</nav> +<main> +{% block content %} +{% endblock %} +</main> +</body> +</html> diff --git a/keys/urls.py b/keys/urls.py index dc8a3c4..7acaa72 100644 --- a/keys/urls.py +++ b/keys/urls.py @@ -19,5 +19,6 @@ from django.urls import path, include urlpatterns = [ path('pki/', include('owlca.urls')), path('pgp/', include('pgp.urls')), + path('ssh/', include('ssh.urls')), path('admin/', admin.site.urls), ] diff --git a/ssh/__init__.py b/ssh/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ssh/admin.py b/ssh/admin.py new file mode 100644 index 0000000..0bb99ca --- /dev/null +++ b/ssh/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from . import models + +admin.site.register(models.SSHPublicKey) diff --git a/ssh/apps.py b/ssh/apps.py new file mode 100644 index 0000000..06aa1aa --- /dev/null +++ b/ssh/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SshConfig(AppConfig): + name = 'ssh' diff --git a/ssh/migrations/0001_initial.py b/ssh/migrations/0001_initial.py new file mode 100644 index 0000000..939e57e --- /dev/null +++ b/ssh/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.3 on 2021-02-19 14:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='SSHPublicKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('hostname', models.CharField(max_length=128)), + ('post', models.IntegerField(blank=True, null=True)), + ('keytype', models.CharField(max_length=32)), + ('key', models.TextField()), + ('created', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/ssh/migrations/0002_auto_20210219_1432.py b/ssh/migrations/0002_auto_20210219_1432.py new file mode 100644 index 0000000..005daa3 --- /dev/null +++ b/ssh/migrations/0002_auto_20210219_1432.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.3 on 2021-02-19 14:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ssh', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='sshpublickey', + old_name='post', + new_name='port', + ), + ] diff --git a/ssh/migrations/0003_sshpublickey_client_cert.py b/ssh/migrations/0003_sshpublickey_client_cert.py new file mode 100644 index 0000000..9ac12e2 --- /dev/null +++ b/ssh/migrations/0003_sshpublickey_client_cert.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.3 on 2021-02-19 22:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ssh', '0002_auto_20210219_1432'), + ] + + operations = [ + migrations.AddField( + model_name='sshpublickey', + name='client_cert', + field=models.BooleanField(default=False), + ), + ] diff --git a/ssh/migrations/__init__.py b/ssh/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ssh/models.py b/ssh/models.py new file mode 100644 index 0000000..c5635ce --- /dev/null +++ b/ssh/models.py @@ -0,0 +1,29 @@ +from base64 import b64encode, b64decode +import hashlib +from django.db import models + +class SSHPublicKey(models.Model): + hostname = models.CharField(max_length=128) + port = models.IntegerField(null=True, blank=True) + keytype = models.CharField(max_length=32) + key = models.TextField() + created = models.DateTimeField(auto_now_add=True) + client_cert = models.BooleanField(default=False) + + def server(self): + if self.port: + return f"{self.hostname}:{self.port}" + return self.hostname + + def __str__(self): + return f"{self.hostname} ({self.keytype})" + + def fingerprint_sha256(self): + raw_key = b64decode(self.key) + digest = b64encode(hashlib.sha256(raw_key).digest()) + return digest.decode().replace("=", "") + + def fingerprint_md5(self): + raw_key = b64decode(self.key) + digest = hashlib.md5(raw_key).hexdigest() + return ":".join(a + b for a, b in zip(digest[::2], digest[1::2])) diff --git a/ssh/templates/ssh/sshpublickey_list.html b/ssh/templates/ssh/sshpublickey_list.html new file mode 100644 index 0000000..478fd57 --- /dev/null +++ b/ssh/templates/ssh/sshpublickey_list.html @@ -0,0 +1,17 @@ +{% extends 'keys/base.html' %} + +{% block content %} +{% if sshpublickey_list %} +<ul> +{% for publickey in sshpublickey_list %} + <li>{{ publickey.server }}:<br /> + SHA256: {{ publickey.fingerprint_sha256 }}<br /> + MD5: {{ publickey.fingerprint_md5 }}<br /> + <pre>{{ publickey.keytype }} {{ publickey.key }} {{ publickey.hostname }}</pre> + </li> +{% endfor %} +</ul> +{% else %} +<p>There are not public keys.</p> +{% endif %} +{% endblock %} diff --git a/ssh/tests.py b/ssh/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/ssh/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ssh/urls.py b/ssh/urls.py new file mode 100644 index 0000000..409a872 --- /dev/null +++ b/ssh/urls.py @@ -0,0 +1,9 @@ + +from django.urls import path + +from . import views + +urlpatterns = [ + path("", views.PublicKeyListView.as_view(), name="ssh-list"), + path("<int:pk>", views.PublicKeyDetailView.as_view(), name="ssh-detail"), +] diff --git a/ssh/views.py b/ssh/views.py new file mode 100644 index 0000000..9725af4 --- /dev/null +++ b/ssh/views.py @@ -0,0 +1,14 @@ +from django.shortcuts import render +from django.views.generic import DetailView, ListView, CreateView +from guardian.mixins import PermissionRequiredMixin, PermissionListMixin +from . import models + + +class PublicKeyListView(PermissionListMixin, ListView): + model = models.SSHPublicKey + permission_required = ['view_sshpublickey'] + +class PublicKeyDetailView(PermissionRequiredMixin, DetailView): + model = models.SSHPublicKey + permission_required = ['view_sshpublickey'] + -- GitLab