Search in sources :

Example 1 with SaslAuthenticationException

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);
    }
}
Also used : KafkaProducer(org.apache.kafka.clients.producer.KafkaProducer) HashMap(java.util.HashMap) ProducerRecord(org.apache.kafka.clients.producer.ProducerRecord) StringSerializer(org.apache.kafka.common.serialization.StringSerializer) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException) Test(org.junit.Test)

Example 2 with 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());
    }
}
Also used : HashMap(java.util.HashMap) StringDeserializer(org.apache.kafka.common.serialization.StringDeserializer) KafkaConsumer(org.apache.kafka.clients.consumer.KafkaConsumer) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException) Test(org.junit.Test)

Example 3 with SaslAuthenticationException

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;
    }
}
Also used : ClientFirstMessage(org.apache.kafka.common.security.scram.ScramMessages.ClientFirstMessage) IOException(java.io.IOException) IllegalSaslStateException(org.apache.kafka.common.errors.IllegalSaslStateException) SaslException(javax.security.sasl.SaslException) InvalidKeyException(java.security.InvalidKeyException) NameCallback(javax.security.auth.callback.NameCallback) ClientFinalMessage(org.apache.kafka.common.security.scram.ScramMessages.ClientFinalMessage) ServerFinalMessage(org.apache.kafka.common.security.scram.ScramMessages.ServerFinalMessage) DelegationTokenCredentialCallback(org.apache.kafka.common.security.token.delegation.DelegationTokenCredentialCallback) ServerFirstMessage(org.apache.kafka.common.security.scram.ScramMessages.ServerFirstMessage) UnsupportedCallbackException(javax.security.auth.callback.UnsupportedCallbackException) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException)

Example 4 with SaslAuthenticationException

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;
        }
    }
}
Also used : SaslAuthenticateResponse(org.apache.kafka.common.requests.SaslAuthenticateResponse) NetworkSend(org.apache.kafka.common.network.NetworkSend) IllegalSaslStateException(org.apache.kafka.common.errors.IllegalSaslStateException) SaslException(javax.security.sasl.SaslException) ByteBuffer(java.nio.ByteBuffer) ApiKeys(org.apache.kafka.common.protocol.ApiKeys) SaslAuthenticateRequest(org.apache.kafka.common.requests.SaslAuthenticateRequest) RequestAndSize(org.apache.kafka.common.requests.RequestAndSize) RequestHeader(org.apache.kafka.common.requests.RequestHeader) RequestContext(org.apache.kafka.common.requests.RequestContext) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException) UnsupportedVersionException(org.apache.kafka.common.errors.UnsupportedVersionException)

Example 5 with SaslAuthenticationException

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];
}
Also used : UnsupportedEncodingException(java.io.UnsupportedEncodingException) SaslException(javax.security.sasl.SaslException) SaslAuthenticationException(org.apache.kafka.common.errors.SaslAuthenticationException)

Aggregations

SaslAuthenticationException (org.apache.kafka.common.errors.SaslAuthenticationException)8 HashMap (java.util.HashMap)4 Test (org.junit.Test)4 SaslException (javax.security.sasl.SaslException)3 IOException (java.io.IOException)2 KafkaProducer (org.apache.kafka.clients.producer.KafkaProducer)2 IllegalSaslStateException (org.apache.kafka.common.errors.IllegalSaslStateException)2 StringSerializer (org.apache.kafka.common.serialization.StringSerializer)2 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 ByteBuffer (java.nio.ByteBuffer)1 InvalidKeyException (java.security.InvalidKeyException)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 NameCallback (javax.security.auth.callback.NameCallback)1 UnsupportedCallbackException (javax.security.auth.callback.UnsupportedCallbackException)1 AdminClient (org.apache.kafka.clients.admin.AdminClient)1 DescribeTopicsResult (org.apache.kafka.clients.admin.DescribeTopicsResult)1 KafkaConsumer (org.apache.kafka.clients.consumer.KafkaConsumer)1 ProducerRecord (org.apache.kafka.clients.producer.ProducerRecord)1 KafkaException (org.apache.kafka.common.KafkaException)1 UnsupportedVersionException (org.apache.kafka.common.errors.UnsupportedVersionException)1