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

Add support for secondary authentication #52093

Merged
merged 3 commits into from
Feb 21, 2020

Conversation

tvernum
Copy link
Contributor

@tvernum tvernum commented Feb 8, 2020

This change makes it possible to send secondary authentication
credentials to select endpoints that need to perform a single action
in the context of two users.

Typically this need arises when a server process needs to call an
endpoint that users should not (or might not) have direct access to,
but some part of that action must be performed using the logged-in
user's identity.

@tvernum tvernum added WIP :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) v8.0.0 v7.7.0 labels Feb 8, 2020
@elasticmachine
Copy link
Collaborator

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

@tvernum
Copy link
Contributor Author

tvernum commented Feb 8, 2020

Draft/WIP because it builds on #52032 which is not merged yet

@jkakavas
Copy link
Member

@tvernum is this good to be reviewed now that #52032 is merged or do you want to make adjustments first ?

@tvernum
Copy link
Contributor Author

tvernum commented Feb 13, 2020 via email

This change makes it possible to send secondary authentication
credentials to select endpoints that need to perform a single action
in the context of two users.

Typically this need arises when a server process needs to call an
endpoint that users should not (or might not) have direct access to,
but some part of that action must be performed using the logged-in
user's identity.
@tvernum tvernum marked this pull request as ready for review February 13, 2020 11:06
@tvernum tvernum removed the WIP label Feb 13, 2020
@tvernum
Copy link
Contributor Author

tvernum commented Feb 14, 2020

(It's ready now, sorry, I forgot to ping)

@tvernum
Copy link
Contributor Author

tvernum commented Feb 18, 2020

Ping @jkakavas

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!

Sorry it took me a while, I wanted to go through and understand all changes and interactions. I left some nitpicking suggestions and a question

*/
public class SecondaryAuthentication {

private static final String THREAD_CTX_KEY = "_xpack_security_2nd_authc";
Copy link
Member

Choose a reason for hiding this comment

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

nit:

Suggested change
private static final String THREAD_CTX_KEY = "_xpack_security_2nd_authc";
private static final String THREAD_CTX_KEY = "_xpack_security_secondary_authc";

purely personal preference, that 2nd doesn't read well

}

@Nullable
public static SecondaryAuthentication restoreFromContext(SecurityContext securityContext) 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 name this readFromContext in order to not imply that it does anything more?


private static final String THREAD_CTX_KEY = "_xpack_security_2nd_authc";

public interface Authenticator {
Copy link
Member

Choose a reason for hiding this comment

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

More of a legitimate question than feedback, what value does this interface add?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It existed so that x-pack-core had something usable even though the implementation was in security.
But actually that doesn't matter anymore because the only use the authentication from within security.

authenticate(authListener -> authenticationService.authenticate(action, request, false, authListener), listener);
}

/**
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/**
/**

));
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
logger.trace("found secondary authentication credentials, placing them in the internal [{}] header for authentication",
UsernamePasswordToken.BASIC_AUTH_HEADER);
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated to this change but we should move BASIC_AUTH_HEADER away from UsernamePasswordToken , it confused me for more time than I'd like to admit :/

@@ -150,6 +150,7 @@ the system itself (e.g. pings, update mappings, share relocation, etc...) and we
it to the action without an associated user (not via REST or transport - this is taken care of by
the {@link Rest} filter and the {@link ServerTransport} filter respectively), it's safe to assume a system user
here if a request is not associated with any other user.
Because we want to fallback to the SystemUser, we don't allow anonymous (AnonymousUser) requests.
Copy link
Member

Choose a reason for hiding this comment

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

This comment found its way back here : #52094 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Splitting this branch up ended up being hard work.
It was worth it because each of the changes was substantial, but there was a lot of rebasing and merging.

*/
@Override
public void authenticate(String action, TransportRequest request, ActionListener<SecondaryAuthentication> listener) {
authenticate(authListener -> authenticationService.authenticate(action, request, false, authListener), listener);
Copy link
Member

Choose a reason for hiding this comment

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

Would it be worth adding a comment or jdoc to mention that allowAnonymous is set to false for a reason?

.put(buildEnvSettings(Settings.EMPTY))
.put("xpack.security.authc.realms.dummy.test_realm.order", 1)
.put("xpack.security.authc.token.enabled", true)
.put("xpack.security.authc.api_key.enabled", true)
Copy link
Member

Choose a reason for hiding this comment

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

We don't actually use ApiKeyService here so we could just mock it below and return false on isEnabled() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I originally expected to include a test for ApiKey authentication, but Bearer Token is sufficient to verify that we're not restricted to the realm chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, this is not as easy as it sounds.
authenticateWithApiKeyIfPresent is package protected, so it's really hard to mock.

@@ -48,7 +49,9 @@

public class SecurityRestFilterTests extends ESTestCase {
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 a test here to verify we process secondary authentication failures correctly?

@tvernum tvernum merged commit 2c6aa90 into elastic:master Feb 21, 2020
tvernum added a commit to tvernum/elasticsearch that referenced this pull request Mar 13, 2020
This change makes it possible to send secondary authentication
credentials to select endpoints that need to perform a single action
in the context of two users.

Typically this need arises when a server process needs to call an
endpoint that users should not (or might not) have direct access to,
but some part of that action must be performed using the logged-in
user's identity.

Backport of: elastic#52093
tvernum added a commit that referenced this pull request Mar 13, 2020
This change makes it possible to send secondary authentication
credentials to select endpoints that need to perform a single action
in the context of two users.

Typically this need arises when a server process needs to call an
endpoint that users should not (or might not) have direct access to,
but some part of that action must be performed using the logged-in
user's identity.

Backport of: #52093
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>enhancement :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

4 participants