Sensor to show expiry date of SSL certificate

Sorry yes I was confusing two threads that I’d recently posted in!
I’m looking for a solution for SSL cert auto-renewal, but was struggling to get the sensor to work to monitor for certificate expiry, to ended up on this thread trying to resolve that part of the problem.
I’m still hunting for a complete solution for SSL automatic renewal, as the methods that I’ve found so far assume that (a) you’re not using Docker, and (b) you are permanently forwarding port 80 to your HA host (which I am unable to do).

Doesn’t the LetsEncrypt addon do the renewals? I used to use an automation to start that addon at midnight every night and it stops running itself. Of you could use the expiry date via cert to trigger it.
I personally use Caddy2 addon which supports DNS domain validation so no port forwarding needed.

LetsEncrypt does have a facility for renewals, but I haven’t managed to get it to work. I have been following these instructions:

I think I have 2 complicating factors:

  1. I am running HA in a Docker container, and have been unable to run the renewal script (certbot) from within the HA environment
  2. I am unable to permanently forward port 80 to my HA host (I run a separate web server on my network) - I believe that the LetsEncrypt certbot requires access via port 80.

If you’re not running HAOS or Supervised you should be able to find a docker container for LE like at linuxserver.io
Also look at Caddy with a DNS validation plugin as I already suggested.

Thanks for that. I’ll see if I can find a docker container for LE. I’m not sure if Caddy is a little more than I need. If I can find a LE docker container that handles auto-renewals would I need Caddy?

It’s totally up to you. I like a reverse proxy for the security it provides.

Does anyone use the isValid attribute? I’m trying to use it as an automation, I have to generate a pks file from the LE cert whenever it updates, but it’s only checking if the cert is valid and not if it’s valid for the URL you are checking on. This should be invalid IMO and consequently means I can’t trigger on isValid when the site shows as invalid SSL cert. If this is expected then I need to find an alternative sensor to determine if the cert is invalid for the site.

Hey everyone, I was trying the solution here, but failed with the sensor. There’ve been some changes to HomeAssistantOS lately.

This works for me:

#############################
########  certbot  ##########
#############################
- platform: template
  sensors:
    days_before_ssl_expiration:
      friendly_name: Days before SSL expiration
      value_template: >-
       {{((as_timestamp(states('sensor.cert_expiry_timestamp_my_domain_name_8123')) - as_timestamp(now())) / 86400) | int }}

After having set up the integration Certificate Expiry, the sensor gives the days till expiry.

And here’s my automation which should work out of the box:

alias: SSL Auto Certbot
description: ''
trigger:
  - platform: state
    entity_id: sensor.days_before_ssl_expiration
    to: '20'
condition: []
action:
  - service: hassio.addon_start
    data:
      addon: core_letsencrypt
  - delay:
      hours: 0
      minutes: 2
      seconds: 0
      milliseconds: 0
  - service: homeassistant.restart
mode: single

hey @CybDis , that sensor works, but i always get this error on restart HA

any idea?

2022-02-13 19:59:18 ERROR (MainThread) [homeassistant.helpers.event] Error while processing template: Template("{{((as_timestamp(states('sensor.cert_expiry_timestamp_XXX_com_8123')) - as_timestamp(now())) / 86400) | int }}")
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 407, in async_render
    render_result = _render_with_context(self.template, compiled, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 1814, in _render_with_context
    return template.render(**kwargs)
  File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 1291, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 925, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 523, in async_render_to_info
    render_info._result = self.async_render(variables, strict=strict, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 409, in async_render
    raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'

I think because cert integration is giving unavaible first upon restart HA, and it cant calculate yet

you’re right, that’s the reason.
There are some discussions/solutions where it’s possible to check for ‘unavailable’ and then not cast it to float, but I was too lazy to implement that and just accept the error.

Here’s a possible fix, if you want to try: Issues with a template after update to 2021-10-0 - #2 by koying

yeah, already fixed :slight_smile:

would you mind sharing?

sure:

  - platform: template
    sensors:
      ssl_expiration:
        friendly_name: Days before SSL expiration
        unit_of_measurement: Days
        value_template: >-
          {{ ( ( states('sensor.cert_expiry_timestamp_xxx_8123') | as_timestamp(0) - now().timestamp() ) / 86400 ) | round(0) }}

I got annoy by cert error on start as some of the service is still down during home assistant startup… thus, here my template checking for validity, otherwise unknown

    hass_cert:
      unit_of_measurement: 'days'
      value_template: >-
          {% if is_state_attr('sensor.cert_expiry_timestamp_xxx', 'is_valid',1) %}
            {{ ((as_timestamp(states("sensor.cert_expiry_timestamp_xxx"),states("sensor.hass_cert")) - as_timestamp(now())  ) / 86400 ) | round(0, "floor") }}
          {% else %}
            {{ states('sensor.hass_cert') }}
          {% endif %}
2 Likes

Playing around Certificate Expiry integration sensor. I found one my expired certificate, configured domain and sensor integration with such expired certificate, and sensor return Unknow value with attributes “is valid: false” and “Error: unable to get local issuer certificate”. What is the expected return value for overdue certificate?
With valid certificate Certificate Expiry sensor returns expected value in my configuration.

Answering to myself: yes, Unknown value with attributes “is valid: false” and “Error: unable to get local issuer certificate” is expected for overdue certificate. Details: Python: how to get expired SSL cert date? - Stack Overflow

Hi All,
I’ve had this sensor installed and working for years without ever having any issues with it. Today I updated my LE Certs and suddenly the sensor reports:
image

I have rebooted HA (2022.11.4) and I have deleted the sensor and recreated it, but to no avail. The setting I use during creation is:

my.domain.com
443

Has anything changed lately? Even though the sensor only updates every 12 hour, I was expecting it to update once immediately upon creation, and also immediately on each reboot of HA before falling dormant for 12 hours?

@tyjtyj Thanks for sharing this.I want to implement this too, but I am struggling as to where (under what section) in configuration.yaml it should be placed.
Any guidance you can provide would be appreciated!

Much has changed since then.
Here is full template

template:
  - sensor:
      - name: hass_cert
        unique_id: hass_cert
        unit_of_measurement: 'days'
        state_class: measurement
        state: '{{ ((as_timestamp(states("sensor.cert_expiry_timestamp_xxxxx_32400"),states("sensor.hass_cert")) - as_timestamp(now())  ) / 86400 ) | round(0, "floor") }}'
        availability: "{{ is_state_attr('sensor.cert_expiry_timestamp_xxxxx_32400', 'is_valid',1) }}"

@tyjtyj Thanks for sharing the updated yaml code. That got it working for me! :smiley: