initial working code

pull/1306/head
Jeff Becker 4 years ago
parent 78256e3228
commit ad882d0d70
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -276,10 +276,6 @@ if(SUBMODULE_CHECK)
endif() endif()
endif() endif()
add_subdirectory(external/loki-mq)
include_directories(external/loki-mq)
include_directories(external/loki-mq/mapbox-variant/include)
# We only actually need pybind11 with WITH_HIVE, but if we don't load it here then something further # We only actually need pybind11 with WITH_HIVE, but if we don't load it here then something further
# down loads a broken PythonInterp that loads Python2, but Python2 headers are not C++17 compatible. # down loads a broken PythonInterp that loads Python2, but Python2 headers are not C++17 compatible.
# So load this here universally so that pybind's more intelligent python finder finds python3.x # So load this here universally so that pybind's more intelligent python finder finds python3.x

@ -64,13 +64,13 @@ elseif(DOWNLOAD_UV)
endif() endif()
include_directories(${LIBUV_INCLUDE_DIRS}) include_directories(${LIBUV_INCLUDE_DIRS})
#find_package(LokiMQ) find_package(LokiMQ 1.2)
#if(LokiMQ_FOUND) if(LokiMQ_FOUND)
# message(STATUS "using system lokimq") message(STATUS "using system lokimq")
#else() else()
message(STATUS "using lokimq submodule") message(STATUS "using lokimq submodule")
add_subdirectory(${CMAKE_SOURCE_DIR}/external/loki-mq) add_subdirectory(${CMAKE_SOURCE_DIR}/external/loki-mq EXCLUDE_FROM_ALL)
#endif() endif()
if(EMBEDDED_CFG OR ${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(EMBEDDED_CFG OR ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
link_libatomic() link_libatomic()

@ -6,19 +6,6 @@
#include "crypto/crypto.hpp" #include "crypto/crypto.hpp"
#include "crypto/types.hpp" #include "crypto/types.hpp"
#ifdef HAVE_CURL
#include <curl/curl.h>
#endif
/// curl callback
static size_t
curl_RecvIdentKey(char* ptr, size_t, size_t nmemb, void* userdata)
{
for (size_t idx = 0; idx < nmemb; idx++)
static_cast<std::vector<char>*>(userdata)->push_back(ptr[idx]);
return nmemb;
}
namespace llarp namespace llarp
{ {
KeyManager::KeyManager() : m_initialized(false), m_needBackup(false) KeyManager::KeyManager() : m_initialized(false), m_needBackup(false)
@ -55,11 +42,6 @@ namespace llarp
m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile); m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile);
m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile); m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile);
m_usingLokid = config.lokid.whitelistRouters;
m_lokidRPCAddr = config.lokid.lokidRPCAddr;
m_lokidRPCUser = config.lokid.lokidRPCUser;
m_lokidRPCPassword = config.lokid.lokidRPCPassword;
RouterContact rc; RouterContact rc;
bool exists = rc.Read(m_rcPath); bool exists = rc.Read(m_rcPath);
if (not exists and not genIfAbsent) if (not exists and not genIfAbsent)
@ -99,7 +81,7 @@ namespace llarp
} }
} }
if (not m_usingLokid) if (not config.lokid.whitelistRouters)
{ {
// load identity key or create if needed // load identity key or create if needed
auto identityKeygen = [](llarp::SecretKey& key) { auto identityKeygen = [](llarp::SecretKey& key) {
@ -109,11 +91,6 @@ namespace llarp
if (not loadOrCreateKey(m_idKeyPath, identityKey, identityKeygen)) if (not loadOrCreateKey(m_idKeyPath, identityKey, identityKeygen))
return false; return false;
} }
else
{
if (not loadIdentityFromLokid())
return false;
}
// load encryption key // load encryption key
auto encryptionKeygen = [](llarp::SecretKey& key) { auto encryptionKeygen = [](llarp::SecretKey& key) {
@ -227,100 +204,4 @@ namespace llarp
return key.LoadFromFile(filepath); return key.LoadFromFile(filepath);
} }
bool
KeyManager::loadIdentityFromLokid()
{
#ifndef HAVE_CURL
LogError("this lokinet was not built with service node mode support");
return false;
#else
CURL* curl = curl_easy_init();
if (curl)
{
bool ret = false;
std::stringstream ss;
ss << "http://" << m_lokidRPCAddr << "/json_rpc";
const auto url = ss.str();
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
const auto auth = m_lokidRPCUser + ":" + m_lokidRPCPassword;
curl_easy_setopt(curl, CURLOPT_USERPWD, auth.c_str());
curl_slist* list = nullptr;
list = curl_slist_append(list, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
nlohmann::json request = {
{"id", "0"}, {"jsonrpc", "2.0"}, {"method", "get_service_node_privkey"}};
const auto data = request.dump();
std::vector<char> resp;
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_RecvIdentKey);
resp.clear();
LogInfo("Getting Identity Keys from lokid...");
if (curl_easy_perform(curl) == CURLE_OK)
{
try
{
auto j = nlohmann::json::parse(resp);
if (not j.is_object())
return false;
const auto itr = j.find("result");
if (itr == j.end())
return false;
if (not itr->is_object())
return false;
const auto k = (*itr)["service_node_ed25519_privkey"].get<std::string>();
if (k.size() != (identityKey.size() * 2))
{
if (k.empty())
{
LogError("lokid gave no identity key");
}
else
{
LogError("lokid gave invalid identity key");
}
return false;
}
if (not HexDecode(k.c_str(), identityKey.data(), identityKey.size()))
return false;
if (CryptoManager::instance()->check_identity_privkey(identityKey))
{
ret = true;
}
else
{
LogError("lokid gave bogus identity key");
}
}
catch (nlohmann::json::exception& ex)
{
LogError("Bad response from lokid: ", ex.what());
}
}
else
{
LogError("failed to get identity keys");
}
if (ret)
{
LogInfo("Got Identity Keys from lokid: ", RouterID(seckey_topublic(identityKey)));
}
curl_easy_cleanup(curl);
curl_slist_free_all(list);
return ret;
}
else
{
LogError("failed to init curl");
return false;
}
#endif
}
} // namespace llarp } // namespace llarp

@ -18,7 +18,6 @@ namespace llarp
/// In addition, the KeyManager detects when the keys obsolete (e.g. as a /// In addition, the KeyManager detects when the keys obsolete (e.g. as a
/// result of a software upgrade) and backs up existing keys before writing /// result of a software upgrade) and backs up existing keys before writing
/// out new ones. /// out new ones.
struct KeyManager struct KeyManager
{ {
/// Utility function to backup a file by moving it. Attempts to find a new /// Utility function to backup a file by moving it. Attempts to find a new
@ -34,8 +33,7 @@ namespace llarp
/// Constructor /// Constructor
KeyManager(); KeyManager();
/// Initializes keys using the provided config, loading from disk and/or /// Initializes keys using the provided config, loading from disk
/// lokid via HTTP request.
/// ///
/// NOTE: Must be called prior to obtaining any keys. /// NOTE: Must be called prior to obtaining any keys.
/// NOTE: blocks on I/O /// NOTE: blocks on I/O
@ -75,11 +73,6 @@ namespace llarp
std::atomic_bool m_initialized; std::atomic_bool m_initialized;
std::atomic_bool m_needBackup; std::atomic_bool m_needBackup;
bool m_usingLokid = false;
std::string m_lokidRPCAddr = "127.0.0.1:22023";
std::string m_lokidRPCUser;
std::string m_lokidRPCPassword;
/// Backup each key file (by copying, e.g. foo -> foo.bak) /// Backup each key file (by copying, e.g. foo -> foo.bak)
bool bool
backupKeyFilesByMoving() const; backupKeyFilesByMoving() const;
@ -92,10 +85,6 @@ namespace llarp
const fs::path& filepath, const fs::path& filepath,
llarp::SecretKey& key, llarp::SecretKey& key,
std::function<void(llarp::SecretKey& key)> keygen); std::function<void(llarp::SecretKey& key)> keygen);
/// Requests the identity key from lokid via HTTP (curl)
bool
loadIdentityFromLokid();
}; };
} // namespace llarp } // namespace llarp

@ -117,9 +117,6 @@ namespace llarp
llarp::LogError("cannot run non configured context"); llarp::LogError("cannot run non configured context");
return 1; return 1;
} }
// run
if (!router->StartJsonRpc())
return 1;
if (!opts.background) if (!opts.background)
{ {

@ -47,6 +47,11 @@ namespace llarp
struct Context; struct Context;
} }
namespace rpc
{
struct LokidRpcClient;
}
namespace path namespace path
{ {
struct PathContext; struct PathContext;
@ -83,6 +88,9 @@ namespace llarp
virtual LMQ_ptr virtual LMQ_ptr
lmq() const = 0; lmq() const = 0;
virtual std::shared_ptr<rpc::LokidRpcClient>
RpcClient() const = 0;
virtual std::shared_ptr<Logic> virtual std::shared_ptr<Logic>
logic() const = 0; logic() const = 0;
@ -153,7 +161,7 @@ namespace llarp
IsServiceNode() const = 0; IsServiceNode() const = 0;
virtual bool virtual bool
StartJsonRpc() = 0; StartRpcServer() = 0;
virtual bool virtual bool
Run() = 0; Run() = 0;

@ -64,7 +64,7 @@ namespace llarp
#else #else
, _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) , _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10))
#endif #endif
, m_lokidRpcClient(m_lmq, this) , m_lokidRpcClient(std::make_shared<rpc::LokidRpcClient>(m_lmq, this))
{ {
m_keyManager = std::make_shared<KeyManager>(); m_keyManager = std::make_shared<KeyManager>();
@ -203,6 +203,8 @@ namespace llarp
bool bool
Router::EnsureIdentity() Router::EnsureIdentity()
{ {
_encryption = m_keyManager->encryptionKey;
if (whitelistRouters) if (whitelistRouters)
{ {
#if defined(ANDROID) || defined(IOS) #if defined(ANDROID) || defined(IOS)
@ -214,10 +216,14 @@ namespace llarp
return false; return false;
#endif #endif
#endif #endif
const auto maybe = RpcClient()->ObtainIdentityKey();
if (maybe.has_value())
_identity = *maybe;
}
else
{
_identity = m_keyManager->identityKey;
} }
_identity = m_keyManager->identityKey;
_encryption = m_keyManager->encryptionKey;
if (_identity.IsZero()) if (_identity.IsZero())
return false; return false;
@ -236,6 +242,23 @@ namespace llarp
} }
_nodedb = nodedb; _nodedb = nodedb;
// we need this first so we can start lmq to fetch keys
enableRPCServer = conf->api.m_enableRPCServer;
rpcBindAddr = conf->api.m_rpcBindAddr;
whitelistRouters = conf->lokid.whitelistRouters;
lokidRPCAddr = conf->lokid.lokidRPCAddr;
if (not StartRpcServer())
throw std::runtime_error("Failed to start rpc server");
m_lmq->start();
if (whitelistRouters)
{
m_lokidRpcClient->ConnectAsync(std::string_view{lokidRPCAddr});
}
// fetch keys
if (not m_keyManager->initialize(*conf, true, isRouter)) if (not m_keyManager->initialize(*conf, true, isRouter))
throw std::runtime_error("KeyManager failed to initialize"); throw std::runtime_error("KeyManager failed to initialize");
@ -555,8 +578,6 @@ namespace llarp
} }
// API config // API config
enableRPCServer = conf->api.m_enableRPCServer;
rpcBindAddr = IpAddress(conf->api.m_rpcBindAddr); // TODO: make this an IpAddress in config
if (not IsServiceNode()) if (not IsServiceNode())
{ {
hiddenServiceContext().AddEndpoint(*conf); hiddenServiceContext().AddEndpoint(*conf);
@ -818,18 +839,19 @@ namespace llarp
} }
bool bool
Router::StartJsonRpc() Router::StartRpcServer()
{ {
if (_running || _stopping) if (_running || _stopping)
return false; return false;
if (enableRPCServer) if (enableRPCServer)
{ {
if (rpcBindAddr.isEmpty()) if (rpcBindAddr.empty())
{ {
rpcBindAddr = DefaultRPCBindAddr; rpcBindAddr = DefaultRPCBindAddr;
} }
LogInfo("Bound RPC server to ", rpcBindAddr); // TODO: set up rpc server
// LogInfo("Bound RPC server to ", rpcBindAddr);
} }
return true; return true;
@ -841,13 +863,6 @@ namespace llarp
if (_running || _stopping) if (_running || _stopping)
return false; return false;
if (whitelistRouters)
{
m_lokidRpcClient.ConnectAsync(std::string_view{lokidRPCAddr});
}
m_lmq->start();
if (!cryptoworker->start()) if (!cryptoworker->start())
{ {
LogError("crypto worker failed to start"); LogError("crypto worker failed to start");

@ -81,6 +81,12 @@ namespace llarp
return m_lmq; return m_lmq;
} }
std::shared_ptr<rpc::LokidRpcClient>
RpcClient() const override
{
return m_lokidRpcClient;
}
std::shared_ptr<Logic> std::shared_ptr<Logic>
logic() const override logic() const override
{ {
@ -250,12 +256,12 @@ namespace llarp
NetworkConfig networkConfig; NetworkConfig networkConfig;
DnsConfig dnsConfig; DnsConfig dnsConfig;
const IpAddress DefaultRPCBindAddr = IpAddress("127.0.0.1:1190"); const std::string DefaultRPCBindAddr = "127.0.0.1:1190";
bool enableRPCServer = false; bool enableRPCServer = false;
IpAddress rpcBindAddr = DefaultRPCBindAddr; std::string rpcBindAddr = DefaultRPCBindAddr;
const llarp_time_t _randomStartDelay; const llarp_time_t _randomStartDelay;
rpc::LokidRpcClient m_lokidRpcClient; std::shared_ptr<rpc::LokidRpcClient> m_lokidRpcClient;
std::string lokidRPCAddr = "ipc://loki.sock"; std::string lokidRPCAddr = "ipc://loki.sock";
std::string lokidRPCUser; std::string lokidRPCUser;
@ -337,7 +343,7 @@ namespace llarp
Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override; Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override;
bool bool
StartJsonRpc() override; StartRpcServer() override;
bool bool
Run() override; Run() override;

@ -61,52 +61,53 @@ namespace llarp
m_lokiMQ->send(*m_Connection, std::move(cmd)); m_lokiMQ->send(*m_Connection, std::move(cmd));
} }
void
LokidRpcClient::UpdateServiceNodeList()
{
nlohmann::json request;
request["pubkey_ed25519"] = true;
request["active_only"] = true;
if (not m_CurrentBlockHash.empty())
request["poll_block_hash"] = m_CurrentBlockHash;
Request(
"rpc.get_service_nodes",
[self = shared_from_this()](bool success, std::vector<std::string> data) {
if (not success)
{
LogWarn("failed to update service node list");
return;
}
if (data.size() < 2)
{
LogWarn("lokid gave empty reply for service node list");
return;
}
try
{
self->HandleGotServiceNodeList(std::move(data[1]));
}
catch (std::exception& ex)
{
LogError("failed to process service node list: ", ex.what());
}
},
request.dump());
}
void void
LokidRpcClient::Connected() LokidRpcClient::Connected()
{ {
constexpr auto PingInterval = 1min; constexpr auto PingInterval = 1min;
constexpr auto NodeListUpdateInterval = 90s; constexpr auto NodeListUpdateInterval = 30s;
LogInfo("we connected to lokid [", *m_Connection, "]"); LogInfo("we connected to lokid [", *m_Connection, "]");
Command("admin.lokinet_ping");
m_lokiMQ->add_timer( m_lokiMQ->add_timer(
[self = shared_from_this()]() { [self = shared_from_this()]() { self->Command("admin.lokinet_ping"); }, PingInterval);
LogInfo("Telling lokid we are live");
self->Command("rpc.lokinet_ping");
},
PingInterval);
UpdateServiceNodeList();
m_lokiMQ->add_timer( m_lokiMQ->add_timer(
[self = shared_from_this()]() { [self = shared_from_this()]() { self->UpdateServiceNodeList(); }, NodeListUpdateInterval);
nlohmann::json request;
request["pubkey_ed25519"] = true;
request["active_only"] = true;
if (not self->m_CurrentBlockHash.empty())
request["poll_block_hash"] = self->m_CurrentBlockHash;
self->Request(
"rpc.get_service_nodes",
[self](bool success, std::vector<std::string> data) {
if (not success)
{
LogWarn("failed to update service node list");
return;
}
if (data.size() < 2)
{
LogWarn("lokid gave empty reply for service node list");
return;
}
try
{
self->HandleGotServiceNodeList(std::move(data[1]));
}
catch (std::exception& ex)
{
LogError("failed to process service node list: ", ex.what());
}
},
request.dump());
},
NodeListUpdateInterval);
} }
void void
@ -154,10 +155,49 @@ namespace llarp
LogWarn("got empty service node list from lokid"); LogWarn("got empty service node list from lokid");
return; return;
} }
// inform router about the new list // inform router about the new list
LogicCall(m_Router->logic(), [r = m_Router, nodeList]() { r->SetRouterWhitelist(nodeList); }); LogicCall(m_Router->logic(), [r = m_Router, nodeList]() { r->SetRouterWhitelist(nodeList); });
} }
std::optional<SecretKey>
LokidRpcClient::ObtainIdentityKey()
{
std::promise<std::optional<SecretKey>> promise;
Request(
"admin.get_service_privkeys",
[self = shared_from_this(), &promise](bool success, std::vector<std::string> data) {
if (not success)
{
LogError("failed to get private key");
promise.set_value(std::nullopt);
return;
}
if (data.size() < 2)
{
LogError("failed to get private key, no response");
promise.set_value(std::nullopt);
return;
}
try
{
auto j = nlohmann::json::parse(data[1]);
SecretKey k;
if (not k.FromHex(j.at("service_node_ed25519_privkey").get<std::string>()))
{
promise.set_value(std::nullopt);
return;
}
promise.set_value(k);
}
catch (std::exception& ex)
{
LogError("failed to get private key: ", ex.what());
promise.set_value(std::nullopt);
}
});
return promise.get_future().get();
}
} // namespace rpc } // namespace rpc
} // namespace llarp } // namespace llarp

@ -3,6 +3,7 @@
#include <router_id.hpp> #include <router_id.hpp>
#include <lokimq/lokimq.h> #include <lokimq/lokimq.h>
#include <crypto/types.hpp>
namespace llarp namespace llarp
{ {
@ -20,6 +21,10 @@ namespace llarp
void void
ConnectAsync(std::string_view url); ConnectAsync(std::string_view url);
/// blocking request identity key from lokid
std::optional<SecretKey>
ObtainIdentityKey();
private: private:
/// called when we have connected to lokid via lokimq /// called when we have connected to lokid via lokimq
void void
@ -29,6 +34,9 @@ namespace llarp
void void
Command(std::string_view cmd); Command(std::string_view cmd);
void
UpdateServiceNodeList();
template <typename HandlerFunc_t, typename Args_t> template <typename HandlerFunc_t, typename Args_t>
void void
Request(std::string_view cmd, HandlerFunc_t func, const Args_t& args) Request(std::string_view cmd, HandlerFunc_t func, const Args_t& args)
@ -36,6 +44,13 @@ namespace llarp
m_lokiMQ->request(*m_Connection, std::move(cmd), std::move(func), args); m_lokiMQ->request(*m_Connection, std::move(cmd), std::move(func), args);
} }
template <typename HandlerFunc_t>
void
Request(std::string_view cmd, HandlerFunc_t func)
{
m_lokiMQ->request(*m_Connection, std::move(cmd), std::move(func));
}
void void
HandleGotServiceNodeList(std::string json); HandleGotServiceNodeList(std::string json);

@ -388,7 +388,8 @@ namespace llarp
// we need to dh // we need to dh
auto dh = new AsyncFrameDecrypt(logic, localIdent, handler, msg, *this, recvPath->intro); auto dh = new AsyncFrameDecrypt(logic, localIdent, handler, msg, *this, recvPath->intro);
dh->path = recvPath; dh->path = recvPath;
return worker->addJob(std::bind(&AsyncFrameDecrypt::Work, dh)); worker->addJob(std::bind(&AsyncFrameDecrypt::Work, dh));
return true;
} }
auto v = new AsyncDecrypt(); auto v = new AsyncDecrypt();
@ -407,7 +408,7 @@ namespace llarp
return false; return false;
} }
v->frame = *this; v->frame = *this;
return worker->addJob([v, msg = std::move(msg), recvPath = std::move(recvPath)]() { worker->addJob([v, msg = std::move(msg), recvPath = std::move(recvPath)]() {
if (not v->frame.Verify(v->si)) if (not v->frame.Verify(v->si))
{ {
LogError("Signature failure from ", v->si.Addr()); LogError("Signature failure from ", v->si.Addr());
@ -427,6 +428,7 @@ namespace llarp
msg->handler->QueueRecvData(std::move(ev)); msg->handler->QueueRecvData(std::move(ev));
delete v; delete v;
}); });
return true;
} }
bool bool

Loading…
Cancel
Save