diff --git a/userspace/libsinsp/container.cpp b/userspace/libsinsp/container.cpp index 325bf6c92a..f6f74a3c09 100644 --- a/userspace/libsinsp/container.cpp +++ b/userspace/libsinsp/container.cpp @@ -166,6 +166,9 @@ std::string sinsp_container_manager::container_to_json(const sinsp_container_inf container["imagetag"] = container_info.m_imagetag; container["imagedigest"] = container_info.m_imagedigest; container["privileged"] = container_info.m_privileged; + container["host_pid"] = container_info.m_host_pid; + container["host_network"] = container_info.m_host_network; + container["host_ipc"] = container_info.m_host_ipc; container["is_pod_sandbox"] = container_info.m_is_pod_sandbox; container["lookup_state"] = static_cast(container_info.get_lookup_status()); container["created_time"] = static_cast(container_info.m_created_time); diff --git a/userspace/libsinsp/container_engine/docker/async_source.cpp b/userspace/libsinsp/container_engine/docker/async_source.cpp index 7941f8a918..2d8d33ef81 100644 --- a/userspace/libsinsp/container_engine/docker/async_source.cpp +++ b/userspace/libsinsp/container_engine/docker/async_source.cpp @@ -823,6 +823,18 @@ bool docker_async_source::parse(const docker_lookup_request& request, if(!privileged.isNull() && privileged.isBool()) { container.m_privileged = privileged.asBool(); } + const Json::Value& host_pid = host_config_obj["PidMode"]; + if(!host_pid.isNull() && host_pid.isString() && host_pid.asString() == "host") { + container.m_host_pid = true; + } + const Json::Value& host_net = host_config_obj["NetworkMode"]; + if(!host_net.isNull() && host_net.isString() && host_net.asString() == "host") { + container.m_host_network = true; + } + const Json::Value& ipc_mode = host_config_obj["IpcMode"]; + if(!ipc_mode.isNull() && ipc_mode.isString() && ipc_mode.asString() == "host") { + container.m_host_ipc = true; + } parse_json_mounts(root["Mounts"], container.m_mounts); diff --git a/userspace/libsinsp/container_info.h b/userspace/libsinsp/container_info.h index 90a9d14d5c..163a3ebab8 100644 --- a/userspace/libsinsp/container_info.h +++ b/userspace/libsinsp/container_info.h @@ -219,6 +219,9 @@ class sinsp_container_info { m_type(CT_UNKNOWN), m_container_ip(0), m_privileged(false), + m_host_pid(false), + m_host_network(false), + m_host_ipc(false), m_memory_limit(0), m_swap_limit(0), m_cpu_shares(1024), @@ -265,6 +268,9 @@ class sinsp_container_info { std::string m_imagedigest; uint32_t m_container_ip; bool m_privileged; + bool m_host_pid; + bool m_host_network; + bool m_host_ipc; std::vector m_mounts; std::vector m_port_mappings; std::map m_labels; diff --git a/userspace/libsinsp/cri.h b/userspace/libsinsp/cri.h index d9ab8a4f14..6ee41949f6 100644 --- a/userspace/libsinsp/cri.h +++ b/userspace/libsinsp/cri.h @@ -366,6 +366,22 @@ class cri_interface { const Json::Value &root, sinsp_container_info &container); + /** + * @brief fill out pod sandbox network namespace mode info + * @param status `status` field of the PodSandboxStatusResponse + * @param container the container info to fill out + */ + void parse_cri_pod_sandbox_pid(const typename api::PodSandboxStatus &status, + sinsp_container_info &container); + + /** + * @brief fill out pod sandbox ipc namespace mode info + * @param status `status` field of the PodSandboxStatusResponse + * @param container the container info to fill out + */ + void parse_cri_pod_sandbox_ipc(const typename api::PodSandboxStatus &status, + sinsp_container_info &container); + ///////////////////////////// // Generic parsers helpers ///////////////////////////// diff --git a/userspace/libsinsp/cri.hpp b/userspace/libsinsp/cri.hpp index c591ad997c..954aac5605 100644 --- a/userspace/libsinsp/cri.hpp +++ b/userspace/libsinsp/cri.hpp @@ -529,6 +529,11 @@ inline bool cri_interface::parse_cri_ext_container_info(const Json::Value & bool priv_found = false; const Json::Value *privileged = nullptr; + + // + // Privileged flag + // + // old containerd? if(walk_down_json(*linux, &privileged, "security_context", "privileged") && privileged->isBool()) { @@ -628,6 +633,13 @@ inline bool cri_interface::parse_cri_pod_sandbox_network( const typename api::PodSandboxStatus &status, const Json::Value &root, sinsp_container_info &container) { + // + // Pod network namespace mode + // + if(status.linux().namespaces().options().network() == api::NamespaceMode::NODE) { + container.m_host_network = true; + } + // // Pod IP // @@ -699,6 +711,30 @@ inline bool cri_interface::parse_cri_pod_sandbox_network( return true; } +template +inline void cri_interface::parse_cri_pod_sandbox_pid( + const typename api::PodSandboxStatus &status, + sinsp_container_info &container) { + // + // Pod pid namespace mode + // + if(status.linux().namespaces().options().pid() == api::NamespaceMode::NODE) { + container.m_host_pid = true; + } +} + +template +inline void cri_interface::parse_cri_pod_sandbox_ipc( + const typename api::PodSandboxStatus &status, + sinsp_container_info &container) { + // + // Pod ipc namespace mode + // + if(status.linux().namespaces().options().ipc() == api::NamespaceMode::NODE) { + container.m_host_ipc = true; + } +} + /////////////////////////////////////////////////////////////////// // Main CRI parse entrypoint (make API calls and parse responses) /////////////////////////////////////////////////////////////////// @@ -739,6 +775,8 @@ inline bool cri_interface::parse(const libsinsp::cgroup_limits::cgroup_limi // elsewhere in the response and add them as labels parse_cri_labels(resp_pod_sandbox_container, container); parse_cri_pod_sandbox_network(resp_pod_sandbox_container, root_pod_sandbox, container); + parse_cri_pod_sandbox_pid(resp_pod_sandbox_container, container); + parse_cri_pod_sandbox_ipc(resp_pod_sandbox_container, container); parse_cri_pod_sandbox_labels(resp_pod_sandbox_container, container); return true; } else { @@ -829,6 +867,8 @@ inline bool cri_interface::parse(const libsinsp::cgroup_limits::cgroup_limi const auto root_pod_sandbox = get_info_jvalue(resp_pod_sandbox_container_info); // Add pod response network and labels to original container parse_cri_pod_sandbox_network(resp_pod_sandbox_container, root_pod_sandbox, container); + parse_cri_pod_sandbox_pid(resp_pod_sandbox_container, container); + parse_cri_pod_sandbox_ipc(resp_pod_sandbox_container, container); parse_cri_pod_sandbox_labels(resp_pod_sandbox_container, container); } diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index b7af5bab59..4c5e825e15 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -4736,6 +4736,18 @@ void sinsp_parser::parse_container_json_evt(sinsp_evt *evt) { if(check_json_val_is_convertible(privileged, Json::booleanValue, "privileged")) { container_info->m_privileged = privileged.asBool(); } + const Json::Value &host_pid = container["host_pid"]; + if(check_json_val_is_convertible(host_pid, Json::booleanValue, "host_pid")) { + container_info->m_host_pid = host_pid.asBool(); + } + const Json::Value &host_network = container["host_network"]; + if(check_json_val_is_convertible(host_network, Json::booleanValue, "host_network")) { + container_info->m_host_network = host_network.asBool(); + } + const Json::Value &host_ipc = container["host_ipc"]; + if(check_json_val_is_convertible(host_ipc, Json::booleanValue, "host_ipc")) { + container_info->m_host_ipc = host_ipc.asBool(); + } const Json::Value &lookup_state = container["lookup_state"]; if(check_json_val_is_convertible(lookup_state, Json::uintValue, "lookup_state")) { container_info->set_lookup_status( diff --git a/userspace/libsinsp/sinsp_filtercheck_container.cpp b/userspace/libsinsp/sinsp_filtercheck_container.cpp index 3ea445c7a0..e0e0b177c3 100644 --- a/userspace/libsinsp/sinsp_filtercheck_container.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_container.cpp @@ -237,6 +237,24 @@ static const filtercheck_field_info sinsp_filter_check_container_fields[] = { "ipv6, dual-stack support) for each network interface (multi-interface support). In " "instances of userspace container engine lookup delays, this field may not be available " "yet."}, + {PT_BOOL, + EPF_NONE, + PF_NA, + "container.host_pid", + "Host PID Namespace", + "'true' if the container is running in the host PID namespace, 'false' otherwise."}, + {PT_BOOL, + EPF_NONE, + PF_NA, + "container.host_network", + "Host Network Namespace", + "'true' if the container is running in the host network namespace, 'false' otherwise."}, + {PT_BOOL, + EPF_NONE, + PF_NA, + "container.host_ipc", + "Host IPC Namespace", + "'true' if the container is running in the host IPC namespace, 'false' otherwise."}, }; sinsp_filter_check_container::sinsp_filter_check_container() { @@ -499,6 +517,34 @@ uint8_t *sinsp_filter_check_container::extract_single(sinsp_evt *evt, m_val.u32 = (container_info->m_privileged ? 1 : 0); } + RETURN_EXTRACT_VAR(m_val.u32); + break; + case TYPE_CONTAINER_HOST_PID: + case TYPE_CONTAINER_HOST_NETWORK: + case TYPE_CONTAINER_HOST_IPC: + if(is_host) { + return NULL; + } else { + if(!container_info) { + return NULL; + } + + // Only return a true/false value for + // container types where we really know the + // host_pid, host_network, host_ipc status. + if(!is_docker_compatible(container_info->m_type)) { + return NULL; + } + + if(m_field_id == TYPE_CONTAINER_HOST_NETWORK) { + m_val.u32 = (container_info->m_host_network ? 1 : 0); + } else if(m_field_id == TYPE_CONTAINER_HOST_IPC) { + m_val.u32 = (container_info->m_host_ipc ? 1 : 0); + } else if(m_field_id == TYPE_CONTAINER_HOST_PID) { + m_val.u32 = (container_info->m_host_pid ? 1 : 0); + } + } + RETURN_EXTRACT_VAR(m_val.u32); break; case TYPE_CONTAINER_MOUNTS: diff --git a/userspace/libsinsp/sinsp_filtercheck_container.h b/userspace/libsinsp/sinsp_filtercheck_container.h index ee62a95b4c..cd4260551d 100644 --- a/userspace/libsinsp/sinsp_filtercheck_container.h +++ b/userspace/libsinsp/sinsp_filtercheck_container.h @@ -48,6 +48,9 @@ class sinsp_filter_check_container : public sinsp_filter_check { TYPE_CONTAINER_DURATION, TYPE_CONTAINER_IP_ADDR, TYPE_CONTAINER_CNIRESULT, + TYPE_CONTAINER_HOST_PID, + TYPE_CONTAINER_HOST_NETWORK, + TYPE_CONTAINER_HOST_IPC, }; sinsp_filter_check_container(); diff --git a/userspace/libsinsp/test/container_engine/container_parser_cri_crio.ut.cpp b/userspace/libsinsp/test/container_engine/container_parser_cri_crio.ut.cpp index ec3d62ec7e..8b711fd741 100644 --- a/userspace/libsinsp/test/container_engine/container_parser_cri_crio.ut.cpp +++ b/userspace/libsinsp/test/container_engine/container_parser_cri_crio.ut.cpp @@ -507,9 +507,9 @@ runtime::v1alpha2::PodSandboxStatusResponse get_default_cri_crio_pod_status_resp // "linux": { // "namespaces": { // "options": { - // "ipc": "POD", + // "ipc": "NODE", // "network": "POD", - // "pid": "CONTAINER", + // "pid": "NODE", // "targetId": "" // } // } @@ -537,6 +537,12 @@ runtime::v1alpha2::PodSandboxStatusResponse get_default_cri_crio_pod_status_resp status->set_created_at((uint64_t)1676262698000004577); // dummy status->mutable_metadata()->set_name("podsandbox1"); status->mutable_network()->set_ip("10.244.0.3"); + status->mutable_linux()->mutable_namespaces()->mutable_options()->set_ipc( + runtime::v1alpha2::NamespaceMode::NODE); + status->mutable_linux()->mutable_namespaces()->mutable_options()->set_network( + runtime::v1alpha2::NamespaceMode::POD); + status->mutable_linux()->mutable_namespaces()->mutable_options()->set_pid( + runtime::v1alpha2::NamespaceMode::NODE); auto labels = status->mutable_labels(); (*labels)["app"] = "myapp"; (*labels)["example-label/custom_one"] = "mylabel"; @@ -632,6 +638,11 @@ TEST_F(sinsp_with_test_input, container_parser_cri_crio) { root_pod_sandbox, container); ASSERT_TRUE(res); + ASSERT_FALSE(container.m_host_network); + cri_api_v1alpha2->parse_cri_pod_sandbox_pid(resp_pod_sandbox_container, container); + ASSERT_TRUE(container.m_host_pid); + cri_api_v1alpha2->parse_cri_pod_sandbox_ipc(resp_pod_sandbox_container, container); + ASSERT_TRUE(container.m_host_ipc); res = cri_api_v1alpha2->parse_cri_pod_sandbox_labels(resp_pod_sandbox_container, container); ASSERT_TRUE(res); @@ -775,6 +786,7 @@ TEST_F(sinsp_with_test_input, container_parser_cri_crio) { ASSERT_EQ(get_field_as_string(evt, "container.image.digest"), "sha256:49ecc282021562c567a8159ef424a06cdd8637efdca5953de9794eafe29adcad"); ASSERT_EQ(get_field_as_string(evt, "container.ip"), "10.244.0.3"); + ASSERT_EQ(get_field_as_string(evt, "container.cni.json"), "{\"cniVersion\":\"1.0.0\",\"interfaces\":[{\"name\":\"bridge\",\"mac\":\"ce:64:08:" "76:88:6a\"},{\"name\":\"veth71b0e931\",\"mac\":\"72:b7:4f:bc:e4:a4\"},{\"name\":" @@ -782,6 +794,9 @@ TEST_F(sinsp_with_test_input, container_parser_cri_crio) { "dec735d1-0e86-44c1-94e0-a102173334a4\"}],\"ips\":[{\"interface\":2,\"address\":\"10." "244.0.3/16\",\"gateway\":\"10.244.0.1\"}],\"routes\":[{\"dst\":\"0.0.0.0/" "0\",\"gw\":\"10.244.0.1\"}],\"dns\":{}}"); + ASSERT_EQ(get_field_as_string(evt, "container.host_pid"), "true"); + ASSERT_EQ(get_field_as_string(evt, "container.host_network"), "false"); + ASSERT_EQ(get_field_as_string(evt, "container.host_ipc"), "true"); ASSERT_EQ(get_field_as_string(evt, "k8s.ns.name"), "redhat.test.crio"); ASSERT_EQ(get_field_as_string(evt, "k8s.pod.name"), "podsandbox1");