Sentinel: add an option to deny online script reconfiguration.

The ability of "SENTINEL SET" to change the reconfiguration script at
runtime is a problem even in the security model of Redis: any client
inside the network may set any executable to be ran once a failover is
triggered.

This option adds protection for this problem: by default the two
SENTINEL SET subcommands modifying scripts paths are denied. However the
user is still able to rever that using the Sentinel configuration file
in order to allow such a feature.
This commit is contained in:
antirez 2018-06-14 18:57:58 +02:00
parent 590f537420
commit befcbfbe7f
2 changed files with 41 additions and 0 deletions

View File

@ -194,3 +194,12 @@ sentinel failover-timeout mymaster 180000
# #
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
# SECURITY
#
# By default SENTINEL SET will not be able to change the notification-script
# and client-reconfig-script at runtime. This avoids a trivial security issue
# where clients can set the script to anything and trigger a failover in order
# to get the program executed.
sentinel deny-scripts-reconfig yes

View File

@ -84,6 +84,7 @@ typedef struct sentinelAddr {
#define SENTINEL_MAX_PENDING_COMMANDS 100 #define SENTINEL_MAX_PENDING_COMMANDS 100
#define SENTINEL_ELECTION_TIMEOUT 10000 #define SENTINEL_ELECTION_TIMEOUT 10000
#define SENTINEL_MAX_DESYNC 1000 #define SENTINEL_MAX_DESYNC 1000
#define SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG 1
/* Failover machine different states. */ /* Failover machine different states. */
#define SENTINEL_FAILOVER_STATE_NONE 0 /* No failover in progress. */ #define SENTINEL_FAILOVER_STATE_NONE 0 /* No failover in progress. */
@ -241,6 +242,8 @@ struct sentinelState {
int announce_port; /* Port that is gossiped to other sentinels if int announce_port; /* Port that is gossiped to other sentinels if
non zero. */ non zero. */
unsigned long simfailure_flags; /* Failures simulation. */ unsigned long simfailure_flags; /* Failures simulation. */
int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script
paths at runtime? */
} sentinel; } sentinel;
/* A script execution job. */ /* A script execution job. */
@ -468,6 +471,7 @@ void initSentinel(void) {
sentinel.announce_ip = NULL; sentinel.announce_ip = NULL;
sentinel.announce_port = 0; sentinel.announce_port = 0;
sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE; sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;
memset(sentinel.myid,0,sizeof(sentinel.myid)); memset(sentinel.myid,0,sizeof(sentinel.myid));
} }
@ -1684,6 +1688,12 @@ char *sentinelHandleConfiguration(char **argv, int argc) {
} else if (!strcasecmp(argv[0],"announce-port") && argc == 2) { } else if (!strcasecmp(argv[0],"announce-port") && argc == 2) {
/* announce-port <port> */ /* announce-port <port> */
sentinel.announce_port = atoi(argv[1]); sentinel.announce_port = atoi(argv[1]);
} else if (!strcasecmp(argv[0],"deny-scripts-reconfig") && argc == 2) {
/* deny-scripts-reconfig <yes|no> */
if ((sentinel.deny_scripts_reconfig = yesnotoi(argv[1])) == -1) {
return "Please specify yes or no for the "
"deny-scripts-reconfig options.";
}
} else { } else {
return "Unrecognized sentinel configuration statement."; return "Unrecognized sentinel configuration statement.";
} }
@ -1704,6 +1714,12 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
line = sdscatprintf(sdsempty(), "sentinel myid %s", sentinel.myid); line = sdscatprintf(sdsempty(), "sentinel myid %s", sentinel.myid);
rewriteConfigRewriteLine(state,"sentinel",line,1); rewriteConfigRewriteLine(state,"sentinel",line,1);
/* sentinel deny-scripts-reconfig. */
line = sdscatprintf(sdsempty(), "sentinel deny-scripts-reconfig %s",
sentinel.deny_scripts_reconfig ? "yes" : "no");
rewriteConfigRewriteLine(state,"sentinel",line,
sentinel.deny_scripts_reconfig != SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG);
/* For every master emit a "sentinel monitor" config entry. */ /* For every master emit a "sentinel monitor" config entry. */
di = dictGetIterator(sentinel.masters); di = dictGetIterator(sentinel.masters);
while((de = dictNext(di)) != NULL) { while((de = dictNext(di)) != NULL) {
@ -3327,6 +3343,14 @@ void sentinelSetCommand(client *c) {
changes++; changes++;
} else if (!strcasecmp(option,"notification-script")) { } else if (!strcasecmp(option,"notification-script")) {
/* notification-script <path> */ /* notification-script <path> */
if (sentinel.deny_scripts_reconfig) {
addReplyError(c,
"Reconfiguration of scripts path is denied for "
"security reasons. Check the deny-scripts-reconfig "
"configuration directive in your Sentinel configuration");
return;
}
if (strlen(value) && access(value,X_OK) == -1) { if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c, addReplyError(c,
"Notification script seems non existing or non executable"); "Notification script seems non existing or non executable");
@ -3338,6 +3362,14 @@ void sentinelSetCommand(client *c) {
changes++; changes++;
} else if (!strcasecmp(option,"client-reconfig-script")) { } else if (!strcasecmp(option,"client-reconfig-script")) {
/* client-reconfig-script <path> */ /* client-reconfig-script <path> */
if (sentinel.deny_scripts_reconfig) {
addReplyError(c,
"Reconfiguration of scripts path is denied for "
"security reasons. Check the deny-scripts-reconfig "
"configuration directive in your Sentinel configuration");
return;
}
if (strlen(value) && access(value,X_OK) == -1) { if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c, addReplyError(c,
"Client reconfiguration script seems non existing or " "Client reconfiguration script seems non existing or "