or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-and-security.mdclassic-mode.mdcli-tools.mdconnection-factory.mdindex.mdregistry-and-discovery.mdservers.mdservices-protocols.mdstreams-and-channels.mdutilities.md

authentication-and-security.mddocs/

0

# Authentication and Security

1

2

Authentication mechanisms and security features for securing RPyC connections and services. Includes SSL/TLS authentication, custom authenticators, and security configuration options for protecting distributed applications.

3

4

## Capabilities

5

6

### SSL/TLS Authentication

7

8

SSL/TLS-based authentication providing encrypted communication and client certificate verification.

9

10

```python { .api }

11

class SSLAuthenticator:

12

"""

13

SSL/TLS authenticator providing certificate-based authentication.

14

Supports client certificate verification and encrypted communication.

15

"""

16

17

def __init__(self, keyfile, certfile, ca_certs=None, cert_reqs=None,

18

ssl_version=None, ciphers=None):

19

"""

20

Initialize SSL authenticator.

21

22

Parameters:

23

- keyfile (str): Path to private key file

24

- certfile (str): Path to certificate file

25

- ca_certs (str): Path to CA certificates file (optional)

26

- cert_reqs: Certificate requirements (ssl.CERT_NONE, ssl.CERT_OPTIONAL, ssl.CERT_REQUIRED)

27

- ssl_version: SSL/TLS version to use

28

- ciphers (str): Cipher suites to allow

29

"""

30

31

def __call__(self, sock):

32

"""

33

Authenticate connection using SSL/TLS.

34

35

Parameters:

36

- sock: Socket to authenticate

37

38

Returns:

39

tuple: (wrapped_socket, credentials) or raises AuthenticationError

40

"""

41

```

42

43

### Custom Authentication

44

45

Base classes and utilities for implementing custom authentication mechanisms.

46

47

```python { .api }

48

class Authenticator:

49

"""

50

Base class for custom authenticators.

51

Subclass to implement custom authentication logic.

52

"""

53

54

def __call__(self, sock):

55

"""

56

Authenticate connection.

57

58

Parameters:

59

- sock: Socket to authenticate

60

61

Returns:

62

tuple: (socket, credentials) or raises AuthenticationError

63

64

Note: Must be implemented by subclasses

65

"""

66

raise NotImplementedError()

67

68

def create_authenticator(auth_func):

69

"""

70

Create authenticator from function.

71

72

Parameters:

73

- auth_func (callable): Function taking socket, returning (socket, credentials)

74

75

Returns:

76

Authenticator: Callable authenticator object

77

"""

78

```

79

80

### Security Configuration

81

82

Security-related configuration options and utilities.

83

84

```python { .api }

85

# Security configuration constants

86

SECURE_DEFAULT_CONFIG = {

87

'allow_all_attrs': False, # Restrict attribute access

88

'allow_pickle': False, # Disable pickle serialization

89

'allow_setattr': False, # Disable attribute setting

90

'allow_delattr': False, # Disable attribute deletion

91

'allow_public_attrs': True, # Allow public attributes only

92

'exposed_prefix': 'exposed_', # Required prefix for exposed methods

93

'safe_attrs': frozenset([ # Safe attributes whitelist

94

'__abs__', '__add__', '__and__', '__bool__', '__cmp__',

95

'__contains__', '__div__', '__divmod__', '__doc__', '__eq__',

96

'__float__', '__floordiv__', '__ge__', '__getitem__',

97

'__gt__', '__hash__', '__hex__', '__iadd__', '__iand__',

98

'__idiv__', '__ifloordiv__', '__ilshift__', '__imod__',

99

'__imul__', '__int__', '__invert__', '__ior__', '__ipow__',

100

'__irshift__', '__isub__', '__iter__', '__itruediv__',

101

'__ixor__', '__le__', '__len__', '__long__', '__lshift__',

102

'__lt__', '__mod__', '__mul__', '__ne__', '__neg__',

103

'__next__', '__oct__', '__or__', '__pos__', '__pow__',

104

'__radd__', '__rand__', '__rdiv__', '__rdivmod__',

105

'__repr__', '__rfloordiv__', '__rlshift__', '__rmod__',

106

'__rmul__', '__ror__', '__rpow__', '__rrshift__',

107

'__rshift__', '__rsub__', '__rtruediv__', '__rxor__',

108

'__setitem__', '__str__', '__sub__', '__truediv__',

109

'__xor__', 'next'

110

]),

111

'instantiate_custom_exceptions': False,

112

'propagate_SystemExit_locally': False,

113

'propagate_KeyboardInterrupt_locally': False,

114

}

115

116

def create_secure_config(additional_options=None):

117

"""

118

Create secure configuration for RPyC connections.

119

120

Parameters:

121

- additional_options (dict): Additional configuration options

122

123

Returns:

124

dict: Secure configuration dictionary

125

"""

126

```

127

128

### Access Control Utilities

129

130

Utilities for implementing fine-grained access control.

131

132

```python { .api }

133

def restricted_service(service_class, allowed_methods=None, denied_methods=None):

134

"""

135

Create restricted version of service class.

136

137

Parameters:

138

- service_class: Service class to restrict

139

- allowed_methods (set): Set of allowed method names

140

- denied_methods (set): Set of denied method names

141

142

Returns:

143

class: Restricted service class

144

"""

145

146

class SecurityPolicy:

147

"""

148

Security policy for controlling access to service methods and attributes.

149

"""

150

151

def __init__(self, allowed_methods=None, denied_methods=None,

152

allowed_attrs=None, denied_attrs=None):

153

"""

154

Initialize security policy.

155

156

Parameters:

157

- allowed_methods (set): Allowed method names

158

- denied_methods (set): Denied method names

159

- allowed_attrs (set): Allowed attribute names

160

- denied_attrs (set): Denied attribute names

161

"""

162

163

def check_method_access(self, method_name):

164

"""

165

Check if method access is allowed.

166

167

Parameters:

168

- method_name (str): Method name to check

169

170

Returns:

171

bool: True if access allowed

172

"""

173

174

def check_attr_access(self, attr_name, is_write=False):

175

"""

176

Check if attribute access is allowed.

177

178

Parameters:

179

- attr_name (str): Attribute name to check

180

- is_write (bool): True for write access, False for read

181

182

Returns:

183

bool: True if access allowed

184

"""

185

```

186

187

## Examples

188

189

### SSL Server with Client Authentication

190

191

```python

192

import rpyc

193

from rpyc.utils.server import ThreadedServer

194

from rpyc.utils.authenticators import SSLAuthenticator

195

import ssl

196

197

class SecureService(rpyc.Service):

198

@rpyc.exposed

199

def get_secret_data(self):

200

return "This is confidential information"

201

202

@rpyc.exposed

203

def process_secure_request(self, data):

204

return f"Securely processed: {data}"

205

206

# Create SSL authenticator with client certificate verification

207

authenticator = SSLAuthenticator(

208

keyfile='server.key',

209

certfile='server.crt',

210

ca_certs='ca.crt',

211

cert_reqs=ssl.CERT_REQUIRED # Require client certificates

212

)

213

214

# Create secure server

215

server = ThreadedServer(

216

SecureService,

217

port=18821, # Standard SSL port

218

authenticator=authenticator

219

)

220

221

print("Secure SSL server with client authentication started")

222

server.start()

223

```

224

225

### SSL Client Connection

226

227

```python

228

import rpyc

229

230

# Connect with SSL client certificate

231

conn = rpyc.ssl_connect(

232

'secure-server.com', 18821,

233

keyfile='client.key',

234

certfile='client.crt',

235

ca_certs='ca.crt'

236

)

237

238

try:

239

# Use secure connection

240

secret = conn.root.get_secret_data()

241

result = conn.root.process_secure_request("sensitive data")

242

print(f"Secret: {secret}")

243

print(f"Result: {result}")

244

finally:

245

conn.close()

246

```

247

248

### Custom Authentication

249

250

```python

251

import rpyc

252

from rpyc.utils.server import ThreadedServer

253

from rpyc.utils.authenticators import AuthenticationError

254

import hashlib

255

import time

256

257

class TokenAuthenticator:

258

"""Custom token-based authenticator"""

259

260

def __init__(self, valid_tokens):

261

self.valid_tokens = set(valid_tokens)

262

263

def __call__(self, sock):

264

"""Authenticate using token exchange"""

265

try:

266

# Receive token from client

267

token_data = sock.recv(1024).decode('utf-8')

268

token_parts = token_data.split(':')

269

270

if len(token_parts) != 2:

271

raise AuthenticationError("Invalid token format")

272

273

username, token = token_parts

274

275

# Validate token

276

if token not in self.valid_tokens:

277

raise AuthenticationError("Invalid token")

278

279

# Send confirmation

280

sock.send(b"AUTH_OK")

281

282

# Return socket and credentials

283

credentials = {'username': username, 'authenticated': True}

284

return sock, credentials

285

286

except Exception as e:

287

raise AuthenticationError(f"Authentication failed: {e}")

288

289

class AuthenticatedService(rpyc.Service):

290

def on_connect(self, conn):

291

print(f"User {conn._config.get('credentials', {}).get('username')} connected")

292

293

@rpyc.exposed

294

def get_user_data(self):

295

# Access credentials from connection

296

username = self.exposed_get_username()

297

return f"Data for user: {username}"

298

299

@rpyc.exposed

300

def get_username(self):

301

# Would access from connection context in real implementation

302

return "authenticated_user"

303

304

# Create server with custom authenticator

305

valid_tokens = ['token123', 'secret456', 'auth789']

306

authenticator = TokenAuthenticator(valid_tokens)

307

308

server = ThreadedServer(

309

AuthenticatedService,

310

port=12345,

311

authenticator=authenticator

312

)

313

314

print("Server with custom token authentication started")

315

server.start()

316

```

317

318

### Secure Configuration

319

320

```python

321

import rpyc

322

from rpyc.utils.authenticators import SSLAuthenticator

323

import ssl

324

325

# Create highly secure configuration

326

secure_config = {

327

'allow_all_attrs': False, # No attribute access

328

'allow_pickle': False, # No pickle serialization

329

'allow_setattr': False, # No attribute setting

330

'allow_delattr': False, # No attribute deletion

331

'allow_public_attrs': False, # No public attributes

332

'exposed_prefix': 'exposed_', # Require exposed prefix

333

'instantiate_custom_exceptions': False,

334

'propagate_SystemExit_locally': True,

335

'propagate_KeyboardInterrupt_locally': True,

336

'sync_request_timeout': 10, # Short timeout

337

}

338

339

class HighSecurityService(rpyc.Service):

340

"""Service with minimal attack surface"""

341

342

@rpyc.exposed

343

def safe_operation(self, data):

344

# Only basic operations on validated data

345

if isinstance(data, (int, float, str)):

346

return f"Processed: {data}"

347

else:

348

raise ValueError("Only basic types allowed")

349

350

@rpyc.exposed

351

def get_public_info(self):

352

return "This is public information"

353

354

# SSL authenticator with strict settings

355

authenticator = SSLAuthenticator(

356

keyfile='server.key',

357

certfile='server.crt',

358

ca_certs='ca.crt',

359

cert_reqs=ssl.CERT_REQUIRED,

360

ssl_version=ssl.PROTOCOL_TLS,

361

ciphers='ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS'

362

)

363

364

# Connect with secure configuration

365

conn = rpyc.ssl_connect(

366

'secure-server.com', 18821,

367

keyfile='client.key',

368

certfile='client.crt',

369

ca_certs='ca.crt',

370

config=secure_config

371

)

372

373

try:

374

# Only allowed operations work

375

result = conn.root.safe_operation("test data")

376

info = conn.root.get_public_info()

377

print(f"Result: {result}")

378

print(f"Info: {info}")

379

380

# These would fail due to security restrictions:

381

# conn.root.some_attr # Would raise AttributeError

382

# conn.root.unsafe_method() # Would not be accessible

383

384

finally:

385

conn.close()

386

```

387

388

### Role-Based Access Control

389

390

```python

391

import rpyc

392

from rpyc.utils.server import ThreadedServer

393

from rpyc.utils.authenticators import AuthenticationError

394

395

class RoleBasedAuthenticator:

396

"""Authenticator that assigns roles based on credentials"""

397

398

def __init__(self, user_roles):

399

self.user_roles = user_roles # {'username': ['role1', 'role2']}

400

401

def __call__(self, sock):

402

# Simple username/password authentication

403

credentials_data = sock.recv(1024).decode('utf-8')

404

username, password = credentials_data.split(':')

405

406

# In real implementation, verify password against database

407

if username in self.user_roles:

408

sock.send(b"AUTH_OK")

409

roles = self.user_roles[username]

410

return sock, {'username': username, 'roles': roles}

411

else:

412

raise AuthenticationError("Invalid credentials")

413

414

class RoleProtectedService(rpyc.Service):

415

"""Service with role-based method protection"""

416

417

def __init__(self):

418

self._connection = None

419

420

def on_connect(self, conn):

421

self._connection = conn

422

print(f"User connected: {self.get_username()} with roles: {self.get_roles()}")

423

424

def get_username(self):

425

return self._connection._config.get('credentials', {}).get('username', 'unknown')

426

427

def get_roles(self):

428

return self._connection._config.get('credentials', {}).get('roles', [])

429

430

def require_role(self, required_role):

431

"""Check if user has required role"""

432

user_roles = self.get_roles()

433

if required_role not in user_roles:

434

raise PermissionError(f"Role '{required_role}' required")

435

436

@rpyc.exposed

437

def public_method(self):

438

return "This method is available to all authenticated users"

439

440

@rpyc.exposed

441

def admin_method(self):

442

self.require_role('admin')

443

return "This method requires admin role"

444

445

@rpyc.exposed

446

def user_method(self):

447

self.require_role('user')

448

return f"Hello {self.get_username()}, you have user access"

449

450

@rpyc.exposed

451

def manager_method(self):

452

self.require_role('manager')

453

return "This method requires manager role"

454

455

# Setup users with roles

456

user_roles = {

457

'alice': ['user', 'admin'],

458

'bob': ['user'],

459

'charlie': ['manager', 'user']

460

}

461

462

authenticator = RoleBasedAuthenticator(user_roles)

463

server = ThreadedServer(

464

RoleProtectedService,

465

port=12345,

466

authenticator=authenticator

467

)

468

469

print("Role-based access control server started")

470

server.start()

471

```

472

473

### Connection Security Monitoring

474

475

```python

476

import rpyc

477

from rpyc.utils.server import ThreadedServer

478

import logging

479

import time

480

481

class SecurityMonitoringService(rpyc.Service):

482

"""Service that logs security events"""

483

484

def __init__(self):

485

self.logger = logging.getLogger('rpyc.security')

486

self.connection_log = {}

487

488

def on_connect(self, conn):

489

client_info = {

490

'connect_time': time.time(),

491

'remote_addr': getattr(conn._channel.stream.sock, 'getpeername', lambda: 'unknown')(),

492

'requests': 0

493

}

494

self.connection_log[id(conn)] = client_info

495

self.logger.info(f"Client connected: {client_info['remote_addr']}")

496

497

def on_disconnect(self, conn):

498

conn_id = id(conn)

499

if conn_id in self.connection_log:

500

info = self.connection_log[conn_id]

501

duration = time.time() - info['connect_time']

502

self.logger.info(f"Client disconnected after {duration:.2f}s, "

503

f"{info['requests']} requests")

504

del self.connection_log[conn_id]

505

506

def log_request(self, method_name):

507

"""Log method invocation"""

508

# In real implementation, get connection from context

509

self.logger.info(f"Method called: {method_name}")

510

511

@rpyc.exposed

512

def secure_operation(self, data):

513

self.log_request('secure_operation')

514

# Validate input

515

if not isinstance(data, str) or len(data) > 1000:

516

self.logger.warning("Invalid input rejected")

517

raise ValueError("Invalid input")

518

return f"Processed: {data}"

519

520

@rpyc.exposed

521

def get_stats(self):

522

self.log_request('get_stats')

523

return {

524

'active_connections': len(self.connection_log),

525

'server_uptime': time.time()

526

}

527

528

# Setup security logging

529

logging.basicConfig(

530

level=logging.INFO,

531

format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',

532

handlers=[

533

logging.FileHandler('rpyc_security.log'),

534

logging.StreamHandler()

535

]

536

)

537

538

server = ThreadedServer(SecurityMonitoringService, port=12345)

539

print("Security monitoring server started")

540

server.start()

541

```

542

543

## Constants

544

545

```python { .api }

546

DEFAULT_SSL_PORT = 18821 # Default SSL server port

547

SSL_CIPHER_SUITES = 'ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS'

548

AUTH_TIMEOUT = 30 # Authentication timeout (seconds)

549

MAX_AUTH_ATTEMPTS = 3 # Maximum authentication attempts

550

```

551

552

## Exceptions

553

554

```python { .api }

555

class AuthenticationError(Exception):

556

"""Raised when authentication fails"""

557

558

class SecurityError(Exception):

559

"""Base exception for security-related errors"""

560

561

class PermissionDeniedError(SecurityError):

562

"""Raised when access is denied due to insufficient permissions"""

563

564

class InvalidCredentialsError(AuthenticationError):

565

"""Raised when provided credentials are invalid"""

566

```