Difference between revisions of "Tech pages/XEP-0368"

From XMPP WIKI
Jump to navigation Jump to search
m
 
(17 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Here is a sample sslh.conf to support XEP-0368 among other things:
[https://xmpp.org/extensions/xep-0368.html XEP-0368] can be used to provide encrypted XMPP service as well as HTTPS on the same port by utilizing [https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation ALPN TLS extension].
Port 443 is commonly allowed by firewalls. To run multiple services on one port a proxy is needed to split the traffic between HTTP server and the XMPP server.
 
Note that this will not hide XMPP traffic from sufficiently intelligent firewalls as ALPN value is still sent unencrypted.
 
This page presents configuration hints for several popular proxies.
 
__TOC__
 
== nginx ==
 
Nginx since version 1.13.10 has additional variable (<code>$ssl_preread_alpn_protocols</code>) available when using [https://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html ngx_stream_ssl_preread] module (this module must be included when compiling nginx).
 
The configuration below routes traffic with ALPN xmpp-client to server xmppserver and the rest (including HTTPS) to httpserver.
 
<nowiki>
stream {
    upstream httpserver {
        server httpserver:8181;
    }
 
    upstream xmppserver {
        server xmppserver:5223;
    }
 
    upstream turnserver {
        server turnserver:3477;
    }
 
    map $ssl_preread_alpn_protocols $upstream {
        default httpserver;
        "xmpp-client" xmppserver;
        "stun.turn" turnserver;
        "stun.nat-discovery" turnserver;
    }
 
    server {
        listen 443;
 
        ssl_preread on;
        proxy_pass $upstream;
    }
}
</nowiki>
 
Nginx will route only TLS traffic in this configuration so this configuration will work only with direct TLS (<code>_xmpps-client</code> SRV record). Connections that start unencrypted and then request encryption (STARTTLS, <code>_xmpp-client</code> record) will not work.
 
== sslh ==
 
 
Here is a sample sslh.conf (Using at least [http://www.rutschle.net/tech/sslh/README.html sslh] 1.18) to support [https://xmpp.org/extensions/xep-0368.html XEP-0368] among other things:


  <nowiki>
  <nowiki>
Line 20: Line 70:


# in this example:
# in this example:
# 5223 is a prosody legacy_ssl_ports "direct-tls" port
# 5223 is a "direct-tls" xmpp port (prosody c2s_direct_tls_ports, ejabberd listen with tls: true)
# 442 is a nginx https port
# 442 is a https port (nginx, apache, etc)
# 22 is an ssh port
# 22 is an ssh port (openssh)
# 5222 is a prosody c2s_ports
# 5222 is a regular/plain/starttls xmpp port (prosody c2s_ports, ejabberd listen with starttls: true)
# 994 is dovecot imaps port
# 994 is "direct-tls" imap port, imaps (dovecot etc)
# 3477 is TURNS (TURN-over-TLS) port (coturn etc) NOTE: coturn by default (newer versions) rejects connections from localhost, you'd either need transparent mode, or to turn this off and open up all the risks it entails, for it to accept connections from sslh
   
   
protocols:
protocols:
(
(
     { name: "tls";    host: "127.0.0.1"; port: "442";  alpn_protocols: [ "h2", "http/1.1" ]; },                 # https/nginx most common case
     { name: "tls";    host: "127.0.0.1"; port: "442";  alpn_protocols: [ "h2", "http/1.1" ]; },                 # https most common case
     { name: "tls";    host: "127.0.0.1"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; },                   # check for xep-0368 xmpp tls
     { name: "tls";    host: "127.0.0.1"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; },                     # check for XEP-0368 xmpp tls (this needs to be above SNI check below because XEP-0368 would send domain.tld in SNI)
     { name: "tls";    host: "127.0.0.1"; port: "442";  sni_hostnames:  [ "www.example.org", "example.org" ]; }, # specific hostnames go to nginx
     { name: "tls";    host: "127.0.0.1"; port: "442";  sni_hostnames:  [ "www.domain.tld", "domain.tld" ]; }, # specific hostnames go to https
     { name: "tls";    host: "127.0.0.1"; port: "994";  sni_hostnames:  [ "imap.example.org" ]; },               # other hostnames go to dovecot
    { name: "tls";    host: "127.0.0.1"; port: "3477"; alpn_protocols: [ "stun.turn", "stun.nat-discovery" ]; }, # turn should send ALPN, but if it doesn't...
     { name: "tls";    host: "127.0.0.1"; port: "442"; },                                                       # anything else TLS assume for nginx
    { name: "tls";    host: "127.0.0.1"; port: "3477"; sni_hostnames:  [ "turn.domain.tld" ]; },                # we can also match turn on SNI
     { name: "ssh";    host: "127.0.0.1"; port: "22"; },                                                         # ssh goes to openssh
     { name: "tls";    host: "127.0.0.1"; port: "994";  sni_hostnames:  [ "imap.domain.tld" ]; },               # other hostnames go to imaps
     { name: "xmpp";    host: "127.0.0.1"; port: "5222"; },                                                       # xmpp goes to prosody
     { name: "tls";    host: "127.0.0.1"; port: "442"; },                                                         # anything else TLS assume for https
     { name: "timeout"; host: "127.0.0.1"; port: "442"; }                                                         # send everything unknown to nginx
     { name: "ssh";    host: "127.0.0.1"; port: "22"; },                                                         # ssh goes to ssh
     { name: "xmpp";    host: "127.0.0.1"; port: "5222"; },                                                       # xmpp goes to regular xmpp port
     { name: "timeout"; host: "127.0.0.1"; port: "442"; }                                                         # send everything unknown to https
);
);


on-timeout: "timeout"; # if timeout elapses (2 seconds here) go to nginx
on-timeout: "timeout"; # if timeout elapses (2 seconds here) go to https
</nowiki>
</nowiki>
 
Another (incorrectly named) example can be found at the [https://wiki.debian.org/InstallingProsody#XMPP_over_HTTPS Debian Wiki]
 
== HAProxy ==
 
Here is a relevant configuration snippet from HAProxy which has XMPP c2s (both STARTTLS and TLS version), https, IMAP and TURN on port 443. Only some of the used backend examples are provided.
 
Note the send-proxy-v2 statement - it uses proxy protocol which must be enabled in XMPP client as in the sections below, or disabled in HAProxy by removing the statement.
 
<nowiki>
frontend ft_https
    bind :443
    mode tcp
    tcp-request inspect-delay 1s
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend bk_jabber_c2s if { payload(0,5) -m str "<?xml" }
    use_backend bk_jabber_c2s_tls if { req.ssl_alpn xmpp-client }
    use_backend bk_turn if { req.ssl_alpn sturn.turn }
    use_backend bk_turn if { req.ssl_alpn sturn.nat-discovery }
    use_backend bk_imaps if { req.ssl_sni -i imap.example.com }
    use_backend bk_https_cdn if { req.ssl_sni -i cdn.example.com }
    default_backend bk_https
 
backend bk_jabber_c2s
    mode tcp
    server jabber_c2s 192.168.1.1:5222 send-proxy-v2
 
backend bk_jabber_c2s_tls
    mode tcp
    server jabber_c2s 192.168.1.1:5223 send-proxy-v2
 
backend bk_turn
    mode tcp
    server turn 192.168.1.1:3477 send-proxy-v2
 
backend bk_https
    mode tcp
    server https 192.168.1.1:443 send-proxy-v2
</nowiki>
 
== Transparent Proxying ==
 
If your XMPP server and protocol multiplexer are supporting the proxy protocol, you should consider enabling it.
That way your XMPP server will see the real IPs of clients that connect to it.
 
=== Nginx ===
To enable the proxy protocol in nginx, modify your configuration as follows:
 
<nowiki>
stream {
    ...
 
    server {
        listen 443;
 
        ssl_preread on;
        proxy_pass $upstream;
        proxy_protocol: on; <-- add this line
    }
}
</nowiki>
 
=== ejabberd ===
In case of ejabberd, you want to add an additional handler that accepts the proxy protocol:
 
<nowiki>
listen:
  ...
  -
    port: 5225
    use_proxy_protocol: true <-- Important!
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    tls: true
    protocol_options: 'TLS_OPTIONS'
  ...
</nowiki>
 
Note that you should only connect to this handler via the proxy protocol, as other connections will be refused.
 
Now ejabberd will display the real IPs of users that connect to your server via multiplexing over port 443.
 
== DNS setup ==
 
You then need to setup your [https://wiki.xmpp.org/web/SRV_Records SRV Records] so clients can find it, personally I have mine set up like so (for a JID like me@domain.tld):
 
_xmpps-client._tcp.domain.tld. 86400 IN SRV 5  0 443  xmpp.domain.tld.
_xmpp-client._tcp.domain.tld.  86400 IN SRV 10 0 443  xmpp.domain.tld.
_xmpp-client._tcp.domain.tld.  86400 IN SRV 15 0 5222 xmpp.domain.tld.
 
This prioritizes XEP-0368 TLS over port 443 first, then plain XMPP over 443 next, and lastly plain XMPP over 5222.  A client that doesn't support XEP-0368 just skips the first record.
 
Please note the target can be anything, domain.tld, xmpp.domain.tld, or some.unrelated.domain.net, just as long as it's listening on those ports and has a valid certificate for domain.tld in this case.
 
If you have any questions feel free to ask the author of XEP-0368 via email, XMPP, or the nick moparisthebest in the [xmpp:xsf@muc.xmpp.org?join XSF MUC]

Latest revision as of 16:44, 26 December 2023

XEP-0368 can be used to provide encrypted XMPP service as well as HTTPS on the same port by utilizing ALPN TLS extension. Port 443 is commonly allowed by firewalls. To run multiple services on one port a proxy is needed to split the traffic between HTTP server and the XMPP server.

Note that this will not hide XMPP traffic from sufficiently intelligent firewalls as ALPN value is still sent unencrypted.

This page presents configuration hints for several popular proxies.

nginx

Nginx since version 1.13.10 has additional variable ($ssl_preread_alpn_protocols) available when using ngx_stream_ssl_preread module (this module must be included when compiling nginx).

The configuration below routes traffic with ALPN xmpp-client to server xmppserver and the rest (including HTTPS) to httpserver.

stream {
    upstream httpserver {
        server httpserver:8181;
    }

    upstream xmppserver {
        server xmppserver:5223;
    }

    upstream turnserver {
        server turnserver:3477;
    }

    map $ssl_preread_alpn_protocols $upstream {
        default httpserver;
        "xmpp-client" xmppserver;
        "stun.turn" turnserver;
        "stun.nat-discovery" turnserver;
    }

    server {
        listen 443;

        ssl_preread on;
        proxy_pass $upstream;
    }
}
 

Nginx will route only TLS traffic in this configuration so this configuration will work only with direct TLS (_xmpps-client SRV record). Connections that start unencrypted and then request encryption (STARTTLS, _xmpp-client record) will not work.

sslh

Here is a sample sslh.conf (Using at least sslh 1.18) to support XEP-0368 among other things:

verbose: false;
foreground: true;
inetd: false;
numeric: true;
transparent: false;
timeout: "2";
user: "nobody";
pidfile: "/run/sslh.pid";

# Note: I had to use IPs everywhere and not hostnames

# List of interfaces on which we should listen
listen:
(
    { host: "0.0.0.0"; port: "443"; },
);

# in this example:
# 5223 is a "direct-tls" xmpp port (prosody c2s_direct_tls_ports, ejabberd listen with tls: true)
# 442 is a https port (nginx, apache, etc)
# 22 is an ssh port (openssh)
# 5222 is a regular/plain/starttls xmpp port (prosody c2s_ports, ejabberd listen with starttls: true)
# 994 is "direct-tls" imap port, imaps (dovecot etc)
# 3477 is TURNS (TURN-over-TLS) port (coturn etc) NOTE: coturn by default (newer versions) rejects connections from localhost, you'd either need transparent mode, or to turn this off and open up all the risks it entails, for it to accept connections from sslh
 
protocols:
(
     { name: "tls";     host: "127.0.0.1"; port: "442";  alpn_protocols: [ "h2", "http/1.1" ]; },                  # https most common case
     { name: "tls";     host: "127.0.0.1"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; },                     # check for XEP-0368 xmpp tls (this needs to be above SNI check below because XEP-0368 would send domain.tld in SNI)
     { name: "tls";     host: "127.0.0.1"; port: "442";  sni_hostnames:  [ "www.domain.tld", "domain.tld" ]; },  # specific hostnames go to https
     { name: "tls";     host: "127.0.0.1"; port: "3477"; alpn_protocols: [ "stun.turn", "stun.nat-discovery" ]; }, # turn should send ALPN, but if it doesn't...
     { name: "tls";     host: "127.0.0.1"; port: "3477"; sni_hostnames:  [ "turn.domain.tld" ]; },                # we can also match turn on SNI
     { name: "tls";     host: "127.0.0.1"; port: "994";  sni_hostnames:  [ "imap.domain.tld" ]; },                # other hostnames go to imaps
     { name: "tls";     host: "127.0.0.1"; port: "442"; },                                                         # anything else TLS assume for https
     { name: "ssh";     host: "127.0.0.1"; port: "22"; },                                                          # ssh goes to ssh
     { name: "xmpp";    host: "127.0.0.1"; port: "5222"; },                                                        # xmpp goes to regular xmpp port
     { name: "timeout"; host: "127.0.0.1"; port: "442"; }                                                          # send everything unknown to https
);

on-timeout: "timeout"; # if timeout elapses (2 seconds here) go to https
 

Another (incorrectly named) example can be found at the Debian Wiki

HAProxy

Here is a relevant configuration snippet from HAProxy which has XMPP c2s (both STARTTLS and TLS version), https, IMAP and TURN on port 443. Only some of the used backend examples are provided.

Note the send-proxy-v2 statement - it uses proxy protocol which must be enabled in XMPP client as in the sections below, or disabled in HAProxy by removing the statement.

frontend ft_https
    bind :443
    mode tcp
    tcp-request inspect-delay 1s
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend bk_jabber_c2s if { payload(0,5) -m str "<?xml" }
    use_backend bk_jabber_c2s_tls if { req.ssl_alpn xmpp-client }
    use_backend bk_turn if { req.ssl_alpn sturn.turn }
    use_backend bk_turn if { req.ssl_alpn sturn.nat-discovery }
    use_backend bk_imaps if { req.ssl_sni -i imap.example.com }
    use_backend bk_https_cdn if { req.ssl_sni -i cdn.example.com }
    default_backend bk_https

backend bk_jabber_c2s
    mode tcp
    server jabber_c2s 192.168.1.1:5222 send-proxy-v2

backend bk_jabber_c2s_tls
    mode tcp
    server jabber_c2s 192.168.1.1:5223 send-proxy-v2

backend bk_turn
    mode tcp
    server turn 192.168.1.1:3477 send-proxy-v2

backend bk_https
    mode tcp
    server https 192.168.1.1:443 send-proxy-v2
 

Transparent Proxying

If your XMPP server and protocol multiplexer are supporting the proxy protocol, you should consider enabling it. That way your XMPP server will see the real IPs of clients that connect to it.

Nginx

To enable the proxy protocol in nginx, modify your configuration as follows:

stream {
    ...

    server {
        listen 443;

        ssl_preread on;
        proxy_pass $upstream;
        proxy_protocol: on; <-- add this line
    }
}
 

ejabberd

In case of ejabberd, you want to add an additional handler that accepts the proxy protocol:

listen:
  ...
  -
    port: 5225
    use_proxy_protocol: true <-- Important!
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    tls: true
    protocol_options: 'TLS_OPTIONS'
  ...
 

Note that you should only connect to this handler via the proxy protocol, as other connections will be refused.

Now ejabberd will display the real IPs of users that connect to your server via multiplexing over port 443.

DNS setup

You then need to setup your SRV Records so clients can find it, personally I have mine set up like so (for a JID like me@domain.tld):

_xmpps-client._tcp.domain.tld. 86400 IN SRV 5  0 443  xmpp.domain.tld.
_xmpp-client._tcp.domain.tld.  86400 IN SRV 10 0 443  xmpp.domain.tld.
_xmpp-client._tcp.domain.tld.  86400 IN SRV 15 0 5222 xmpp.domain.tld.

This prioritizes XEP-0368 TLS over port 443 first, then plain XMPP over 443 next, and lastly plain XMPP over 5222. A client that doesn't support XEP-0368 just skips the first record.

Please note the target can be anything, domain.tld, xmpp.domain.tld, or some.unrelated.domain.net, just as long as it's listening on those ports and has a valid certificate for domain.tld in this case.

If you have any questions feel free to ask the author of XEP-0368 via email, XMPP, or the nick moparisthebest in the XSF MUC