Repository: deis/deis

Issue:
invalid key material causes 500 errors
I added a key using the `deis keys:add` command and it failed with a 500 error ... but still shows up in the keys:list

```
$ deis keys:list
=== user Keys
paul@home AAAAB3Nz...KsJHQocJP5
Cz-Drone ssh-rsa AAAAB3Nz...AztjOGNY3R
paul@work ssh-rsa AAAAB3Nz...Igu44TEIib
$ deis keys:remove Cz-Drone
Removing Cz-Drone SSH Key... 500 INTERNAL SERVER ERROR
<h1>Server Error (500)</h1>
```

is there any way to force remove it without getting in and mucking with the database ?



Discussion:
If `deis keys:remove` is raising an error like that, then the only way to get rid of the key is manually. Safest is probably to enable the Django web admin and do it there, or you could enter the controller and run `./manage.py dbshell` to get into PostgreSQL.

Do you have any logs from `deisctl journal controller`? That would probably make it clear where things are going wrong at least.

Thanks.   There was a bad character in the key.   I deleted it from the database, fixed it, and it worked fine.

I figured out Deis is adding to the database,  then verifying the keyfile and pushing the key to etcd where the builder can see it.    it should probably do the verify before adding it to the database.

@paulczar Thanks for the report! We want to investigate better error handling for this. You should always be able to delete the key.


Resolving patch:
diff --git a/controller/api/models.py b/controller/api/models.py
--- a/controller/api/models.py
+++ b/controller/api/models.py
@@ -5,6 +5,7 @@
 """
 
 from __future__ import unicode_literals
+import base64
 import etcd
 import importlib
 import logging
@@ -44,6 +45,14 @@ def log_event(app, msg, level=logging.INFO):
     app.log(msg)            # local filesystem
 
 
+def validate_base64(value):
+    """Check that value contains only valid base64 characters."""
+    try:
+        base64.b64decode(value.split()[1])
+    except Exception as e:
+        raise ValidationError(e)
+
+
 def validate_id_is_docker_compatible(value):
     """
     Check that the ID follows docker's image name constraints
@@ -825,7 +834,7 @@ class Key(UuidAuditedModel):
 
     owner = models.ForeignKey(settings.AUTH_USER_MODEL)
     id = models.CharField(max_length=128)
-    public = models.TextField(unique=True)
+    public = models.TextField(unique=True, validators=[validate_base64])
 
     class Meta:
         verbose_name = 'SSH Key'


Test patch:
diff --git a/controller/api/tests/test_key.py b/controller/api/tests/test_key.py
--- a/controller/api/tests/test_key.py
+++ b/controller/api/tests/test_key.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Unit tests for the Deis api app.
 
@@ -31,6 +32,10 @@
     "+MQnrrza0jaQ3QUAQ= autotest@autotesting comment"
 )
 
+BAD_KEY = (
+    "ssh-rsa foo_bar"
+)
+
 
 class KeyTest(TestCase):
 
@@ -63,12 +68,27 @@ def _check_key(self, pubkey):
         response = self.client.delete(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
         self.assertEqual(response.status_code, 204)
 
+    def _check_bad_key(self, pubkey):
+        """
+        Test that a user cannot add invalid SSH public keys
+        """
+        url = '/v1/keys'
+        body = {'id': 'mykey@box.local', 'public': pubkey}
+        response = self.client.post(url, json.dumps(body), content_type='application/json',
+                                    HTTP_AUTHORIZATION='token {}'.format(self.token))
+        self.assertEqual(response.status_code, 400)
+        return response
+
     def test_rsa_key(self):
         self._check_key(RSA_PUBKEY)
 
     def test_ecdsa_key(self):
         self._check_key(ECDSA_PUBKEY)
 
+    def test_bad_key(self):
+        response = self._check_bad_key(BAD_KEY)
+        self.assertEqual(response.data, {'public': ['Incorrect padding']})
+
     def _check_duplicate_key(self, pubkey):
         """
         Test that a user cannot add a duplicate key