From 273963331bd303f595e820ca6da17cd63f5514db Mon Sep 17 00:00:00 2001 From: Damien Ciabrini Date: Sat, 2 Dec 2017 11:53:56 +0100 Subject: [PATCH] redis: add support for tunneling replication traffic Add parameters in the resource agent to assign specific redis port to each pacemaker node. When redis slave wants to connect to a redis master, it will instead connect to a tunnel host, on the port assigned to the targeted redis master. This makes it possible for redis replication traffic to go through pre-existing tunnels. This can be used to encrypt such traffic. --- heartbeat/redis | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/heartbeat/redis b/heartbeat/redis index fcd8c234..d9e29e2c 100755 --- a/heartbeat/redis +++ b/heartbeat/redis @@ -38,6 +38,7 @@ : ${OCF_RESKEY_pidfile_name:=redis-server.pid} : ${OCF_RESKEY_socket_name:=redis.sock} : ${OCF_RESKEY_port:=6379} +: ${OCF_RESKEY_tunnel_host:=127.0.0.1} if [ -z "$OCF_RESKEY_config" ]; then if [ -f "/etc/redis.conf" ]; then @@ -156,6 +157,39 @@ Port for replication client to connect to on remote server + + +When replication traffic is tunnelled, this is the host to target +to forward outgoing traffic to the redis master. The resource +agent configures the redis slave to target the master via +tunnel_host:tunnel_port. + +Note that in order to enable replication traffic tunneling, +parameter {tunnel_port_map} must be populated. + +Tunnel host for replication traffic + + + + + +A mapping of pacemaker node names to redis port number. + +To be used when redis servers need to tunnel replication traffic. +On every node where the redis resource is running, the redis server +listens to a different port. Each redis server can access its peers +for replication traffic via a tunnel accessible at {tunnel_host}:port. + +The mapping the form of: +pcmk1-name:port-for-redis1;pcmk2-name:port-for-redis2;pcmk3-name:port-for-redis3 + +where the redis resource started on node pcmk1-name would listen on +port port-for-redis1 + +Mapping of Redis server name to redis port + + + During redis cluster bootstrap, wait for the last known master to be @@ -291,6 +325,8 @@ simple_status() { function monitor() { local res + local master_name + local last_known_master_port simple_status res=$? @@ -334,14 +370,48 @@ redis_monitor() { return $OCF_ERR_GENERIC fi if [[ "${info[master_host]}" != "$(last_known_master)" ]]; then - ocf_log err "monitor: Slave mode current master does not match running master. current=${info[master_host]}, running=$(last_known_master)" - return $OCF_ERR_GENERIC + if [ -n "${OCF_RESKEY_tunnel_port_map}" ]; then + master_name=$(port_to_redis_node ${info[master_port]}) + last_known_master_port=$(redis_node_to_port $(last_known_master)) + if [[ "${info[master_host]}" != "${OCF_RESKEY_tunnel_host}" ]] || + [[ "${info[master_port]}" != "${last_known_master_port}" ]]; then + ocf_log err "monitor: Slave mode current tunnelled connection to redis server does not match running master. tunnelled='${info[master_host]}:${info[master_port]} (${master_name})', running='$(last_known_master)'" + return $OCF_ERR_GENERIC + fi + else + ocf_log err "monitor: Slave mode current master does not match running master. current=${info[master_host]}, running=$(last_known_master)" + return $OCF_ERR_GENERIC + fi fi fi fi return $OCF_SUCCESS } +redis_node_to_port() +{ + local node=$1 + echo "$OCF_RESKEY_tunnel_port_map" | tr ';' '\n' | tr -d ' ' | sed 's/:/ /' | awk -F' ' '$1=="'"$node"'" {print $2;exit}' +} + +port_to_redis_node() +{ + local port=$1 + echo "$OCF_RESKEY_tunnel_port_map" | tr ';' '\n' | tr -d ' ' | sed 's/:/ /' | awk -F' ' '$2=="'"$port"'" {print $1;exit}' +} + +get_tunnel_port_from_master() +{ + local master_name=$1 + crm_attribute --node "$master_name" -l forever --name ${INSTANCE_ATTR_NAME}-tunnel-port --query -q 2>/dev/null +} + +get_master_from_tunnel_port() +{ + local master_name=$1 + crm_attribute --node "$master_name" -l forever --name ${INSTANCE_ATTR_NAME}-tunnel-port --query -q 2>/dev/null +} + function check_dump_file() { if ! have_binary "$REDIS_CHECK_DUMP"; then @@ -479,6 +549,7 @@ redis_promote() { function demote() { local master_host local master_port + local tunnel_port # client kill is only supported in Redis 2.8.12 or greater version=$(redis_client -v | awk '{print $NF}') @@ -512,7 +583,19 @@ redis_demote() { master_host="no-such-master" fi - ocf_log info "demote: Setting master to '$master_host'" + if [ -n "${OCF_RESKEY_tunnel_port_map}" ]; then + # master_host can be the special marker "no-such-master" + # while a master is being selected. In this case, no + # tunnel port is returned, but this is not fatal. + tunnel_port=$(redis_node_to_port "$master_host") + if [ -n "$tunnel_port" ]; then + ocf_log info "demote: Setting master to '$master_host' via local tunnel '${OCF_RESKEY_tunnel_host}' on port '$tunnel_port'" + master_host="${OCF_RESKEY_tunnel_host}" + master_port="$tunnel_port" + fi + else + ocf_log info "demote: Setting master to '$master_host'" + fi redis_client slaveof "$master_host" "$master_port" -- 2.14.3