Web2py – GNUPGP [GPG]

In this brief documentation for myself, I go over the process of sending GPG Encrypted emails in web2py. At the server level, there is a few things you need to do to install gnupgp and generate a public/private key with the email used for sending with web2py.

First you will need a few things via apt

sudo apt-get install gnupg rng-tools
sudo rngd -r /dev/urandom

Then proceeding to create the public/private key pair.

su www-data 
export GNUPGHOME="/home/www-data/.gnupg"
gpg --gen-key

You can now generate a public key for your users to import for decrypting your email messages.

gpg --output desiredFileName.gpg --export emailusedwithweb2py@thisSite.com

I had done this as root initially and it didn’t work properly. I think the www-data user did not have the necessary permissions because the export variable was left unset. In order to be able to use this I changed to the web2py server user su www-data and set my environment variable properly

export GNUPGHOME="/home/www-data/.gnupg"

As a side note be sure that the user/group managing this gpg setup is the www-data user or whatever user is managing your web2py server.

chown -R www-data:www-data /home/www-data/.gnupg

However there is a few things necessary still and that is. In order for you to be able to send a message to someone with this functionality from your website, you need to have there public key in your key ring. As the www-data user you can see the keys in your key ring like this:

gpg --list-keys

So the question is how do I get this to happen? I chose to have my users generate and upload their own public key file. Then in web2py I ran an os command to have my users public key file entered in to my key chain.

I did this by creating an onvalidation function that verifies that the files mimetype is set to a valid type for me to want to run an os.system call with. (This is accomplished by using the python magic library). Then on success of validation executing the gpg –import command from an os.system() call which imports my users public key in to my keychain.

def verify_upload_gpg(form):
    tmppath = os.path.join(request.folder, 'uploads', 'tmpupf%f.gpg' % random.random())
    filename = form.vars.upload_gpg.file
    filepath = open(tmppath, 'wb')
    shutil.copyfileobj(filename, filepath)
    filepath.close()
    in_filename = str(tmppath)
    validate_type = magic.from_file(in_filename, mime=True)
    if validate_type != 'application/x-gnupg-keyring':
       form.errors.upload_gpg = T("Not a valid mimetype")
    else:
        os.system("gpg --import %s" % (in_filename))


def upload_gpg(form):
    form = SQLFORM(db.gpg_messages, formstyle='bootstrap')
    if form.process(onvalidation=verify_upload_gpg).accepted:
       session.flash = "Succesfully Installed Public GPG Email Key"
       return dict(form=form)
    elif form.errors:
       response.flash = 'Please correct the highlighted fields'
       return dict(form=form)
    else:
       return dict(form=form)

Then just to be sure in your server.

gpg --list-keys

In my case I wanted to send a verification email prior so I ran the gpg settings in my controller for the emails I wished to send that are encrypted.

    if form.accepts(request.vars, session):
        mail.settings.gpg_home = '/home/www-data/.gnupg'
        mail.settings.cipher_type = 'gpg'
        mail.settings.sign = True
        mail.settings.sign_passphrase = GPG_PASSPHRASE
        mail.settings.encrypt = True
        mail.send(to=EMAIL, subject='Access for file %s' % FILENAME, message='The key to decrypt this file %s is provided here: %s. We have also attached a python script to make the decryption for this file go that much easier.' % (FILENAME, form.vars.key), attachments = mail.Attachment(os.path.join(request.folder, 'static/emails/', 'script.py')))
        if mail.error != None:
            session.flash = "Errors: %s" % (mail.error)
        else:
            session.flash = "An email with the AES Encryption Key has been sent to %s " % (EMAIL)
        return redirect(URL('default', 'index'))
    elif form.errors:
        response.flash = 'Please correct the highlighted fields'
        return dict(form=form)
    else:
        return dict(form=form)

And that is my setup with GNUPGP and web2py.