pulsar-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zhai...@apache.org
Subject [pulsar.wiki] branch master updated: PIP-30: change authentication provider API to support mutual authentication(like SASL)
Date Sat, 23 Feb 2019 13:02:45 GMT
This is an automated email from the ASF dual-hosted git repository.

zhaijia pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.wiki.git


The following commit(s) were added to refs/heads/master by this push:
     new f69f2c9  PIP-30: change authentication provider API to support mutual authentication(like
SASL)
f69f2c9 is described below

commit f69f2c98861bcbe52c0cf579a310d8abe220c5be
Author: Jia Zhai <zhaijia@apache.org>
AuthorDate: Sat Feb 23 21:02:44 2019 +0800

    PIP-30: change authentication provider API to support mutual authentication(like SASL)
---
 ...-to-support-mutual-authentication(like-SASL).md | 194 +++++++++++++++++++++
 1 file changed, 194 insertions(+)

diff --git a/PIP-30:-change-authentication-provider-API-to-support-mutual-authentication(like-SASL).md
b/PIP-30:-change-authentication-provider-API-to-support-mutual-authentication(like-SASL).md
new file mode 100644
index 0000000..c23d375
--- /dev/null
+++ b/PIP-30:-change-authentication-provider-API-to-support-mutual-authentication(like-SASL).md
@@ -0,0 +1,194 @@
+* **Status**: Discussing
+* **Author**: Jia Zhai
+* **Pull Request**: 
+* **Mailing List discussion**:
+* **Release**: 
+
+
+## Motivation
+
+Pulsar has a [pluggable authentication mechanism](http://pulsar.apache.org/docs/en/security-extending/#authentication)
that currently supports several auth providers.
+But currently all the provided authentication are a kind of “single-step" authentication.
And under current api it is not able to support mutual authentication between client and server,
such as [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer). 
+So this PIP is try to discuss the interface changes to support mutual authentication.
+
+## Proposal
+
+In Pulsar, authentication is happened when a new connection is creating between client and
broker. When connecting, Client sends authentication data to Broker by `CommandConnect`, and
Broker do the authentication and once success send command `CommandConnected` back to client.
+
+In PulsarApi.proto, [CommandConnect](https://github.com/apache/pulsar/blob/master/pulsar-common/src/main/proto/PulsarApi.proto#L173),
it contains `auth_method_name` and `auth_data` fields.  But broker no need to send auth data
to client, so CommandConnected not contains auth data.
+
+```
+message CommandConnect {
+	required string client_version = 1;
+	optional AuthMethod auth_method = 2; // Deprecated. Use "auth_method_name" instead.
+	optional string auth_method_name = 5;
+	optional bytes auth_data = 3;
+	…
+}
+
+message CommandConnected {
+	required string server_version = 1;
+	optional int32 protocol_version = 2 [default = 0];
+}
+```
+
+The propose is to reuse these 2 commands related to connecting and auth, and also add auth
data fields in CommandConnected. So when server need send auth data back to client, broker
could reuse command CommandConnected. 
+
+A basic logic for the mutual authentication is like this :
+
+1, Client side newConnectCommand(authDataClient) and send to Broker;
+2, Broker side handleConnect(authDataClient),  do the auth in Broker side, and get authDataBroker.
+- If auth is complete Broker.newConnected(), finish the auth, and send command back to Client.
+- If auth is not complete, Broker.newConnecting(authDataBroker) and send command back to
Client.
+3, Client side
+- If received Connected command, complete the auth, and connection established. 
+- If received Connecting command, do the auth with authDataBroker, and get authDataClient,
then send connect command back to Broker. Broker will repeat the process of step 2 until auth
complete.
+
+
+## Changes
+
+### Proto
+In PulsarApi.proto, [CommandConnected](https://github.com/apache/pulsar/blob/master/pulsar-common/src/main/proto/PulsarApi.proto#L197)
need to add auth data fields. So Broker could reuse this command to send auth data back to
Client.
+
+```
+message CommandConnected {
+	required string server_version = 1;
+	optional int32 protocol_version = 2 [default = 0];
+
+	// To support mutual authentication type, such as Sasl, reuse this command to do mutual
auth.
+	optional string auth_method_name = 3;
+	optional bytes auth_data = 4;
+}
+```
+
+### API changes
+
+#### setCommandData changes in `AuthenticationDataSource` and `AuthenticationDataProvider`:
+
+When establish connection(between client and broker), in each connection, Broker side will
have a [AuthenticationDataSource](https://github.com/apache/pulsar/blob/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java),
and Client side will have a 
+[AuthenticationProvider.java](https://github.com/apache/pulsar/blob/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProvider.java).
They both have a `getCommandData` method, which get command data from proto command.
+To achieve the mutual authn, we need a method `authenticate`, which instead `getCommandData`
but will compute use passed in data and return evaluated and challenged data back to peer.
+
+And since in PulsarApi.proto, we store auth data as bytes in [CommandConnected](https://github.com/apache/pulsar/blob/master/pulsar-common/src/main/proto/PulsarApi.proto#L197)
and `CommandConnected` command, and in [SaslServer](https://docs.oracle.com/javase/7/docs/api/javax/security/sasl/SaslServer.html)
and [SaslClient](https://docs.oracle.com/javase/7/docs/api/javax/security/sasl/SaslClient.html),
it do the valuate and challenge also use bytes, like this:
+```
+byte[]  evaluateResponse(byte[] response)
+```
+It would be better to use byte[] in method `authenticate`, this could avoid the converting

+`bytes(proto cmd) <-> String(pulsar api) <-> bytes(sasl api)`
+each time. 
+
+
+Client change in: [AuthenticationProvider.java](https://github.com/apache/pulsar/blob/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProvider.java)
+
+```
+    /**
+     * @return authentication data which is stored in a command
+     */
+    default String getCommandData() {
+        return null;
+    }
+
+    /**
+     * For mutual authentication, This method use passed in `data` to evaluate and challenge,
+     * then returns null if authentication has completed;
+     * returns authenticated data back to peer side, if authentication has not completed.
+     *
+     * used for mutual authentication like sasl.
+     */
+    default byte[] authenticate(byte[] data) throws IOException { // << add this method
+        throw new UnsupportedOperationException();
+    }
+```
+
+Additionally, broker side will check whether `authenticate` returns null or not to know whether
the authentication between client and broker is completed. And Broker could make decision
of send different command back to Client. If it is complete, send Connected command, and client
will stop the mutual authn; else send Connecting command, and client will continue mutual
authn.
+Broker change in: [AuthenticationDataSource](https://github.com/apache/pulsar/blob/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java)
+
+```
+    /**
+     * @return authentication data which is stored in a command
+     */
+    default String getCommandData() {
+        return null;
+    }
+
+    /**
+     * For mutual authentication, This method use passed in `data` to evaluate and challenge,
+     * then returns null if authentication has completed;
+     * returns authenticated data back to peer side, if authentication has not completed.
+     *
+     * used for mutual authentication like sasl.
+     */
+    default byte[] authenticate(byte[] data) throws IOException { // << add this method
+        throw new UnsupportedOperationException();
+    }
+```
+
+
+
+#### Return the specific `AuthenticationDataProvider` for each new Connection in ClientCnx
+
+As mentioned above, we leverage [AuthenticationDataProvider](https://github.com/apache/pulsar/blob/master/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/AuthenticationDataProvider.java)
in client to keep authn data between [ClientCnx](https://github.com/apache/pulsar/blob/master/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientCnx.java)
and Broker. But current [Authentication](https://github.com/apache/pulsar/blob/master/pulsar-client-api/src/main/java/org/ap
[...]
+Need to add a method to Get/Create an `AuthenticationDataProvider` which provides the data
provider that stands for the specific broker.
+
+```
+    /**
+     *
+     * Get/Create an authentication data provider which provides the data that this client
will be sent to the broker.
+     * Some authentication method need to authn between each client channel. So it need the
broker, who it will talk to.
+     *
+     * @param brokerHostName
+     *          target broker host name
+     *
+     * @return The authentication data provider
+     */
+    default AuthenticationDataProvider getAuthData(String brokerHostName) throws PulsarClientException
{
+        throw new UnsupportedAuthenticationException("Method not implemented!");
+    }
+```
+
+
+#### Bring in an `AuthState` interface to unify the auth process in ServerCnx.
+
+This is a suggestion from @sijie, in this [codereview](https://github.com/apache/pulsar/pull/3658#discussion_r259224620).
+An `AuthState` basically is holding the authentication state, tell broker whether the authentication
is completed or not, if completed, what is the AuthRole.
+
+```
+interface AuthState {
+    AuthenticationData getAuthData();
+    String getAuthRole();
+
+    /**
+      * Returns null if authentication has completed, and no auth data is required to send
back to client.
+      * Returns the auth data back to client, if authentication has not completed.
+      */
+    byte[] authenticate(byte[] authData);
+
+}
+```
+
+Then add a `newAuthState` in the AuthenticationProvider. The default implementation can be
the `OneStageAuthState`, It can be shared across existing authentication providers. 
+
+
+So the implementation in serverCnx can be very simple:
+
+```
+AuthState authState = authenticationProvider.newAuthState();
+
+byte[] clientAuthData = connect.getAuthData().toByteArray();
+byte[] brokerAuthData = authState.authenticate(clientAuthData);
+if (null == brokerAuthData) {
+     // authentication has completed.
+     authData = authState.getAuthenticateData();
+     authRole = authState.getAuthRole();
+     // we are done here
+} else {
+     // it is a multi-stage authentication mechanism, send the auth data back
+     ctx.writeAndFlush(Commands.newConnecting(authMethod, data));
+}
+```
+
+For the current existing authentication providers, implement a common OneStageAuthState.
+For the mutual authenciation provider, like SASL, implement a SaslAuthState.
+This would produce a clean interface between AuthenticationProvider and Brokers and avoiding
add Sasl specific logic in ServerCnx.
+
+To reference a more detailed changes at [here](https://github.com/apache/pulsar/compare/master...jiazhai:pip_auth)
\ No newline at end of file


Mime
View raw message