certbot-py 0.10.1
Python module to integrate automated Let's Encrypt `certbot certonly` certificate creation into Python projects.
License: MIT
Keywords: certbot letsencrypt ssl certificate https secure encrypt encryption

certbot\_py Documentation
-------------------------

Current Status: Very Alpha, Proof of Concept
''''''''''''''''''''''''''''''''''''''''''''

Use at your own peril, until there's some shakedown.

Overview
^^^^^^^^

This library is a wrapper around the ``certbot``/``certbot-auto``
command line tool operating ``certonly`` in manual, non-interactive
mode. It does this via Python's ``subprocess.Popen()``.

This allows Python developers to automate the creation of certificates
in a web app (ie. Flask or Django). Let's Encrypt's certificate
generation process, simplified:

1. ``certbot`` sends a certificate request for a domain (or multiple
   domains in the case of SAN/UCC)
2. Let's Encrypt responds with validation instructions for each
   requested domain
3. For each set of validation instructions, << developer/operations team
   member needs to manually perform some action - DNS record addition or
   web server file creation >>
4. Let's Encrypt attempts to validate each domain
5. Upon successful validation, a certificate is provided (either single
   domain or SAN/UCC)

Step 3 is where this library allows the developer to customize and
**automate** their process of setting up validation (see ``auth_script``
below). This library currently only supports web server validation (ie
http-01).

An example scenario where this library becomes useful: > You have a
blogging platform (ie. blog-platform.io) where your customers can sign
up and create their own blog (ie. customer1.blog-platform.io). > >The
Customer would like to host their new blog on their own sub-domain (ie.
blog.customer-site.com) for SEO purposes - obviously they want to make
sure it's HTTPS enabled. They can easily do this by adding a CNAME from
blog.customer-site.com to customer1.blog-platform.io. > > So your
blogging platform needs to automatically provision HTTPS certificates as
your customers create new blogs and set up CNAMEs for them.
``certbot_py`` to the rescue.

Please note that there is **NO** mechanism for renewal included; `using
``certbot renew`` via a cron job is the recommended
way <https://certbot.eff.org/docs/using.html#renewing-certificates>`__.

Installation
^^^^^^^^^^^^

1. You must install ``certbot`` or ``certbot-auto`` as you will need to
   specify the full path to it. It does all the heavy lifting.
2. The user running your Python project code must have access to run
   ``sudo certbot`` or ``sudo certbot-auto`` without a password, which
   is largely dependent on how you configure your ``gunicorn``,
   ``uwsgi``, etc to run (if in a web environment).

   -  This likely means running ``sudo visudo`` or adding an entry to
      ``/etc/sudoers.d/``.
   -  **For security, it is highly recommended to only allow ``sudo``
      access to just the one command (``certbot`` or
      ``certbot-auto``).**

3. Register an account with Let's Encrypt's servers (if you haven't
   already). Note that ``certbot_py`` (this library) defaults to using
   Let's Encrypt staging servers, while ``certbot`` and ``certbot-auto``
   default to production servers. An example of registration for staging
   servers:
   ``certbot register --staging # OR    certbot-auto register --staging``
4. In your Python project's virtual environment, install ``certbot_py``:
   ``pip install certbot_py``

Usage
^^^^^

Ensure you register an account with Let's Encrypt, as mentioned above.

There is a single ``generate_certificate`` method which requires 3
parameters: ``domains``, ``certbot_command``, and ``auth_script``. There
are many other optional parameters which mostly map to corresponding
``certbot`` arguments.

::

    from certbot_py import client

    command = '/my/path/to/certbot'
    script = '/my/other/path/to/auth-hook-script.sh'
    my_domains = ['example1.com', 'example2.com', 'example3.com']
    results = client.generate_certificate(
        domains=my_domains,
        certbot_command=command,
        auth_script=script
    )

If you wanted to generate a SAN (ie. UCC) certificate instead, use
``san_ucc=True``. As with ``certbot``, the first domain in ``domains``
will be the common name listed on the resulting cert.

::

    results = client.generate_certificate(
        domains=my_domains,
        certbot_command=command,
        auth_script=script,
        san_ucc=True
    )

There are many more options, most of the pertinent ones are listed
below. Skip further down for more information on ``auth_script``.

Option List
'''''''''''

Full list of ``generate_certificate`` parameters (order is unimportant
as they must be passed as keyword arguments):

::

    account = None
    allow_domain_subset = False
    allow_self_upgrade = False
    auth_script = None
    certbot_command = None
    domains = None
    hsts = False
    must_staple = False
    production = False
    redirect = False
    rsa_key_size = None
    san_ucc = False
    staple_ocsp = False
    uir = False

These mostly map to `corresponding ``certbot``
arguments <https://certbot.eff.org/docs/using.html#certbot-command-line-options>`__,
with a few exceptions:

-  ``production`` will enable the live generation of certificates from
   Let's Encrypt's production servers. By default (and safely),
   ``certbot_py`` uses staging servers.
-  ``san_ucc`` indicates that a SAN/UCC certificate is wanted, otherwise
   an individual cert will be requested for each domain passed in.
-  ``certbot_command`` is the full path the the installed ``certbot`` or
   ``certbot-auto`` command line executable.
-  ``auth_script`` is the full path to a script which will use the
   ``certbot``-provided ``$CERTBOT_DOMAIN``, ``$CERTBOT_VALIDATION``,
   and ``$CERTBOT_TOKEN`` environment variables to perform some
   developer-specific action (ie. add ``$CERTBOT_VALIDATION`` and
   ``$CERTBOT_TOKEN`` to a database) so that the subsequent validation
   request from Let's Encrypt's servers can succeed.
-  ``allow_self_upgrade`` would allow auto-upgrading (``certbot-auto``
   only), which has been disabled by default to prevent breakage due to
   tool upgrades

Example ``auth_script`` (Django example), just a single bash script:

::

    #~/bin/bash
    /home/webuser/.virtualenvs/bin/python /home/webuser/my_project/manage.py set_domain_validation "$CERTBOT_DOMAIN" "$CERTBOT_VALIDATION" "$CERTBOT_TOKEN"

Command Line
''''''''''''

There is a command line alias configured upon ``pip install`` that you
can use to test with. Simply use ``certbot_py`` on the command line,
full help is available.

Notes
^^^^^

1. ``certbot`` version 0.10.0 is the first version to expose the
   necessary command line arguments - prior versions will fail.
2. This library should be updated for security and bug fixes (obviously)
   but also may require updating if the underlying arguments to
   ``certbot`` change or features are added.

Future
^^^^^^

Gee, I should mock in some tests...

Longer term, I look forward to having this library change (and improve!)
so that it no longer needs Python's ``subprocess.Popen()`` or a
``certbot`` installation. This is technically possible using Let's
Encrypt's ```acme``
library <https://github.com/certbot/certbot/tree/master/acme>`__;
however creating a client around ``acme`` involves much more than
something simple like ``acme.generate_certificate(...)``. Much in this
ACME/Let's Encrypt world seems in flux at the moment, so implementing
this wrapper felt like the easiest path forward for the time being - and
retains full compatibility with the standard Let's Encrypt command line
tools.

Feedback is encouraged and appreciated. `File issues on
Github <https://github.com/jaddison/certbot_py/issues>`__. Feel free to
fork and suggest improvements.