From 6f2d0b505f4715c696cc5d49874d442cf790b98a Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Thu, 28 Nov 2019 17:06:11 +0000 Subject: Move WebSocket config to its own class. --- src/modules/m_websocket.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'src/modules/m_websocket.cpp') diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index 51dada299..3437fdb1a 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -31,12 +31,19 @@ static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static const char whitespace[] = " \t\r\n"; static dynamic_reference_nocheck* sha1; -class WebSocketHookProvider : public IOHookProvider +struct WebSocketConfig { - public: + // The HTTP origins that can connect to the server. OriginList allowedorigins; + + // Whether to send as UTF-8 text instead of binary data. bool sendastext; +}; +class WebSocketHookProvider : public IOHookProvider +{ + public: + WebSocketConfig config; WebSocketHookProvider(Module* mod) : IOHookProvider(mod, "websocket", IOHookProvider::IOH_UNKNOWN, true) { @@ -110,8 +117,7 @@ class WebSocketHook : public IOHookMiddle State state; time_t lastpingpong; - OriginList& allowedorigins; - bool& sendastext; + WebSocketConfig& config; static size_t FillHeader(unsigned char* outbuf, size_t sendlength, OpCode opcode) { @@ -318,7 +324,7 @@ class WebSocketHook : public IOHookMiddle if (originheader.Find(recvq, "Origin:", 7, reqend)) { const std::string origin = originheader.ExtractValue(recvq); - for (OriginList::const_iterator iter = allowedorigins.begin(); iter != allowedorigins.end(); ++iter) + for (OriginList::const_iterator iter = config.allowedorigins.begin(); iter != config.allowedorigins.end(); ++iter) { if (InspIRCd::Match(origin, *iter, ascii_case_insensitive_map)) { @@ -364,12 +370,11 @@ class WebSocketHook : public IOHookMiddle } public: - WebSocketHook(IOHookProvider* Prov, StreamSocket* sock, OriginList& AllowedOrigins, bool& SendAsText) + WebSocketHook(IOHookProvider* Prov, StreamSocket* sock, WebSocketConfig& cfg) : IOHookMiddle(Prov) , state(STATE_HTTPREQ) , lastpingpong(0) - , allowedorigins(AllowedOrigins) - , sendastext(SendAsText) + , config(cfg) { sock->AddIOHook(this); } @@ -390,7 +395,7 @@ class WebSocketHook : public IOHookMiddle if (*chr == '\n') { // We have found an entire message. Send it in its own frame. - if (sendastext) + if (config.sendastext) { // If we send messages as text then we need to ensure they are valid UTF-8. std::string encoded; @@ -451,7 +456,7 @@ class WebSocketHook : public IOHookMiddle void WebSocketHookProvider::OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) { - new WebSocketHook(this, sock, allowedorigins, sendastext); + new WebSocketHook(this, sock, config); } class ModuleWebSocket : public Module @@ -473,7 +478,7 @@ class ModuleWebSocket : public Module if (tags.first == tags.second) throw ModuleException("You have loaded the websocket module but not configured any allowed origins!"); - OriginList allowedorigins; + WebSocketConfig config; for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; @@ -483,12 +488,14 @@ class ModuleWebSocket : public Module if (allow.empty()) throw ModuleException(" is a mandatory field, at " + tag->getTagLocation()); - allowedorigins.push_back(allow); + config.allowedorigins.push_back(allow); } ConfigTag* tag = ServerInstance->Config->ConfValue("websocket"); - hookprov->sendastext = tag->getBool("sendastext", true); - hookprov->allowedorigins.swap(allowedorigins); + config.sendastext = tag->getBool("sendastext", true); + + // Everything is okay; apply the new config. + hookprov->config = config; } void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE -- cgit v1.3.1-10-gc9f91 From bb1f892f68cb70537b224bca85cc40f1ed23017d Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Thu, 28 Nov 2019 17:59:35 +0000 Subject: Implement support for websocket connections via a proxy like nginx. --- docs/conf/modules.conf.example | 17 ++++++++++++----- src/modules/m_websocket.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'src/modules/m_websocket.cpp') diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index 639f02335..9cb78daee 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -2307,11 +2307,18 @@ # Requires SHA-1 hash support available in the sha1 module. # # -# Whether to re-encode messages as UTF-8 before sending to WebSocket -# clients. This is recommended as the WebSocket protocol requires all -# text frames to be sent as UTF-8. If you do not have this enabled -# messages will be sent as binary frames instead. -# +# behindproxy: Whether the server is behind a proxy that sends the +# X-Real-IP or X-Forwarded-For headers. If enabled the +# server will use the IP address specified by those HTTP +# headers. You should NOT enable this unless you are using +# a HTTP proxy like nginx as it will allow IP spoofing. +# sendastext: Whether to re-encode messages as UTF-8 before sending to +# WebSocket clients. This is recommended as the WebSocket +# protocol requires all text frames to be sent as UTF-8. +# If you do not have this enabled messages will be sent as +# binary frames instead. +# # # If you use the websocket module you MUST specify one or more origins # which are allowed to connect to the server. You should set this as diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index 3437fdb1a..79cabf4e5 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -36,6 +36,9 @@ struct WebSocketConfig // The HTTP origins that can connect to the server. OriginList allowedorigins; + // Whether to trust the X-Real-IP or X-Forwarded-For headers. + bool behindproxy; + // Whether to send as UTF-8 text instead of binary data. bool sendastext; }; @@ -340,6 +343,29 @@ class WebSocketHook : public IOHookMiddle return -1; } + if (config.behindproxy && sock->type == StreamSocket::SS_USER) + { + LocalUser* luser = static_cast(sock)->user; + irc::sockets::sockaddrs realsa(luser->client_sa); + + HTTPHeaderFinder proxyheader; + if (proxyheader.Find(recvq, "X-Real-IP:", 10, reqend) + && irc::sockets::aptosa(proxyheader.ExtractValue(recvq), realsa.port(), realsa)) + { + // Nothing to do here. + } + else if (proxyheader.Find(recvq, "X-Forwarded-For:", 16, reqend) + && irc::sockets::aptosa(proxyheader.ExtractValue(recvq), realsa.port(), realsa)) + { + // Nothing to do here. + } + + // Give the user their real IP address. + if (realsa != luser->client_sa) + luser->SetClientIP(realsa); + } + + HTTPHeaderFinder keyheader; if (!keyheader.Find(recvq, "Sec-WebSocket-Key:", 18, reqend)) { @@ -492,6 +518,7 @@ class ModuleWebSocket : public Module } ConfigTag* tag = ServerInstance->Config->ConfValue("websocket"); + config.behindproxy = tag->getBool("behindproxy"); config.sendastext = tag->getBool("sendastext", true); // Everything is okay; apply the new config. -- cgit v1.3.1-10-gc9f91 From afb5972ab54d64f8c4e7b09962fb2088e427920b Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Fri, 29 Nov 2019 11:09:36 +0000 Subject: WebSocket: replace the behindproxy switch with a proxy IP list. --- docs/conf/modules.conf.example | 8 ++++---- src/modules/m_websocket.cpp | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) (limited to 'src/modules/m_websocket.cpp') diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index 9cb78daee..cee785436 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -2307,9 +2307,9 @@ # Requires SHA-1 hash support available in the sha1 module. # # -# behindproxy: Whether the server is behind a proxy that sends the -# X-Real-IP or X-Forwarded-For headers. If enabled the -# server will use the IP address specified by those HTTP +# proxyranges: A space-delimited list of glob or CIDR matches to trust +# the X-Real-IP or X-Forwarded-For headers from. If enabled +# the server will use the IP address specified by those HTTP # headers. You should NOT enable this unless you are using # a HTTP proxy like nginx as it will allow IP spoofing. # sendastext: Whether to re-encode messages as UTF-8 before sending to @@ -2317,7 +2317,7 @@ # protocol requires all text frames to be sent as UTF-8. # If you do not have this enabled messages will be sent as # binary frames instead. -# # # If you use the websocket module you MUST specify one or more origins diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index 79cabf4e5..ee1c00e97 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -33,11 +33,13 @@ static dynamic_reference_nocheck* sha1; struct WebSocketConfig { + typedef std::vector ProxyRanges; + // The HTTP origins that can connect to the server. OriginList allowedorigins; - // Whether to trust the X-Real-IP or X-Forwarded-For headers. - bool behindproxy; + // The IP ranges which send trustworthy X-Real-IP or X-Forwarded-For headers. + ProxyRanges proxyranges; // Whether to send as UTF-8 text instead of binary data. bool sendastext; @@ -343,7 +345,7 @@ class WebSocketHook : public IOHookMiddle return -1; } - if (config.behindproxy && sock->type == StreamSocket::SS_USER) + if (!config.proxyranges.empty() && sock->type == StreamSocket::SS_USER) { LocalUser* luser = static_cast(sock)->user; irc::sockets::sockaddrs realsa(luser->client_sa); @@ -360,9 +362,16 @@ class WebSocketHook : public IOHookMiddle // Nothing to do here. } - // Give the user their real IP address. - if (realsa != luser->client_sa) - luser->SetClientIP(realsa); + for (WebSocketConfig::ProxyRanges::const_iterator iter = config.proxyranges.begin(); iter != config.proxyranges.end(); ++iter) + { + if (InspIRCd::MatchCIDR(*iter, luser->GetIPString(), ascii_case_insensitive_map)) + { + // Give the user their real IP address. + if (realsa == luser->client_sa) + luser->SetClientIP(realsa); + break; + } + } } @@ -518,9 +527,12 @@ class ModuleWebSocket : public Module } ConfigTag* tag = ServerInstance->Config->ConfValue("websocket"); - config.behindproxy = tag->getBool("behindproxy"); config.sendastext = tag->getBool("sendastext", true); + irc::spacesepstream proxyranges(tag->getString("proxyranges")); + for (std::string proxyrange; proxyranges.GetToken(proxyrange); ) + config.proxyranges.push_back(proxyrange); + // Everything is okay; apply the new config. hookprov->config = config; } -- cgit v1.3.1-10-gc9f91 From 965460400b271a178cc415783414de43c89341bf Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Fri, 29 Nov 2019 11:11:11 +0000 Subject: WebSocket: move the OriginList typedef inside WebSocketConfig. --- src/modules/m_websocket.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/modules/m_websocket.cpp') diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index ee1c00e97..5f0f9bcc8 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -25,14 +25,13 @@ #include -typedef std::vector OriginList; - static const char MagicGUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static const char whitespace[] = " \t\r\n"; static dynamic_reference_nocheck* sha1; struct WebSocketConfig { + typedef std::vector OriginList; typedef std::vector ProxyRanges; // The HTTP origins that can connect to the server. @@ -329,7 +328,7 @@ class WebSocketHook : public IOHookMiddle if (originheader.Find(recvq, "Origin:", 7, reqend)) { const std::string origin = originheader.ExtractValue(recvq); - for (OriginList::const_iterator iter = config.allowedorigins.begin(); iter != config.allowedorigins.end(); ++iter) + for (WebSocketConfig::OriginList::const_iterator iter = config.allowedorigins.begin(); iter != config.allowedorigins.end(); ++iter) { if (InspIRCd::Match(origin, *iter, ascii_case_insensitive_map)) { -- cgit v1.3.1-10-gc9f91 From aea5500b46890665ccb26d436217cb7014e93a32 Mon Sep 17 00:00:00 2001 From: iwalkalone Date: Fri, 6 Dec 2019 18:07:49 +0100 Subject: Fixing MatchCIDR call when checking proxy range --- src/modules/m_websocket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/modules/m_websocket.cpp') diff --git a/src/modules/m_websocket.cpp b/src/modules/m_websocket.cpp index 5f0f9bcc8..8ec896847 100644 --- a/src/modules/m_websocket.cpp +++ b/src/modules/m_websocket.cpp @@ -363,10 +363,10 @@ class WebSocketHook : public IOHookMiddle for (WebSocketConfig::ProxyRanges::const_iterator iter = config.proxyranges.begin(); iter != config.proxyranges.end(); ++iter) { - if (InspIRCd::MatchCIDR(*iter, luser->GetIPString(), ascii_case_insensitive_map)) + if (InspIRCd::MatchCIDR(luser->GetIPString(), *iter, ascii_case_insensitive_map)) { // Give the user their real IP address. - if (realsa == luser->client_sa) + if (realsa != luser->client_sa) luser->SetClientIP(realsa); break; } -- cgit v1.3.1-10-gc9f91