[Loginserver] Health Checks (#1665)

* Health checks stash

* Healthcheck work
This commit is contained in:
Chris Miles 2021-11-03 14:39:51 -05:00 committed by GitHub
parent e4138b871b
commit 6e26e8953c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 196 additions and 17 deletions

View File

@ -3,6 +3,7 @@
#include "../common/event/task_scheduler.h" #include "../common/event/task_scheduler.h"
#include "../common/event/event_loop.h" #include "../common/event/event_loop.h"
#include "../common/net/dns.h" #include "../common/net/dns.h"
#include "../common/string_util.h"
extern LoginServer server; extern LoginServer server;
EQ::Event::TaskScheduler task_runner; EQ::Event::TaskScheduler task_runner;
@ -377,16 +378,22 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
} }
); );
EQ::Net::DNSLookup( auto s = SplitString(server.options.GetEQEmuLoginServerAddress(), ':');
"login.eqemulator.net", 5999, false, [&](const std::string &addr) { if (s.size() == 2) {
if (addr.empty()) { auto address = s[0];
ret = 0; auto port = std::stoi(s[1]);
running = false;
}
mgr.Connect(addr, 5999); EQ::Net::DNSLookup(
} address, port, false, [&](const std::string &addr) {
); if (addr.empty()) {
ret = 0;
running = false;
}
mgr.Connect(addr, port);
}
);
}
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
@ -407,3 +414,114 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
return res.get(); return res.get();
} }
uint32 AccountManagement::HealthCheckUserLogin()
{
std::string in_account_username = "healthcheckuser";
std::string in_account_password = "healthcheckpassword";
auto res = task_runner.Enqueue(
[&]() -> uint32 {
bool running = true;
uint32 ret = 0;
EQ::Net::DaybreakConnectionManager mgr;
std::shared_ptr<EQ::Net::DaybreakConnection> c;
mgr.OnNewConnection(
[&](std::shared_ptr<EQ::Net::DaybreakConnection> connection) {
c = connection;
}
);
mgr.OnConnectionStateChange(
[&](
std::shared_ptr<EQ::Net::DaybreakConnection> conn,
EQ::Net::DbProtocolStatus from,
EQ::Net::DbProtocolStatus to
) {
if (EQ::Net::StatusConnected == to) {
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 1); //OP_SessionReady
p.PutUInt32(2, 2);
c->QueuePacket(p);
}
else if (EQ::Net::StatusDisconnected == to) {
running = false;
}
}
);
mgr.OnPacketRecv(
[&](std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p) {
auto opcode = p.GetUInt16(0);
switch (opcode) {
case 0x0017: //OP_ChatMessage
{
size_t buffer_len =
in_account_username.length() + in_account_password.length() + 2;
std::unique_ptr<char[]> buffer(new char[buffer_len]);
strcpy(&buffer[0], in_account_username.c_str());
strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str());
size_t encrypted_len = buffer_len;
if (encrypted_len % 8 > 0) {
encrypted_len = ((encrypted_len / 8) + 1) * 8;
}
EQ::Net::DynamicPacket p;
p.Resize(12 + encrypted_len);
p.PutUInt16(0, 2); //OP_Login
p.PutUInt32(2, 3);
eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true);
c->QueuePacket(p);
break;
}
case 0x0018: {
auto encrypt_size = p.Length() - 12;
if (encrypt_size % 8 > 0) {
encrypt_size = (encrypt_size / 8) * 8;
}
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false);
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
auto response_error = sp.GetUInt16(1);
{
// we only care to see the response code
ret = response_error;
running = false;
}
break;
}
}
}
);
mgr.Connect("127.0.0.1", 5999);
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
auto &loop = EQ::EventLoop::Get();
while (running) {
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > 2000) {
ret = 0;
running = false;
}
loop.Process();
}
return ret;
}
);
return res.get();
}

View File

@ -89,6 +89,8 @@ public:
uint32 in_account_id, uint32 in_account_id,
const std::string &in_account_password_hash const std::string &in_account_password_hash
); );
static uint32 HealthCheckUserLogin();
}; };

View File

@ -233,11 +233,16 @@ void Client::Handle_Login(const char *data, unsigned int size)
user = components[1]; user = components[1];
} }
// health checks
if (ProcessHealthCheck(user)) {
DoFailedLogin();
return;
}
LogInfo( LogInfo(
"Attempting password based login [{0}] login [{1}] user [{2}]", "Attempting password based login [{0}] login [{1}]",
user, user,
db_loginserver, db_loginserver
user
); );
ParseAccountString(user, user, db_loginserver); ParseAccountString(user, user, db_loginserver);
@ -376,6 +381,7 @@ void Client::AttemptLoginAccountCreation(
return; return;
} }
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(
user, user,
pass pass
@ -766,3 +772,11 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p)
m_login_connection->Close(); m_login_connection->Close();
} }
} }
bool Client::ProcessHealthCheck(std::string username)
{
if (username == "healthcheckuser") {
return true;
}
return false;
}

View File

@ -208,6 +208,7 @@ private:
void LoginSendSessionReady(); void LoginSendSessionReady();
void LoginSendLogin(); void LoginSendLogin();
void LoginProcessLoginResponse(const EQ::Net::Packet &p); void LoginProcessLoginResponse(const EQ::Net::Packet &p);
static bool ProcessHealthCheck(std::string username);
}; };
#endif #endif

View File

@ -38,6 +38,7 @@ namespace LoginserverCommandHandler {
function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens;
function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount;
function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword; function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword;
function_map["health:check-login"] = &LoginserverCommandHandler::HealthCheckLogin;
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
} }
@ -281,4 +282,24 @@ namespace LoginserverCommandHandler {
cmd(3).str() cmd(3).str()
); );
} }
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Checks login health using a test user";
std::vector<std::string> arguments = {};
std::vector<std::string> options = {};
if (cmd[{"-h", "--help"}]) {
return;
}
LogInfo("[CLI] [HealthCheck] Response code [{}]", AccountManagement::HealthCheckUserLogin());
}
} }

View File

@ -14,6 +14,7 @@ namespace LoginserverCommandHandler {
void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description);
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description);
}; };

View File

@ -28,7 +28,7 @@ namespace LoginserverWebserver {
} }
Json::Value response; Json::Value response;
auto iter = server.server_manager->getWorldServers().begin(); auto iter = server.server_manager->getWorldServers().begin();
while (iter != server.server_manager->getWorldServers().end()) { while (iter != server.server_manager->getWorldServers().end()) {
Json::Value row; Json::Value row;
row["server_long_name"] = (*iter)->GetServerLongName(); row["server_long_name"] = (*iter)->GetServerLongName();
@ -297,6 +297,24 @@ namespace LoginserverWebserver {
LoginserverWebserver::SendResponse(response, res); LoginserverWebserver::SendResponse(response, res);
} }
); );
api.Get(
"/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) {
Json::Value response;
uint32 login_response = AccountManagement::HealthCheckUserLogin();
response["status"] = login_response;
if (login_response == 0) {
response["message"] = "Process unresponsive, exiting...";
LogInfo("Probes healthcheck unresponsive, exiting...");
}
LoginserverWebserver::SendResponse(response, res);
if (login_response == 0) {
std::exit(0);
}
}
);
} }
/** /**

View File

@ -10,6 +10,7 @@
#include "login_server.h" #include "login_server.h"
#include "loginserver_webserver.h" #include "loginserver_webserver.h"
#include "loginserver_command_handler.h" #include "loginserver_command_handler.h"
#include "../common/string_util.h"
#include <time.h> #include <time.h>
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
@ -20,6 +21,7 @@ LoginServer server;
EQEmuLogSys LogSys; EQEmuLogSys LogSys;
bool run_server = true; bool run_server = true;
void ResolveAddresses();
void CatchSignal(int sig_num) void CatchSignal(int sig_num)
{ {
} }
@ -118,11 +120,13 @@ void start_web_server()
httplib::Server api; httplib::Server api;
api.set_logger([](const auto& req, const auto& res) { api.set_logger(
if (!req.path.empty()) { [](const auto &req, const auto &res) {
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); if (!req.path.empty()) {
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port);
}
} }
}); );
LoginserverWebserver::RegisterRoutes(api); LoginserverWebserver::RegisterRoutes(api);
api.listen("0.0.0.0", web_api_port); api.listen("0.0.0.0", web_api_port);