aboutsummaryrefslogtreecommitdiffstats
path: root/util/prosodyctl/check.lua
diff options
context:
space:
mode:
Diffstat (limited to 'util/prosodyctl/check.lua')
-rw-r--r--util/prosodyctl/check.lua142
1 files changed, 91 insertions, 51 deletions
diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua
index 9d460158..8d085c85 100644
--- a/util/prosodyctl/check.lua
+++ b/util/prosodyctl/check.lua
@@ -1,24 +1,24 @@
-local configmanager = require "core.configmanager";
-local moduleapi = require "core.moduleapi";
-local show_usage = require "util.prosodyctl".show_usage;
-local show_warning = require "util.prosodyctl".show_warning;
-local is_prosody_running = require "util.prosodyctl".isrunning;
-local parse_args = require "util.argparse".parse;
-local dependencies = require "util.dependencies";
+local configmanager = require "prosody.core.configmanager";
+local moduleapi = require "prosody.core.moduleapi";
+local show_usage = require "prosody.util.prosodyctl".show_usage;
+local show_warning = require "prosody.util.prosodyctl".show_warning;
+local is_prosody_running = require "prosody.util.prosodyctl".isrunning;
+local parse_args = require "prosody.util.argparse".parse;
+local dependencies = require "prosody.util.dependencies";
local socket = require "socket";
local socket_url = require "socket.url";
-local jid_split = require "util.jid".prepped_split;
-local modulemanager = require "core.modulemanager";
-local async = require "util.async";
-local httputil = require "util.http";
+local jid_split = require "prosody.util.jid".prepped_split;
+local modulemanager = require "prosody.core.modulemanager";
+local async = require "prosody.util.async";
+local httputil = require "prosody.util.http";
local function api(host)
return setmetatable({ name = "prosodyctl.check"; host = host; log = prosody.log }, { __index = moduleapi })
end
local function check_ojn(check_type, target_host)
- local http = require "net.http"; -- .new({});
- local json = require "util.json";
+ local http = require "prosody.net.http"; -- .new({});
+ local json = require "prosody.util.json";
local response, err = async.wait_for(http.request(
("https://observe.jabber.network/api/v1/check/%s"):format(httputil.urlencode(check_type)),
@@ -46,7 +46,7 @@ local function check_ojn(check_type, target_host)
end
local function check_probe(base_url, probe_module, target)
- local http = require "net.http"; -- .new({});
+ local http = require "prosody.net.http"; -- .new({});
local params = httputil.formencode({ module = probe_module; target = target })
local response, err = async.wait_for(http.request(base_url .. "?" .. params));
@@ -67,8 +67,8 @@ local function check_probe(base_url, probe_module, target)
end
local function check_turn_service(turn_service, ping_service)
- local ip = require "util.ip";
- local stun = require "net.stun";
+ local ip = require "prosody.util.ip";
+ local stun = require "prosody.net.stun";
-- Create UDP socket for communication with the server
local sock = assert(require "socket".udp());
@@ -160,7 +160,7 @@ local function check_turn_service(turn_service, ping_service)
result.error = "TURN server did not response to allocation request: "..err;
return result;
elseif alloc_response:is_err_resp() then
- result.error = ("TURN allocation failed: %d (%s)"):format(alloc_response:get_error());
+ result.error = ("TURN server failed to create allocation: %d (%s)"):format(alloc_response:get_error());
return result;
elseif not alloc_response:is_success_resp() then
result.error = ("Unexpected TURN response: %d (%s)"):format(alloc_response:get_type());
@@ -309,18 +309,15 @@ local function check(arg)
print("Error: Unknown parameter: "..opts_info);
return 1;
end
- local array = require "util.array";
- local set = require "util.set";
- local it = require "util.iterators";
+ local array = require "prosody.util.array";
+ local set = require "prosody.util.set";
+ local it = require "prosody.util.iterators";
local ok = true;
+ local function contains_match(hayset, needle) for member in hayset do if member:find(needle) then return true end end end
local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end
local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end
- if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs" or what == "connectivity" or what == "turn") then
- show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs', 'disabled', 'turn' or 'connectivity'.", what);
- show_warning("Note: The connectivity check will connect to a remote server.");
- return 1;
- end
- if not what or what == "disabled" then
+ local checks = {};
+ function checks.disabled()
local disabled_hosts_set = set.new();
for host in it.filter("*", pairs(configmanager.getconfig())) do
if api(host):get_option_boolean("enabled") == false then
@@ -335,7 +332,7 @@ local function check(arg)
print""
end
end
- if not what or what == "config" then
+ function checks.config()
print("Checking config...");
if what == "config" then
@@ -510,9 +507,9 @@ local function check(arg)
end
for k, v in pairs(modules) do
if type(k) ~= "number" or type(v) ~= "string" then
- print(" The " .. name .. " in the " .. host .. " section should not be a map of " .. type(k) .. " to " .. type(v)
- .. " but a list of strings, e.g.");
+ print(" The " .. name .. " in the " .. host .. " section should be a list of strings, e.g.");
print(" " .. name .. " = { \"name_of_module\", \"another_plugin\", }")
+ print(" It should not contain key = value pairs, try putting them outside the {} brackets.");
ok = false
break
end
@@ -740,7 +737,7 @@ local function check(arg)
-- Check hostname validity
do
- local idna = require "util.encodings".idna;
+ local idna = require "prosody.util.encodings".idna;
local invalid_hosts = {};
local alabel_hosts = {};
for host in it.filter("*", pairs(configmanager.getconfig())) do
@@ -791,14 +788,14 @@ local function check(arg)
print("Done.\n");
end
- if not what or what == "dns" then
- local dns = require "net.dns";
+ function checks.dns()
+ local dns = require "prosody.net.dns";
pcall(function ()
- local unbound = require"net.unbound";
+ local unbound = require"prosody.net.unbound";
dns = unbound.dns;
end)
- local idna = require "util.encodings".idna;
- local ip = require "util.ip";
+ local idna = require "prosody.util.encodings".idna;
+ local ip = require "prosody.util.ip";
local global = api("*");
local c2s_ports = global:get_option_set("c2s_ports", {5222});
local s2s_ports = global:get_option_set("s2s_ports", {5269});
@@ -861,7 +858,7 @@ local function check(arg)
end
end
- local local_addresses = require"util.net".local_addresses() or {};
+ local local_addresses = require"prosody.util.net".local_addresses() or {};
for addr in it.values(local_addresses) do
if not ip.new_ip(addr).private then
@@ -1028,9 +1025,6 @@ local function check(arg)
end
local known_http_modules = set.new { "bosh"; "http_files"; "http_file_share"; "http_openmetrics"; "websocket" };
- local function contains_match(hayset, needle)
- for member in hayset do if member:find(needle) then return true end end
- end
if modules:contains("http") or not set.intersection(modules, known_http_modules):empty()
or contains_match(modules, "^http_") or contains_match(modules, "_web$") then
@@ -1166,11 +1160,14 @@ local function check(arg)
ok = false;
end
end
- if not what or what == "certs" then
+ function checks.certs()
local cert_ok;
print"Checking certificates..."
- local x509_verify_identity = require"util.x509".verify_identity;
- local create_context = require "core.certmanager".create_context;
+ local x509_verify_identity = require"prosody.util.x509".verify_identity;
+ local use_dane = configmanager.get("*", "use_dane");
+ local pem2der = require"prosody.util.x509".pem2der;
+ local sha256 = require"prosody.util.hashes".sha256;
+ local create_context = require "prosody.core.certmanager".create_context;
local ssl = dependencies.softreq"ssl";
-- local datetime_parse = require"util.datetime".parse_x509;
local load_cert = ssl and ssl.loadcertificate;
@@ -1183,13 +1180,14 @@ local function check(arg)
cert_ok = false
else
for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do
+ local modules = modulemanager.get_modules_for_host(host);
print("Checking certificate for "..host);
-- First, let's find out what certificate this host uses.
local host_ssl_config = configmanager.rawget(host, "ssl")
or configmanager.rawget(host:match("%.(.*)"), "ssl");
local global_ssl_config = configmanager.rawget("*", "ssl");
- local ok, err, ssl_config = create_context(host, "server", host_ssl_config, global_ssl_config);
- if not ok then
+ local ctx_ok, err, ssl_config = create_context(host, "server", host_ssl_config, global_ssl_config);
+ if not ctx_ok then
print(" Error: "..err);
cert_ok = false
elseif not ssl_config.certificate then
@@ -1224,17 +1222,39 @@ local function check(arg)
elseif not cert:validat(os.time() + 86400*31) then
print(" Certificate expires within one month.")
end
- if select(2, modulemanager.get_modules_for_host(host)) == nil
- and not x509_verify_identity(host, "_xmpp-client", cert) then
+ if modules:contains("c2s") and not x509_verify_identity(host, "_xmpp-client", cert) then
print(" Not valid for client connections to "..host..".")
cert_ok = false
end
- if (not (api(host):get_option_boolean("anonymous_login", false)
- or api(host):get_option_string("authentication", "internal_hashed") == "anonymous"))
- and not x509_verify_identity(host, "_xmpp-server", cert) then
+ local anon = api(host):get_option_string("authentication", "internal_hashed") == "anonymous";
+ local anon_s2s = api(host):get_option_boolean("allow_anonymous_s2s", false);
+ if modules:contains("s2s") and (anon_s2s or not anon) and not x509_verify_identity(host, "_xmpp-server", cert) then
print(" Not valid for server-to-server connections to "..host..".")
cert_ok = false
end
+
+ local known_http_modules = set.new { "bosh"; "http_files"; "http_file_share"; "http_openmetrics"; "websocket" };
+ local http_loaded = modules:contains("http")
+ or not set.intersection(modules, known_http_modules):empty()
+ or contains_match(modules, "^http_")
+ or contains_match(modules, "_web$");
+
+ local http_host = api(host):get_option_string("http_host", host);
+ if api(host):get_option_string("http_external_url") then
+ -- Assumed behind a reverse proxy
+ http_loaded = false;
+ end
+ if http_loaded and not x509_verify_identity(http_host, nil, cert) then
+ print(" Not valid for HTTPS connections to "..host..".")
+ cert_ok = false
+ end
+ if use_dane then
+ if cert.pubkey then
+ print(" DANE: TLSA 3 1 1 "..sha256(pem2der(cert:pubkey()), true))
+ elseif cert.pem then
+ print(" DANE: TLSA 3 0 1 "..sha256(pem2der(cert:pem()), true))
+ end
+ end
end
end
end
@@ -1247,7 +1267,7 @@ local function check(arg)
print("")
end
-- intentionally not doing this by default
- if what == "connectivity" then
+ function checks.connectivity()
local _, prosody_is_running = is_prosody_running();
if api("*"):get_option_string("pidfile") and not prosody_is_running then
print("Prosody does not appear to be running, which is required for this test.");
@@ -1339,7 +1359,7 @@ local function check(arg)
print("Note: It does not ensure that the check actually reaches this specific prosody instance.")
end
- if not what or what == "turn" then
+ function checks.turn()
local turn_enabled_hosts = {};
local turn_services = {};
@@ -1414,6 +1434,26 @@ local function check(arg)
end
end
end
+ if what == nil or what == "all" then
+ local ret;
+ ret = checks.disabled();
+ if ret ~= nil then return ret; end
+ ret = checks.config();
+ if ret ~= nil then return ret; end
+ ret = checks.dns();
+ if ret ~= nil then return ret; end
+ ret = checks.certs();
+ if ret ~= nil then return ret; end
+ ret = checks.turn();
+ if ret ~= nil then return ret; end
+ elseif checks[what] then
+ local ret = checks[what]();
+ if ret ~= nil then return ret; end
+ else
+ show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs', 'disabled', 'turn' or 'connectivity'.", what);
+ show_warning("Note: The connectivity check will connect to a remote server.");
+ return 1;
+ end
if not ok then
print("Problems found, see above.");