docs: add IP-gate access control spec
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# IP-gate: ограничение доступа на время разработки
|
||||
|
||||
## Задача
|
||||
|
||||
Сайт доступен на реальном домене (через VPS + NPM + Netbird), но находится в активной разработке/тестировании. Нужно ограничить доступ, не мешая разработке и полному тестированию функционала (включая OAuth и webhook-и).
|
||||
|
||||
## Решение
|
||||
|
||||
IP-whitelist на уровне Fastify (`onRequest` хук). Только запросы с разрешённых IP проходят. Внешние webhook-и и OAuth callback-и исключены из проверки.
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### `.env`
|
||||
|
||||
```env
|
||||
SITE_ACCESS_IPS=1.2.3.4,5.6.7.8
|
||||
```
|
||||
|
||||
- Не задана или пуста — защита **отключена**
|
||||
- IP через запятую, пробелы игнорируются (трим)
|
||||
- `request.ip` возвращает реальный IP клиента благодаря `trustProxy: true`
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Новый плагин: `server/src/plugins/ip-gate.js`
|
||||
|
||||
Регистрируется в `server/src/index.js` **перед** всеми маршрутами.
|
||||
|
||||
```js
|
||||
fastify.register(async function ipGate(fastify, opts) {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
// защита выключена
|
||||
// путь в исключениях
|
||||
// ip в списке
|
||||
// иначе 403
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Логика `onRequest`
|
||||
|
||||
1. `SITE_ACCESS_IPS` пуст → `return` (пропустить)
|
||||
2. Путь запроса в списке исключений → `return`
|
||||
3. `request.ip` есть в `SITE_ACCESS_IPS` → `return`
|
||||
4. Иначе → `reply.code(403).type('text/html').send(htmlPage)`
|
||||
|
||||
### Исключения
|
||||
|
||||
Маршруты, которые должны работать всегда (их вызывают внешние сервисы, а не браузер тестировщика):
|
||||
|
||||
| Путь | Причина |
|
||||
|---|---|
|
||||
| `/api/auth/oauth/vk/callback` | VK OAuth callback |
|
||||
| `/api/auth/oauth/yandex/callback` | Yandex OAuth callback |
|
||||
| `/api/webhooks/yookassa` | YooKassa payment webhook |
|
||||
| `/api/admin/notifications/telegram/webhook` | Telegram webhook |
|
||||
|
||||
Статика (загружается браузером тестировщика, поэтому тоже проверяется):
|
||||
- `/uploads/*` и `/uploads-resized/*` — **не** исключаем, блокируются вместе со всем остальным
|
||||
|
||||
### 403-страница
|
||||
|
||||
HTML-страница с информацией о магазине и статусе разработки:
|
||||
|
||||
- Название: «Любимый Креатив»
|
||||
- Подзаголовок: «Изделия ручной работы: вещи с характером и вниманием к деталям»
|
||||
- Сообщение: «Сайт находится в разработке и скоро будет доступен»
|
||||
- Показывает IP посетителя (чтобы можно было сообщить для добавления в whitelist)
|
||||
- Минимальная стилизация (чистый HTML + inline CSS, без внешних ресурсов)
|
||||
|
||||
## Точки регистрации
|
||||
|
||||
В `server/src/index.js`:
|
||||
|
||||
```js
|
||||
// после trustProxy, перед маршрутами
|
||||
await fastify.register(require('./plugins/ip-gate'))
|
||||
```
|
||||
|
||||
## Тестирование
|
||||
|
||||
- **Юнит-тесты**: `server/src/plugins/__tests__/ip-gate.test.js`
|
||||
- IP в списке → запрос проходит
|
||||
- IP не в списке → 403
|
||||
- Путь-исключение → проходит с любым IP
|
||||
- `SITE_ACCESS_IPS` не задан → защита выключена
|
||||
- Пробелы в списке IP → корректная работа
|
||||
|
||||
## Включение/выключение
|
||||
|
||||
- **Включить**: задать `SITE_ACCESS_IPS` в `.env`
|
||||
- **Выключить**: удалить `SITE_ACCESS_IPS` или оставить пустым
|
||||
- Перезапуск сервера не требуется если используется `node --watch`
|
||||
Reference in New Issue
Block a user