From db408d9cf0fbec0bc273ceaf23c80e53dddcd95d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 21 Apr 2017 15:11:25 +0200 Subject: prosodyctl: cert import: Command to copy certificates into prosodys certificate directory (fixes #892) --- prosodyctl | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/prosodyctl b/prosodyctl index 637fdd12..cc410f5a 100755 --- a/prosodyctl +++ b/prosodyctl @@ -829,6 +829,72 @@ function cert_commands.generate(arg) 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"; -- cgit v1.2.3