From 94f1b974c54868807d58ed39da800a20d0d5df47 Mon Sep 17 00:00:00 2001 From: Pritha Srivastava Date: Wed, 22 Jul 2020 21:51:25 +0530 Subject: [PATCH] rgw/sts: adding code for federated user as owner in case of STS. A new user under the namespace 'oidc' is created for every federated user in case of AssumeRoleWithWebIdentity. In case of AssumeRole, the user that needs cross account access becomes the owner. In both cases buckets are created in the tenant that the role belongs to. Signed-off-by: Pritha Srivastava --- src/rgw/rgw_admin.cc | 9 ++++++ src/rgw/rgw_auth.cc | 49 ++++++++++++++++++++++++++++-- src/rgw/rgw_auth.h | 23 ++++++++++---- src/rgw/rgw_auth_filters.h | 4 +++ src/rgw/rgw_basic_types.h | 50 +++++++++++++++++++++++++------ src/rgw/rgw_common.cc | 9 ++++-- src/rgw/rgw_op.cc | 23 ++++++++++---- src/rgw/rgw_rest_s3.cc | 4 +++ src/rgw/rgw_rest_sts.cc | 16 ++++++++-- src/rgw/rgw_rest_sts.h | 5 +++- src/rgw/rgw_sts.cc | 2 +- src/test/cli/radosgw-admin/help.t | 1 + 12 files changed, 163 insertions(+), 32 deletions(-) diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index c4ad92155b5af..d0bc930d5db68 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -279,6 +279,7 @@ void usage() cout << " subscription ack ack (remove) an events in a pubsub subscription\n"; cout << "options:\n"; cout << " --tenant= tenant name\n"; + cout << " --user_ns= namespace of user (oidc in case of users authenticated with oidc provider)\n"; cout << " --uid= user id\n"; cout << " --new-uid= new user id\n"; cout << " --subuser= subuser name\n"; @@ -3025,6 +3026,7 @@ int main(int argc, const char **argv) rgw_user user_id; string tenant; + string user_ns; rgw_user new_user_id; std::string access_key, secret_key, user_email, display_name; std::string bucket_name, pool_name, object; @@ -3230,6 +3232,8 @@ int main(int argc, const char **argv) } else if (ceph_argparse_witharg(args, i, &val, "--tenant", (char*)NULL)) { tenant = val; opt_tenant = val; + } else if (ceph_argparse_witharg(args, i, &val, "--user_ns", (char*)NULL)) { + user_ns = val; } else if (ceph_argparse_witharg(args, i, &val, "--access-key", (char*)NULL)) { access_key = val; } else if (ceph_argparse_witharg(args, i, &val, "--subuser", (char*)NULL)) { @@ -3708,6 +3712,11 @@ int main(int argc, const char **argv) } user_id.tenant = tenant; } + if (user_ns.empty()) { + user_ns = user_id.ns; + } else { + user_id.ns = user_ns; + } if (!new_user_id.empty() && !tenant.empty()) { new_user_id.tenant = tenant; diff --git a/src/rgw/rgw_auth.cc b/src/rgw/rgw_auth.cc index 60b01d8588efb..ac9c5c5d01b9e 100644 --- a/src/rgw/rgw_auth.cc +++ b/src/rgw/rgw_auth.cc @@ -355,6 +355,44 @@ string rgw::auth::WebIdentityApplier::get_idp_url() const return idp_url; } +void rgw::auth::WebIdentityApplier::create_account(const DoutPrefixProvider* dpp, + const rgw_user& acct_user, + const string& display_name, + RGWUserInfo& user_info) const /* out */ +{ + user_info.user_id = acct_user; + user_info.display_name = display_name; + user_info.type = TYPE_WEB; + + user_info.max_buckets = + cct->_conf.get_val("rgw_user_max_buckets"); + rgw_apply_default_bucket_quota(user_info.bucket_quota, cct->_conf); + rgw_apply_default_user_quota(user_info.user_quota, cct->_conf); + + int ret = ctl->user->store_info(user_info, null_yield, + RGWUserCtl::PutParams().set_exclusive(true)); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user=" + << user_info.user_id << " ret=" << ret << dendl; + throw ret; + } +} + +void rgw::auth::WebIdentityApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const { + rgw_user federated_user; + federated_user.id = token_claims.sub; + federated_user.tenant = role_tenant; + federated_user.ns = "oidc"; + + if (ctl->user->get_info_by_uid(federated_user, &user_info, null_yield) >= 0) { + /* Succeeded. */ + return; + } + + ldpp_dout(dpp, 0) << "NOTICE: couldn't map oidc federated user " << federated_user << dendl; + create_account(dpp, federated_user, token_claims.user_name, user_info); +} + void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const { s->info.args.append("sub", token_claims.sub); @@ -698,7 +736,14 @@ bool rgw::auth::RoleApplier::is_identity(const idset_t& ids) const { } } else { string id = p.get_id(); - if (user_id.id == id) { + string tenant = p.get_tenant(); + string oidc_id; + if (user_id.ns.empty()) { + oidc_id = user_id.id; + } else { + oidc_id = user_id.ns + "$" + user_id.id; + } + if (oidc_id == id && user_id.tenant == tenant) { return true; } } @@ -710,8 +755,6 @@ void rgw::auth::RoleApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUs { /* Load the user id */ user_info.user_id = this->user_id; - - user_info.user_id.tenant = role.tenant; } void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const diff --git a/src/rgw/rgw_auth.h b/src/rgw/rgw_auth.h index 3d841213a492e..0ede174e01491 100644 --- a/src/rgw/rgw_auth.h +++ b/src/rgw/rgw_auth.h @@ -79,6 +79,8 @@ public: /* Subuser of Account */ virtual string get_subuser() const = 0; + + virtual string get_role_tenant() const { return ""; } }; inline std::ostream& operator<<(std::ostream& out, @@ -366,26 +368,28 @@ protected: CephContext* const cct; RGWCtl* const ctl; string role_session; + string role_tenant; rgw::web_idp::WebTokenClaims token_claims; string get_idp_url() const; + void create_account(const DoutPrefixProvider* dpp, + const rgw_user& acct_user, + const string& display_name, + RGWUserInfo& user_info) const; /* out */ public: WebIdentityApplier( CephContext* const cct, RGWCtl* const ctl, const string& role_session, + const string& role_tenant, const rgw::web_idp::WebTokenClaims& token_claims) : cct(cct), ctl(ctl), role_session(role_session), + role_tenant(role_tenant), token_claims(token_claims) { } - void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override { - user_info.user_id = rgw_user(token_claims.sub); - user_info.display_name = token_claims.user_name; - } - void modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const override; uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override { @@ -397,6 +401,9 @@ public: } bool is_owner_of(const rgw_user& uid) const override { + if (uid.id == token_claims.sub && uid.tenant == role_tenant && uid.ns == "oidc") { + return true; + } return false; } @@ -408,6 +415,8 @@ public: bool is_identity(const idset_t& ids) const override; + void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override; + uint32_t get_identity_type() const override { return TYPE_WEB; } @@ -426,6 +435,7 @@ public: virtual aplptr_t create_apl_web_identity( CephContext* cct, const req_state* s, const string& role_session, + const string& role_tenant, const rgw::web_idp::WebTokenClaims& token) const = 0; }; }; @@ -661,7 +671,7 @@ public: return false; } bool is_owner_of(const rgw_user& uid) const override { - return false; + return (this->user_id.id == uid.id && this->user_id.tenant == uid.tenant && this->user_id.ns == this->user_id.ns); } bool is_identity(const idset_t& ids) const override; uint32_t get_perm_mask() const override { @@ -673,6 +683,7 @@ public: string get_acct_name() const override { return {}; } string get_subuser() const override { return {}; } void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const override; + string get_role_tenant() const override { return role.tenant; } struct Factory { virtual ~Factory() {} diff --git a/src/rgw/rgw_auth_filters.h b/src/rgw/rgw_auth_filters.h index 9dbcf677ac0ad..f4b70b88feb83 100644 --- a/src/rgw/rgw_auth_filters.h +++ b/src/rgw/rgw_auth_filters.h @@ -105,6 +105,10 @@ public: get_decoratee().to_str(out); } + string get_role_tenant() const override { /* in/out */ + return get_decoratee().get_role_tenant(); + } + void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override { /* out */ return get_decoratee().load_acct_info(dpp, user_info); } diff --git a/src/rgw/rgw_basic_types.h b/src/rgw/rgw_basic_types.h index 62808b29e377a..81094fe569dcf 100644 --- a/src/rgw/rgw_basic_types.h +++ b/src/rgw/rgw_basic_types.h @@ -14,36 +14,49 @@ class cls_user_bucket; struct rgw_user { std::string tenant; std::string id; + std::string ns; rgw_user() {} explicit rgw_user(const std::string& s) { from_str(s); } - rgw_user(const std::string& tenant, const std::string& id) + rgw_user(const std::string& tenant, const std::string& id, const std::string& ns="") : tenant(tenant), - id(id) { + id(id), + ns(ns) { } - rgw_user(std::string&& tenant, std::string&& id) + rgw_user(std::string&& tenant, std::string&& id, std::string&& ns="") : tenant(std::move(tenant)), - id(std::move(id)) { + id(std::move(id)), + ns(std::move(ns)) { } void encode(ceph::buffer::list& bl) const { - ENCODE_START(1, 1, bl); + ENCODE_START(2, 1, bl); encode(tenant, bl); encode(id, bl); + encode(ns, bl); ENCODE_FINISH(bl); } void decode(ceph::buffer::list::const_iterator& bl) { - DECODE_START(1, bl); + DECODE_START(2, bl); decode(tenant, bl); decode(id, bl); + if (struct_v >= 2) { + decode(ns, bl); + } DECODE_FINISH(bl); } void to_str(std::string& str) const { if (!tenant.empty()) { - str = tenant + '$' + id; + if (!ns.empty()) { + str = tenant + '$' + ns + '$' + id; + } else { + str = tenant + '$' + id; + } + } else if (!ns.empty()) { + str = '$' + ns + '$' + id; } else { str = id; } @@ -52,6 +65,7 @@ struct rgw_user { void clear() { tenant.clear(); id.clear(); + ns.clear(); } bool empty() const { @@ -68,9 +82,19 @@ struct rgw_user { size_t pos = str.find('$'); if (pos != std::string::npos) { tenant = str.substr(0, pos); - id = str.substr(pos + 1); + string_view sv = str; + string_view ns_id = sv.substr(pos + 1); + size_t ns_pos = ns_id.find('$'); + if (ns_pos != std::string::npos) { + ns = string(ns_id.substr(0, ns_pos)); + id = string(ns_id.substr(ns_pos + 1)); + } else { + ns.clear(); + id = string(ns_id); + } } else { tenant.clear(); + ns.clear(); id = str; } } @@ -84,7 +108,10 @@ struct rgw_user { int r = tenant.compare(u.tenant); if (r != 0) return r; - + r = ns.compare(u.ns); + if (r != 0) { + return r; + } return id.compare(u.id); } int compare(const std::string& str) const { @@ -104,6 +131,11 @@ struct rgw_user { } else if (tenant > rhs.tenant) { return false; } + if (ns < rhs.ns) { + return true; + } else if (ns > rhs.ns) { + return false; + } return (id < rhs.id); } void dump(ceph::Formatter *f) const; diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 9c49e0708b6ef..5e8da7dbc483a 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -2035,7 +2035,7 @@ RGWBucketInfo::~RGWBucketInfo() } void RGWBucketInfo::encode(bufferlist& bl) const { - ENCODE_START(22, 4, bl); + ENCODE_START(23, 4, bl); encode(bucket, bl); encode(owner.id, bl); encode(flags, bl); @@ -2068,11 +2068,12 @@ void RGWBucketInfo::encode(bufferlist& bl) const { encode(*sync_policy, bl); } encode(layout, bl); + encode(owner.ns, bl); ENCODE_FINISH(bl); } void RGWBucketInfo::decode(bufferlist::const_iterator& bl) { - DECODE_START_LEGACY_COMPAT_LEN_32(22, 4, 4, bl); + DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl); decode(bucket, bl); if (struct_v >= 2) { string s; @@ -2146,7 +2147,9 @@ void RGWBucketInfo::decode(bufferlist::const_iterator& bl) { if (struct_v >= 22) { decode(layout, bl); } - + if (struct_v >= 23) { + decode(owner.ns, bl); + } DECODE_FINISH(bl); } diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 07cf934c58e64..f7cc9069a233c 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -2271,7 +2271,14 @@ int RGWListBuckets::verify_permission() rgw::Partition partition = rgw::Partition::aws; rgw::Service service = rgw::Service::s3; - if (!verify_user_permission(this, s, ARN(partition, service, "", s->user->get_tenant(), "*"), rgw::IAM::s3ListAllMyBuckets)) { + string tenant; + if (s->auth.identity->get_identity_type() == TYPE_ROLE) { + tenant = s->auth.identity->get_role_tenant(); + } else { + tenant = s->user->get_tenant(); + } + + if (!verify_user_permission(this, s, ARN(partition, service, "", tenant, "*"), rgw::IAM::s3ListAllMyBuckets)) { return -EACCES; } @@ -2843,12 +2850,16 @@ int RGWCreateBucket::verify_permission() } if (s->user->get_tenant() != s->bucket_tenant) { - ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant" - << " (user_id.tenant=" << s->user->get_tenant() - << " requested=" << s->bucket_tenant << ")" - << dendl; - return -EACCES; + //AssumeRole is meant for cross account access + if (s->auth.identity->get_identity_type() != TYPE_ROLE) { + ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant" + << " (user_id.tenant=" << s->user->get_tenant() + << " requested=" << s->bucket_tenant << ")" + << dendl; + return -EACCES; + } } + if (s->user->get_max_buckets() < 0) { return -EPERM; } diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index add055ffa9ca0..b15b213d2173b 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -4644,6 +4644,10 @@ int RGWHandler_REST_S3::postauth_init() rgw_parse_url_bucket(t->url_bucket, s->user->get_tenant(), s->bucket_tenant, s->bucket_name); + if (s->auth.identity->get_identity_type() == TYPE_ROLE) { + s->bucket_tenant = s->auth.identity->get_role_tenant(); + } + dout(10) << "s->object=" << s->object << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl; diff --git a/src/rgw/rgw_rest_sts.cc b/src/rgw/rgw_rest_sts.cc index 0e5c7b76d92d1..0f184b5c843c9 100644 --- a/src/rgw/rgw_rest_sts.cc +++ b/src/rgw/rgw_rest_sts.cc @@ -49,14 +49,22 @@ WebTokenEngine::is_applicable(const std::string& token) const noexcept return ! token.empty(); } -boost::optional -WebTokenEngine::get_provider(const string& role_arn, const string& iss) const +std::string +WebTokenEngine::get_role_tenant(const string& role_arn) const { string tenant; auto r_arn = rgw::ARN::parse(role_arn); if (r_arn) { tenant = r_arn->account; } + return tenant; +} + +boost::optional +WebTokenEngine::get_provider(const string& role_arn, const string& iss) const +{ + string tenant = get_role_tenant(role_arn); + string idp_url = iss; auto pos = idp_url.find("http://"); if (pos == std::string::npos) { @@ -333,7 +341,9 @@ WebTokenEngine::authenticate( const DoutPrefixProvider* dpp, if (role_session.empty()) { return result_t::deny(-EACCES); } - auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, *t); + string role_arn = s->info.args.get("RoleArn"); + string role_tenant = get_role_tenant(role_arn); + auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, role_tenant, *t); return result_t::grant(std::move(apl)); } return result_t::deny(-EACCES); diff --git a/src/rgw/rgw_rest_sts.h b/src/rgw/rgw_rest_sts.h index 4845010d650da..faf9bcd6062f8 100644 --- a/src/rgw/rgw_rest_sts.h +++ b/src/rgw/rgw_rest_sts.h @@ -30,6 +30,8 @@ class WebTokenEngine : public rgw::auth::Engine { boost::optional get_provider(const string& role_arn, const string& iss) const; + std::string get_role_tenant(const string& role_arn) const; + boost::optional get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s) const; @@ -78,9 +80,10 @@ class DefaultStrategy : public rgw::auth::Strategy, aplptr_t create_apl_web_identity( CephContext* cct, const req_state* s, const string& role_session, + const string& role_tenant, const rgw::web_idp::WebTokenClaims& token) const override { auto apl = rgw::auth::add_sysreq(cct, ctl, s, - rgw::auth::WebIdentityApplier(cct, ctl, role_session, token)); + rgw::auth::WebIdentityApplier(cct, ctl, role_session, role_tenant, token)); return aplptr_t(new decltype(apl)(std::move(apl))); } diff --git a/src/rgw/rgw_sts.cc b/src/rgw/rgw_sts.cc index 2415f59fac3f4..9550b2ba5f036 100644 --- a/src/rgw/rgw_sts.cc +++ b/src/rgw/rgw_sts.cc @@ -105,7 +105,7 @@ int Credentials::generateCredentials(CephContext* cct, if (user) token.user = *user; else { - rgw_user u({}, {}); + rgw_user u({}, {}, {}); token.user = u; } diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index c22230627593d..286aa6ebcc855 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -174,6 +174,7 @@ subscription ack ack (remove) an events in a pubsub subscription options: --tenant= tenant name + --user_ns= namespace of user (oidc in case of users authenticated with oidc provider) --uid= user id --new-uid= new user id --subuser= subuser name -- 2.39.5