summaryrefslogtreecommitdiffstats
path: root/lib/remote/apilistener.hpp
blob: fced0a8afb180ca2479fea623fb6e0a756ca3ac0 (plain)
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */

#ifndef APILISTENER_H
#define APILISTENER_H

#include "remote/apilistener-ti.hpp"
#include "remote/jsonrpcconnection.hpp"
#include "remote/httpserverconnection.hpp"
#include "remote/endpoint.hpp"
#include "remote/messageorigin.hpp"
#include "base/configobject.hpp"
#include "base/process.hpp"
#include "base/shared.hpp"
#include "base/timer.hpp"
#include "base/workqueue.hpp"
#include "base/tcpsocket.hpp"
#include "base/tlsstream.hpp"
#include "base/threadpool.hpp"
#include <atomic>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <cstdint>
#include <mutex>
#include <set>

namespace icinga
{

class JsonRpcConnection;

/**
 * @ingroup remote
 */
struct ConfigDirInformation
{
	Dictionary::Ptr UpdateV1;
	Dictionary::Ptr UpdateV2;
	Dictionary::Ptr Checksums;
};

/**
 * If the version reported by icinga::Hello is not enough to tell whether
 * the peer has a specific capability, add the latter to this bitmask.
 *
 * Note that due to the capability exchange via JSON-RPC and the state storage via JSON
 * the bitmask numbers are stored in IEEE 754 64-bit floats.
 * The latter have 53 digit bits which limit the bitmask.
 * Not to run out of bits:
 *
 * Once all Icinga versions which don't have a specific capability are completely EOL,
 * remove the respective capability checks and assume the peer has the capability.
 * Once all Icinga versions which still check for the capability are completely EOL,
 * remove the respective bit from icinga::Hello.
 * Once all Icinga versions which still have the respective bit in icinga::Hello
 * are completely EOL, remove the bit here.
 * Once all Icinga versions which still have the respective bit here
 * are completely EOL, feel free to re-use the bit.
 *
 * completely EOL = not supported, even if an important customer of us used it and
 * not expected to appear in a multi-level cluster, e.g. a 4 level cluster with
 * v2.11 -> v2.10 -> v2.9 -> v2.8 - v2.7 isn't here
 *
 * @ingroup remote
 */
enum class ApiCapabilities : uint_fast64_t
{
	ExecuteArbitraryCommand = 1u << 0u,
	IfwApiCheckCommand = 1u << 1u,
};

/**
* @ingroup remote
*/
class ApiListener final : public ObjectImpl<ApiListener>
{
public:
	DECLARE_OBJECT(ApiListener);
	DECLARE_OBJECTNAME(ApiListener);

	static boost::signals2::signal<void(bool)> OnMasterChanged;

	ApiListener();

	static String GetApiDir();
	static String GetApiZonesDir();
	static String GetApiZonesStageDir();
	static String GetCertsDir();
	static String GetCaDir();
	static String GetCertificateRequestsDir();

	std::shared_ptr<X509> RenewCert(const std::shared_ptr<X509>& cert, bool ca = false);
	void UpdateSSLContext();

	static ApiListener::Ptr GetInstance();

	Endpoint::Ptr GetMaster() const;
	bool IsMaster() const;

	Endpoint::Ptr GetLocalEndpoint() const;

	void SyncSendMessage(const Endpoint::Ptr& endpoint, const Dictionary::Ptr& message);
	void RelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log);

	static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata);
	std::pair<Dictionary::Ptr, Dictionary::Ptr> GetStatus();

	bool AddAnonymousClient(const JsonRpcConnection::Ptr& aclient);
	void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient);
	std::set<JsonRpcConnection::Ptr> GetAnonymousClients() const;

	void AddHttpClient(const HttpServerConnection::Ptr& aclient);
	void RemoveHttpClient(const HttpServerConnection::Ptr& aclient);
	std::set<HttpServerConnection::Ptr> GetHttpClients() const;

	static double CalculateZoneLag(const Endpoint::Ptr& endpoint);

	/* filesync */
	static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
	void HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);

	/* configsync */
	static void ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, const Value& cookie);
	static Value ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
	static Value ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);

	/* API config packages */
	void SetActivePackageStage(const String& package, const String& stage);
	String GetActivePackageStage(const String& package);
	void RemoveActivePackageStage(const String& package);

	static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);

	static void UpdateObjectAuthority();

	static bool IsHACluster();
	static String GetFromZoneName(const Zone::Ptr& fromZone);

	static String GetDefaultCertPath();
	static String GetDefaultKeyPath();
	static String GetDefaultCaPath();

	static inline
	bool UpdatedObjectAuthority()
	{
		return m_UpdatedObjectAuthority.load();
	}

	double GetTlsHandshakeTimeout() const override;
	void SetTlsHandshakeTimeout(double value, bool suppress_events, const Value& cookie) override;

protected:
	void OnConfigLoaded() override;
	void OnAllConfigLoaded() override;
	void Start(bool runtimeCreated) override;
	void Stop(bool runtimeDeleted) override;

	void ValidateTlsProtocolmin(const Lazy<String>& lvalue, const ValidationUtils& utils) override;
	void ValidateTlsHandshakeTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils) override;

private:
	Shared<boost::asio::ssl::context>::Ptr m_SSLContext;
	boost::shared_mutex m_SSLContextMutex;

	mutable std::mutex m_AnonymousClientsLock;
	mutable std::mutex m_HttpClientsLock;
	std::set<JsonRpcConnection::Ptr> m_AnonymousClients;
	std::set<HttpServerConnection::Ptr> m_HttpClients;

	Timer::Ptr m_Timer;
	Timer::Ptr m_ReconnectTimer;
	Timer::Ptr m_AuthorityTimer;
	Timer::Ptr m_CleanupCertificateRequestsTimer;
	Timer::Ptr m_ApiPackageIntegrityTimer;
	Timer::Ptr m_RenewOwnCertTimer;

	Endpoint::Ptr m_LocalEndpoint;

	static ApiListener::Ptr m_Instance;
	static std::atomic<bool> m_UpdatedObjectAuthority;

	void ApiTimerHandler();
	void ApiReconnectTimerHandler();
	void CleanupCertificateRequestsTimerHandler();
	void CheckApiPackageIntegrity();

	bool AddListener(const String& node, const String& service);
	void AddConnection(const Endpoint::Ptr& endpoint);

	void NewClientHandler(
		boost::asio::yield_context yc, const Shared<boost::asio::io_context::strand>::Ptr& strand,
		const Shared<AsioTlsStream>::Ptr& client, const String& hostname, ConnectionRole role
	);
	void NewClientHandlerInternal(
		boost::asio::yield_context yc, const Shared<boost::asio::io_context::strand>::Ptr& strand,
		const Shared<AsioTlsStream>::Ptr& client, const String& hostname, ConnectionRole role
	);
	void ListenerCoroutineProc(boost::asio::yield_context yc, const Shared<boost::asio::ip::tcp::acceptor>::Ptr& server);

	WorkQueue m_RelayQueue;
	WorkQueue m_SyncQueue{0, 4};

	std::mutex m_LogLock;
	Stream::Ptr m_LogFile;
	size_t m_LogMessageCount{0};

	bool RelayMessageOne(const Zone::Ptr& zone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentZoneMaster);
	void SyncRelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log);
	void PersistMessage(const Dictionary::Ptr& message, const ConfigObject::Ptr& secobj);

	void OpenLogFile();
	void RotateLogFile();
	void CloseLogFile();
	static void LogGlobHandler(std::vector<int>& files, const String& file);
	void ReplayLog(const JsonRpcConnection::Ptr& client);

	static void CopyCertificateFile(const String& oldCertPath, const String& newCertPath);

	void UpdateStatusFile(boost::asio::ip::tcp::endpoint localEndpoint);
	void RemoveStatusFile();

	/* filesync */
	static std::mutex m_ConfigSyncStageLock;

	void SyncLocalZoneDirs() const;
	void SyncLocalZoneDir(const Zone::Ptr& zone) const;
	void RenewOwnCert();
	void RenewCA();

	void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);

	static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config);

	static ConfigDirInformation LoadConfigDir(const String& dir);
	static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file);

	static void TryActivateZonesStage(const std::vector<String>& relativePaths);

	static String GetChecksum(const String& content);
	static bool CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig);

	void UpdateLastFailedZonesStageValidation(const String& log);
	void ClearLastFailedZonesStageValidation();

	/* configsync */
	void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
		const JsonRpcConnection::Ptr& client = nullptr);
	void DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
		const JsonRpcConnection::Ptr& client = nullptr);
	void SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient);

	void SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoint::Ptr& endpoint, bool needSync);

	/* API Config Packages */
	mutable std::mutex m_ActivePackageStagesLock;
	std::map<String, String> m_ActivePackageStages;

	void UpdateActivePackageStagesCache();
};

}

#endif /* APILISTENER_H */