Skip to content

Commit 0d29977

Browse files
attilabukorhaohaoc
authored andcommitted
KUDU-2972 Add Ranger authorization provider
Adds a new authz_provider implementation that uses Ranger and plugs it in to catalog_manager. As of this patch this is considered experimental and an experimental flag is needed to be set to enable it. Change-Id: I6e7672a5947d6406e0cad83a0c900bf5b2c03012 Reviewed-on: http://gerrit.cloudera.org:8080/15207 Tested-by: Kudu Jenkins Reviewed-by: Adar Dembo <adar@cloudera.com> Reviewed-by: Hao Hao <hao.hao@cloudera.com>
1 parent 6f01667 commit 0d29977

File tree

5 files changed

+317
-2
lines changed

5 files changed

+317
-2
lines changed

src/kudu/master/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(MASTER_SRCS
4545
master_service.cc
4646
mini_master.cc
4747
placement_policy.cc
48+
ranger_authz_provider.cc
4849
sentry_authz_provider.cc
4950
sentry_client_metrics.cc
5051
sentry_privileges_cache_metrics.cc
@@ -62,7 +63,9 @@ target_link_libraries(master
6263
kserver
6364
kudu_common
6465
kudu_hms
66+
kudu_ranger
6567
kudu_sentry
68+
kudu_subprocess
6669
kudu_thrift
6770
kudu_util
6871
master_proto

src/kudu/master/catalog_manager.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#include "kudu/master/master.pb.h"
107107
#include "kudu/master/master_cert_authority.h"
108108
#include "kudu/master/placement_policy.h"
109+
#include "kudu/master/ranger_authz_provider.h"
109110
#include "kudu/master/sentry_authz_provider.h"
110111
#include "kudu/master/sys_catalog.h"
111112
#include "kudu/master/table_metrics.h"
@@ -133,6 +134,7 @@
133134
#include "kudu/util/debug/trace_event.h"
134135
#include "kudu/util/fault_injection.h"
135136
#include "kudu/util/flag_tags.h"
137+
#include "kudu/util/flag_validators.h"
136138
#include "kudu/util/logging.h"
137139
#include "kudu/util/metrics.h"
138140
#include "kudu/util/monotime.h"
@@ -312,6 +314,20 @@ DECLARE_int64(tsk_rotation_seconds);
312314

313315
METRIC_DEFINE_entity(table);
314316

317+
DECLARE_string(sentry_service_rpc_addresses);
318+
DECLARE_string(ranger_config_path);
319+
320+
static bool ValidateRangerSentryFlags() {
321+
if (!FLAGS_sentry_service_rpc_addresses.empty() &&
322+
!FLAGS_ranger_config_path.empty()) {
323+
LOG(ERROR) << "--sentry_service_rpc_addresses and --ranger_config_path "
324+
"cannot be set at the same time.";
325+
return false;
326+
}
327+
return true;
328+
}
329+
GROUP_FLAG_VALIDATOR(ranger_sentry_flags, ValidateRangerSentryFlags);
330+
315331
using base::subtle::NoBarrier_CompareAndSwap;
316332
using base::subtle::NoBarrier_Load;
317333
using boost::make_optional;
@@ -752,6 +768,8 @@ CatalogManager::CatalogManager(Master* master)
752768
leader_lock_(RWMutex::Priority::PREFER_WRITING) {
753769
if (SentryAuthzProvider::IsEnabled()) {
754770
authz_provider_.reset(new SentryAuthzProvider(master_->metric_entity()));
771+
} else if (RangerAuthzProvider::IsEnabled()) {
772+
authz_provider_.reset(new RangerAuthzProvider(master_->metric_entity()));
755773
} else {
756774
authz_provider_.reset(new DefaultAuthzProvider);
757775
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#include "kudu/master/ranger_authz_provider.h"
19+
20+
#include <algorithm>
21+
#include <ostream>
22+
23+
#include <gflags/gflags.h>
24+
#include <glog/logging.h>
25+
26+
#include "kudu/common/common.pb.h"
27+
#include "kudu/gutil/map-util.h"
28+
#include "kudu/ranger/ranger.pb.h"
29+
#include "kudu/security/token.pb.h"
30+
#include "kudu/util/status.h"
31+
32+
DECLARE_string(ranger_config_path);
33+
34+
namespace kudu {
35+
class MetricEntity;
36+
namespace master {
37+
38+
using kudu::security::ColumnPrivilegePB;
39+
using kudu::security::TablePrivilegePB;
40+
using kudu::ranger::ActionPB;
41+
using kudu::ranger::ActionHash;
42+
using std::string;
43+
using std::unordered_set;
44+
45+
RangerAuthzProvider::RangerAuthzProvider(const scoped_refptr<MetricEntity>& metric_entity) :
46+
client_(metric_entity) {}
47+
48+
Status RangerAuthzProvider::Start() {
49+
RETURN_NOT_OK(client_.Start());
50+
51+
return Status::OK();
52+
}
53+
54+
Status RangerAuthzProvider::AuthorizeCreateTable(const string& table_name,
55+
const string& user,
56+
const string& /*owner*/) {
57+
if (IsTrustedUser(user)) {
58+
return Status::OK();
59+
}
60+
return client_.AuthorizeAction(user, ActionPB::CREATE, table_name);
61+
}
62+
63+
Status RangerAuthzProvider::AuthorizeDropTable(const string& table_name,
64+
const string& user) {
65+
if (IsTrustedUser(user)) {
66+
return Status::OK();
67+
}
68+
return client_.AuthorizeAction(user, ActionPB::DROP, table_name);
69+
}
70+
71+
Status RangerAuthzProvider::AuthorizeAlterTable(const string& old_table,
72+
const string& new_table,
73+
const string& user) {
74+
if (IsTrustedUser(user)) {
75+
return Status::OK();
76+
}
77+
// Table alteration requires ALTER ON TABLE if no rename is done. To prevent
78+
// privilege escalation we require ALL on the old table and CREATE on the new
79+
// table (database).
80+
if (old_table == new_table) {
81+
return client_.AuthorizeAction(user, ActionPB::ALTER, old_table);
82+
}
83+
84+
RETURN_NOT_OK(client_.AuthorizeAction(user, ActionPB::ALL, old_table));
85+
return client_.AuthorizeAction(user, ActionPB::CREATE, new_table);
86+
}
87+
88+
Status RangerAuthzProvider::AuthorizeGetTableMetadata(const string& table_name,
89+
const string& user) {
90+
if (IsTrustedUser(user)) {
91+
return Status::OK();
92+
}
93+
return client_.AuthorizeAction(user, ActionPB::METADATA, table_name);
94+
}
95+
96+
Status RangerAuthzProvider::AuthorizeListTables(const string& user,
97+
unordered_set<string>* table_names,
98+
bool* checked_table_names) {
99+
if (IsTrustedUser(user)) {
100+
*checked_table_names = false;
101+
return Status::OK();
102+
}
103+
104+
*checked_table_names = true;
105+
return client_.AuthorizeActionMultipleTables(user, ActionPB::METADATA, table_names);
106+
}
107+
108+
Status RangerAuthzProvider::AuthorizeGetTableStatistics(const string& table_name,
109+
const string& user) {
110+
if (IsTrustedUser(user)) {
111+
return Status::OK();
112+
}
113+
// Statistics contain data (e.g. number of rows) that requires the 'SELECT ON TABLE'
114+
// privilege.
115+
return client_.AuthorizeAction(user, ActionPB::SELECT, table_name);
116+
}
117+
118+
Status RangerAuthzProvider::FillTablePrivilegePB(const string& table_name,
119+
const string& user,
120+
const SchemaPB& schema_pb,
121+
TablePrivilegePB* pb) {
122+
DCHECK(pb);
123+
DCHECK(pb->has_table_id());
124+
if (IsTrustedUser(user) || client_.AuthorizeAction(user, ActionPB::ALL, table_name).ok()) {
125+
pb->set_delete_privilege(true);
126+
pb->set_insert_privilege(true);
127+
pb->set_scan_privilege(true);
128+
pb->set_update_privilege(true);
129+
return Status::OK();
130+
}
131+
132+
unordered_set<ActionPB, ActionHash> actions = {
133+
ActionPB::DELETE,
134+
ActionPB::INSERT,
135+
ActionPB::UPDATE,
136+
ActionPB::SELECT
137+
};
138+
139+
// Check if the user has any table-level privileges. If yes, we set them. If
140+
// select is included, we can also return.
141+
auto s = client_.AuthorizeActions(user, table_name, &actions);
142+
if (s.ok()) {
143+
for (const ActionPB& action : actions) {
144+
switch (action) {
145+
case ActionPB::DELETE:
146+
pb->set_delete_privilege(true);
147+
break;
148+
case ActionPB::UPDATE:
149+
pb->set_update_privilege(true);
150+
break;
151+
case ActionPB::INSERT:
152+
pb->set_insert_privilege(true);
153+
break;
154+
case ActionPB::SELECT:
155+
pb->set_scan_privilege(true);
156+
break;
157+
default:
158+
LOG(WARNING) << "Unexpected action returned by Ranger: " << ActionPB_Name(action);
159+
break;
160+
}
161+
if (pb->scan_privilege()) {
162+
return Status::OK();
163+
}
164+
}
165+
}
166+
167+
// If select is not allowed on the table level we need to dig in and set
168+
// select permissions on the column level.
169+
static ColumnPrivilegePB scan_col_privilege;
170+
scan_col_privilege.set_scan_privilege(true);
171+
172+
unordered_set<string> column_names;
173+
for (const auto& col : schema_pb.columns()) {
174+
column_names.emplace(col.name());
175+
}
176+
177+
// If AuthorizeAction returns NotAuthorized, that means no column-level select
178+
// is allowed to the user. In this case we return the previous status.
179+
// Otherwise we populate schema_pb and return OK.
180+
//
181+
// TODO(abukor): revisit if it's worth merge this into the previous request
182+
if (!client_.AuthorizeActionMultipleColumns(user, ActionPB::SELECT, table_name,
183+
&column_names).ok()) {
184+
return s;
185+
}
186+
187+
for (const auto& col : schema_pb.columns()) {
188+
if (ContainsKey(column_names, col.name())) {
189+
InsertOrDie(pb->mutable_column_privileges(), col.id(), scan_col_privilege);
190+
}
191+
}
192+
193+
194+
return Status::OK();
195+
}
196+
197+
bool RangerAuthzProvider::IsEnabled() {
198+
return !FLAGS_ranger_config_path.empty();
199+
}
200+
201+
} // namespace master
202+
} // namespace kudu
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#pragma once
19+
20+
#include <string>
21+
#include <unordered_set>
22+
23+
#include "kudu/gutil/port.h"
24+
#include "kudu/gutil/ref_counted.h"
25+
#include "kudu/master/authz_provider.h"
26+
#include "kudu/ranger/ranger_client.h"
27+
#include "kudu/util/status.h"
28+
29+
namespace kudu {
30+
31+
class MetricEntity;
32+
class SchemaPB;
33+
34+
namespace security {
35+
class TablePrivilegePB;
36+
} // namespace security
37+
38+
namespace master {
39+
40+
// An implementation of AuthzProvider that connects to Ranger and translates
41+
// authorization requests to Ranger and allows or denies the actions based on
42+
// the received responses.
43+
class RangerAuthzProvider : public AuthzProvider {
44+
public:
45+
46+
explicit RangerAuthzProvider(const scoped_refptr<MetricEntity>& metric_entity);
47+
48+
Status Start() override;
49+
50+
void Stop() override {}
51+
52+
Status ResetCache() override {
53+
return Status::NotSupported("Resetting cache is not supported with Ranger");
54+
}
55+
56+
Status AuthorizeCreateTable(const std::string& table_name,
57+
const std::string& user,
58+
const std::string& owner) override WARN_UNUSED_RESULT;
59+
60+
Status AuthorizeDropTable(const std::string& table_name,
61+
const std::string& user) override WARN_UNUSED_RESULT;
62+
63+
Status AuthorizeAlterTable(const std::string& old_table,
64+
const std::string& new_table,
65+
const std::string& user) override WARN_UNUSED_RESULT;
66+
67+
Status AuthorizeGetTableMetadata(const std::string& table_name,
68+
const std::string& user) override WARN_UNUSED_RESULT;
69+
70+
Status AuthorizeListTables(const std::string& user,
71+
std::unordered_set<std::string>* table_names,
72+
bool* checked_table_names) override WARN_UNUSED_RESULT;
73+
74+
Status AuthorizeGetTableStatistics(const std::string& table_name,
75+
const std::string& user) override WARN_UNUSED_RESULT;
76+
77+
Status FillTablePrivilegePB(const std::string& table_name,
78+
const std::string& user,
79+
const SchemaPB& schema_pb,
80+
security::TablePrivilegePB* pb) override WARN_UNUSED_RESULT;
81+
82+
// Returns true if 'ranger_policy_server' flag is set indicating Ranger
83+
// authorization is enabled.
84+
static bool IsEnabled();
85+
86+
private:
87+
ranger::RangerClient client_;
88+
};
89+
90+
} // namespace master
91+
} // namespace kudu

src/kudu/master/sentry_authz_provider.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717

1818
#include "kudu/master/sentry_authz_provider.h"
1919

20+
#include <algorithm>
2021
#include <unordered_map>
2122
#include <unordered_set>
2223
#include <utility>
2324
#include <vector>
2425

2526
#include <gflags/gflags.h>
26-
#include <gflags/gflags_declare.h>
2727
#include <glog/logging.h>
2828

2929
#include "kudu/common/common.pb.h"
@@ -42,7 +42,8 @@
4242
DEFINE_bool(sentry_require_db_privileges_for_list_tables, false,
4343
"Whether Kudu will require database-level privileges to authorize "
4444
"ListTables requests. When set to false, table-level privileges are "
45-
"required for each table.");
45+
"required for each table. ranger_config_path must not be set if "
46+
"this is set");
4647
TAG_FLAG(sentry_require_db_privileges_for_list_tables, advanced);
4748

4849
DECLARE_string(sentry_service_rpc_addresses);

0 commit comments

Comments
 (0)