or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-commands.mdconfiguration.mdindex.mdnode-management.mdrequest-routing.mdsecurity.mdsession-distribution.mdsession-queuing.mdsession-storage.md

security.mddocs/

0

# Security Features

1

2

Selenium Grid provides authentication and authorization mechanisms to secure grid endpoints, manage access control, and protect sensitive operations through secret management and request filtering.

3

4

## Capabilities

5

6

### Secret Management

7

8

Core secret handling for authentication and authorization across grid components.

9

10

```java { .api }

11

/**

12

* Wrapper for sensitive values like passwords and API keys

13

*/

14

class Secret {

15

// Implementation intentionally opaque for security

16

17

/** Create a secret from a string value */

18

static Secret fromString(String value);

19

20

/** Create a secret from environment variable */

21

static Secret fromEnvironment(String envVar);

22

23

/** Create a secret from file contents */

24

static Secret fromFile(Path filePath) throws IOException;

25

26

/** Check if this secret matches another secret */

27

boolean matches(Secret other);

28

29

/** Check if this secret matches a string value */

30

boolean matches(String value);

31

32

// Note: No methods to retrieve the actual secret value

33

// This prevents accidental logging or exposure

34

}

35

```

36

37

**Usage Example:**

38

39

```java

40

// Create secrets from various sources

41

Secret registrationSecret = Secret.fromEnvironment("SE_REGISTRATION_SECRET");

42

Secret apiKey = Secret.fromFile(Paths.get("/etc/selenium/api-key"));

43

Secret password = Secret.fromString("my-secure-password");

44

45

// Use secrets for authentication

46

Node node = LocalNode.builder(tracer, eventBus, nodeUri, gridUri, registrationSecret)

47

.build();

48

49

// Secrets automatically used in HTTP headers for authentication

50

// Grid components validate secrets before processing requests

51

```

52

53

### Basic Authentication Filter

54

55

HTTP Basic Authentication support for protecting grid endpoints.

56

57

```java { .api }

58

/**

59

* HTTP Basic Authentication filter for grid endpoints

60

*/

61

class BasicAuthenticationFilter implements Filter {

62

/** Create filter with username and password secret */

63

BasicAuthenticationFilter(String username, Secret password);

64

65

/** Create filter from configuration */

66

static BasicAuthenticationFilter create(Config config);

67

68

@Override

69

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

70

throws IOException, ServletException;

71

}

72

```

73

74

**Configuration Example:**

75

76

```toml

77

[server]

78

enable-basic-auth = true

79

username = "admin"

80

password = "secret_password"

81

82

[server.auth]

83

realm = "Selenium Grid"

84

```

85

86

### Secret Validation Filter

87

88

Filter that validates request secrets for API access control.

89

90

```java { .api }

91

/**

92

* Filter that validates secret tokens in request headers

93

*/

94

class RequiresSecretFilter implements Filter {

95

/** Create filter with required secret */

96

RequiresSecretFilter(Secret requiredSecret);

97

98

/** Create filter from configuration */

99

static RequiresSecretFilter create(Config config);

100

101

@Override

102

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

103

throws IOException, ServletException;

104

105

/** Get the expected header name for the secret */

106

static String getSecretHeaderName();

107

}

108

```

109

110

**Usage Example:**

111

112

```java

113

// Configure secret validation

114

Secret apiSecret = Secret.fromEnvironment("SE_API_SECRET");

115

RequiresSecretFilter secretFilter = new RequiresSecretFilter(apiSecret);

116

117

// Apply to specific endpoints

118

server.addFilter(secretFilter, "/grid/api/*");

119

120

// Clients must include secret in headers

121

// X-Registration-Secret: <secret-value>

122

```

123

124

### Configuration Options

125

126

Security-specific configuration settings.

127

128

```java { .api }

129

/**

130

* Configuration options for security features

131

*/

132

class SecretOptions {

133

static final String SECRET_SECTION = "secret";

134

135

/** Get registration secret for node authentication */

136

Secret getRegistrationSecret(Config config);

137

138

/** Get API access secret */

139

Secret getApiSecret(Config config);

140

141

/** Get whether to require secrets for node registration */

142

boolean requireRegistrationSecret(Config config);

143

144

/** Get whether to require secrets for API access */

145

boolean requireApiSecret(Config config);

146

}

147

148

/**

149

* Security-related command-line flags

150

*/

151

class SecurityFlags {

152

@Parameter(names = {"--registration-secret"},

153

description = "Secret required for node registration")

154

String registrationSecret;

155

156

@Parameter(names = {"--api-secret"},

157

description = "Secret required for API access")

158

String apiSecret;

159

160

@Parameter(names = {"--require-registration-secret"},

161

description = "Require secret for node registration")

162

boolean requireRegistrationSecret = false;

163

164

@Parameter(names = {"--enable-basic-auth"},

165

description = "Enable HTTP Basic Authentication")

166

boolean enableBasicAuth = false;

167

168

@Parameter(names = {"--basic-auth-username"},

169

description = "Username for Basic Authentication")

170

String basicAuthUsername = "admin";

171

172

@Parameter(names = {"--basic-auth-password"},

173

description = "Password for Basic Authentication")

174

String basicAuthPassword;

175

}

176

```

177

178

## Authentication Patterns

179

180

### Node Registration Security

181

182

```java

183

// Secure node registration with shared secret

184

public class SecureNodeRegistration {

185

public static void registerNode() {

186

// Node includes registration secret

187

Secret registrationSecret = Secret.fromEnvironment("SE_REGISTRATION_SECRET");

188

189

Node node = LocalNode.builder(tracer, eventBus, nodeUri, gridUri, registrationSecret)

190

.add(chromeCapabilities, chromeFactory)

191

.build();

192

193

// Grid validates secret before accepting registration

194

// If secret doesn't match, registration is rejected

195

}

196

197

// Grid side validation

198

public boolean validateNodeRegistration(HttpRequest request, Secret expectedSecret) {

199

String secretHeader = request.getHeader("X-Registration-Secret");

200

if (secretHeader == null) {

201

return !requireRegistrationSecret; // Allow if not required

202

}

203

204

return expectedSecret.matches(secretHeader);

205

}

206

}

207

```

208

209

### API Access Control

210

211

```java

212

// Protect sensitive grid operations

213

@Path("/grid/admin")

214

public class AdminEndpoints {

215

216

@POST

217

@Path("/shutdown")

218

@RequiresSecret

219

public Response shutdownGrid() {

220

// Only accessible with valid API secret

221

gridManager.shutdown();

222

return Response.ok().build();

223

}

224

225

@DELETE

226

@Path("/sessions")

227

@RequiresSecret

228

public Response clearAllSessions() {

229

// Requires API secret to clear all sessions

230

sessionMap.clear();

231

return Response.ok().build();

232

}

233

}

234

235

// Custom annotation for secret requirement

236

@Target({METHOD, TYPE})

237

@Retention(RUNTIME)

238

public @interface RequiresSecret {

239

}

240

```

241

242

### TLS/SSL Configuration

243

244

```java

245

// Configure HTTPS for grid communications

246

public class TLSConfiguration {

247

public static Server createSecureServer(Config config) {

248

ServerOptions serverOptions = new ServerOptions(config);

249

250

if (serverOptions.isTlsEnabled()) {

251

return new NettyServer(

252

serverOptions,

253

HttpHandler.combine(

254

new TlsHandler(

255

serverOptions.getKeyStore(),

256

serverOptions.getKeyStorePassword(),

257

serverOptions.getTrustStore()

258

),

259

routeHandlers

260

)

261

);

262

}

263

264

return new NettyServer(serverOptions, routeHandlers);

265

}

266

}

267

```

268

269

**TLS Configuration Example:**

270

271

```toml

272

[server]

273

https = true

274

keystore = "/etc/selenium/keystore.jks"

275

keystore-password = "keystore_password"

276

truststore = "/etc/selenium/truststore.jks"

277

```

278

279

## Authorization Patterns

280

281

### Role-Based Access Control

282

283

```java

284

// Different access levels for different operations

285

public enum GridRole {

286

ADMIN("admin"), // Full access to all operations

287

OPERATOR("operator"), // Can manage sessions and nodes

288

VIEWER("viewer"); // Read-only access

289

290

private final String roleName;

291

292

GridRole(String roleName) {

293

this.roleName = roleName;

294

}

295

}

296

297

public class RoleBasedAuthFilter implements Filter {

298

private final Map<String, Set<GridRole>> endpointRoles;

299

300

@Override

301

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {

302

String path = ((HttpServletRequest) request).getPathInfo();

303

Set<GridRole> requiredRoles = endpointRoles.get(path);

304

305

if (requiredRoles != null && !hasRequiredRole(request, requiredRoles)) {

306

((HttpServletResponse) response).setStatus(403);

307

return;

308

}

309

310

chain.doFilter(request, response);

311

}

312

313

private boolean hasRequiredRole(ServletRequest request, Set<GridRole> required) {

314

// Extract user roles from token/certificate/etc.

315

Set<GridRole> userRoles = extractUserRoles(request);

316

return userRoles.stream().anyMatch(required::contains);

317

}

318

}

319

```

320

321

### IP-Based Access Control

322

323

```java

324

// Restrict access by client IP address

325

public class IPWhitelistFilter implements Filter {

326

private final Set<String> allowedIPs;

327

private final Set<String> allowedSubnets;

328

329

public IPWhitelistFilter(Config config) {

330

this.allowedIPs = Set.of(config.get("security", "allowed-ips").orElse("").split(","));

331

this.allowedSubnets = Set.of(config.get("security", "allowed-subnets").orElse("").split(","));

332

}

333

334

@Override

335

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {

336

String clientIP = request.getRemoteAddr();

337

338

if (!isAllowedIP(clientIP)) {

339

((HttpServletResponse) response).setStatus(403);

340

return;

341

}

342

343

chain.doFilter(request, response);

344

}

345

346

private boolean isAllowedIP(String clientIP) {

347

return allowedIPs.contains(clientIP) ||

348

allowedSubnets.stream().anyMatch(subnet -> isInSubnet(clientIP, subnet));

349

}

350

}

351

```

352

353

## Security Best Practices

354

355

### Secret Rotation

356

357

```java

358

// Support for rotating secrets without downtime

359

public class RotatingSecretManager {

360

private final List<Secret> validSecrets; // Current + previous secrets

361

private final ScheduledExecutorService scheduler;

362

363

public boolean validateSecret(String providedSecret) {

364

return validSecrets.stream()

365

.anyMatch(secret -> secret.matches(providedSecret));

366

}

367

368

public void rotateSecret(Secret newSecret) {

369

// Add new secret while keeping old one valid

370

validSecrets.add(0, newSecret); // New secret first

371

372

// Schedule removal of old secret after grace period

373

scheduler.schedule(() -> {

374

if (validSecrets.size() > 1) {

375

validSecrets.remove(validSecrets.size() - 1); // Remove oldest

376

}

377

}, 24, TimeUnit.HOURS);

378

}

379

}

380

```

381

382

### Audit Logging

383

384

```java

385

// Security event logging

386

public class SecurityAuditor {

387

private static final Logger auditLog = LoggerFactory.getLogger("SECURITY_AUDIT");

388

389

public void logAuthenticationAttempt(String clientIP, String username, boolean success) {

390

auditLog.info("AUTH_ATTEMPT: ip={}, user={}, success={}", clientIP, username, success);

391

}

392

393

public void logAPIAccess(String clientIP, String endpoint, String method, boolean authorized) {

394

auditLog.info("API_ACCESS: ip={}, endpoint={}, method={}, authorized={}",

395

clientIP, endpoint, method, authorized);

396

}

397

398

public void logNodeRegistration(String nodeIP, String nodeId, boolean accepted) {

399

auditLog.info("NODE_REGISTRATION: ip={}, nodeId={}, accepted={}", nodeIP, nodeId, accepted);

400

}

401

402

public void logSecurityViolation(String clientIP, String reason) {

403

auditLog.warn("SECURITY_VIOLATION: ip={}, reason={}", clientIP, reason);

404

}

405

}

406

```

407

408

### Secure Configuration

409

410

```toml

411

# Example secure configuration

412

[server]

413

https = true

414

keystore = "/etc/selenium/keystore.p12"

415

keystore-password = "${KEYSTORE_PASSWORD}"

416

enable-basic-auth = true

417

username = "admin"

418

password = "${ADMIN_PASSWORD}"

419

420

[security]

421

registration-secret = "${REGISTRATION_SECRET}"

422

api-secret = "${API_SECRET}"

423

require-registration-secret = true

424

allowed-ips = "127.0.0.1,10.0.0.0/8,192.168.0.0/16"

425

session-timeout = "300s"

426

max-login-attempts = 3

427

lockout-duration = "300s"

428

429

[audit]

430

enable-security-logging = true

431

log-file = "/var/log/selenium/security.log"

432

```

433

434

## Error Handling

435

436

```java

437

// Security-related error responses

438

public class SecurityErrorHandler {

439

440

public static Response handleAuthenticationFailure(String reason) {

441

// Don't reveal specific failure reasons

442

return Response.status(401)

443

.header("WWW-Authenticate", "Basic realm=\"Selenium Grid\"")

444

.entity(Map.of("error", "Authentication required"))

445

.build();

446

}

447

448

public static Response handleAuthorizationFailure() {

449

return Response.status(403)

450

.entity(Map.of("error", "Insufficient permissions"))

451

.build();

452

}

453

454

public static Response handleRateLimitExceeded() {

455

return Response.status(429)

456

.header("Retry-After", "300")

457

.entity(Map.of("error", "Rate limit exceeded"))

458

.build();

459

}

460

}