MACRO: Check certificate end date v0.1

Check certificate end date Macro (v11.0.4)

I'm not sure what rules are here for putting own macros, but maybe my small solution will be usable for someone.

The idea is to check and display validity dates for certificates from set of hosts (included in macro). I maintain a few small VPS and use LetsEncrypt to generate certificates. Most of them use mechanism like cron to refresh certificates but sometime that automation fails. So I thought that it is good occasion to learn more about KM and build some small tool.

Certificate is taken directly from the service (using openssl s_client) and analysed by openssl x509 . Because date of validity is formatted in very specific way, I have to analyse it and convert for further processing. The certificates could be also checked on other services (f.ex.mail's protocols: IMAP or POP), that's why specifying port is also possible.

The result is displayed in window in form:

The character '*' prefix the line where number of days to the end is less than 90 (defined by variable local_WarnPeriod),

Im very fresh adept of KM so please be understanding.

The trigger is not defined, but in my environment it its connected to the cron.

Check certificate end date.kmmacros (16 KB)

2 Likes

Thanks for this. I just recently have been mucking around with certs on a website I've been managing. This will help.

1 Like

Nicely done.

You can save yourself some text munging by getting the enddate directly, using dateopt to format it in ISO 8601 format. For example:

echo | openssl s_client -showcerts -connect wiki.keyboardmaestro.com:443 2>/dev/null | openssl x509  -inform pem -noout -enddate -dateopt iso_8601

...returns:

notAfter=2025-10-02 23:16:09Z

Edit to add
It seems dateopt is only available for some versions of openssl x509 -- it's in OpenSSL 3.3.1 on my Air but not LibreSSL 3.3.6 on my iMac.

Unfortunately I have no idea which is the default macOS-installed one (I'll keep digging), so tread carefully!

1 Like

Nice trick, thanks.
openssl has so much options :slight_smile:

If you haven't already, take a look at x509's -checkend <seconds> option, which exits nonzero if the cert expires within the next <seconds>.

That's the one I use most -- I don't usually care when a cert will expire, just if it will expire within the week so needs renewing now.

OK - good point.

My solution partially was taken from my daily job where we manage plenty of certificates in different environments and with different task like planning controlled change (mostly renew), delegate to dedicated person, etc., so information like in your proposal is not enough.
Anyway thanks.

BTW. What is the practice in this section to update macro to newer version?

2 Likes

And so does date, which can save you even more parsing -- and, given the compatibility problem I found with x509's dateopt flag (see the edit at the bottom of the above post), we can easily work on the unmodified certificate date string.

If, in the shell script, we use date to convert the certificate's end date to "seconds since epoch" then the days remaining is a simple KM calculation:

FLOOR((Local_certEndEpochSeconds - NOW()) / 86400)

One potential gotcha is that the shell script's date conversion uses localised abbreviations for months. I don't know if the certificate's month abbreviation is similarly localised -- if it is always "English" and your system language is not some kind of English you may have to force the locale. If the shell script throws an error then try adding the line

LC_ALL=en_US.UTF-8

...immediately after the shebang. (Apologies -- being firmly Anglo-Saxon I don't have the linguistic chops to test properly!)

We're also (ab)using the fact that the "Filter" action's "sort" option will parse numbers in a text string as numbers rather than characters and sort accordingly, so "7 b" comes before "11 a" -- the prefixed "*" doesn't change that behaviour because that is even earlier in the sort order so bubbles to the top anyway.

Your original macro didn't work for me, failing on one of the regexs, so I couldn't check the output formatting. I've cheated and gone with a tab-delimited output -- also handy if you want to paste the results into a spreadsheet or web form.

Check certificate end date (alternative version).kmmacros (10.2 KB)

Image

I'm not saying that this version is in any way better than yours. But I was noodling around between staff IT inductions and (IMO) it shows a couple of neat tricks you might find useful. I'm also certain it could easily be improved :wink:

1 Like

If the shell script throws an error then try adding the line

LC_ALL=en_US.UTF-8

...immediately after the shebang. (Apologies -- being firmly Anglo-Saxon I don't have the linguistic chops to test properly!)

It will not work this way if the date is external (spawned/forked) program. You must use:

export LC_ALL=en_US.UTF-8

or (what is preffered sometime by me) use env vars as prefix to running command (one time shot)

LC_ALL=en_US.UTF-8 date ...

There is also problem with compatibility (heh!) of date between MacOS (in real FreeBSD, but also OpenBSD, NetBSD) and Linux - the last don't have -j parameter.
BTW I work with 5 different Unix versions (Linux, 3 different BSDs, MacOS and Mainframe USS), and frequently it is annoying how "a bit incompatible" can be that all.
I know this $(command) interpretation and use it (sometimes on more than one level), but in final I use AWK as natural tool :slight_smile: - so ... habits from old days (all in plays of cut).

I have polish regional settings: LANG=pl_PL.UTF-8 what means comma in place of dot, different names of months and different format of date, but it not changed format of default notAfter, so it seems be safer to use default format (not iso with -dateopt)

We're also (ab)using the fact that the "Filter" action's "sort" option will parse numbers in a text string as numbers rather than characters and sort accordingly, so "7 b" comes before "11 a" -- the prefixed "*" doesn't change that behaviour because that is even earlier in the sort order so bubbles to the top anyway.

Program sort by default use character, not numeric, interpretation. That is why I always use format yyyy-mm-dd if I want to sort something based on date - it will be always sorted in valid order. Of course I can always change definition of sorted fields and field's separators as additional arguments of sort and change the interpretation as numeric (parameter -n of sort). Of course problem with string interoperation will arise in EBCDIC (mainframe environment).

Your original macro didn't work for me, failing on one of the regexs, so I couldn't check the output formatting. I've cheated and gone with a tab-delimited output -- also handy if you want to paste the results into a spreadsheet or web form.

Interesting what could be wrong in regex based output generated in previous step? Did you see any message or just empty result? Could you run my original macro with display output before the final regex parsing?

Yes -- but this is a KM macro, so we can be fairly sure it's available...

And -- again -- we know we're running this on a Mac, where this numeric interpretation is built in. You see this sort order all the time in Finder -- we may as well make use of it here!

If I was parsing this data to create dates to be used elsewhere then yes, I'd also convert them to (or create them as) yyyy-MM-dd. But all we want is the difference between two date-and-times so I'm going to take the lazy route :wink:

It's actually your shell script failing, with openssl unable to load the certificate

blah,blah,blah...Expecting: TRUSTED CERTIFICATE

...so no date string extracted, so the regex fails.

Maybe a chain-of-trust issue? I can fix it by adding the -connect option:

cat /dev/null | openssl s_client -connect $KMVAR_...

Ooops
I didn't know that MacOS has sort with numeric option by default - it is totally strange and incompatible with other Unix systems! So how can I get alphanumeric sort ? I could find valid flag for sort

In classical Unix you have (even in FreeBSD):

beastie% echo "-23\n+23" | sort
+23
-23
beastie% echo "-23\n+23" | sort -n
-23
+23
beastie%

Horrible :frowning:

MacOS is certified as UNIX 03 complaint. If your favourite version of Unix doesn't work the same, it's not Unix. At least this is what I read when I google your comment.

Ha ha ha :slight_smile:

So my VPS machines with:

  • FreeBSD
  • OpenBSD
  • Linux Debian
    and accounts on
  • NetBSD
  • Ilumos (SolarisOS)
    are not Unix compliant :smiley:

... or ...
maybe some settings in system ... just thinking

I guess that's the problem with standards... there are so many to choose from.

FreeBSD, and OpenBSD, which you mention, are "Unix-like". Neither has been certified. In fact, the sort command is one of the commands that they have modified which makes them non-compliant with Unix 03.

I found the answer ... in the standard of course :smiley:
See here:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html

Section DESCRIPTION:

Comparisons shall be based on one or more sort keys extracted from each line of input (or, if no sort keys are specified, the entire line up to, but not including, the terminating ), and shall be performed using the collating sequence of the current locale. If this collating sequence does not have a total ordering of all characters (see XBD LC_COLLATE), any lines of input that collate equally should be further compared byte-by-byte using the collating sequence for the POSIX locale.

If I set:

export  LANG=C.UTF-8

the sort is in order of ASCII (UTF below 128) and sort without -n gives sequence:

echo "+23\n-23" | sort
+23
-23

but if I change to f.ex.

export  LANG=en_US.UTF-8

sort returns:

echo "+23\n-23" | sort
-23
+23

The system API as used by Finder not only uses the Unicode Collation Algorithm but it also tries to treat numbers that are in a string of text as "proper" numbers when you sort. It's why you see this in Finder:

image

...rather than the ASCIIbetical

iMac-1701:Desktop nigel$ ls -1 -d Folder*
Folder 1
Folder 10
Folder 9

The sort binary doesn't have that option, but you can sometimes spoof it using the "version sort" option:

iMac-1701:Desktop nigel$ ls -1 -d Folder* | sort -V
Folder 1
Folder 9
Folder 10

But if you want to sort just numbers, I'm with you -- sort -n is probably the way to go. But in a KM "Execute a Shell Script" action I think the default is LANG=en_US.UTF-8, so compare what happens in your terminal app to what happens in KM.

As for this:

...ignore it, because I seem to be talking total rubbish. I swear I've seen this behaviour, but now I go to reproduce it in a demo I can't. Bad choice of test inputs and/or total stupidity on my part. Sorry for that -- and now I must go and change some macros...

So my macro works -- but only for the test data :frowning: Changes need to be made to the way the sorting is done. An easy fix would be to not add the * until later, first using sort -V to sort the list, then treating it as a pseudo array with \n separators, iterating over each line to add either *%Tab% or just %Tab% at the start:

Check certificate end date (alternative v2).kmmacros (11.0 KB)

Image

The added complications (and reduced speed) of which is making your awk routine look more and more attractive :wink:

But it does let us see another neat KM trick in action -- not treating text as a \n-delimited pseudo array and iterating through the elements:

...but also in-place replacement of the text in an element:

image

But in my version * is added after sort, so now I'm a bit confused.
I do exactly what you write, iterate and add * only if time is less than 90 days.
Why did you move putting * in first stage ?

Nów I understand your comment about *.

The idea to prefix the record by * was to mark records requiring special attention, nothing else.

My first (failed) attempt was putting it in first, the second version puts it in after the sort like yours does -- it just does it a different way.