view mod_checkcerts/mod_checkcerts.lua @ 1268:854a3933cfcd

mod_muc_log_http: URL-encode room names. This allows special characters in room names to work. Ideally this escaping shouldn’t be done in the user visible content, but the module’s template system doesn’t currently allow that.
author Waqas Hussain <waqas20@gmail.com>
date Sat, 04 Jan 2014 16:50:57 -0500
parents cbbeac61f1ab
children d9fcf9d8e787
line wrap: on
line source

local ssl = require"ssl";
local datetime_parse = require"util.datetime".parse;
local load_cert = ssl.x509 and ssl.x509.load;
local st = require"util.stanza"

-- These are in days.
local nag_time = module:get_option_number("checkcerts_notify", 7) * 86400;

if not load_cert then
	module:log("error", "This version of LuaSec (%s) does not support certificate checking", ssl._VERSION);
	return
end

local pat = "^([JFMAONSD][ceupao][glptbvyncr])  ?(%d%d?) (%d%d):(%d%d):(%d%d) (%d%d%d%d) GMT$";
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};
local function parse_x509_datetime(s)
	local month, day, hour, min, sec, year = s:match(pat); month = months[month];
	return datetime_parse(("%04d-%02d-%02dT%02d:%02d:%02dZ"):format(year, month, day, hour, min, sec));
end

local timeunits = {"minute",60,"hour",3600,"day",86400,"week",604800,"month",2629746,"year",31556952,};
local function humantime(timediff)
	local ret = {};
	for i=#timeunits,2,-2 do
		if timeunits[i] < timediff then
			local n = math.floor(timediff / timeunits[i]);
			if n > 0 and #ret < 2 then
				ret[#ret+1] = ("%d %s%s"):format(n, timeunits[i-1], n ~= 1 and "s" or "");
				timediff = timediff - n*timeunits[i];
			end
		end
	end
	return table.concat(ret, " and ")
end

local function check_certs_validity()
	local now = os.time();

	-- First, let's find out what certificate this host uses.
	local ssl_config = config.rawget(module.host, "ssl");
	if not ssl_config then
		local base_host = module.host:match("%.(.*)");
		ssl_config = config.get(base_host, "ssl");
	end

	if ssl_config and ssl_config.certificate then
		local certfile = ssl_config.certificate;
		local fh = io.open(certfile); -- Load the file.
		cert = fh and fh:read"*a";
		fh = fh and fh:close();
		local cert = cert and load_cert(cert); -- And parse

		if not cert then
			module:log("warn", "No certificate configured for this host, please fix this and reload this module to check expiry");
			return
		end
		local expires_at = parse_x509_datetime(cert:notafter());
		local expires_in = os.difftime(expires_at, now);
		local fmt =  "Certificate %s expires in %s"
		local nag_admin = expires_in < nag_time;
		local log_warn = expires_in < nag_time * 2;
		local timediff = expires_in;
		if expires_in < 0 then
			fmt =  "Certificate %s expired %s ago";
			timediff = -timediff;
		end
		timediff = humantime(timediff);
		module:log(log_warn and "warn" or "info", fmt, certfile, timediff);
		if nag_admin then
			local body = fmt:format("for host ".. module.host, timediff);
			for _,admin in ipairs(module:get_option_array("admins", {})) do
				module:send(st.message({ from = module.host, to = admin, type = "chat" }, body));
			end
		end
		return math.max(86400, expires_in / 3);
	end
end

module:add_timer(1, check_certs_validity);