Mercurial > prosody-modules
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);
