mod_checkcerts/mod_checkcerts.lua
author Matthew Wild <mwild1@gmail.com>
Sat, 24 Sep 2022 09:26:26 +0100
changeset 5063 5f1120c284c5
parent 4611 c8ccaac78f64
permissions -rw-r--r--
mod_cloud_notify_extensions: Add note about dependency Noting here because people might not click through to see it on the mod_cloud_notify_encrypted page.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2949
ec7f9c8f2a5f mod_checkcerts: Fixed luacheck warnings
Michel Le Bihan <michel@lebihan.pl>
parents: 1884
diff changeset
     1
local config = require "core.configmanager";
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
     2
local ssl = require"ssl";
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
     3
local datetime_parse = require"util.datetime".parse;
1676
d9fcf9d8e787 mod_checkcerts: Fix for removal of globals in LuaSec
Kim Alvefur <zash@zash.se>
parents: 1098
diff changeset
     4
local load_cert = ssl.loadcertificate;
941
a6c2345bcf87 mod_checkcerts: Nag admins about certs that have, or are about to expire. Often.
Kim Alvefur <zash@zash.se>
parents: 855
diff changeset
     5
local st = require"util.stanza"
855
1983d4d51e1a mod_checkcerts: Improve, add comments, add forward compatibility.
Kim Alvefur <zash@zash.se>
parents: 667
diff changeset
     6
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
     7
-- These are in days.
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
     8
local nag_time = module:get_option_number("checkcerts_notify", 7) * 86400;
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
     9
855
1983d4d51e1a mod_checkcerts: Improve, add comments, add forward compatibility.
Kim Alvefur <zash@zash.se>
parents: 667
diff changeset
    10
if not load_cert then
1983d4d51e1a mod_checkcerts: Improve, add comments, add forward compatibility.
Kim Alvefur <zash@zash.se>
parents: 667
diff changeset
    11
	module:log("error", "This version of LuaSec (%s) does not support certificate checking", ssl._VERSION);
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    12
	return
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    13
end
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    14
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    15
local pat = "^([JFMAONSD][ceupao][glptbvyncr])  ?(%d%d?) (%d%d):(%d%d):(%d%d) (%d%d%d%d) GMT$";
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    16
local months = {Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12};
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    17
local function parse_x509_datetime(s)
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    18
	local month, day, hour, min, sec, year = s:match(pat); month = months[month];
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    19
	return datetime_parse(("%04d-%02d-%02dT%02d:%02d:%02dZ"):format(year, month, day, hour, min, sec));
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    20
end
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    21
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    22
local timeunits = {"minute",60,"hour",3600,"day",86400,"week",604800,"month",2629746,"year",31556952,};
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    23
local function humantime(timediff)
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    24
	local ret = {};
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    25
	for i=#timeunits,2,-2 do
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    26
		if timeunits[i] < timediff then
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    27
			local n = math.floor(timediff / timeunits[i]);
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    28
			if n > 0 and #ret < 2 then
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    29
				ret[#ret+1] = ("%d %s%s"):format(n, timeunits[i-1], n ~= 1 and "s" or "");
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    30
				timediff = timediff - n*timeunits[i];
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    31
			end
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    32
		end
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    33
	end
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    34
	return table.concat(ret, " and ")
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    35
end
941
a6c2345bcf87 mod_checkcerts: Nag admins about certs that have, or are about to expire. Often.
Kim Alvefur <zash@zash.se>
parents: 855
diff changeset
    36
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    37
local function check_certs_validity()
941
a6c2345bcf87 mod_checkcerts: Nag admins about certs that have, or are about to expire. Often.
Kim Alvefur <zash@zash.se>
parents: 855
diff changeset
    38
	local now = os.time();
a6c2345bcf87 mod_checkcerts: Nag admins about certs that have, or are about to expire. Often.
Kim Alvefur <zash@zash.se>
parents: 855
diff changeset
    39
855
1983d4d51e1a mod_checkcerts: Improve, add comments, add forward compatibility.
Kim Alvefur <zash@zash.se>
parents: 667
diff changeset
    40
	-- First, let's find out what certificate this host uses.
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    41
	local ssl_config = config.rawget(module.host, "ssl");
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    42
	if not ssl_config or not ssl_config.certificate then
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    43
		ssl_config = config.get(module.host:match("%.(.*)"), "ssl");
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    44
	end
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    45
	if not ssl_config or not ssl_config.certificate then
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    46
		ssl_config = config.get("*", "ssl");
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    47
	end
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    48
	if not ssl_config or not ssl_config.certificate then
2949
ec7f9c8f2a5f mod_checkcerts: Fixed luacheck warnings
Michel Le Bihan <michel@lebihan.pl>
parents: 1884
diff changeset
    49
		module:log("warn", "Could not find a certificate to check");
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    50
		return;
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    51
	end
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    52
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    53
	local certfile = ssl_config.certificate;
1884
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    54
	local fh, ferr = io.open(certfile); -- Load the file.
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    55
	if not fh then
2949
ec7f9c8f2a5f mod_checkcerts: Fixed luacheck warnings
Michel Le Bihan <michel@lebihan.pl>
parents: 1884
diff changeset
    56
		module:log("warn", "Could not open certificate %s", ferr);
1884
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    57
		return;
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    58
	end
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    59
	local cert, lerr = load_cert(fh:read("*a")); -- And parse
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    60
	fh:close();
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    61
	if not cert then
2949
ec7f9c8f2a5f mod_checkcerts: Fixed luacheck warnings
Michel Le Bihan <michel@lebihan.pl>
parents: 1884
diff changeset
    62
		module:log("warn", "Could not parse certificate %s: %s", certfile, lerr or "");
1884
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    63
		return;
a7c1f1b6ef05 mod_checkcerts: Improve error handling when loading certificate
Kim Alvefur <zash@zash.se>
parents: 1883
diff changeset
    64
	end
855
1983d4d51e1a mod_checkcerts: Improve, add comments, add forward compatibility.
Kim Alvefur <zash@zash.se>
parents: 667
diff changeset
    65
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    66
	local expires_at = parse_x509_datetime(cert:notafter());
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    67
	local expires_in = os.difftime(expires_at, now);
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    68
	local fmt =  "Certificate %s expires in %s"
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    69
	local nag_admin = expires_in < nag_time;
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    70
	local log_warn = expires_in < nag_time * 2;
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    71
	local timediff = expires_in;
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    72
	if expires_in < 0 then
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    73
		fmt =  "Certificate %s expired %s ago";
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    74
		timediff = -timediff;
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    75
	end
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    76
	timediff = humantime(timediff);
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    77
	module:log(log_warn and "warn" or "info", fmt, certfile, timediff);
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    78
	if nag_admin then
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    79
		local body = fmt:format("for host ".. module.host, timediff);
4611
c8ccaac78f64 mod_checkcerts: Notify both host-local and global admins
Kim Alvefur <zash@zash.se>
parents: 2949
diff changeset
    80
		for admin in module:get_option_inherited_set("admins", {}) do
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    81
			module:send(st.message({ from = module.host, to = admin, type = "chat" }, body));
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    82
		end
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    83
	end
1883
18123e0f5d58 mod_checkcerts: Improve logic for finding an ssl section with a certificate
Kim Alvefur <zash@zash.se>
parents: 1676
diff changeset
    84
	return math.max(86400, expires_in / 3);
667
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    85
end
ea9941812721 mod_checkcerts: New module that logs a warning when your cert is about to expire.
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    86
1098
cbbeac61f1ab mod_checkcerts: Add timestamp parsing, format time until expiry more human-readable, adjust check intervals to time left.
Kim Alvefur <zash@zash.se>
parents: 943
diff changeset
    87
module:add_timer(1, check_certs_validity);