use of org.apache.kafka.common.errors.SaslAuthenticationException in project apache-kafka-on-k8s by banzaicloud.
the class ClientAuthenticationFailureTest method testProducerWithInvalidCredentials.
@Test
public void testProducerWithInvalidCredentials() {
Map<String, Object> props = new HashMap<>(saslClientConfigs);
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:" + server.port());
StringSerializer serializer = new StringSerializer();
try (KafkaProducer<String, String> producer = new KafkaProducer<>(props, serializer, serializer)) {
ProducerRecord<String, String> record = new ProducerRecord<>(topic, "message");
producer.send(record).get();
fail("Expected an authentication error!");
} catch (Exception e) {
assertTrue("Expected SaslAuthenticationException, got " + e.getCause().getClass(), e.getCause() instanceof SaslAuthenticationException);
}
}
use of org.apache.kafka.common.errors.SaslAuthenticationException in project apache-kafka-on-k8s by banzaicloud.
the class ClientAuthenticationFailureTest method testConsumerWithInvalidCredentials.
@Test
public void testConsumerWithInvalidCredentials() {
Map<String, Object> props = new HashMap<>(saslClientConfigs);
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:" + server.port());
StringDeserializer deserializer = new StringDeserializer();
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props, deserializer, deserializer)) {
consumer.subscribe(Arrays.asList(topic));
consumer.poll(100);
fail("Expected an authentication error!");
} catch (SaslAuthenticationException e) {
// OK
} catch (Exception e) {
fail("Expected only an authentication error, but another error occurred: " + e.getMessage());
}
}
use of org.apache.kafka.common.errors.SaslAuthenticationException in project apache-kafka-on-k8s by banzaicloud.
the class ScramSaslServer method evaluateResponse.
/**
* @throws SaslAuthenticationException if the requested authorization id is not the same as username.
* <p>
* <b>Note:</b> This method may throw {@link SaslAuthenticationException} to provide custom error messages
* to clients. But care should be taken to avoid including any information in the exception message that
* should not be leaked to unauthenticated clients. It may be safer to throw {@link SaslException} in
* most cases so that a standard error message is returned to clients.
* </p>
*/
@Override
public byte[] evaluateResponse(byte[] response) throws SaslException, SaslAuthenticationException {
try {
switch(state) {
case RECEIVE_CLIENT_FIRST_MESSAGE:
this.clientFirstMessage = new ClientFirstMessage(response);
this.scramExtensions = clientFirstMessage.extensions();
if (!SUPPORTED_EXTENSIONS.containsAll(scramExtensions.extensionNames())) {
log.debug("Unsupported extensions will be ignored, supported {}, provided {}", SUPPORTED_EXTENSIONS, scramExtensions.extensionNames());
}
String serverNonce = formatter.secureRandomString();
try {
String saslName = clientFirstMessage.saslName();
this.username = formatter.username(saslName);
NameCallback nameCallback = new NameCallback("username", username);
ScramCredentialCallback credentialCallback;
if (scramExtensions.tokenAuthenticated()) {
DelegationTokenCredentialCallback tokenCallback = new DelegationTokenCredentialCallback();
credentialCallback = tokenCallback;
callbackHandler.handle(new Callback[] { nameCallback, tokenCallback });
if (tokenCallback.tokenOwner() == null)
throw new SaslException("Token Authentication failed: Invalid tokenId : " + username);
this.authorizationId = tokenCallback.tokenOwner();
} else {
credentialCallback = new ScramCredentialCallback();
callbackHandler.handle(new Callback[] { nameCallback, credentialCallback });
this.authorizationId = username;
}
this.scramCredential = credentialCallback.scramCredential();
if (scramCredential == null)
throw new SaslException("Authentication failed: Invalid user credentials");
String authorizationIdFromClient = clientFirstMessage.authorizationId();
if (!authorizationIdFromClient.isEmpty() && !authorizationIdFromClient.equals(username))
throw new SaslAuthenticationException("Authentication failed: Client requested an authorization id that is different from username");
if (scramCredential.iterations() < mechanism.minIterations())
throw new SaslException("Iterations " + scramCredential.iterations() + " is less than the minimum " + mechanism.minIterations() + " for " + mechanism);
this.serverFirstMessage = new ServerFirstMessage(clientFirstMessage.nonce(), serverNonce, scramCredential.salt(), scramCredential.iterations());
setState(State.RECEIVE_CLIENT_FINAL_MESSAGE);
return serverFirstMessage.toBytes();
} catch (IOException | NumberFormatException | UnsupportedCallbackException e) {
throw new SaslException("Authentication failed: Credentials could not be obtained", e);
}
case RECEIVE_CLIENT_FINAL_MESSAGE:
try {
ClientFinalMessage clientFinalMessage = new ClientFinalMessage(response);
verifyClientProof(clientFinalMessage);
byte[] serverKey = scramCredential.serverKey();
byte[] serverSignature = formatter.serverSignature(serverKey, clientFirstMessage, serverFirstMessage, clientFinalMessage);
ServerFinalMessage serverFinalMessage = new ServerFinalMessage(null, serverSignature);
clearCredentials();
setState(State.COMPLETE);
return serverFinalMessage.toBytes();
} catch (InvalidKeyException e) {
throw new SaslException("Authentication failed: Invalid client final message", e);
}
default:
throw new IllegalSaslStateException("Unexpected challenge in Sasl server state " + state);
}
} catch (SaslException e) {
clearCredentials();
setState(State.FAILED);
throw e;
}
}
use of org.apache.kafka.common.errors.SaslAuthenticationException in project apache-kafka-on-k8s by banzaicloud.
the class SaslServerAuthenticator method handleSaslToken.
private void handleSaslToken(byte[] clientToken) throws IOException {
if (!enableKafkaSaslAuthenticateHeaders) {
byte[] response = saslServer.evaluateResponse(clientToken);
if (response != null) {
netOutBuffer = new NetworkSend(connectionId, ByteBuffer.wrap(response));
flushNetOutBufferAndUpdateInterestOps();
}
} else {
ByteBuffer requestBuffer = ByteBuffer.wrap(clientToken);
RequestHeader header = RequestHeader.parse(requestBuffer);
ApiKeys apiKey = header.apiKey();
short version = header.apiVersion();
RequestContext requestContext = new RequestContext(header, connectionId, clientAddress(), KafkaPrincipal.ANONYMOUS, listenerName, securityProtocol);
RequestAndSize requestAndSize = requestContext.parseRequest(requestBuffer);
if (apiKey != ApiKeys.SASL_AUTHENTICATE) {
IllegalSaslStateException e = new IllegalSaslStateException("Unexpected Kafka request of type " + apiKey + " during SASL authentication.");
sendKafkaResponse(requestContext, requestAndSize.request.getErrorResponse(e));
throw e;
}
if (!apiKey.isVersionSupported(version)) {
// This should not normally occur since clients typically check supported versions using ApiVersionsRequest
throw new UnsupportedVersionException("Version " + version + " is not supported for apiKey " + apiKey);
}
SaslAuthenticateRequest saslAuthenticateRequest = (SaslAuthenticateRequest) requestAndSize.request;
try {
byte[] responseToken = saslServer.evaluateResponse(Utils.readBytes(saslAuthenticateRequest.saslAuthBytes()));
// For versions with SASL_AUTHENTICATE header, send a response to SASL_AUTHENTICATE request even if token is empty.
ByteBuffer responseBuf = responseToken == null ? EMPTY_BUFFER : ByteBuffer.wrap(responseToken);
sendKafkaResponse(requestContext, new SaslAuthenticateResponse(Errors.NONE, null, responseBuf));
} catch (SaslAuthenticationException | SaslException e) {
String errorMessage = e instanceof SaslAuthenticationException ? e.getMessage() : "Authentication failed due to invalid credentials with SASL mechanism " + saslMechanism;
sendKafkaResponse(requestContext, new SaslAuthenticateResponse(Errors.SASL_AUTHENTICATION_FAILED, errorMessage));
throw e;
}
}
}
use of org.apache.kafka.common.errors.SaslAuthenticationException in project apache-kafka-on-k8s by banzaicloud.
the class PlainSaslServer method evaluateResponse.
/**
* @throws SaslAuthenticationException if username/password combination is invalid or if the requested
* authorization id is not the same as username.
* <p>
* <b>Note:</b> This method may throw {@link SaslAuthenticationException} to provide custom error messages
* to clients. But care should be taken to avoid including any information in the exception message that
* should not be leaked to unauthenticated clients. It may be safer to throw {@link SaslException} in
* some cases so that a standard error message is returned to clients.
* </p>
*/
@Override
public byte[] evaluateResponse(byte[] response) throws SaslException, SaslAuthenticationException {
/*
* Message format (from https://tools.ietf.org/html/rfc4616):
*
* message = [authzid] UTF8NUL authcid UTF8NUL passwd
* authcid = 1*SAFE ; MUST accept up to 255 octets
* authzid = 1*SAFE ; MUST accept up to 255 octets
* passwd = 1*SAFE ; MUST accept up to 255 octets
* UTF8NUL = %x00 ; UTF-8 encoded NUL character
*
* SAFE = UTF1 / UTF2 / UTF3 / UTF4
* ;; any UTF-8 encoded Unicode character except NUL
*/
String[] tokens;
try {
tokens = new String(response, "UTF-8").split("\u0000");
} catch (UnsupportedEncodingException e) {
throw new SaslException("UTF-8 encoding not supported", e);
}
if (tokens.length != 3)
throw new SaslException("Invalid SASL/PLAIN response: expected 3 tokens, got " + tokens.length);
String authorizationIdFromClient = tokens[0];
String username = tokens[1];
String password = tokens[2];
if (username.isEmpty()) {
throw new SaslException("Authentication failed: username not specified");
}
if (password.isEmpty()) {
throw new SaslException("Authentication failed: password not specified");
}
String expectedPassword = jaasContext.configEntryOption(JAAS_USER_PREFIX + username, PlainLoginModule.class.getName());
if (!password.equals(expectedPassword)) {
throw new SaslAuthenticationException("Authentication failed: Invalid username or password");
}
if (!authorizationIdFromClient.isEmpty() && !authorizationIdFromClient.equals(username))
throw new SaslAuthenticationException("Authentication failed: Client requested an authorization id that is different from username");
this.authorizationId = username;
complete = true;
return new byte[0];
}
Aggregations