Skip to content

Commit 29bb1cc

Browse files
committed
[server][auth] Support SASL/PLAIN authentication.
1 parent ad8b70b commit 29bb1cc

33 files changed

+2938
-23
lines changed

fluss-auth/fluss-auth-sasl/pom.xml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2025 Alibaba Group Holding Ltd.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0"
18+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
<parent>
22+
<groupId>com.alibaba.fluss</groupId>
23+
<artifactId>fluss-auth</artifactId>
24+
<version>0.7-SNAPSHOT</version>
25+
</parent>
26+
27+
<artifactId>fluss-auth-sasl</artifactId>
28+
29+
<dependencies>
30+
<dependency>
31+
<groupId>com.alibaba.fluss</groupId>
32+
<artifactId>fluss-common</artifactId>
33+
<version>${project.version}</version>
34+
<scope>provided</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>com.alibaba.fluss</groupId>
38+
<artifactId>fluss-common</artifactId>
39+
<version>${project.version}</version>
40+
<type>test-jar</type>
41+
<scope>provided</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>com.alibaba.fluss</groupId>
45+
<artifactId>fluss-rpc</artifactId>
46+
<version>${project.version}</version>
47+
<scope>test</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>com.alibaba.fluss</groupId>
51+
<artifactId>fluss-rpc</artifactId>
52+
<version>${project.version}</version>
53+
<type>test-jar</type>
54+
<scope>provided</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>com.alibaba.fluss</groupId>
58+
<artifactId>fluss-test-utils</artifactId>
59+
<version>${project.version}</version>
60+
<scope>test</scope>
61+
</dependency>
62+
</dependencies>
63+
</project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.fluss.security.auth.sasl;
18+
19+
import javax.security.auth.callback.CallbackHandler;
20+
import javax.security.auth.login.AppConfigurationEntry;
21+
22+
import java.util.List;
23+
24+
/** An extension of {@link CallbackHandler} for Fluss that provides additional configuration. */
25+
public interface AuthenticateCallbackHandler extends CallbackHandler {
26+
27+
void configure(String saslMechanism, List<AppConfigurationEntry> jaasConfigEntries);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.fluss.security.auth.sasl.authenticator;
18+
19+
import com.alibaba.fluss.config.Configuration;
20+
import com.alibaba.fluss.security.auth.ClientAuthenticationPlugin;
21+
import com.alibaba.fluss.security.auth.ClientAuthenticator;
22+
import com.alibaba.fluss.security.auth.ServerAuthenticationPlugin;
23+
import com.alibaba.fluss.security.auth.ServerAuthenticator;
24+
25+
/** Authentication plugin for SASL. */
26+
public class SaslAuthenticationPlugin
27+
implements ClientAuthenticationPlugin, ServerAuthenticationPlugin {
28+
static final String SASL_AUTH_PROTOCOL = "sasl";
29+
30+
@Override
31+
public ClientAuthenticator createClientAuthenticator(Configuration configuration) {
32+
return new SaslClientAuthenticator(configuration);
33+
}
34+
35+
@Override
36+
public ServerAuthenticator createServerAuthenticator(Configuration configuration) {
37+
return new SaslServerAuthenticator(configuration);
38+
}
39+
40+
@Override
41+
public String authProtocol() {
42+
return SASL_AUTH_PROTOCOL;
43+
}
44+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) 2025 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.fluss.security.auth.sasl.authenticator;
18+
19+
import com.alibaba.fluss.config.ConfigOption;
20+
import com.alibaba.fluss.config.Configuration;
21+
import com.alibaba.fluss.exception.AuthenticationException;
22+
import com.alibaba.fluss.security.auth.ClientAuthenticator;
23+
import com.alibaba.fluss.security.auth.sasl.jaas.JaasContext;
24+
import com.alibaba.fluss.security.auth.sasl.jaas.LoginManager;
25+
26+
import javax.annotation.Nullable;
27+
import javax.security.auth.login.LoginException;
28+
import javax.security.sasl.SaslClient;
29+
30+
import java.util.Map;
31+
32+
import static com.alibaba.fluss.config.ConfigBuilder.key;
33+
import static com.alibaba.fluss.security.auth.sasl.jaas.JaasContext.SASL_JAAS_CONFIG;
34+
import static com.alibaba.fluss.security.auth.sasl.jaas.SaslServerFactory.createSaslClient;
35+
36+
/** An authenticator that uses SASL to authenticate with a server. */
37+
public class SaslClientAuthenticator implements ClientAuthenticator {
38+
public static final String CLIENT_SECURITY_PREFIX = "client.security.sasl.";
39+
private static final ConfigOption<String> CLIENT_MECHANISM =
40+
key(CLIENT_SECURITY_PREFIX + "mechanism")
41+
.stringType()
42+
.noDefaultValue()
43+
.withDescription(
44+
"SASL mechanism to use for authentication.Currently, we only support plain.");
45+
46+
private static final ConfigOption<String> CLIENT_SASL_JAAS_CONFIG =
47+
key(CLIENT_SECURITY_PREFIX + SASL_JAAS_CONFIG)
48+
.stringType()
49+
.noDefaultValue()
50+
.withDescription(
51+
"JAAS configuration string for the client. If not provided, uses the JVM option -Djava.security.auth.login.config. \n"
52+
+ "Example: com.alibaba.fluss.security.auth.sasl.plain.PlainLoginModule required username=\"admin\" password=\"wrong-secret\")");
53+
private final String mechanism;
54+
private final Map<String, String> pros;
55+
private final String jaasConfig;
56+
57+
private SaslClient saslClient;
58+
private LoginManager loginManager;
59+
60+
public SaslClientAuthenticator(Configuration configuration) {
61+
this.mechanism = configuration.get(CLIENT_MECHANISM).toUpperCase();
62+
this.jaasConfig = configuration.getString(CLIENT_SASL_JAAS_CONFIG);
63+
this.pros = configuration.toMap();
64+
}
65+
66+
@Override
67+
public String protocol() {
68+
return mechanism;
69+
}
70+
71+
@Nullable
72+
@Override
73+
public byte[] authenticate(byte[] data) throws AuthenticationException {
74+
try {
75+
return saslClient.evaluateChallenge(data);
76+
} catch (Exception e) {
77+
throw new AuthenticationException("Failed to evaluate SASL challenge", e);
78+
}
79+
}
80+
81+
@Override
82+
public boolean isCompleted() {
83+
return saslClient.isComplete();
84+
}
85+
86+
@Override
87+
public boolean hasInitialTokenResponse() {
88+
return saslClient.hasInitialResponse();
89+
}
90+
91+
@Override
92+
public void initialize(AuthenticateContext context) throws AuthenticationException {
93+
String hostAddress = context.ipAddress();
94+
JaasContext jaasContext = JaasContext.loadClientContext(jaasConfig);
95+
96+
try {
97+
loginManager = LoginManager.acquireLoginManager(jaasContext);
98+
} catch (LoginException exception) {
99+
throw new AuthenticationException("Failed to load login manager", exception);
100+
}
101+
102+
try {
103+
saslClient = createSaslClient(mechanism, hostAddress, pros, loginManager);
104+
} catch (Exception e) {
105+
throw new AuthenticationException("Failed to create SASL client", e);
106+
}
107+
108+
if (saslClient == null) {
109+
throw new AuthenticationException(
110+
"Unable to find a matching SASL mechanism for " + mechanism);
111+
}
112+
}
113+
114+
@Override
115+
public void close() {
116+
if (loginManager != null) {
117+
loginManager.release();
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)