Search in sources :

Example 1 with ApiVersionsResponse

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

the class SaslAuthenticatorTest method testUnauthenticatedApiVersionsRequest.

/**
     * Tests that Kafka ApiVersionsRequests are handled by the SASL server authenticator
     * prior to SASL handshake flow and that subsequent authentication succeeds
     * when transport layer is PLAINTEXT/SSL. This test uses a non-SASL client that simulates
     * SASL authentication after ApiVersionsRequest.
     * <p>
     * Test sequence (using <tt>securityProtocol=PLAINTEXT</tt> as an example):
     * <ol>
     *   <li>Starts a SASL_PLAINTEXT test server that simply echoes back client requests after authentication.</li>
     *   <li>A (non-SASL) PLAINTEXT test client connects to the SASL server port. Client is now unauthenticated.<./li>
     *   <li>The unauthenticated non-SASL client sends an ApiVersionsRequest and validates the response.
     *       A valid response indicates that {@link SaslServerAuthenticator} of the test server responded to
     *       the ApiVersionsRequest even though the client is not yet authenticated.</li>
     *   <li>The unauthenticated non-SASL client sends a SaslHandshakeRequest and validates the response. A valid response
     *       indicates that {@link SaslServerAuthenticator} of the test server responded to the SaslHandshakeRequest
     *       after processing ApiVersionsRequest.</li>
     *   <li>The unauthenticated non-SASL client sends the SASL/PLAIN packet containing username/password to authenticate
     *       itself. The client is now authenticated by the server. At this point this test client is at the
     *       same state as a regular SASL_PLAINTEXT client that is <tt>ready</tt>.</li>
     *   <li>The authenticated client sends random data to the server and checks that the data is echoed
     *       back by the test server (ie, not Kafka request-response) to ensure that the client now
     *       behaves exactly as a regular SASL_PLAINTEXT client that has completed authentication.</li>
     * </ol>
     */
private void testUnauthenticatedApiVersionsRequest(SecurityProtocol securityProtocol) throws Exception {
    configureMechanisms("PLAIN", Arrays.asList("PLAIN"));
    server = createEchoServer(securityProtocol);
    // Create non-SASL connection to manually authenticate after ApiVersionsRequest
    String node = "1";
    SecurityProtocol clientProtocol;
    switch(securityProtocol) {
        case SASL_PLAINTEXT:
            clientProtocol = SecurityProtocol.PLAINTEXT;
            break;
        case SASL_SSL:
            clientProtocol = SecurityProtocol.SSL;
            break;
        default:
            throw new IllegalArgumentException("Server protocol " + securityProtocol + " is not SASL");
    }
    createClientConnection(clientProtocol, node);
    NetworkTestUtils.waitForChannelReady(selector, node);
    // Send ApiVersionsRequest and check response
    ApiVersionsResponse versionsResponse = sendVersionRequestReceiveResponse(node);
    assertEquals(ApiKeys.SASL_HANDSHAKE.oldestVersion(), versionsResponse.apiVersion(ApiKeys.SASL_HANDSHAKE.id).minVersion);
    assertEquals(ApiKeys.SASL_HANDSHAKE.latestVersion(), versionsResponse.apiVersion(ApiKeys.SASL_HANDSHAKE.id).maxVersion);
    // Send SaslHandshakeRequest and check response
    SaslHandshakeResponse handshakeResponse = sendHandshakeRequestReceiveResponse(node);
    assertEquals(Collections.singletonList("PLAIN"), handshakeResponse.enabledMechanisms());
    // Complete manual authentication and check send/receive succeed
    authenticateUsingSaslPlainAndCheckConnection(node);
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) SaslHandshakeResponse(org.apache.kafka.common.requests.SaslHandshakeResponse) SecurityProtocol(org.apache.kafka.common.protocol.SecurityProtocol)

Example 2 with ApiVersionsResponse

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

the class StreamsKafkaClient method checkBrokerCompatibility.

/**
     * Check if the used brokers have version 0.10.1.x or higher.
     * <p>
     * Note, for <em>pre</em> 0.10.x brokers the broker version cannot be checked and the client will hang and retry
     * until it {@link StreamsConfig#REQUEST_TIMEOUT_MS_CONFIG times out}.
     *
     * @throws StreamsException if brokers have version 0.10.0.x
     */
public void checkBrokerCompatibility() throws StreamsException {
    final ClientRequest clientRequest = kafkaClient.newClientRequest(getAnyReadyBrokerId(), new ApiVersionsRequest.Builder(), Time.SYSTEM.milliseconds(), true);
    final ClientResponse clientResponse = sendRequest(clientRequest);
    if (!clientResponse.hasResponse()) {
        throw new StreamsException("Empty response for client request.");
    }
    if (!(clientResponse.responseBody() instanceof ApiVersionsResponse)) {
        throw new StreamsException("Inconsistent response type for API versions request. " + "Expected ApiVersionsResponse but received " + clientResponse.responseBody().getClass().getName());
    }
    final ApiVersionsResponse apiVersionsResponse = (ApiVersionsResponse) clientResponse.responseBody();
    if (apiVersionsResponse.apiVersion(ApiKeys.CREATE_TOPICS.id) == null) {
        throw new StreamsException("Kafka Streams requires broker version 0.10.1.x or higher.");
    }
}
Also used : ClientResponse(org.apache.kafka.clients.ClientResponse) ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) StreamsException(org.apache.kafka.streams.errors.StreamsException) ApiVersionsRequest(org.apache.kafka.common.requests.ApiVersionsRequest) ClientRequest(org.apache.kafka.clients.ClientRequest)

Example 3 with ApiVersionsResponse

use of org.apache.kafka.common.requests.ApiVersionsResponse 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 4 with ApiVersionsResponse

use of org.apache.kafka.common.requests.ApiVersionsResponse 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 5 with ApiVersionsResponse

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

the class NetworkClientTest method testInvalidApiVersionsRequest.

@Test
public void testInvalidApiVersionsRequest() {
    // initiate the connection
    client.ready(node, time.milliseconds());
    // handle the connection, send the ApiVersionsRequest
    client.poll(0, time.milliseconds());
    // check that the ApiVersionsRequest has been initiated
    assertTrue(client.hasInFlightRequests(node.idString()));
    // prepare response
    delayedApiVersionsResponse(0, ApiKeys.API_VERSIONS.latestVersion(), new ApiVersionsResponse(new ApiVersionsResponseData().setErrorCode(Errors.INVALID_REQUEST.code()).setThrottleTimeMs(0)));
    // handle completed receives
    client.poll(0, time.milliseconds());
    // the ApiVersionsRequest is gone
    assertFalse(client.hasInFlightRequests(node.idString()));
    // various assertions
    assertFalse(client.isReady(node, time.milliseconds()));
}
Also used : ApiVersionsResponse(org.apache.kafka.common.requests.ApiVersionsResponse) ApiVersionsResponseData(org.apache.kafka.common.message.ApiVersionsResponseData) Test(org.junit.jupiter.api.Test)

Aggregations

ApiVersionsResponse (org.apache.kafka.common.requests.ApiVersionsResponse)30 ApiVersionsRequest (org.apache.kafka.common.requests.ApiVersionsRequest)11 ByteBuffer (java.nio.ByteBuffer)10 Test (org.junit.jupiter.api.Test)8 RequestHeader (org.apache.kafka.common.requests.RequestHeader)7 ApiVersion (org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion)6 NetworkReceive (org.apache.kafka.common.network.NetworkReceive)6 SecurityProtocol (org.apache.kafka.common.security.auth.SecurityProtocol)6 ApiVersionsResponseData (org.apache.kafka.common.message.ApiVersionsResponseData)5 ListenerName (org.apache.kafka.common.network.ListenerName)5 SaslHandshakeResponse (org.apache.kafka.common.requests.SaslHandshakeResponse)5 HashMap (java.util.HashMap)4 SaslChannelBuilder (org.apache.kafka.common.network.SaslChannelBuilder)4 JaasContext (org.apache.kafka.common.security.JaasContext)4 Map (java.util.Map)3 ClientRequest (org.apache.kafka.clients.ClientRequest)3 ApiVersionCollection (org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersionCollection)3 NetworkSend (org.apache.kafka.common.network.NetworkSend)3 NioEchoServer (org.apache.kafka.common.network.NioEchoServer)3 TransportLayer (org.apache.kafka.common.network.TransportLayer)3