robust_server_socket 0.3.3 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.en.md CHANGED
@@ -2,7 +2,72 @@
2
2
 
3
3
  Gem for inter-service authorization, used in pair with RobustClientSocket
4
4
 
5
- ### ⚠️ Not Production Tested (yet)
5
+ ### ⚠️ Not Production Tested (yet) but tested in staging environment
6
+
7
+ `Not vibecoded`
8
+
9
+ ## WHY
10
+
11
+ ### The Problem
12
+
13
+ When building microservice architecture, the server side faces:
14
+
15
+ - **Lack of verification**: How to verify that a request came from a trusted service?
16
+ - **Replay attacks**: Intercepted requests can be replayed
17
+ - **DDoS attacks**: Need to limit request frequency
18
+ - **Boilerplate code**: Repetitive validation logic in every service
19
+
20
+ #### Even if infrastructure is behind a DMZ in a private network, there is still room for SSRF or OpenRedirect attacks
21
+
22
+ ### The Solution
23
+
24
+ RobustServerSocket provides:
25
+
26
+ - **RSA decryption**: Token authenticity verification
27
+ - **Client whitelist**: Only authorized services allowed
28
+ - **Replay protection**: Blacklist of used tokens in Redis
29
+ - **Rate limiting**: Sliding window per-client request limits
30
+
31
+ ## HOW IT WORKS
32
+
33
+ ### Architecture
34
+
35
+ ```
36
+ Incoming request with Secure-Token
37
+
38
+ v
39
+ ┌──────────────────────────────┐
40
+ │ RobustServerSocket │
41
+ │ │
42
+ │ 1. RSA Decrypt │
43
+ │ 2. Validate Format │
44
+ │ 3. Check Client Whitelist │
45
+ │ 4. Check Rate Limit │
46
+ │ 5. Check Token Reuse │
47
+ │ 6. Check Token Expiration │
48
+ └──────────────┬───────────────┘
49
+
50
+ ┌────────┼────────┐
51
+ v v
52
+ ✅ Success ❌ Error
53
+ (continue) (401/403/429)
54
+ ```
55
+
56
+ ### Validation Flow
57
+
58
+ 1. **Decryption**: Base64 decode → RSA decrypt with private key
59
+ 2. **Parsing**: Extract `{client_name}_{timestamp_ms}` from token
60
+ 3. **Whitelist**: Verify client_name is in `allowed_services`
61
+ 4. **Rate limit**: Sliding window — check request count within `rate_limit_window_seconds`
62
+ 5. **Replay check**: Verify token hasn't been used (Redis)
63
+ 6. **Staleness**: Verify timestamp is current (with ±30s clock skew tolerance)
64
+
65
+ ### Modular System
66
+
67
+ Checks are enabled via `using_modules`:
68
+ - `:client_auth_protection` — client whitelist
69
+ - `:replay_attack_protection` — prevent token reuse
70
+ - `:rate_limit_protection` — sliding window rate limiting
6
71
 
7
72
  ## 📋 Table of Contents
8
73
 
@@ -19,28 +84,25 @@ RobustServerSocket implements a multi-layered protection system for inter-servic
19
84
  ### 1. Cryptographic Protection
20
85
  - **RSA-2048 Encryption**: Uses RSA key pairs with minimum 2048-bit length
21
86
  - **Key Validation**: Automatic key size verification during configuration
22
- - **Asymmetric Encryption**: Private key on server, public keys on clients
23
87
 
24
- ### 2. Token Reuse Protection
25
- - **One-time Tokens**: Each token can only be used once
26
- - **Redis Blacklist**: Used tokens are automatically added to blacklist
27
- - **Atomic Verification**: Race conditions prevented via Redis Lua scripts
88
+ ### 2. Access Control
89
+ - **Client Whitelist**: Only authorized services can connect, when `:client_auth_protection` module is enabled
90
+ - **Name-based Identification**: Each client must be explicitly listed in `allowed_services`
28
91
 
29
- ### 3. Time-based Restrictions
30
- - **Expiration Time**: Configurable token lifetime
31
- - **Automatic Expiration**: Tokens automatically become invalid after expiration
32
- - **Replay Attack Protection**: Old tokens cannot be reused
92
+ ### 3. Replay Attack Protection
93
+ - **One-time Tokens**: Used tokens are added to a Redis blacklist, when `:replay_attack_protection` is enabled
94
+ - **Staleness Check**: Tokens automatically become invalid after `token_expiration_time`
95
+ - **Clock Skew Tolerance**: ±30 seconds (CLOCK_SKEW)
96
+ - **Blacklist TTL**: Computed automatically as `token_expiration_time + CLOCK_SKEW`
33
97
 
34
- ### 4. Access Control
35
- - **Client Whitelist**: Only authorized services can connect
36
- - **Name-based Identification**: Each client must be explicitly listed in `allowed_services`
37
- - **Token Format Validation**: Strict token structure verification
98
+ ### 4. Rate Limiting
99
+ - **Sliding Window**: When `:rate_limit_protection` is enabled — precise request counting without the burst effect of fixed windows
100
+ - **Fail-open Strategy**: If Redis is unavailable, requests are allowed through (for service reliability)
101
+ - **Per-client Isolation**: Counter is tracked individually per `client_name`
38
102
 
39
- ### 5. Rate Limiting (optional)
40
- - **DDoS Protection**: Limit number of requests from each client
41
- - **Sliding Window**: Fair distribution of requests over time
42
- - **Fail-open Strategy**: If Redis is unavailable, requests are allowed (for reliability)
43
- - **Per-client Limits**: Individual counters for each client
103
+ ### 5. SSL Stripping / MITM Protection
104
+ - **Enforce HTTPS on server**: All requests should be made over HTTPS to protect tokens from interception
105
+ - **Enabled on RobustClientSocket with `ssl_verify: true`**
44
106
 
45
107
  ### 6. Injection Protection
46
108
  - **Input Validation**: Type, length, and format verification of tokens
@@ -49,16 +111,13 @@ RobustServerSocket implements a multi-layered protection system for inter-servic
49
111
 
50
112
  ## 📦 Installation
51
113
 
52
- Add to Gemfile:
53
-
54
114
  ```ruby
55
115
  gem 'robust_server_socket'
56
116
  ```
57
117
 
58
- Then execute:
59
-
60
- ```bash
61
- bundle install
118
+ and on the client:
119
+ ```ruby
120
+ gem 'robust_client_socket'
62
121
  ```
63
122
 
64
123
  ## ⚙️ Configuration
@@ -67,198 +126,120 @@ Create file `config/initializers/robust_server_socket.rb`:
67
126
 
68
127
  ```ruby
69
128
  RobustServerSocket.configure do |c|
70
- # REQUIRED PARAMETERS
71
-
129
+ c.using_modules = %i[
130
+ client_auth_protection
131
+ replay_attack_protection
132
+ rate_limit_protection
133
+ ]
134
+
72
135
  # Service private key (RSA-2048 or higher)
73
- # IMPORTANT: Store in environment variables, DO NOT commit to git!
74
136
  c.private_key = ENV['ROBUST_SERVER_PRIVATE_KEY']
75
-
76
- # Token lifetime in seconds
77
- # Recommendation: 1-3 seconds for production (time from client request to server)
78
- c.token_expiration_time = 3
79
-
137
+
138
+ # Token lifetime in seconds (must match TTL on client side)
139
+ c.token_expiration_time = 10
140
+
80
141
  # List of allowed services (whitelist)
81
- # Must match names in client keychain
142
+ # Must match service_name in RobustClientSocket
82
143
  c.allowed_services = %w[core payments notifications]
83
-
84
- # Redis for storing used tokens
144
+
145
+ # Redis for replay_attack_protection and rate_limit_protection
85
146
  c.redis_url = ENV.fetch('REDIS_URL', 'redis://localhost:6379/0')
86
- c.redis_pass = ENV['REDIS_PASSWORD'] # can be nil for local development
87
-
88
- # OPTIONAL PARAMETERS: Rate Limiting
89
-
90
- # Enable rate limiting (default: false)
91
- c.rate_limit_enabled = true
92
-
93
- # Maximum requests per time window (default: 100)
94
- c.rate_limit_max_requests = 100
95
-
96
- # Time window size in seconds (default: 60)
97
- c.rate_limit_window_seconds = 60
147
+ c.redis_pass = ENV['REDIS_PASSWORD']
148
+
149
+ # rate_limit_protection
150
+ c.rate_limit_max_requests = 100 # max requests per window (default: 100)
151
+ c.rate_limit_window_seconds = 60 # window size in seconds (default: 60)
98
152
  end
99
153
 
100
- # Load configuration with validation
101
154
  RobustServerSocket.load!
102
155
  ```
103
156
 
157
+ ### Configuration Options
158
+
159
+ | Parameter | Type | Required | Default | Description |
160
+ |-----------------------------|---------|----------|-------------------------------------------------------------------------------------|-------------------------------------------------|
161
+ | `private_key` | String | ✅ | — | Service private RSA key (RSA-2048 or higher) |
162
+ | `token_expiration_time` | Integer | ✅ | 10 | Token lifetime in seconds |
163
+ | `allowed_services` | Array | ✅ | — | Allowed services whitelist |
164
+ | `redis_url` | String | ✅ | — | Redis connection URL |
165
+ | `redis_pass` | String | ❌ | nil | Redis password |
166
+ | `using_modules` | Array | ❌ | `[:client_auth_protection, :rate_limit_protection, :replay_attack_protection]` | Enabled modules |
167
+ | `rate_limit_max_requests` | Integer | ❌ | 100 | Max requests per window |
168
+ | `rate_limit_window_seconds` | Integer | ❌ | 60 | Window size in seconds |
169
+
170
+ > `store_used_token_time` is no longer configurable — computed automatically as `token_expiration_time + 30` (CLOCK_SKEW).
171
+
172
+ ### Compatibility with RobustClientSocket
173
+
174
+ The token contains a timestamp in **milliseconds**. RobustClientSocket starting from version X.X must generate:
175
+
176
+ ```ruby
177
+ Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
178
+ ```
179
+
180
+ The legacy `Time.now.utc.to_i` (seconds) will cause all tokens to be rejected as `stale`.
181
+
104
182
  ## 🚀 Usage
105
183
 
106
184
  ### Basic Authorization
107
185
 
108
186
  ```ruby
109
- # In controller or middleware
110
187
  class ApiController < ApplicationController
111
188
  before_action :authenticate_service!
112
-
189
+
113
190
  private
114
-
191
+
115
192
  def authenticate_service!
116
- # Header configured in RobustClientSocket (SECURE-TOKEN default)
117
193
  token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
118
-
119
- @current_service = RobustServerSocket::ClientToken.validate!(token) # bang method (raises errors)
194
+ @current_service = RobustServerSocket::ClientToken.validate!(token)
120
195
  rescue RobustServerSocket::ClientToken::InvalidToken
121
196
  render json: { error: 'Invalid token' }, status: :unauthorized
122
- rescue RobustServerSocket::ClientToken::UnauthorizedClient
197
+ rescue RobustServerSocket::Modules::ClientAuthProtection::UnauthorizedClient
123
198
  render json: { error: 'Unauthorized service' }, status: :forbidden
124
- rescue RobustServerSocket::ClientToken::UsedToken
199
+ rescue RobustServerSocket::Modules::ReplayAttackProtection::UsedToken
125
200
  render json: { error: 'Token already used' }, status: :unauthorized
126
- rescue RobustServerSocket::ClientToken::StaleToken
201
+ rescue RobustServerSocket::Modules::ReplayAttackProtection::StaleToken
127
202
  render json: { error: 'Token expired' }, status: :unauthorized
128
203
  rescue RobustServerSocket::RateLimiter::RateLimitExceeded => e
129
204
  render json: { error: e.message }, status: :too_many_requests
130
205
  end
131
-
132
- def authenticate_service
133
- token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
134
- @current_service = RobustServerSocket::ClientToken.new(token)
135
-
136
- if @current_service.valid? # doesn't raise errors
137
- # Token is valid
138
- else
139
- # Token is invalid
140
- render json: { error: 'Unauthorized' }, status: :unauthorized
141
- end
142
- end
143
206
  end
144
207
  ```
145
208
 
146
- ### Advanced Usage
209
+ ### valid? (non-raising)
147
210
 
148
211
  ```ruby
149
- # Create token object
150
- token_string = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
151
- client_token = RobustServerSocket::ClientToken.new(token_string)
212
+ token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
213
+ client_token = RobustServerSocket::ClientToken.new(token)
152
214
 
153
- # Check validity (returns true/false)
154
215
  if client_token.valid?
155
- # Get client name
156
216
  client_name = client_token.client
157
- puts "Authorized client: #{client_name}"
158
217
  else
159
- # Token is invalid
160
218
  render json: { error: 'Unauthorized' }, status: :unauthorized
161
219
  end
162
-
163
- # Quick validation with exceptions (recommended)
164
- begin
165
- service_token = RobustServerSocket::ClientToken.validate!(token_string)
166
- client_name = service_token.client
167
- rescue => e
168
- # Handle specific errors
169
- end
170
- ```
171
-
172
- ### Manual Rate Limiting
173
-
174
- ```ruby
175
- # Check current attempt count
176
- attempts = RobustServerSocket::RateLimiter.current_attempts('core')
177
- puts "Core service made #{attempts} requests"
178
-
179
- # Reset counter for specific client
180
- RobustServerSocket::RateLimiter.reset!('core')
181
-
182
- # Check with exception on exceeded limit
183
- begin
184
- RobustServerSocket::RateLimiter.check!('core')
185
- rescue RobustServerSocket::RateLimiter::RateLimitExceeded => e
186
- puts e.message # "Rate limit exceeded for core: 101/100 requests per 60s"
187
- end
188
-
189
- # Check without exception (returns false when exceeded)
190
- if RobustServerSocket::RateLimiter.check('core')
191
- # Limit not exceeded
192
- else
193
- # Limit exceeded
194
- end
195
- ```
196
-
197
- ## 🚦 Rate Limiting (Request Rate Limiting)
198
-
199
- ### How It Works
200
-
201
- Rate Limiter protects your service from overload by limiting the number of requests from each client within a time window.
202
-
203
- **Characteristics:**
204
- - **Per-client counters**: Separate counter for each service
205
- - **Sliding window**: Window resets automatically after time expires
206
- - **Atomicity**: Increment and check are performed atomically (Redis LUA script)
207
- - **Fail-open**: When Redis is unavailable, requests are allowed (not blocked)
208
-
209
- ### Limit Configuration
210
-
211
- ```ruby
212
- RobustServerSocket.configure do |c|
213
- # Enable rate limiting
214
- c.rate_limit_enabled = true
215
-
216
- # For low-traffic microservices
217
- c.rate_limit_max_requests = 50
218
- c.rate_limit_window_seconds = 60
219
- end
220
- ```
221
-
222
- ### Monitoring
223
-
224
- ```ruby
225
- # Check current state
226
- clients = ['core', 'payments', 'notifications']
227
- clients.each do |client|
228
- attempts = RobustServerSocket::RateLimiter.current_attempts(client)
229
- max = RobustServerSocket.configuration.rate_limit_max_requests
230
- puts "#{client}: #{attempts}/#{max}"
231
- end
232
-
233
- # In metrics (Prometheus, StatsD, etc.)
234
- clients.each do |client|
235
- attempts = RobustServerSocket::RateLimiter.current_attempts(client)
236
- Metrics.gauge("rate_limiter.attempts.#{client}", attempts)
237
- end
238
220
  ```
239
221
 
240
222
  ## ❌ Error Handling
241
223
 
242
224
  ### Exception Types
243
225
 
244
- | Exception | Reason | HTTP Status | Action |
245
- |-----------|--------|-------------|--------|
246
- | `InvalidToken` | Token cannot be decrypted or has invalid format | 401 | Check token and key correctness |
247
- | `UnauthorizedClient` | Client not in whitelist | 403 | Add client to `allowed_services` |
248
- | `UsedToken` | Token has already been used | 401 | Client must request new token |
249
- | `StaleToken` | Token has expired | 401 | Client must request new token |
250
- | `RateLimitExceeded` | Rate limit exceeded | 429 | Client should wait or retry later |
226
+ | Exception | Reason | HTTP Status |
227
+ |------------------------------------------------------------|-------------------------------------------|-------------|
228
+ | `ClientToken::InvalidToken` | Token cannot be decrypted or wrong format | 401 |
229
+ | `Modules::ClientAuthProtection::UnauthorizedClient` | Client not in whitelist | 403 |
230
+ | `Modules::ReplayAttackProtection::UsedToken` | Token has already been used | 401 |
231
+ | `Modules::ReplayAttackProtection::StaleToken` | Token expired or from the future (>30s) | 401 |
232
+ | `RateLimiter::RateLimitExceeded` | Rate limit exceeded | 429 |
251
233
 
252
234
  ### Centralized Error Handling
253
235
 
254
236
  ```ruby
255
- # In ApplicationController
256
237
  rescue_from RobustServerSocket::ClientToken::InvalidToken,
257
- RobustServerSocket::ClientToken::UsedToken,
258
- RobustServerSocket::ClientToken::StaleToken,
238
+ RobustServerSocket::Modules::ReplayAttackProtection::UsedToken,
239
+ RobustServerSocket::Modules::ReplayAttackProtection::StaleToken,
259
240
  with: :unauthorized_response
260
241
 
261
- rescue_from RobustServerSocket::ClientToken::UnauthorizedClient,
242
+ rescue_from RobustServerSocket::Modules::ClientAuthProtection::UnauthorizedClient,
262
243
  with: :forbidden_response
263
244
 
264
245
  rescue_from RobustServerSocket::RateLimiter::RateLimitExceeded,
@@ -267,106 +248,24 @@ rescue_from RobustServerSocket::RateLimiter::RateLimitExceeded,
267
248
  private
268
249
 
269
250
  def unauthorized_response(exception)
270
- render json: {
271
- error: 'Authentication failed',
272
- message: exception.message,
273
- type: exception.class.name
274
- }, status: :unauthorized
251
+ render json: { error: 'Authentication failed', message: exception.message }, status: :unauthorized
275
252
  end
276
253
 
277
254
  def forbidden_response(exception)
278
- render json: {
279
- error: 'Access denied',
280
- message: exception.message,
281
- type: exception.class.name
282
- }, status: :forbidden
255
+ render json: { error: 'Access denied', message: exception.message }, status: :forbidden
283
256
  end
284
257
 
285
258
  def rate_limit_response(exception)
286
259
  render json: {
287
260
  error: 'Too many requests',
288
261
  message: exception.message,
289
- type: exception.class.name,
290
262
  retry_after: RobustServerSocket.configuration.rate_limit_window_seconds
291
263
  }, status: :too_many_requests
292
264
  end
293
265
  ```
294
266
 
295
- ## 💡 Usage Recommendations
296
-
297
- ### 1. Key Management
298
-
299
- **✅ DO:**
300
- ```ruby
301
- # Store keys in environment variables
302
- c.private_key = ENV['ROBUST_SERVER_PRIVATE_KEY']
303
-
304
- # Use secrets management (AWS Secrets Manager, Vault, etc.)
305
- c.private_key = Rails.application.credentials.dig(:robust_server, :private_key)
306
-
307
- # Generate keys correctly
308
- # openssl genrsa -out private_key.pem 2048
309
- # openssl rsa -in private_key.pem -pubout -out public_key.pem
310
- ```
311
-
312
- **❌ DON'T:**
313
- ```ruby
314
- # DON'T commit keys to git
315
- c.private_key = "-----BEGIN PRIVATE KEY-----\nMII..."
316
-
317
- # DON'T use weak keys
318
- # Minimum RSA-2048, RSA-4096 recommended for high security
319
- ```
320
-
321
- ### 2. Redis Configuration
322
-
323
- **✅ DO:**
324
- ```ruby
325
- # Use separate namespace for each environment
326
- c.redis_url = ENV.fetch('REDIS_URL', 'redis://localhost:6379/0')
327
-
328
- # Configure connection pool in production
329
- # In config/initializers/redis.rb
330
- Redis.current = ConnectionPool.new(size: 5, timeout: 5) do
331
- Redis.new(url: ENV['REDIS_URL'], password: ENV['REDIS_PASSWORD'])
332
- end
333
-
334
- # Monitor Redis status
335
- # Use Redis Sentinel or Cluster for high availability
336
- ```
337
-
338
- **❌ DON'T:**
339
- ```ruby
340
- # DON'T use same Redis DB for all environments, use separate Redis DB
341
- # DON'T ignore Redis errors (rate limiter is already fail-open, but log them)
342
- ```
343
-
344
- ### 5. Service Whitelist
345
-
346
- ```ruby
347
- # Explicitly specify only necessary services
348
- c.allowed_services = %w[core payments] # ✅
349
-
350
- # DON'T use wildcards or regular expressions
351
- c.allowed_services = %w[*] # ❌ DANGEROUS!
352
-
353
- # Synchronize with client keychain
354
- # Server (robust_server_socket):
355
- c.allowed_services = %w[core]
356
-
357
- # Client (robust_client_socket):
358
- c.keychain = {
359
- core: { # ← Must match
360
- base_uri: 'https://core.example.com',
361
- public_key: '-----BEGIN PUBLIC KEY-----...'
362
- }
363
- }
364
- ```
365
-
366
267
  ## 🤝 Integration with RobustClientSocket
367
268
 
368
- For full functionality, configure the client side:
369
-
370
269
  ```ruby
371
270
  # On client (RobustClientSocket)
372
271
  RobustClientSocket.configure do |c|
@@ -374,272 +273,49 @@ RobustClientSocket.configure do |c|
374
273
  c.keychain = {
375
274
  payments: {
376
275
  base_uri: 'https://payments.example.com',
377
- public_key: '-----BEGIN PUBLIC KEY-----...' # Public key of payments server
276
+ public_key: '-----BEGIN PUBLIC KEY-----...'
378
277
  }
379
278
  }
380
279
  end
381
280
 
382
281
  # On server (RobustServerSocket)
383
282
  RobustServerSocket.configure do |c|
384
- c.allowed_services = %w[core] # ← Matches client's service_name
385
- c.private_key = '-----BEGIN PRIVATE KEY-----...' # Private pair to public_key
283
+ c.allowed_services = %w[core]
284
+ c.private_key = '-----BEGIN PRIVATE KEY-----...'
386
285
  end
387
286
  ```
388
287
 
389
- ## 📚 Additional Resources
390
-
391
- - [RobustClientSocket documentation](https://github.com/tee0zed/robust_client_socket)
392
- - [RSA encryption best practices](https://www.openssl.org/docs/)
393
- - [Redis security guide](https://redis.io/topics/security)
394
-
395
- ## 📝 License
396
-
397
- See [MIT-LICENSE](MIT-LICENSE) file
398
-
399
- ## 🐛 Bugs and Suggestions
400
-
401
- Report issues through your repository's issue tracker.
288
+ ## 📊 Performance
402
289
 
403
-
404
- #### Test: 1000 Requests with Token Validation
290
+ ### Benchmark: 1000 Requests with Token Validation
405
291
 
406
292
  **Without RobustServerSocket (plain HTTP controller):**
407
- ```ruby
408
- Benchmark.measure do
409
- 1000.times do
410
- # Regular request without authorization
411
- get '/api/v1/partners/719a68e4-3457-45dd-8d7f-73f1d367b87a/merchants'
412
- end
413
- end
414
- ```
415
-
416
- **Results (approximate):**
417
- - **Real time**: ~2.5 seconds
418
- - No token verification
419
- - No RSA decryption
420
- - No Redis checks
421
-
422
- ---
293
+ - Real time: ~2.5 seconds
294
+ - No token verification, no RSA decryption, no Redis checks
423
295
 
424
296
  **With RobustServerSocket (full protection):**
425
- ```ruby
426
- Benchmark.measure do
427
- 1000.times do
428
- # Request with RobustClientSocket (RSA + tokens)
429
- RobustClientSocket::CoreApi.get('/api/v1/partners/719a68e4-3457-45dd-8d7f-73f1d367b87a/merchants')
430
- end
431
- end
432
- ```
433
-
434
- **Results:**
435
- - **Real time**: 2.77 seconds
436
- - **User CPU**: 0.23 seconds
437
- - **System CPU**: 0.54 seconds
438
- - **Total CPU**: 0.77 seconds
439
-
440
- ### 📊 Security Overhead Analysis
441
-
442
- | Operation | Time | % of Request |
443
- |-----------|------|-------------|
444
- | **RSA Decryption** | ~0.1-0.2ms | 3-7% |
445
- | **Redis Token Check** | ~0.05-0.1ms | 2-3% |
446
- | **Rate Limiting** | ~0.02-0.05ms | 1% |
447
- | **Whitelist Validation** | <0.01ms | <1% |
448
- | **Total Overhead** | **~0.2-0.4ms** | **~10-15%** |
449
-
450
- ### 🎯 Key Findings
451
-
452
- 1. **Minimal overhead (~0.3ms per request)**
453
- - RSA-2048 decryption: ~0.15ms
454
- - Redis operations: ~0.08ms
455
- - Rate limiting: ~0.03ms
456
-
457
- 2. **Scales linearly**
458
- - 100 req/s = +30ms overhead
459
- - 1000 req/s = +300ms overhead
460
- - Acceptable for most applications
461
-
462
- 3. **Redis is main bottleneck**
463
- - Use Redis Sentinel/Cluster
464
- - Connection pooling is critical
465
- - Fail-open strategy for reliability
466
-
467
- ### 💡 Performance Optimization
468
-
469
- **1. Redis Connection Pool:**
470
-
471
- ```ruby
472
- # config/initializers/redis.rb
473
- require 'connection_pool'
474
-
475
- REDIS_POOL = ConnectionPool.new(size: 25, timeout: 5) do
476
- Redis.new(
477
- url: ENV['REDIS_URL'],
478
- password: ENV['REDIS_PASSWORD'],
479
- reconnect_attempts: 3,
480
- reconnect_delay: 0.5,
481
- reconnect_delay_max: 5.0
482
- )
483
- end
484
-
485
- # In RobustServerSocket::SecureToken::Cacher
486
- def self.with_redis
487
- REDIS_POOL.with do |redis|
488
- yield redis
489
- end
490
- end
491
- ```
492
-
493
- **2. Public Key Caching:**
494
-
495
- ```ruby
496
- # Keys are already cached at load!, but can be optimized
497
- class RobustServerSocket::ClientToken
498
- # Keys are loaded once at application startup
499
- # No additional optimization needed
500
- end
501
- ```
502
-
503
- **3. Rate Limiting Optimization:**
504
-
505
- ```ruby
506
- RobustServerSocket.configure do |c|
507
- # For high-load systems
508
- c.rate_limit_enabled = true
509
- c.rate_limit_max_requests = 1000 # Increase limit
510
- c.rate_limit_window_seconds = 60
511
-
512
- # For low-load systems
513
- c.rate_limit_max_requests = 100
514
- c.rate_limit_window_seconds = 60
515
- end
516
- ```
517
-
518
- **4. Token Expiration Optimization:**
519
-
520
- ```ruby
521
- # Short lifetime = more requests for new tokens
522
- c.token_expiration_time = 10.minutes # ❌ High traffic
523
-
524
- # Optimal time for inter-service calls
525
- c.token_expiration_time = 3 # ✅ 3 seconds is enough
526
- ```
527
-
528
- ### 🔬 Performance Monitoring
529
-
530
- **Metrics to Track:**
531
-
532
- ```ruby
533
- class ApiController < ApplicationController
534
- around_action :track_auth_performance
535
-
536
- private
537
-
538
- def track_auth_performance
539
- start = Time.now
540
-
541
- begin
542
- yield
543
- ensure
544
- duration = ((Time.now - start) * 1000).round(2)
545
-
546
- # Total request time
547
- Metrics.timing('request.duration', duration, tags: [
548
- "controller:#{controller_name}",
549
- "action:#{action_name}"
550
- ])
551
-
552
- # Authentication attempts
553
- if @current_service
554
- Metrics.increment('auth.success', tags: ["service:#{@current_service.client}"])
555
- else
556
- Metrics.increment('auth.failure')
557
- end
558
- end
559
- end
560
- end
561
-
562
- # Specific metrics for RobustServerSocket
563
- module RobustServerSocket
564
- class ClientToken
565
- def self.validate_with_metrics!(token)
566
- start = Time.now
567
- result = validate!(token)
568
- duration = ((Time.now - start) * 1000).round(2)
569
-
570
- Metrics.timing('robust_server.validation.duration', duration)
571
- result
572
- rescue StandardError => e
573
- Metrics.increment('robust_server.validation.error', tags: ["error:#{e.class.name}"])
574
- raise
575
- end
576
- end
577
- end
578
- ```
297
+ - Real time: ~2.77 seconds
298
+ - User CPU: 0.23s, System CPU: 0.54s
579
299
 
580
- ### 📈 Performance at Different Loads
300
+ ### Security Overhead Analysis
581
301
 
582
- | Req/s | Without Protection | With RobustServerSocket | Overhead | Acceptable |
583
- |-------|-------------------|------------------------|----------|-----------|
584
- | 10 | 100ms | 103ms | 3ms | ✅ Excellent |
585
- | 100 | 1s | 1.03s | 30ms | ✅ Excellent |
586
- | 500 | 5s | 5.15s | 150ms | ✅ Good |
587
- | 1,000 | 10s | 10.3s | 300ms | ✅ Acceptable |
588
- | 5,000 | 50s | 51.5s | 1.5s | ⚠️ Redis scaling needed |
589
- | 10,000 | 100s | 103s | 3s | ⚠️ Need Redis Cluster |
302
+ | Operation | Time | % of Request |
303
+ |------------------------|-------------|-------------|
304
+ | RSA Decryption | ~0.1–0.2ms | 3–7% |
305
+ | Redis Token Check | ~0.05–0.1ms | 2–3% |
306
+ | Rate Limiting | ~0.02–0.05ms| 1% |
307
+ | Whitelist Validation | <0.01ms | <1% |
308
+ | **Total Overhead** | **~0.2–0.4ms** | **~10–15%** |
590
309
 
591
- **Conclusion:** Up to 1000 req/s - excellent performance. Higher loads require Redis scaling.
310
+ Up to 1000 req/s excellent performance. Higher loads require Redis Sentinel/Cluster.
592
311
 
593
- ### 🚀 Production Recommendations
312
+ ## 🗺️ TODO
594
313
 
595
- **For high-load systems (>1000 req/s):**
596
-
597
- 1. **Redis Cluster** - distributed load
598
- 2. **Connection Pool** - minimum 25-50 connections
599
- 3. **Monitor Redis** - latency, memory, connections
600
- 4. **Fail-over Strategy** - Redis Sentinel
601
- 5. **CDN for static** - reduce overall load
602
-
603
- **For medium load (100-1000 req/s):**
604
-
605
- 1. **Standalone Redis** with persistence
606
- 2. **Connection Pool** - 10-25 connections
607
- 3. **Basic monitoring**
608
- 4. **Rate limiting** - spike protection
609
-
610
- **For low load (<100 req/s):**
611
-
612
- 1. **Simple Redis** setup
613
- 2. **Default connection pool** (5)
614
- 3. **Standard configuration**
615
-
616
- ## 🤝 Integration with RobustClientSocket
617
-
618
- For full functionality, configure the client side:
619
-
620
- ```ruby
621
- # On client (RobustClientSocket)
622
- RobustClientSocket.configure do |c|
623
- c.service_name = 'core' # ← Must be in server's allowed_services
624
- c.keychain = {
625
- payments: {
626
- base_uri: 'https://payments.example.com',
627
- public_key: '-----BEGIN PUBLIC KEY-----...' # Public key of payments server
628
- }
629
- }
630
- end
631
-
632
- # On server (RobustServerSocket)
633
- RobustServerSocket.configure do |c|
634
- c.allowed_services = %w[core] # ← Matches client's service_name
635
- c.private_key = '-----BEGIN PRIVATE KEY-----...' # Private pair to public_key
636
- end
637
- ```
314
+ - [ ] **Per-client rate limit keys** — configurable individual limits per `client_name` instead of a single global limit
638
315
 
639
316
  ## 📚 Additional Resources
640
317
 
641
- - [BENCHMARK_ANALYSIS.md](BENCHMARK_ANALYSIS.md)
642
- - [RobustClientSocket documentation](../robust_client_socket/README.md)
318
+ - [RobustClientSocket](https://github.com/tee0zed/robust_client_socket)
643
319
  - [RSA encryption best practices](https://www.openssl.org/docs/)
644
320
  - [Redis security guide](https://redis.io/topics/security)
645
321
 
@@ -649,4 +325,4 @@ See [MIT-LICENSE](MIT-LICENSE) file
649
325
 
650
326
  ## 🐛 Bugs and Suggestions
651
327
 
652
- Report issues through your repository's issue tracker.
328
+ Report issues via GitHub issues, or directly at Telegram @cruel_mango or email tee0zed@gmail.com