Allow a 'monitor' to be connected to remotely if configured.

So far monitors have the access an permissions of an
authenticated admin.

Change-Id: I59dfa8a646a60584a5c113ee0521e9afba4f6b76
private/mmeeks/monitoring
Michael Meeks 2018-04-18 19:20:54 +01:00 committed by Jan Holesovsky
parent d66e8d13b7
commit b483f477dd
6 changed files with 188 additions and 9 deletions

View File

@ -116,4 +116,8 @@
<password desc="The password of the admin console. Deprecated on most platforms. Instead, use PAM or loolconfig to set up a secure password."></password>
</admin_console>
<monitors desc="Addresses of servers we connect to on start for monitoring">
<monitor>ws://localhost:9042/foo</monitor>
</monitors>
</config>

View File

@ -118,7 +118,7 @@ public:
}
/// Create socket of the given type.
int createSocket(Type type);
static int createSocket(Type type);
/// Returns the OS native socket fd.
int getFD() const { return _fd; }

View File

@ -432,7 +432,12 @@ protected:
const std::string res = oss.str();
LOG_TRC("#" << socket->getFD() << ": Sending WS Upgrade response: " << res);
socket->send(res);
setWebSocket();
}
void setWebSocket()
{
std::shared_ptr<StreamSocket> socket = _socket.lock();
socket->setWebSocket();
// No need to ping right upon connection/upgrade,

View File

@ -28,11 +28,15 @@
# include <SslSocket.hpp>
#endif
SocketPoll DumpSocketPoll("websocket");
// Dumps incoming websocket messages and doesn't respond.
class DumpSocketHandler : public WebSocketHandler
{
public:
DumpSocketHandler()
DumpSocketHandler(const std::weak_ptr<StreamSocket>& socket,
const Poco::Net::HTTPRequest& request) :
WebSocketHandler(socket, request)
{
}
@ -63,7 +67,7 @@ private:
}
/// Called after successful socket reads.
void handleIncomingMessage(SocketDisposition & /* disposition */) override
void handleIncomingMessage(SocketDisposition &disposition) override
{
std::shared_ptr<StreamSocket> socket = _socket.lock();
std::vector<char>& in = socket->_inBuffer;
@ -135,7 +139,9 @@ private:
Poco::StringTokenizer::TOK_TRIM);
if (request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
socket->setHandler(std::make_shared<DumpSocketHandler>());
{
socket->setHandler(std::make_shared<DumpSocketHandler>(_socket, request));
}
else
{
Poco::Net::HTTPResponse response;
@ -143,7 +149,7 @@ private:
response.setContentLength(0);
LOG_INF("DumpWebSockets bad request");
socket->send(response);
socket->shutdown();
disposition.setClosed();
}
}
catch (const std::exception& exc)
@ -187,7 +193,7 @@ class DumpSocketFactory final : public SocketFactory
{
std::shared_ptr<Socket> create(const int physicalFd) override
{
#if ENABLE_SSL
#if 0 && ENABLE_SSL
return StreamSocket::create<SslStreamSocket>(physicalFd, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
#else
return StreamSocket::create<StreamSocket>(physicalFd, std::unique_ptr<SocketHandlerInterface>{ new ClientRequestDispatcher });
@ -208,12 +214,14 @@ int main (int argc, char **argv)
int port = 9042;
(void) argc; (void) argv;
Log::initialize("WebSocketDump", "trace", true, false,
std::map<std::string, std::string>());
SocketPoll acceptPoll("accept");
SocketPoll DumpSocketPoll("websocket");
// Setup listening socket with a factory for connected sockets.
auto serverSocket = std::make_shared<ServerSocket>(
Socket::Type::IPv4, DumpSocketPoll,
Socket::Type::All, DumpSocketPoll,
std::make_shared<DumpSocketFactory>());
if (!serverSocket->bind(ServerSocket::Type::Public, port))
@ -233,7 +241,7 @@ int main (int argc, char **argv)
while (true)
{
DumpSocketPoll.poll(1000);
DumpSocketPoll.poll(100 * 1000);
}
}

View File

@ -34,6 +34,7 @@
#include <Util.hpp>
#include <net/Socket.hpp>
#include <net/SslSocket.hpp>
#include <net/WebSocketHandler.hpp>
#include <common/SigUtil.hpp>
@ -278,6 +279,14 @@ AdminSocketHandler::AdminSocketHandler(Admin* adminManager,
{
}
AdminSocketHandler::AdminSocketHandler(Admin* adminManager)
: WebSocketHandler(),
_admin(adminManager),
_sessionId(0),
_isAuthenticated(true)
{
}
void AdminSocketHandler::sendTextFrame(const std::string& message)
{
UnitWSD::get().onAdminQueryMessage(message);
@ -613,8 +622,156 @@ void Admin::dumpState(std::ostream& os)
SocketPoll::dumpState(os);
}
class MonitorSocketHandler : public AdminSocketHandler
{
bool _connecting;
public:
MonitorSocketHandler(Admin *admin) :
AdminSocketHandler(admin),
_connecting(true)
{
}
int getPollEvents(std::chrono::steady_clock::time_point now,
int &timeoutMaxMs) override
{
if (_connecting)
{
LOG_TRC("Waiting for outbound connection to complete");
return POLLOUT;
}
else
return AdminSocketHandler::getPollEvents(now, timeoutMaxMs);
}
void performWrites() override
{
LOG_TRC("Outbound monitor - connected");
_connecting = false;
setWebSocket();
return AdminSocketHandler::performWrites();
}
};
void Admin::connectToMonitor(const Poco::URI &uri)
{
LOG_INF("Connecting to monitor " << uri.getHost() << " : " << uri.getPort() << " : " << uri.getPath());
// FIXME: put this in a ClientSocket class ?
// FIXME: store the address there - and ... (so on) ...
struct addrinfo* ainfo = nullptr;
struct addrinfo hints;
std::memset(&hints, 0, sizeof(hints));
int rc = getaddrinfo(uri.getHost().c_str(),
std::to_string(uri.getPort()).c_str(),
&hints, &ainfo);
std::string canonicalName;
bool isSSL = uri.getScheme() != "ws";
#if !ENABLE_SSL
if (isSSL)
{
LOG_ERR("Error: wss for monitor requested but SSL not compiled in.");
return;
}
#endif
if (!rc && ainfo)
{
for (struct addrinfo* ai = ainfo; ai; ai = ai->ai_next)
{
if (ai->ai_canonname)
canonicalName = ai->ai_canonname;
if (ai->ai_addrlen && ai->ai_addr)
{
int fd = socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
int res = connect(fd, ai->ai_addr, ai->ai_addrlen);
// FIXME: SSL sockets presumably need some setup, checking etc. and ... =)
if (fd < 0 || (res < 0 && errno != EINPROGRESS))
{
LOG_ERR("Failed to connect to " << uri.getHost());
close(fd);
}
else
{
std::shared_ptr<StreamSocket> socket;
std::shared_ptr<SocketHandlerInterface> handler = std::make_shared<MonitorSocketHandler>(this);
#if ENABLE_SSL
if (isSSL)
socket = StreamSocket::create<SslStreamSocket>(fd, handler);
#endif
if (!socket && !isSSL)
socket = StreamSocket::create<StreamSocket>(fd, handler);
if (socket)
{
LOG_DBG("Connected to monitor " << uri.getHost() << " #" << socket->getFD());
// cf. WebSocketHandler::upgradeToWebSocket (?)
// send Sec-WebSocket-Key: <hmm> ... Sec-WebSocket-Protocol: chat, Sec-WebSocket-Version: 13
std::ostringstream oss;
oss << "GET " << uri.getHost() << " HTTP/1.1\r\n"
"Connection:Upgrade\r\n"
"Sec-WebSocket-Accept:GAcwqP21iVOY2yKefQ64c0yVN5M=\r\n"
"Upgrade:websocket\r\n"
"Accept-Encoding:gzip, deflate, br\r\n"
"Accept-Language:en\r\n"
"Cache-Control:no-cache\r\n"
"Pragma:no-cache\r\n"
"Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits\r\n"
"Sec-WebSocket-Key:fxTaWTEMVhq1PkWsMoLxGw==\r\n"
"Sec-WebSocket-Version:13\r\n"
"User-Agent: " << WOPI_AGENT_STRING << "\r\n"
"\r\n";
socket->send(oss.str());
handler->onConnect(socket);
insertNewSocket(socket);
}
else
{
LOG_ERR("Failed to allocate socket for monitor " << uri.getHost());
close(fd);
}
break;
}
}
}
freeaddrinfo(ainfo);
}
else
LOG_ERR("Failed to lookup monitor host '" << uri.getHost() << "' skipping");
}
void Admin::start()
{
bool haveMonitors = false;
const auto& config = Application::instance().config();
for (size_t i = 0; ; ++i)
{
const std::string path = "monitors.monitor[" + std::to_string(i) + "]";
const std::string uri = config.getString(path, "");
if (!config.has(path))
break;
if (!uri.empty())
{
Poco::URI monitor(uri);
if (monitor.getScheme() == "wss" || monitor.getScheme() == "ws")
{
addCallback([=] { connectToMonitor(monitor); } );
haveMonitors = true;
}
else
LOG_ERR("Unhandled monitor URI: '" << uri << "' should be \"wss://foo:1234/baa\"");
}
}
if (!haveMonitors)
LOG_TRC("No monitors configured.");
startThread();
}

View File

@ -23,6 +23,8 @@ class Admin;
class AdminSocketHandler : public WebSocketHandler
{
public:
AdminSocketHandler(Admin* adminManager);
AdminSocketHandler(Admin* adminManager,
const std::weak_ptr<StreamSocket>& socket,
const Poco::Net::HTTPRequest& request);
@ -130,6 +132,9 @@ private:
return ((value + MinStatsIntervalMs - 1) / MinStatsIntervalMs) * MinStatsIntervalMs;
}
/// Synchronous connection setup to remote monitoring server
void connectToMonitor(const Poco::URI &uri);
private:
/// The model is accessed only during startup & in
/// the Admin Poll thread.