Add password authentication to Redis ports (#2952)

* Implement Redis authentication

* Throw exception for legacy Ray

* Add test

* Formatting

* Fix bugs in CLI

* Fix bugs in Raylet

* Move default password to constants.h

* Use pytest.fixture

* Fix bug

* Authenticate using formatted strings

* Add missing passwords

* Add test

* Improve authentication of async contexts

* Disable Redis authentication for credis

* Update test for credis

* Fix rebase artifacts

* Fix formatting

* Add workaround for issue #3045

* Increase timeout for test

* Improve C++ readability

* Fixes for CLI

* Add security docs

* Address comments

* Address comments

* Adress comments

* Use ray.get

* Fix lint
This commit is contained in:
Peter Schafhalter
2018-10-16 22:48:30 -07:00
committed by Philipp Moritz
parent a9e454f6fd
commit a41bbc10ef
22 changed files with 462 additions and 115 deletions
+19 -11
View File
@@ -71,10 +71,12 @@ namespace gcs {
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
const ClientID &client_id, CommandType command_type,
bool is_test_client = false) {
bool is_test_client = false,
const std::string &password = "") {
primary_context_ = std::make_shared<RedisContext>();
RAY_CHECK_OK(primary_context_->Connect(address, port, /*sharding=*/true));
RAY_CHECK_OK(
primary_context_->Connect(address, port, /*sharding=*/true, /*password=*/password));
if (!is_test_client) {
// Moving sharding into constructor defaultly means that sharding = true.
@@ -94,12 +96,13 @@ AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
RAY_CHECK(shard_contexts_.size() == addresses.size());
for (size_t i = 0; i < addresses.size(); ++i) {
RAY_CHECK_OK(
shard_contexts_[i]->Connect(addresses[i], ports[i], /*sharding=*/true));
RAY_CHECK_OK(shard_contexts_[i]->Connect(addresses[i], ports[i], /*sharding=*/true,
/*password=*/password));
}
} else {
shard_contexts_.push_back(std::make_shared<RedisContext>());
RAY_CHECK_OK(shard_contexts_[0]->Connect(address, port, /*sharding=*/true));
RAY_CHECK_OK(shard_contexts_[0]->Connect(address, port, /*sharding=*/true,
/*password=*/password));
}
client_table_.reset(new ClientTable({primary_context_}, this, client_id));
@@ -126,12 +129,16 @@ AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
// Use of kChain currently only applies to Table::Add which affects only the
// task table, and when RAY_USE_NEW_GCS is set at compile time.
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
const ClientID &client_id, bool is_test_client = false)
: AsyncGcsClient(address, port, client_id, CommandType::kChain, is_test_client) {}
const ClientID &client_id, bool is_test_client = false,
const std::string &password = "")
: AsyncGcsClient(address, port, client_id, CommandType::kChain, is_test_client,
password) {}
#else
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
const ClientID &client_id, bool is_test_client = false)
: AsyncGcsClient(address, port, client_id, CommandType::kRegular, is_test_client) {}
const ClientID &client_id, bool is_test_client = false,
const std::string &password = "")
: AsyncGcsClient(address, port, client_id, CommandType::kRegular, is_test_client,
password) {}
#endif // RAY_USE_NEW_GCS
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
@@ -143,8 +150,9 @@ AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
: AsyncGcsClient(address, port, ClientID::from_random(), command_type,
is_test_client) {}
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port)
: AsyncGcsClient(address, port, ClientID::from_random()) {}
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port,
const std::string &password = "")
: AsyncGcsClient(address, port, ClientID::from_random(), false, password) {}
AsyncGcsClient::AsyncGcsClient(const std::string &address, int port, bool is_test_client)
: AsyncGcsClient(address, port, ClientID::from_random(), is_test_client) {}
+4 -3
View File
@@ -31,13 +31,14 @@ class RAY_EXPORT AsyncGcsClient {
/// \param command_type GCS command type. If CommandType::kChain, chain-replicated
/// versions of the tables might be used, if available.
AsyncGcsClient(const std::string &address, int port, const ClientID &client_id,
CommandType command_type, bool is_test_client);
CommandType command_type, bool is_test_client,
const std::string &redis_password);
AsyncGcsClient(const std::string &address, int port, const ClientID &client_id,
bool is_test_client);
bool is_test_client, const std::string &password);
AsyncGcsClient(const std::string &address, int port, CommandType command_type);
AsyncGcsClient(const std::string &address, int port, CommandType command_type,
bool is_test_client);
AsyncGcsClient(const std::string &address, int port);
AsyncGcsClient(const std::string &address, int port, const std::string &password);
AsyncGcsClient(const std::string &address, int port, bool is_test_client);
/// Attach this client to a plasma event loop. Note that only
+30 -1
View File
@@ -135,7 +135,30 @@ RedisContext::~RedisContext() {
}
}
Status RedisContext::Connect(const std::string &address, int port, bool sharding) {
Status AuthenticateRedis(redisContext *context, const std::string &password) {
if (password == "") {
return Status::OK();
}
redisReply *reply =
reinterpret_cast<redisReply *>(redisCommand(context, "AUTH %s", password.c_str()));
REDIS_CHECK_ERROR(context, reply);
freeReplyObject(reply);
return Status::OK();
}
Status AuthenticateRedis(redisAsyncContext *context, const std::string &password) {
if (password == "") {
return Status::OK();
}
int status = redisAsyncCommand(context, NULL, NULL, "AUTH %s", password.c_str());
if (status == REDIS_ERR) {
return Status::RedisError(std::string(context->errstr));
}
return Status::OK();
}
Status RedisContext::Connect(const std::string &address, int port, bool sharding,
const std::string &password = "") {
int connection_attempts = 0;
context_ = redisConnect(address.c_str(), port);
while (context_ == nullptr || context_->err) {
@@ -155,6 +178,8 @@ Status RedisContext::Connect(const std::string &address, int port, bool sharding
context_ = redisConnect(address.c_str(), port);
connection_attempts += 1;
}
RAY_CHECK_OK(AuthenticateRedis(context_, password));
redisReply *reply = reinterpret_cast<redisReply *>(
redisCommand(context_, "CONFIG SET notify-keyspace-events Kl"));
REDIS_CHECK_ERROR(context_, reply);
@@ -166,12 +191,16 @@ Status RedisContext::Connect(const std::string &address, int port, bool sharding
RAY_LOG(FATAL) << "Could not establish connection to redis " << address << ":"
<< port;
}
RAY_CHECK_OK(AuthenticateRedis(async_context_, password));
// Connect to subscribe context
subscribe_context_ = redisAsyncConnect(address.c_str(), port);
if (subscribe_context_ == nullptr || subscribe_context_->err) {
RAY_LOG(FATAL) << "Could not establish subscribe connection to redis " << address
<< ":" << port;
}
RAY_CHECK_OK(AuthenticateRedis(subscribe_context_, password));
return Status::OK();
}
+2 -1
View File
@@ -51,7 +51,8 @@ class RedisContext {
RedisContext()
: context_(nullptr), async_context_(nullptr), subscribe_context_(nullptr) {}
~RedisContext();
Status Connect(const std::string &address, int port, bool sharding);
Status Connect(const std::string &address, int port, bool sharding,
const std::string &password);
Status AttachToEventLoop(aeEventLoop *loop);
/// Run an operation on some table key.
+6 -4
View File
@@ -19,7 +19,7 @@ int main(int argc, char *argv[]) {
ray::RayLog::ShutDownRayLog, argv[0], RAY_INFO,
/*log_dir=*/"");
ray::RayLog::InstallFailureSignalHandler();
RAY_CHECK(argc == 11);
RAY_CHECK(argc == 11 || argc == 12);
const std::string raylet_socket_name = std::string(argv[1]);
const std::string store_socket_name = std::string(argv[2]);
@@ -31,6 +31,7 @@ int main(int argc, char *argv[]) {
const std::string static_resource_list = std::string(argv[8]);
const std::string python_worker_command = std::string(argv[9]);
const std::string java_worker_command = std::string(argv[10]);
const std::string redis_password = (argc == 12 ? std::string(argv[11]) : "");
// Configuration for the node manager.
ray::raylet::NodeManagerConfig node_manager_config;
@@ -92,7 +93,8 @@ int main(int argc, char *argv[]) {
<< "object_chunk_size = " << object_manager_config.object_chunk_size;
// initialize mock gcs & object directory
auto gcs_client = std::make_shared<ray::gcs::AsyncGcsClient>(redis_address, redis_port);
auto gcs_client = std::make_shared<ray::gcs::AsyncGcsClient>(redis_address, redis_port,
redis_password);
RAY_LOG(DEBUG) << "Initializing GCS client "
<< gcs_client->client_table().GetLocalClientId();
@@ -100,8 +102,8 @@ int main(int argc, char *argv[]) {
boost::asio::io_service main_service;
ray::raylet::Raylet server(main_service, raylet_socket_name, node_ip_address,
redis_address, redis_port, node_manager_config,
object_manager_config, gcs_client);
redis_address, redis_port, redis_password,
node_manager_config, object_manager_config, gcs_client);
// Destroy the Raylet on a SIGTERM. The pointer to main_service is
// guaranteed to be valid since this function will run the event loop
+2 -2
View File
@@ -15,8 +15,8 @@ namespace raylet {
/// the Ray configuration), then the monitor will mark that Raylet as dead in
/// the client table, which broadcasts the event to all other Raylets.
Monitor::Monitor(boost::asio::io_service &io_service, const std::string &redis_address,
int redis_port)
: gcs_client_(redis_address, redis_port),
int redis_port, const std::string &redis_password)
: gcs_client_(redis_address, redis_port, redis_password),
num_heartbeats_timeout_(RayConfig::instance().num_heartbeats_timeout()),
heartbeat_timer_(io_service) {
RAY_CHECK_OK(gcs_client_.Attach(io_service));
+1 -1
View File
@@ -19,7 +19,7 @@ class Monitor {
/// \param redis_address The GCS Redis address to connect to.
/// \param redis_port The GCS Redis port to connect to.
Monitor(boost::asio::io_service &io_service, const std::string &redis_address,
int redis_port);
int redis_port, const std::string &redis_password);
/// Start the monitor. Listen for heartbeats from Raylets and mark Raylets
/// that do not send a heartbeat within a given period as dead.
+3 -2
View File
@@ -8,14 +8,15 @@ int main(int argc, char *argv[]) {
ray::RayLog::ShutDownRayLog, argv[0], RAY_INFO,
/*log_dir=*/"");
ray::RayLog::InstallFailureSignalHandler();
RAY_CHECK(argc == 3);
RAY_CHECK(argc == 3 || argc == 4);
const std::string redis_address = std::string(argv[1]);
int redis_port = std::stoi(argv[2]);
const std::string redis_password = (argc == 4 ? std::string(argv[3]) : "");
// Initialize the monitor.
boost::asio::io_service io_service;
ray::raylet::Monitor monitor(io_service, redis_address, redis_port);
ray::raylet::Monitor monitor(io_service, redis_address, redis_port, redis_password);
monitor.Start();
io_service.run();
}
@@ -60,7 +60,7 @@ class TestObjectManagerBase : public ::testing::Test {
om_config_1.store_socket_name = store_sock_1;
om_config_1.push_timeout_ms = 10000;
server1.reset(new ray::raylet::Raylet(
main_service, "raylet_1", "0.0.0.0", "127.0.0.1", 6379,
main_service, "raylet_1", "0.0.0.0", "127.0.0.1", 6379, "",
GetNodeManagerConfig("raylet_1", store_sock_1), om_config_1, gcs_client_1));
// start second server
@@ -70,7 +70,7 @@ class TestObjectManagerBase : public ::testing::Test {
om_config_2.store_socket_name = store_sock_2;
om_config_2.push_timeout_ms = 10000;
server2.reset(new ray::raylet::Raylet(
main_service, "raylet_2", "0.0.0.0", "127.0.0.1", 6379,
main_service, "raylet_2", "0.0.0.0", "127.0.0.1", 6379, "",
GetNodeManagerConfig("raylet_2", store_sock_2), om_config_2, gcs_client_2));
// connect to stores.
+6 -4
View File
@@ -13,7 +13,8 @@ namespace raylet {
Raylet::Raylet(boost::asio::io_service &main_service, const std::string &socket_name,
const std::string &node_ip_address, const std::string &redis_address,
int redis_port, const NodeManagerConfig &node_manager_config,
int redis_port, const std::string &redis_password,
const NodeManagerConfig &node_manager_config,
const ObjectManagerConfig &object_manager_config,
std::shared_ptr<gcs::AsyncGcsClient> gcs_client)
: gcs_client_(gcs_client),
@@ -33,9 +34,9 @@ Raylet::Raylet(boost::asio::io_service &main_service, const std::string &socket_
DoAcceptObjectManager();
DoAcceptNodeManager();
RAY_CHECK_OK(RegisterGcs(node_ip_address, socket_name_,
object_manager_config.store_socket_name, redis_address,
redis_port, main_service, node_manager_config));
RAY_CHECK_OK(RegisterGcs(
node_ip_address, socket_name_, object_manager_config.store_socket_name,
redis_address, redis_port, redis_password, main_service, node_manager_config));
RAY_CHECK_OK(RegisterPeriodicTimer(main_service));
}
@@ -52,6 +53,7 @@ ray::Status Raylet::RegisterGcs(const std::string &node_ip_address,
const std::string &raylet_socket_name,
const std::string &object_store_socket_name,
const std::string &redis_address, int redis_port,
const std::string &redis_password,
boost::asio::io_service &io_service,
const NodeManagerConfig &node_manager_config) {
RAY_RETURN_NOT_OK(gcs_client_->Attach(io_service));
+4 -1
View File
@@ -29,6 +29,7 @@ class Raylet {
/// \param node_ip_address The IP address of this node.
/// \param redis_address The IP address of the redis instance we are connecting to.
/// \param redis_port The port of the redis instance we are connecting to.
/// \param redis_password The password of the redis instance we are connecting to.
/// \param node_manager_config Configuration to initialize the node manager.
/// scheduler with.
/// \param object_manager_config Configuration to initialize the object
@@ -36,7 +37,8 @@ class Raylet {
/// \param gcs_client A client connection to the GCS.
Raylet(boost::asio::io_service &main_service, const std::string &socket_name,
const std::string &node_ip_address, const std::string &redis_address,
int redis_port, const NodeManagerConfig &node_manager_config,
int redis_port, const std::string &redis_password,
const NodeManagerConfig &node_manager_config,
const ObjectManagerConfig &object_manager_config,
std::shared_ptr<gcs::AsyncGcsClient> gcs_client);
@@ -49,6 +51,7 @@ class Raylet {
const std::string &raylet_socket_name,
const std::string &object_store_socket_name,
const std::string &redis_address, int redis_port,
const std::string &redis_password,
boost::asio::io_service &io_service, const NodeManagerConfig &);
ray::Status RegisterPeriodicTimer(boost::asio::io_service &io_service);