Webserver/API access is only allowed from these subnets.
+.. _setting-webserver-hash-plaintext-credentials:
+
+``webserver-hash-plaintext-credentials``
+----------------------------------------
+..versionadded:: 4.6.0
+
+- Boolean
+- Default: no
+
+Whether passwords and API keys supplied as plaintext should be hashed during startup, to prevent the plaintext versions from staying in memory. Doing so increases significantly the cost of verifying credentials.
+
.. _setting-webserver-loglevel:
``webserver-loglevel``
::arg().set("webserver-allow-from","Webserver/API access is only allowed from these subnets")="127.0.0.1,::1";
::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
::arg().set("webserver-max-bodysize","Webserver/API maximum request/response body size in megabytes")="2";
+ ::arg().setSwitch("webserver-hash-plaintext-credentials","Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime")="no";
::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
/* if the password is in cleartext and hashing is available,
the hashed form will be kept in memory */
-CredentialsHolder::CredentialsHolder(std::string&& password)
+CredentialsHolder::CredentialsHolder(std::string&& password, bool hashPlaintext)
{
bool locked = false;
if (isHashingAvailable()) {
if (!isPasswordHashed(password)) {
- d_credentials = hashPassword(password);
- locked = true;
+ if (hashPlaintext) {
+ d_credentials = hashPassword(password);
+ locked = true;
+ d_isHashed = true;
+ }
}
else {
d_wasHashed = true;
+ d_isHashed = true;
d_credentials = std::move(password);
}
}
- else {
+
+ if (!d_isHashed) {
d_fallbackHashPerturb = random();
d_fallbackHash = burtle(reinterpret_cast<const unsigned char*>(password.data()), password.size(), d_fallbackHashPerturb);
d_credentials = std::move(password);
bool CredentialsHolder::matches(const std::string& password) const
{
- if (isHashingAvailable()) {
+ if (d_isHashed) {
return verifyPassword(d_credentials, password);
}
else {
public:
/* if the password is in cleartext and hashing is available,
the hashed form will be kept in memory */
- CredentialsHolder(std::string&& password);
+ CredentialsHolder(std::string&& password, bool hashPlaintext);
~CredentialsHolder();
CredentialsHolder(const CredentialsHolder&) = delete;
{
return d_wasHashed;
}
+ /* whether it is hashed in memory */
+ bool isHashed() const
+ {
+ return d_isHashed;
+ }
static bool isHashingAvailable();
uint32_t d_fallbackHash{0};
/* whether it was constructed from a hashed and salted string */
bool d_wasHashed{false};
+ /* whether it is hashed in memory */
+ bool d_isHashed{false};
};
SListen(sock, 5);
auto launch=[sock, local, password, apiKey, customHeaders, acl]() {
if (password) {
- auto holder = make_unique<CredentialsHolder>(std::string(*password));
+ auto holder = make_unique<CredentialsHolder>(std::string(*password), false);
if (!holder->wasHashed() && holder->isHashingAvailable()) {
warnlog("Passing a plain-text password to 'webserver()' is deprecated, please use 'setWebserverConfig()' with a hashed password instead.");
}
}
if (apiKey) {
- auto holder = make_unique<CredentialsHolder>(std::string(*apiKey));
+ auto holder = make_unique<CredentialsHolder>(std::string(*apiKey), false);
setWebserverAPIKey(std::move(holder));
}
return ;
}
+ bool hashPlaintextCredentials = false;
+ if (vars->count("hashPlaintextCredentials")) {
+ hashPlaintextCredentials = boost::get<bool>(vars->at("hashPlaintextCredentials"));
+ }
+
if (vars->count("password")) {
std::string password = boost::get<std::string>(vars->at("password"));
- auto holder = make_unique<CredentialsHolder>(std::move(password));
+ auto holder = make_unique<CredentialsHolder>(std::move(password), hashPlaintextCredentials);
if (!holder->wasHashed() && holder->isHashingAvailable()) {
warnlog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is deprecated, please generate a hashed one using 'hashPassword()' instead.");
}
if (vars->count("apiKey")) {
std::string apiKey = boost::get<std::string>(vars->at("apiKey"));
- auto holder = make_unique<CredentialsHolder>(std::move(apiKey));
+ auto holder = make_unique<CredentialsHolder>(std::move(apiKey), hashPlaintextCredentials);
if (!holder->wasHashed() && holder->isHashingAvailable()) {
warnlog("Passing a plain-text API key via the 'apiKey' parameter to 'setWebserverConfig()' is deprecated, please generate a hashed one using 'hashPassword()' instead.");
}
.. versionchanged:: 1.7.0
The optional ``password`` and ``apiKey`` parameters now accept hashed passwords.
+ The optional ``hashPlaintextCredentials`` parameter has been added.
Setup webserver configuration. See :func:`webserver`.
* ``acl=newACL``: string - List of IP addresses, as a string, that are allowed to open a connection to the web server. Defaults to "127.0.0.1, ::1".
* ``statsRequireAuthentication``: bool - Whether access to the statistics (/metrics and /jsonstat endpoints) require a valid password or API key. Defaults to true.
* ``maxConcurrentConnections``: int - The maximum number of concurrent web connections, or 0 which means an unlimited number. Defaults to 100.
+ * ``hashPlaintextCredentials``: bool - Whether passwords and API keys provided in plaintext should be hashed during startup, to prevent the plaintext versions from staying in memory. Doing so increases significantly the cost of verifying credentials. Defaults to false.
.. function:: registerWebHandler(path, handler)
::arg().set("webserver-password", "Password required for accessing the webserver") = "";
::arg().set("webserver-allow-from","Webserver access is only allowed from these subnets")="127.0.0.1,::1";
::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
+ ::arg().setSwitch("webserver-hash-plaintext-credentials","Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime")="no";
::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats")="";
::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address")="";
::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates")="30";
specifying an IP address without a netmask uses an implicit netmask
of /32 or /128.
+.. _setting-webserver-hash-plaintext-credentials:
+
+``webserver-hash-plaintext-credentials``
+----------------------------------------
+..versionadded:: 4.6.0
+
+- Boolean
+- Default: no
+
+Whether passwords and API keys supplied as plaintext should be hashed during startup, to prevent the plaintext versions from staying in memory. Doing so increases significantly the cost of verifying credentials.
+
.. _setting-webserver-loglevel:
``webserver-loglevel``
{
const std::string plaintext("test");
- auto holder = CredentialsHolder(std::string(plaintext));
+ auto holder = CredentialsHolder(std::string(plaintext), false);
BOOST_CHECK(holder.matches(plaintext));
BOOST_CHECK(!holder.matches("not test"));
BOOST_CHECK(!holder.wasHashed());
+ BOOST_CHECK(!holder.isHashed());
#ifdef HAVE_CRYPTO_PWHASH_STR
BOOST_CHECK(CredentialsHolder::isHashingAvailable());
const std::string sampleHash("$argon2id$v=19$m=65536,t=2,p=1$ndQKu3+ZsWedqRrlNFUaNw$tnb0MJVe5C2hlqkDt0Ln3R6VKCYkfMYdxDy+puXes3s");
- auto fromHashedHolder = CredentialsHolder(std::string(sampleHash));
+ auto fromHashedHolder = CredentialsHolder(std::string(sampleHash), true);
BOOST_CHECK(fromHashedHolder.wasHashed());
+ BOOST_CHECK(fromHashedHolder.isHashed());
BOOST_CHECK(fromHashedHolder.matches(plaintext));
BOOST_CHECK(!fromHashedHolder.matches("not test"));
+
+ auto fromPlaintextHolder = CredentialsHolder(std::string(plaintext), true);
+ BOOST_CHECK(!fromPlaintextHolder.wasHashed());
+ BOOST_CHECK(fromPlaintextHolder.isHashed());
+ BOOST_CHECK(fromPlaintextHolder.matches(plaintext));
+ BOOST_CHECK(!fromPlaintextHolder.matches("not test"));
#else
BOOST_CHECK(!CredentialsHolder::isHashingAvailable());
#endif
WebServer(string listenaddress, int port);
virtual ~WebServer() { };
- void setApiKey(const string &apikey) {
+ void setApiKey(const string &apikey, bool hashPlaintext) {
if (!apikey.empty()) {
- d_apikey = make_unique<CredentialsHolder>(std::string(apikey));
+ d_apikey = make_unique<CredentialsHolder>(std::string(apikey), hashPlaintext);
}
else {
d_apikey.reset();
}
}
- void setPassword(const string &password) {
+ void setPassword(const string &password, bool hashPlaintext) {
if (!password.empty()) {
- d_webserverPassword = make_unique<CredentialsHolder>(std::string(password));
+ d_webserverPassword = make_unique<CredentialsHolder>(std::string(password), hashPlaintext);
}
else {
d_webserverPassword.reset();
d_min5(0),
d_min1(0)
{
- if(arg().mustDo("webserver") || arg().mustDo("api")) {
+ if (arg().mustDo("webserver") || arg().mustDo("api")) {
d_ws = unique_ptr<WebServer>(new WebServer(arg()["webserver-address"], arg().asNum("webserver-port")));
- d_ws->setApiKey(arg()["api-key"]);
- d_ws->setPassword(arg()["webserver-password"]);
+ d_ws->setApiKey(arg()["api-key"], arg().mustDo("webserver-hash-plaintext-credentials"));
+ d_ws->setPassword(arg()["webserver-password"], arg().mustDo("webserver-hash-plaintext-credentials"));
d_ws->setLogLevel(arg()["webserver-loglevel"]);
NetmaskGroup acl;
d_ws = make_unique<AsyncWebServer>(fdm, arg()["webserver-address"], arg().asNum("webserver-port"));
- d_ws->setApiKey(arg()["api-key"]);
- d_ws->setPassword(arg()["webserver-password"]);
+ d_ws->setApiKey(arg()["api-key"], arg().mustDo("webserver-hash-plaintext-credentials"));
+ d_ws->setPassword(arg()["webserver-password"], arg().mustDo("webserver-hash-plaintext-credentials"));
d_ws->setLogLevel(arg()["webserver-loglevel"]);
NetmaskGroup acl;