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