mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-02-05 11:24:15 +00:00
Merge pull request #17684 from Chocobo1/subnet
Allow to use subnet notation in reverse proxy list
This commit is contained in:
commit
257914b0d5
@ -548,10 +548,9 @@ QVector<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
|
|||||||
|
|
||||||
for (const QString &rawSubnet : subnets)
|
for (const QString &rawSubnet : subnets)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
const std::optional<Utils::Net::Subnet> subnet = Utils::Net::parseSubnet(rawSubnet.trimmed());
|
||||||
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(rawSubnet.trimmed(), &ok);
|
if (subnet)
|
||||||
if (ok)
|
ret.append(subnet.value());
|
||||||
ret.append(subnet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -561,9 +560,7 @@ void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
|
|||||||
{
|
{
|
||||||
Algorithm::removeIf(subnets, [](const QString &subnet)
|
Algorithm::removeIf(subnets, [](const QString &subnet)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
return !Utils::Net::parseSubnet(subnet.trimmed()).has_value();
|
||||||
Utils::Net::parseSubnet(subnet.trimmed(), &ok);
|
|
||||||
return !ok;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setValue(u"Preferences/WebUI/AuthSubnetWhitelist"_qs, subnets);
|
setValue(u"Preferences/WebUI/AuthSubnetWhitelist"_qs, subnets);
|
||||||
|
@ -47,22 +47,15 @@ namespace Utils
|
|||||||
return !QHostAddress(ip).isNull();
|
return !QHostAddress(ip).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Subnet parseSubnet(const QString &subnetStr, bool *ok)
|
std::optional<Subnet> parseSubnet(const QString &subnetStr)
|
||||||
{
|
{
|
||||||
const Subnet invalid = qMakePair(QHostAddress(), -1);
|
|
||||||
const Subnet subnet = QHostAddress::parseSubnet(subnetStr);
|
const Subnet subnet = QHostAddress::parseSubnet(subnetStr);
|
||||||
if (ok)
|
const Subnet invalid = {QHostAddress(), -1};
|
||||||
*ok = (subnet != invalid);
|
if (subnet == invalid)
|
||||||
|
return std::nullopt;
|
||||||
return subnet;
|
return subnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canParseSubnet(const QString &subnetStr)
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
parseSubnet(subnetStr, &ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLoopbackAddress(const QHostAddress &addr)
|
bool isLoopbackAddress(const QHostAddress &addr)
|
||||||
{
|
{
|
||||||
return (addr == QHostAddress::LocalHost)
|
return (addr == QHostAddress::LocalHost)
|
||||||
@ -70,7 +63,7 @@ namespace Utils
|
|||||||
|| (addr == QHostAddress(u"::ffff:127.0.0.1"_qs));
|
|| (addr == QHostAddress(u"::ffff:127.0.0.1"_qs));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isIPInRange(const QHostAddress &addr, const QVector<Subnet> &subnets)
|
bool isIPInSubnets(const QHostAddress &addr, const QVector<Subnet> &subnets)
|
||||||
{
|
{
|
||||||
QHostAddress protocolEquivalentAddress;
|
QHostAddress protocolEquivalentAddress;
|
||||||
bool addrConversionOk = false;
|
bool addrConversionOk = false;
|
||||||
|
@ -28,8 +28,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QHostAddress>
|
#include <optional>
|
||||||
|
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
class QSslCertificate;
|
class QSslCertificate;
|
||||||
class QSslKey;
|
class QSslKey;
|
||||||
@ -37,13 +39,13 @@ class QString;
|
|||||||
|
|
||||||
namespace Utils::Net
|
namespace Utils::Net
|
||||||
{
|
{
|
||||||
|
// alias for `QHostAddress::parseSubnet()` return type
|
||||||
using Subnet = QPair<QHostAddress, int>;
|
using Subnet = QPair<QHostAddress, int>;
|
||||||
|
|
||||||
bool isValidIP(const QString &ip);
|
bool isValidIP(const QString &ip);
|
||||||
Subnet parseSubnet(const QString &subnetStr, bool *ok = nullptr);
|
std::optional<Subnet> parseSubnet(const QString &subnetStr);
|
||||||
bool canParseSubnet(const QString &subnetStr);
|
|
||||||
bool isLoopbackAddress(const QHostAddress &addr);
|
bool isLoopbackAddress(const QHostAddress &addr);
|
||||||
bool isIPInRange(const QHostAddress &addr, const QVector<Subnet> &subnets);
|
bool isIPInSubnets(const QHostAddress &addr, const QVector<Subnet> &subnets);
|
||||||
QString subnetToString(const Subnet &subnet);
|
QString subnetToString(const Subnet &subnet);
|
||||||
QHostAddress canonicalIPv6Addr(const QHostAddress &addr);
|
QHostAddress canonicalIPv6Addr(const QHostAddress &addr);
|
||||||
|
|
||||||
|
@ -90,16 +90,15 @@ void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted()
|
|||||||
|
|
||||||
void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked()
|
void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked()
|
||||||
{
|
{
|
||||||
bool ok = false;
|
const std::optional<Utils::Net::Subnet> subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text());
|
||||||
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text(), &ok);
|
if (!subnet)
|
||||||
if (!ok)
|
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid."));
|
QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_model->insertRow(m_model->rowCount());
|
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_ui->txtIPSubnet->clear();
|
||||||
m_modified = true;
|
m_modified = true;
|
||||||
}
|
}
|
||||||
@ -114,5 +113,5 @@ void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked()
|
|||||||
|
|
||||||
void IPSubnetWhitelistOptionsDialog::on_txtIPSubnet_textChanged(const QString &subnetStr)
|
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());
|
||||||
}
|
}
|
||||||
|
@ -3397,7 +3397,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="textTrustedReverseProxiesList">
|
<widget class="QLineEdit" name="textTrustedReverseProxiesList">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Specify reverse proxy IPs in order to use forwarded client address (X-Forwarded-For attribute), use ';' to split multiple entries.</string>
|
<string>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.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "base/global.h"
|
|
||||||
#include "base/http/httperror.h"
|
#include "base/http/httperror.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
@ -402,15 +401,29 @@ void WebApplication::configure()
|
|||||||
m_isReverseProxySupportEnabled = pref->isWebUIReverseProxySupportEnabled();
|
m_isReverseProxySupportEnabled = pref->isWebUIReverseProxySupportEnabled();
|
||||||
if (m_isReverseProxySupportEnabled)
|
if (m_isReverseProxySupportEnabled)
|
||||||
{
|
{
|
||||||
m_trustedReverseProxyList.clear();
|
|
||||||
|
|
||||||
const QStringList proxyList = pref->getWebUITrustedReverseProxiesList().split(u';', Qt::SkipEmptyParts);
|
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 (!proxy.contains(u'/'))
|
||||||
if (ip.setAddress(proxy))
|
{
|
||||||
m_trustedReverseProxyList.push_back(ip);
|
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<Utils::Net::Subnet> subnet = Utils::Net::parseSubnet(proxy);
|
||||||
|
if (subnet)
|
||||||
|
m_trustedReverseProxyList.push_back(subnet.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_trustedReverseProxyList.isEmpty())
|
if (m_trustedReverseProxyList.isEmpty())
|
||||||
@ -579,7 +592,7 @@ bool WebApplication::isAuthNeeded()
|
|||||||
{
|
{
|
||||||
if (!m_isLocalAuthEnabled && Utils::Net::isLoopbackAddress(m_clientAddress))
|
if (!m_isLocalAuthEnabled && Utils::Net::isLoopbackAddress(m_clientAddress))
|
||||||
return false;
|
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 false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -728,7 +741,7 @@ QHostAddress WebApplication::resolveClientAddress() const
|
|||||||
return m_env.clientAddress;
|
return m_env.clientAddress;
|
||||||
|
|
||||||
// Only reverse proxy can overwrite client address
|
// 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;
|
return m_env.clientAddress;
|
||||||
|
|
||||||
const QString forwardedFor = m_request.headers.value(Http::HEADER_X_FORWARDED_FOR);
|
const QString forwardedFor = m_request.headers.value(Http::HEADER_X_FORWARDED_FOR);
|
||||||
|
@ -34,11 +34,13 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QHostAddress>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/applicationcomponent.h"
|
#include "base/applicationcomponent.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
@ -233,7 +235,7 @@ private:
|
|||||||
|
|
||||||
// Reverse proxy
|
// Reverse proxy
|
||||||
bool m_isReverseProxySupportEnabled;
|
bool m_isReverseProxySupportEnabled;
|
||||||
QVector<QHostAddress> m_trustedReverseProxyList;
|
QVector<Utils::Net::Subnet> m_trustedReverseProxyList;
|
||||||
QHostAddress m_clientAddress;
|
QHostAddress m_clientAddress;
|
||||||
|
|
||||||
QVector<Http::Header> m_prebuiltHeaders;
|
QVector<Http::Header> m_prebuiltHeaders;
|
||||||
|
@ -847,7 +847,11 @@
|
|||||||
<label for="webui_domain_textarea">QBT_TR(Server domains:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
<label for="webui_domain_textarea">QBT_TR(Server domains:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<textarea id="webui_domain_textarea" rows="1" cols="60"></textarea>
|
<textarea id="webui_domain_textarea" rows="1" cols="60" title="QBT_TR(Whitelist for filtering HTTP Host header values.
|
||||||
|
In order to defend against DNS rebinding attack,
|
||||||
|
you should put in domain names used by WebUI server.
|
||||||
|
|
||||||
|
Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsDialog]"></textarea>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -869,7 +873,7 @@
|
|||||||
</legend>
|
</legend>
|
||||||
<div class="formRow">
|
<div class="formRow">
|
||||||
<label for="webUIReverseProxiesListTextarea">QBT_TR(Trusted proxies list:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
<label for="webUIReverseProxiesListTextarea">QBT_TR(Trusted proxies list:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
<input type="text" id="webUIReverseProxiesListTextarea" />
|
<input type="text" id="webUIReverseProxiesListTextarea" title="QBT_TR(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.)QBT_TR[CONTEXT=OptionsDialog]" />
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user