File: container.h

package info (click to toggle)
falcosecurity-libs 0.1.1dev%2Bgit20220316.e5c53d64-5.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,732 kB
  • sloc: cpp: 55,770; ansic: 37,330; makefile: 74; sh: 13
file content (224 lines) | stat: -rw-r--r-- 8,898 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
Copyright (C) 2021 The Falco Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

#pragma once

#include <functional>
#include <memory>
#include <unordered_map>

#include "scap.h"

#include "event.h"
#include "container_info.h"

#if !defined(_WIN32) && !defined(CYGWING_AGENT) && defined(HAS_CAPTURE) && !defined(MINIMAL_BUILD)
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/multi.h>
#endif

#include "container_engine/container_cache_interface.h"
#include "container_engine/container_engine_base.h"
#include "container_engine/sinsp_container_type.h"
#include "mutex.h"

class sinsp_container_manager :
	public libsinsp::container_engine::container_cache_interface
{
public:
	using map_ptr_t = libsinsp::ConstMutexGuard<std::unordered_map<std::string, sinsp_container_info::ptr_t>>;

	/**
	 * Due to how the container manager is architected, it makes it difficult
	 * to dynamically select which engines we want to be valid. As such, we unfortunately
	 * need to indicate at construction time which we want, with the only possible options
	 * right now being "static" or not. I'm sure we will find time in the future to do this
	 * in a more general way. 2020/11/24
	 */
	sinsp_container_manager(sinsp* inspector,
	                        bool static_container = false,
	                        const std::string static_id = "",
	                        const std::string static_name = "",
	                        const std::string static_image = "");

	virtual ~sinsp_container_manager();

	/**
	 * @brief Get the whole container map (read-only)
	 * @return the map of container_id -> shared_ptr<container_info>
	 */
	map_ptr_t get_containers() const;
	bool remove_inactive_containers();

	/**
	 * @brief Add/update a container in the manager map, executing on_new_container callbacks
	 *
	 * @param container_info shared_ptr owning the container_info to add/update
	 * @param thread a thread in the container, only passed to callbacks
	 */
	void add_container(const sinsp_container_info::ptr_t& container_info, sinsp_threadinfo *thread) override;

	/**
	 * @brief Update a container by replacing its entry with a new one
	 *
	 * Does not call on_new_container callbacks
	 *
	 * @param container_info shared_ptr owning the updated container_info
	 */
	void replace_container(const sinsp_container_info::ptr_t& container_info) override;

	/**
	 * @brief Get a container_info by container id
	 * @param id the id of the container to look up
	 * @return a const pointer to the container_info
	 *
	 * Note: you cannot modify the returned object in any way, to update
	 * the container, get a new shared_ptr<sinsp_container_info> and pass it
	 * to replace_container()
	 */
	sinsp_container_info::ptr_t get_container(const std::string &id) const override;

	/**
	 * @brief Generate container JSON event from a new container
	 * @param container_info reference to the new sinsp_container_info
	 *
	 * Note: this is unrelated to on_new_container callbacks even though
	 * both happen during container creation
	 */
	void notify_new_container(const sinsp_container_info& container_info, sinsp_threadinfo *tinfo = nullptr) override;

	bool async_allowed() const override;

	/**
	 * @brief Detect container engine for a thread
	 * @param tinfo the thread to do container detection for
	 * @param query_os_for_missing_info should we consult external data sources?
	 * 		if true, we're working with a live capture and should
	 * 		query the OS (external files, daemons etc.); if false,
	 * 		we're reading a scap file so only rely on the thread info itself
	 * @return true if we have successfully determined the container engine,
	 * 		false otherwise
	 *
	 * Note: a return value of false doesn't mean that container detection failed,
	 * it may still be happening in the background asynchronously
	 */
	bool resolve_container(sinsp_threadinfo* tinfo, bool query_os_for_missing_info);
	void dump_containers(scap_dumper_t* dumper);
	std::string get_container_name(sinsp_threadinfo* tinfo) const;

	// Set tinfo's m_category based on the container context.  It
	// will *not* change any category to NONE, so a threadinfo
	// that initially has a category will retain its category
	// across execs e.g. "sh -c /bin/true" execing /bin/true.
	void identify_category(sinsp_threadinfo *tinfo);

	bool container_exists(const std::string& container_id) const override{
		auto containers = m_containers.lock();
		return containers->find(container_id) != containers->end() ||
			m_lookups.find(container_id) != m_lookups.end();
	}

	typedef std::function<void(const sinsp_container_info&, sinsp_threadinfo *)> new_container_cb;
	typedef std::function<void(const sinsp_container_info&)> remove_container_cb;
	void subscribe_on_new_container(new_container_cb callback);
	void subscribe_on_remove_container(remove_container_cb callback);

	void create_engines();

	/**
	 * Update the container_info associated with the given type and container_id
	 * to include the size of the container layer. This is not filled in the
	 * initial request because it can easily take seconds.
	 */
	void update_container_with_size(sinsp_container_type type,
					const std::string& container_id);
	void cleanup();

	void set_docker_socket_path(std::string socket_path);
	void set_query_docker_image_info(bool query_image_info);
	void set_cri_extra_queries(bool extra_queries);
	void set_cri_socket_path(const std::string& path);
	void add_cri_socket_path(const std::string &path);
	void set_cri_timeout(int64_t timeout_ms);
	void set_cri_async(bool async);
	void set_cri_delay(uint64_t delay_ms);
	void set_container_labels_max_len(uint32_t max_label_len);
	sinsp* get_inspector() { return m_inspector; }

	/**
	 * \brief set the status of an async container metadata lookup
	 * @param container_id the container id we're looking up
	 * @param ctype the container engine that is doing the lookup
	 * @param state the state of the lookup
	 *
	 * Container engines that do not do any lookups in external services need not
	 * bother with this. Otherwise, the engine needs to maintain the current
	 * state of the lookup via this method and call should_lookup() before
	 * starting a new lookup.
	 */
	void set_lookup_status(const std::string& container_id, sinsp_container_type ctype, sinsp_container_lookup_state state) override
	{
		m_lookups[container_id][ctype] = state;
	}

	/**
	 * \brief do we want to start a new lookup for container metadata?
	 * @param container_id the container id we want to look up
	 * @param ctype the container engine that is doing the lookup
	 * @return true if there's no lookup in progress and we're free to start
	 * a new one, false otherwise
	 *
	 * This method effectively checks if m_lookups[container_id][ctype]
	 * exists, without creating unnecessary map entries along the way.
	 */
	bool should_lookup(const std::string& container_id, sinsp_container_type ctype) override
	{
		auto container_lookups = m_lookups.find(container_id);
		if(container_lookups == m_lookups.end())
		{
			return true;
		}
		auto engine_lookup = container_lookups->second.find(ctype);
		return engine_lookup == container_lookups->second.end();
	}
private:
	std::string container_to_json(const sinsp_container_info& container_info);
	bool container_to_sinsp_event(const std::string& json, sinsp_evt* evt, std::shared_ptr<sinsp_threadinfo> tinfo);
	std::string get_docker_env(const Json::Value &env_vars, const std::string &mti);

	std::list<std::shared_ptr<libsinsp::container_engine::container_engine_base>> m_container_engines;
	std::map<sinsp_container_type, std::shared_ptr<libsinsp::container_engine::container_engine_base>> m_container_engine_by_type;

	sinsp* m_inspector;
	libsinsp::Mutex<std::unordered_map<std::string, std::shared_ptr<const sinsp_container_info>>> m_containers;
	std::unordered_map<std::string, std::unordered_map<sinsp_container_type, sinsp_container_lookup_state>> m_lookups;
	uint64_t m_last_flush_time_ns;
	std::list<new_container_cb> m_new_callbacks;
	std::list<remove_container_cb> m_remove_callbacks;

	// indicates whether we should use only the static container engine, or the other engines.
	// if true, we expect to have the subsequent bits of metadata as well. If this bool is false,
	// then the values of those metadata are undefined
	bool m_static_container;
	std::string m_static_id;
	std::string m_static_name;
	std::string m_static_image;

	friend class test_helper;
};