# HG changeset patch # User Pascal Mathis # Date 1522262861 -7200 # Node ID 504bb330e910fef2529909c01742ee70ea613b2d # Parent 6b01600b9c02a2a9ba5c8985d23533f4c8e96385 mod_net_proxy: Added proxy_trusted_proxies for whitelisting incoming connections diff -r 6b01600b9c02 -r 504bb330e910 mod_net_proxy/README.markdown --- a/mod_net_proxy/README.markdown Wed Mar 28 19:03:10 2018 +0200 +++ b/mod_net_proxy/README.markdown Wed Mar 28 20:47:41 2018 +0200 @@ -37,22 +37,40 @@ ```lua --[[ - Hint: While you can manually override the ports this module is listening on with - the "proxy_ports" directive, it is highly recommended to not set it and instead - only configure the appropriate mappings with "proxy_port_mappings", which will - automatically start listening on all mapped ports. -]]-- - + Maps TCP ports to a specific Prosody network service. Further information about + available service names can be found further down below in the module documentation. +]]-- proxy_port_mappings = { [15222] = "c2s", [15269] = "s2s" } + +--[[ + Specifies a list of trusted hosts or networks which may use the PROXY protocol + If not specified, it will default to: 127.0.0.1, ::1 (local connections only) + An empty table ({}) can be configured to allow connections from any source. + Please read the module documentation about potential security impact. +]]-- +proxy_trusted_proxies = { + "192.168.10.1", + "172.16.0.0/16" +} + +--[[ + While you can manually override the ports this module is listening on with + the "proxy_ports" directive, it is highly recommended to not set it and instead + only configure the appropriate mappings with "proxy_port_mappings", which will + automatically start listening on all mapped ports. + + Example: proxy_ports = { 15222, 15269 } +]]-- ``` The above example configuration, which needs to be placed in the global section, -would listen on both tcp/15222 and tcp/15269. All incoming connections to these ports -have to be initiated by a PROXYv1 or PROXYv2 sender and will get mapped to the -configured service name after initializating the connection. +would listen on both tcp/15222 and tcp/15269. All incoming connections have to +originate from trusted hosts/networks (configured by _proxy_trusted_proxies_) and +must be initiated by a PROXYv1 or PROXYv2 sender. After processing the PROXY +protocol, those connections will get mapped to the configured service name. Please note that each port handled by _mod_net_proxy_ must be mapped to another service name by adding an item to _proxy_port_mappings_, otherwise a warning will diff -r 6b01600b9c02 -r 504bb330e910 mod_net_proxy/mod_net_proxy.lua --- a/mod_net_proxy/mod_net_proxy.lua Wed Mar 28 19:03:10 2018 +0200 +++ b/mod_net_proxy/mod_net_proxy.lua Wed Mar 28 20:47:41 2018 +0200 @@ -67,9 +67,15 @@ }; local PROTO_HANDLER_STATUS = { SUCCESS = 0, POSTPONE = 1, FAILURE = 2 }; +-- Configuration Variables +local config_mappings = module:get_option("proxy_port_mappings", {}); +local config_ports = module:get_option_set("proxy_ports", {}); +local config_trusted_proxies = module:get_option_set("proxy_trusted_proxies", {"127.0.0.1", "::1"}); + -- Persistent In-Memory Storage local sessions = {}; local mappings = {}; +local trusted_networks = set.new(); -- Proxy Data Methods local proxy_data_mt = {}; proxy_data_mt.__index = proxy_data_mt; @@ -314,10 +320,37 @@ return service_listener.onincoming(conn, session.buffer); end +local function is_trusted_proxy(conn) + -- If no trusted proxies were configured, trust any incoming connection + -- While this may seem insecure, the module defaults to only trusting 127.0.0.1 and ::1 + if trusted_networks:empty() then + return true; + end + + -- Iterate through all trusted proxies and check for match against connected IP address + local conn_ip = ip.new_ip(conn:ip()); + for trusted_network in trusted_networks:items() do + if ip.match(trusted_network.ip, conn_ip, trusted_network.cidr) then + return true; + end + end + + -- Connection does not match any trusted proxy + return false; +end + -- Network Listener Methods local listener = {}; function listener.onconnect(conn) + -- Check if connection is coming from a trusted proxy + if not is_trusted_proxy(conn) then + conn:close(); + module:log("warn", "Dropped connection from untrusted proxy: %s", conn:ip()); + return; + end + + -- Initialize session variables sessions[conn] = { handler = nil; buffer = nil; @@ -388,9 +421,19 @@ listener.ondetach = listener.ondisconnect; +-- Parse trusted proxies which can either contain single hosts or networks +if not config_trusted_proxies:empty() then + for trusted_proxy in config_trusted_proxies:items() do + local network = {}; + network.ip, network.cidr = ip.parse_cidr(trusted_proxy); + trusted_networks:add(network); + end +else + module:log("warn", "No trusted proxies configured, all connections will be accepted - this might be dangerous"); +end + -- Process all configured port mappings and generate a list of mapped ports local mapped_ports = {}; -local config_mappings = module:get_option("proxy_port_mappings", {}); for port, mapping in pairs(config_mappings) do table.insert(mapped_ports, port); mappings[port] = { @@ -400,7 +443,6 @@ end -- Log error message when user manually specifies ports without configuring the necessary port mappings -local config_ports = module:get_option_set("proxy_ports", {}); if not config_ports:empty() then local missing_ports = config_ports - set.new(mapped_ports); if not missing_ports:empty() then