Merge pull request #53915 from pritha-srivastava/wip-rgw-sts-update-oidc-provider

rgw/iam: add AddClientIdToOIDCProvider/UpdateOidcProviderThumbprint

Reviewed-by: Matt Benjamin <mbenjamin@redhat.com>
Reviewed-by: Casey Bodley <cbodley@redhat.com>
This commit is contained in:
Casey Bodley 2024-08-28 15:07:21 -04:00 committed by GitHub
commit 3e26494df4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 235 additions and 5 deletions

View File

@ -45,7 +45,7 @@ Example::
POST "<hostname>?Action=Action=CreateOpenIDConnectProvider
&ThumbprintList.list.1=F7D7B3515DD0D319DD219A43A9EA727AD6065287
&ClientIDList.list.1=app-profile-jsp
&Url=http://localhost:8080/auth/realms/quickstart
&Url=http://localhost:8080/auth/realms/quickstart"
DeleteOpenIDConnectProvider
@ -63,7 +63,7 @@ Request Parameters
Example::
POST "<hostname>?Action=Action=DeleteOpenIDConnectProvider
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart"
GetOpenIDConnectProvider
@ -81,7 +81,7 @@ Request Parameters
Example::
POST "<hostname>?Action=Action=GetOpenIDConnectProvider
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart"
ListOpenIDConnectProviders
--------------------------
@ -95,3 +95,50 @@ None
Example::
POST "<hostname>?Action=Action=ListOpenIDConnectProviders
AddClientIDToOpenIDConnectProvider
----------------------------------
Add a client id to the list of existing client ids registered while creating an OpenIDConnectProvider.
Request Parameters
~~~~~~~~~~~~~~~~~~
``OpenIDConnectProviderArn``
:Description: ARN of the IDP which is returned by the Create API.
:Type: String
``ClientID``
:Description: Client Id to add to the existing OpenIDConnectProvider.
:Type: String
Example::
POST "<hostname>?Action=Action=AddClientIDToOpenIDConnectProvider
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart
&ClientID=app-jee-jsp"
UpdateOpenIDConnectProviderThumbprint
-------------------------------------
Update the existing thumbprint list of an OpenIDConnectProvider with the given list.
This API removes the existing thumbprint list and replaces that with the input thumbprint list.
Request Parameters
~~~~~~~~~~~~~~~~~~
``OpenIDConnectProviderArn``
:Description: ARN of the IDP which is returned by the Create API.
:Type: String
``ThumbprintList.member.N``
:Description: List of OpenID Connect IDP's server certificates' thumbprints. A maximum of 5 thumbprints are allowed.
:Type: Array of Strings
Example::
POST "<hostname>?Action=Action=UpdateOpenIDConnectProviderThumbprint
&OpenIDConnectProviderArn=arn:aws:iam:::oidc-provider/localhost:8080/auth/realms/quickstart
&&ThumbprintList.list.1=ABCDB3515DD0D319DD219A43A9EA727AD6061234"

View File

@ -495,6 +495,8 @@ bool is_non_s3_op(RGWOpType op_type)
case RGW_OP_DELETE_OIDC_PROVIDER:
case RGW_OP_GET_OIDC_PROVIDER:
case RGW_OP_LIST_OIDC_PROVIDERS:
case RGW_OP_ADD_CLIENTID_TO_OIDC_PROVIDER:
case RGW_OP_UPDATE_OIDC_PROVIDER_THUMBPRINT:
case RGW_OP_PUBSUB_TOPIC_CREATE:
case RGW_OP_PUBSUB_TOPICS_LIST:
case RGW_OP_PUBSUB_TOPIC_GET:

View File

@ -160,6 +160,8 @@ static const actpair actpairs[] =
{ "iam:DeleteOIDCProvider", iamDeleteOIDCProvider},
{ "iam:GetOIDCProvider", iamGetOIDCProvider},
{ "iam:ListOIDCProviders", iamListOIDCProviders},
{ "iam:AddClientIdToOIDCProvider", iamAddClientIdToOIDCProvider},
{ "iam:UpdateOIDCProviderThumbprint", iamUpdateOIDCProviderThumbprint},
{ "iam:TagRole", iamTagRole},
{ "iam:ListRoleTags", iamListRoleTags},
{ "iam:UntagRole", iamUntagRole},
@ -1550,6 +1552,12 @@ const char* action_bit_string(uint64_t action) {
case iamListOIDCProviders:
return "iam:ListOIDCProviders";
case iamAddClientIdToOIDCProvider:
return "iam:AddClientIdToOIDCProvider";
case iamUpdateOIDCProviderThumbprint:
return "iam:UpdateOIDCProviderThumbprint";
case iamTagRole:
return "iam:TagRole";

View File

@ -143,6 +143,8 @@ enum {
iamDeleteOIDCProvider,
iamGetOIDCProvider,
iamListOIDCProviders,
iamAddClientIdToOIDCProvider,
iamUpdateOIDCProviderThumbprint,
iamTagRole,
iamListRoleTags,
iamUntagRole,

View File

@ -161,5 +161,7 @@ enum RGWOpType {
RGW_OP_DELETE_OIDC_PROVIDER,
RGW_OP_GET_OIDC_PROVIDER,
RGW_OP_LIST_OIDC_PROVIDERS,
RGW_OP_ADD_CLIENTID_TO_OIDC_PROVIDER,
RGW_OP_UPDATE_OIDC_PROVIDER_THUMBPRINT,
};

View File

@ -45,6 +45,8 @@ static const std::unordered_map<std::string_view, op_generator> op_generators =
{"ListOpenIDConnectProviders", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListOIDCProviders;}},
{"GetOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWGetOIDCProvider;}},
{"DeleteOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWDeleteOIDCProvider;}},
{"AddClientIDToOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWAddClientIdToOIDCProvider;}},
{"UpdateOpenIDConnectProviderThumbprint", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWUpdateOIDCProviderThumbprint;}},
{"TagRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWTagRole(bl_post_body);}},
{"ListRoleTags", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListRoleTags;}},
{"UntagRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWUntagRole(bl_post_body);}},

View File

@ -333,3 +333,147 @@ void RGWListOIDCProviders::execute(optional_yield y)
s->formatter->close_section();
}
}
RGWAddClientIdToOIDCProvider::RGWAddClientIdToOIDCProvider()
: RGWRestOIDCProvider(rgw::IAM::iamAddClientIdToOIDCProvider, RGW_CAP_WRITE)
{
}
int RGWAddClientIdToOIDCProvider::init_processing(optional_yield y)
{
std::string_view account;
if (const auto& acc = s->auth.identity->get_account(); acc) {
account = acc->id;
} else {
account = s->user->get_tenant();
}
std::string provider_arn = s->info.args.get("OpenIDConnectProviderArn");
auto ret = validate_provider_arn(provider_arn, account,
resource, url, s->err.message);
if (ret < 0) {
return ret;
}
client_id = s->info.args.get("ClientID");
if (client_id.empty()) {
s->err.message = "Missing required element ClientID";
ldpp_dout(this, 20) << "ERROR: ClientID is empty" << dendl;
return -EINVAL;
}
if (client_id.size() > MAX_OIDC_CLIENT_ID_LEN) {
s->err.message = "ClientID cannot exceed the maximum length of "
+ std::to_string(MAX_OIDC_CLIENT_ID_LEN);
ldpp_dout(this, 20) << "ERROR: ClientID length exceeded " << MAX_OIDC_CLIENT_ID_LEN << dendl;
return -EINVAL;
}
return 0;
}
void RGWAddClientIdToOIDCProvider::execute(optional_yield y)
{
RGWOIDCProviderInfo info;
op_ret = driver->load_oidc_provider(this, y, resource.account, url, info);
if (op_ret < 0) {
if (op_ret != -ENOENT && op_ret != -EINVAL) {
op_ret = ERR_INTERNAL_ERROR;
}
return;
}
if(std::find(info.client_ids.begin(), info.client_ids.end(), client_id) != info.client_ids.end()) {
op_ret = -EEXIST;
} else {
info.client_ids.emplace_back(client_id);
constexpr bool exclusive = false;
op_ret = driver->store_oidc_provider(this, y, info, exclusive);
}
if (op_ret == 0 || op_ret == -EEXIST) {
op_ret = 0;
s->formatter->open_object_section("AddClientIDToOpenIDConnectProviderResponse");
s->formatter->open_object_section("ResponseMetadata");
s->formatter->dump_string("RequestId", s->trans_id);
s->formatter->close_section();
s->formatter->open_object_section("AddClientIDToOpenIDConnectProviderResponse");
dump_oidc_provider(info, s->formatter);
s->formatter->close_section();
s->formatter->close_section();
}
}
RGWUpdateOIDCProviderThumbprint::RGWUpdateOIDCProviderThumbprint()
: RGWRestOIDCProvider(rgw::IAM::iamUpdateOIDCProviderThumbprint, RGW_CAP_WRITE)
{
}
int RGWUpdateOIDCProviderThumbprint::init_processing(optional_yield y)
{
std::string_view account;
if (const auto& acc = s->auth.identity->get_account(); acc) {
account = acc->id;
} else {
account = s->user->get_tenant();
}
std::string provider_arn = s->info.args.get("OpenIDConnectProviderArn");
auto ret = validate_provider_arn(provider_arn, account,
resource, url, s->err.message);
if (ret < 0) {
return ret;
}
auto val_map = s->info.args.get_params();
/* From AWS documentation here: https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateOpenIDConnectProviderThumbprint.html
The list that you pass with this operation completely replaces the existing list of thumbprints. (The lists are not merged.) */
for (auto& it : val_map) {
if (it.first.find("ThumbprintList.member.") != string::npos) {
if (it.second.size() > MAX_OIDC_THUMBPRINT_LEN) {
s->err.message = "Thumbprint cannot exceed the maximum length of "
+ std::to_string(MAX_OIDC_THUMBPRINT_LEN);
ldpp_dout(this, 20) << "ERROR: Thumbprint exceeds maximum length of " << MAX_OIDC_THUMBPRINT_LEN << dendl;
return -EINVAL;
}
thumbprints.emplace_back(it.second);
}
}
if (thumbprints.empty()) {
s->err.message = "Missing required element ThumbprintList";
ldpp_dout(this, 20) << "ERROR: Thumbprints list is empty" << dendl;
return -EINVAL;
}
return 0;
}
void RGWUpdateOIDCProviderThumbprint::execute(optional_yield y)
{
RGWOIDCProviderInfo info;
op_ret = driver->load_oidc_provider(this, y, resource.account, url, info);
if (op_ret < 0) {
if (op_ret != -ENOENT && op_ret != -EINVAL) {
op_ret = ERR_INTERNAL_ERROR;
}
return;
}
info.thumbprints = std::move(thumbprints);
constexpr bool exclusive = false;
op_ret = driver->store_oidc_provider(this, y, info, exclusive);
if (op_ret == 0) {
s->formatter->open_object_section("AddClientIDToOpenIDConnectProviderResponse");
s->formatter->open_object_section("ResponseMetadata");
s->formatter->dump_string("RequestId", s->trans_id);
s->formatter->close_section();
s->formatter->open_object_section("AddClientIDToOpenIDConnectProviderResponse");
dump_oidc_provider(info, s->formatter);
s->formatter->close_section();
s->formatter->close_section();
}
}

View File

@ -62,3 +62,27 @@ class RGWListOIDCProviders : public RGWRestOIDCProvider {
const char* name() const override { return "list_oidc_providers"; }
RGWOpType get_type() override { return RGW_OP_LIST_OIDC_PROVIDERS; }
};
class RGWAddClientIdToOIDCProvider : public RGWRestOIDCProvider {
std::string url;
std::string client_id;
public:
RGWAddClientIdToOIDCProvider();
int init_processing(optional_yield y);
void execute(optional_yield y) override;
const char* name() const override { return "add_client_id_to_oidc_provider"; }
RGWOpType get_type() override { return RGW_OP_ADD_CLIENTID_TO_OIDC_PROVIDER; }
};
class RGWUpdateOIDCProviderThumbprint : public RGWRestOIDCProvider {
std::string url;
std::vector<std::string> thumbprints;
public:
RGWUpdateOIDCProviderThumbprint();
int init_processing(optional_yield y);
void execute(optional_yield y) override;
const char* name() const override { return "update_oidc_provider_thumbprint"; }
RGWOpType get_type() override { return RGW_OP_UPDATE_OIDC_PROVIDER_THUMBPRINT; }
};

View File

@ -384,7 +384,6 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
found_valid_cert = true;
break;
}
found_valid_cert = true;
}
if (! found_valid_cert) {
ldpp_dout(dpp, 0) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;

View File

@ -1854,7 +1854,7 @@ do_rgw_create_users()
--access-key ABCDEFGHIJKLMNOPQRST \
--secret abcdefghijklmnopqrstuvwxyzabcdefghijklmn \
--display-name youruseridhere \
--email s3@example.com --caps="roles=*;user-policy=*" -c $conf_fn > /dev/null
--email s3@example.com --caps="roles=*;user-policy=*;oidc-provider=*" -c $conf_fn > /dev/null
$CEPH_BIN/radosgw-admin user create \
--uid 56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234 \
--access-key NOPQRSTUVWXYZABCDEFG \