Document new QUIC and outgoing support
This commit is contained in:
parent
8e612cf02e
commit
62a97d3721
@ -3,7 +3,7 @@ name = "xmpp-proxy"
|
||||
version = "1.0.0"
|
||||
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
||||
|
||||
description = "Reverse XMPP proxy."
|
||||
description = "XMPP reverse proxy and outgoing proxy"
|
||||
repository = "https://code.moparisthebest.com/moparisthebest/xmpp-proxy"
|
||||
keywords = ["xmpp", "proxy"]
|
||||
|
||||
@ -49,5 +49,5 @@ default = ["incoming", "outgoing", "quic"]
|
||||
#default = ["outgoing"]
|
||||
#default = ["incoming"]
|
||||
incoming = ["tokio-rustls"]
|
||||
outgoing = ["trust-dns-resolver", "webpki-roots", "lazy_static", "tokio-rustls"]
|
||||
outgoing = ["tokio-rustls", "trust-dns-resolver", "webpki-roots", "lazy_static"]
|
||||
quic = ["quinn"]
|
||||
|
93
README.md
93
README.md
@ -2,11 +2,27 @@
|
||||
|
||||
[![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 for XMPP servers, providing STARTTLS and TLS over plain-text XMPP connections
|
||||
and limiting stanza sizes without an XML parser.
|
||||
xmpp-proxy is a reverse proxy and outgoing proxy for XMPP servers and clients, providing STARTTLS,
|
||||
[Direct TLS](https://xmpp.org/extensions/xep-0368.html), and [QUIC](https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport)
|
||||
connectivity to plain-text XMPP servers and clients and limiting stanza sizes without an XML parser.
|
||||
|
||||
xmpp-proxy will listen on any number of interfaces/ports and accept any STARTTLS or [Direct TLS](https://xmpp.org/extensions/xep-0368.html)
|
||||
c2s or s2s connections, terminate TLS, and connect them to a real XMPP server, limiting stanza sizes as configured.
|
||||
xmpp-proxy in reverse proxy (incoming) mode will:
|
||||
1. listen on any number of interfaces/ports
|
||||
2. accept any STARTTLS, Direct TLS, or QUIC c2s or s2s connections from the internet
|
||||
3. terminate TLS
|
||||
4. connect them to a local real XMPP server over plain-text TCP
|
||||
5. send the [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) v1 header if configured, so the
|
||||
XMPP server knows the real client IP
|
||||
6. limit incoming stanza sizes as configured
|
||||
|
||||
xmpp-proxy in outgoing mode will:
|
||||
1. listen on any number of interfaces/ports
|
||||
2. accept any plain-text TCP connection from a local XMPP server or client
|
||||
3. look up the required SRV records
|
||||
4. connect to a real XMPP server across the internet over STARTTLS, Direct TLS, or QUIC
|
||||
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
|
||||
|
||||
#### Installation
|
||||
* `cargo install xmpp-proxy`
|
||||
@ -25,10 +41,59 @@ c2s or s2s connections, terminate TLS, and connect them to a real XMPP server, l
|
||||
|
||||
#### How do I adapt my running Prosody config to use this instead?
|
||||
|
||||
Add these to modules_enabled:
|
||||
You have 2 options here, use xmpp-proxy as only a reverse proxy, or as both reverse and outgoing proxy, I'll cover both:
|
||||
|
||||
###### Reverse proxy and outgoing proxy
|
||||
|
||||
In this mode both prosody doesn't need to do any TLS at all, so it needs no certs. xmpp-proxy need proper TLS
|
||||
certificates, move prosody's TLS key to `/etc/xmpp-proxy/le.key` and TLS cert to `/etc/xmpp-proxy/fullchain.cer`, and
|
||||
use the provided `xmpp-proxy.toml` configuration as-is.
|
||||
|
||||
Edit `/etc/prosody/prosody.cfg.lua`, Add these to modules_enabled:
|
||||
```
|
||||
"secure_interfaces";
|
||||
"net_proxy";
|
||||
"s2s_outgoing_proxy";
|
||||
```
|
||||
Until prosody-modules is updated, use my new module [mod_s2s_outgoing_proxy.lua](https://www.moparisthebest.com/mod_s2s_outgoing_proxy.lua).
|
||||
|
||||
Add this config:
|
||||
```
|
||||
-- only need to listen on localhost
|
||||
interfaces = { "127.0.0.1" }
|
||||
|
||||
-- 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
|
||||
c2s_require_encryption = false
|
||||
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 = { "127.0.0.1", 15270 }
|
||||
|
||||
-- handle PROXY protocol on these ports
|
||||
proxy_port_mappings = {
|
||||
[15222] = "c2s",
|
||||
[15269] = "s2s"
|
||||
}
|
||||
|
||||
-- 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}
|
||||
```
|
||||
|
||||
###### Reverse proxy only, prosody makes outgoing connections directly itself
|
||||
|
||||
In this mode both prosody and xmpp-proxy need proper TLS certificates, copy prosody's TLS key to `/etc/xmpp-proxy/le.key`
|
||||
and TLS cert to `/etc/xmpp-proxy/fullchain.cer`, and use the provided `xmpp-proxy.toml` configuration as-is.
|
||||
|
||||
Edit `/etc/prosody/prosody.cfg.lua`, Add these to modules_enabled:
|
||||
```
|
||||
"net_proxy";
|
||||
"secure_interfaces";
|
||||
```
|
||||
Until prosody-modules is updated, use my patched version of [mod_secure_interfaces.lua](https://www.moparisthebest.com/mod_secure_interfaces.lua)
|
||||
which also works for s2s.
|
||||
@ -49,11 +114,21 @@ proxy_port_mappings = {
|
||||
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 = {15269}
|
||||
s2s_ports = {15268}
|
||||
```
|
||||
|
||||
Copy prosody's TLS key to `/etc/xmpp-proxy/le.key` and TLS cert to `/etc/xmpp-proxy/fullchain.cer`, and use the provided
|
||||
`xmpp-proxy.toml` configuration as-is.
|
||||
#### Customize the build
|
||||
|
||||
If you are a grumpy power user who wants to build xmpp-proxy with exactly the features you want, nothing less, nothing
|
||||
more, this section is for you!
|
||||
|
||||
xmpp-proxy has 3 compile-time features:
|
||||
1. `incoming` - enables `incoming_listen` config option for reverse proxy STARTTLS/TLS
|
||||
2. `outgoing` - enables `outgoing_listen` config option for outgoing proxy STARTTLS/TLS
|
||||
3. `quic` - enables `quic_listen` config option for reverse proxy QUIC, and QUIC support for `outgoing` if it is enabled
|
||||
|
||||
So to build only supporting reverse proxy STARTTLS/TLS, no QUIC, run: `cargo build --release --no-default-features --features incoming`
|
||||
To build a reverse proxy only, but supporting all of STARTTLS/TLS/QUIC, run: `cargo build --release --no-default-features --features incoming,quic`
|
||||
|
||||
#### License
|
||||
GNU/AGPLv3 - Check LICENSE.md for details
|
||||
|
@ -79,7 +79,7 @@ macro_rules! debug {
|
||||
struct Config {
|
||||
tls_key: String,
|
||||
tls_cert: String,
|
||||
listen: Option<Vec<String>>,
|
||||
incoming_listen: Option<Vec<String>>,
|
||||
quic_listen: Option<Vec<String>>,
|
||||
outgoing_listen: Option<Vec<String>>,
|
||||
max_stanza_size_bytes: usize,
|
||||
@ -272,7 +272,7 @@ async fn main() {
|
||||
|
||||
let mut handles: Vec<JoinHandle<Result<()>>> = Vec::new();
|
||||
#[cfg(feature = "incoming")]
|
||||
if let Some(ref listeners) = main_config.listen {
|
||||
if let Some(ref listeners) = main_config.incoming_listen {
|
||||
let acceptor = main_config.tls_acceptor().die("invalid cert/key ?");
|
||||
for listener in listeners {
|
||||
handles.push(spawn_tls_listener(listener.parse().die("invalid listener address"), config.clone(), acceptor.clone()));
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::*;
|
||||
|
||||
async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr: SocketAddr, max_stanza_size_bytes: usize) -> Result<()> {
|
||||
println!("INFO: outgoing {} connected", client_addr);
|
||||
println!("INFO: out {} connected", client_addr);
|
||||
|
||||
let in_filter = StanzaFilter::new(max_stanza_size_bytes);
|
||||
|
||||
@ -18,9 +18,9 @@ async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr:
|
||||
// todo: unsure how legit changing to a string here is...
|
||||
let domain = to_str(stream_open.extract_between(b" to='", b"'").or_else(|_| stream_open.extract_between(b" to=\"", b"\""))?);
|
||||
|
||||
println!("INFO: {} is_c2s: {}, domain: {}", client_addr, is_c2s, domain);
|
||||
println!("INFO: out {} is_c2s: {}, domain: {}", client_addr, is_c2s, domain);
|
||||
|
||||
debug!("< {} {} '{}'", client_addr, c2s(is_c2s), to_str(&stream_open));
|
||||
debug!("out < {} {} '{}'", client_addr, c2s(is_c2s), to_str(&stream_open));
|
||||
let (mut out_wr, mut out_rd, stream_open) = srv_connect(&domain, is_c2s, &stream_open, &mut in_filter).await?;
|
||||
// send server response to client
|
||||
in_wr.write_all(&stream_open).await?;
|
||||
@ -35,7 +35,7 @@ async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr:
|
||||
match buf {
|
||||
None => break,
|
||||
Some(buf) => {
|
||||
debug!("< {} {} '{}'", client_addr, c2s(is_c2s), to_str(buf));
|
||||
debug!("out < {} {} '{}'", domain, c2s(is_c2s), to_str(buf));
|
||||
in_wr.write_all(buf).await?;
|
||||
in_wr.flush().await?;
|
||||
}
|
||||
@ -47,14 +47,14 @@ async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr:
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
debug!("> {} {} '{}'", client_addr, c2s(is_c2s), to_str(&out_buf[0..n]));
|
||||
debug!("out > {} {} '{}'", domain, c2s(is_c2s), to_str(&out_buf[0..n]));
|
||||
out_wr.write_all(&out_buf[0..n]).await?;
|
||||
out_wr.flush().await?;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
println!("INFO: {} disconnected", client_addr);
|
||||
println!("INFO: out {} disconnected", client_addr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -15,14 +15,14 @@ pub async fn quic_connect(target: SocketAddr, server_name: &str, is_c2s: bool) -
|
||||
endpoint_builder.default_client_config(client_cfg);
|
||||
let (endpoint, _incoming) = endpoint_builder.bind(&bind_addr)?;
|
||||
// connect to server
|
||||
let quinn::NewConnection { connection, .. } = endpoint.connect(&target, server_name).unwrap().await.unwrap();
|
||||
let quinn::NewConnection { connection, .. } = endpoint.connect(&target, server_name).unwrap().await?;
|
||||
debug!("[client] connected: addr={}", connection.remote_address());
|
||||
|
||||
if is_c2s {
|
||||
let (wrt, rd) = connection.open_bi().await.unwrap();
|
||||
let (wrt, rd) = connection.open_bi().await?;
|
||||
Ok((Box::new(wrt), Box::new(rd)))
|
||||
} else {
|
||||
let wrt = connection.open_uni().await.unwrap();
|
||||
let wrt = connection.open_uni().await?;
|
||||
Ok((Box::new(wrt), Box::new(NoopIo)))
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ impl XmppConnection {
|
||||
let ips = RESOLVER.lookup_ip(self.target.clone()).await?;
|
||||
debug!("trying 1 domain {}, SRV: {:?}", domain, self);
|
||||
for ip in ips.iter() {
|
||||
debug!("trying domain {}, ip {}, SRV: {:?}", domain, ip, self);
|
||||
debug!("trying domain {}, ip {}, is_c2s: {}, SRV: {:?}", domain, ip, is_c2s, self);
|
||||
match self.conn_type {
|
||||
XmppConnectionType::StartTLS => match crate::starttls_connect(SocketAddr::new(ip, self.port), domain, is_c2s, &stream_open, &mut in_filter).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd)),
|
||||
|
@ -1,8 +1,10 @@
|
||||
|
||||
# interfaces to listen for external XMPP connections on
|
||||
listen = [ "0.0.0.0:5222", "0.0.0.0:5269" ]
|
||||
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
|
||||
incoming_listen = [ "0.0.0.0:5222", "0.0.0.0:5269" ]
|
||||
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
|
||||
quic_listen = [ "0.0.0.0:443" ]
|
||||
outgoing_listen = [ "0.0.0.0:5252" ]
|
||||
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
|
||||
outgoing_listen = [ "127.0.0.1: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
|
||||
|
Loading…
Reference in New Issue
Block a user