From e530c37b0efd3c53ce92a5e2a9ef3a7c2bc5ac0e Mon Sep 17 00:00:00 2001 From: Simon Mo Date: Tue, 17 Dec 2019 19:41:19 -0800 Subject: [PATCH] Use localhost and set redis password by default (#6481) --- doc/source/configure.rst | 7 +++++++ python/ray/cluster_utils.py | 4 +++- python/ray/parameter.py | 2 +- python/ray/ray_constants.py | 7 +++++++ python/ray/scripts/scripts.py | 1 + python/ray/tests/test_failure.py | 10 ++++++++-- python/ray/tests/test_multi_node_2.py | 3 ++- python/ray/worker.py | 4 ++-- 8 files changed, 31 insertions(+), 7 deletions(-) diff --git a/doc/source/configure.rst b/doc/source/configure.rst index 2c8bac625..1363ce66b 100644 --- a/doc/source/configure.rst +++ b/doc/source/configure.rst @@ -138,6 +138,13 @@ While Redis port authentication may protect against external attackers, Ray does not encrypt traffic between nodes so man-in-the-middle attacks are possible for clusters on untrusted networks. +One of most common attack with Redis is port-scanning attack. Attacker scans +open port with unprotected redis instance and execute arbitrary code. Ray +enables a default password for redis. Even though this does not prevent brute +force password cracking, the default password should alleviate most of the +port-scanning attack. Furtheremore, redis and other ray services are bind +to localhost when the ray is started using ``ray.init``. + See the `Redis security documentation `__ for more information. diff --git a/python/ray/cluster_utils.py b/python/ray/cluster_utils.py index 27f7f8ba1..2a7cc0580 100644 --- a/python/ray/cluster_utils.py +++ b/python/ray/cluster_utils.py @@ -8,6 +8,7 @@ import time import redis import ray +from ray import ray_constants logger = logging.getLogger(__name__) @@ -91,7 +92,8 @@ class Cluster(object): spawn_reaper=self._shutdown_at_exit) self.head_node = node self.redis_address = self.head_node.redis_address - self.redis_password = node_args.get("redis_password") + self.redis_password = node_args.get( + "redis_password", ray_constants.REDIS_DEFAULT_PASSWORD) self.webui_url = self.head_node.webui_url else: ray_params.update_if_absent(redis_address=self.redis_address) diff --git a/python/ray/parameter.py b/python/ray/parameter.py index af8785886..4c710dd9c 100644 --- a/python/ray/parameter.py +++ b/python/ray/parameter.py @@ -106,7 +106,7 @@ class RayParams(object): redirect_output=None, num_redis_shards=None, redis_max_clients=None, - redis_password=None, + redis_password=ray_constants.REDIS_DEFAULT_PASSWORD, plasma_directory=None, worker_path=None, huge_pages=False, diff --git a/python/ray/ray_constants.py b/python/ray/ray_constants.py index adb8ea6be..f805aa8d7 100644 --- a/python/ray/ray_constants.py +++ b/python/ray/ray_constants.py @@ -195,3 +195,10 @@ PICKLE_BUFFER_METADATA = b"PICKLE" PICKLE5_BUFFER_METADATA = b"PICKLE5" AUTOSCALER_RESOURCE_REQUEST_CHANNEL = b"autoscaler_resource_request" + +# The default password to prevent redis port scanning attack. +# Hex for ray. +REDIS_DEFAULT_PASSWORD = "5241590000000000" + +# The default ip address to bind to. +NODE_DEFAULT_IP = "127.0.0.1" diff --git a/python/ray/scripts/scripts.py b/python/ray/scripts/scripts.py index 62bfb52fd..e2417cdc8 100644 --- a/python/ray/scripts/scripts.py +++ b/python/ray/scripts/scripts.py @@ -98,6 +98,7 @@ def cli(logging_level, logging_format): "--redis-password", required=False, type=str, + default=ray_constants.REDIS_DEFAULT_PASSWORD, help="If provided, secure Redis ports with this password") @click.option( "--redis-shard-ports", diff --git a/python/ray/tests/test_failure.py b/python/ray/tests/test_failure.py index 5120c4af4..5b7b39037 100644 --- a/python/ray/tests/test_failure.py +++ b/python/ray/tests/test_failure.py @@ -729,11 +729,17 @@ def test_redis_module_failure(ray_start_regular): def run_failure_test(expecting_message, *command): with pytest.raises( Exception, match=".*{}.*".format(expecting_message)): - client = redis.StrictRedis(host=address[0], port=int(address[1])) + client = redis.StrictRedis( + host=address[0], + port=int(address[1]), + password=ray_constants.REDIS_DEFAULT_PASSWORD) client.execute_command(*command) def run_one_command(*command): - client = redis.StrictRedis(host=address[0], port=int(address[1])) + client = redis.StrictRedis( + host=address[0], + port=int(address[1]), + password=ray_constants.REDIS_DEFAULT_PASSWORD) client.execute_command(*command) run_failure_test("wrong number of arguments", "RAY.TABLE_ADD", 13) diff --git a/python/ray/tests/test_multi_node_2.py b/python/ray/tests/test_multi_node_2.py index b2db5e78e..4608e490b 100644 --- a/python/ray/tests/test_multi_node_2.py +++ b/python/ray/tests/test_multi_node_2.py @@ -60,7 +60,8 @@ def test_internal_config(ray_start_cluster_head): def setup_monitor(address): - monitor = Monitor(address, None) + monitor = Monitor( + address, None, redis_password=ray_constants.REDIS_DEFAULT_PASSWORD) monitor.subscribe(ray.gcs_utils.XRAY_HEARTBEAT_BATCH_CHANNEL) monitor.subscribe(ray.gcs_utils.XRAY_JOB_CHANNEL) # TODO: Remove? monitor.update_raylet_map(_append_port=True) diff --git a/python/ray/worker.py b/python/ray/worker.py index 87859afe9..044858967 100644 --- a/python/ray/worker.py +++ b/python/ray/worker.py @@ -534,7 +534,7 @@ def init(address=None, driver_object_store_memory=None, redis_max_memory=None, log_to_driver=True, - node_ip_address=None, + node_ip_address=ray_constants.NODE_DEFAULT_IP, object_id_seed=None, local_mode=False, redirect_worker_output=None, @@ -542,7 +542,7 @@ def init(address=None, ignore_reinit_error=False, num_redis_shards=None, redis_max_clients=None, - redis_password=None, + redis_password=ray_constants.REDIS_DEFAULT_PASSWORD, plasma_directory=None, huge_pages=False, include_webui=False,