Add full WebTransport support
This commit is contained in:
parent
3bdb461142
commit
42aed6cbb2
38
Cargo.lock
generated
38
Cargo.lock
generated
@ -1920,6 +1920,43 @@ version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
|
||||
|
||||
[[package]]
|
||||
name = "webtransport-generic"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webtransport-proto"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webtransport-quinn"
|
||||
version = "0.6.1"
|
||||
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"http",
|
||||
"log",
|
||||
"quinn",
|
||||
"quinn-proto",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"url",
|
||||
"webtransport-generic",
|
||||
"webtransport-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
@ -2088,4 +2125,5 @@ dependencies = [
|
||||
"toml",
|
||||
"trust-dns-resolver",
|
||||
"webpki-roots",
|
||||
"webtransport-quinn",
|
||||
]
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -68,11 +68,14 @@ rustls-pemfile = { version = "1.0", optional = true }
|
||||
tokio-tungstenite = { version = "0.19", optional = true, default-features = false, features = ["handshake"] }
|
||||
futures-util = { version = "0.3", default-features = false, features = ["async-await", "sink", "std"], optional = true }
|
||||
|
||||
# webtransport deps
|
||||
webtransport-quinn = { version = "0.6", optional = true }
|
||||
|
||||
# systemd dep
|
||||
nix = { version = "0.26", optional = true, default-features = false, features = ["socket"]}
|
||||
|
||||
[features]
|
||||
default = ["c2s-incoming", "c2s-outgoing", "s2s-incoming", "s2s-outgoing", "tls", "quic", "websocket", "logging", "tls-ca-roots-native", "systemd"]
|
||||
default = ["c2s-incoming", "c2s-outgoing", "s2s-incoming", "s2s-outgoing", "tls", "quic", "websocket", "webtransport", "logging", "tls-ca-roots-native", "systemd"]
|
||||
|
||||
# you must pick one of these or the other, not both: todo: enable picking both and choosing at runtime
|
||||
# don't need either of these if only doing c2s-incoming
|
||||
@ -97,6 +100,7 @@ s2s-outgoing = ["outgoing", "s2s"]
|
||||
tls = ["tokio-rustls", "webpki", "rustls"]
|
||||
quic = ["quinn", "rustls"]
|
||||
websocket = ["tokio-tungstenite", "futures-util", "tls"] # websocket+incoming also enables incoming TLS support as it's free
|
||||
webtransport = ["webtransport-quinn", "quic"] # webtransport requires quic
|
||||
|
||||
logging = ["rand", "env_logger"]
|
||||
systemd = ["nix"]
|
||||
@ -106,3 +110,8 @@ net-test = []
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0"
|
||||
|
||||
# need this until a release is made with this commit in it
|
||||
[patch.crates-io]
|
||||
webtransport-quinn = { git = "https://github.com/kixelated/webtransport-rs", rev = "ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd" }
|
||||
|
||||
|
20
README.md
20
README.md
@ -11,11 +11,11 @@
|
||||
[![Build Status](https://ci.moparisthe.best/job/moparisthebest/job/xmpp-proxy/job/master/badge/icon%3Fstyle=plastic)](https://ci.moparisthe.best/job/moparisthebest/job/xmpp-proxy/job/master/)
|
||||
|
||||
xmpp-proxy is a reverse proxy and outgoing proxy for XMPP servers and clients, providing [STARTTLS], [Direct TLS], [QUIC],
|
||||
[WebSocket C2S], and [WebSocket S2S] connectivity to plain-text XMPP servers and clients and limiting stanza sizes without an XML parser.
|
||||
[WebSocket C2S], [WebSocket S2S], and [WebTransport] connectivity to plain-text XMPP servers and clients and limiting stanza sizes without an XML parser.
|
||||
|
||||
xmpp-proxy in reverse proxy (incoming) mode will:
|
||||
1. listen on any number of interfaces/ports
|
||||
2. accept any STARTTLS, Direct TLS, QUIC, or WebSocket c2s or s2s connections from the internet
|
||||
2. accept any STARTTLS, Direct TLS, QUIC, WebSocket, or WebTransport c2s or s2s connections from the internet
|
||||
3. terminate TLS
|
||||
4. for s2s require a client cert and validate it correctly (using CAs, host-meta, host-meta2, and POSH) for SASL EXTERNAL auth
|
||||
5. connect them to a local real XMPP server over plain-text TCP
|
||||
@ -25,8 +25,8 @@ xmpp-proxy in reverse proxy (incoming) mode will:
|
||||
xmpp-proxy in outgoing mode will:
|
||||
1. listen on any number of interfaces/ports
|
||||
2. accept any plain-text TCP or WebSocket connection from a local XMPP server or client
|
||||
3. look up the required SRV, [host-meta], host-meta2, and [POSH] records
|
||||
4. connect to a real XMPP server across the internet over STARTTLS, Direct TLS, QUIC, or WebSocket
|
||||
3. look up the required SRV, [host-meta], [host-meta2], and [POSH] records
|
||||
4. connect to a real XMPP server across the internet over STARTTLS, Direct TLS, QUIC, WebSocket, or WebTransport
|
||||
5. fallback to next SRV target or defaults as required to fully connect
|
||||
6. perform all the proper required certificate validation logic
|
||||
7. limit incoming stanza sizes as configured
|
||||
@ -137,10 +137,11 @@ choose between 1-4 directions:
|
||||
3. `s2s-incoming` - enables a server to accept incoming s2s connections
|
||||
4. `s2s-outgoing` - enables a server to make outgoing s2s connections
|
||||
|
||||
choose between 1-3 transport protocols:
|
||||
choose between 1-4 transport protocols:
|
||||
1. `tls` - enables STARTTLS/TLS support
|
||||
2. `quic` - enables QUIC support
|
||||
3. `websocket` - enables WebSocket support, also enables TLS incoming support if the appropriate directions are enabled
|
||||
4. `webtransport` - enables WebTransport support, also enables QUIC
|
||||
|
||||
choose exactly 1 of these methods to get trusted CA roots, not needed if only `c2s-incoming` is enabled:
|
||||
1. `tls-ca-roots-native` - reads CA roots from operating system
|
||||
@ -168,15 +169,18 @@ GNU/AGPLv3 - Check LICENSE.md for details
|
||||
Thanks [rxml](https://github.com/horazont/rxml) for afl-fuzz seeds
|
||||
|
||||
#### Todo
|
||||
1. write "host-meta2" XEP for QUIC and WebSocket S2S Discovery
|
||||
2. optional [systemd](https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html) [integration](https://mgdm.net/weblog/systemd/)
|
||||
3. seamless Tor integration, connecting to and from .onion domains
|
||||
1. seamless Tor integration, connecting to and from .onion domains
|
||||
2. Write WebTransport XEP
|
||||
3. Document systemd activation support
|
||||
4. Document use-as-a-library support
|
||||
|
||||
[STARTTLS]: https://datatracker.ietf.org/doc/html/rfc6120#section-5
|
||||
[Direct TLS]: https://xmpp.org/extensions/xep-0368.html
|
||||
[QUIC]: https://xmpp.org/extensions/xep-0467.html
|
||||
[WebSocket C2S]: https://datatracker.ietf.org/doc/html/rfc7395
|
||||
[WebSocket S2S]: https://xmpp.org/extensions/xep-0468.html
|
||||
[WebTransport]: https://www.w3.org/TR/webtransport/
|
||||
[POSH]: https://datatracker.ietf.org/doc/html/rfc7711
|
||||
[host-meta]: https://xmpp.org/extensions/xep-0156.html
|
||||
[host-meta2]: https://xmpp.org/extensions/inbox/host-meta-2.html
|
||||
[PROXY protocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||||
|
@ -47,7 +47,7 @@ perms_optional() {
|
||||
all_features() {
|
||||
for optional in "" $(perms_optional logging systemd)
|
||||
do
|
||||
for proto in $(perms tls quic websocket)
|
||||
for proto in $(perms tls quic websocket webtransport)
|
||||
do
|
||||
for direction in $(perms c2s-incoming c2s-outgoing s2s-incoming s2s-outgoing)
|
||||
do
|
||||
@ -61,7 +61,7 @@ all_features() {
|
||||
|
||||
for optional in "" $(perms_optional logging systemd)
|
||||
do
|
||||
for proto in $(perms tls quic websocket)
|
||||
for proto in $(perms tls quic websocket webtransport)
|
||||
do
|
||||
echo c2s-incoming,$proto$optional
|
||||
done
|
||||
|
23
integration/29-webtransport-host-meta-json/example.org.zone
Normal file
23
integration/29-webtransport-host-meta-json/example.org.zone
Normal file
@ -0,0 +1,23 @@
|
||||
$TTL 300
|
||||
; example.org
|
||||
@ IN SOA ns1.example.org. postmaster.example.org. (
|
||||
2018111111 ; Serial
|
||||
28800 ; Refresh
|
||||
1800 ; Retry
|
||||
604800 ; Expire - 1 week
|
||||
86400 ) ; Negative Cache TTL
|
||||
IN NS ns1
|
||||
ns1 IN A 192.5.0.10
|
||||
server1 IN A 192.5.0.20
|
||||
server2 IN A 192.5.0.30
|
||||
xp1 IN A 192.5.0.40
|
||||
xp2 IN A 192.5.0.50
|
||||
xp3 IN A 192.5.0.60
|
||||
web1 IN A 192.5.0.70
|
||||
web2 IN A 192.5.0.80
|
||||
|
||||
one IN CNAME web1
|
||||
two IN CNAME web2
|
||||
|
||||
scansion.one IN CNAME xp3
|
||||
scansion.two IN CNAME xp3
|
48
integration/29-webtransport-host-meta-json/nginx1.conf
Normal file
48
integration/29-webtransport-host-meta-json/nginx1.conf
Normal file
@ -0,0 +1,48 @@
|
||||
daemon off;
|
||||
worker_processes 1;
|
||||
error_log stderr;
|
||||
|
||||
events {
|
||||
worker_connections 32;
|
||||
}
|
||||
|
||||
http {
|
||||
access_log /dev/stdout;
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name one.example.org;
|
||||
|
||||
ssl_certificate /etc/prosody/certs/one.example.org.crt;
|
||||
ssl_certificate_key /etc/prosody/certs/one.example.org.key;
|
||||
|
||||
location = /.well-known/host-meta.json {
|
||||
default_type application/json;
|
||||
return 200 '{
|
||||
"links": [
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:s2s-webtransport",
|
||||
"href": "https://xp1.example.org/xmpp-webtransport",
|
||||
"ips": [
|
||||
"192.5.0.40"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp1.example.org"
|
||||
},
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:webtransport",
|
||||
"href": "https://xp1.example.org/xmpp-webtransport",
|
||||
"ips": [
|
||||
"192.5.0.40"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp1.example.org"
|
||||
}
|
||||
]
|
||||
}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
48
integration/29-webtransport-host-meta-json/nginx2.conf
Normal file
48
integration/29-webtransport-host-meta-json/nginx2.conf
Normal file
@ -0,0 +1,48 @@
|
||||
daemon off;
|
||||
worker_processes 1;
|
||||
error_log stderr;
|
||||
|
||||
events {
|
||||
worker_connections 32;
|
||||
}
|
||||
|
||||
http {
|
||||
access_log /dev/stdout;
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name two.example.org;
|
||||
|
||||
ssl_certificate /etc/prosody/certs/two.example.org.crt;
|
||||
ssl_certificate_key /etc/prosody/certs/two.example.org.key;
|
||||
|
||||
location = /.well-known/host-meta.json {
|
||||
default_type application/json;
|
||||
return 200 '{
|
||||
"links": [
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:s2s-webtransport",
|
||||
"href": "https://xp2.example.org/xmpp-webtransport",
|
||||
"ips": [
|
||||
"192.5.0.50"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp2.example.org"
|
||||
},
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:webtransport",
|
||||
"href": "https://xp2.example.org/xmpp-webtransport",
|
||||
"ips": [
|
||||
"192.5.0.50"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp2.example.org"
|
||||
}
|
||||
]
|
||||
}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
251
integration/29-webtransport-host-meta-json/prosody1.cfg.lua
Normal file
251
integration/29-webtransport-host-meta-json/prosody1.cfg.lua
Normal file
@ -0,0 +1,251 @@
|
||||
--Important for systemd
|
||||
-- daemonize is important for systemd. if you set this to false the systemd startup will freeze.
|
||||
daemonize = false
|
||||
run_as_root = true
|
||||
|
||||
pidfile = "/run/prosody/prosody.pid"
|
||||
|
||||
plugin_paths = { "/opt/xmpp-proxy/prosody-modules", "/opt/prosody-modules" }
|
||||
|
||||
-- Prosody Example Configuration File
|
||||
--
|
||||
-- Information on configuring Prosody can be found on our
|
||||
-- website at https://prosody.im/doc/configure
|
||||
--
|
||||
-- Tip: You can check that the syntax of this file is correct
|
||||
-- when you have finished by running this command:
|
||||
-- prosodyctl check config
|
||||
-- If there are any errors, it will let you know what and where
|
||||
-- they are, otherwise it will keep quiet.
|
||||
--
|
||||
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
|
||||
-- blanks. Good luck, and happy Jabbering!
|
||||
|
||||
|
||||
---------- Server-wide settings ----------
|
||||
-- Settings in this section apply to the whole server and are the default settings
|
||||
-- for any virtual hosts
|
||||
|
||||
-- This is a (by default, empty) list of accounts that are admins
|
||||
-- for the server. Note that you must create the accounts separately
|
||||
-- (see https://prosody.im/doc/creating_accounts for info)
|
||||
-- Example: admins = { "user1@example.com", "user2@example.net" }
|
||||
admins = { }
|
||||
|
||||
-- Enable use of libevent for better performance under high load
|
||||
-- For more information see: https://prosody.im/doc/libevent
|
||||
--use_libevent = true
|
||||
|
||||
-- Prosody will always look in its source directory for modules, but
|
||||
-- this option allows you to specify additional locations where Prosody
|
||||
-- will look for modules first. For community modules, see https://modules.prosody.im/
|
||||
--plugin_paths = {}
|
||||
|
||||
-- This is the list of modules Prosody will load on startup.
|
||||
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
|
||||
-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
|
||||
modules_enabled = {
|
||||
|
||||
-- Generally required
|
||||
"roster"; -- Allow users to have a roster. Recommended ;)
|
||||
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
|
||||
--"tls"; -- Add support for secure TLS on c2s/s2s connections
|
||||
--"dialback"; -- s2s dialback support
|
||||
"disco"; -- Service discovery
|
||||
|
||||
-- Not essential, but recommended
|
||||
"carbons"; -- Keep multiple clients in sync
|
||||
"pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
|
||||
"private"; -- Private XML storage (for room bookmarks, etc.)
|
||||
"blocklist"; -- Allow users to block communications with other users
|
||||
"vcard4"; -- User profiles (stored in PEP)
|
||||
"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
|
||||
"limits"; -- Enable bandwidth limiting for XMPP connections
|
||||
|
||||
-- Nice to have
|
||||
"version"; -- Replies to server version requests
|
||||
"uptime"; -- Report how long server has been running
|
||||
"time"; -- Let others know the time here on this server
|
||||
"ping"; -- Replies to XMPP pings with pongs
|
||||
"register"; -- Allow users to register on this server using a client and change passwords
|
||||
--"mam"; -- Store messages in an archive and allow users to access it
|
||||
--"csi_simple"; -- Simple Mobile optimizations
|
||||
|
||||
-- Admin interfaces
|
||||
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
|
||||
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
|
||||
|
||||
-- HTTP modules
|
||||
--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
|
||||
--"websocket"; -- XMPP over WebSockets
|
||||
--"http_files"; -- Serve static files from a directory over HTTP
|
||||
|
||||
-- Other specific functionality
|
||||
--"groups"; -- Shared roster support
|
||||
--"server_contact_info"; -- Publish contact information for this service
|
||||
--"announce"; -- Send announcement to all online users
|
||||
--"welcome"; -- Welcome users who register accounts
|
||||
--"watchregistrations"; -- Alert admins of registrations
|
||||
--"motd"; -- Send a message to users when they log in
|
||||
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
|
||||
--"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
|
||||
"net_proxy";
|
||||
"s2s_outgoing_proxy";
|
||||
}
|
||||
|
||||
-- These modules are auto-loaded, but should you want
|
||||
-- to disable them then uncomment them here:
|
||||
modules_disabled = {
|
||||
-- "offline"; -- Store offline messages
|
||||
-- "c2s"; -- Handle client connections
|
||||
-- "s2s"; -- Handle server-to-server connections
|
||||
-- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
|
||||
}
|
||||
|
||||
-- Disable account creation by default, for security
|
||||
-- For more information see https://prosody.im/doc/creating_accounts
|
||||
allow_registration = false
|
||||
|
||||
-- we don't need prosody doing any encryption, xmpp-proxy does this now
|
||||
-- these are likely set to true somewhere in your file, find them, make them false
|
||||
-- you can also remove all certificates from your config
|
||||
s2s_require_encryption = false
|
||||
s2s_secure_auth = false
|
||||
|
||||
-- xmpp-proxy outgoing is listening on this port, make all outgoing s2s connections directly to here
|
||||
s2s_outgoing_proxy = { "192.5.0.40", 15270 }
|
||||
|
||||
-- handle PROXY protocol on these ports
|
||||
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.5.0.40"
|
||||
}
|
||||
|
||||
-- don't listen on any normal c2s/s2s ports (xmpp-proxy listens on these now)
|
||||
-- you might need to comment these out further down in your config file if you set them
|
||||
c2s_ports = {}
|
||||
legacy_ssl_ports = {}
|
||||
-- you MUST have at least one s2s_ports defined if you want outgoing S2S to work, don't ask..
|
||||
s2s_ports = {15268}
|
||||
|
||||
-- Force clients to use encrypted connections? This option will
|
||||
-- prevent clients from authenticating unless they are using encryption.
|
||||
|
||||
c2s_require_encryption = false
|
||||
allow_unencrypted_plain_auth = true
|
||||
|
||||
-- Some servers have invalid or self-signed certificates. You can list
|
||||
-- remote domains here that will not be required to authenticate using
|
||||
-- certificates. They will be authenticated using DNS instead, even
|
||||
-- when s2s_secure_auth is enabled.
|
||||
|
||||
--s2s_insecure_domains = { "insecure.example" }
|
||||
|
||||
-- Even if you disable s2s_secure_auth, you can still require valid
|
||||
-- certificates for some domains by specifying a list here.
|
||||
|
||||
--s2s_secure_domains = { "jabber.org" }
|
||||
|
||||
-- Enable rate limits for incoming client and server connections
|
||||
|
||||
limits = {
|
||||
c2s = {
|
||||
rate = "10kb/s";
|
||||
};
|
||||
s2sin = {
|
||||
rate = "30kb/s";
|
||||
};
|
||||
}
|
||||
|
||||
-- Select the authentication backend to use. The 'internal' providers
|
||||
-- use Prosody's configured data storage to store the authentication data.
|
||||
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Select the storage backend to use. By default Prosody uses flat files
|
||||
-- in its configured data directory, but it also supports more backends
|
||||
-- through modules. An "sql" backend is included by default, but requires
|
||||
-- additional dependencies. See https://prosody.im/doc/storage for more info.
|
||||
|
||||
--storage = "sql" -- Default is "internal"
|
||||
|
||||
-- For the "sql" backend, you can uncomment *one* of the below to configure:
|
||||
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
|
||||
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
|
||||
|
||||
-- Archiving configuration
|
||||
-- If mod_mam is enabled, Prosody will store a copy of every message. This
|
||||
-- is used to synchronize conversations between multiple clients, even if
|
||||
-- they are offline. This setting controls how long Prosody will keep
|
||||
-- messages in the archive before removing them.
|
||||
|
||||
archive_expires_after = "1w" -- Remove archived messages after 1 week
|
||||
|
||||
-- You can also configure messages to be stored in-memory only. For more
|
||||
-- archiving options, see https://prosody.im/doc/modules/mod_mam
|
||||
|
||||
-- Logging configuration
|
||||
-- For advanced logging see https://prosody.im/doc/logging
|
||||
log = {
|
||||
-- info = "prosody.log"; -- Change 'info' to 'debug' for verbose logging
|
||||
-- error = "prosody.err";
|
||||
--info = "*syslog"; -- Uncomment this for logging to syslog
|
||||
debug = "*console"; -- Log to the console, useful for debugging with daemonize=false
|
||||
}
|
||||
|
||||
-- Uncomment to enable statistics
|
||||
-- For more info see https://prosody.im/doc/statistics
|
||||
-- statistics = "internal"
|
||||
|
||||
-- Certificates
|
||||
-- Every virtual host and component needs a certificate so that clients and
|
||||
-- servers can securely verify its identity. Prosody will automatically load
|
||||
-- certificates/keys from the directory specified here.
|
||||
-- For more information, including how to use 'prosodyctl' to auto-import certificates
|
||||
-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates
|
||||
|
||||
-- Location of directory to find certificates in (relative to main config file):
|
||||
certificates = "certs"
|
||||
|
||||
-- HTTPS currently only supports a single certificate, specify it here:
|
||||
--https_certificate = "/etc/prosody/certs/localhost.crt"
|
||||
|
||||
----------- Virtual hosts -----------
|
||||
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
|
||||
-- Settings under each VirtualHost entry apply *only* to that host.
|
||||
|
||||
VirtualHost "one.example.org"
|
||||
|
||||
--VirtualHost "example.com"
|
||||
-- certificate = "/path/to/example.crt"
|
||||
|
||||
------ Components ------
|
||||
-- You can specify components to add hosts that provide special services,
|
||||
-- like multi-user conferences, and transports.
|
||||
-- For more information on components, see https://prosody.im/doc/components
|
||||
|
||||
---Set up a MUC (multi-user chat) room server on conference.example.com:
|
||||
--Component "conference.example.com" "muc"
|
||||
--- Store MUC messages in an archive and allow users to access it
|
||||
--modules_enabled = { "muc_mam" }
|
||||
|
||||
---Set up an external component (default component port is 5347)
|
||||
--
|
||||
-- External components allow adding various services, such as gateways/
|
||||
-- transports to other networks like ICQ, MSN and Yahoo. For more info
|
||||
-- see: https://prosody.im/doc/components#adding_an_external_component
|
||||
--
|
||||
--Component "gateway.example.com"
|
||||
-- component_secret = "password"
|
251
integration/29-webtransport-host-meta-json/prosody2.cfg.lua
Normal file
251
integration/29-webtransport-host-meta-json/prosody2.cfg.lua
Normal file
@ -0,0 +1,251 @@
|
||||
--Important for systemd
|
||||
-- daemonize is important for systemd. if you set this to false the systemd startup will freeze.
|
||||
daemonize = false
|
||||
run_as_root = true
|
||||
|
||||
pidfile = "/run/prosody/prosody.pid"
|
||||
|
||||
plugin_paths = { "/opt/xmpp-proxy/prosody-modules", "/opt/prosody-modules" }
|
||||
|
||||
-- Prosody Example Configuration File
|
||||
--
|
||||
-- Information on configuring Prosody can be found on our
|
||||
-- website at https://prosody.im/doc/configure
|
||||
--
|
||||
-- Tip: You can check that the syntax of this file is correct
|
||||
-- when you have finished by running this command:
|
||||
-- prosodyctl check config
|
||||
-- If there are any errors, it will let you know what and where
|
||||
-- they are, otherwise it will keep quiet.
|
||||
--
|
||||
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
|
||||
-- blanks. Good luck, and happy Jabbering!
|
||||
|
||||
|
||||
---------- Server-wide settings ----------
|
||||
-- Settings in this section apply to the whole server and are the default settings
|
||||
-- for any virtual hosts
|
||||
|
||||
-- This is a (by default, empty) list of accounts that are admins
|
||||
-- for the server. Note that you must create the accounts separately
|
||||
-- (see https://prosody.im/doc/creating_accounts for info)
|
||||
-- Example: admins = { "user1@example.com", "user2@example.net" }
|
||||
admins = { }
|
||||
|
||||
-- Enable use of libevent for better performance under high load
|
||||
-- For more information see: https://prosody.im/doc/libevent
|
||||
--use_libevent = true
|
||||
|
||||
-- Prosody will always look in its source directory for modules, but
|
||||
-- this option allows you to specify additional locations where Prosody
|
||||
-- will look for modules first. For community modules, see https://modules.prosody.im/
|
||||
--plugin_paths = {}
|
||||
|
||||
-- This is the list of modules Prosody will load on startup.
|
||||
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
|
||||
-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
|
||||
modules_enabled = {
|
||||
|
||||
-- Generally required
|
||||
"roster"; -- Allow users to have a roster. Recommended ;)
|
||||
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
|
||||
--"tls"; -- Add support for secure TLS on c2s/s2s connections
|
||||
--"dialback"; -- s2s dialback support
|
||||
"disco"; -- Service discovery
|
||||
|
||||
-- Not essential, but recommended
|
||||
"carbons"; -- Keep multiple clients in sync
|
||||
"pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
|
||||
"private"; -- Private XML storage (for room bookmarks, etc.)
|
||||
"blocklist"; -- Allow users to block communications with other users
|
||||
"vcard4"; -- User profiles (stored in PEP)
|
||||
"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
|
||||
"limits"; -- Enable bandwidth limiting for XMPP connections
|
||||
|
||||
-- Nice to have
|
||||
"version"; -- Replies to server version requests
|
||||
"uptime"; -- Report how long server has been running
|
||||
"time"; -- Let others know the time here on this server
|
||||
"ping"; -- Replies to XMPP pings with pongs
|
||||
"register"; -- Allow users to register on this server using a client and change passwords
|
||||
--"mam"; -- Store messages in an archive and allow users to access it
|
||||
--"csi_simple"; -- Simple Mobile optimizations
|
||||
|
||||
-- Admin interfaces
|
||||
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
|
||||
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
|
||||
|
||||
-- HTTP modules
|
||||
--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
|
||||
--"websocket"; -- XMPP over WebSockets
|
||||
--"http_files"; -- Serve static files from a directory over HTTP
|
||||
|
||||
-- Other specific functionality
|
||||
--"groups"; -- Shared roster support
|
||||
--"server_contact_info"; -- Publish contact information for this service
|
||||
--"announce"; -- Send announcement to all online users
|
||||
--"welcome"; -- Welcome users who register accounts
|
||||
--"watchregistrations"; -- Alert admins of registrations
|
||||
--"motd"; -- Send a message to users when they log in
|
||||
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
|
||||
--"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
|
||||
"net_proxy";
|
||||
"s2s_outgoing_proxy";
|
||||
}
|
||||
|
||||
-- These modules are auto-loaded, but should you want
|
||||
-- to disable them then uncomment them here:
|
||||
modules_disabled = {
|
||||
-- "offline"; -- Store offline messages
|
||||
-- "c2s"; -- Handle client connections
|
||||
-- "s2s"; -- Handle server-to-server connections
|
||||
-- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
|
||||
}
|
||||
|
||||
-- Disable account creation by default, for security
|
||||
-- For more information see https://prosody.im/doc/creating_accounts
|
||||
allow_registration = false
|
||||
|
||||
-- we don't need prosody doing any encryption, xmpp-proxy does this now
|
||||
-- these are likely set to true somewhere in your file, find them, make them false
|
||||
-- you can also remove all certificates from your config
|
||||
s2s_require_encryption = false
|
||||
s2s_secure_auth = false
|
||||
|
||||
-- xmpp-proxy outgoing is listening on this port, make all outgoing s2s connections directly to here
|
||||
s2s_outgoing_proxy = { "192.5.0.50", 15270 }
|
||||
|
||||
-- handle PROXY protocol on these ports
|
||||
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.5.0.50"
|
||||
}
|
||||
|
||||
-- don't listen on any normal c2s/s2s ports (xmpp-proxy listens on these now)
|
||||
-- you might need to comment these out further down in your config file if you set them
|
||||
c2s_ports = {}
|
||||
legacy_ssl_ports = {}
|
||||
-- you MUST have at least one s2s_ports defined if you want outgoing S2S to work, don't ask..
|
||||
s2s_ports = {15268}
|
||||
|
||||
-- Force clients to use encrypted connections? This option will
|
||||
-- prevent clients from authenticating unless they are using encryption.
|
||||
|
||||
c2s_require_encryption = false
|
||||
allow_unencrypted_plain_auth = true
|
||||
|
||||
-- Some servers have invalid or self-signed certificates. You can list
|
||||
-- remote domains here that will not be required to authenticate using
|
||||
-- certificates. They will be authenticated using DNS instead, even
|
||||
-- when s2s_secure_auth is enabled.
|
||||
|
||||
--s2s_insecure_domains = { "insecure.example" }
|
||||
|
||||
-- Even if you disable s2s_secure_auth, you can still require valid
|
||||
-- certificates for some domains by specifying a list here.
|
||||
|
||||
--s2s_secure_domains = { "jabber.org" }
|
||||
|
||||
-- Enable rate limits for incoming client and server connections
|
||||
|
||||
limits = {
|
||||
c2s = {
|
||||
rate = "10kb/s";
|
||||
};
|
||||
s2sin = {
|
||||
rate = "30kb/s";
|
||||
};
|
||||
}
|
||||
|
||||
-- Select the authentication backend to use. The 'internal' providers
|
||||
-- use Prosody's configured data storage to store the authentication data.
|
||||
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Select the storage backend to use. By default Prosody uses flat files
|
||||
-- in its configured data directory, but it also supports more backends
|
||||
-- through modules. An "sql" backend is included by default, but requires
|
||||
-- additional dependencies. See https://prosody.im/doc/storage for more info.
|
||||
|
||||
--storage = "sql" -- Default is "internal"
|
||||
|
||||
-- For the "sql" backend, you can uncomment *one* of the below to configure:
|
||||
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
|
||||
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
|
||||
|
||||
|
||||
-- Archiving configuration
|
||||
-- If mod_mam is enabled, Prosody will store a copy of every message. This
|
||||
-- is used to synchronize conversations between multiple clients, even if
|
||||
-- they are offline. This setting controls how long Prosody will keep
|
||||
-- messages in the archive before removing them.
|
||||
|
||||
archive_expires_after = "1w" -- Remove archived messages after 1 week
|
||||
|
||||
-- You can also configure messages to be stored in-memory only. For more
|
||||
-- archiving options, see https://prosody.im/doc/modules/mod_mam
|
||||
|
||||
-- Logging configuration
|
||||
-- For advanced logging see https://prosody.im/doc/logging
|
||||
log = {
|
||||
-- info = "prosody.log"; -- Change 'info' to 'debug' for verbose logging
|
||||
-- error = "prosody.err";
|
||||
--info = "*syslog"; -- Uncomment this for logging to syslog
|
||||
debug = "*console"; -- Log to the console, useful for debugging with daemonize=false
|
||||
}
|
||||
|
||||
-- Uncomment to enable statistics
|
||||
-- For more info see https://prosody.im/doc/statistics
|
||||
-- statistics = "internal"
|
||||
|
||||
-- Certificates
|
||||
-- Every virtual host and component needs a certificate so that clients and
|
||||
-- servers can securely verify its identity. Prosody will automatically load
|
||||
-- certificates/keys from the directory specified here.
|
||||
-- For more information, including how to use 'prosodyctl' to auto-import certificates
|
||||
-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates
|
||||
|
||||
-- Location of directory to find certificates in (relative to main config file):
|
||||
certificates = "certs"
|
||||
|
||||
-- HTTPS currently only supports a single certificate, specify it here:
|
||||
--https_certificate = "/etc/prosody/certs/localhost.crt"
|
||||
|
||||
----------- Virtual hosts -----------
|
||||
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
|
||||
-- Settings under each VirtualHost entry apply *only* to that host.
|
||||
|
||||
VirtualHost "two.example.org"
|
||||
|
||||
--VirtualHost "example.com"
|
||||
-- certificate = "/path/to/example.crt"
|
||||
|
||||
------ Components ------
|
||||
-- You can specify components to add hosts that provide special services,
|
||||
-- like multi-user conferences, and transports.
|
||||
-- For more information on components, see https://prosody.im/doc/components
|
||||
|
||||
---Set up a MUC (multi-user chat) room server on conference.example.com:
|
||||
--Component "conference.example.com" "muc"
|
||||
--- Store MUC messages in an archive and allow users to access it
|
||||
--modules_enabled = { "muc_mam" }
|
||||
|
||||
---Set up an external component (default component port is 5347)
|
||||
--
|
||||
-- External components allow adding various services, such as gateways/
|
||||
-- transports to other networks like ICQ, MSN and Yahoo. For more info
|
||||
-- see: https://prosody.im/doc/components#adding_an_external_component
|
||||
--
|
||||
--Component "gateway.example.com"
|
||||
-- component_secret = "password"
|
42
integration/29-webtransport-host-meta-json/xmpp-proxy1.toml
Normal file
42
integration/29-webtransport-host-meta-json/xmpp-proxy1.toml
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
|
||||
# incoming_listen = [ "0.0.0.0:5281" ]
|
||||
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
|
||||
quic_listen = [ "0.0.0.0:443" ]
|
||||
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
|
||||
outgoing_listen = [ "0.0.0.0:15270" ]
|
||||
|
||||
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
|
||||
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
|
||||
|
||||
# c2s port backend XMPP server listens on
|
||||
c2s_target = "192.5.0.20:15222"
|
||||
|
||||
# s2s port backend XMPP server listens on
|
||||
s2s_target = "192.5.0.20:15269"
|
||||
|
||||
# send PROXYv1 header to backend XMPP server
|
||||
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||||
# prosody module: https://modules.prosody.im/mod_net_proxy.html
|
||||
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
|
||||
proxy = true
|
||||
|
||||
# limit incoming stanzas to this many bytes, default to ejabberd's default
|
||||
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
|
||||
# xmpp-proxy will use this many bytes + 16k per connection
|
||||
max_stanza_size_bytes = 262_144
|
||||
|
||||
# TLS key/certificate valid for all your XMPP domains, PEM format
|
||||
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
|
||||
tls_key = "/etc/prosody/certs/xp1.example.org.key"
|
||||
tls_cert = "/etc/prosody/certs/xp1.example.org.crt"
|
||||
|
||||
# configure logging, defaults are commented
|
||||
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
|
||||
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
|
||||
#log_level = "info"
|
||||
# for development/debugging:
|
||||
log_level = "info,xmpp_proxy=trace"
|
||||
|
||||
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
|
||||
#log_style = "never"
|
42
integration/29-webtransport-host-meta-json/xmpp-proxy2.toml
Normal file
42
integration/29-webtransport-host-meta-json/xmpp-proxy2.toml
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
|
||||
# incoming_listen = [ "0.0.0.0:5281" ]
|
||||
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
|
||||
quic_listen = [ "0.0.0.0:443" ]
|
||||
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
|
||||
outgoing_listen = [ "0.0.0.0:15270" ]
|
||||
|
||||
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
|
||||
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
|
||||
|
||||
# c2s port backend XMPP server listens on
|
||||
c2s_target = "192.5.0.30:15222"
|
||||
|
||||
# s2s port backend XMPP server listens on
|
||||
s2s_target = "192.5.0.30:15269"
|
||||
|
||||
# send PROXYv1 header to backend XMPP server
|
||||
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||||
# prosody module: https://modules.prosody.im/mod_net_proxy.html
|
||||
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
|
||||
proxy = true
|
||||
|
||||
# limit incoming stanzas to this many bytes, default to ejabberd's default
|
||||
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
|
||||
# xmpp-proxy will use this many bytes + 16k per connection
|
||||
max_stanza_size_bytes = 262_144
|
||||
|
||||
# TLS key/certificate valid for all your XMPP domains, PEM format
|
||||
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
|
||||
tls_key = "/etc/prosody/certs/xp2.example.org.key"
|
||||
tls_cert = "/etc/prosody/certs/xp2.example.org.crt"
|
||||
|
||||
# configure logging, defaults are commented
|
||||
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
|
||||
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
|
||||
#log_level = "info"
|
||||
# for development/debugging:
|
||||
log_level = "info,xmpp_proxy=trace"
|
||||
|
||||
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
|
||||
#log_style = "never"
|
44
integration/29-webtransport-host-meta-json/xmpp-proxy3.toml
Normal file
44
integration/29-webtransport-host-meta-json/xmpp-proxy3.toml
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
|
||||
incoming_listen = [ ]
|
||||
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
|
||||
quic_listen = [ ]
|
||||
# interfaces to listen for reverse proxy TLS WebSocket (wss) XMPP connections on, should be open to the internet
|
||||
websocket_listen = [ ]
|
||||
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
|
||||
outgoing_listen = [ "0.0.0.0:5222" ]
|
||||
|
||||
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
|
||||
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
|
||||
|
||||
# c2s port backend XMPP server listens on
|
||||
c2s_target = "127.0.0.1:15222"
|
||||
|
||||
# s2s port backend XMPP server listens on
|
||||
s2s_target = "127.0.0.1:15269"
|
||||
|
||||
# send PROXYv1 header to backend XMPP server
|
||||
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||||
# prosody module: https://modules.prosody.im/mod_net_proxy.html
|
||||
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
|
||||
proxy = true
|
||||
|
||||
# limit incoming stanzas to this many bytes, default to ejabberd's default
|
||||
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
|
||||
# xmpp-proxy will use this many bytes + 16k per connection
|
||||
max_stanza_size_bytes = 262_144
|
||||
|
||||
# TLS key/certificate valid for all your XMPP domains, PEM format
|
||||
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
|
||||
tls_key = "/etc/certs/rsa/one.example.org.key"
|
||||
tls_cert = "/etc/certs/rsa/one.example.org.crt"
|
||||
|
||||
# configure logging, defaults are commented
|
||||
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
|
||||
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
|
||||
#log_level = "info"
|
||||
# for development/debugging:
|
||||
log_level = "info,xmpp_proxy=trace"
|
||||
|
||||
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
|
||||
#log_style = "never"
|
@ -39,24 +39,35 @@ pub fn server_config(certs_key: Arc<CertsKey>) -> Result<ServerConfig> {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "s2s-incoming"))]
|
||||
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
|
||||
pub type ServerCerts = ();
|
||||
|
||||
#[cfg(feature = "s2s-incoming")]
|
||||
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
|
||||
#[derive(Clone)]
|
||||
pub enum ServerCerts {
|
||||
Tls(&'static ServerConnection),
|
||||
#[cfg(feature = "quic")]
|
||||
Quic(Arc<quinn::Connection>),
|
||||
Quic(Option<Vec<Certificate>>, Option<String>, Option<Vec<u8>>), // todo: wrap this in arc or something now
|
||||
}
|
||||
|
||||
#[cfg(feature = "s2s-incoming")]
|
||||
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
|
||||
impl ServerCerts {
|
||||
#[cfg(feature = "quic")]
|
||||
pub fn quic(conn: &quinn::Connection) -> ServerCerts {
|
||||
let certs = conn.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec());
|
||||
let (sni, alpn) = conn
|
||||
.handshake_data()
|
||||
.and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
|
||||
.map(|h| (h.server_name, h.protocol))
|
||||
.unwrap_or_default();
|
||||
ServerCerts::Quic(certs, sni, alpn)
|
||||
}
|
||||
|
||||
pub fn peer_certificates(&self) -> Option<Vec<Certificate>> {
|
||||
match self {
|
||||
ServerCerts::Tls(c) => c.peer_certificates().map(|c| c.to_vec()),
|
||||
#[cfg(feature = "quic")]
|
||||
ServerCerts::Quic(c) => c.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec()),
|
||||
ServerCerts::Quic(certs, _, _) => certs.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +75,7 @@ impl ServerCerts {
|
||||
match self {
|
||||
ServerCerts::Tls(c) => c.server_name().map(|s| s.to_string()),
|
||||
#[cfg(feature = "quic")]
|
||||
ServerCerts::Quic(c) => c.handshake_data().and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok()).and_then(|h| h.server_name),
|
||||
ServerCerts::Quic(_, sni, _) => sni.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +83,7 @@ impl ServerCerts {
|
||||
match self {
|
||||
ServerCerts::Tls(c) => c.alpn_protocol().map(|s| s.to_vec()),
|
||||
#[cfg(feature = "quic")]
|
||||
ServerCerts::Quic(c) => c.handshake_data().and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok()).and_then(|h| h.protocol),
|
||||
ServerCerts::Quic(_, _, alpn) => alpn.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +91,7 @@ impl ServerCerts {
|
||||
match self {
|
||||
ServerCerts::Tls(_) => true,
|
||||
#[cfg(feature = "quic")]
|
||||
ServerCerts::Quic(_) => false,
|
||||
ServerCerts::Quic(_, _, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,13 @@ impl OutgoingConfig {
|
||||
_ => ClientConfig::builder().with_safe_defaults().with_custom_certificate_verifier(cert_verifier).with_no_client_auth(),
|
||||
};
|
||||
|
||||
#[cfg(feature = "webtransport")]
|
||||
let config_webtransport_alpn = {
|
||||
let mut config = config.clone();
|
||||
config.alpn_protocols.push(webtransport_quinn::ALPN.to_vec());
|
||||
Arc::new(config)
|
||||
};
|
||||
|
||||
let mut config_alpn = config.clone();
|
||||
config_alpn.alpn_protocols.push(if is_c2s { ALPN_XMPP_CLIENT } else { ALPN_XMPP_SERVER }.to_vec());
|
||||
|
||||
@ -30,6 +37,8 @@ impl OutgoingConfig {
|
||||
|
||||
OutgoingVerifierConfig {
|
||||
max_stanza_size_bytes: self.max_stanza_size_bytes,
|
||||
#[cfg(feature = "webtransport")]
|
||||
config_webtransport_alpn,
|
||||
config_alpn,
|
||||
connector_alpn,
|
||||
connector,
|
||||
@ -41,6 +50,9 @@ impl OutgoingConfig {
|
||||
pub struct OutgoingVerifierConfig {
|
||||
pub max_stanza_size_bytes: usize,
|
||||
|
||||
#[cfg(feature = "webtransport")]
|
||||
pub config_webtransport_alpn: Arc<ClientConfig>,
|
||||
|
||||
pub config_alpn: Arc<ClientConfig>,
|
||||
pub connector_alpn: TlsConnector,
|
||||
|
||||
|
@ -17,6 +17,9 @@ pub mod srv;
|
||||
#[cfg(feature = "websocket")]
|
||||
pub mod websocket;
|
||||
|
||||
#[cfg(feature = "webtransport")]
|
||||
pub mod webtransport;
|
||||
|
||||
#[cfg(any(feature = "s2s-incoming", feature = "outgoing"))]
|
||||
pub mod verify;
|
||||
|
||||
|
@ -36,26 +36,21 @@ fn internal_spawn_quic_listener(incoming: Endpoint, local_addr: SocketAddr, conf
|
||||
tokio::spawn(async move {
|
||||
if let Ok(new_conn) = incoming_conn.await {
|
||||
let client_addr = Context::new("quic-in", new_conn.remote_address());
|
||||
|
||||
let new_conn = Arc::new(new_conn);
|
||||
#[cfg(feature = "s2s-incoming")]
|
||||
let server_certs = ServerCerts::Quic(new_conn.clone());
|
||||
#[cfg(not(feature = "s2s-incoming"))]
|
||||
let server_certs = ();
|
||||
|
||||
info!("{} connected new connection", client_addr.log_from());
|
||||
|
||||
while let Ok((wrt, rd)) = new_conn.accept_bi().await {
|
||||
let config = config.clone();
|
||||
let mut client_addr = client_addr.clone();
|
||||
let server_certs = server_certs.clone();
|
||||
info!("{} connected new stream", client_addr.log_from());
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
|
||||
error!("{} {}", client_addr.log_from(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
|
||||
let server_certs = {
|
||||
let server_certs = ServerCerts::quic(&new_conn);
|
||||
#[cfg(feature = "webtransport")]
|
||||
if server_certs.alpn().map(|a| a == webtransport_quinn::ALPN).unwrap_or(false) {
|
||||
return crate::webtransport::incoming::handle_webtransport_session(new_conn, config, server_certs, local_addr, client_addr).await;
|
||||
}
|
||||
server_certs
|
||||
};
|
||||
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
|
||||
let server_certs = ();
|
||||
|
||||
handle_quic_session(new_conn, config, server_certs, local_addr, client_addr).await
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -64,7 +59,23 @@ fn internal_spawn_quic_listener(incoming: Endpoint, local_addr: SocketAddr, conf
|
||||
})
|
||||
}
|
||||
|
||||
pub fn quic_server_config(server_config: rustls::ServerConfig) -> ServerConfig {
|
||||
pub async fn handle_quic_session(conn: quinn::Connection, config: Arc<IncomingConfig>, server_certs: ServerCerts, local_addr: SocketAddr, client_addr: Context<'static>) {
|
||||
while let Ok((wrt, rd)) = conn.accept_bi().await {
|
||||
let config = config.clone();
|
||||
let mut client_addr = client_addr.clone();
|
||||
let server_certs = server_certs.clone();
|
||||
info!("{} connected new stream", client_addr.log_from());
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
|
||||
error!("{} {}", client_addr.log_from(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn quic_server_config(mut server_config: rustls::ServerConfig) -> ServerConfig {
|
||||
#[cfg(feature = "webtransport")]
|
||||
server_config.alpn_protocols.push(webtransport_quinn::ALPN.to_vec());
|
||||
let transport_config = quinn::TransportConfig::default();
|
||||
// todo: configure transport_config here if needed
|
||||
let mut server_config = ServerConfig::with_crypto(Arc::new(server_config));
|
||||
|
76
src/srv.rs
76
src/srv.rs
@ -13,7 +13,7 @@ use crate::{
|
||||
use anyhow::{bail, Result};
|
||||
use data_encoding::BASE64;
|
||||
use log::{debug, error, trace};
|
||||
use reqwest::Client;
|
||||
use reqwest::{Client, Url};
|
||||
use ring::digest::{Algorithm, Context as DigestContext, SHA256, SHA512};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
@ -60,7 +60,10 @@ enum XmppConnectionType {
|
||||
#[cfg(feature = "quic")]
|
||||
QUIC,
|
||||
#[cfg(feature = "websocket")]
|
||||
// uri, origin
|
||||
WebSocket(Uri, String),
|
||||
#[cfg(feature = "webtransport")]
|
||||
WebTransport(Url),
|
||||
}
|
||||
|
||||
impl XmppConnectionType {
|
||||
@ -69,11 +72,13 @@ impl XmppConnectionType {
|
||||
#[cfg(feature = "quic")]
|
||||
XmppConnectionType::QUIC => 0,
|
||||
#[cfg(feature = "tls")]
|
||||
XmppConnectionType::DirectTLS => 1,
|
||||
XmppConnectionType::DirectTLS => 2,
|
||||
#[cfg(feature = "tls")]
|
||||
XmppConnectionType::StartTLS => 2,
|
||||
XmppConnectionType::StartTLS => 3,
|
||||
#[cfg(feature = "websocket")]
|
||||
XmppConnectionType::WebSocket(_, _) => 3,
|
||||
XmppConnectionType::WebSocket(_, _) => 4,
|
||||
#[cfg(feature = "webtransport")]
|
||||
XmppConnectionType::WebTransport(_) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,7 +89,7 @@ impl Ord for XmppConnectionType {
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// so they are the same type, but WebSocket is a special case...
|
||||
// so they are the same type, but WebSocket and WebTransport is a special case...
|
||||
match (self, other) {
|
||||
#[cfg(feature = "websocket")]
|
||||
(XmppConnectionType::WebSocket(self_uri, self_origin), XmppConnectionType::WebSocket(other_uri, other_origin)) => {
|
||||
@ -94,6 +99,8 @@ impl Ord for XmppConnectionType {
|
||||
}
|
||||
self_origin.cmp(other_origin)
|
||||
}
|
||||
#[cfg(feature = "webtransport")]
|
||||
(XmppConnectionType::WebTransport(self_url), XmppConnectionType::WebTransport(other_url)) => self_url.cmp(other_url),
|
||||
(_, _) => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
@ -237,6 +244,20 @@ impl XmppConnection {
|
||||
}
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "webtransport")]
|
||||
XmppConnectionType::WebTransport(ref url) => match crate::webtransport::outgoing::webtransport_connect(to_addr, domain, url, config).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "webtransport-out")),
|
||||
Err(e) => {
|
||||
if self.secure && self.target != orig_domain {
|
||||
match crate::webtransport::outgoing::webtransport_connect(to_addr, orig_domain, url, config).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "webtransport-out")),
|
||||
Err(e2) => error!("webtransport connection failed to IP {} from URL {}, error try 1: {}, error try 2: {}", to_addr, url, e, e2),
|
||||
}
|
||||
} else {
|
||||
error!("websocket connection failed to IP {} from URL {}, error: {}", to_addr, url, e)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
bail!("cannot connect to any IPs for SRV: {}", self.target)
|
||||
@ -301,6 +322,21 @@ fn wss_to_srv(url: &str, secure: bool) -> Option<XmppConnection> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "webtransport")]
|
||||
fn wt_to_srv(url: &str) -> Option<(XmppConnectionType, u16)> {
|
||||
let url = match Url::parse(url) {
|
||||
Ok(url) => url,
|
||||
Err(e) => {
|
||||
debug!("invalid URL record '{}': {}", url, e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let port = if let Some(port) = url.port() { port } else { 443 };
|
||||
|
||||
Some((XmppConnectionType::WebTransport(url), port))
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
fn collect_txts(ret: &mut Vec<XmppConnection>, txt_records: std::result::Result<TxtLookup, ResolveError>, is_c2s: bool) {
|
||||
if let Ok(txt_records) = txt_records {
|
||||
@ -534,6 +570,12 @@ enum Link {
|
||||
#[serde(flatten)]
|
||||
link: Option<LinkCommon>,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:webtransport")]
|
||||
WebTransport {
|
||||
href: String,
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:tls")]
|
||||
DirectTLS {
|
||||
#[serde(flatten)]
|
||||
@ -546,6 +588,12 @@ enum Link {
|
||||
link: LinkCommon,
|
||||
port: u16,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:s2s-webtransport")]
|
||||
S2SWebTransport {
|
||||
href: String,
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:s2s-websocket")]
|
||||
S2SWebSocket {
|
||||
href: String,
|
||||
@ -630,6 +678,24 @@ impl Link {
|
||||
None
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "webtransport")]
|
||||
Link::WebTransport { href, link } => {
|
||||
return if is_c2s {
|
||||
let (conn_type, port) = wt_to_srv(&href)?;
|
||||
link.into_xmpp_connection(conn_type, port)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "webtransport")]
|
||||
Link::S2SWebTransport { href, link } => {
|
||||
return if !is_c2s {
|
||||
let (conn_type, port) = wt_to_srv(&href)?;
|
||||
link.into_xmpp_connection(conn_type, port)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
|
@ -127,12 +127,12 @@ pub async fn handle_tls_connection<S: AsyncReadWritePeekSplit>(mut stream: S, cl
|
||||
// where we read the first stanza, where we are guaranteed the handshake is complete, but I can't
|
||||
// do that without ignoring the lifetime and just pulling a C programmer and pinky promising to be
|
||||
// *very careful* that this reference doesn't outlive stream...
|
||||
#[cfg(feature = "s2s-incoming")]
|
||||
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
|
||||
let server_certs = {
|
||||
let server_connection: &'static ServerConnection = unsafe { std::mem::transmute(server_connection) };
|
||||
ServerCerts::Tls(server_connection)
|
||||
};
|
||||
#[cfg(not(feature = "s2s-incoming"))]
|
||||
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
|
||||
let server_certs = ();
|
||||
|
||||
#[cfg(not(feature = "websocket"))]
|
||||
|
44
src/webtransport/incoming.rs
Normal file
44
src/webtransport/incoming.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::{
|
||||
common::incoming::{shuffle_rd_wr, IncomingConfig, ServerCerts},
|
||||
context::Context,
|
||||
in_out::{StanzaRead, StanzaWrite},
|
||||
};
|
||||
|
||||
use log::{error, info};
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
pub async fn handle_webtransport_session(conn: quinn::Connection, config: Arc<IncomingConfig>, server_certs: ServerCerts, local_addr: SocketAddr, mut client_addr: Context<'static>) {
|
||||
client_addr.set_proto("webtransport-in");
|
||||
|
||||
// Perform the WebTransport handshake.
|
||||
let request = match webtransport_quinn::accept(conn).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("{} {}", client_addr.log_from(), e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
info!("{} received request URL: {}", client_addr.log_from(), request.url());
|
||||
|
||||
// Accept the session.
|
||||
let session = match request.ok().await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("{} {}", client_addr.log_from(), e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
info!("{} connected new session", client_addr.log_from());
|
||||
|
||||
while let Ok((wrt, rd)) = session.accept_bi().await {
|
||||
let config = config.clone();
|
||||
let mut client_addr = client_addr.clone();
|
||||
let server_certs = server_certs.clone();
|
||||
info!("{} connected new stream", client_addr.log_from());
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
|
||||
error!("{} {}", client_addr.log_from(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
5
src/webtransport/mod.rs
Normal file
5
src/webtransport/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[cfg(feature = "incoming")]
|
||||
pub mod incoming;
|
||||
|
||||
#[cfg(feature = "outgoing")]
|
||||
pub mod outgoing;
|
26
src/webtransport/outgoing.rs
Normal file
26
src/webtransport/outgoing.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use crate::{
|
||||
common::outgoing::OutgoingVerifierConfig,
|
||||
in_out::{StanzaRead, StanzaWrite},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use log::trace;
|
||||
use reqwest::Url;
|
||||
|
||||
pub async fn webtransport_connect(target: SocketAddr, server_name: &str, url: &Url, config: &OutgoingVerifierConfig) -> Result<(StanzaWrite, StanzaRead)> {
|
||||
let bind_addr = "0.0.0.0:0".parse().unwrap();
|
||||
|
||||
let mut endpoint = quinn::Endpoint::client(bind_addr)?;
|
||||
endpoint.set_default_client_config(quinn::ClientConfig::new(config.config_webtransport_alpn.clone()));
|
||||
|
||||
// connect to server
|
||||
let connection = endpoint.connect(target, server_name)?.await?;
|
||||
trace!("quic pre-wt connected: addr={}", connection.remote_address());
|
||||
|
||||
let connection = webtransport_quinn::connect_with(connection, url).await?;
|
||||
trace!("webtransport connected: addr={}", connection.remote_address());
|
||||
|
||||
let (wrt, rd) = connection.open_bi().await?;
|
||||
Ok((StanzaWrite::new(wrt), StanzaRead::new(rd)))
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
# interfaces to listen for reverse proxy STARTTLS/Direct TLS/TLS WebSocket (wss) XMPP connections on, should be open to the internet
|
||||
incoming_listen = [ "0.0.0.0:5222", "0.0.0.0:5269", "0.0.0.0:443" ]
|
||||
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
|
||||
# interfaces to listen for reverse proxy QUIC/WebTransport XMPP connections on, should be open to the internet
|
||||
quic_listen = [ "0.0.0.0:443" ]
|
||||
# interfaces to listen for outgoing proxy TCP or WebSocket XMPP connections on, should be localhost or a path for a unix socket
|
||||
outgoing_listen = [ "127.0.0.1:15270" ]
|
||||
@ -39,4 +39,4 @@ tls_cert = "/etc/xmpp-proxy/fullchain.cer"
|
||||
#log_level = "info,xmpp_proxy=trace"
|
||||
|
||||
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
|
||||
#log_style = "never"
|
||||
#log_style = "never"
|
||||
|
Loading…
Reference in New Issue
Block a user