rpc/auth: allow SSL identity to be used for authorization
Access to a volume is now controlled by the following options, based on whether SSL is enabled or not. * server.ssl-allow: get identity from certificate, no password needed * auth.allow: get identity and matching password from command line It is not possible to allow both simultaneously, since the connection itself is either using SSL or it isn't. Change-Id: I5a5be66520f56778563d62f4b3ab35c66cc41ac0 BUG: 1114604 Signed-off-by: Jeff Darcy <jdarcy@redhat.com> Reviewed-on: http://review.gluster.org/3695 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
This commit is contained in:
parent
831efecf92
commit
caa8a4ea50
@ -447,6 +447,10 @@ rpc_transport_destroy (rpc_transport_t *this)
|
||||
if (this->dl_handle)
|
||||
dlclose (this->dl_handle);
|
||||
|
||||
if (this->ssl_name) {
|
||||
GF_FREE(this->ssl_name);
|
||||
}
|
||||
|
||||
GF_FREE (this);
|
||||
fail:
|
||||
return ret;
|
||||
|
@ -214,6 +214,7 @@ struct rpc_transport {
|
||||
struct list_head list;
|
||||
int bind_insecure;
|
||||
void *dl_handle; /* handle of dlopen() */
|
||||
char *ssl_name;
|
||||
};
|
||||
|
||||
struct rpc_transport_ops {
|
||||
|
@ -246,7 +246,7 @@ out:
|
||||
#define ssl_read_one(t,b,l) ssl_do((t),(b),(l),(SSL_trinary_func *)SSL_read)
|
||||
#define ssl_write_one(t,b,l) ssl_do((t),(b),(l),(SSL_trinary_func *)SSL_write)
|
||||
|
||||
static int
|
||||
static char *
|
||||
ssl_setup_connection (rpc_transport_t *this, int server)
|
||||
{
|
||||
X509 *peer = NULL;
|
||||
@ -297,7 +297,7 @@ ssl_setup_connection (rpc_transport_t *this, int server)
|
||||
NID_commonName, peer_CN, sizeof(peer_CN)-1);
|
||||
peer_CN[sizeof(peer_CN)-1] = '\0';
|
||||
gf_log(this->name,GF_LOG_INFO,"peer CN = %s", peer_CN);
|
||||
return 0;
|
||||
return gf_strdup(peer_CN);
|
||||
|
||||
/* Error paths. */
|
||||
ssl_error:
|
||||
@ -307,7 +307,7 @@ free_ssl:
|
||||
SSL_free(priv->ssl_ssl);
|
||||
priv->ssl_ssl = NULL;
|
||||
done:
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -2262,15 +2262,23 @@ socket_poller (void *ctx)
|
||||
gf_boolean_t to_write = _gf_false;
|
||||
int ret = 0;
|
||||
uint32_t gen = 0;
|
||||
char *cname = NULL;
|
||||
|
||||
priv->ot_state = OT_RUNNING;
|
||||
|
||||
if (priv->use_ssl) {
|
||||
if (ssl_setup_connection(this,priv->connected) < 0) {
|
||||
cname = ssl_setup_connection(this,priv->connected);
|
||||
if (!cname) {
|
||||
gf_log (this->name,GF_LOG_ERROR, "%s setup failed",
|
||||
priv->connected ? "server" : "client");
|
||||
goto err;
|
||||
}
|
||||
if (priv->connected) {
|
||||
this->ssl_name = cname;
|
||||
}
|
||||
else {
|
||||
GF_FREE(cname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->bio) {
|
||||
@ -2450,6 +2458,7 @@ socket_server_event_handler (int fd, int idx, void *data,
|
||||
socklen_t addrlen = sizeof (new_sockaddr);
|
||||
socket_private_t *new_priv = NULL;
|
||||
glusterfs_ctx_t *ctx = NULL;
|
||||
char *cname = NULL;
|
||||
|
||||
this = data;
|
||||
GF_VALIDATE_OR_GOTO ("socket", this, out);
|
||||
@ -2560,7 +2569,8 @@ socket_server_event_handler (int fd, int idx, void *data,
|
||||
|
||||
new_priv->ssl_ctx = priv->ssl_ctx;
|
||||
if (priv->use_ssl && !priv->own_thread) {
|
||||
if (ssl_setup_connection(new_trans,1) < 0) {
|
||||
cname = ssl_setup_connection(new_trans,1);
|
||||
if (!cname) {
|
||||
gf_log(this->name,GF_LOG_ERROR,
|
||||
"server setup failed");
|
||||
close(new_sock);
|
||||
@ -2568,6 +2578,7 @@ socket_server_event_handler (int fd, int idx, void *data,
|
||||
GF_FREE (new_trans);
|
||||
goto unlock;
|
||||
}
|
||||
this->ssl_name = cname;
|
||||
}
|
||||
|
||||
if (!priv->bio && !priv->own_thread) {
|
||||
@ -2634,6 +2645,9 @@ unlock:
|
||||
pthread_mutex_unlock (&priv->lock);
|
||||
|
||||
out:
|
||||
if (cname && (cname != this->ssl_name)) {
|
||||
GF_FREE(cname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2694,6 +2708,7 @@ socket_connect (rpc_transport_t *this, int port)
|
||||
gf_boolean_t refd = _gf_false;
|
||||
socket_connect_error_state_t *arg = NULL;
|
||||
pthread_t th_id = {0, };
|
||||
char *cname = NULL;
|
||||
|
||||
GF_VALIDATE_OR_GOTO ("socket", this, err);
|
||||
GF_VALIDATE_OR_GOTO ("socket", this->private, err);
|
||||
@ -2857,12 +2872,20 @@ socket_connect (rpc_transport_t *this, int port)
|
||||
}
|
||||
|
||||
if (priv->use_ssl && !priv->own_thread) {
|
||||
ret = ssl_setup_connection(this,0);
|
||||
if (ret < 0) {
|
||||
cname = ssl_setup_connection(this,0);
|
||||
if (!cname) {
|
||||
errno = ENOTCONN;
|
||||
ret = -1;
|
||||
gf_log(this->name,GF_LOG_ERROR,
|
||||
"client setup failed");
|
||||
goto handler;
|
||||
}
|
||||
if (priv->connected) {
|
||||
this->ssl_name = cname;
|
||||
}
|
||||
else {
|
||||
GF_FREE(cname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->bio && !priv->own_thread) {
|
||||
|
@ -24,6 +24,7 @@ ln $SSL_CERT $SSL_CA
|
||||
TEST $CLI volume create $V0 $H0:$B0/1
|
||||
TEST $CLI volume set $V0 server.ssl on
|
||||
TEST $CLI volume set $V0 client.ssl on
|
||||
TEST $CLI volume set $V0 auth.ssl-allow Anyone
|
||||
TEST $CLI volume start $V0
|
||||
|
||||
TEST glusterfs --volfile-server=$H0 --volfile-id=$V0 $M0
|
||||
|
53
tests/features/ssl-authz.t
Executable file
53
tests/features/ssl-authz.t
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
. $(dirname $0)/../include.rc
|
||||
|
||||
ping_file () {
|
||||
echo hello > $1 2> /dev/null
|
||||
}
|
||||
|
||||
SSL_BASE=/etc/ssl
|
||||
SSL_KEY=$SSL_BASE/glusterfs.key
|
||||
SSL_CERT=$SSL_BASE/glusterfs.pem
|
||||
SSL_CA=$SSL_BASE/glusterfs.ca
|
||||
|
||||
cleanup;
|
||||
rm -f $SSL_BASE/glusterfs.*
|
||||
mkdir -p $B0/1
|
||||
mkdir -p $M0
|
||||
|
||||
TEST glusterd
|
||||
TEST pidof glusterd
|
||||
TEST $CLI volume info;
|
||||
|
||||
TEST openssl genrsa -out $SSL_KEY 1024
|
||||
TEST openssl req -new -x509 -key $SSL_KEY -subj /CN=Anyone -out $SSL_CERT
|
||||
ln $SSL_CERT $SSL_CA
|
||||
|
||||
TEST $CLI volume create $V0 $H0:$B0/1
|
||||
TEST $CLI volume set $V0 server.ssl on
|
||||
TEST $CLI volume set $V0 client.ssl on
|
||||
TEST $CLI volume set $V0 auth.ssl-allow Anyone
|
||||
TEST $CLI volume start $V0
|
||||
|
||||
# This mount should WORK.
|
||||
TEST glusterfs --volfile-server=$H0 --volfile-id=$V0 $M0
|
||||
TEST ping_file $M0/before
|
||||
TEST umount $M0
|
||||
|
||||
# Change the authorized user name. Note that servers don't pick up changes
|
||||
# automagically like clients do, so we have to stop/start ourselves.
|
||||
TEST $CLI volume stop $V0
|
||||
TEST $CLI volume set $V0 auth.ssl-allow NotYou
|
||||
TEST $CLI volume start $V0
|
||||
|
||||
# This mount should FAIL because the identity given by our certificate does not
|
||||
# match the allowed user. In other words, authentication works (they know who
|
||||
# we are) but authorization doesn't (we're not the right person).
|
||||
TEST glusterfs --volfile-server=$H0 --volfile-id=$V0 $M0
|
||||
|
||||
# Looks like /*/bin/glusterfs isn't returning error status correctly (again).
|
||||
# Actually try doing something to get a real error.
|
||||
TEST ! ping_file $M0/after
|
||||
|
||||
cleanup;
|
@ -1517,6 +1517,7 @@ server_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo,
|
||||
gf_boolean_t quota_enabled = _gf_true;
|
||||
gf_boolean_t pgfid_feat = _gf_false;
|
||||
char *value = NULL;
|
||||
char *ssl_user = NULL;
|
||||
|
||||
brickinfo = param;
|
||||
path = brickinfo->path;
|
||||
@ -1816,6 +1817,15 @@ server_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dict_get_str (volinfo->dict, "auth.ssl-allow", &ssl_user) == 0) {
|
||||
memset (key, 0, sizeof (key));
|
||||
snprintf (key, sizeof (key), "auth.login.%s.ssl-allow", path);
|
||||
|
||||
ret = xlator_set_option (xl, key, ssl_user);
|
||||
if (ret)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = volgen_graph_set_options_generic (graph, set_dict,
|
||||
(xlator && loglevel) ? (void *)set_dict : volinfo,
|
||||
(xlator && loglevel) ? &server_spec_extended_option_handler :
|
||||
|
@ -950,6 +950,12 @@ struct volopt_map_entry glusterd_volopt_map[] = {
|
||||
.type = NO_DOC,
|
||||
.op_version = 2
|
||||
},
|
||||
{ .key = "auth.ssl-allow",
|
||||
.voltype = "protocol/server",
|
||||
.option = "!ssl-allow",
|
||||
.type = NO_DOC,
|
||||
.op_version = GD_OP_VERSION_3_6_0,
|
||||
},
|
||||
{ .key = "server.manage-gids",
|
||||
.voltype = "protocol/server",
|
||||
.op_version = GD_OP_VERSION_3_6_0,
|
||||
|
@ -19,37 +19,44 @@
|
||||
auth_result_t gf_auth (dict_t *input_params, dict_t *config_params)
|
||||
{
|
||||
auth_result_t result = AUTH_DONT_CARE;
|
||||
int ret = 0;
|
||||
data_t *allow_user = NULL;
|
||||
data_t *username_data = NULL;
|
||||
data_t *passwd_data = NULL;
|
||||
data_t *password_data = NULL;
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char *brick_name = NULL;
|
||||
char *searchstr = NULL;
|
||||
char *username_str = NULL;
|
||||
char *tmp = NULL;
|
||||
char *username_cpy = NULL;
|
||||
int ret = 0;
|
||||
data_t *allow_user = NULL;
|
||||
data_t *username_data = NULL;
|
||||
data_t *passwd_data = NULL;
|
||||
data_t *password_data = NULL;
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char *brick_name = NULL;
|
||||
char *searchstr = NULL;
|
||||
char *username_str = NULL;
|
||||
char *tmp = NULL;
|
||||
char *username_cpy = NULL;
|
||||
gf_boolean_t using_ssl = _gf_false;
|
||||
|
||||
username_data = dict_get (input_params, "username");
|
||||
if (!username_data) {
|
||||
gf_log ("auth/login", GF_LOG_DEBUG,
|
||||
"username not found, returning DONT-CARE");
|
||||
goto out;
|
||||
username_data = dict_get (input_params, "ssl-name");
|
||||
if (username_data) {
|
||||
gf_log ("auth/login", GF_LOG_INFO,
|
||||
"connecting user name: %s", username_data->data);
|
||||
using_ssl = _gf_true;
|
||||
result = AUTH_REJECT;
|
||||
}
|
||||
else {
|
||||
username_data = dict_get (input_params, "username");
|
||||
if (!username_data) {
|
||||
gf_log ("auth/login", GF_LOG_DEBUG,
|
||||
"username not found, returning DONT-CARE");
|
||||
goto out;
|
||||
}
|
||||
password_data = dict_get (input_params, "password");
|
||||
if (!password_data) {
|
||||
gf_log ("auth/login", GF_LOG_WARNING,
|
||||
"password not found, returning DONT-CARE");
|
||||
goto out;
|
||||
}
|
||||
password = data_to_str (password_data);
|
||||
}
|
||||
|
||||
username = data_to_str (username_data);
|
||||
|
||||
password_data = dict_get (input_params, "password");
|
||||
if (!password_data) {
|
||||
gf_log ("auth/login", GF_LOG_WARNING,
|
||||
"password not found, returning DONT-CARE");
|
||||
goto out;
|
||||
}
|
||||
|
||||
password = data_to_str (password_data);
|
||||
|
||||
brick_name = data_to_str (dict_get (input_params, "remote-subvolume"));
|
||||
if (!brick_name) {
|
||||
gf_log ("auth/login", GF_LOG_ERROR,
|
||||
@ -58,7 +65,8 @@ auth_result_t gf_auth (dict_t *input_params, dict_t *config_params)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = gf_asprintf (&searchstr, "auth.login.%s.allow", brick_name);
|
||||
ret = gf_asprintf (&searchstr, "auth.login.%s.%s", brick_name,
|
||||
using_ssl ? "ssl-allow" : "allow");
|
||||
if (-1 == ret) {
|
||||
gf_log ("auth/login", GF_LOG_WARNING,
|
||||
"asprintf failed while setting search string, "
|
||||
@ -70,14 +78,26 @@ auth_result_t gf_auth (dict_t *input_params, dict_t *config_params)
|
||||
GF_FREE (searchstr);
|
||||
|
||||
if (allow_user) {
|
||||
gf_log ("auth/login", GF_LOG_INFO,
|
||||
"allowed user names: %s", allow_user->data);
|
||||
username_cpy = gf_strdup (allow_user->data);
|
||||
if (!username_cpy)
|
||||
goto out;
|
||||
|
||||
username_str = strtok_r (username_cpy, " ,", &tmp);
|
||||
|
||||
/*
|
||||
* We have to match a user's *authenticated* name to one in the
|
||||
* list. If we're using SSL, they're already authenticated.
|
||||
* Otherwise, they need a matching password to complete the
|
||||
* process.
|
||||
*/
|
||||
while (username_str) {
|
||||
if (!fnmatch (username_str, username, 0)) {
|
||||
if (using_ssl) {
|
||||
result = AUTH_ACCEPT;
|
||||
break;
|
||||
}
|
||||
ret = gf_asprintf (&searchstr,
|
||||
"auth.login.%s.password",
|
||||
username);
|
||||
|
@ -450,6 +450,13 @@ server_setvolume (rpcsvc_request_t *req)
|
||||
req->trans->xl_private = client;
|
||||
|
||||
auth_set_username_passwd (params, config_params, client);
|
||||
if (req->trans->ssl_name) {
|
||||
if (dict_set_str(params,"ssl-name",req->trans->ssl_name) != 0) {
|
||||
gf_log (this->name, GF_LOG_WARNING,
|
||||
"failed to set ssl_name %s", req->trans->ssl_name);
|
||||
/* Not fatal, auth will just fail. */
|
||||
}
|
||||
}
|
||||
|
||||
ret = dict_get_int32 (params, "fops-version", &fop_version);
|
||||
if (ret < 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user