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.md CHANGED
@@ -1,8 +1,73 @@
1
1
  # RobustServerSocket
2
2
 
3
- Gem для межсервисной авторизации, используется в паре с RobustClientSocket
3
+ Gem для межсервисной аутентификации, используется в паре с 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
+ ### Проблема
12
+
13
+ При построении микросервисной архитектуры серверная сторона сталкивается с:
14
+
15
+ - **Отсутствием верификации**: Как проверить, что запрос пришёл от доверенного сервиса?
16
+ - **Replay-атаками**: Перехваченные запросы могут быть повторены
17
+ - **DDoS-атаками**: Необходимость ограничения частоты запросов
18
+ - **Boilerplate кодом**: Повторяющаяся логика валидации в каждом сервисе
19
+
20
+ #### Даже если инфраструктура находится за DMZ, в своей локальной сети, остаётся пространство для SSRF или OpenRedirect атак
21
+
22
+ ### Решение
23
+
24
+ RobustServerSocket предоставляет:
25
+
26
+ - **RSA-дешифрование**: Проверка подлинности токенов
27
+ - **Whitelist клиентов**: Только разрешённые сервисы
28
+ - **Защиту от replay**: Блэклист использованных токенов в Redis
29
+ - **Rate limiting**: Скользящее окно запросов на клиента
30
+
31
+ ## КАК ЭТО РАБОТАЕТ (HOW)
32
+
33
+ ### Архитектура
34
+
35
+ ```
36
+ Входящий запрос с 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
+ ### Поток валидации
57
+
58
+ 1. **Расшифровка**: Base64 decode → RSA decrypt с приватным ключом
59
+ 2. **Парсинг**: Извлечение `{client_name}_{timestamp_ms}` из токена
60
+ 3. **Whitelist**: Проверка client_name в `allowed_services`
61
+ 4. **Rate limit**: Скользящее окно — проверка количества запросов за `rate_limit_window_seconds`
62
+ 5. **Replay check**: Проверка, что токен не использован (Redis)
63
+ 6. **Staleness**: Проверка timestamp на актуальность (с допуском ±30 секунд на рассинхрон часов)
64
+
65
+ ### Модульная система
66
+
67
+ Проверки подключаются через `using_modules`:
68
+ - `:client_auth_protection` — whitelist клиентов
69
+ - `:replay_attack_protection` — защита от повторного использования токена
70
+ - `:rate_limit_protection` — rate limiting по скользящему окну
6
71
 
7
72
  ## 📋 Содержание
8
73
 
@@ -19,33 +84,25 @@ RobustServerSocket реализует многоуровневую систем
19
84
  ### 1. Криптографическая защита
20
85
  - **RSA-2048 шифрование**: Используется пара ключей RSA с минимальной длиной 2048 бит
21
86
  - **Валидация ключей**: Автоматическая проверка размера ключа при конфигурации
22
- - **Асимметричное шифрование**: Приватный ключ на сервере, публичный — у клиентов
23
87
 
24
- ### 2. Защита от повторного использования токенов
25
- - **Одноразовые токены**: Каждый токен может быть использован только один раз
26
- - **Blacklist в Redis**: Использованные токены автоматически добавляются в черный список
27
- - **Атомарная проверка**: Race condition защищена благодаря Redis Lua скриптам
28
-
29
- ### 3. Временные ограничения
30
- - **Expiration time**: Настраиваемое время жизни токена
31
- - **Автоматическое истечение**: Токены автоматически становятся недействительными после истечения времени
32
- - **Защита от replay attacks**: Старые токены не могут быть использованы повторно
33
-
34
- ### 4. Контроль доступа
35
- - **Whitelist клиентов**: Только авторизованные сервисы могут подключаться
88
+ ### 2. Контроль доступа
89
+ - **Whitelist клиентов**: Только авторизованные сервисы могут подключаться, при включенном модуле `:client_auth_protection`
36
90
  - **Идентификация по имени**: Каждый клиент должен быть явно указан в `allowed_services`
37
- - **Валидация формата токена**: Строгая проверка структуры токена
38
91
 
39
- ### 5. Rate Limiting (опционально)
40
- - **Защита от DDoS**: Ограничение количества запросов от каждого клиента
41
- - **Sliding window**: Справедливое распределение запросов во времени
42
- - **Fail-open стратегия**: Если Redis недоступен, запросы пропускаются (для надёжности)
43
- - **Per-client лимиты**: Индивидуальные счётчики для каждого клиента
92
+ ### 3. Защита от перехвата токенов (replay-attack)
93
+ - **Защита от replay-attack**: использованные токены добавляются в черный список, при включенном модуле `:replay_attack_protection`
94
+ - **Staleness**: Токены автоматически становятся недействительными после истечения `token_expiration_time`
95
+ - **Допуск на рассинхрон часов**: ±30 секунд (CLOCK_SKEW)
96
+ - **TTL блэклиста**: вычисляется автоматически как `token_expiration_time + CLOCK_SKEW`
97
+
98
+ ### 4. Rate limiting
99
+ - **Скользящее окно**: при включенном модуле `:rate_limit_protection` — точный подсчёт запросов без граничного burst-эффекта фиксированного окна
100
+ - **Fail-open стратегия**: если Redis недоступен, запросы пропускаются (для надёжности сервиса)
101
+ - **Изоляция по клиентам**: лимит считается отдельно для каждого `client_name`
44
102
 
45
- ### 6. Защита от инъекций
46
- - **Валидация входных данных**: Проверка типа, длины и формата токенов
47
- - **Максимальная длина токена**: Ограничение 2048 символов
48
- - **Проверка на пустые значения**: Отклонение пустых или некорректных токенов
103
+ ### 5. Защита от SSL stripping MITM attack
104
+ - **Принудительное HTTPS на сервере**: Все запросы должны быть совершены по HTTPS, чтобы защитить токены от перехвата
105
+ - **Включается на RobustClientSocket, ключём `ssl_verify: true`**
49
106
 
50
107
  ## 📦 Установка
51
108
 
@@ -53,197 +110,131 @@ RobustServerSocket реализует многоуровневую систем
53
110
  gem 'robust_server_socket'
54
111
  ```
55
112
 
113
+ и на клиенте:
114
+ ```ruby
115
+ gem 'robust_client_socket'
116
+ ```
117
+
56
118
  ## ⚙️ Конфигурация
57
119
 
58
120
  Создайте файл `config/initializers/robust_server_socket.rb`:
59
121
 
60
122
  ```ruby
61
123
  RobustServerSocket.configure do |c|
62
- # ОБЯЗАТЕЛЬНЫЕ ПАРАМЕТРЫ
63
-
124
+ c.using_modules = %i[
125
+ client_auth_protection
126
+ replay_attack_protection
127
+ rate_limit_protection
128
+ ]
129
+
64
130
  # Приватный ключ сервиса (RSA-2048 или выше)
65
131
  c.private_key = ENV['ROBUST_SERVER_PRIVATE_KEY']
66
- c.token_expiration_time = 3
67
-
132
+
133
+ # Время жизни токена в секундах (должно совпадать с TTL на клиенте)
134
+ c.token_expiration_time = 10
135
+
68
136
  # Список разрешённых сервисов (whitelist)
69
- # Должен совпадать с именами RobustClientSocket клиента
137
+ # Должен совпадать с service_name RobustClientSocket клиента
70
138
  c.allowed_services = %w[core payments notifications]
71
-
72
- # Redis для работы replay-attack protection и throttling
139
+
140
+ # Redis для replay_attack_protection и rate_limit_protection
73
141
  c.redis_url = ENV.fetch('REDIS_URL', 'redis://localhost:6379/0')
74
142
  c.redis_pass = ENV['REDIS_PASSWORD']
75
143
 
76
- # НЕОБЯЗАТЕЛЬНЫЕ ПАРАМЕТРЫ
77
- # Включить ограничение частоты запросов (по умолчанию: false)
78
- c.rate_limit_enabled = true
79
- # Максимальное количество запросов в окне времени (по умолчанию: 100)
80
- c.rate_limit_max_requests = 100
81
- # Размер временного окна в секундах (по умолчанию: 60)
82
- c.rate_limit_window_seconds = 60
144
+ # rate_limit_protection
145
+ c.rate_limit_max_requests = 100 # максимум запросов в окне (по умолчанию: 100)
146
+ c.rate_limit_window_seconds = 60 # размер окна в секундах (по умолчанию: 60)
83
147
  end
84
148
 
85
- # Загрузка конфигурации с валидацией
86
149
  RobustServerSocket.load!
87
150
  ```
88
151
 
89
- ### Опции конфигурации сервиса
152
+ ### Опции конфигурации
153
+
154
+ | Параметр | Тип | Обязательный | Default | Описание |
155
+ |-----------------------------|---------|-------------|-------------------------------------------------------------------------------------|-------------------------------------------------|
156
+ | `private_key` | String | ✅ | — | Приватный RSA ключ сервиса (RSA-2048 или выше) |
157
+ | `token_expiration_time` | Integer | ✅ | 10 | Время жизни токена в секундах |
158
+ | `allowed_services` | Array | ✅ | — | Список разрешённых сервисов (whitelist) |
159
+ | `redis_url` | String | ✅ | — | URL для подключения к Redis |
160
+ | `redis_pass` | String | ❌ | nil | Пароль для Redis |
161
+ | `using_modules` | Array | ❌ | `[:client_auth_protection, :rate_limit_protection, :replay_attack_protection]` | Используемые модули |
162
+ | `rate_limit_max_requests` | Integer | ❌ | 100 | Максимальное количество запросов в окне |
163
+ | `rate_limit_window_seconds` | Integer | ❌ | 60 | Размер временного окна в секундах |
164
+
165
+ > `store_used_token_time` больше не является конфигурируемым — вычисляется автоматически как `token_expiration_time + 30` (CLOCK_SKEW).
90
166
 
91
- | Параметр | Тип | Обязательный | Default | Описание |
92
- |----------|-----|--------------|---------|----------|
93
- | `private_key` | String | | - | Приватный RSA ключ сервиса (RSA-2048 или выше) |
94
- | `token_expiration_time` | Integer | ✅ | - | Время жизни токена в секундах |
95
- | `allowed_services` | Array | ✅ | - | Список разрешённых сервисов (whitelist) |
96
- | `redis_url` | String | ✅ | - | URL для подключения к Redis |
97
- | `redis_pass` | String | ❌ | nil | Пароль для Redis (если требуется) |
98
- | `rate_limit_enabled` | Boolean | ❌ | false | Включить ограничение частоты запросов |
99
- | `rate_limit_max_requests` | Integer | | 100 | Максимальное количество запросов в окне времени |
100
- | `rate_limit_window_seconds` | Integer | ❌ | 60 | Размер временного окна в секундах |
167
+ ### Совместимость с RobustClientSocket
168
+
169
+ Токен содержит таймстамп в **миллисекундах**. RobustClientSocket начиная с версии X.X должен генерировать:
170
+
171
+ ```ruby
172
+ Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
173
+ ```
174
+
175
+ Устаревший `Time.now.utc.to_i` (секунды) приведёт к тому, что все токены будут отклонены как `stale`.
101
176
 
102
177
  ## 🚀 Использование
103
178
 
104
179
  ### Базовая авторизация
105
180
 
106
181
  ```ruby
107
- # В контроллере или middleware
108
182
  class ApiController < ApplicationController
109
183
  before_action :authenticate_service!
110
-
184
+
111
185
  private
112
-
186
+
113
187
  def authenticate_service!
114
- # Хедер, прописанный в RobustClientSocket (SECURE-TOKEN default)
115
188
  token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
116
-
117
- @current_service = RobustServerSocket::ClientToken.validate!(token) # bang method (рейзит ошибки)
189
+ @current_service = RobustServerSocket::ClientToken.validate!(token)
118
190
  rescue RobustServerSocket::ClientToken::InvalidToken
119
191
  render json: { error: 'Invalid token' }, status: :unauthorized
120
- rescue RobustServerSocket::ClientToken::UnauthorizedClient
192
+ rescue RobustServerSocket::Modules::ClientAuthProtection::UnauthorizedClient
121
193
  render json: { error: 'Unauthorized service' }, status: :forbidden
122
- rescue RobustServerSocket::ClientToken::UsedToken
194
+ rescue RobustServerSocket::Modules::ReplayAttackProtection::UsedToken
123
195
  render json: { error: 'Token already used' }, status: :unauthorized
124
- rescue RobustServerSocket::ClientToken::StaleToken
196
+ rescue RobustServerSocket::Modules::ReplayAttackProtection::StaleToken
125
197
  render json: { error: 'Token expired' }, status: :unauthorized
126
198
  rescue RobustServerSocket::RateLimiter::RateLimitExceeded => e
127
199
  render json: { error: e.message }, status: :too_many_requests
128
200
  end
129
-
130
- def authenticate_service
131
- token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
132
- @current_service = RobustServerSocket::ClientToken.valid?(token) # не рейзит
133
-
134
- if @current_service
135
- # Токен валиден
136
- else
137
- # Токен невалиден
138
- render json: { error: 'Unauthorized' }, status: :unauthorized
139
- end
140
- end
141
201
  end
142
202
  ```
143
203
 
144
- ### Расширенное использование
204
+ ### valid? (не рейзит)
145
205
 
146
206
  ```ruby
147
- # Создание объекта токена
148
- token_string = request.headers['Authorization']&.sub(/^Bearer /, '')
149
- client_token = RobustServerSocket::ClientToken.new(token_string)
207
+ token = request.headers['SECURE-TOKEN']&.sub(/^Bearer /, '')
208
+ client_token = RobustServerSocket::ClientToken.new(token)
150
209
 
151
- # Проверка валидности (возвращает true/false)
152
210
  if client_token.valid?
153
- # Получение имени клиента
154
211
  client_name = client_token.client
155
- puts "Authorized client: #{client_name}"
156
212
  else
157
- # Токен невалиден
158
213
  render json: { error: 'Unauthorized' }, status: :unauthorized
159
214
  end
160
-
161
- # Быстрая валидация с исключениями
162
- begin
163
- service_token = RobustServerSocket::ClientToken.validate!(token_string)
164
- client_name = service_token.client
165
- rescue => e
166
- # Обработка специфичных ошибок
167
- end
168
- ```
169
-
170
- ### Rate Limiting вручную
171
-
172
- ```ruby
173
- # Проверка текущего количества попыток
174
- attempts = RobustServerSocket::RateLimiter.current_attempts('core')
175
- puts "Core service made #{attempts} requests"
176
-
177
- # Сброс счётчика для конкретного клиента
178
- RobustServerSocket::RateLimiter.reset!('core')
179
-
180
- # Проверка с исключением при превышении
181
- begin
182
- RobustServerSocket::RateLimiter.check!('core')
183
- rescue RobustServerSocket::RateLimiter::RateLimitExceeded => e
184
- puts e.message # "Rate limit exceeded for core: 101/100 requests per 60s"
185
- end
186
-
187
- # Проверка без исключения (возвращает false при превышении)
188
- if RobustServerSocket::RateLimiter.check('core')
189
- # Лимит не превышен
190
- else
191
- # Лимит превышен
192
- end
193
- ```
194
-
195
- ## 🚦 Rate Limiting (Ограничение частоты запросов)
196
-
197
- ### Принцип работы
198
-
199
- Rate Limiter защищает ваш сервис от перегрузки, ограничивая количество запросов от каждого клиента в определённом временном окне.
200
-
201
- **Характеристики:**
202
- - **Per-client counters**: Отдельный счётчик для каждого сервиса
203
- - **Sliding window**: Окно сбрасывается автоматически после истечения времени
204
- - **Атомарность**: Инкремент и проверка выполняются атомарно (Redis LUA script)
205
- - **Fail-open**: При недоступности Redis запросы пропускаются (не блокируются)
206
-
207
- ### Мониторинг
208
-
209
- ```ruby
210
- # Проверка текущего состояния
211
- clients = ['core', 'payments', 'notifications']
212
- clients.each do |client|
213
- attempts = RobustServerSocket::RateLimiter.current_attempts(client)
214
- max = RobustServerSocket.configuration.rate_limit_max_requests
215
- puts "#{client}: #{attempts}/#{max}"
216
- end
217
-
218
- # В метриках (Prometheus, StatsD и т.д.)
219
- clients.each do |client|
220
- attempts = RobustServerSocket::RateLimiter.current_attempts(client)
221
- Metrics.gauge("rate_limiter.attempts.#{client}", attempts)
222
- end
223
215
  ```
224
216
 
225
217
  ## ❌ Обработка ошибок
226
218
 
227
219
  ### Типы исключений
228
220
 
229
- | Исключение | Причина | HTTP статус | Действие |
230
- |-----------|---------|-------------|----------|
231
- | `InvalidToken` | Токен не может быть расшифрован или имеет неверный формат | 401 | Проверьте корректность токена и ключей |
232
- | `UnauthorizedClient` | Клиент не в whitelist | 403 | Добавьте клиента в `allowed_services` |
233
- | `UsedToken` | Токен уже был использован | 401 | Клиент должен запросить новый токен |
234
- | `StaleToken` | Токен истёк | 401 | Клиент должен запросить новый токен |
235
- | `RateLimitExceeded` | Превышен лимит запросов | 429 | Клиент должен подождать или ретраить позже |
221
+ | Исключение | Причина | HTTP статус |
222
+ |---------------------------------------------------------|----------------------------------------------|-------------|
223
+ | `ClientToken::InvalidToken` | Токен не расшифрован или неверный формат | 401 |
224
+ | `Modules::ClientAuthProtection::UnauthorizedClient` | Клиент не в whitelist | 403 |
225
+ | `Modules::ReplayAttackProtection::UsedToken` | Токен уже был использован | 401 |
226
+ | `Modules::ReplayAttackProtection::StaleToken` | Токен истёк или из будущего (>30s) | 401 |
227
+ | `RateLimiter::RateLimitExceeded` | Превышен лимит запросов | 429 |
236
228
 
237
229
  ### Централизованная обработка
238
230
 
239
231
  ```ruby
240
- # В ApplicationController
241
232
  rescue_from RobustServerSocket::ClientToken::InvalidToken,
242
- RobustServerSocket::ClientToken::UsedToken,
243
- RobustServerSocket::ClientToken::StaleToken,
233
+ RobustServerSocket::Modules::ReplayAttackProtection::UsedToken,
234
+ RobustServerSocket::Modules::ReplayAttackProtection::StaleToken,
244
235
  with: :unauthorized_response
245
236
 
246
- rescue_from RobustServerSocket::ClientToken::UnauthorizedClient,
237
+ rescue_from RobustServerSocket::Modules::ClientAuthProtection::UnauthorizedClient,
247
238
  with: :forbidden_response
248
239
 
249
240
  rescue_from RobustServerSocket::RateLimiter::RateLimitExceeded,
@@ -252,106 +243,24 @@ rescue_from RobustServerSocket::RateLimiter::RateLimitExceeded,
252
243
  private
253
244
 
254
245
  def unauthorized_response(exception)
255
- render json: {
256
- error: 'Authentication failed',
257
- message: exception.message,
258
- type: exception.class.name
259
- }, status: :unauthorized
246
+ render json: { error: 'Authentication failed', message: exception.message }, status: :unauthorized
260
247
  end
261
248
 
262
249
  def forbidden_response(exception)
263
- render json: {
264
- error: 'Access denied',
265
- message: exception.message,
266
- type: exception.class.name
267
- }, status: :forbidden
250
+ render json: { error: 'Access denied', message: exception.message }, status: :forbidden
268
251
  end
269
252
 
270
253
  def rate_limit_response(exception)
271
254
  render json: {
272
255
  error: 'Too many requests',
273
256
  message: exception.message,
274
- type: exception.class.name,
275
257
  retry_after: RobustServerSocket.configuration.rate_limit_window_seconds
276
258
  }, status: :too_many_requests
277
259
  end
278
260
  ```
279
261
 
280
- ## 💡 Рекомендации по использованию
281
-
282
- ### 1. Управление ключами
283
-
284
- **✅ DO:**
285
- ```ruby
286
- # Храните ключи в переменных окружения
287
- c.private_key = ENV['ROBUST_SERVER_PRIVATE_KEY']
288
-
289
- # Используйте secrets management (AWS Secrets Manager, Vault, и т.д.)
290
- c.private_key = Rails.application.credentials.dig(:robust_server, :private_key)
291
-
292
- # Генерируйте ключи правильно
293
- # openssl genrsa -out private_key.pem 2048
294
- # openssl rsa -in private_key.pem -pubout -out public_key.pem
295
- ```
296
-
297
- **❌ DON'T:**
298
- ```ruby
299
- # НЕ коммитьте ключи в git
300
- c.private_key = "-----BEGIN PRIVATE KEY-----\nMII..."
301
-
302
- # НЕ используйте слабые ключи
303
- # Минимум RSA-2048, рекомендуется RSA-4096 для высокой безопасности
304
- ```
305
-
306
- ### 2. Конфигурация Redis
307
-
308
- **✅ DO:**
309
- ```ruby
310
- # Используйте отдельный namespace для каждого окружения
311
- c.redis_url = ENV.fetch('REDIS_URL', 'redis://localhost:6379/0')
312
-
313
- # Настройте connection pool в production
314
- # В config/initializers/redis.rb
315
- Redis.current = ConnectionPool.new(size: 5, timeout: 5) do
316
- Redis.new(url: ENV['REDIS_URL'], password: ENV['REDIS_PASSWORD'])
317
- end
318
-
319
- # Мониторьте состояние Redis
320
- # Используйте Redis Sentinel или Cluster для высокой доступности
321
- ```
322
-
323
- **❌ DON'T:**
324
- ```ruby
325
- # НЕ используйте одну БД Redis для всех окружений, используйте отдельную bd redis
326
- # НЕ игнорируйте ошибки Redis (rate limiter уже fail-open, но логируйте их)
327
- ```
328
-
329
- ### 5. Whitelist сервисов
330
-
331
- ```ruby
332
- # Явно указывайте только необходимые сервисы
333
- c.allowed_services = %w[core payments] # ✅
334
-
335
- # НЕ используйте wildcards или регулярные выражения
336
- c.allowed_services = %w[*] # ❌ ОПАСНО!
337
-
338
- # Синхронизируйте с keychain клиента
339
- # Server (robust_server_socket):
340
- c.allowed_services = %w[core]
341
-
342
- # Client (robust_client_socket):
343
- c.keychain = {
344
- core: { # ← Должно совпадать
345
- base_uri: 'https://core.example.com',
346
- public_key: '-----BEGIN PUBLIC KEY-----...'
347
- }
348
- }
349
- ```
350
-
351
262
  ## 🤝 Интеграция с RobustClientSocket
352
263
 
353
- Для полноценной работы необходимо настроить клиентскую часть:
354
-
355
264
  ```ruby
356
265
  # На клиенте (RobustClientSocket)
357
266
  RobustClientSocket.configure do |c|
@@ -359,22 +268,25 @@ RobustClientSocket.configure do |c|
359
268
  c.keychain = {
360
269
  payments: {
361
270
  base_uri: 'https://payments.example.com',
362
- public_key: '-----BEGIN PUBLIC KEY-----...' # Публичный ключ сервера payments
271
+ public_key: '-----BEGIN PUBLIC KEY-----...'
363
272
  }
364
273
  }
365
274
  end
366
275
 
367
276
  # На сервере (RobustServerSocket)
368
277
  RobustServerSocket.configure do |c|
369
- c.allowed_services = %w[core] # ← Соответствует service_name клиента
370
- c.private_key = '-----BEGIN PRIVATE KEY-----...' # Приватная пара к public_key
278
+ c.allowed_services = %w[core]
279
+ c.private_key = '-----BEGIN PRIVATE KEY-----...'
371
280
  end
372
281
  ```
373
282
 
283
+ ## 🗺️ TODO
284
+
285
+ - [ ] **Per-client ключи для rate limiter** — настраиваемые индивидуальные лимиты для каждого `client_name` вместо единого глобального лимита
286
+
374
287
  ## 📚 Дополнительные ресурсы
375
288
 
376
- - [BENCHMARK_ANALYSIS.md](BENCHMARK_ANALYSIS.md)
377
- - [RobustClientSocket documentation](https://github.com/tee0zed/robust_client_socket)
289
+ - [RobustClientSocket](https://github.com/tee0zed/robust_client_socket)
378
290
  - [RSA encryption best practices](https://www.openssl.org/docs/)
379
291
  - [Redis security guide](https://redis.io/topics/security)
380
292
 
@@ -384,4 +296,4 @@ end
384
296
 
385
297
  ## 🐛 Баги и предложения
386
298
 
387
- Сообщайте о проблемах через issue tracker вашего репозитория.
299
+ Сообщайте о багах через ишью, или напрямую тг @cruel_mango или email tee0zed@gmail.com
data/Rakefile CHANGED
@@ -1,12 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- t.test_files = FileList["test/**/test_*.rb"]
10
- end
6
+ RSpec::Core::RakeTask.new(:spec)
11
7
 
12
- task default: :test
8
+ task default: :spec