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"
|
version = "1.0.0"
|
||||||
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
||||||
|
|
||||||
description = "Reverse XMPP proxy."
|
description = "XMPP reverse proxy and outgoing proxy"
|
||||||
repository = "https://code.moparisthebest.com/moparisthebest/xmpp-proxy"
|
repository = "https://code.moparisthebest.com/moparisthebest/xmpp-proxy"
|
||||||
keywords = ["xmpp", "proxy"]
|
keywords = ["xmpp", "proxy"]
|
||||||
|
|
||||||
@ -49,5 +49,5 @@ default = ["incoming", "outgoing", "quic"]
|
|||||||
#default = ["outgoing"]
|
#default = ["outgoing"]
|
||||||
#default = ["incoming"]
|
#default = ["incoming"]
|
||||||
incoming = ["tokio-rustls"]
|
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"]
|
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/)
|
[![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
|
xmpp-proxy is a reverse proxy and outgoing proxy for XMPP servers and clients, providing STARTTLS,
|
||||||
and limiting stanza sizes without an XML parser.
|
[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)
|
xmpp-proxy in reverse proxy (incoming) mode will:
|
||||||
c2s or s2s connections, terminate TLS, and connect them to a real XMPP server, limiting stanza sizes as configured.
|
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
|
#### Installation
|
||||||
* `cargo install xmpp-proxy`
|
* `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?
|
#### 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";
|
"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)
|
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.
|
which also works for s2s.
|
||||||
@ -49,11 +114,21 @@ proxy_port_mappings = {
|
|||||||
c2s_ports = {}
|
c2s_ports = {}
|
||||||
legacy_ssl_ports = {}
|
legacy_ssl_ports = {}
|
||||||
-- you MUST have at least one s2s_ports defined if you want outgoing S2S to work, don't ask..
|
-- 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
|
#### Customize the build
|
||||||
`xmpp-proxy.toml` configuration as-is.
|
|
||||||
|
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
|
#### License
|
||||||
GNU/AGPLv3 - Check LICENSE.md for details
|
GNU/AGPLv3 - Check LICENSE.md for details
|
||||||
|
@ -79,7 +79,7 @@ macro_rules! debug {
|
|||||||
struct Config {
|
struct Config {
|
||||||
tls_key: String,
|
tls_key: String,
|
||||||
tls_cert: String,
|
tls_cert: String,
|
||||||
listen: Option<Vec<String>>,
|
incoming_listen: Option<Vec<String>>,
|
||||||
quic_listen: Option<Vec<String>>,
|
quic_listen: Option<Vec<String>>,
|
||||||
outgoing_listen: Option<Vec<String>>,
|
outgoing_listen: Option<Vec<String>>,
|
||||||
max_stanza_size_bytes: usize,
|
max_stanza_size_bytes: usize,
|
||||||
@ -272,7 +272,7 @@ async fn main() {
|
|||||||
|
|
||||||
let mut handles: Vec<JoinHandle<Result<()>>> = Vec::new();
|
let mut handles: Vec<JoinHandle<Result<()>>> = Vec::new();
|
||||||
#[cfg(feature = "incoming")]
|
#[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 ?");
|
let acceptor = main_config.tls_acceptor().die("invalid cert/key ?");
|
||||||
for listener in listeners {
|
for listener in listeners {
|
||||||
handles.push(spawn_tls_listener(listener.parse().die("invalid listener address"), config.clone(), acceptor.clone()));
|
handles.push(spawn_tls_listener(listener.parse().die("invalid listener address"), config.clone(), acceptor.clone()));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr: SocketAddr, max_stanza_size_bytes: usize) -> Result<()> {
|
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);
|
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...
|
// 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"\""))?);
|
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?;
|
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
|
// send server response to client
|
||||||
in_wr.write_all(&stream_open).await?;
|
in_wr.write_all(&stream_open).await?;
|
||||||
@ -35,7 +35,7 @@ async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr:
|
|||||||
match buf {
|
match buf {
|
||||||
None => break,
|
None => break,
|
||||||
Some(buf) => {
|
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.write_all(buf).await?;
|
||||||
in_wr.flush().await?;
|
in_wr.flush().await?;
|
||||||
}
|
}
|
||||||
@ -47,14 +47,14 @@ async fn handle_outgoing_connection(stream: tokio::net::TcpStream, client_addr:
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
break;
|
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.write_all(&out_buf[0..n]).await?;
|
||||||
out_wr.flush().await?;
|
out_wr.flush().await?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("INFO: {} disconnected", client_addr);
|
println!("INFO: out {} disconnected", client_addr);
|
||||||
Ok(())
|
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);
|
endpoint_builder.default_client_config(client_cfg);
|
||||||
let (endpoint, _incoming) = endpoint_builder.bind(&bind_addr)?;
|
let (endpoint, _incoming) = endpoint_builder.bind(&bind_addr)?;
|
||||||
// connect to server
|
// 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());
|
debug!("[client] connected: addr={}", connection.remote_address());
|
||||||
|
|
||||||
if is_c2s {
|
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)))
|
Ok((Box::new(wrt), Box::new(rd)))
|
||||||
} else {
|
} else {
|
||||||
let wrt = connection.open_uni().await.unwrap();
|
let wrt = connection.open_uni().await?;
|
||||||
Ok((Box::new(wrt), Box::new(NoopIo)))
|
Ok((Box::new(wrt), Box::new(NoopIo)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ impl XmppConnection {
|
|||||||
let ips = RESOLVER.lookup_ip(self.target.clone()).await?;
|
let ips = RESOLVER.lookup_ip(self.target.clone()).await?;
|
||||||
debug!("trying 1 domain {}, SRV: {:?}", domain, self);
|
debug!("trying 1 domain {}, SRV: {:?}", domain, self);
|
||||||
for ip in ips.iter() {
|
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 {
|
match self.conn_type {
|
||||||
XmppConnectionType::StartTLS => match crate::starttls_connect(SocketAddr::new(ip, self.port), domain, is_c2s, &stream_open, &mut in_filter).await {
|
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)),
|
Ok((wr, rd)) => return Ok((wr, rd)),
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
# interfaces to listen for external XMPP connections on
|
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
|
||||||
listen = [ "0.0.0.0:5222", "0.0.0.0:5269" ]
|
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" ]
|
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
|
# 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
|
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
|
||||||
|
Loading…
Reference in New Issue
Block a user