From dcecf6c2986e35c1cdb195532641f9d090438e46 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Fri, 24 Jun 2022 12:36:25 +0000 Subject: [PATCH 1/4] fix(gvisor): use args/env size when parsing procfs Signed-off-by: Luca Guerra --- userspace/libscap/engine/gvisor/parsers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/userspace/libscap/engine/gvisor/parsers.cpp b/userspace/libscap/engine/gvisor/parsers.cpp index 2dbce13301..706880a70e 100644 --- a/userspace/libscap/engine/gvisor/parsers.cpp +++ b/userspace/libscap/engine/gvisor/parsers.cpp @@ -808,7 +808,7 @@ procfs_result parse_procfs_json(const std::string &input, const std::string &san return res; } std::string args; - for(Json::Value::ArrayIndex i = 0; i != root.size(); i++) + for(Json::Value::ArrayIndex i = 0; i < root["args"].size(); i++) { args += root["args"][i].asString(); args.push_back('\0'); @@ -824,7 +824,7 @@ procfs_result parse_procfs_json(const std::string &input, const std::string &san return res; } std::string env; - for(Json::Value::ArrayIndex i = 0; i != root.size(); i++) + for(Json::Value::ArrayIndex i = 0; i < root["env"].size(); i++) { env += root["env"][i].asString(); env.push_back('\0'); From c1c0918cc3dd7716f8cebc8bfdf235e268c129d5 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Fri, 24 Jun 2022 12:37:44 +0000 Subject: [PATCH 2/4] fix(gvisor): fix uid/gid unit test with new format Signed-off-by: Luca Guerra --- .../libscap/test/scap_gvisor_parsers.ut.cpp | 100 +++++++++--------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/userspace/libscap/test/scap_gvisor_parsers.ut.cpp b/userspace/libscap/test/scap_gvisor_parsers.ut.cpp index 70c9b103c3..d999c2ea92 100644 --- a/userspace/libscap/test/scap_gvisor_parsers.ut.cpp +++ b/userspace/libscap/test/scap_gvisor_parsers.ut.cpp @@ -192,55 +192,57 @@ TEST(gvisor_parsers, procfs_entry) EXPECT_EQ(res.status, SCAP_FAILURE); EXPECT_STREQ(res.error.c_str(), "Malformed json string: cannot parse procfs entry"); - std::string json = - "{" - " \"args\" : [ \"bash\" ]," - " \"clone_ts\" : 1655473752715788585," - " \"cwd\" : \"/\"," - " \"env\" : [" - " \"HOSTNAME=91e91fdd849d\"," - " \"TERM=xterm\"," - " \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"," - " \"HOME=/root\"" - " ]," - " \"exe\" : \"/usr/bin/bash\"," - " \"fdlist\" : [" - " {" - " \"path\" : \"host:[1]\"" - " }," - " {" - " \"number\" : 1," - " \"path\" : \"host:[1]\"" - " }," - " {" - " \"number\" : 2," - " \"path\" : \"host:[1]\"" - " }," - " {" - " \"number\" : 255," - " \"path\" : \"host:[1]\"" - " }" - " ]," - " \"limits\" : {" - " \"RLIMIT_NOFILE\" : {" - " \"cur\" : 1048576," - " \"max\" : 1048576" - " }" - " }," - " \"root\" : \"/\"," - " \"stat\" : {" - " \"pgid\" : 1," - " \"sid\" : 1" - " }," - " \"status\" : {" - " \"comm\" : \"bash\"," - " \"gid\" : {}," - " \"pid\" : 1," - " \"uid\" : {}," - " \"vm_rss\" : 4664," - " \"vm_size\" : 12164" - " }" - "}\n"; + std::string json = R"( +{ + "args": [ "bash" ], + "clone_ts": 1655473752715788585, + "cwd": "/", + "env": [ + "HOSTNAME=91e91fdd849d", + "TERM=xterm", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME=/root" + ], + "exe": "/usr/bin/bash", + "fdlist": [ + { + "path": "host:[1]" + }, + { + "number": 1, + "path": "host:[1]" + }, + { + "number": 2, + "path": "host:[1]" + }, + { + "number": 255, + "path": "host:[1]" + } + ], + "limits": { + "RLIMIT_NOFILE": { + "cur": 1048576, + "max": 1048576 + } + }, + "root": "/", + "stat": { + "pgid": 1, + "sid": 1 + }, + "status": { + "comm": "bash", + "gid": {"effective": 0, "real": 0, "saved": 0}, + "pid": 1, + "uid": {"effective": 0, "real": 0, "saved": 0}, + "vm_rss": 4664, + "vm_size": 12164 + } +} + )"; + res = scap_gvisor::parsers::parse_procfs_json(json, sandbox_id); EXPECT_EQ(res.status, SCAP_SUCCESS); EXPECT_EQ(res.tinfo.vtid, 1); From c6594708c48972ebbbcea81f1ec213136c6be04b Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Fri, 24 Jun 2022 12:38:40 +0000 Subject: [PATCH 3/4] new(gvisor): get socket by parsing supplied configuration file Signed-off-by: Luca Guerra --- userspace/libscap/engine/gvisor/gvisor.cpp | 4 +- userspace/libscap/engine/gvisor/gvisor.h | 13 +++- userspace/libscap/engine/gvisor/parsers.cpp | 60 +++++++++++++++++++ .../libscap/engine/gvisor/scap_gvisor.cpp | 25 +++++++- userspace/libscap/scap.c | 2 +- userspace/libscap/scap_open.h | 3 +- userspace/libsinsp/sinsp.cpp | 13 ++-- userspace/libsinsp/sinsp.h | 5 +- 8 files changed, 105 insertions(+), 20 deletions(-) diff --git a/userspace/libscap/engine/gvisor/gvisor.cpp b/userspace/libscap/engine/gvisor/gvisor.cpp index 9891e5c0b7..d6f58971fd 100644 --- a/userspace/libscap/engine/gvisor/gvisor.cpp +++ b/userspace/libscap/engine/gvisor/gvisor.cpp @@ -48,7 +48,7 @@ static SCAP_HANDLE_T *gvisor_alloc_handle(scap_t* main_handle, char *lasterr_ptr static int32_t gvisor_init(scap_t* main_handle, scap_open_args* open_args) { scap_gvisor::engine *gv = main_handle->m_engine.m_handle; - return gv->init(open_args->gvisor_socket, open_args->gvisor_root_path, open_args->gvisor_trace_session_path); + return gv->init(open_args->gvisor_config_path, open_args->gvisor_root_path); } static void gvisor_free_handle(struct scap_engine_handle engine) @@ -78,7 +78,7 @@ static int32_t gvisor_next(struct scap_engine_handle engine, scap_evt **pevent, static bool gvisor_match(scap_open_args* open_args) { - return open_args->gvisor_socket != NULL; + return open_args->gvisor_config_path != NULL; } static int32_t gvisor_configure(struct scap_engine_handle engine, enum scap_setting setting, unsigned long arg1, unsigned long arg2) diff --git a/userspace/libscap/engine/gvisor/gvisor.h b/userspace/libscap/engine/gvisor/gvisor.h index b6d1349d21..afb6e1c12e 100644 --- a/userspace/libscap/engine/gvisor/gvisor.h +++ b/userspace/libscap/engine/gvisor/gvisor.h @@ -60,6 +60,15 @@ struct procfs_result { scap_threadinfo tinfo; }; +struct config_result { + // the scap status of the operation + uint32_t status; + // description of the error in case of failure + std::string error; + // the socket path + std::string socket_path; +}; + /*! \brief Translate a gVisor seccheck protobuf into one, or more, scap events \param gvisor_buf the source buffer that contains the raw event coming from gVisor @@ -82,6 +91,8 @@ procfs_result parse_procfs_json(const std::string &input, const std::string &san uint64_t get_vxid(uint64_t vxid); +config_result parse_config(std::string config); + } // namespace parsers namespace runsc @@ -115,7 +126,7 @@ class engine { public: engine(char *lasterr); ~engine(); - int32_t init(std::string socket_path, std::string root_path, std::string trace_session_path); + int32_t init(std::string config_path, std::string root_path); int32_t close(); int32_t start_capture(); diff --git a/userspace/libscap/engine/gvisor/parsers.cpp b/userspace/libscap/engine/gvisor/parsers.cpp index 706880a70e..32758d968c 100644 --- a/userspace/libscap/engine/gvisor/parsers.cpp +++ b/userspace/libscap/engine/gvisor/parsers.cpp @@ -882,5 +882,65 @@ procfs_result parse_procfs_json(const std::string &input, const std::string &san return res; } +config_result parse_config(std::string config) +{ + config_result res; + res.socket_path = ""; + res.error = ""; + res.status = SCAP_FAILURE; + + std::string err; + Json::Value root; + Json::CharReaderBuilder builder; + const std::unique_ptr reader(builder.newCharReader()); + + bool json_parse = reader->parse(config.c_str(), config.c_str() + config.size() - 1, &root, &err); + if(!json_parse) + { + res.error = "Could not parse configuration file contents."; + return res; + } + + if(!root.isMember("trace_session")) + { + res.error = "Could not find trace_session entry in configuration"; + return res; + } + Json::Value &trace_session = root["trace_session"]; + + if(!trace_session.isMember("sinks") || !trace_session["sinks"].isArray()) + { + res.error = "Could not find trace_session -> sinks array in configuration"; + return res; + } + + if(trace_session["sinks"].size() == 0) + { + res.error = "trace_session -> sinks array is empty"; + return res; + } + + // We don't know how to distinguish between sinks in case there is more than one + // we're taking the first for now but this can be tweaked if necessary. + Json::Value &sink = trace_session["sinks"][0]; + + if(!sink.isMember("config")) + { + res.error = "Could not find config in sink item"; + return res; + } + Json::Value &sink_config = sink["config"]; + + if(!sink_config.isMember("endpoint") || !sink_config["endpoint"].isString()) + { + res.error = "Could not find endpoint in sink configuration"; + return res; + } + + res.socket_path = sink_config["endpoint"].asString(); + res.status = SCAP_SUCCESS; + return res; +} + } // namespace parsers } // namespace scap_gvisor \ No newline at end of file diff --git a/userspace/libscap/engine/gvisor/scap_gvisor.cpp b/userspace/libscap/engine/gvisor/scap_gvisor.cpp index a0eeec78f0..3f48f50b9e 100644 --- a/userspace/libscap/engine/gvisor/scap_gvisor.cpp +++ b/userspace/libscap/engine/gvisor/scap_gvisor.cpp @@ -25,6 +25,8 @@ limitations under the License. #include #include +#include +#include #include "gvisor.h" #include "pkg/sentry/seccheck/points/common.pb.h" @@ -86,13 +88,30 @@ engine::~engine() } -int32_t engine::init(std::string socket_path, std::string root_path, std::string trace_session_path) +int32_t engine::init(std::string config_path, std::string root_path) { m_root_path = root_path; - m_trace_session_path = trace_session_path; + m_trace_session_path = config_path; + std::ifstream config_file(config_path); + if (config_file.fail()) + { + snprintf(m_lasterr, SCAP_LASTERR_SIZE, "Could not open gVisor configuration file %s", config_path.c_str()); + return SCAP_FAILURE; + } + std::stringstream config_buf; + config_buf << config_file.rdbuf(); + + parsers::config_result config_result = parsers::parse_config(config_buf.str()); + if(config_result.status != SCAP_SUCCESS) + { + snprintf(m_lasterr, SCAP_LASTERR_SIZE, "Could not parse gVisor configuration file %s : %s", + config_path.c_str(), config_result.error.c_str()); + return config_result.status; + } + // Initialize the listen fd - m_socket_path = socket_path; + m_socket_path = config_result.socket_path; if (m_socket_path.empty()) { strlcpy(m_lasterr, "Empty gVisor socket path", SCAP_LASTERR_SIZE); diff --git a/userspace/libscap/scap.c b/userspace/libscap/scap.c index 5aa084f860..5ce1e1600a 100644 --- a/userspace/libscap/scap.c +++ b/userspace/libscap/scap.c @@ -901,7 +901,7 @@ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc) args.import_users, args.suppressed_comms); } - else if (args.gvisor_socket != NULL) + else if (args.gvisor) { return scap_open_gvisor_int(error, rc, &args); } diff --git a/userspace/libscap/scap_open.h b/userspace/libscap/scap_open.h index b71452e0c7..8a4c95c419 100644 --- a/userspace/libscap/scap_open.h +++ b/userspace/libscap/scap_open.h @@ -86,9 +86,8 @@ typedef struct scap_open_args // values via scap_suppress_events_comm(). bool udig; ///< If true, UDIG will be used for event capture. bool gvisor; //< If true, gVisor will be used for event capture - const char *gvisor_socket; ///< When using gVisor, this contains the gvisor socket const char *gvisor_root_path; ///< When using gvisor, the root path used by runsc commands - const char *gvisor_trace_session_path; ///< When using gvisor, the trace session config file + const char *gvisor_config_path; ///< When using gvisor, the path to the configuration file interesting_ppm_sc_set ppm_sc_of_interest; diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index baccd1c304..40618c5fd5 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -529,13 +529,11 @@ void sinsp::open_live_common(uint32_t timeout_ms, scap_mode_t mode) oargs.gvisor = m_gvisor; if (m_gvisor) { - oargs.gvisor_socket = m_gvisor_socket.c_str(); oargs.gvisor_root_path = m_gvisor_root_path.c_str(); - oargs.gvisor_trace_session_path = m_gvisor_trace_session_path.c_str(); + oargs.gvisor_config_path = m_gvisor_config_path.c_str(); } else { - oargs.gvisor_socket = NULL; oargs.gvisor_root_path = NULL; - oargs.gvisor_trace_session_path = NULL; + oargs.gvisor_config_path = NULL; } fill_syscalls_of_interest(&oargs); @@ -596,12 +594,11 @@ void sinsp::open_udig(uint32_t timeout_ms) open_live_common(timeout_ms, SCAP_MODE_LIVE); } -void sinsp::open_gvisor(std::string socket_path, std::string root_path, std::string trace_session_path, uint32_t timeout_ms) +void sinsp::open_gvisor(std::string config_path, std::string root_path, uint32_t timeout_ms) { m_gvisor = true; - m_gvisor_socket = socket_path; - m_gvisor_root_path = root_path, - m_gvisor_trace_session_path = trace_session_path; + m_gvisor_root_path = root_path; + m_gvisor_config_path = config_path; open_live_common(timeout_ms, SCAP_MODE_LIVE); set_get_procs_cpu_from_driver(false); } diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 0d6f05d7e9..1063490338 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -250,7 +250,7 @@ class SINSP_PUBLIC sinsp : public capture_stats_source, public wmi_handle_source void fdopen(int fd); void open_udig(uint32_t timeout_ms = SCAP_TIMEOUT_MS); - void open_gvisor(std::string socket_path, std::string root_path, std::string trace_session_path, uint32_t timeout_ms = SCAP_TIMEOUT_MS); + void open_gvisor(std::string config_path, std::string root_path, uint32_t timeout_ms = SCAP_TIMEOUT_MS); std::string generate_gvisor_config(std::string socket_path); @@ -1069,9 +1069,8 @@ VISIBILITY_PRIVATE bool m_bpf; bool m_udig; bool m_gvisor; - std::string m_gvisor_socket = ""; std::string m_gvisor_root_path = ""; - std::string m_gvisor_trace_session_path = ""; + std::string m_gvisor_config_path = ""; bool m_is_windows; std::string m_bpf_probe; bool m_isdebug_enabled; From 5866c813cc30d3962e71d10b66501daa02162e83 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Fri, 24 Jun 2022 12:38:56 +0000 Subject: [PATCH 4/4] new(gvisor): add test for config file parser Signed-off-by: Luca Guerra --- .../libscap/test/scap_gvisor_parsers.ut.cpp | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/userspace/libscap/test/scap_gvisor_parsers.ut.cpp b/userspace/libscap/test/scap_gvisor_parsers.ut.cpp index d999c2ea92..44c318f722 100644 --- a/userspace/libscap/test/scap_gvisor_parsers.ut.cpp +++ b/userspace/libscap/test/scap_gvisor_parsers.ut.cpp @@ -269,4 +269,68 @@ TEST(gvisor_parsers, procfs_entry) EXPECT_EQ(res.status, SCAP_FAILURE); EXPECT_STREQ(res.error.c_str(), "Missing json field or wrong type: cannot parse procfs entry"); -} \ No newline at end of file +} + +TEST(gvisor_parsers, config_socket) +{ + std::string config = R"( +{ + "trace_session": { + "name": "Default", + "points": [ + { + "name": "container/start", + "context_fields": [ + "cwd", + "time" + ] + }, + { + "name": "syscall/openat/enter", + "context_fields": [ + "credentials", + "container_id", + "thread_id", + "task_start_time", + "time" + ] + }, + { + "name": "syscall/openat/exit", + "context_fields": [ + "credentials", + "container_id", + "thread_id", + "task_start_time", + "time" + ] + }, + { + "name": "sentry/task_exit", + "context_fields": [ + "credentials", + "container_id", + "thread_id", + "task_start_time", + "time" + ] + } + ], + "sinks": [ + { + "name": "remote", + "config": { + "endpoint": "/tmp/gvisor.sock" + } + } + ] + } +} + )"; + + scap_gvisor::parsers::config_result res; + + res = scap_gvisor::parsers::parse_config(config); + EXPECT_EQ(res.status, SCAP_SUCCESS); + EXPECT_STREQ(res.socket_path.c_str(), "/tmp/gvisor.sock"); +}