Skip to main content

Automatic TLS cert issuance and renewal for Python web apps

Project description

autocert

Automatic TLS cert issuance and renewal for Python web apps

Overview

Autocert is a package for automatically obtaining and renewing TLS certificates from Let's Encrypt using the ACME protocol. It is based on the API and design of the Go package with the same name: autocert. To work its magic, autocert only requires a single TCP socket listening on your desired HTTPS port (usually 443). This can come directly from an application or from a management system such as systemd.

Motivation

I often find myself with the need to host simple Python web apps as demos for friends / clients. In these situations, I want the demo to be properly secured via HTTPS but I don't want to worry about the extra infrastructure that comes with setting up a reverse proxy (NGINX, Caddy, etc). This library allows you to add an auto-renewing TLS certificate from Let's Encrypt to your website with a single line of code.

There is quite a bit of dogma surrounding the hosting of Python-based web applications. Most folks believe that utilizing a reverse proxy is a hard requirement for achieving a secure, fast, and reliable deployment. However, I really like the simplicity of hosting a Flask or Django app by combining a pure-Python WSGI server (such as Waitress) with this project. It might not be the best deployment option for a high-traffic, business-critical applications but it can easily support a few hundred requests per second on a small Digital Ocean droplet.

Install

If you are unfamiliar with virtual environments, I suggest taking a brief moment to learn about them and set one up. The Python docs provide a great tutorial for getting started with virtual environments and packages.

Autocert can be installed via pip:

pip install autocert

Examples

These examples utilize the Waitress WSGI server but you can follow your own preference. However, whichever server you choose must be able to accept pre-listening sockets (you'll see what I mean). More examples can be found in the examples directory.

Waitress can be installed via pip:

pip install waitress

basic

Here is a basic example of autocert usage (requires root):

from autocert import autocert, wsgi
import socket
import waitress

# open a socket on port 443
s443 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s443.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s443.bind(('0.0.0.0', 443))
s443.listen()

# setup automatic cert issuance and renewal
s443_tls = autocert.manage(s443, 'example.org', 'www.example.org', accept_tos=True)

# serve your app with TLS!
waitress.serve(wsgi.hello_world_app, sockets=[s443_tls], url_scheme='https')

systemd w/ redirect

Here is an example of using autocert with systemd sockets and an HTTP->HTTPS redirect:

from autocert import autocert, wsgi
import os
import socket
import threading
import waitress

# make sure systemd PID matches
if os.environ['LISTEN_PID'] != str(os.getpid()):
    raise SystemExit('Mismatched LISTEN_PID')

# expecting 2 ports here: 80, 443
if os.environ['LISTEN_FDS'] != '2':
    raise SystemExit('Expected 2 socket fds for ports 80 and 443')

# create sockets from the fds opened by systemd
s80 = socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM)
s443 = socket.fromfd(4, socket.AF_INET, socket.SOCK_STREAM)

# start autocert's wsgi.redirect_app in a background thread
t = threading.Thread(
    target=waitress.serve,
    args=(wsgi.redirect_app,),
    kwargs={'sockets': [s80]},
    daemon=True,
)
t.start()

# setup automatic cert issuance and renewal
s443_tls = autocert.manage(s443, 'example.org', 'www.example.org', accept_tos=True)

# serve your app with TLS!
waitress.serve(wsgi.hello_world_app, sockets=[s443_tls], url_scheme='https')

Testing

Autocert is tested using pytest. Unit tests can be executed without anything special but the integration tests expect a locally-running ACME server. Running pebble in a container is a great way to accomplish this!

To run the tests, first install pytest:

pip install pytest

Then, unit tests can be ran via:

pytest tests/unit/

The integration tests need that local ACME server so let's start it up and set some necessary vars:

docker run -p 14000:14000 --detach letsencrypt/pebble

Now the integration tests can be ran:

pytest tests/integration/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

autocert-0.0.7.tar.gz (13.3 kB view hashes)

Uploaded Source

Built Distribution

autocert-0.0.7-py3-none-any.whl (13.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page