Search in sources :

Example 1 with ApiVersionsRequest

use of org.apache.kafka.common.requests.ApiVersionsRequest in project apache-kafka-on-k8s by banzaicloud.

the class SaslClientAuthenticator method authenticate.

/**
 * Sends an empty message to the server to initiate the authentication process. It then evaluates server challenges
 * via `SaslClient.evaluateChallenge` and returns client responses until authentication succeeds or fails.
 *
 * The messages are sent and received as size delimited bytes that consists of a 4 byte network-ordered size N
 * followed by N bytes representing the opaque payload.
 */
public void authenticate() throws IOException {
    short saslHandshakeVersion = 0;
    if (netOutBuffer != null && !flushNetOutBufferAndUpdateInterestOps())
        return;
    switch(saslState) {
        case SEND_APIVERSIONS_REQUEST:
            // Always use version 0 request since brokers treat requests with schema exceptions as GSSAPI tokens
            ApiVersionsRequest apiVersionsRequest = new ApiVersionsRequest((short) 0);
            send(apiVersionsRequest.toSend(node, nextRequestHeader(ApiKeys.API_VERSIONS, apiVersionsRequest.version())));
            setSaslState(SaslState.RECEIVE_APIVERSIONS_RESPONSE);
            break;
        case RECEIVE_APIVERSIONS_RESPONSE:
            ApiVersionsResponse apiVersionsResponse = (ApiVersionsResponse) receiveKafkaResponse();
            if (apiVersionsResponse == null)
                break;
            else {
                saslHandshakeVersion = apiVersionsResponse.apiVersion(ApiKeys.SASL_HANDSHAKE.id).maxVersion;
                ApiVersion authenticateVersion = apiVersionsResponse.apiVersion(ApiKeys.SASL_AUTHENTICATE.id);
                if (authenticateVersion != null)
                    saslAuthenticateVersion((short) Math.min(authenticateVersion.maxVersion, ApiKeys.SASL_AUTHENTICATE.latestVersion()));
                setSaslState(SaslState.SEND_HANDSHAKE_REQUEST);
            // Fall through to send handshake request with the latest supported version
            }
        case SEND_HANDSHAKE_REQUEST:
            SaslHandshakeRequest handshakeRequest = createSaslHandshakeRequest(saslHandshakeVersion);
            send(handshakeRequest.toSend(node, nextRequestHeader(ApiKeys.SASL_HANDSHAKE, handshakeRequest.version())));
            setSaslState(SaslState.RECEIVE_HANDSHAKE_RESPONSE);
            break;
        case RECEIVE_HANDSHAKE_RESPONSE:
            SaslHandshakeResponse handshakeResponse = (SaslHandshakeResponse) receiveKafkaResponse();
            if (handshakeResponse == null)
                break;
            else {
                handleSaslHandshakeResponse(handshakeResponse);
                setSaslState(SaslState.INITIAL);
            // Fall through and start SASL authentication using the configured client mechanism
            }
        case INITIAL:
            sendSaslClientToken(new byte[0], true);
            setSaslState(SaslState.INTERMEDIATE);
            break;
        case INTERMEDIATE:
            byte[] serverToken = receiveToken();
            boolean noResponsesPending = serverToken != null && !sendSaslClientToken(serverToken, false);
            // For versions with SASL_AUTHENTICATE header, server always sends a response to each SASL_AUTHENTICATE request.
            if (saslClient.isComplete()) {
                if (saslAuthenticateVersion == DISABLE_KAFKA_SASL_AUTHENTICATE_HEADER || noResponsesPending)
                    setSaslState(SaslState.COMPLETE);
                else
                    setSaslState(SaslState.CLIENT_COMPLETE);
            }
            break;
        case CLIENT_COMPLETE:
            byte[] serverResponse = receiveToken();
            if (serverResponse != null)
                setSaslState(SaslState.COMPLETE);
            break;
        case COMPLETE:
            break;
        case FAILED:
            // Should never get here since exception would have been propagated earlier
            throw new IllegalStateException("SASL handshake has already failed");
    }
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) ApiVersion(org.apache.kafka.common.requests.ApiVersionsResponse.ApiVersion) SaslHandshakeResponse(org.apache.kafka.common.requests.SaslHandshakeResponse) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest) SaslHandshakeRequest(org.apache.kafka.common.requests.SaslHandshakeRequest)

Example 2 with ApiVersionsRequest

use of org.apache.kafka.common.requests.ApiVersionsRequest in project apache-kafka-on-k8s by banzaicloud.

the class SaslAuthenticatorTest method testApiVersionsRequestWithUnsupportedVersion.

/**
 * Tests that unsupported version of ApiVersionsRequest before SASL handshake request
 * returns error response and does not result in authentication failure. This test
 * is similar to {@link #testUnauthenticatedApiVersionsRequest(SecurityProtocol, short)}
 * where a non-SASL client is used to send requests that are processed by
 * {@link SaslServerAuthenticator} of the server prior to client authentication.
 */
@Test
public void testApiVersionsRequestWithUnsupportedVersion() throws Exception {
    short handshakeVersion = ApiKeys.SASL_HANDSHAKE.latestVersion();
    SecurityProtocol securityProtocol = SecurityProtocol.SASL_PLAINTEXT;
    configureMechanisms("PLAIN", Arrays.asList("PLAIN"));
    server = createEchoServer(securityProtocol);
    // Send ApiVersionsRequest with unsupported version and validate error response.
    String node = "1";
    createClientConnection(SecurityProtocol.PLAINTEXT, node);
    RequestHeader header = new RequestHeader(ApiKeys.API_VERSIONS, Short.MAX_VALUE, "someclient", 1);
    ApiVersionsRequest request = new ApiVersionsRequest.Builder().build();
    selector.send(request.toSend(node, header));
    ByteBuffer responseBuffer = waitForResponse();
    ResponseHeader.parse(responseBuffer);
    ApiVersionsResponse response = ApiVersionsResponse.parse(responseBuffer, (short) 0);
    assertEquals(Errors.UNSUPPORTED_VERSION, response.error());
    // Send ApiVersionsRequest with a supported version. This should succeed.
    sendVersionRequestReceiveResponse(node);
    // Test that client can authenticate successfully
    sendHandshakeRequestReceiveResponse(node, handshakeVersion);
    authenticateUsingSaslPlainAndCheckConnection(node, handshakeVersion > 0);
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) SecurityProtocol(org.apache.kafka.common.security.auth.SecurityProtocol) RequestHeader(org.apache.kafka.common.requests.RequestHeader) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest) ByteBuffer(java.nio.ByteBuffer) Test(org.junit.Test)

Example 3 with ApiVersionsRequest

use of org.apache.kafka.common.requests.ApiVersionsRequest in project kafka by apache.

the class NetworkClient method handleApiVersionsResponse.

private void handleApiVersionsResponse(List<ClientResponse> responses, InFlightRequest req, long now, ApiVersionsResponse apiVersionsResponse) {
    final String node = req.destination;
    if (apiVersionsResponse.data().errorCode() != Errors.NONE.code()) {
        if (req.request.version() == 0 || apiVersionsResponse.data().errorCode() != Errors.UNSUPPORTED_VERSION.code()) {
            log.warn("Received error {} from node {} when making an ApiVersionsRequest with correlation id {}. Disconnecting.", Errors.forCode(apiVersionsResponse.data().errorCode()), node, req.header.correlationId());
            this.selector.close(node);
            processDisconnection(responses, node, now, ChannelState.LOCAL_CLOSE);
        } else {
            // Starting from Apache Kafka 2.4, ApiKeys field is populated with the supported versions of
            // the ApiVersionsRequest when an UNSUPPORTED_VERSION error is returned.
            // If not provided, the client falls back to version 0.
            short maxApiVersion = 0;
            if (apiVersionsResponse.data().apiKeys().size() > 0) {
                ApiVersion apiVersion = apiVersionsResponse.data().apiKeys().find(ApiKeys.API_VERSIONS.id);
                if (apiVersion != null) {
                    maxApiVersion = apiVersion.maxVersion();
                }
            }
            nodesNeedingApiVersionsFetch.put(node, new ApiVersionsRequest.Builder(maxApiVersion));
        }
        return;
    }
    NodeApiVersions nodeVersionInfo = new NodeApiVersions(apiVersionsResponse.data().apiKeys());
    apiVersions.update(node, nodeVersionInfo);
    this.connectionStates.ready(node);
    log.debug("Node {} has finalized features epoch: {}, finalized features: {}, supported features: {}, API versions: {}.", node, apiVersionsResponse.data().finalizedFeaturesEpoch(), apiVersionsResponse.data().finalizedFeatures(), apiVersionsResponse.data().supportedFeatures(), nodeVersionInfo);
}
Also used : ApiVersion(org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest)

Example 4 with ApiVersionsRequest

use of org.apache.kafka.common.requests.ApiVersionsRequest in project kafka by apache.

the class SaslAuthenticatorTest method testInvalidApiVersionsRequest.

/**
 * Tests that invalid ApiVersionRequest is handled by the server correctly and
 * returns an INVALID_REQUEST error.
 */
@Test
public void testInvalidApiVersionsRequest() throws Exception {
    short handshakeVersion = ApiKeys.SASL_HANDSHAKE.latestVersion();
    SecurityProtocol securityProtocol = SecurityProtocol.SASL_PLAINTEXT;
    configureMechanisms("PLAIN", Arrays.asList("PLAIN"));
    server = createEchoServer(securityProtocol);
    // Send ApiVersionsRequest with invalid version and validate error response.
    String node = "1";
    short version = ApiKeys.API_VERSIONS.latestVersion();
    createClientConnection(SecurityProtocol.PLAINTEXT, node);
    RequestHeader header = new RequestHeader(ApiKeys.API_VERSIONS, version, "someclient", 1);
    ApiVersionsRequest request = new ApiVersionsRequest(new ApiVersionsRequestData().setClientSoftwareName("  ").setClientSoftwareVersion("   "), version);
    selector.send(new NetworkSend(node, request.toSend(header)));
    ByteBuffer responseBuffer = waitForResponse();
    ResponseHeader.parse(responseBuffer, ApiKeys.API_VERSIONS.responseHeaderVersion(version));
    ApiVersionsResponse response = ApiVersionsResponse.parse(responseBuffer, version);
    assertEquals(Errors.INVALID_REQUEST.code(), response.data().errorCode());
    // Send ApiVersionsRequest with a supported version. This should succeed.
    sendVersionRequestReceiveResponse(node);
    // Test that client can authenticate successfully
    sendHandshakeRequestReceiveResponse(node, handshakeVersion);
    authenticateUsingSaslPlainAndCheckConnection(node, handshakeVersion > 0);
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) SecurityProtocol(org.apache.kafka.common.security.auth.SecurityProtocol) RequestHeader(org.apache.kafka.common.requests.RequestHeader) NetworkSend(org.apache.kafka.common.network.NetworkSend) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest) ApiVersionsRequestData(org.apache.kafka.common.message.ApiVersionsRequestData) ByteBuffer(java.nio.ByteBuffer) Test(org.junit.jupiter.api.Test)

Example 5 with ApiVersionsRequest

use of org.apache.kafka.common.requests.ApiVersionsRequest in project kafka by apache.

the class SaslAuthenticatorTest method testApiVersionsRequestWithServerUnsupportedVersion.

/**
 * Tests that unsupported version of ApiVersionsRequest before SASL handshake request
 * returns error response and does not result in authentication failure. This test
 * is similar to {@link #testUnauthenticatedApiVersionsRequest(SecurityProtocol, short)}
 * where a non-SASL client is used to send requests that are processed by
 * {@link SaslServerAuthenticator} of the server prior to client authentication.
 */
@Test
public void testApiVersionsRequestWithServerUnsupportedVersion() throws Exception {
    short handshakeVersion = ApiKeys.SASL_HANDSHAKE.latestVersion();
    SecurityProtocol securityProtocol = SecurityProtocol.SASL_PLAINTEXT;
    configureMechanisms("PLAIN", Arrays.asList("PLAIN"));
    server = createEchoServer(securityProtocol);
    // Send ApiVersionsRequest with unsupported version and validate error response.
    String node = "1";
    createClientConnection(SecurityProtocol.PLAINTEXT, node);
    RequestHeader header = new RequestHeader(new RequestHeaderData().setRequestApiKey(ApiKeys.API_VERSIONS.id).setRequestApiVersion(Short.MAX_VALUE).setClientId("someclient").setCorrelationId(1), (short) 2);
    ApiVersionsRequest request = new ApiVersionsRequest.Builder().build();
    selector.send(new NetworkSend(node, request.toSend(header)));
    ByteBuffer responseBuffer = waitForResponse();
    ResponseHeader.parse(responseBuffer, ApiKeys.API_VERSIONS.responseHeaderVersion((short) 0));
    ApiVersionsResponse response = ApiVersionsResponse.parse(responseBuffer, (short) 0);
    assertEquals(Errors.UNSUPPORTED_VERSION.code(), response.data().errorCode());
    ApiVersion apiVersion = response.data().apiKeys().find(ApiKeys.API_VERSIONS.id);
    assertNotNull(apiVersion);
    assertEquals(ApiKeys.API_VERSIONS.id, apiVersion.apiKey());
    assertEquals(ApiKeys.API_VERSIONS.oldestVersion(), apiVersion.minVersion());
    assertEquals(ApiKeys.API_VERSIONS.latestVersion(), apiVersion.maxVersion());
    // Send ApiVersionsRequest with a supported version. This should succeed.
    sendVersionRequestReceiveResponse(node);
    // Test that client can authenticate successfully
    sendHandshakeRequestReceiveResponse(node, handshakeVersion);
    authenticateUsingSaslPlainAndCheckConnection(node, handshakeVersion > 0);
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) ApiVersion(org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion) RequestHeaderData(org.apache.kafka.common.message.RequestHeaderData) SecurityProtocol(org.apache.kafka.common.security.auth.SecurityProtocol) RequestHeader(org.apache.kafka.common.requests.RequestHeader) NetworkSend(org.apache.kafka.common.network.NetworkSend) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest) ByteBuffer(java.nio.ByteBuffer) Test(org.junit.jupiter.api.Test)

Aggregations

ApiVersionsRequest (org.apache.kafka.common.requests.ApiVersionsRequest)16 ApiVersionsResponse (org.apache.kafka.common.requests.ApiVersionsResponse)10 RequestHeader (org.apache.kafka.common.requests.RequestHeader)10 ByteBuffer (java.nio.ByteBuffer)8 SecurityProtocol (org.apache.kafka.common.security.auth.SecurityProtocol)7 Test (org.junit.jupiter.api.Test)6 NetworkSend (org.apache.kafka.common.network.NetworkSend)4 IllegalSaslStateException (org.apache.kafka.common.errors.IllegalSaslStateException)3 ApiKeys (org.apache.kafka.common.protocol.ApiKeys)3 Test (org.junit.Test)3 InvalidRequestException (org.apache.kafka.common.errors.InvalidRequestException)2 UnsupportedSaslMechanismException (org.apache.kafka.common.errors.UnsupportedSaslMechanismException)2 ApiVersion (org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion)2 RequestAndSize (org.apache.kafka.common.requests.RequestAndSize)2 RequestContext (org.apache.kafka.common.requests.RequestContext)2 SaslHandshakeResponse (org.apache.kafka.common.requests.SaslHandshakeResponse)2 IOException (java.io.IOException)1 InetAddress (java.net.InetAddress)1 Collections (java.util.Collections)1 HashMap (java.util.HashMap)1