-
Notifications
You must be signed in to change notification settings - Fork 25.1k
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
Create API Key on behalf of other user #52886
Conversation
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.
Pinging @elastic/es-security (:Security/Authentication) |
This lacks:
but it's big enough as it is, and I want to keep it to a reviewable size. |
Ping: @peterschretlen @mikecote |
There was a problem hiding this 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
...ugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/GrantApiKeyRequest.java
Show resolved
Hide resolved
invalidateApiKeysForUser(END_USER); | ||
} | ||
|
||
public void testGrantApiKeyForOtherUser() throws IOException { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
...curity/src/main/java/org/elasticsearch/xpack/security/action/TransportGrantApiKeyAction.java
Show resolved
Hide resolved
...n/security/src/main/java/org/elasticsearch/xpack/security/authc/support/ApiKeyGenerator.java
Show resolved
Hide resolved
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 |
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
* {@code null} authentication object. | ||
*/ | ||
public void authenticateToken(SecureString tokenString, ActionListener<Authentication> listener) { | ||
if (isEnabled()) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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);
}
}));
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM Tim
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
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
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