Skip to content

Commit

Permalink
Event scope escape (draios#733)
Browse files Browse the repository at this point in the history
* add scope utils and k8s/docker events scope checks

* factor out scope check and assembly

* event scope class

* restrict key with regex, allow anything in value (e340987)

* replace c++11 regex with posix

* simplify regex check

* fixes from @bertocci review
  • Loading branch information
aleks-f authored and Damian Myerscough committed Mar 3, 2017
1 parent 0551f54 commit ae8ffce
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 36 deletions.
17 changes: 5 additions & 12 deletions userspace/libsinsp/docker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,10 @@ void docker::handle_event(Json::Value&& root)
{
image = img.asString();
}
std::string scope("host.mac=");
event_scope scope;
if(m_machine_id.length())
{
scope.append(m_machine_id);
}
else
{
scope.clear();
scope.add("host.mac", m_machine_id);
}
if(is_image_event(event_name))
{
Expand All @@ -359,13 +355,11 @@ void docker::handle_event(Json::Value&& root)
}
if(!id.empty())
{
if(scope.length()) { scope.append(" and "); }
scope.append("container.image=").append(id);
scope.add("container.image", id);
}
else if(!image.empty())
{
if(scope.length()) { scope.append(" and "); }
scope.append("container.image=").append(image);
scope.add("container.image", image);
}
else
{
Expand All @@ -377,8 +371,7 @@ void docker::handle_event(Json::Value&& root)
{
if(id.length() >= 12)
{
if(scope.length()) { scope.append(" and "); }
scope.append("container.id=").append(id.substr(0, 12));
scope.add("container.id", id.substr(0, 12));
}
}
if(status.length())
Expand Down
44 changes: 25 additions & 19 deletions userspace/libsinsp/k8s_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ bool k8s_event_t::update(const Json::Value& item, k8s_state_t& state)
std::string event_name;
std::string description;
severity_t severity = sinsp_logger::SEV_EVT_INFORMATION;
std::string scope;
event_scope scope;
tag_map_t tags;

const Json::Value& obj = item["involvedObject"];
Expand Down Expand Up @@ -875,26 +875,34 @@ bool k8s_event_t::update(const Json::Value& item, k8s_state_t& state)
const k8s_component* comp = state.get_component(component_uid, &t);
if(comp && !t.empty())
{
std::string node_name = comp->get_node_name();
const std::string& node_name = comp->get_node_name();
if(!node_name.empty())
{
if(scope.length()) { scope.append(" and "); }
scope.append("kubernetes.node.name=").append(node_name);
scope.add("kubernetes.node.name", node_name);
}
const std::string& ns = comp->get_namespace();
if(!ns.empty())
{
if(scope.length()) { scope.append(" and "); }
scope.append("kubernetes.namespace.name=").append(ns);
scope.add("kubernetes.namespace.name", ns);
}
const std::string& comp_name = comp->get_name();
if(comp_name.empty())
{
scope.add(std::string("kubernetes.").append(t).append(".name"), comp_name);
}
if(scope.length()) { scope.append(" and "); }
scope.append("kubernetes.").append(t).append(".name=").append(comp->get_name());
/* no labels for now
for(const auto& label : comp->get_labels())
{
tags[label.first] = label.second;
g_logger.log("EVENT label: [" + label.first + ':' + label.second + ']', sinsp_logger::SEV_DEBUG);
scope.append(" and kubernetes.").append(t).append(".label.").append(label.first).append(1, '=').append(label.second);
//g_logger.log("EVENT label: [" + label.first + ':' + label.second + ']', sinsp_logger::SEV_DEBUG);
if(event_scope::check(label.second))
{
scope.append(" and kubernetes.").append(t).append(".label.").append(label.first).append(1, '=').append(label.second);
}
else
{
g_logger.log("K8s invalid scope entry: [" + label.second + ']', sinsp_logger::SEV_WARNING);
}
}*/
}
else if(epoch_time_now_s < (epoch_time_evt_s + 120))
Expand Down Expand Up @@ -922,33 +930,31 @@ bool k8s_event_t::update(const Json::Value& item, k8s_state_t& state)

tags["source"] = "kubernetes";
g_logger.log(sinsp_user_event::to_string(epoch_time_evt_s, std::move(event_name), std::move(description),
std::move(scope), std::move(tags)), severity);
std::move(scope), std::move(tags)), severity);

// TODO: sysdig capture?
#endif // _WIN32

return true;
}

void k8s_event_t::make_scope_impl(const Json::Value& obj, std::string comp, std::string& scope, bool ns)
void k8s_event_t::make_scope_impl(const Json::Value& obj, std::string comp, event_scope& scope, bool ns)
{
if(ns)
{
std::string ns_name = get_json_string(obj, "namespace");
const std::string& ns_name = get_json_string(obj, "namespace");
if(!ns_name.empty())
{
if(scope.length()) { scope.append(" and "); }
scope.append("kubernetes.namespace.name=").append(ns_name);
scope.add("kubernetes.namespace.name", ns_name);
}
}
if(comp.length() && ci_compare::is_equal(get_json_string(obj, "kind"), comp))
{
std::string comp_name = get_json_string(obj, "name");
const std::string& comp_name = get_json_string(obj, "name");
if(!comp_name.empty())
{
if(scope.length()) { scope.append(" and "); }
comp[0] = tolower(comp[0]);
scope.append("kubernetes.").append(comp).append(".name=").append(comp_name);
scope.add(std::string("kubernetes.").append(comp).append(".name"), comp_name);
}
if(comp_name.empty())
{
Expand All @@ -961,7 +967,7 @@ void k8s_event_t::make_scope_impl(const Json::Value& obj, std::string comp, std:
}
}

void k8s_event_t::make_scope(const Json::Value& obj, std::string& scope)
void k8s_event_t::make_scope(const Json::Value& obj, event_scope& scope)
{
if(ci_compare::is_equal(get_json_string(obj, "kind"), "Pod"))
{
Expand Down
5 changes: 3 additions & 2 deletions userspace/libsinsp/k8s_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ class k8s_deployment_t : public k8s_component
//

class k8s_state_t;
class event_scope;

class k8s_event_t : public k8s_component
{
Expand All @@ -522,8 +523,8 @@ class k8s_event_t : public k8s_component
typedef sinsp_logger::event_severity severity_t;
typedef std::unordered_map<std::string, std::string> name_translation_map_t;

void make_scope(const Json::Value& obj, std::string& scope);
void make_scope_impl(const Json::Value& obj, std::string comp, std::string& scope, bool ns = true);
void make_scope(const Json::Value& obj, event_scope& scope);
void make_scope_impl(const Json::Value& obj, std::string comp, event_scope& scope, bool ns = true);

name_translation_map_t m_name_translation;
std::map<std::string, Json::Value> m_postponed_events;
Expand Down
112 changes: 110 additions & 2 deletions userspace/libsinsp/user_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,114 @@ along with sysdig. If not, see <http://www.gnu.org/licenses/>.
#include "sinsp_int.h"
#include "user_event.h"

//
// event_scope
//

const std::string event_scope::SCOPE_OP_AND = "and";

// these string lists contain reserved strings; some of the reserved
// strings are escaped and mandatory to be first in RESERVED_STRINGS
// and have their escaped counterparts in the REPLACEMENT_STRINGS,
// in the same order as they appear in RESERVED_STRINGS
const event_scope::string_list_t event_scope::RESERVED_STRINGS =
{"'"};
const event_scope::string_list_t event_scope::REPLACEMENT_STRINGS =
{"\\'"};

// scope key name format regex
const std::string event_scope::KEY_FORMAT = "[a-zA-Z0-9_/\\.-]*";

event_scope::event_scope(const std::string& key, const std::string& value)
{
add(key, value, "");
}

bool event_scope::add(const std::string& key, const std::string& value, const std::string& op)
{
if(check_key_format(key))
{
std::string k(key);
std::string o(!m_scope.empty() ? op : "");
std::string v(value);
replace(v);
if(!v.empty())
{
if(!o.empty())
{
m_scope.append(1, ' ').append(trim(o)).append(1, ' ');
}
m_scope.append(trim(k)).append("='").append(trim(v)).append(1, 0x27);
return true;
}
}
else
{
g_logger.log("Scope key is invalid: [" + key + "], entry will not be added to scope.",
sinsp_logger::SEV_WARNING);
}
return false;
}

string& event_scope::replace(std::string& value)
{
ASSERT(RESERVED_STRINGS.size() == REPLACEMENT_STRINGS.size());

string_list_t::const_iterator res_it = RESERVED_STRINGS.cbegin();
string_list_t::const_iterator res_end = RESERVED_STRINGS.cend();
string_list_t::const_iterator rep_it = REPLACEMENT_STRINGS.cbegin();
string_list_t::const_iterator rep_end = REPLACEMENT_STRINGS.cend();
for(; res_it != res_end && rep_it != rep_end; ++res_it, ++rep_it)
{
replace_in_place(value, *res_it, *rep_it);
}

return value;
}

void event_scope::regex_error(const std::string& call, size_t ret, regex_t* preg, const std::string& str)
{
if(!preg) { return; }
char errbuf[256] = {0};
if(regerror(ret, preg, errbuf, 256))
{
g_logger.log(call + "() error: " + errbuf, sinsp_logger::SEV_WARNING);
}
else
{
g_logger.log("Can't obtain " + call + "() [" + str + "] error.", sinsp_logger::SEV_WARNING);
}
}

bool event_scope::check_key_format(const std::string& key)
{
if(key.empty()) { return false; }
bool result = false;
std::string exp(KEY_FORMAT);
regex_t reg = {0};
size_t ret = regcomp(&reg, exp.c_str(), REG_EXTENDED);
if(0 == ret)
{
regmatch_t rm = {0};
ret = regexec(&reg, key.c_str(), 1, &rm, 0);
if(0 == ret)
{
if((rm.rm_eo - rm.rm_so) == static_cast<regoff_t>(key.length()))
{
result = true;
}
}
else { regex_error("regexec", ret, &reg, key); }
}
else { regex_error("regcomp", ret, &reg, exp); }
regfree(&reg);
return result;
}


//
// user_event_meta_t
//
const std::string user_event_meta_t::PERMIT_ALL = "all";

user_event_meta_t::user_event_meta_t(const std::string& kind, const type_list_t& types):
Expand Down Expand Up @@ -266,7 +374,7 @@ sinsp_user_event& sinsp_user_event::operator=(sinsp_user_event&& other)
std::string sinsp_user_event::to_string(uint64_t timestamp,
std::string&& name,
std::string&& description,
std::string&& scope,
event_scope&& scope,
tag_map_t&& tags,
uint32_t sev)
{
Expand All @@ -277,7 +385,7 @@ std::string sinsp_user_event::to_string(uint64_t timestamp,
ostr << "timestamp: " << timestamp << '\n' <<
"name: \"" << replace_in_place(name, from, to) << "\"\n"
"description: \"" << replace_in_place(description, from, to) << "\"\n"
"scope: \"" << replace_in_place(scope, from, to) << "\"\n";
"scope: \"" << replace_in_place(scope.get_ref(), from, to) << "\"\n";

if(sev != UNKNOWN_SEVERITY)
{
Expand Down
56 changes: 55 additions & 1 deletion userspace/libsinsp/user_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,60 @@ along with sysdig. If not, see <http://www.gnu.org/licenses/>.
#include <string>
#include <set>
#include <unordered_map>
#include <regex.h>

//
// scope utilities
//
class event_scope
{
public:
typedef std::vector<std::string> string_list_t;

static const std::string SCOPE_OP_AND;
static const string_list_t RESERVED_STRINGS;
static const string_list_t REPLACEMENT_STRINGS;
static const std::string KEY_FORMAT;

event_scope(const std::string& key = "", const std::string& value = "");

bool add(const std::string& key, const std::string& value, const std::string& op = SCOPE_OP_AND);

const std::string& get() const;
std::string& get_ref();

void clear();

// utility function to check that a scope entry key is valid;
// valid entries match KEY_FORMAT regular expression
static bool check_key_format(const std::string& key);

private:

// utility function to replace RESERVED_STRINGS with their
// counterparts in REPLACEMENT_STRINGS
static string& replace(std::string& scope);

static void regex_error(const std::string& call, size_t ret, regex_t* preg, const std::string& str);

std::string m_scope;
};

inline const std::string& event_scope::get() const
{
return m_scope;
}

inline std::string& event_scope::get_ref()
{
return m_scope;
}

inline void event_scope::clear()
{
m_scope.clear();
}


//
// user-configured event meta
Expand Down Expand Up @@ -207,7 +261,7 @@ class sinsp_user_event
static std::string to_string(uint64_t timestamp,
std::string&& name,
std::string&& description,
std::string&& scope,
event_scope&& scope,
tag_map_t&& tags,
uint32_t sev = UNKNOWN_SEVERITY);

Expand Down

0 comments on commit ae8ffce

Please sign in to comment.