0
# Security
1
2
SASL-based authentication system for secure shuffle operations, managing application secrets and authenticating client connections.
3
4
## Capabilities
5
6
### ShuffleSecretManager
7
8
Central component for managing SASL authentication secrets for shuffle operations.
9
10
```java { .api }
11
/**
12
* A class that manages shuffle secret used by the external shuffle service.
13
* Implements SecretKeyHolder for SASL authentication integration.
14
*/
15
public class ShuffleSecretManager implements SecretKeyHolder {
16
/**
17
* Creates a new shuffle secret manager with empty secret map.
18
*/
19
public ShuffleSecretManager();
20
21
/**
22
* Register an application with its secret.
23
* Executors need to first authenticate themselves with the same secret before
24
* fetching shuffle files written by other executors in this application.
25
*
26
* @param appId application identifier
27
* @param shuffleSecret secret key for this application
28
*/
29
public void registerApp(String appId, String shuffleSecret);
30
31
/**
32
* Unregister an application and remove its secret.
33
* Called when application terminates.
34
*
35
* @param appId application identifier
36
*/
37
public void unregisterApp(String appId);
38
39
/**
40
* Get the secret key for a given application.
41
* Implementation of SecretKeyHolder interface for SASL integration.
42
*
43
* @param appId application identifier
44
* @return secret key for the application, or null if not registered
45
*/
46
@Override
47
public String getSecretKey(String appId);
48
49
/**
50
* Get the SASL user for a given application.
51
* Returns the standard Spark SASL user identifier.
52
*
53
* @param appId application identifier (unused, same user for all apps)
54
* @return SASL user identifier ("sparkSaslUser")
55
*/
56
@Override
57
public String getSaslUser(String appId);
58
}
59
```
60
61
**Usage Examples:**
62
63
```java
64
import org.apache.spark.network.sasl.ShuffleSecretManager;
65
66
// Create secret manager
67
ShuffleSecretManager secretManager = new ShuffleSecretManager();
68
69
// Register application secrets
70
String appId1 = "application_1234567890_0001";
71
String secret1 = "app1-secret-key-abcd1234";
72
secretManager.registerApp(appId1, secret1);
73
74
String appId2 = "application_1234567890_0002";
75
String secret2 = "app2-secret-key-efgh5678";
76
secretManager.registerApp(appId2, secret2);
77
78
// Retrieve secrets for authentication
79
String retrievedSecret = secretManager.getSecretKey(appId1);
80
if (retrievedSecret != null) {
81
System.out.println("Secret found for app: " + appId1);
82
// Use secret for SASL authentication
83
} else {
84
System.err.println("No secret registered for app: " + appId1);
85
}
86
87
// Get SASL user (same for all applications)
88
String saslUser = secretManager.getSaslUser(appId1);
89
System.out.println("SASL user: " + saslUser); // Prints: sparkSaslUser
90
91
// Clean up when application terminates
92
secretManager.unregisterApp(appId1);
93
System.out.println("Unregistered app: " + appId1);
94
```
95
96
### SASL Authentication Integration
97
98
The secret manager integrates with Spark's SASL authentication system:
99
100
```java
101
// Integration with ExternalShuffleClient
102
import org.apache.spark.network.sasl.SecretKeyHolder;
103
import org.apache.spark.network.shuffle.ExternalShuffleClient;
104
import org.apache.spark.network.util.TransportConf;
105
106
// Create client with SASL authentication enabled
107
TransportConf conf = new TransportConf("shuffle");
108
ShuffleSecretManager secretManager = new ShuffleSecretManager();
109
110
// Register application secret
111
secretManager.registerApp("myApp", "mySecretKey");
112
113
// Create authenticated client
114
ExternalShuffleClient client = new ExternalShuffleClient(
115
conf,
116
secretManager, // SecretKeyHolder for authentication
117
true // Enable authentication
118
);
119
120
client.init("myApp");
121
// Client will automatically authenticate using registered secret
122
```
123
124
### Server-Side Authentication
125
126
The shuffle service validates client authentication:
127
128
```java
129
import org.apache.spark.network.shuffle.ExternalShuffleBlockHandler;
130
import org.apache.spark.network.client.TransportClient;
131
132
// Server-side authentication check (simplified example)
133
public class AuthenticatedShuffleHandler extends ExternalShuffleBlockHandler {
134
135
private void checkAuth(TransportClient client, String appId) {
136
if (client.getClientId() != null && !client.getClientId().equals(appId)) {
137
throw new SecurityException(String.format(
138
"Client for %s not authorized for application %s.",
139
client.getClientId(), appId));
140
}
141
}
142
143
// Authentication is enforced in message handling
144
protected void handleMessage(BlockTransferMessage msgObj,
145
TransportClient client,
146
RpcResponseCallback callback) {
147
try {
148
if (msgObj instanceof OpenBlocks) {
149
OpenBlocks msg = (OpenBlocks) msgObj;
150
checkAuth(client, msg.appId); // Validate client authorization
151
// Process request...
152
} else if (msgObj instanceof RegisterExecutor) {
153
RegisterExecutor msg = (RegisterExecutor) msgObj;
154
checkAuth(client, msg.appId); // Validate client authorization
155
// Process registration...
156
}
157
} catch (SecurityException e) {
158
callback.onFailure(e); // Return authentication error to client
159
}
160
}
161
}
162
```
163
164
## Security Configuration
165
166
### Transport Configuration for SASL
167
168
```java
169
import org.apache.spark.network.util.TransportConf;
170
import org.apache.spark.network.util.ConfigProvider;
171
172
// Configure transport for SASL authentication
173
ConfigProvider configProvider = new ConfigProvider() {
174
@Override
175
public String get(String name) {
176
switch (name) {
177
case "spark.authenticate":
178
return "true";
179
case "spark.network.sasl.maxEncryptedBlockSize":
180
return "64k";
181
case "spark.network.sasl.serverAlwaysEncrypt":
182
return "true";
183
default:
184
return null;
185
}
186
}
187
188
@Override
189
public Iterable<Map.Entry<String, String>> getAll() {
190
Map<String, String> configs = new HashMap<>();
191
configs.put("spark.authenticate", "true");
192
configs.put("spark.network.sasl.maxEncryptedBlockSize", "64k");
193
configs.put("spark.network.sasl.serverAlwaysEncrypt", "true");
194
return configs.entrySet();
195
}
196
};
197
198
TransportConf conf = new TransportConf("shuffle", configProvider);
199
```
200
201
### Secret Generation and Management
202
203
```java
204
import java.security.SecureRandom;
205
import java.util.Base64;
206
207
/**
208
* Example utility for generating secure shuffle secrets
209
*/
210
public class SecretGenerator {
211
private static final SecureRandom random = new SecureRandom();
212
213
/**
214
* Generates a cryptographically secure random secret.
215
*
216
* @param lengthBytes length of secret in bytes
217
* @return base64-encoded secret string
218
*/
219
public static String generateSecret(int lengthBytes) {
220
byte[] secretBytes = new byte[lengthBytes];
221
random.nextBytes(secretBytes);
222
return Base64.getEncoder().encodeToString(secretBytes);
223
}
224
225
/**
226
* Generates a 256-bit (32 byte) secret suitable for shuffle authentication.
227
*/
228
public static String generateShuffleSecret() {
229
return generateSecret(32);
230
}
231
}
232
233
// Usage example
234
String appId = "application_1234567890_0001";
235
String secret = SecretGenerator.generateShuffleSecret();
236
secretManager.registerApp(appId, secret);
237
System.out.println("Generated secret for " + appId + ": " + secret.substring(0, 8) + "...");
238
```
239
240
## Security Best Practices
241
242
### Secret Lifecycle Management
243
244
```java
245
/**
246
* Example application lifecycle manager with proper secret handling
247
*/
248
public class SecureShuffleLifecycleManager {
249
private final ShuffleSecretManager secretManager;
250
private final Map<String, String> appSecrets;
251
252
public SecureShuffleLifecycleManager() {
253
this.secretManager = new ShuffleSecretManager();
254
this.appSecrets = new ConcurrentHashMap<>();
255
}
256
257
/**
258
* Register application with generated secret
259
*/
260
public void registerApplication(String appId) {
261
// Generate unique secret for this application
262
String secret = SecretGenerator.generateShuffleSecret();
263
264
// Store secret securely (in production, use secure key management)
265
appSecrets.put(appId, secret);
266
secretManager.registerApp(appId, secret);
267
268
System.out.println("Registered application with secure secret: " + appId);
269
}
270
271
/**
272
* Clean up application secrets when application terminates
273
*/
274
public void unregisterApplication(String appId) {
275
// Remove from secret manager
276
secretManager.unregisterApp(appId);
277
278
// Clear secret from memory
279
String secret = appSecrets.remove(appId);
280
if (secret != null) {
281
// Overwrite secret in memory (basic security measure)
282
char[] secretChars = secret.toCharArray();
283
Arrays.fill(secretChars, '\0');
284
}
285
286
System.out.println("Unregistered application and cleared secrets: " + appId);
287
}
288
289
/**
290
* Get secret manager for client configuration
291
*/
292
public ShuffleSecretManager getSecretManager() {
293
return secretManager;
294
}
295
}
296
```
297
298
### Error Handling and Security Logging
299
300
```java
301
/**
302
* Secure error handling with appropriate logging
303
*/
304
public class SecureErrorHandler {
305
private static final Logger logger = LoggerFactory.getLogger(SecureErrorHandler.class);
306
307
public void handleAuthenticationFailure(String appId, String clientId, Exception e) {
308
// Log security events (without exposing secrets)
309
logger.warn("Authentication failed for app {} from client {}: {}",
310
appId, clientId, e.getMessage());
311
312
// In production, consider:
313
// - Rate limiting failed attempts
314
// - Alerting security teams
315
// - Temporary blocking of suspicious clients
316
}
317
318
public void handleSecretRegistration(String appId) {
319
logger.info("Registered shuffle secret for application: {}", appId);
320
// Do NOT log the actual secret value
321
}
322
323
public void handleSecretUnregistration(String appId) {
324
logger.info("Unregistered shuffle secret for application: {}", appId);
325
}
326
}
327
```
328
329
## Common Security Scenarios
330
331
### Client Authentication Flow
332
333
```java
334
// 1. Application registers with shuffle service
335
String appId = "spark-app-123";
336
String secret = generateSecureSecret();
337
shuffleSecretManager.registerApp(appId, secret);
338
339
// 2. Client connects with authentication
340
TransportConf conf = new TransportConf("shuffle");
341
ExternalShuffleClient client = new ExternalShuffleClient(conf, shuffleSecretManager, true);
342
client.init(appId);
343
344
// 3. Client performs authenticated operations
345
try {
346
client.registerWithShuffleServer(host, port, execId, executorInfo);
347
client.fetchBlocks(host, port, execId, blockIds, listener, null);
348
} catch (SecurityException e) {
349
System.err.println("Authentication failed: " + e.getMessage());
350
// Handle authentication failure
351
}
352
```
353
354
### Multi-Application Security
355
356
```java
357
// Managing secrets for multiple applications
358
ShuffleSecretManager secretManager = new ShuffleSecretManager();
359
360
// Register multiple applications with different secrets
361
Map<String, String> appSecrets = new HashMap<>();
362
for (int i = 1; i <= 5; i++) {
363
String appId = "application_" + System.currentTimeMillis() + "_000" + i;
364
String secret = SecretGenerator.generateShuffleSecret();
365
366
secretManager.registerApp(appId, secret);
367
appSecrets.put(appId, secret);
368
369
System.out.println("Registered app " + i + ": " + appId);
370
}
371
372
// Verify secrets are isolated
373
for (String appId : appSecrets.keySet()) {
374
String retrievedSecret = secretManager.getSecretKey(appId);
375
assert retrievedSecret.equals(appSecrets.get(appId)) : "Secret mismatch for " + appId;
376
}
377
378
System.out.println("All application secrets verified");
379
```
380
381
## Error Handling
382
383
Security-related errors that may occur:
384
385
- **SecurityException**: Client authentication failures, unauthorized access attempts
386
- **IllegalArgumentException**: Invalid application IDs, malformed secrets
387
- **NullPointerException**: Missing secrets for registered applications
388
389
**Security Error Handling Example:**
390
391
```java
392
try {
393
String secret = secretManager.getSecretKey(appId);
394
if (secret == null) {
395
throw new SecurityException("No secret registered for application: " + appId);
396
}
397
// Use secret for authentication
398
} catch (SecurityException e) {
399
logger.warn("Security violation: {}", e.getMessage());
400
// Handle security failure appropriately
401
throw e; // Re-throw to caller
402
}
403
```