Home Assistant SSL Certificate renewal with uPnP

A slight niggle that has been going on with my Home Assistant install for a little while now where SSL certificate renewals were not happening, and I would end up having to renew them manually. Reason I am doing renewals in the first place is because I am using the very capable and free LetsEncrypt certificates to secure my Home Assistant instance.

I am not going to go into details of how you can provision certificates on Home Assistant, as this is covered very well on the HASS website already LINK

What this post is going to elaborate on, is the authentication process for getting / renewing a certificate.

Lets Encrypt have a number of security measures for verifying you are the owner of a system that a certificate is being requested for. In terms of HTTP/HTTPS authentication, it will only test against HTTP port 80, or HTTPS port 443, as these are considered privileged ports in Linux requiring elevated permissions to have an application claim them for listening upon.
I would hope most others are alike myself where I do not like the idea of opening/redirecting either of these ports to my Home Assistant server just for a certificate renewal, and rather it be temporary.

The certbot tool which is used to handle the certificate request/renewal process, can be configured to launch a http web server on a custom port, then you have to redirect either TCP port 80, or 443 (depending on your renewal parameters) to this custom port which certbot is listening on. Usually this step is done at your home router.
Again, configurations of home routers is not something to go into detail here and varies wildly.

I did not want to leave a forward permanently open so Home Assistant could automate it’s renewal request, and my router does not have any capability to automate configuration of port forwarding. So I decided to look at a different approach.

In comes uPnP – a network protocol which allows discovery and management of network devices. A subset of this is the ability for an application to request a port to be forward to it from your Internet router. This is more applicable in home environments than corporate. You likely have applications which do this already. This is where we are going to tag along with those applications and request a port forward temporarily to allow the certificate renewal.

More information about uPnP is available here.
The implementation I have used in the script for certbot is based on Python’s MiniuPnP and code written by Silas S. Brown

My complete Home Assistant configurations are available on GitHub, but extracts are below too.

Configuration is generally the same as the Home Assistant guide here but we will make a few tweaks to step 8 (auto-renew process).

I have created a small script which will forward the port using uPNP, then request the certificate and remove the port once done to handle the renewal process. I have customized the process to use port 8124 temporarily so I do not have to stop/restart Home Assistant to perform the renewal.

So the script looks like this:

#!/bin/bash

~/.homeassistant/bin/certbot_upnp.sh add
~/certbot/certbot-auto renew --no-self-upgrade --standalone --preferred-challenges tls-sni-01 --tls-sni-01-port 8124
~/.homeassistant/bin/certbot_upnp.sh remove

sudo systemctl restart [email protected]

Simply replace the ‘certbot-auto’ line in step 8 of the guide with the path the the above script. Also save the script file below to the same location.

The certbot_upnp.sh script being called above, is an additional Python script to perform the actual port forwarding. I customized it from Silas code to simplify the request, and have hard coded the ports in the script for now. You can edit this and the certbot-auto line should you wish to use a different port:

#!/usr/bin/env python
import miniupnpc
u = miniupnpc.UPnP()
u.discoverdelay=200;u.discover();u.selectigd()

import sys
if len(sys.argv) != 2:
    sys.stderr.write("Syntax: certbot_upnp [add]/[remove]")
    sys.exit(1)

if sys.argv[1] == 'add':
    u.addportmapping(443,'TCP',u.lanaddr,8124,'CertBot_Renew','')

if sys.argv[1] == 'remove':
    u.deleteportmapping(443,'TCP')

NOTE – There are some prerequisites you need to for the uPnP module to work. You will need the python-miniupnp library for the script to work. On Ubuntu, you can install this by:

sudo apt-get install python-miniupnp

I am sure there are much more fluid ways to integrate this, and incorporate in a Home Assistant component, but I am still rather green to Python, but would be interested to see how others would take this and customize into their workflows!

Leave a Reply

Your email address will not be published. Required fields are marked *