Software / code / prosody
Comparison
util/prosodyctl/check.lua @ 11779:f4f0bdaeabd2
prosodyctl: Add external connectivity check based on observe.jabber.network
This uses the (experimental) observe.jabber.network API to
perform external connectivity checks. The idea is to complement
the checks prosodyctl can already do with a (nearly) complete
s2s/c2s handshake from a remote party to test the entire stack.
| author | Jonas Schäfer <jonas@wielicki.name> |
|---|---|
| date | Wed, 06 May 2020 18:20:33 +0200 |
| parent | 11778:f254fd16218a |
| child | 11780:98ae95235775 |
comparison
equal
deleted
inserted
replaced
| 11778:f254fd16218a | 11779:f4f0bdaeabd2 |
|---|---|
| 4 local dependencies = require "util.dependencies"; | 4 local dependencies = require "util.dependencies"; |
| 5 local socket = require "socket"; | 5 local socket = require "socket"; |
| 6 local jid_split = require "util.jid".prepped_split; | 6 local jid_split = require "util.jid".prepped_split; |
| 7 local modulemanager = require "core.modulemanager"; | 7 local modulemanager = require "core.modulemanager"; |
| 8 | 8 |
| 9 local function check_api(check_type, target_host) | |
| 10 local async = require "util.async"; | |
| 11 local wait, done = async.waiter(); | |
| 12 local http = require "net.http"; -- .new({}); | |
| 13 local urlencode = require "util.http".urlencode; | |
| 14 local json = require "util.json"; | |
| 15 | |
| 16 local ok = false; | |
| 17 local err = nil; | |
| 18 local decoded_body = nil; | |
| 19 | |
| 20 http.request( | |
| 21 ("https://observe.jabber.network/api/v1/check/%s"):format(urlencode(check_type)), | |
| 22 { | |
| 23 method="POST", | |
| 24 headers={["Accept"] = "application/json"; ["Content-Type"] = "application/json"}, | |
| 25 body=json.encode({target=target_host}), | |
| 26 }, | |
| 27 function (body, code) | |
| 28 if code ~= 200 then | |
| 29 err = ("API replied with non-200 code: %d"):format(code) | |
| 30 else | |
| 31 decoded_body, err = json.decode(body); | |
| 32 if decoded_body == nil then | |
| 33 err = ("Failed to parse API JSON: %s"):format(err) | |
| 34 else | |
| 35 ok = true | |
| 36 end | |
| 37 end | |
| 38 done(); | |
| 39 end | |
| 40 ); | |
| 41 wait(); | |
| 42 | |
| 43 if not ok then | |
| 44 return false, err | |
| 45 end | |
| 46 | |
| 47 local success = decoded_body["success"]; | |
| 48 return success == true, nil; | |
| 49 end | |
| 50 | |
| 51 local function skip_bare_jid_hosts(host) | |
| 52 if jid_split(host) then | |
| 53 -- See issue #779 | |
| 54 return false; | |
| 55 end | |
| 56 return true; | |
| 57 end | |
| 58 | |
| 9 local function check(arg) | 59 local function check(arg) |
| 10 if arg[1] == "--help" then | 60 if arg[1] == "--help" then |
| 11 show_usage([[check]], [[Perform basic checks on your Prosody installation]]); | 61 show_usage([[check]], [[Perform basic checks on your Prosody installation]]); |
| 12 return 1; | 62 return 1; |
| 13 end | 63 end |
| 15 local set = require "util.set"; | 65 local set = require "util.set"; |
| 16 local it = require "util.iterators"; | 66 local it = require "util.iterators"; |
| 17 local ok = true; | 67 local ok = true; |
| 18 local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end | 68 local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end |
| 19 local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end | 69 local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end |
| 20 if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs") then | 70 if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs" or what == "connectivity") then |
| 21 show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs' or 'disabled'.", what); | 71 show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs', 'disabled' or 'connectivity'.", what); |
| 72 show_warning("Note: The connectivity check will connect to a remote server."); | |
| 22 return 1; | 73 return 1; |
| 23 end | 74 end |
| 24 if not what or what == "disabled" then | 75 if not what or what == "disabled" then |
| 25 local disabled_hosts_set = set.new(); | 76 local disabled_hosts_set = set.new(); |
| 26 for host, host_options in it.filter("*", pairs(configmanager.getconfig())) do | 77 for host, host_options in it.filter("*", pairs(configmanager.getconfig())) do |
| 604 if what == "certs" then cert_ok = false end | 655 if what == "certs" then cert_ok = false end |
| 605 elseif not load_cert then | 656 elseif not load_cert then |
| 606 print("This version of LuaSec (" .. ssl._VERSION .. ") does not support certificate checking"); | 657 print("This version of LuaSec (" .. ssl._VERSION .. ") does not support certificate checking"); |
| 607 cert_ok = false | 658 cert_ok = false |
| 608 else | 659 else |
| 609 local function skip_bare_jid_hosts(host) | |
| 610 if jid_split(host) then | |
| 611 -- See issue #779 | |
| 612 return false; | |
| 613 end | |
| 614 return true; | |
| 615 end | |
| 616 for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do | 660 for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do |
| 617 print("Checking certificate for "..host); | 661 print("Checking certificate for "..host); |
| 618 -- First, let's find out what certificate this host uses. | 662 -- First, let's find out what certificate this host uses. |
| 619 local host_ssl_config = configmanager.rawget(host, "ssl") | 663 local host_ssl_config = configmanager.rawget(host, "ssl") |
| 620 or configmanager.rawget(host:match("%.(.*)"), "ssl"); | 664 or configmanager.rawget(host:match("%.(.*)"), "ssl"); |
| 675 print("For more information about certificates please see https://prosody.im/doc/certificates"); | 719 print("For more information about certificates please see https://prosody.im/doc/certificates"); |
| 676 ok = false | 720 ok = false |
| 677 end | 721 end |
| 678 print("") | 722 print("") |
| 679 end | 723 end |
| 724 -- intentionally not doing this by default | |
| 725 if what == "connectivity" then | |
| 726 for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do | |
| 727 local modules, component_module = modulemanager.get_modules_for_host(host); | |
| 728 if component_module then | |
| 729 modules:add(component_module) | |
| 730 end | |
| 731 | |
| 732 print("Checking external connectivity for "..host.." via observe.jabber.network") | |
| 733 local function check_connectivity(protocol) | |
| 734 local success, err = check_api(protocol, host); | |
| 735 if not success and err ~= nil then | |
| 736 print((" %s: Failed to request check at API: %s"):format(protocol, err)) | |
| 737 elseif success then | |
| 738 print((" %s: Works"):format(protocol)) | |
| 739 else | |
| 740 print((" %s: Check service failed to establish (secure) connection"):format(protocol)) | |
| 741 ok = false | |
| 742 end | |
| 743 end | |
| 744 | |
| 745 if modules:contains("c2s") then | |
| 746 check_connectivity("xmpp-client") | |
| 747 end | |
| 748 | |
| 749 if modules:contains("s2s") then | |
| 750 check_connectivity("xmpp-server") | |
| 751 end | |
| 752 | |
| 753 print() | |
| 754 end | |
| 755 print("Note: The connectivity check only checks the reachability of the domain.") | |
| 756 print("Note: It does not ensure that the check actually reaches this specific prosody instance.") | |
| 757 end | |
| 680 if not ok then | 758 if not ok then |
| 681 print("Problems found, see above."); | 759 print("Problems found, see above."); |
| 682 else | 760 else |
| 683 print("All checks passed, congratulations!"); | 761 print("All checks passed, congratulations!"); |
| 684 end | 762 end |