Node ID persistence (#3816)

* Node ID persistence

After this change, node IDs will persist on the disk file system and
reused. The node ID will not be changing after every restart.
This will help to more reliably track PRs.

The node can be forced to generate a new ID by deleting the file:
node_id_private.key

Note that this change does not prevent the man in the middle attack.

However, it makes it possible to detect telemetry spoofing. Sites like
nanolooker and nanoticker can maintain associations between node ID and PR
and only accept a telemetry packet, if the telemetry message is signed
by the known Node ID.

Co-authored-by: clemahieu <clemahieu@gmail.com>
This commit is contained in:
Dimitrios Siganos 2022-05-12 22:01:32 +01:00 committed by GitHub
commit 53154c0cf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 1 deletions

View file

@ -11,6 +11,7 @@
#include <boost/make_shared.hpp>
#include <boost/variant.hpp>
#include <fstream>
#include <numeric>
using namespace std::chrono_literals;
@ -4472,3 +4473,30 @@ TEST (node, pruning_depth)
ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send1->hash ()));
ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send2->hash ()));
}
TEST (node_config, node_id_private_key_persistence)
{
nano::logger_mt logger;
// create the directory and the file
auto path = nano::unique_path ();
ASSERT_TRUE (boost::filesystem::create_directories (path));
auto priv_key_filename = path / "node_id_private.key";
// check that the key generated is random when the key does not exist
nano::keypair kp1 = nano::load_or_create_node_id (path, logger);
boost::filesystem::remove (priv_key_filename);
nano::keypair kp2 = nano::load_or_create_node_id (path, logger);
ASSERT_NE (kp1.prv, kp2.prv);
// check that the key persists
nano::keypair kp3 = nano::load_or_create_node_id (path, logger);
ASSERT_EQ (kp2.prv, kp3.prv);
// write the key file manually and check that right key is loaded
std::ofstream ofs (priv_key_filename.string (), std::ofstream::out | std::ofstream::trunc);
ofs << "3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9" << std::flush;
ofs.close ();
nano::keypair kp4 = nano::load_or_create_node_id (path, logger);
ASSERT_EQ (kp4.prv, nano::keypair ("3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9").prv);
}

View file

@ -16,6 +16,7 @@
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <future>
#include <sstream>
@ -74,6 +75,33 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (re
return composite;
}
nano::keypair nano::load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger)
{
auto node_private_key_path = application_path / "node_id_private.key";
std::ifstream ifs (node_private_key_path.c_str ());
if (ifs.good ())
{
logger.always_log (boost::str (boost::format ("%1% exists, reading node id from it") % node_private_key_path.string ()));
std::string node_private_key;
ifs >> node_private_key;
release_assert (node_private_key.size () == 64);
nano::keypair kp = nano::keypair (node_private_key);
return kp;
}
else
{
// no node_id found, generate new one
logger.always_log (boost::str (boost::format ("%1% does not exist, creating a new node_id") % node_private_key_path.string ()));
nano::keypair kp;
std::ofstream ofs (node_private_key_path.c_str (), std::ofstream::out | std::ofstream::trunc);
ofs << kp.prv.to_string () << std::endl
<< std::flush;
ofs.close ();
release_assert (!ofs.fail ());
return kp;
}
}
nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, boost::filesystem::path const & application_path_a, nano::logging const & logging_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
node (io_ctx_a, application_path_a, nano::node_config (peering_port_a, logging_a), work_a, flags_a, seq)
{
@ -395,7 +423,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, boost::filesystem::path co
logger.always_log (stream.str ());
}
node_id = nano::keypair ();
node_id = nano::load_or_create_node_id (application_path, logger);
logger.always_log ("Node ID: ", node_id.pub.to_node_id ());
if ((network_params.network.is_live_network () || network_params.network.is_beta_network ()) && !flags.inactive_node)

View file

@ -219,6 +219,7 @@ private:
nano::locked<std::future<void>> epoch_upgrading;
};
nano::keypair load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger);
std::unique_ptr<container_info_component> collect_container_info (node & node, std::string const & name);
nano::node_flags const & inactive_node_flag_defaults ();