Software / code / prosody
Comparison
prosodyctl @ 6054:7a5ddbaf758d
Merge 0.9->0.10
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Wed, 02 Apr 2014 17:41:38 +0100 |
| parent | 6038:b3ceb7627e27 |
| child | 6062:6cc6b4d407df |
comparison
equal
deleted
inserted
replaced
| 6053:2f93a04564b2 | 6054:7a5ddbaf758d |
|---|---|
| 272 ----------------------- | 272 ----------------------- |
| 273 local commands = {}; | 273 local commands = {}; |
| 274 local command = arg[1]; | 274 local command = arg[1]; |
| 275 | 275 |
| 276 function commands.adduser(arg) | 276 function commands.adduser(arg) |
| 277 local jid_split = require "util.jid".split; | |
| 277 if not arg[1] or arg[1] == "--help" then | 278 if not arg[1] or arg[1] == "--help" then |
| 278 show_usage([[adduser JID]], [[Create the specified user account in Prosody]]); | 279 show_usage([[adduser JID]], [[Create the specified user account in Prosody]]); |
| 279 return 1; | 280 return 1; |
| 280 end | 281 end |
| 281 local user, host = arg[1]:match("([^@]+)@(.+)"); | 282 local user, host = jid_split(arg[1]); |
| 282 if not user and host then | 283 if not user and host then |
| 283 show_message [[Failed to understand JID, please supply the JID you want to create]] | 284 show_message [[Failed to understand JID, please supply the JID you want to create]] |
| 284 show_usage [[adduser user@host]] | 285 show_usage [[adduser user@host]] |
| 285 return 1; | 286 return 1; |
| 286 end | 287 end |
| 311 show_message(msg) | 312 show_message(msg) |
| 312 return 1; | 313 return 1; |
| 313 end | 314 end |
| 314 | 315 |
| 315 function commands.passwd(arg) | 316 function commands.passwd(arg) |
| 317 local jid_split = require "util.jid".split; | |
| 316 if not arg[1] or arg[1] == "--help" then | 318 if not arg[1] or arg[1] == "--help" then |
| 317 show_usage([[passwd JID]], [[Set the password for the specified user account in Prosody]]); | 319 show_usage([[passwd JID]], [[Set the password for the specified user account in Prosody]]); |
| 318 return 1; | 320 return 1; |
| 319 end | 321 end |
| 320 local user, host = arg[1]:match("([^@]+)@(.+)"); | 322 local user, host = jid_split(arg[1]); |
| 321 if not user and host then | 323 if not user and host then |
| 322 show_message [[Failed to understand JID, please supply the JID you want to set the password for]] | 324 show_message [[Failed to understand JID, please supply the JID you want to set the password for]] |
| 323 show_usage [[passwd user@host]] | 325 show_usage [[passwd user@host]] |
| 324 return 1; | 326 return 1; |
| 325 end | 327 end |
| 350 show_message(error_messages[msg]) | 352 show_message(error_messages[msg]) |
| 351 return 1; | 353 return 1; |
| 352 end | 354 end |
| 353 | 355 |
| 354 function commands.deluser(arg) | 356 function commands.deluser(arg) |
| 357 local jid_split = require "util.jid".split; | |
| 355 if not arg[1] or arg[1] == "--help" then | 358 if not arg[1] or arg[1] == "--help" then |
| 356 show_usage([[deluser JID]], [[Permanently remove the specified user account from Prosody]]); | 359 show_usage([[deluser JID]], [[Permanently remove the specified user account from Prosody]]); |
| 357 return 1; | 360 return 1; |
| 358 end | 361 end |
| 359 local user, host = arg[1]:match("([^@]+)@(.+)"); | 362 local user, host = jid_split(arg[1]); |
| 360 if not user and host then | 363 if not user and host then |
| 361 show_message [[Failed to understand JID, please supply the JID you want to set the password for]] | 364 show_message [[Failed to understand JID, please supply the JID you want to set the password for]] |
| 362 show_usage [[passwd user@host]] | 365 show_usage [[passwd user@host]] |
| 363 return 1; | 366 return 1; |
| 364 end | 367 end |
| 779 end | 782 end |
| 780 end | 783 end |
| 781 show_usage("cert config|request|generate|key", "Helpers for generating X.509 certificates and keys.") | 784 show_usage("cert config|request|generate|key", "Helpers for generating X.509 certificates and keys.") |
| 782 end | 785 end |
| 783 | 786 |
| 787 function commands.check(arg) | |
| 788 if arg[1] == "--help" then | |
| 789 show_usage([[check]], [[Perform basic checks on your Prosody installation]]); | |
| 790 return 1; | |
| 791 end | |
| 792 local what = table.remove(arg, 1); | |
| 793 local array, set = require "util.array", require "util.set"; | |
| 794 local it = require "util.iterators"; | |
| 795 local ok = true; | |
| 796 if not what or what == "config" then | |
| 797 print("Checking config..."); | |
| 798 local known_global_options = set.new({ | |
| 799 "pidfile", "log", "plugin_paths", "prosody_user", "prosody_group", "daemonize", | |
| 800 "umask", "prosodyctl_timeout", "use_ipv6", "use_libevent", "network_settings" | |
| 801 }); | |
| 802 local config = config.getconfig(); | |
| 803 -- Check that we have any global options (caused by putting a host at the top) | |
| 804 if it.count(it.filter("log", pairs(config["*"]))) == 0 then | |
| 805 ok = false; | |
| 806 print(""); | |
| 807 print(" No global options defined. Perhaps you have put a host definition at the top") | |
| 808 print(" of the config file? They should be at the bottom, see http://prosody.im/doc/configure#overview"); | |
| 809 end | |
| 810 -- Check for global options under hosts | |
| 811 local global_options = set.new(it.to_array(it.keys(config["*"]))); | |
| 812 for host, options in it.filter("*", pairs(config)) do | |
| 813 local host_options = set.new(it.to_array(it.keys(options))); | |
| 814 local misplaced_options = set.intersection(host_options, known_global_options); | |
| 815 for name in pairs(options) do | |
| 816 if name:match("^interfaces?") | |
| 817 or name:match("_ports?$") or name:match("_interfaces?$") | |
| 818 or name:match("_ssl$") then | |
| 819 misplaced_options:add(name); | |
| 820 end | |
| 821 end | |
| 822 if not misplaced_options:empty() then | |
| 823 ok = false; | |
| 824 print(""); | |
| 825 local n = it.count(misplaced_options); | |
| 826 print(" You have "..n.." option"..(n>1 and "s " or " ").."set under "..host.." that should be"); | |
| 827 print(" in the global section of the config file, above any VirtualHost or Component definitions,") | |
| 828 print(" see http://prosody.im/doc/configure#overview for more information.") | |
| 829 print(""); | |
| 830 print(" You need to move the following option"..(n>1 and "s" or "")..": "..table.concat(it.to_array(misplaced_options), ", ")); | |
| 831 end | |
| 832 local subdomain = host:match("^[^.]+"); | |
| 833 if not(host_options:contains("component_module")) and (subdomain == "jabber" or subdomain == "xmpp" | |
| 834 or subdomain == "chat" or subdomain == "im") then | |
| 835 print(""); | |
| 836 print(" Suggestion: If "..host.. " is a new host with no real users yet, consider renaming it now to"); | |
| 837 print(" "..host:gsub("^[^.]+%.", "")..". You can use SRV records to redirect XMPP clients and servers to "..host.."."); | |
| 838 print(" For more information see: http://prosody.im/doc/dns"); | |
| 839 end | |
| 840 end | |
| 841 | |
| 842 print("Done.\n"); | |
| 843 end | |
| 844 if not what or what == "dns" then | |
| 845 local dns = require "net.dns"; | |
| 846 local idna = require "util.encodings".idna; | |
| 847 local ip = require "util.ip"; | |
| 848 local c2s_ports = set.new(config.get("*", "c2s_ports") or {5222}); | |
| 849 local s2s_ports = set.new(config.get("*", "s2s_ports") or {5269}); | |
| 850 | |
| 851 local c2s_srv_required, s2s_srv_required; | |
| 852 if not c2s_ports:contains(5222) then | |
| 853 c2s_srv_required = true; | |
| 854 end | |
| 855 if not s2s_ports:contains(5269) then | |
| 856 s2s_srv_required = true; | |
| 857 end | |
| 858 | |
| 859 local problem_hosts = set.new(); | |
| 860 | |
| 861 local external_addresses, internal_addresses = set.new(), set.new(); | |
| 862 | |
| 863 local fqdn = socket.dns.tohostname(socket.dns.gethostname()); | |
| 864 if fqdn then | |
| 865 local res = dns.lookup(idna.to_ascii(fqdn), "A"); | |
| 866 if res then | |
| 867 for _, record in ipairs(res) do | |
| 868 external_addresses:add(record.a); | |
| 869 end | |
| 870 end | |
| 871 local res = dns.lookup(idna.to_ascii(fqdn), "AAAA"); | |
| 872 if res then | |
| 873 for _, record in ipairs(res) do | |
| 874 external_addresses:add(record.aaaa); | |
| 875 end | |
| 876 end | |
| 877 end | |
| 878 | |
| 879 local local_addresses = require"util.net".local_addresses() or {}; | |
| 880 | |
| 881 for addr in it.values(local_addresses) do | |
| 882 if not ip.new_ip(addr).private then | |
| 883 external_addresses:add(addr); | |
| 884 else | |
| 885 internal_addresses:add(addr); | |
| 886 end | |
| 887 end | |
| 888 | |
| 889 if external_addresses:empty() then | |
| 890 print(""); | |
| 891 print(" Failed to determine the external addresses of this server. Checks may be inaccurate."); | |
| 892 c2s_srv_required, s2s_srv_required = true, true; | |
| 893 end | |
| 894 | |
| 895 local v6_supported = not not socket.tcp6; | |
| 896 | |
| 897 for host, host_options in it.filter("*", pairs(config.getconfig())) do | |
| 898 local all_targets_ok, some_targets_ok = true, false; | |
| 899 | |
| 900 local is_component = not not host_options.component_module; | |
| 901 print("Checking DNS for "..(is_component and "component" or "host").." "..host.."..."); | |
| 902 local target_hosts = set.new(); | |
| 903 if not is_component then | |
| 904 local res = dns.lookup("_xmpp-client._tcp."..idna.to_ascii(host)..".", "SRV"); | |
| 905 if res then | |
| 906 for _, record in ipairs(res) do | |
| 907 target_hosts:add(record.srv.target); | |
| 908 if not c2s_ports:contains(record.srv.port) then | |
| 909 print(" SRV target "..record.srv.target.." contains unknown client port: "..record.srv.port); | |
| 910 end | |
| 911 end | |
| 912 else | |
| 913 if c2s_srv_required then | |
| 914 print(" No _xmpp-client SRV record found for "..host..", but it looks like you need one."); | |
| 915 all_targst_ok = false; | |
| 916 else | |
| 917 target_hosts:add(host); | |
| 918 end | |
| 919 end | |
| 920 end | |
| 921 local res = dns.lookup("_xmpp-server._tcp."..idna.to_ascii(host)..".", "SRV"); | |
| 922 if res then | |
| 923 for _, record in ipairs(res) do | |
| 924 target_hosts:add(record.srv.target); | |
| 925 if not s2s_ports:contains(record.srv.port) then | |
| 926 print(" SRV target "..record.srv.target.." contains unknown server port: "..record.srv.port); | |
| 927 end | |
| 928 end | |
| 929 else | |
| 930 if s2s_srv_required then | |
| 931 print(" No _xmpp-server SRV record found for "..host..", but it looks like you need one."); | |
| 932 all_targets_ok = false; | |
| 933 else | |
| 934 target_hosts:add(host); | |
| 935 end | |
| 936 end | |
| 937 if target_hosts:empty() then | |
| 938 target_hosts:add(host); | |
| 939 end | |
| 940 | |
| 941 if target_hosts:contains("localhost") then | |
| 942 print(" Target 'localhost' cannot be accessed from other servers"); | |
| 943 target_hosts:remove("localhost"); | |
| 944 end | |
| 945 | |
| 946 local modules = set.new(it.to_array(it.values(host_options.modules_enabled))) | |
| 947 + set.new(it.to_array(it.values(config.get("*", "modules_enabled")))) | |
| 948 + set.new({ config.get(host, "component_module") }); | |
| 949 | |
| 950 if modules:contains("proxy65") then | |
| 951 local proxy65_target = config.get(host, "proxy65_address") or host; | |
| 952 local A, AAAA = dns.lookup(idna.to_ascii(proxy65_target), "A"), dns.lookup(idna.to_ascii(proxy65_target), "AAAA"); | |
| 953 local prob = {}; | |
| 954 if not A then | |
| 955 table.insert(prob, "A"); | |
| 956 end | |
| 957 if v6_supported and not AAAA then | |
| 958 table.insert(prob, "AAAA"); | |
| 959 end | |
| 960 if #prob > 0 then | |
| 961 print(" File transfer proxy "..proxy65_target.." has no "..table.concat(prob, "/").." record. Create one or set 'proxy65_address' to the correct host/IP."); | |
| 962 end | |
| 963 end | |
| 964 | |
| 965 for host in target_hosts do | |
| 966 local host_ok_v4, host_ok_v6; | |
| 967 local res = dns.lookup(idna.to_ascii(host), "A"); | |
| 968 if res then | |
| 969 for _, record in ipairs(res) do | |
| 970 if external_addresses:contains(record.a) then | |
| 971 some_targets_ok = true; | |
| 972 host_ok_v4 = true; | |
| 973 elseif internal_addresses:contains(record.a) then | |
| 974 host_ok_v4 = true; | |
| 975 some_targets_ok = true; | |
| 976 print(" "..host.." A record points to internal address, external connections might fail"); | |
| 977 else | |
| 978 print(" "..host.." A record points to unknown address "..record.a); | |
| 979 all_targets_ok = false; | |
| 980 end | |
| 981 end | |
| 982 end | |
| 983 local res = dns.lookup(idna.to_ascii(host), "AAAA"); | |
| 984 if res then | |
| 985 for _, record in ipairs(res) do | |
| 986 if external_addresses:contains(record.aaaa) then | |
| 987 some_targets_ok = true; | |
| 988 host_ok_v6 = true; | |
| 989 elseif internal_addresses:contains(record.aaaa) then | |
| 990 host_ok_v6 = true; | |
| 991 some_targets_ok = true; | |
| 992 print(" "..host.." AAAA record points to internal address, external connections might fail"); | |
| 993 else | |
| 994 print(" "..host.." AAAA record points to unknown address "..record.aaaa); | |
| 995 all_targets_ok = false; | |
| 996 end | |
| 997 end | |
| 998 end | |
| 999 | |
| 1000 local bad_protos = {} | |
| 1001 if not host_ok_v4 then | |
| 1002 table.insert(bad_protos, "IPv4"); | |
| 1003 end | |
| 1004 if not host_ok_v6 then | |
| 1005 table.insert(bad_protos, "IPv6"); | |
| 1006 end | |
| 1007 if #bad_protos > 0 then | |
| 1008 print(" Host "..host.." does not seem to resolve to this server ("..table.concat(bad_protos, "/")..")"); | |
| 1009 end | |
| 1010 if host_ok_v6 and not v6_supported then | |
| 1011 print(" Host "..host.." has AAAA records, but your version of LuaSocket does not support IPv6."); | |
| 1012 print(" Please see http://prosody.im/doc/ipv6 for more information."); | |
| 1013 end | |
| 1014 end | |
| 1015 if not all_targets_ok then | |
| 1016 print(" "..(some_targets_ok and "Only some" or "No").." targets for "..host.." appear to resolve to this server."); | |
| 1017 if is_component then | |
| 1018 print(" DNS records are necessary if you want users on other servers to access this component."); | |
| 1019 end | |
| 1020 problem_hosts:add(host); | |
| 1021 end | |
| 1022 print(""); | |
| 1023 end | |
| 1024 if not problem_hosts:empty() then | |
| 1025 print(""); | |
| 1026 print("For more information about DNS configuration please see http://prosody.im/doc/dns"); | |
| 1027 print(""); | |
| 1028 ok = false; | |
| 1029 end | |
| 1030 end | |
| 1031 if not what or what == "certs" then | |
| 1032 local cert_ok; | |
| 1033 print"Checking certificates..." | |
| 1034 local x509_verify_identity = require"util.x509".verify_identity; | |
| 1035 local ssl = dependencies.softreq"ssl"; | |
| 1036 -- local datetime_parse = require"util.datetime".parse_x509; | |
| 1037 local load_cert = ssl and ssl.x509 and ssl.x509.load; | |
| 1038 -- or ssl.cert_from_pem | |
| 1039 if not ssl then | |
| 1040 print("LuaSec not available, can't perform certificate checks") | |
| 1041 if what == "certs" then cert_ok = false end | |
| 1042 elseif not load_cert then | |
| 1043 print("This version of LuaSec (" .. ssl._VERSION .. ") does not support certificate checking"); | |
| 1044 cert_ok = false | |
| 1045 else | |
| 1046 for host in pairs(hosts) do | |
| 1047 if host ~= "*" then -- Should check global certs too. | |
| 1048 print("Checking certificate for "..host); | |
| 1049 -- First, let's find out what certificate this host uses. | |
| 1050 local ssl_config = config.rawget(host, "ssl"); | |
| 1051 if not ssl_config then | |
| 1052 local base_host = host:match("%.(.*)"); | |
| 1053 ssl_config = config.get(base_host, "ssl"); | |
| 1054 end | |
| 1055 if not ssl_config then | |
| 1056 print(" No 'ssl' option defined for "..host) | |
| 1057 cert_ok = false | |
| 1058 elseif not ssl_config.certificate then | |
| 1059 print(" No 'certificate' set in ssl option for "..host) | |
| 1060 cert_ok = false | |
| 1061 elseif not ssl_config.key then | |
| 1062 print(" No 'key' set in ssl option for "..host) | |
| 1063 cert_ok = false | |
| 1064 else | |
| 1065 local key, err = io.open(ssl_config.key); -- Permissions check only | |
| 1066 if not key then | |
| 1067 print(" Could not open "..ssl_config.key..": "..err); | |
| 1068 cert_ok = false | |
| 1069 else | |
| 1070 key:close(); | |
| 1071 end | |
| 1072 local cert_fh, err = io.open(ssl_config.certificate); -- Load the file. | |
| 1073 if not cert_fh then | |
| 1074 print(" Could not open "..ssl_config.certificate..": "..err); | |
| 1075 cert_ok = false | |
| 1076 else | |
| 1077 print(" Certificate: "..ssl_config.certificate) | |
| 1078 local cert = load_cert(cert_fh:read"*a"); cert_fh = cert_fh:close(); | |
| 1079 if not cert:validat(os.time()) then | |
| 1080 print(" Certificate has expired.") | |
| 1081 cert_ok = false | |
| 1082 end | |
| 1083 if config.get(host, "component_module") == nil | |
| 1084 and not x509_verify_identity(host, "_xmpp-client", cert) then | |
| 1085 print(" Not vaild for client connections to "..host..".") | |
| 1086 cert_ok = false | |
| 1087 end | |
| 1088 if (not (config.get(name, "anonymous_login") | |
| 1089 or config.get(name, "authentication") == "anonymous")) | |
| 1090 and not x509_verify_identity(host, "_xmpp-client", cert) then | |
| 1091 print(" Not vaild for server-to-server connections to "..host..".") | |
| 1092 cert_ok = false | |
| 1093 end | |
| 1094 end | |
| 1095 end | |
| 1096 end | |
| 1097 end | |
| 1098 if cert_ok == false then | |
| 1099 print("") | |
| 1100 print("For more information about certificates please see http://prosody.im/doc/certificates"); | |
| 1101 ok = false | |
| 1102 end | |
| 1103 end | |
| 1104 print("") | |
| 1105 end | |
| 1106 if not ok then | |
| 1107 print("Problems found, see above."); | |
| 1108 else | |
| 1109 print("All checks passed, congratulations!"); | |
| 1110 end | |
| 1111 return ok and 0 or 2; | |
| 1112 end | |
| 1113 | |
| 784 --------------------- | 1114 --------------------- |
| 785 | 1115 |
| 786 if command and command:match("^mod_") then -- Is a command in a module | 1116 if command and command:match("^mod_") then -- Is a command in a module |
| 787 local module_name = command:match("^mod_(.+)"); | 1117 local module_name = command:match("^mod_(.+)"); |
| 788 local ret, err = modulemanager.load("*", module_name); | 1118 local ret, err = modulemanager.load("*", module_name); |