Launch a new EC2 instance and SSH into it in seconds — all from a tablet!

No PC required! The past two days I have been forced to see how productive I can be with no PC and only an android tablet. My Asus Zenbook, died yesterday morning, within warranty, luckily, and is currently on its way to the service center. My normal web-app development environment consists of a CentOS linux server running in a VirtualBox VM on my local machine. Vim is my editor of choice, so all I need is a couple terminal windows into that VM and I’m happy.
I bought a cheap bluetooth keyboard for my Vaio Tablet Z and set out to re-create a development environment with only the android tablet (and the cloud)! A quick app search showed there were plenty of SSH clients available for android. The biggest hurdle I anticipated was key file juggling with the private keys AWS instances launch with for login. This turned out to be a snap, though!
I was frankly shocked that in about 4 minutes I had launched an EC2 instance and connected a fairly decent terminal editor to it. Here is how I went about it.
1. Opened the AWS Management Console and logged in. Selected EC2 and clicked Launch Instance. Selected RHEL 7.0, m3.medium instance type and clicked Launch.
2. Chose Create a New Key Pair from the drop down. Gave it a name and saved the .pem file to the sd card on my tablet.
3. Clicked Launch Instances and watched the instance start up. Copied the public IP address of the running instance.
(2 minutes in…)
4. Opened Google Play and searched for SSH under apps. Read a couple reviews and downloaded JuiceSSH.
5. Opened JuiceSSH and here is where I really started to get impressed. Select Connections, gave it a name, pasted the IP address and clicked New under Identity.
6. Entered the username of “ec2-user” that all base EC2 instances use for connection. Under Private Key, selected Set and pointed it to the .pem file I downloaded in step 2.
7. Clicked connect and I was shocked that it all worked on the first try. I was staring at a full screen terminal window, logged in as ec2-user and ready to go!
My initial experiences with Amazon’s private key authentication mechanisms using Putty on a PC years ago were far more problematic.
I proceeded to switch to root (“sudo su -“) and started to rebuild my server in the cloud! My next project will be to automate all of the configuration to go from a blank linux image to a running application environment – using Puppet. That will be for another post!

Hashed Keys in Django Models with Class Based Views

Obfuscate your models’ primary keys to discourage url manipulation attempts.  For example, say you had an application to store an retrieve books and your url pattern to view a book with primary key 3 was this: www.mysite.com/books/3/.  A clever user may abuse this url scheme to view other books by replacing the primary key in the url and avoid your application interface to view any other books.  The approach below will also help deter someone from writing a script to automate the download of  all of the books using a predictable, incrementing primary key.

The approach is overriding the slug property that is normally attached to a model, so if you are using slugs for something else, you will need to modify this. I am using basehash for a reversible hashing method.

myproject/base/models.py:

alphabet = 'QtpdO9AqKEGBzVoby7RiwT.2YSZxm1F0cDh-WMeLI6k_UgXJj8usHal534PfNCvnr'

def hash_key(pk):
    # slug will be a hashed representation of the pk for use in URLs.
    # Using a hash method that is reversible at any hash length
    # look for bad words and change key length if needed

    BAD_WORDS = ['nose','toe'] make you own array of bad words to avoid in your hashes.
    slug = ''

    hasher = basehash.base(alphabet)

    slug_len = 5
    if (pk > 16777215):
        slug_len = 6
    elif (pk > 1073741823):
        slug_len = 7

    slug = hasher.hash(pk, slug_len)
    while (re.search('|'.join(BAD_WORDS), slug, re.IGNORECASE) and slug_len < 9):
        slug_len += 1
        slug = hasher.hash(pk, slug_len)

    return slug

def unhash_key(hash):
    hasher = basehash.base(alphabet)

    return hasher.unhash(hash)

class BaseModel(models.Model):
    ...

    def get_url_key(self):
        return hash_key(self.pk)
    slug = property(get_url_key)

    @classmethod
    def get_pk(cls,hash):
        return unhash_key(hash)

Next, subclass BaseModel wherever you want to use an obfuscated pk slug…

Create a Mixin for your views(s). This get_object method is modified from the SingleObjectMixin in the django source. Removed the original comments for brevity.

class HashedPkMixin(object):
    def get_object(self, queryset=None):

        if queryset is None:
            queryset = self.get_queryset()

        pk = self.kwargs.get(self.pk_url_kwarg, None)
        slug = self.kwargs.get(self.slug_url_kwarg, None)

        if pk is not None:
            queryset = queryset.filter(pk=pk)
        elif slug is not None:
            ## These are the only 2 lines replaced in the get_object method.
            upk = self.model.get_pk(slug)
            queryset = queryset.filter(pk=upk)
        else:
            raise AttributeError("Generic detail view %s must be called with "
                                 "either an object pk or a slug."
                                 % self.__class__.__name__)

        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()
        except:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj

Finally, add your mixin to the single object generic views and use them to base your views off of.
myproject/base/generic_views.py:

class HashedDetailView (HashedPkMixin, DetailView):
    pass

class HashedUpdateView (HashedPkMixin, UpdateView):
    pass

While this method is NOT cryptographically secure, the approach could be adapted if that was a requirement.  The reason I chose this type of hash over a more secure one was purely for hash length.  I wanted my urls to still have a somewhat short primary key value. One could also modify the hash_key and unhash_key methods to use django signing instead, but that increases your url length and reveals your primary keys. The latter giving your user additional information about the size of your data set and the order added for any given item.