]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/sts: adding code for federated user as owner in case of STS.
authorPritha Srivastava <prsrivas@redhat.com>
Wed, 22 Jul 2020 16:21:25 +0000 (21:51 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Tue, 15 Sep 2020 16:09:00 +0000 (21:39 +0530)
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 <prsrivas@redhat.com>
12 files changed:
src/rgw/rgw_admin.cc
src/rgw/rgw_auth.cc
src/rgw/rgw_auth.h
src/rgw/rgw_auth_filters.h
src/rgw/rgw_basic_types.h
src/rgw/rgw_common.cc
src/rgw/rgw_op.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_sts.cc
src/rgw/rgw_rest_sts.h
src/rgw/rgw_sts.cc
src/test/cli/radosgw-admin/help.t

index c4ad92155b5afd78865870ba9150e1868ace259c..d0bc930d5db68c4e450ee78b13cd9adf79f092d7 100644 (file)
@@ -279,6 +279,7 @@ void usage()
   cout << "  subscription ack           ack (remove) an events in a pubsub subscription\n";
   cout << "options:\n";
   cout << "   --tenant=<tenant>         tenant name\n";
+  cout << "   --user_ns=<namespace>     namespace of user (oidc in case of users authenticated with oidc provider)\n";
   cout << "   --uid=<id>                user id\n";
   cout << "   --new-uid=<id>            new user id\n";
   cout << "   --subuser=<name>          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;
index 60b01d8588efb2df119bccba14582915827f76e7..ac9c5c5d01b9eef26bca202897c8ac649d903c5c 100644 (file)
@@ -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<int64_t>("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
index 3d841213a492eb524d7fb75c8267276101dff34a..0ede174e014911cd8ce4f8793ad5f3abb43fa0c9 100644 (file)
@@ -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() {}
index 9dbcf677ac0ada63e85631b3ecb8601637c037a9..f4b70b88feb8357e56c0371029584e7a14ca4c33 100644 (file)
@@ -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);
   }
index 62808b29e377a86127d715d3363e69f834a75afe..81094fe569dcf894c6dc344b1427631b3a209103 100644 (file)
@@ -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;
index 9c49e0708b6ef9b05777cfa22665e9615866a4a3..5e8da7dbc483a42d19385941e8b05a0f05e7cd20 100644 (file)
@@ -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);
 }
 
index 07cf934c58e640898df0675b373854347af630b5..f7cc9069a233c4d1c64cfc2f9ca89d73e81c805f 100644 (file)
@@ -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;
   }
index add055ffa9ca0a041db0d77d8d761ce30bfa820e..b15b213d2173b3a52dc0ad85b7862f16485f5763 100644 (file)
@@ -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;
 
index 0e5c7b76d92d17c27de03d195b5ad8f5eb6c1519..0f184b5c843c9ac8e5d68956c57d9b657aa6d03f 100644 (file)
@@ -49,14 +49,22 @@ WebTokenEngine::is_applicable(const std::string& token) const noexcept
   return ! token.empty();
 }
 
-boost::optional<RGWOIDCProvider>
-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<RGWOIDCProvider>
+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);
index 4845010d650daade6bf189ddde1234c4c5121821..faf9bcd6062f8e39f1a1e23e7772dc740369091a 100644 (file)
@@ -30,6 +30,8 @@ class WebTokenEngine : public rgw::auth::Engine {
 
   boost::optional<RGWOIDCProvider> get_provider(const string& role_arn, const string& iss) const;
 
+  std::string get_role_tenant(const string& role_arn) const;
+
   boost::optional<WebTokenEngine::token_t>
   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)));
   }
 
index 2415f59fac3f4b0fe0bdbaa85a5bbec0c82717a3..9550b2ba5f036130f6f15aae6301eeb159940a95 100644 (file)
@@ -105,7 +105,7 @@ int Credentials::generateCredentials(CephContext* cct,
   if (user)
     token.user = *user;
   else {
-    rgw_user u({}, {});
+    rgw_user u({}, {}, {});
     token.user = u;
   }
 
index c22230627593d296440496eaf2385c0b324753fe..286aa6ebcc855f51f0de1f398c01008c4b6bfb0c 100644 (file)
     subscription ack           ack (remove) an events in a pubsub subscription
   options:
      --tenant=<tenant>         tenant name
+     --user_ns=<namespace>     namespace of user (oidc in case of users authenticated with oidc provider)
      --uid=<id>                user id
      --new-uid=<id>            new user id
      --subuser=<name>          subuser name