--- 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
--- 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