changeset 6478:bd785f524fd2

mod_s2s_v6mesh: Fix various bugs, s2s auth policies now work as expected
author Matthew Wild <mwild1@gmail.com>
date Wed, 18 Mar 2026 18:54:30 +0000
parents 88852e4f81bf
children 66f529b0af60
files mod_s2s_v6mesh/README.md mod_s2s_v6mesh/mod_s2s_v6mesh.lua
diffstat 2 files changed, 61 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/mod_s2s_v6mesh/README.md	Tue Mar 17 23:08:39 2026 +0000
+++ b/mod_s2s_v6mesh/README.md	Wed Mar 18 18:54:30 2026 +0000
@@ -49,14 +49,10 @@
 IP address `2a00:1098:3a0::1` but you should replace it with your own):
 
 ```
-$ prosodyctl shell v6alt get_domain localhost 2a00:1098:3a0::1
+$ prosodyctl shell v6alt get_domain 2a00:1098:3a0::1
 OK: Encoded hostname: fiabbgadu-e.v6.alt
 ```
 
-In this command, "localhost" must be an existing domain in your Prosody config
-("localhost" is in the default config), however it's required just to make the
-command work and has no effect on the output.
-
 You can then use this domain name in your config file, for example:
 
 ```lua
@@ -76,14 +72,18 @@
 
 Additionally, if the IP is within one of the CIDR ranges listed in the
 `s2s_v6mesh_secure_ranges` configuration option, then the module will tell
-Prosody to not even perform TLS for the connection. Prosody will treat the
-connections as totally secure, so be very sure you trust the underlying
-network's security layer before adding an IP range to this list. If you want
-similar behaviour for non-`v6.alt` domains, look at [mod_secure_interfaces](https://modules.prosody.im/mod_secure_interfaces)
-and configure it with your mesh interface's address.
+Prosody to disable TLS encryption for the connection.
 
-Because of the above mechanisms in place, it is strongly recommended to keep
-Prosody's default settings of `s2s_require_encryption = true` and
+**Warning:** Prosody will treat connections within any configured ranges as
+already totally secure, and **TLS will be disabled**. Be *very* sure you trust
+the underlying network's security layer before adding an IP range to this
+list, and never add IPs that use the public internet. If you want similar
+behaviour for other (non-`v6.alt`) domains, look at [mod_secure_interfaces](https://modules.prosody.im/mod_secure_interfaces)
+and configure it with your mesh interface's address. If in doubt, just don't
+set any secure ranges, and use self-signed certificates with TLS instead.
+
+Finally, because of the above mechanisms in place, it is strongly recommended
+to keep Prosody's default settings of `s2s_require_encryption = true` and
 `s2s_secure_auth = true` so that connections not covered by this module are
 always encrypted and authenticated properly.
 
@@ -163,4 +163,4 @@
 | `fe80::1`                                 | `72-e.v6.alt`                      |
 | `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` | `77777777777777777777777774.v6.alt`|
 | `1111:0000:0000:1111::1111`               | `ceiqaaaaaairc-rce.v6.alt`         |
-| `1111:0000:0000:0000:1111::1111`          | `ceiq-eiraaaaaaarce.v6.alt`        |
\ No newline at end of file
+| `1111:0000:0000:0000:1111::1111`          | `ceiq-eiraaaaaaarce.v6.alt`        |
--- a/mod_s2s_v6mesh/mod_s2s_v6mesh.lua	Tue Mar 17 23:08:39 2026 +0000
+++ b/mod_s2s_v6mesh/mod_s2s_v6mesh.lua	Wed Mar 18 18:54:30 2026 +0000
@@ -1,5 +1,7 @@
 --% requires: s2sout-pre-connect-event
 
+module:set_global();
+
 local basic_resolver = require "net.resolvers.basic";
 local base32 = module:require("base32");
 local net = require "prosody.util.net";
@@ -36,47 +38,57 @@
 	return net.ntop(raw), raw;
 end
 
-module:hook("s2sout-pre-connect", function(event)
-	local host = event.session.to_host;
-	local dom = host:match("([%w-]+)%.v6%.alt$");
-	if not dom then return; end
+function module.add_host(host_module)
+	host_module:hook("s2sout-pre-connect", function(event)
+		local session = event.session;
+		local host = session.to_host;
+		local dom = host:match("([%w-]+)%.v6%.alt$");
+		if not dom then return; end
 
-	local target_ip = lookup(dom);
-	if not target_ip then
-		return;
-	end
+		local target_ip = lookup(dom);
+		if not target_ip then
+			return;
+		end
 
-	module:log("debug", "Resolved %s to [%s]:%d", target_ip, 5269);
+		host_module:log("debug", "Resolved %s to [%s]:%d", host, target_ip, 5269);
 
-	event.resolver = basic_resolver.new(target_ip, 5269, "tcp", {});
+		event.resolver = basic_resolver.new(target_ip, 5269, "tcp", {});
 
-	if is_secure_range(ip.new_ip(target_ip, "IPv6")) then
-		module:log("debug", "Treating non-TLS connection to %s as secure because it's in a secure IP range", host);
-		event.session.secure = true;
-	end
-end);
+		if is_secure_range(ip.new_ip(target_ip, "IPv6")) then
+			host_module:log("debug", "Treating non-TLS connection to %s as secure because it's in a secure IP range", host);
+			session.secure = true;
+			session.authenticated_remote = true;
+			session.cert_chain_status = "valid";
+			session.cert_identity_status = "valid";
+		end
+	end);
 
-module:hook("s2s-stream-features", function (event)
-	local host = event.origin.from_host;
+	host_module:hook("s2s-stream-features", function (event)
+		local session = event.origin;
+		local host = session.from_host;
 
-	local dom = host:match("([%w-]+)%.v6%.alt$");
-	if not dom then
-		return;
-	end
+		local dom = host:match("([%w-]+)%.v6%.alt$");
+		if not dom then
+			return;
+		end
 
-	local remote_ip = ip.new_ip(event.origin.ip);
+		local remote_ip = ip.new_ip(session.ip);
 
-	if select(2, lookup(dom)) ~= remote_ip.packed then
-		module:log("warn", "Rejecting incoming connection from %s: unexpected IP %s", host, remote_ip);
-		event.origin:close({ condition = "not-authorized", text = "Hostname does not match IP address" });
-		return;
-	end
+		if select(2, lookup(dom)) ~= remote_ip.packed then
+			host_module:log("warn", "Rejecting incoming connection from %s: unexpected IP %s", host, remote_ip);
+			session:close({ condition = "not-authorized", text = "Hostname does not match IP address" });
+			return;
+		end
 
-	if is_secure_range(remote_ip) then
-		module:log("debug", "Treating non-TLS connection from %s as secure because it's in a secure IP range", host);
-		event.origin.secure = true;
-	end
-end, 100);
+		if is_secure_range(remote_ip) then
+			host_module:log("debug", "Treating non-TLS connection from %s as secure because it's in a secure IP range", host);
+			session.secure = true;
+			session.authenticated_remote = true;
+			session.cert_chain_status = "valid";
+			session.cert_identity_status = "valid";
+		end
+	end, 200);
+end
 
 module:hook("s2s-check-certificate", function(event)
 	local session, host = event.session, event.host;
@@ -97,7 +109,7 @@
 	session.cert_chain_status = "valid";
 	session.cert_identity_status = "valid";
 	return true;
-end, 100);
+end, 600);
 
 module:add_item("shell-command", {
 	section = "v6alt";
@@ -105,11 +117,9 @@
 	name = "get_domain";
 	desc = "Convert an IPv6 address to a .v6.alt domain";
 	args = {
-		{ name = "host", type = "string" };
 		{ name = "ip", type = "string" };
 	};
-	host_selector = "host";
-	handler = function(self, host, ip_str) --luacheck: ignore 212/self 212/host
+	handler = function(self, ip_str) --luacheck: ignore 212/self 212/host
 		local user_ip = ip.new_ip(ip_str, "IPv6");
 		if not user_ip then
 			return nil, "Invalid IP address: "..tostring(ip_str);
@@ -135,11 +145,9 @@
 	name = "get_ip";
 	desc = "Convert a .v6.alt domain to an IP address";
 	args = {
-		{ name = "host", type = "string" };
 		{ name = "domain", type = "string" };
 	};
-	host_selector = "host";
-	handler = function(self, host, domain) --luacheck: ignore 212/self 212/host
+	handler = function(self, domain) --luacheck: ignore 212/self 212/host
 		local encoded = domain:match("([%w-]+)%.v6%.alt$");
 		if not encoded then
 			return nil, "Invalid domain: "..domain;
@@ -155,11 +163,9 @@
 	name = "is_secure_range";
 	desc = "Check whether an IP address is considered secure";
 	args = {
-		{ name = "host", type = "string" };
 		{ name = "ip", type = "string" };
 	};
-	host_selector = "host";
-	handler = function(self, host, ip_str) --luacheck: ignore 212/self 212/host
+	handler = function(self, ip_str) --luacheck: ignore 212/self 212/host
 		local user_ip = ip.new_ip(ip_str, "IPv6");
 		if not user_ip then
 			return nil, "Invalid IP address: "..tostring(ip_str);