Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create API Key on behalf of other user #52886

Merged
merged 9 commits into from
Mar 12, 2020

Conversation

tvernum
Copy link
Contributor

@tvernum tvernum commented Feb 27, 2020

This change adds a "grant API key action"

POST /_security/api_key/grant

that creates a new API key using the privileges of one user ("the
system user") to execute the action, but creates the API key with
the roles of the second user ("the end user").

This allows a system (such as Kibana) to create API keys representing
the identity and access of an authenticated user without requiring
that user to have permission to create API keys on their own.

Relates: #48716

This change adds a "grant API key action"

   POST /_security/api_key/grant

that creates a new API key using the privileges of one user ("the
system user") to execute the action, but creates the API key with
the roles of the second user ("the end user").

This allows a system (such as Kibana) to create API keys representing
the identity and access of an authenticated user without requiring
that user to have permission to create API keys on their own.
@tvernum tvernum added >feature :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) v8.0.0 v7.7.0 labels Feb 27, 2020
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-security (:Security/Authentication)

@tvernum
Copy link
Contributor Author

tvernum commented Feb 27, 2020

This lacks:

  • docs
  • rest spec
  • rest (yaml) tests
  • HLRC
  • a new privilege (grant_api_key)

but it's big enough as it is, and I want to keep it to a reviewable size.

@tvernum
Copy link
Contributor Author

tvernum commented Feb 28, 2020

Ping: @peterschretlen @mikecote

Copy link
Member

@jkakavas jkakavas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a first pass but didn't look that closely into tests. Will make another pass early next week

invalidateApiKeysForUser(END_USER);
}

public void testGrantApiKeyForOtherUser() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add one with access_token grant since we're at it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't in this test, because basic license doesn't suppor tokens, but I can move the test elsewhere and do that if you'd like it.

Since the transport action test already covers Username+Password and AccessToken (both valid and invalid), I didn't think it was necessary to cover it again in the IT. I just wanted something integrated to test it top-to-bottom.

Testing's a tricky balance, so if it's something you think we need, I'm happy to make it happen (but it will probably means a whole new QA project for rest tests using security on trial licenses).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM to leave it as is

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't in this test, because basic license doesn't support tokens

But you can start a trial, as it's done in SecurityWithBasicLicenseIT#testWithTrialLicense , right ? If this is possible, I would also just slightly prefer a test case using tokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because a cluster can only start a trial once, and the other test does that.
If we want to test this with a non-basic license then we'll need a new QA project.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You convinced me that I was just being lazy, so I've added a new QA project, and a new test for authenticating with a token + API key expiration.

@mikecote
Copy link

mikecote commented Mar 2, 2020

Hi @tvernum I played with this PR locally last Friday. Thank you for getting this done 🙏 From my perspective it works great and I can see what needs to be done on Kibana side without issue. As you noted, the privileges aren't given to the kibana user so I did my testing with elastic / others with manage_api_key privilege for now and it works! 👍

This test was piggy-backing on the security-in-basic QA project we
had, but the grant-api-key endpoint has the ability to use tokens,
which are not a basic licensed feature.

This creates a new QA project for security on trial licenses and run
the API key tests there
@tvernum tvernum requested a review from jkakavas March 5, 2020 03:48
* {@code null} authentication object.
*/
public void authenticateToken(SecureString tokenString, ActionListener<Authentication> listener) {
if (isEnabled()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this new method should return the new fancy 400 exceptions when the token service is not enabled (since it's not used in the authentication chain).

listener::onFailure
));
} else {
listener.onResponse(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also because this new method is not used in the authn chain, when a token fails decoding it's a client error.
I believe:

listener.onFailure(new IllegalArgumentException());

would do, it would return 400 (currently this falls back to 500 in ApiKeyGenerator#generateApiKey).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point @albertzaharovits

try (XContentParser parser = request.contentParser()) {
final GrantApiKeyRequest grantRequest = PARSER.parse(parser, null);
if (refresh != null) {
grantRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.parse(refresh));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refresh_policy should be forwarded to the inner CreateApiKeyRequest to take effect.

public void onFailure(Exception e) {
RestStatus status = ExceptionsHelper.status(e);
if (status == RestStatus.UNAUTHORIZED) {
sendFailure(new ElasticsearchSecurityException("Failed to authenticate api key grant", RestStatus.FORBIDDEN, e));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this wrapping to translate 401 to 403!
Assuming this is the sole reason of existence for the inner class, in order to make it easier to read, I would make it more concise:

            return channel -> client.execute(GrantApiKeyAction.INSTANCE, grantRequest,
                    ActionListener.delegateResponse(new RestToXContentListener<>(channel),
                            (l, e) -> {
                                RestStatus status = ExceptionsHelper.status(e);
                                if (status == RestStatus.UNAUTHORIZED) {
                                    l.onFailure(new ElasticsearchSecurityException("Failed to authenticate api key grant",
                                            RestStatus.FORBIDDEN, e));
                                } else {
                                    l.onFailure(e);
                                }
                            }));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @albertzaharovits that was a huge improvement.

Copy link
Contributor

@albertzaharovits albertzaharovits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, I spotted only minor problems, no second take is necessary after we settle those.

Copy link
Member

@jkakavas jkakavas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Tim

@tvernum tvernum merged commit 150735c into elastic:master Mar 12, 2020
tvernum added a commit to tvernum/elasticsearch that referenced this pull request Mar 23, 2020
This change adds a "grant API key action"

   POST /_security/api_key/grant

that creates a new API key using the privileges of one user ("the
system user") to execute the action, but creates the API key with
the roles of the second user ("the end user").

This allows a system (such as Kibana) to create API keys representing
the identity and access of an authenticated user without requiring
that user to have permission to create API keys on their own.

This also creates a new QA project for security on trial licenses and runs
the API key tests there

Backport of: elastic#52886
tvernum added a commit that referenced this pull request Mar 23, 2020
This change adds a "grant API key action"

   POST /_security/api_key/grant

that creates a new API key using the privileges of one user ("the
system user") to execute the action, but creates the API key with
the roles of the second user ("the end user").

This allows a system (such as Kibana) to create API keys representing
the identity and access of an authenticated user without requiring
that user to have permission to create API keys on their own.

This also creates a new QA project for security on trial licenses and runs
the API key tests there

Backport of: #52886
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>feature :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) v7.7.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants