prosodyctl: cert import: Command to copy certificates into prosodys certificate directory (fixes #892)
authorKim Alvefur <zash@zash.se>
Fri, 21 Apr 2017 15:11:25 +0200
changeset 8114 3cbb311f8468
parent 8113 9aeb1c631f62
child 8115 d8ecefcb7c97
child 8116 cfb5ab763384
prosodyctl: cert import: Command to copy certificates into prosodys certificate directory (fixes #892)
prosodyctl
--- a/prosodyctl	Fri Apr 21 14:44:28 2017 +0200
+++ b/prosodyctl	Fri Apr 21 15:11:25 2017 +0200
@@ -829,6 +829,72 @@
 	end
 end
 
+local function sh_esc(s)
+	return "'" .. s:gsub("'", "'\\''") .. "'";
+end
+
+local function copy(from, to, umask, owner, group)
+	local old_umask = umask and pposix.umask(umask);
+	local attrs = lfs.attributes(to);
+	if attrs then -- Move old file out of the way
+		local backup = to..".bkp~"..os.date("%FT%T", attrs.change);
+		os.rename(to, backup);
+	end
+	-- FIXME friendlier error handling, maybe move above backup back?
+	local input = assert(io.open(from));
+	local output = assert(io.open(to, "w"));
+	local data = input:read(2^11);
+	while data and output:write(data) do
+		data = input:read(2^11);
+	end
+	assert(input:close());
+	assert(output:close());
+	if owner and group then
+		local ok = os.execute(("chown %s.%s %s"):format(sh_esc(owner), sh_esc(group), sh_esc(to)));
+		assert(ok == true or ok == 0, "Failed to change ownership of "..to);
+	end
+	if old_umask then pposix.umask(old_umask); end
+	return true;
+end
+
+function cert_commands.import(arg)
+	local hostnames = {};
+	-- Move hostname arguments out of arg, the rest should be a list of paths
+	while arg[1] and prosody.hosts[ arg[1] ] do
+		table.insert(hostnames, table.remove(arg, 1));
+	end
+	if not arg[1] or arg[1] == "--help" then -- Probably forgot the path
+		show_usage("cert import HOSTNAME [HOSTNAME+] /path/to/certs [/other/paths/]+",
+			"Copies certificates to "..cert_basedir);
+		return 1;
+	end
+	local owner, group;
+	if pposix.getuid() == 0 then -- We need root to change ownership
+		owner = config.get("*", "prosody_user") or "prosody";
+		group = config.get("*", "prosody_group") or owner;
+	end
+	for _, host in ipairs(hostnames) do
+		for _, dir in ipairs(arg) do
+			if lfs.attributes(dir .. "/" .. host .. "/fullchain.pem")
+			and lfs.attributes(dir .. "/" .. host .. "/privkey.pem") then
+				copy(dir .. "/" .. host .. "/fullchain.pem", cert_basedir .. "/" .. host .. ".crt", nil, owner, group);
+				copy(dir .. "/" .. host .. "/privkey.pem", cert_basedir .. "/" .. host .. ".key", "0377", owner, group);
+				show_message("Imported certificate and key for "..host);
+			elseif lfs.attributes(dir .. "/" .. host .. ".crt")
+			and lfs.attributes(dir .. "/" .. host .. ".key") then
+				copy(dir .. "/" .. host .. ".crt", cert_basedir .. "/" .. host .. ".crt", nil, owner, group);
+				copy(dir .. "/" .. host .. ".key", cert_basedir .. "/" .. host .. ".key", "0377", owner, group);
+				show_message("Imported certificate and key for "..host);
+			else
+				show_warning("No certificate for host "..host.." found :(");
+			end
+			-- TODO Additional checks
+			-- Certificate names matches the hostname
+			-- Private key matches public key in certificate
+		end
+	end
+end
+
 function commands.cert(arg)
 	if #arg >= 1 and arg[1] ~= "--help" then
 		openssl = require "util.openssl";