Metadata-Version: 2.1
Name: autocert
Version: 0.0.6
Summary: Automatic TLS cert issuance and renewal for Python web apps
Home-page: https://github.com/theandrew168/autocert
Author: Andrew Dailey
License: UNKNOWN
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](https://letsencrypt.org/) using the [ACME](https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment) protocol.
        It is based on the API and design of the [Go](https://golang.org/) package with the same name: [autocert](https://pkg.go.dev/golang.org/x/crypto/acme/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](https://www.freedesktop.org/software/systemd/man/systemd.socket.html).
        
        ## 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](https://en.wikipedia.org/wiki/HTTPS) but I don't want to worry about the extra infrastructure that comes with setting up a reverse proxy ([NGINX](http://nginx.org/), [Caddy](https://caddyserver.com/), etc).
        This library allows you to add an auto-renewing TLS certificate from [Let's Encrypt](https://letsencrypt.org/) 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](https://en.wikipedia.org/wiki/Reverse_proxy) is a hard requirement for achieving a secure, fast, and reliable deployment.
        However, I really like the simplicity of hosting a [Flask](https://flask.palletsprojects.com/en/1.1.x/) or [Django](https://www.djangoproject.com/) app by combining a pure-Python [WSGI server](https://www.python.org/dev/peps/pep-3333/) (such as [Waitress](https://docs.pylonsproject.org/projects/waitress/en/stable/)) 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](https://www.digitalocean.com/) droplet.
        
        ## Install
        If you are unfamiliar with [virtual environments](https://docs.python.org/3/library/venv.html), I suggest taking a brief moment to learn about them and set one up.
        The Python docs provide a great [tutorial](https://docs.python.org/3/tutorial/venv.html) for getting started with virtual environments and packages.
        
        Autocert can be installed via pip:
        ```
        pip install autocert
        ```
        
        ## Examples
        These examples utilize the [Waitress](https://docs.pylonsproject.org/projects/waitress/en/stable/) 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](https://github.com/theandrew168/autocert/tree/main/examples).
        
        Waitress can be installed via pip:
        ```
        pip install waitress
        ```
        
        ### basic
        Here is a basic example of autocert usage (requires root):
        ```python
        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:
        ```python
        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/
        ```
        
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: System :: Networking
Requires-Python: >=3.0
Description-Content-Type: text/markdown
