KAFKA-4292: Configurable SASL callback handlers#2022
Conversation
f7189cf to
7f42041
Compare
7f42041 to
27b132a
Compare
38213b8 to
dfd26c9
Compare
|
Refer to this link for build results (access rights to CI server needed): |
|
Refer to this link for build results (access rights to CI server needed): |
|
Refer to this link for build results (access rights to CI server needed): |
|
Refer to this link for build results (access rights to CI server needed): |
|
Refer to this link for build results (access rights to CI server needed): |
|
Refer to this link for build results (access rights to CI server needed): |
b40d503 to
1d54a0e
Compare
There was a problem hiding this comment.
nit: letter "F" at the end of the line
There was a problem hiding this comment.
nit: Similar to the other sasl config descriptions., we can use "SASL" Acronym in descriptions.
1d54a0e to
27ffbc7
Compare
|
@junrao Do you have time to review this PR? Thank you! |
There was a problem hiding this comment.
Example should use ":" instead of "=": PLAIN:CustomPlainCallbackHandler,SCRAM-SHA-256:CustomScramCallbackHandler
It also might be good to somehow include an indication that the values are fully-qualified class names ("A map between SASL mechanisms and fully-qualified SASL server callback handler class names", or "org.mypackage.CustomPlainCalbackHandler")
There was a problem hiding this comment.
Would it ever be acceptable to use the config map to override the callback handlers for the built-in mechanism implementations (GSSAPI, PLAIN, and the two SCRAM-related ones)? If so then the code is fine, but if not then the built-in mechanism names should be explicitly checked for first. Same goes for the client side checks above.
There was a problem hiding this comment.
@rondagostino Thanks for the review. Yes, it is acceptable to override callback handlers for built-in mechanisms. One of the motivations for the KIP is to enable customization of built-in mechanisms to integrate with existing authentication servers (e.g. use an existing server or database to verify passwords for PLAIN).
There was a problem hiding this comment.
See comment below about the order of built-in mechanisms vs. non-built-in ones.
There was a problem hiding this comment.
Same as before, callback handlers can be overridden for built-in mechanisms too.
792e52d to
97f5c9b
Compare
97f5c9b to
3301fd0
Compare
|
Refer to this link for build results (access rights to CI server needed): |
3301fd0 to
0394612
Compare
bdf84b9 to
84d14ef
Compare
| JaasContext.Type contextType, | ||
| String saslMechanism, | ||
| Class<? extends Login> defaultLoginClass) { | ||
| String prefix = contextType == JaasContext.Type.SERVER ? ListenerName.saslMechanismPrefix(saslMechanism) : ""; |
There was a problem hiding this comment.
From SaslConfigs#SASL_LOGIN_CLASS_DOC:
For brokers, login config must be prefixed with listener prefix and SASL mechanism name in lower-case. For example, listener.name.sasl_ssl.scram-sha-256.sasl.login.class=com.example.CustomScramLogin
This code is only using the SASL mechanism name in lower-case as the prefix. Assuming this observation is in fact a problem, I think it raises a broader issue, which is that the listener (SASL_SSL vs. SASL_PLAIN) is not known at this point in the code (or at least it isn't readily available).
There was a problem hiding this comment.
Listener prefix is removed from the config name before it gets here. This code will find scram-sha-256.sasl.login.class in configs only if it was prefixed with the listener name that this method is being invoked for. It is the same with callback handlers too.
There was a problem hiding this comment.
Ok, so no problem in the code at this point. This behavior -- stripping the listener prefix off before passing the config map to the code when it runs for a particular listener -- probably warrants documentation, but I'm not sure where that documentation should be (or if it exists already and I just haven't seen). Thanks for the clarification.
|
@rondagostino KIP-86 didnt make login callbacks configurable since we didn't have a mechanism where we needed it. We currently use the same |
|
@rajinisivaram Ok, I now understand the difference between the two client-side callback handlers. The SASL Client callback handler is supposed to mediate between the SASL Client and the JAAS Login Module -- when the SASL client needs something it should use the SASL Client callback handler to get it rather than going to get it by itself (for example, looking in the Subject's public or private credentials). By using the callback handler like this (as opposed to hard-coding the SASL Client to look somewhere) everything becomes pluggable. I hadn't been doing that, so I will make that change in the OAuth code. Now that I understand this difference, yes, we do need to be able to configure the Login callback handler. In the OAuth case this callback handler is responsible for providing the OAuthBearerToken. Can you add the |
84d14ef to
6cd1533
Compare
| LoginMetadata<?> loginMetadata) throws IOException, LoginException { | ||
| this.loginMetadata = loginMetadata; | ||
| this.login = Utils.newInstance(loginMetadata.loginClass); | ||
| AuthenticateCallbackHandler callbackHandler = Utils.newInstance(loginMetadata.loginCallbackClass); |
There was a problem hiding this comment.
@rajinisivaram The login callback handler class isn't getting its #configure(Map<String, ?>, String, List) invoked. Perhaps it might be better to treat the login callback handler class the same way the client and server callback handler classes are treated, which is to create/configure them in SaslChannelBuilder? Note that the login callback handler class is potentially used both on the client side and on the server side (it is used on the broker when the mechanism is the inter-broker protocol).
There was a problem hiding this comment.
@rondagostino Thanks for the review. Good catch. Added invocation of configure and missing handler in KafkaConfig. The lifecycle of LoginCallbackHandler is the same as that of Login, both managed by LoginManager. In particular, LoginCallbackHandler/Login can outlive SaslChannelBuilder. Hence these instances are created in LoginManager.
| + "that implements the AuthenticateCallbackHandler interface."; | ||
|
|
||
| public static final String SASL_LOGIN_CALLBACK_HANDLER_CLASS = "sasl.login.callback.handler.class"; | ||
| public static final String SASL_LOGIN_CALLBACK_HANDLER_CLASS_DOC = "The fully qualified name of a SASL login callback handler class " |
There was a problem hiding this comment.
I think this documentation should state that "For brokers, login callback handler config must be prefixed with listener prefix and SASL mechanism name in lower-case" because this is valid to use on both the client-side and the broker side.
junrao
left a comment
There was a problem hiding this comment.
@rajinisivaram : Thanks for the patch. Looks good overall. Just a few minor comments below.
| * @param saslMechanism Negotiated SASL mechanism | ||
| * @param jaasConfigEntries JAAS configuration entries from the JAAS login context. | ||
| * This list contains a single entry for clients and may contain more than | ||
| * one entry for servers if multiple mechanisms are enabled on a listener. |
There was a problem hiding this comment.
Hmm, in the ChannelBuilder, it seems that we always pass in the entry for one mechanism.
Also, could we add some comments to clarify the difference between configs and jaasConfigEntries? For example, if a key config is present in both, which one takes precedence.
There was a problem hiding this comment.
jaasConfigEntries can contain multiple entries if using static JAAS config with multiple entries in a single KafkaServer login context when multiple mechanisms are enabled. Updated the doc to reflect that and also added clarification for configs and jaasConfigEntries.
| */ | ||
| void configure(Map<String, ?> configs, JaasContext jaasContext); | ||
| void configure(Map<String, ?> configs, String contextName, Configuration configuration, | ||
| AuthenticateCallbackHandler loginCallbackHandler); |
There was a problem hiding this comment.
It would be useful to add a comment to describe the difference between configs and Configuration.
| import org.apache.kafka.common.config.ConfigResource | ||
| import org.apache.kafka.common.security.JaasUtils | ||
| import org.apache.kafka.common.security.scram._ | ||
| import org.apache.kafka.common.security.scram.internal.{ScramCredentialUtils, ScramFormatter, ScramMechanism} |
There was a problem hiding this comment.
The import for org.apache.kafka.common.security.scram._ seems unused now?
| * If login module name is specified, return option value only from that module. | ||
| */ | ||
| public String configEntryOption(String key, String loginModuleName) { | ||
| return configEntryOption(configurationEntries, key, loginModuleName); |
There was a problem hiding this comment.
It seems configEntryOption() is never used?
| .define(SaslMechanismInterBrokerProtocolProp, STRING, Defaults.SaslMechanismInterBrokerProtocol, MEDIUM, SaslMechanismInterBrokerProtocolDoc) | ||
| .define(SaslJaasConfigProp, PASSWORD, null, MEDIUM, SaslJaasConfigDoc) | ||
| .define(SaslEnabledMechanismsProp, LIST, Defaults.SaslEnabledMechanisms, MEDIUM, SaslEnabledMechanismsDoc) | ||
| .define(SaslServerCallbackHandlerClassProp, STRING, null, MEDIUM, SaslServerCallbackHandlerClassProp) |
There was a problem hiding this comment.
The last param should be Doc instead of Prop?
| private final Map<String, ?> configs; | ||
| private final KafkaPrincipalBuilder principalBuilder; | ||
| private final DelegationTokenCache tokenCache; | ||
| private final Map<String, AuthenticateCallbackHandler> callbackHandlers; |
There was a problem hiding this comment.
tokenCache, credentialCache and jaasContexts are no longer used?
| TestServerCallbackHandler.class.getName()); | ||
| server = createEchoServer(securityProtocol); | ||
|
|
||
| jaasConfig.setClientOptions("PLAIN", TestServerCallbackHandler.USERNAME, TestServerCallbackHandler.PASSWORD); |
There was a problem hiding this comment.
These are the configs for the server. So, the name setClientOptions is a bit mis-leading.
There was a problem hiding this comment.
It is setting the client options based on the values used in the server callback. Added a comment to clarify.
| @Test | ||
| public void testAuthenticateCallbackHandlerMechanisms() throws Exception { | ||
| SecurityProtocol securityProtocol = SecurityProtocol.SASL_PLAINTEXT; | ||
| configureMechanisms("DIGEST-MD5", Arrays.asList("DIGEST-MD5", "PLAIN")); |
There was a problem hiding this comment.
Do we need to add DIGEST-MD5?
There was a problem hiding this comment.
The test is using two mechanisms with two different callbacks to verify that the right callback is used when there are multiple mechanisms. Added comment and also updated the test to verify both mechanisms.
| String prefix = ListenerName.forSecurityProtocol(securityProtocol).saslMechanismConfigPrefix("PLAIN"); | ||
| saslServerConfigs.put(prefix + SaslConfigs.SASL_LOGIN_CLASS, TestLogin.class.getName()); | ||
| server = createEchoServer(securityProtocol); | ||
| assertEquals(1, TestLogin.loginCount.get()); |
There was a problem hiding this comment.
Hmm, there is no connection yet, why would count be 1?
There was a problem hiding this comment.
login() is performed when the server channel builder is created, hence the count is one when the server starts even before any clients connect.
|
|
||
| // This test uses SASL callback handler overrides for server connections of Kafka broker | ||
| // and client connections of Kafka producers and consumers. Client connections of Kafka brokers | ||
| // use default callback handlers. The second client used in the multi-user test |
There was a problem hiding this comment.
"Client connections of Kafka brokers use default callback handlers." It seems that the clients are using customized callbacks?
There was a problem hiding this comment.
Broker use default client callback handlers for inter-broker communication. Producers/consumers are configured with customized callbacks. Updated the comment.
2bea156 to
a8aaa90
Compare
|
@rondagostino @junrao Thanks for the reviews. I have addressed the comments. |
junrao
left a comment
There was a problem hiding this comment.
@rajinisivaram Thanks for the updated patch. LGTM. Just a few minor comments below.
| * from `jaasConfiguration`. | ||
| * @param contextName JAAS context name for this login which may be used to obtain | ||
| * the login context from `jaasConfiguration`. | ||
| * @param loginCallbackHandler Login callback handler instance to use for this Login. |
There was a problem hiding this comment.
Could we add the java doc for jaasConfiguration?
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(configInfo, loginClass); |
There was a problem hiding this comment.
Does the hashcode need to include loginCallbackClass ?
|
|
||
| // This test uses SASL callback handler overrides for server connections of Kafka broker | ||
| // and client connections of Kafka producers and consumers. Client connections from Kafka brokers | ||
| // used for inter-broker communication use default callback handlers. The second client used in |
There was a problem hiding this comment.
We set KafkaConfig.SaslClientCallbackHandlerClassProp in line 106. So, it seems that the broker connection also has a customized callback?
There was a problem hiding this comment.
Sorry, I had missed that earlier, updated the comment.
|
@junrao Thanks for the review. I have addressed the comments. If there are no other comments, I will merge later on today after the builds complete. |
|
@rondagostino @junrao Thanks for the reviews, merging to trunk. |
Implementation of KIP-86. Client, server and login callback handlers have been made configurable for both brokers and clients. Reviewers: Jun Rao <junrao@gmail.com>, Ron Dagostino <rndgstn@gmail.com>, Manikumar Reddy <manikumar.reddy@gmail.com>
Implementation of KIP-86: https://cwiki.apache.org/confluence/display/KAFKA/KIP-86%3A+Configurable+SASL+callback+handlers