Skip to content

Commit

Permalink
feat(security): implement meta server access controller (#655)
Browse files Browse the repository at this point in the history
  • Loading branch information
levy5307 authored Nov 20, 2020
1 parent b1b5866 commit 7889269
Show file tree
Hide file tree
Showing 18 changed files with 430 additions and 3 deletions.
7 changes: 7 additions & 0 deletions include/dsn/tool-api/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ class rpc_session : public ref_counter
void set_negotiation_succeed();
bool is_negotiation_succeed() const;

void set_client_username(const std::string &user_name);
const std::string &get_client_username() const;

public:
///
/// for subclass to implement receiving message
Expand Down Expand Up @@ -328,6 +331,10 @@ class rpc_session : public ref_counter
rpc_client_matcher *_matcher;

std::atomic_int _delay_server_receive_ms;

// _client_username is only valid if it is a server rpc_session.
// it represents the name of the corresponding client
std::string _client_username;
};

// --------- inline implementation --------------
Expand Down
1 change: 1 addition & 0 deletions include/dsn/utility/error_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,5 @@ DEFINE_ERR_CODE(ERR_KRB5_INTERNAL)

DEFINE_ERR_CODE(ERR_SASL_INTERNAL)
DEFINE_ERR_CODE(ERR_SASL_INCOMPLETE)
DEFINE_ERR_CODE(ERR_ACL_DENY)
} // namespace dsn
6 changes: 6 additions & 0 deletions include/dsn/utility/strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <vector>
#include <list>
#include <map>
#include <unordered_set>
#include <iostream>

namespace dsn {
Expand All @@ -14,6 +15,11 @@ void split_args(const char *args,
char splitter = ' ',
bool keep_place_holder = false);

void split_args(const char *args,
/*out*/ std::unordered_set<std::string> &sargs,
char splitter = ' ',
bool keep_place_holder = false);

void split_args(const char *args, /*out*/ std::list<std::string> &sargs, char splitter = ' ');

// kv_map sample (when item_splitter = ',' and kv_splitter = ':'):
Expand Down
16 changes: 16 additions & 0 deletions src/meta/meta_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "meta/duplication/meta_duplication_service.h"
#include "meta_split_service.h"
#include "meta_bulk_load_service.h"
#include "runtime/security/access_controller.h"

namespace dsn {
namespace replication {
Expand Down Expand Up @@ -76,6 +77,8 @@ meta_service::meta_service()
"replica server disconnect count in the recent period");
_unalive_nodes_count.init_app_counter(
"eon.meta_service", "unalive_nodes", COUNTER_TYPE_NUMBER, "current count of unalive nodes");

_access_controller = security::create_meta_access_controller();
}

meta_service::~meta_service()
Expand Down Expand Up @@ -120,6 +123,12 @@ int meta_service::check_leader(TRpcHolder rpc, rpc_address *forward_address)
template <typename TRpcHolder>
bool meta_service::check_status(TRpcHolder rpc, rpc_address *forward_address)
{
if (!_access_controller->allowed(rpc.dsn_request())) {
rpc.response().err = ERR_ACL_DENY;
ddebug("reject request with ERR_ACL_DENY");
return false;
}

int result = check_leader(rpc, forward_address);
if (result == 0)
return false;
Expand All @@ -141,6 +150,13 @@ bool meta_service::check_status(TRpcHolder rpc, rpc_address *forward_address)
template <typename TRespType>
bool meta_service::check_status_with_msg(message_ex *req, TRespType &response_struct)
{
if (!_access_controller->allowed(req)) {
ddebug("reject request with ERR_ACL_DENY");
response_struct.err = ERR_ACL_DENY;
reply(req, response_struct);
return false;
}

int result = check_leader(req, nullptr);
if (result == 0) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/meta/meta_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
#include "block_service/block_service_manager.h"

namespace dsn {
namespace security {
class access_controller;
} // namespace security
namespace replication {

class server_state;
Expand Down Expand Up @@ -261,6 +264,8 @@ class meta_service : public serverlet<meta_service>
perf_counter_wrapper _unalive_nodes_count;

dsn::task_tracker _tracker;

std::unique_ptr<security::access_controller> _access_controller;
};

} // namespace replication
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/rpc/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@ bool rpc_session::is_negotiation_succeed() const
}
}

void rpc_session::set_client_username(const std::string &user_name)
{
_client_username = user_name;
}

const std::string &rpc_session::get_client_username() const { return _client_username; }

////////////////////////////////////////////////////////////////////////////////////////////////
network::network(rpc_engine *srv, network *inner_provider)
: _engine(srv), _client_hdr_format(NET_HDR_DSN), _unknown_msg_header_format(NET_HDR_INVALID)
Expand Down
47 changes: 47 additions & 0 deletions src/runtime/security/access_controller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include "access_controller.h"

#include <dsn/utility/flags.h>
#include <dsn/utility/strings.h>
#include <dsn/utility/smart_pointers.h>
#include "meta_access_controller.h"

namespace dsn {
namespace security {
DSN_DEFINE_bool("security", enable_acl, false, "whether enable access controller or not");
DSN_DEFINE_string("security", super_users, "", "super user for access controller");

access_controller::access_controller() { utils::split_args(FLAGS_super_users, _super_users, ','); }

access_controller::~access_controller() {}

bool access_controller::pre_check(const std::string &user_name)
{
if (!FLAGS_enable_acl || _super_users.find(user_name) != _super_users.end()) {
return true;
}
return false;
}

std::unique_ptr<access_controller> create_meta_access_controller()
{
return make_unique<meta_access_controller>();
}
} // namespace security
} // namespace dsn
54 changes: 54 additions & 0 deletions src/runtime/security/access_controller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#pragma once

#include <memory>
#include <unordered_set>

namespace dsn {
class message_ex;
namespace security {

class access_controller
{
public:
access_controller();
virtual ~access_controller() = 0;

/**
* reset the access controller
* acls - the new acls to reset
**/
virtual void reset(const std::string &acls){};

/**
* check if the message received is allowd to do something.
* msg - the message received
**/
virtual bool allowed(message_ex *msg) = 0;

protected:
bool pre_check(const std::string &user_name);
friend class meta_access_controller_test;

std::unordered_set<std::string> _super_users;
};

std::unique_ptr<access_controller> create_meta_access_controller();
} // namespace security
} // namespace dsn
69 changes: 69 additions & 0 deletions src/runtime/security/meta_access_controller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include "meta_access_controller.h"

#include <dsn/tool-api/rpc_message.h>
#include <dsn/utility/flags.h>
#include <dsn/tool-api/network.h>
#include <dsn/dist/fmt_logging.h>

namespace dsn {
namespace security {
DSN_DEFINE_string("security",
meta_acl_rpc_allow_list,
"",
"allowed list of rpc codes for meta_access_controller");

meta_access_controller::meta_access_controller()
{
// MetaServer serves the allow-list RPC from all users. RPCs unincluded are accessible to only
// superusers.
if (strlen(FLAGS_meta_acl_rpc_allow_list) == 0) {
register_allowed_list("RPC_CM_LIST_APPS");
register_allowed_list("RPC_CM_LIST_NODES");
register_allowed_list("RPC_CM_CLUSTER_INFO");
register_allowed_list("RPC_CM_QUERY_PARTITION_CONFIG_BY_INDEX");
} else {
std::vector<std::string> rpc_code_white_list;
utils::split_args(FLAGS_meta_acl_rpc_allow_list, rpc_code_white_list, ',');
for (const auto &rpc_code : rpc_code_white_list) {
register_allowed_list(rpc_code);
}
}
}

bool meta_access_controller::allowed(message_ex *msg)
{
if (pre_check(msg->io_session->get_client_username()) ||
_allowed_rpc_code_list.find(msg->rpc_code().code()) != _allowed_rpc_code_list.end()) {
return true;
}
return false;
}

void meta_access_controller::register_allowed_list(const std::string &rpc_code)
{
auto code = task_code::try_get(rpc_code, TASK_CODE_INVALID);
dassert_f(code != TASK_CODE_INVALID,
"invalid task code({}) in rpc_code_white_list of security section",
rpc_code);

_allowed_rpc_code_list.insert(code);
}
} // namespace security
} // namespace dsn
40 changes: 40 additions & 0 deletions src/runtime/security/meta_access_controller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#pragma once

#include "access_controller.h"

#include <unordered_set>

namespace dsn {
class message_ex;
namespace security {

class meta_access_controller : public access_controller
{
public:
meta_access_controller();
bool allowed(message_ex *msg) override;

private:
void register_allowed_list(const std::string &rpc_code);

std::unordered_set<int> _allowed_rpc_code_list;
};
} // namespace security
} // namespace dsn
21 changes: 21 additions & 0 deletions src/runtime/security/sasl_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "sasl_client_wrapper.h"

#include <sasl/sasl.h>
#include <dsn/utility/fail_point.h>

namespace dsn {
namespace security {
Expand All @@ -39,6 +40,26 @@ sasl_wrapper::~sasl_wrapper()
}
}

error_s sasl_wrapper::retrive_username(std::string &output)
{
FAIL_POINT_INJECT_F("sasl_wrapper_retrive_username", [](dsn::string_view str) {
error_code err = error_code::try_get(str.data(), ERR_UNKNOWN);
return error_s::make(err);
});

// retrive username from _conn.
// If this is a sasl server, it gets the name of the corresponding sasl client.
// But if this is a sasl client, it gets the name of itself
char *username = nullptr;
error_s err_s = wrap_error(sasl_getprop(_conn, SASL_USERNAME, (const void **)&username));
if (err_s.is_ok()) {
output = username;
output = output.substr(0, output.find_last_of('@'));
output = output.substr(0, output.find_first_of('/'));
}
return err_s;
}

error_s sasl_wrapper::wrap_error(int sasl_err)
{
error_s ret;
Expand Down
6 changes: 6 additions & 0 deletions src/runtime/security/sasl_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ class sasl_wrapper
virtual error_s init() = 0;
virtual error_s start(const std::string &mechanism, const blob &input, blob &output) = 0;
virtual error_s step(const blob &input, blob &output) = 0;
/**
* retrive username from sasl connection.
* If this is a sasl server, it gets the name of the corresponding sasl client.
* But if this is a sasl client, it gets the name of itself
**/
error_s retrive_username(/*out*/ std::string &output);

protected:
sasl_wrapper() = default;
Expand Down
Loading

0 comments on commit 7889269

Please sign in to comment.