From dfa58fda6e409b0a38d19ccc5da6ca0a6bf267ab Mon Sep 17 00:00:00 2001 From: zhao liwei Date: Mon, 23 Nov 2020 17:44:28 +0800 Subject: [PATCH] feat(security): implement replica server access controller (#670) --- src/replica/replica.cpp | 7 ++ src/replica/replica.h | 5 ++ src/replica/replica_2pc.cpp | 5 ++ src/runtime/security/access_controller.cpp | 6 ++ src/runtime/security/access_controller.h | 2 + .../security/replica_access_controller.cpp | 45 +++++++++++ .../security/replica_access_controller.h | 40 ++++++++++ .../test/replica_access_controller_test.cpp | 76 +++++++++++++++++++ 8 files changed, 186 insertions(+) create mode 100644 src/runtime/security/replica_access_controller.cpp create mode 100644 src/runtime/security/replica_access_controller.h create mode 100644 src/runtime/test/replica_access_controller_test.cpp diff --git a/src/replica/replica.cpp b/src/replica/replica.cpp index 298444c8c3..188d14fd65 100644 --- a/src/replica/replica.cpp +++ b/src/replica/replica.cpp @@ -31,6 +31,7 @@ #include "duplication/replica_duplicator_manager.h" #include "backup/replica_backup_manager.h" #include "bulk_load/replica_bulk_loader.h" +#include "runtime/security/access_controller.h" #include #include @@ -101,6 +102,8 @@ replica::replica( _extra_envs.insert( std::make_pair(backup_restore_constant::FORCE_RESTORE, std::string("true"))); } + + _access_controller = security::create_replica_access_controller(name()); } void replica::update_last_checkpoint_generate_time() @@ -157,6 +160,10 @@ replica::~replica(void) void replica::on_client_read(dsn::message_ex *request) { + if (!_access_controller->allowed(request)) { + response_client_read(request, ERR_ACL_DENY); + } + if (status() == partition_status::PS_INACTIVE || status() == partition_status::PS_POTENTIAL_SECONDARY) { response_client_read(request, ERR_INVALID_STATE); diff --git a/src/replica/replica.h b/src/replica/replica.h index a260532543..6dec27ce96 100644 --- a/src/replica/replica.h +++ b/src/replica/replica.h @@ -57,6 +57,9 @@ #include "utils/throttling_controller.h" namespace dsn { +namespace security { +class access_controller; +} // namespace security namespace replication { class replication_app_base; @@ -562,6 +565,8 @@ class replica : public serverlet, public ref_counter, public replica_ba dsn::task_tracker _tracker; // the thread access checker dsn::thread_access_checker _checker; + + std::unique_ptr _access_controller; }; typedef dsn::ref_ptr replica_ptr; } // namespace replication diff --git a/src/replica/replica_2pc.cpp b/src/replica/replica_2pc.cpp index 145705e688..694e001e66 100644 --- a/src/replica/replica_2pc.cpp +++ b/src/replica/replica_2pc.cpp @@ -29,6 +29,7 @@ #include "mutation_log.h" #include "replica_stub.h" #include "bulk_load/replica_bulk_loader.h" +#include "runtime/security/access_controller.h" #include #include #include @@ -40,6 +41,10 @@ void replica::on_client_write(dsn::message_ex *request, bool ignore_throttling) { _checker.only_one_thread_access(); + if (!_access_controller->allowed(request)) { + response_client_read(request, ERR_ACL_DENY); + } + if (_deny_client_write) { // Do not relay any message to the peer client to let it timeout, it's OK coz some users // may retry immediately when they got a not success code which will make the server side diff --git a/src/runtime/security/access_controller.cpp b/src/runtime/security/access_controller.cpp index 1ea8053795..88830ea68f 100644 --- a/src/runtime/security/access_controller.cpp +++ b/src/runtime/security/access_controller.cpp @@ -21,6 +21,7 @@ #include #include #include "meta_access_controller.h" +#include "replica_access_controller.h" namespace dsn { namespace security { @@ -43,5 +44,10 @@ std::unique_ptr create_meta_access_controller() { return make_unique(); } + +std::unique_ptr create_replica_access_controller(const std::string &name) +{ + return make_unique(name); +} } // namespace security } // namespace dsn diff --git a/src/runtime/security/access_controller.h b/src/runtime/security/access_controller.h index 517161fd2d..acaf1df427 100644 --- a/src/runtime/security/access_controller.h +++ b/src/runtime/security/access_controller.h @@ -50,5 +50,7 @@ class access_controller }; std::unique_ptr create_meta_access_controller(); + +std::unique_ptr create_replica_access_controller(const std::string &name); } // namespace security } // namespace dsn diff --git a/src/runtime/security/replica_access_controller.cpp b/src/runtime/security/replica_access_controller.cpp new file mode 100644 index 0000000000..23310ebd22 --- /dev/null +++ b/src/runtime/security/replica_access_controller.cpp @@ -0,0 +1,45 @@ +// 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 "replica_access_controller.h" + +#include +#include +#include + +namespace dsn { +namespace security { +replica_access_controller::replica_access_controller(const std::string &name) { _name = name; } + +bool replica_access_controller::allowed(message_ex *msg) +{ + const std::string &user_name = msg->io_session->get_client_username(); + if (pre_check(user_name)) { + return true; + } + + { + utils::auto_read_lock l(_lock); + if (_users.find(user_name) == _users.end()) { + ddebug_f("{}: user_name {} doesn't exist in acls map", _name, user_name); + return false; + } + return true; + } +} +} // namespace security +} // namespace dsn diff --git a/src/runtime/security/replica_access_controller.h b/src/runtime/security/replica_access_controller.h new file mode 100644 index 0000000000..c6f636cc8f --- /dev/null +++ b/src/runtime/security/replica_access_controller.h @@ -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 +#include "access_controller.h" + +namespace dsn { +namespace security { +class replica_access_controller : public access_controller +{ +public: + replica_access_controller(const std::string &name); + bool allowed(message_ex *msg); + +private: + utils::rw_lock_nr _lock; // [ + std::unordered_set _users; + // ] + std::string _name; + + friend class replica_access_controller_test; +}; +} // namespace security +} // namespace dsn diff --git a/src/runtime/test/replica_access_controller_test.cpp b/src/runtime/test/replica_access_controller_test.cpp new file mode 100644 index 0000000000..0892972b7a --- /dev/null +++ b/src/runtime/test/replica_access_controller_test.cpp @@ -0,0 +1,76 @@ +// 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 +#include +#include +#include "runtime/security/replica_access_controller.h" +#include "runtime/rpc/network.sim.h" + +namespace dsn { +namespace security { +DSN_DECLARE_bool(enable_acl); + +class replica_access_controller_test : public testing::Test +{ +public: + replica_access_controller_test() + { + _replica_access_controller = make_unique("test"); + } + + bool allowed(dsn::message_ex *msg) { return _replica_access_controller->allowed(msg); } + + void set_replica_users(std::unordered_set &&replica_users) + { + _replica_access_controller->_users.swap(replica_users); + } + + std::unique_ptr _replica_access_controller; +}; + +TEST_F(replica_access_controller_test, allowed) +{ + struct + { + std::unordered_set replica_users; + std::string client_user; + bool result; + } tests[] = {{{"replica_user1", "replica_user2"}, "replica_user1", true}, + {{"replica_user1", "replica_user2"}, "not_replica_user", false}, + {{}, "user_name", false}}; + + bool origin_enable_acl = FLAGS_enable_acl; + FLAGS_enable_acl = true; + + std::unique_ptr sim_net( + new tools::sim_network_provider(nullptr, nullptr)); + auto sim_session = sim_net->create_client_session(rpc_address("localhost", 10086)); + dsn::message_ptr msg = message_ex::create_request(RPC_CM_LIST_APPS); + msg->io_session = sim_session; + + for (auto &test : tests) { + set_replica_users(std::move(test.replica_users)); + sim_session->set_client_username(test.client_user); + + ASSERT_EQ(allowed(msg), test.result); + } + + FLAGS_enable_acl = origin_enable_acl; +} +} // namespace security +} // namespace dsn