From 4094a4c448fb0a5b30c182d02c2cf2a53c7ab36f Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 7 Sep 2022 12:51:01 +0800 Subject: [PATCH 1/4] Simplify functions --- src/base/preferences.cpp | 11 ++++------- src/base/utils/net.cpp | 15 ++++----------- src/base/utils/net.h | 8 +++++--- src/gui/ipsubnetwhitelistoptionsdialog.cpp | 9 ++++----- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 7742ac281..b76198e93 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -548,10 +548,9 @@ QVector Preferences::getWebUiAuthSubnetWhitelist() const for (const QString &rawSubnet : subnets) { - bool ok = false; - const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(rawSubnet.trimmed(), &ok); - if (ok) - ret.append(subnet); + const std::optional subnet = Utils::Net::parseSubnet(rawSubnet.trimmed()); + if (subnet) + ret.append(subnet.value()); } return ret; @@ -561,9 +560,7 @@ void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets) { Algorithm::removeIf(subnets, [](const QString &subnet) { - bool ok = false; - Utils::Net::parseSubnet(subnet.trimmed(), &ok); - return !ok; + return !Utils::Net::parseSubnet(subnet.trimmed()).has_value(); }); setValue(u"Preferences/WebUI/AuthSubnetWhitelist"_qs, subnets); diff --git a/src/base/utils/net.cpp b/src/base/utils/net.cpp index aab83656c..4d46323b6 100644 --- a/src/base/utils/net.cpp +++ b/src/base/utils/net.cpp @@ -47,22 +47,15 @@ namespace Utils return !QHostAddress(ip).isNull(); } - Subnet parseSubnet(const QString &subnetStr, bool *ok) + std::optional parseSubnet(const QString &subnetStr) { - const Subnet invalid = qMakePair(QHostAddress(), -1); const Subnet subnet = QHostAddress::parseSubnet(subnetStr); - if (ok) - *ok = (subnet != invalid); + const Subnet invalid = {QHostAddress(), -1}; + if (subnet == invalid) + return std::nullopt; return subnet; } - bool canParseSubnet(const QString &subnetStr) - { - bool ok = false; - parseSubnet(subnetStr, &ok); - return ok; - } - bool isLoopbackAddress(const QHostAddress &addr) { return (addr == QHostAddress::LocalHost) diff --git a/src/base/utils/net.h b/src/base/utils/net.h index f9712d59c..3f885f852 100644 --- a/src/base/utils/net.h +++ b/src/base/utils/net.h @@ -28,8 +28,10 @@ #pragma once -#include +#include + #include +#include class QSslCertificate; class QSslKey; @@ -37,11 +39,11 @@ class QString; namespace Utils::Net { + // alias for `QHostAddress::parseSubnet()` return type using Subnet = QPair; bool isValidIP(const QString &ip); - Subnet parseSubnet(const QString &subnetStr, bool *ok = nullptr); - bool canParseSubnet(const QString &subnetStr); + std::optional parseSubnet(const QString &subnetStr); bool isLoopbackAddress(const QHostAddress &addr); bool isIPInRange(const QHostAddress &addr, const QVector &subnets); QString subnetToString(const Subnet &subnet); diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.cpp b/src/gui/ipsubnetwhitelistoptionsdialog.cpp index 96da8fcbc..3a0e92a2f 100644 --- a/src/gui/ipsubnetwhitelistoptionsdialog.cpp +++ b/src/gui/ipsubnetwhitelistoptionsdialog.cpp @@ -90,16 +90,15 @@ void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted() void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked() { - bool ok = false; - const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text(), &ok); - if (!ok) + const std::optional subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text()); + if (!subnet) { QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid.")); return; } m_model->insertRow(m_model->rowCount()); - m_model->setData(m_model->index(m_model->rowCount() - 1, 0), Utils::Net::subnetToString(subnet)); + m_model->setData(m_model->index(m_model->rowCount() - 1, 0), Utils::Net::subnetToString(subnet.value())); m_ui->txtIPSubnet->clear(); m_modified = true; } @@ -114,5 +113,5 @@ void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked() void IPSubnetWhitelistOptionsDialog::on_txtIPSubnet_textChanged(const QString &subnetStr) { - m_ui->buttonWhitelistIPSubnet->setEnabled(Utils::Net::canParseSubnet(subnetStr)); + m_ui->buttonWhitelistIPSubnet->setEnabled(Utils::Net::parseSubnet(subnetStr).has_value()); } From 851374e517ff1d2647eca699bf506f13fd0fa55c Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 7 Sep 2022 13:25:29 +0800 Subject: [PATCH 2/4] Rename function --- src/base/utils/net.cpp | 2 +- src/base/utils/net.h | 2 +- src/webui/webapplication.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/base/utils/net.cpp b/src/base/utils/net.cpp index 4d46323b6..4a8e5690f 100644 --- a/src/base/utils/net.cpp +++ b/src/base/utils/net.cpp @@ -63,7 +63,7 @@ namespace Utils || (addr == QHostAddress(u"::ffff:127.0.0.1"_qs)); } - bool isIPInRange(const QHostAddress &addr, const QVector &subnets) + bool isIPInSubnets(const QHostAddress &addr, const QVector &subnets) { QHostAddress protocolEquivalentAddress; bool addrConversionOk = false; diff --git a/src/base/utils/net.h b/src/base/utils/net.h index 3f885f852..f3c5d8ddd 100644 --- a/src/base/utils/net.h +++ b/src/base/utils/net.h @@ -45,7 +45,7 @@ namespace Utils::Net bool isValidIP(const QString &ip); std::optional parseSubnet(const QString &subnetStr); bool isLoopbackAddress(const QHostAddress &addr); - bool isIPInRange(const QHostAddress &addr, const QVector &subnets); + bool isIPInSubnets(const QHostAddress &addr, const QVector &subnets); QString subnetToString(const Subnet &subnet); QHostAddress canonicalIPv6Addr(const QHostAddress &addr); diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 179db7108..a22002c45 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -579,7 +579,7 @@ bool WebApplication::isAuthNeeded() { if (!m_isLocalAuthEnabled && Utils::Net::isLoopbackAddress(m_clientAddress)) return false; - if (m_isAuthSubnetWhitelistEnabled && Utils::Net::isIPInRange(m_clientAddress, m_authSubnetWhitelist)) + if (m_isAuthSubnetWhitelistEnabled && Utils::Net::isIPInSubnets(m_clientAddress, m_authSubnetWhitelist)) return false; return true; } From 109c45bb959d89b7e70d933c0d4515fa4d0b39fc Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 7 Sep 2022 13:29:46 +0800 Subject: [PATCH 3/4] Allow to use subnet notation in reverse proxy list Closes #17475. --- src/webui/webapplication.cpp | 29 +++++++++++++++++++++-------- src/webui/webapplication.h | 4 +++- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index a22002c45..d35a48c87 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -42,7 +42,6 @@ #include #include "base/algorithm.h" -#include "base/global.h" #include "base/http/httperror.h" #include "base/logger.h" #include "base/preferences.h" @@ -402,15 +401,29 @@ void WebApplication::configure() m_isReverseProxySupportEnabled = pref->isWebUIReverseProxySupportEnabled(); if (m_isReverseProxySupportEnabled) { - m_trustedReverseProxyList.clear(); - const QStringList proxyList = pref->getWebUITrustedReverseProxiesList().split(u';', Qt::SkipEmptyParts); - for (const QString &proxy : proxyList) + m_trustedReverseProxyList.clear(); + m_trustedReverseProxyList.reserve(proxyList.size()); + + for (QString proxy : proxyList) { - QHostAddress ip; - if (ip.setAddress(proxy)) - m_trustedReverseProxyList.push_back(ip); + if (!proxy.contains(u'/')) + { + const QAbstractSocket::NetworkLayerProtocol protocol = QHostAddress(proxy).protocol(); + if (protocol == QAbstractSocket::IPv4Protocol) + { + proxy.append(u"/32"); + } + else if (protocol == QAbstractSocket::IPv6Protocol) + { + proxy.append(u"/128"); + } + } + + const std::optional subnet = Utils::Net::parseSubnet(proxy); + if (subnet) + m_trustedReverseProxyList.push_back(subnet.value()); } if (m_trustedReverseProxyList.isEmpty()) @@ -728,7 +741,7 @@ QHostAddress WebApplication::resolveClientAddress() const return m_env.clientAddress; // Only reverse proxy can overwrite client address - if (!m_trustedReverseProxyList.contains(m_env.clientAddress)) + if (!Utils::Net::isIPInSubnets(m_env.clientAddress, m_trustedReverseProxyList)) return m_env.clientAddress; const QString forwardedFor = m_request.headers.value(Http::HEADER_X_FORWARDED_FOR); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 02a8d576b..f5b0034fd 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -34,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "base/applicationcomponent.h" #include "base/global.h" @@ -233,7 +235,7 @@ private: // Reverse proxy bool m_isReverseProxySupportEnabled; - QVector m_trustedReverseProxyList; + QVector m_trustedReverseProxyList; QHostAddress m_clientAddress; QVector m_prebuiltHeaders; From 5cfaaa5d1af3f4699e7ff916de88b268b67cbe65 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 7 Sep 2022 13:37:58 +0800 Subject: [PATCH 4/4] Add tooltips --- src/gui/optionsdialog.ui | 2 +- src/webui/www/private/views/preferences.html | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 35b7664a5..284107c85 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3397,7 +3397,7 @@ Use ';' to split multiple entries. Can use wildcard '*'. - Specify reverse proxy IPs in order to use forwarded client address (X-Forwarded-For attribute), use ';' to split multiple entries. + Specify reverse proxy IPs (or subnets, e.g. 0.0.0.0/24) in order to use forwarded client address (X-Forwarded-For header). Use ';' to split multiple entries. diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 4f3939849..7328dc972 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -847,7 +847,11 @@ - + @@ -869,7 +873,7 @@
- +