Tip of the day: Check out Special users on how to give trusted users/bots more rights without making them IRCOp. |
Proxy block
The proxy block allows you to configure host spoofing via web reverse proxies or WEBIRC.
NOTE: If you are running UnrealIRCd 6.1.0 or older, then instead see the WebIRC block for webirc support.
Is a reverse proxy a good idea?
Note that while using WEBIRC is reasonably common, using other proxy types like using a reverse proxy for IRC is rare. Using a reverse proxy could be useful in the specific case where you don't expose the IRC server directly to the internet and only allow it to be accessed through websocket connections through a reverse proxy (like, having an IRC server for webchats only). Using a reverse proxy makes considerable less sense when your IRC server is still directly reachable from the internet (direct IRC without websockets, without the reverse proxy).
Also, using a reverse proxy adds another Point of Failure (another host in-between that may be down, have glitches, network resets, etc.). For example, Cloudflare resets idle websocket connections after 2 minutes and may reset websocket connections during day to day upgrades of their servers. Make sure you understand the effects before going this route.
If you were considering reverse proxies to distribute user load, then better read Distributing user load and doing server maintenance instead, which uses DNS RR and takes away the need for the reverse proxy inbetween.
Syntax
proxy name-of-proxy { type [webirc|forwarded|x-forwarded|cloudflare]; match { ip 1.2.3.4; } password "somepass"; };
The name-of-proxy is a short name for the proxy. It is not used yet as of UnrealIRCd 6.1.1, but may be used or displayed somewhere at a later point.
Within the block:
- type is the type of proxy:
- webirc: a WEBIRC / CGI:IRC gateway, see also WebIRC Support.
- forwarded: a web proxy sending
Forwarded
headers (modern standard, but less common) - x-forwarded: a web proxy sending
X-Forwarded-For
andX-Forwarded-Proto
headers (not standardized but more common) - cloudflare: if you are using cloudflare as a reverse proxy, this uses a combination of
CF-Connecting-IP
andX-Forwarded-Proto
.
IMPORTANT: Cloudflare terminates idle websocket connections after 2 minutes and has a policy of only allowing a certain number of concurrent websocket connections. See the Cloudflare documentation. As you can see, the maximum concurrent connections vary by plan and the exact numbers are unspecified. Using Cloudflare for IRC websockets is likely not an ideal choice.
- match is checked against the proxy that is trying to connect. It is a Mask item. We recommend strict IP address restrictions here. For proxy type forwarded/x-forwarded/cloudflare, which don't use a password, we automatically exempt hosts matching this from connect floods and from blacklist checks (in UnrealIRCd 6.1.8 and later).
- password is only used for type webirc
Examples
We have several examples:
WEBIRC example with KiwiIRC
The example below configures KiwiIRC and UnrealIRCd. Note that this is just an example, if you use something other than KiwiIRC you edit that instead of the KiwiIRC-side.
The most important thing is that you set the same password on both the Kiwi side and on the UnrealIRCd side, we use ThisIsMySecretWebIRCPassword
as an example here.
KiwiIRC-side (old and outdated)
Edit kiwiirc's config.js as follows (note: this is NOT a complete configuration file, it just shows 3 important sections):
// WebIRC passwords enabled for this server conf.webirc_pass = { "irc1.yournetwork.org": "ThisIsMySecretWebIRCPassword" }; // Default settings for the client. These may be changed in the browser conf.client = { server: 'irc1.yournetwork.org', port: 6697, ssl: true, channel: '#test', nick: 'kiwi_?' }; // or use conf.restrict_server etc... to achieve the same effect (see example config.js file). // What matters is that the server name in conf.client or conf.restrict_server // match the server name in conf.webirc_pass (and use the right password). // So be sure to replace BOTH the irc1.yournetwork.org instances with the same server name. // Now something else... "send ip as username".... // Be sure *NOT* to list your server here !!!! // If you add it here, then the IP will show up in the ident. This is NOT what you want // as the IP is already sent via WEBIRC. Doing so would reveal the users' IP to everyone which is bad. conf.ip_as_username = [ "do_not_set_your_server_here" ];
KiwiIRC-side (new aka nextclient)
Edit kiwiirc's webircgateway config.conf as follows (note: this is NOT a complete configuration file):
// WebIRC passwords enabled for this server [upstream.1] webirc = "ThisIsMySecretWebIRCPassword"
UnrealIRCd-side
Then, in your unrealircd.conf you add a webirc block:
proxy { type webirc; mask 127.0.0.1; password "ThisIsMySecretWebIRCPassword"; }; except ban { mask 127.0.0.1; type { connect-flood; handshake-data-flood; blacklist; }; };
The Except ban block block is highly recommended. This will ensure your webirc gateway is not seen as connection flooding and the blacklist exception will make it connect faster. If you don't have an except ban block for your webirc gateway then users will not be able to connect during peak hours or after a server restart.
NGINX reverse proxy for websockets
In this example we use a reverse proxy in NGINX to proxy websocket connections via NGINX to your UnrealIRCd. We choose to use the "forwarded" type which is less common but standardized.
Note that UnrealIRCd also supports X-Forwarded-For
header, but this is NOT used in the example below (we set the Forwarded
header).
UnrealIRCd configuration
First, set up websockets on port 8000 with SSL/TLS:
loadmodule "websocket"; loadmodule "webserver"; listen { ip *; port 8000; options { websocket { type text; } tls; } }
Then add the proxy block:
proxy { type forwarded; match { ip 6.6.6.6; } // IP of your reverse proxy }
NGINX configuration
This is based on NGINX docs plus the addition of $proxy_protocol from ourselves
Add this to your config so the $remote_addr
variable becomes available:
map $https $proxy_protocol { "^on$" "proto=https"; default "proto=http"; } map $remote_addr $proxy_forwarded_elem { # IPv4 addresses can be sent as-is ~^[0-9.]+$ "for=$remote_addr;$proxy_protocol"; # IPv6 addresses need to be bracketed and quoted ~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\";$proxy_protocol"; # Unix domain socket names cannot be represented in RFC 7239 syntax default "for=unknown;$proxy_protocol"; } map $http_forwarded $proxy_add_forwarded { # If the incoming Forwarded header is syntactically valid, append to it "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem"; # Otherwise, replace it default "$proxy_forwarded_elem"; }
And then add something like this in your site block:
location /websocket { proxy_set_header Forwarded $proxy_add_forwarded; proxy_pass https://5.5.5.5:8000/; }
This assumes your UnrealIRCd is running at 5.5.5.5 with websockets enabled at port 8000 and it is a SSL/TLS port, as we configured earlier above.
Test
Be sure to REHASH
the IRCd and reload your NGINX (systemctl restart nginx.service
). Then connect with a websocket to https://yourproxy/websocket
Your websocket client should pop up on IRC with the end-user IP address (and not the IP address of the proxy). If it doesn't work, then also connect as IRCOp via normal (non-websocket) IRC and see if there is anything logged by the IRCd, UnrealIRCd log messages such as "proxy did not send a valid forwarded header".
- If you see the "proxy did not send a valid forwarded header" warning then it means your proxy is not sending the
Forwarded
header. For example it could be using the wrong headers such asVia
orX-Forwarded-For
:- Either reconfigure the proxy to send the modern
Forwarded
header instead. This is all explained under #NGINX configuration above. - Or, if you want to use
X-Forwarded-For
instead ofForwarded
, then in UnrealIRCd useproxy { type x-forwarded; [..] }
- Either reconfigure the proxy to send the modern