Store RPC SSL key/cert for consistent authentication between runs
This commit is contained in:
parent
cb70ae9450
commit
9867a913dc
|
@ -265,6 +265,12 @@ namespace net_utils
|
||||||
template<class t_callback>
|
template<class t_callback>
|
||||||
bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
|
bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
|
||||||
|
|
||||||
|
boost::asio::ssl::context& get_ssl_context() noexcept
|
||||||
|
{
|
||||||
|
assert(m_state != nullptr);
|
||||||
|
return m_state->ssl_context;
|
||||||
|
}
|
||||||
|
|
||||||
typename t_protocol_handler::config_type& get_config_object()
|
typename t_protocol_handler::config_type& get_config_object()
|
||||||
{
|
{
|
||||||
assert(m_state != nullptr); // always set in constructor
|
assert(m_state != nullptr); // always set in constructor
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <boost/utility/string_ref.hpp>
|
#include <boost/utility/string_ref.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
#define SSL_FINGERPRINT_SIZE 32
|
#define SSL_FINGERPRINT_SIZE 32
|
||||||
|
@ -144,6 +145,9 @@ namespace net_utils
|
||||||
|
|
||||||
bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
|
bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
|
||||||
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
|
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
|
||||||
|
|
||||||
|
//! Store private key for `ssl` at `base + ".key"` unencrypted and certificate for `ssl` at `base + ".crt"`.
|
||||||
|
boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/cerrno.hpp>
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/lambda/lambda.hpp>
|
#include <boost/lambda/lambda.hpp>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
|
@ -567,6 +569,51 @@ bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base)
|
||||||
|
{
|
||||||
|
EVP_PKEY* ssl_key = nullptr;
|
||||||
|
X509* ssl_cert = nullptr;
|
||||||
|
const auto ctx = ssl.native_handle();
|
||||||
|
CHECK_AND_ASSERT_MES(ctx, boost::system::error_code(EINVAL, boost::system::system_category()), "Context is null");
|
||||||
|
CHECK_AND_ASSERT_MES(base.has_filename(), boost::system::error_code(EINVAL, boost::system::system_category()), "Need filename");
|
||||||
|
if (!(ssl_key = SSL_CTX_get0_privatekey(ctx)) || !(ssl_cert = SSL_CTX_get0_certificate(ctx)))
|
||||||
|
return {EINVAL, boost::system::system_category()};
|
||||||
|
|
||||||
|
using file_closer = int(std::FILE*);
|
||||||
|
boost::system::error_code error{};
|
||||||
|
std::unique_ptr<std::FILE, file_closer*> file{nullptr, std::fclose};
|
||||||
|
|
||||||
|
// write key file unencrypted
|
||||||
|
{
|
||||||
|
const boost::filesystem::path key_file{base.string() + ".key"};
|
||||||
|
file.reset(std::fopen(key_file.string().c_str(), "wb"));
|
||||||
|
if (!file)
|
||||||
|
return {errno, boost::system::system_category()};
|
||||||
|
boost::filesystem::permissions(key_file, boost::filesystem::owner_read, error);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
if (!PEM_write_PrivateKey(file.get(), ssl_key, nullptr, nullptr, 0, nullptr, nullptr))
|
||||||
|
return boost::asio::error::ssl_errors(ERR_get_error());
|
||||||
|
if (std::fclose(file.release()) != 0)
|
||||||
|
return {errno, boost::system::system_category()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// write certificate file in standard SSL X.509 unencrypted
|
||||||
|
const boost::filesystem::path cert_file{base.string() + ".crt"};
|
||||||
|
file.reset(std::fopen(cert_file.string().c_str(), "wb"));
|
||||||
|
if (!file)
|
||||||
|
return {errno, boost::system::system_category()};
|
||||||
|
const auto cert_perms = (boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read);
|
||||||
|
boost::filesystem::permissions(cert_file, cert_perms, error);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
if (!PEM_write_X509(file.get(), ssl_cert))
|
||||||
|
return boost::asio::error::ssl_errors(ERR_get_error());
|
||||||
|
if (std::fclose(file.release()) != 0)
|
||||||
|
return {errno, boost::system::system_category()};
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,7 @@ namespace cryptonote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
disable_rpc_ban = rpc_config->disable_rpc_ban;
|
disable_rpc_ban = rpc_config->disable_rpc_ban;
|
||||||
|
const std::string data_dir{command_line::get_arg(vm, cryptonote::arg_data_dir)};
|
||||||
std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
|
std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
|
||||||
if (!address.empty() && allow_rpc_payment)
|
if (!address.empty() && allow_rpc_payment)
|
||||||
{
|
{
|
||||||
|
@ -306,7 +307,7 @@ namespace cryptonote
|
||||||
}
|
}
|
||||||
m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
|
m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
|
||||||
m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
|
m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
|
||||||
m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir));
|
m_rpc_payment->load(data_dir);
|
||||||
m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
|
m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,12 +334,32 @@ namespace cryptonote
|
||||||
if (m_rpc_payment)
|
if (m_rpc_payment)
|
||||||
m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000);
|
m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000);
|
||||||
|
|
||||||
|
bool store_ssl_key = !restricted && rpc_config->ssl_options.auth.certificate_path.empty();
|
||||||
|
const auto ssl_base_path = (boost::filesystem::path{data_dir} / "rpc_ssl").string();
|
||||||
|
if (store_ssl_key && boost::filesystem::exists(ssl_base_path + ".crt"))
|
||||||
|
{
|
||||||
|
// load key from previous run, password prompted by OpenSSL
|
||||||
|
store_ssl_key = false;
|
||||||
|
rpc_config->ssl_options.auth =
|
||||||
|
epee::net_utils::ssl_authentication_t{ssl_base_path + ".key", ssl_base_path + ".crt"};
|
||||||
|
}
|
||||||
|
|
||||||
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
|
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
|
||||||
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
|
const bool inited = epee::http_server_impl_base<core_rpc_server, connection_context>::init(
|
||||||
rng, std::move(port), std::move(bind_ip_str),
|
rng, std::move(port), std::move(bind_ip_str),
|
||||||
std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
|
||||||
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
|
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (store_ssl_key && inited)
|
||||||
|
{
|
||||||
|
// new keys were generated, store for next run
|
||||||
|
const auto error = epee::net_utils::store_ssl_keys(m_net_server.get_ssl_context(), ssl_base_path);
|
||||||
|
if (error)
|
||||||
|
MFATAL("Failed to store HTTP SSL cert/key for " << (restricted ? "restricted " : "") << "RPC server: " << error.message());
|
||||||
|
return !bool(error);
|
||||||
|
}
|
||||||
|
return inited;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash)
|
bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash)
|
||||||
|
|
Loading…
Reference in New Issue