2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[#1721] Added redactConfig function

This commit is contained in:
Francis Dupont
2021-03-23 00:23:12 +01:00
committed by Andrei Pavel
parent 406859c21f
commit 396c9ea6b9
9 changed files with 183 additions and 77 deletions

View File

@@ -11,6 +11,7 @@
#include <cc/simple_parser.h>
#include <cc/command_interpreter.h>
#include <http/basic_auth_config.h>
#include <process/redact_config.h>
#include <exceptions/exceptions.h>
using namespace isc::config;
@@ -141,62 +142,17 @@ CtrlAgentCfgMgr::parse(ConstElementPtr config_set, bool check_only) {
ConstElementPtr
CtrlAgentCfgMgr::redactConfig(ConstElementPtr config) const {
bool redacted = false;
ConstElementPtr result = redactElement(config, redacted);
const std::set<std::string> follow = {
"Control-agent", "authentication", "clients"
};
ConstElementPtr result =
isc::process::redactConfig(config, redacted, follow);
if (redacted) {
return (result);
}
return (config);
}
ConstElementPtr
CtrlAgentCfgMgr::redactElement(ConstElementPtr elem, bool& redacted) const {
// From isc::data::copy.
if (!elem) {
isc_throw(BadValue, "redactElement got a null pointer");
}
// Redact lists.
if (elem->getType() == Element::list) {
ElementPtr result = ElementPtr(new ListElement());
for (auto item : elem->listValue()) {
// add wants a ElementPtr so use a shallow copy.
ElementPtr copy = data::copy(redactElement(item, redacted), 0);
result->add(copy);
}
if (redacted) {
return (result);
}
return (elem);
}
// Redact maps.
if (elem->getType() == Element::map) {
ElementPtr result = ElementPtr(new MapElement());
for (auto kv : elem->mapValue()) {
auto key = kv.first;
auto value = kv.second;
if (key == "password") {
// Handle passwords.
redacted = true;
result->set(key, Element::create(std::string("*****")));
} else if ((key == "Control-agent") ||
(key == "authentication") ||
(key == "clients")) {
// Handle the arc where are passwords.
result->set(key, redactElement(value, redacted));
} else {
// Default case: no password here.
result->set(key, value);
}
}
if (redacted) {
return (result);
}
return (elem);
}
// Handle other element types.
return (elem);
}
data::ConstElementPtr
CtrlAgentCfgContext::getControlSocketInfo(const std::string& service) const {
auto si = ctrl_sockets_.find(service);

View File

@@ -308,20 +308,6 @@ protected:
/// replaced by asterisks so can be safely logged to an unprivileged place.
virtual isc::data::ConstElementPtr
redactConfig(isc::data::ConstElementPtr config) const;
private:
/// @brief Redact an element.
///
/// Recursive helper of redactConfig.
///
/// @param elem An element to redact.
/// @param redacted The reference to redacted flag: true means the result
/// was redacted so cannot be shared.
/// @return unmodified element or a copy of the element: in the second
/// case embedded passwords were replaced by asterisks and the redacted
/// flag was set to true.
virtual isc::data::ConstElementPtr
redactElement(isc::data::ConstElementPtr elem, bool& redacted) const;
};
/// @brief Defines a shared pointer to CtrlAgentCfgMgr.

View File

@@ -28,6 +28,7 @@ libkea_process_la_SOURCES += daemon.cc daemon.h
libkea_process_la_SOURCES += log_parser.cc log_parser.h
libkea_process_la_SOURCES += logging_info.cc logging_info.h
libkea_process_la_SOURCES += process_messages.cc process_messages.h
libkea_process_la_SOURCES += redact_config.cc redact_config.h
libkea_process_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_process_la_CPPFLAGS = $(AM_CPPFLAGS)
@@ -95,4 +96,5 @@ libkea_process_include_HEADERS = \
d_process.h \
logging_info.h \
log_parser.h \
process_messages.h
process_messages.h \
redact_config.h

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,6 +11,7 @@
#include <process/d_log.h>
#include <process/d_cfg_mgr.h>
#include <process/daemon.h>
#include <process/redact_config.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
@@ -56,8 +57,15 @@ DCfgMgrBase::setContext(ConfigPtr& context) {
context_ = context;
}
isc::data::ConstElementPtr
DCfgMgrBase::redactConfig(isc::data::ConstElementPtr config_set) const {
ConstElementPtr
DCfgMgrBase::redactConfig(ConstElementPtr config_set) const {
bool redacted = false;
set<string> follow = { };
ConstElementPtr result =
isc::process::redactConfig(config_set, redacted, follow);
if (redacted) {
return (result);
}
return (config_set);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -212,8 +212,12 @@ protected:
/// @brief Redact the configuration.
///
/// This method replaces passwords by asterisks. By default it does
/// nothing: derived class must redefine it.
/// This method replaces passwords and secrets by asterisks. By
/// default it follows all subtrees at the exception of user
/// contexts. Please derive the method to allow a reasonable
/// performance by following only subtrees where the syntax allows
/// the presence of passwords and secrets.
///
/// @param config the Element tree structure that describes the configuration.
/// @return unmodified config or a copy of the config where passwords were

View File

@@ -166,7 +166,7 @@ during startup and the subsequent receipt of a SIGHUP:
@section redact Redact Passwords
There are two tools to remove sensitive data as passwords from logs:
There are two tools to remove sensitive data as passwords or secrets from logs:
- redactedAccessString for database access strings
- redactConfig for full configurations
@@ -174,11 +174,8 @@ The redactConfig method must be defined in derived classes following this
procedure:
- take the grammar (bison input file with the .yy extension)
- get the arcs between the start symbol and tokens handling sensitive
data e.g. PASSWORD
- walk the full configuration following only these arcs (so ignoring
other parts of the configuration tree)
- replace sensitive data by something else e.g. "*****"
- try to share unchanged subtrees vs. copy to identical.
data i.e. passwords and secrets
- give the set of keywords of these args to the redactConfig function
@section cplMTConsiderations Multi-Threading Consideration for Controllable Process Layer

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <process/redact_config.h>
using namespace isc::data;
using namespace std;
namespace isc {
namespace process {
ConstElementPtr
redactConfig(ConstElementPtr elem, bool& redacted, const set<string>& follow) {
// From isc::data::copy.
if (!elem) {
isc_throw(BadValue, "redactConfig got a null pointer");
}
// Redact lists.
if (elem->getType() == Element::list) {
ElementPtr result = ElementPtr(new ListElement());
for (auto item : elem->listValue()) {
// add wants a ElementPtr so use a shallow copy.
ElementPtr copy =
data::copy(redactConfig(item, redacted, follow), 0);
result->add(copy);
}
if (redacted) {
return (result);
}
return (elem);
}
// Redact maps.
if (elem->getType() == Element::map) {
ElementPtr result = ElementPtr(new MapElement());
for (auto kv : elem->mapValue()) {
auto key = kv.first;
auto value = kv.second;
if ((key == "password") || (key == "secret")) {
// Handle passwords.
redacted = true;
result->set(key, Element::create(std::string("*****")));
} else if (key == "user-context") {
// Skip user contexts.
result->set(key, value);
} else if (follow.empty() || follow.count(key)) {
// Handle this subtree where are passwords or secrets.
result->set(key, redactConfig(value, redacted, follow));
} else {
// Not follow: no passwords and secrets in this subtree.
result->set(key, value);
}
}
if (redacted) {
return (result);
}
return (elem);
}
// Handle other element types.
return (elem);
}
} // namespace process
} // namespace isc

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef REDACT_CONFIG_H
#define REDACT_CONFIG_H
#include <cc/data.h>
#include <set>
namespace isc {
namespace process {
/// @brief Redact a configuration.
///
/// This method walks on the configuration tree:
/// - it copies only subtrees where a change was done.
/// - it replaces passwords and secrets by asterisks.
/// - it skips user context.
/// - if a not empty list of keywords is given it follows only them.
///
/// @param config the Element tree structure that describes the configuration.
/// @param redacted The reference to redacted flag: true means the result
/// was redacted so cannot be shared.
/// @param follow The set of keywords of subtrees where a password or a
/// secret can be found.
/// @return unmodified config or a copy of the config where passwords and
/// secrets were replaced by asterisks so can be safely logged to an
/// unprivileged place.
isc::data::ConstElementPtr redactConfig(isc::data::ConstElementPtr elem,
bool& redacted,
const std::set<std::string>& follow);
} // namespace process
} // namespace isc
#endif // REDACT_CONFIG_H

View File

@@ -10,6 +10,7 @@
#include <exceptions/exceptions.h>
#include <process/testutils/d_test_stubs.h>
#include <process/d_cfg_mgr.h>
#include <process/redact_config.h>
#include <boost/foreach.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -281,4 +282,47 @@ TEST_F(DStubCfgMgrTest, simpleParseConfigWithCallback) {
EXPECT_TRUE(checkAnswer(1));
}
// This test checks that redactConfig works as expected.
TEST_F(DStubCfgMgrTest, redactConfig) {
// Basic case.
bool redacted = false;
set<string> empty = { };
string config = "{ \"foo\": 1 }";
ConstElementPtr elem;
ASSERT_NO_THROW(elem = Element::fromJSON(config));
ConstElementPtr ret;
ASSERT_NO_THROW(ret = redactConfig(elem, redacted, empty));
EXPECT_FALSE(redacted);
EXPECT_EQ(ret->str(), elem->str());
// Verify redaction.
redacted = false;
config = "{ \"password\": \"foo\", \"secret\": \"bar\" }";
ASSERT_NO_THROW(elem = Element::fromJSON(config));
ASSERT_NO_THROW(ret = redactConfig(elem, redacted, empty));
EXPECT_TRUE(redacted);
string expected = "{ \"password\": \"*****\", \"secret\": \"*****\" }";
EXPECT_EQ(expected, ret->str());
// Verify that user context are skipped.
redacted = false;
config = "{ \"user-context\": { \"password\": \"foo\" } }";
ASSERT_NO_THROW(elem = Element::fromJSON(config));
ASSERT_NO_THROW(ret = redactConfig(elem, redacted, empty));
EXPECT_FALSE(redacted);
EXPECT_EQ(ret->str(), elem->str());
// Verify that only given subtrees are handled.
redacted = false;
set<string> keys = { "foo" };
config = "{ \"foo\": { \"password\": \"foo\" }, ";
config += "\"next\": { \"secret\": \"bar\" } }";
ASSERT_NO_THROW(elem = Element::fromJSON(config));
ASSERT_NO_THROW(ret = redactConfig(elem, redacted, keys));
EXPECT_TRUE(redacted);
expected = "{ \"foo\": { \"password\": \"*****\" }, ";
expected += "\"next\": { \"secret\": \"bar\" } }";
EXPECT_EQ(expected, ret->str());
}
} // end of anonymous namespace