Support dynamic runtime modification of tls-allowlist, and add config rewrite support
This commit is contained in:
parent
5d833a7173
commit
f7476575a1
12
keydb.conf
12
keydb.conf
@ -265,16 +265,12 @@ tcp-keepalive 300
|
|||||||
# Setup a allowlist of allowed Common Names (CNs)/Subject Alternative Names (SANs)
|
# Setup a allowlist of allowed Common Names (CNs)/Subject Alternative Names (SANs)
|
||||||
# that are allowed to connect to this server. This includes both normal clients as
|
# that are allowed to connect to this server. This includes both normal clients as
|
||||||
# well as other servers connected for replication/clustering purposes. If nothing is
|
# well as other servers connected for replication/clustering purposes. If nothing is
|
||||||
# specified, then no allowlist is used. Supports IPv4, DNS, RFC822, and URI SAN types.
|
# specified, then no allowlist is used and all certificates are accepted.
|
||||||
# You can opt to either put all of the names on one line as follows:
|
# Supports IPv4, DNS, RFC822, and URI SAN types.
|
||||||
|
# You can put multiple names on one line as follows:
|
||||||
#
|
#
|
||||||
# tls-allowlist <dns1> <dns2> <dns3> ...
|
# tls-allowlist <dns1> <dns2> <dns3> ...
|
||||||
#
|
#
|
||||||
# or place then all on their own seperate line (or a combination of the two):
|
|
||||||
#
|
|
||||||
# tls-allowlist <dns1>
|
|
||||||
# tls-allowlist <dns2>
|
|
||||||
# ...
|
|
||||||
#
|
#
|
||||||
# This configuration also allows for wildcard characters with glob style formatting
|
# This configuration also allows for wildcard characters with glob style formatting
|
||||||
# i.e. "*.com" would allow all clients to connect with a CN/SAN that ends with ".com"
|
# i.e. "*.com" would allow all clients to connect with a CN/SAN that ends with ".com"
|
||||||
@ -2073,4 +2069,4 @@ server-threads 2
|
|||||||
# this to 1. Very write heavy workloads may benefit from higher numbers.
|
# this to 1. Very write heavy workloads may benefit from higher numbers.
|
||||||
#
|
#
|
||||||
# By default KeyDB sets this to 2.
|
# By default KeyDB sets this to 2.
|
||||||
replica-weighting-factor 2
|
replica-weighting-factor 2
|
||||||
|
@ -742,10 +742,14 @@ void loadServerConfigFromString(char *config) {
|
|||||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(argv[0], "tls-allowlist")) {
|
} else if (!strcasecmp(argv[0], "tls-allowlist")) {
|
||||||
if (!g_pserver->tls_allowlist_enabled)
|
if (argc < 2) {
|
||||||
g_pserver->tls_allowlist_enabled = true;
|
err = "must supply at least one element in the allow list"; goto loaderr;
|
||||||
|
}
|
||||||
|
if (!g_pserver->tls_allowlist.empty()) {
|
||||||
|
err = "tls-allowlist may only be set once"; goto loaderr;
|
||||||
|
}
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
g_pserver->tls_allowlist.insert(zstrdup(argv[i]));
|
g_pserver->tls_allowlist.emplace(argv[i], strlen(argv[i]));
|
||||||
} else if (!strcasecmp(argv[0], "version-override") && argc == 2) {
|
} else if (!strcasecmp(argv[0], "version-override") && argc == 2) {
|
||||||
KEYDB_SET_VERSION = zstrdup(argv[1]);
|
KEYDB_SET_VERSION = zstrdup(argv[1]);
|
||||||
serverLog(LL_WARNING, "Warning version is overriden to: %s\n", KEYDB_SET_VERSION);
|
serverLog(LL_WARNING, "Warning version is overriden to: %s\n", KEYDB_SET_VERSION);
|
||||||
@ -866,8 +870,18 @@ void configSetCommand(client *c) {
|
|||||||
int err;
|
int err;
|
||||||
const char *errstr = NULL;
|
const char *errstr = NULL;
|
||||||
serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
|
serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
|
||||||
serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
|
|
||||||
o = c->argv[3];
|
if (c->argc < 4 || c->argc > 4) {
|
||||||
|
o = nullptr;
|
||||||
|
// Variadic set is only supported for tls-allowlist
|
||||||
|
if (strcasecmp(szFromObj(c->argv[2]), "tls-allowlist")) {
|
||||||
|
addReplySubcommandSyntaxError(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
o = c->argv[3];
|
||||||
|
serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
|
||||||
|
}
|
||||||
|
|
||||||
/* Iterate the configs that are standard */
|
/* Iterate the configs that are standard */
|
||||||
for (standardConfig *config = configs; config->name != NULL; config++) {
|
for (standardConfig *config = configs; config->name != NULL; config++) {
|
||||||
@ -1029,6 +1043,12 @@ void configSetCommand(client *c) {
|
|||||||
enableWatchdog(ll);
|
enableWatchdog(ll);
|
||||||
else
|
else
|
||||||
disableWatchdog();
|
disableWatchdog();
|
||||||
|
} config_set_special_field("tls-allowlist") {
|
||||||
|
g_pserver->tls_allowlist.clear();
|
||||||
|
for (int i = 3; i < c->argc; ++i) {
|
||||||
|
robj *val = c->argv[i];
|
||||||
|
g_pserver->tls_allowlist.emplace(szFromObj(val), sdslen(szFromObj(val)));
|
||||||
|
}
|
||||||
/* Everything else is an error... */
|
/* Everything else is an error... */
|
||||||
} config_set_else {
|
} config_set_else {
|
||||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||||
@ -1232,6 +1252,14 @@ void configGetCommand(client *c) {
|
|||||||
addReplyBulkCString(c, g_pserver->fActiveReplica ? "yes" : "no");
|
addReplyBulkCString(c, g_pserver->fActiveReplica ? "yes" : "no");
|
||||||
matches++;
|
matches++;
|
||||||
}
|
}
|
||||||
|
if (stringmatch(pattern, "tls-allowlist", 1)) {
|
||||||
|
addReplyBulkCString(c,"tls-allowlist");
|
||||||
|
addReplyArrayLen(c, (long)g_pserver->tls_allowlist.size());
|
||||||
|
for (auto &elem : g_pserver->tls_allowlist) {
|
||||||
|
addReplyBulkCBuffer(c, elem.get(), elem.size()); // addReplyBulkSds will free which we absolutely don't want
|
||||||
|
}
|
||||||
|
matches++;
|
||||||
|
}
|
||||||
|
|
||||||
setDeferredMapLen(c,replylen,matches);
|
setDeferredMapLen(c,replylen,matches);
|
||||||
}
|
}
|
||||||
@ -1901,6 +1929,20 @@ int rewriteConfig(char *path, int force_all) {
|
|||||||
rewriteConfigStringOption(state, "version-override",KEYDB_SET_VERSION,KEYDB_REAL_VERSION);
|
rewriteConfigStringOption(state, "version-override",KEYDB_SET_VERSION,KEYDB_REAL_VERSION);
|
||||||
rewriteConfigOOMScoreAdjValuesOption(state);
|
rewriteConfigOOMScoreAdjValuesOption(state);
|
||||||
|
|
||||||
|
if (!g_pserver->tls_allowlist.empty()) {
|
||||||
|
sds conf = sdsnew("tls-allowlist ");
|
||||||
|
for (auto &elem : g_pserver->tls_allowlist) {
|
||||||
|
conf = sdscatsds(conf, (sds)elem.get());
|
||||||
|
conf = sdscat(conf, " ");
|
||||||
|
}
|
||||||
|
// trim the trailing space
|
||||||
|
sdsrange(conf, 0, -1);
|
||||||
|
rewriteConfigRewriteLine(state,"tls-allowlist",conf,1 /*force*/);
|
||||||
|
// note: conf is owned by rewriteConfigRewriteLine - no need to free
|
||||||
|
} else {
|
||||||
|
rewriteConfigMarkAsProcessed(state, "tls-allowlist"); // ensure the line is removed if it existed
|
||||||
|
}
|
||||||
|
|
||||||
/* Rewrite Sentinel config if in Sentinel mode. */
|
/* Rewrite Sentinel config if in Sentinel mode. */
|
||||||
if (g_pserver->sentinel_mode) rewriteConfigSentinelOption(state);
|
if (g_pserver->sentinel_mode) rewriteConfigSentinelOption(state);
|
||||||
|
|
||||||
@ -2907,7 +2949,7 @@ NULL
|
|||||||
};
|
};
|
||||||
|
|
||||||
addReplyHelp(c, help);
|
addReplyHelp(c, help);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"set") && c->argc == 4) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"set") && c->argc >= 3) {
|
||||||
configSetCommand(c);
|
configSetCommand(c);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"get") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"get") && c->argc == 3) {
|
||||||
configGetCommand(c);
|
configGetCommand(c);
|
||||||
|
@ -2612,8 +2612,7 @@ struct redisServer {
|
|||||||
int tls_auth_clients;
|
int tls_auth_clients;
|
||||||
int tls_rotation;
|
int tls_rotation;
|
||||||
|
|
||||||
int tls_allowlist_enabled;
|
std::set<sdsstring> tls_allowlist;
|
||||||
std::unordered_set<char *> tls_allowlist;
|
|
||||||
redisTLSContextConfig tls_ctx_config;
|
redisTLSContextConfig tls_ctx_config;
|
||||||
|
|
||||||
/* cpu affinity */
|
/* cpu affinity */
|
||||||
|
@ -484,14 +484,17 @@ bool tlsCheckAgainstAllowlist(const char * client){
|
|||||||
/* Because of wildcard matching, we need to iterate over the entire set.
|
/* Because of wildcard matching, we need to iterate over the entire set.
|
||||||
* If we were doing simply straight matching, we could just directly
|
* If we were doing simply straight matching, we could just directly
|
||||||
* check to see if the client name is in the set in O(1) time */
|
* check to see if the client name is in the set in O(1) time */
|
||||||
for (char * client_pattern: g_pserver->tls_allowlist){
|
for (auto &client_pattern: g_pserver->tls_allowlist){
|
||||||
if (stringmatchlen(client_pattern, strlen(client_pattern), client, strlen(client), 1))
|
if (stringmatchlen(client_pattern.get(), client_pattern.size(), client, strlen(client), 1))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tlsValidateCertificateName(tls_connection* conn){
|
bool tlsValidateCertificateName(tls_connection* conn){
|
||||||
|
if (g_pserver->tls_allowlist.empty())
|
||||||
|
return true; // Empty list implies acceptance of all
|
||||||
|
|
||||||
X509 * cert = SSL_get_peer_certificate(conn->ssl);
|
X509 * cert = SSL_get_peer_certificate(conn->ssl);
|
||||||
/* Check the common name (CN) of the certificate first */
|
/* Check the common name (CN) of the certificate first */
|
||||||
X509_NAME_ENTRY * ne = X509_NAME_get_entry(X509_get_subject_name(cert), X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1));
|
X509_NAME_ENTRY * ne = X509_NAME_get_entry(X509_get_subject_name(cert), X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1));
|
||||||
@ -776,7 +779,7 @@ void tlsHandleEvent(tls_connection *conn, int mask) {
|
|||||||
conn->c.state = CONN_STATE_ERROR;
|
conn->c.state = CONN_STATE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
/* Validate name */
|
/* Validate name */
|
||||||
if (g_pserver->tls_allowlist_enabled && !tlsValidateCertificateName(conn)){
|
if (!tlsValidateCertificateName(conn)){
|
||||||
conn->c.state = CONN_STATE_ERROR;
|
conn->c.state = CONN_STATE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
conn->c.state = CONN_STATE_CONNECTED;
|
conn->c.state = CONN_STATE_CONNECTED;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user