docs: API token saga — DDoS-Guard User-Agent fix, new tokens, endpoint matrix
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# API Token Saga — 30.06.2026
|
||||
|
||||
**Дата:** 30 июня 2026
|
||||
**Контекст:** попытка доступа к Nubes Cloud API для правки Terraform Provider
|
||||
|
||||
---
|
||||
|
||||
## Кратко
|
||||
|
||||
6 часов debug'а. DDoS-Guard резал запросы, выдавая `403 Forbidden`.
|
||||
Причина: **User-Agent фильтрация** + **протухшие токены** + **неправильный endpoint**.
|
||||
|
||||
---
|
||||
|
||||
## Хронология ошибок
|
||||
|
||||
### Ошибка 1: 403 от DDoS-Guard (2 часа потеряно)
|
||||
|
||||
```
|
||||
$ curl https://deck-api.ngcloud.ru/api/v1/index.cfm/services/90
|
||||
→ 403 Forbidden
|
||||
```
|
||||
|
||||
**Что думали:** DDoS-Guard блокирует наш IP, нужно добавлять в whitelist.
|
||||
Проверяли с ВМ, из подов кластера — везде 403.
|
||||
|
||||
**Реальная причина:** DDoS-Guard фильтрует по `User-Agent`. `curl/8.x` блокируется, браузерный `Mozilla/5.0 ...` пропускается.
|
||||
|
||||
**Как нашли:** HAR-файлы (`HAR/OK.har`) показали что успешные запросы из браузера идут с HTTP/2 и браузерным User-Agent. Провайдер форсит HTTP/1.1 с Go-шным UA.
|
||||
|
||||
**Фикс:**
|
||||
```bash
|
||||
# НЕ работает:
|
||||
curl https://deck-api.ngcloud.ru/...
|
||||
|
||||
# РАБОТАЕТ:
|
||||
curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
|
||||
https://deck-api.ngcloud.ru/...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Ошибка 2: Старые токены без token_type (30 минут)
|
||||
|
||||
```
|
||||
$ curl -H "Authorization: Bearer $OLD_TOKEN" ... (с правильным UA)
|
||||
→ {"message":"invalid JWT token type"}
|
||||
```
|
||||
|
||||
**Что думали:** Токены протухли. Проверили — живые (exp: август-сентябрь 2026).
|
||||
Потом думали что API изменился и требует новый формат.
|
||||
|
||||
**Реальная причина:** Старые токены из `secrets/prod.token` и `secrets/dev.token` были выданы БЕЗ поля `token_type` в JWT. API обновился и теперь требует `token_type: "tech"`.
|
||||
|
||||
**Декодирование старого токена:**
|
||||
```json
|
||||
{
|
||||
"iss": "auth-api",
|
||||
"sub": "0199e325-1cdf-7cda-9319-e5302a85e291",
|
||||
"exp": 1786932260,
|
||||
// token_type: ОТСУТСТВУЕТ ← причина ошибки
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Ошибка 3: Токен не того стенда (30 минут)
|
||||
|
||||
```
|
||||
$ curl -H "Authorization: Bearer $TEST_TOKEN" https://deck-api.ngcloud.ru/...
|
||||
→ {"message":"IAM error"}
|
||||
```
|
||||
|
||||
**Что думали:** Токен битый, неправильная подпись.
|
||||
|
||||
**Реальная причина:** Токены привязаны к конкретному API-endpoint. TEST-токен работает только на `deck-api-test.ngcloud.ru`, PROD — на `deck-api.ngcloud.ru`, DEV — на `deck-api-dev.ngcloud.ru`.
|
||||
|
||||
---
|
||||
|
||||
### Ошибка 4: Двойной сбой (1 час)
|
||||
|
||||
На `prod.token` была комбинация двух проблем:
|
||||
1. Старый формат (без token_type) → `"invalid JWT token type"`
|
||||
2. Плюс DDoS-Guard скрывал реальный ответ API за `403 Forbidden`
|
||||
|
||||
Пока не добавили User-Agent, мы даже не видели сообщение `"invalid JWT token type"` — видели только `403`.
|
||||
|
||||
---
|
||||
|
||||
## Как это работает (правильная схема)
|
||||
|
||||
### 1. API endpoints
|
||||
|
||||
| Стенд | Endpoint | Токен |
|
||||
|-------|----------|-------|
|
||||
| **PROD** | `https://deck-api.ngcloud.ru/api/v1/index.cfm` | `secrets/prod.token` |
|
||||
| **TEST** | `https://deck-api-test.ngcloud.ru/api/v1/index.cfm` | `secrets/test.token` |
|
||||
| **DEV** | `https://deck-api-dev.ngcloud.ru/api/v1/index.cfm` | `secrets/dev.token` |
|
||||
|
||||
Токены НЕ взаимозаменяемы между стендами.
|
||||
|
||||
### 2. Аутентификация
|
||||
|
||||
```
|
||||
┌──────────┐ Bearer JWT ┌───────────┐ validate ┌──────────┐
|
||||
│ Client │ ──────────────────→ │ deck-api │ ──────────────→ │ auth-api │
|
||||
│ (curl/Go)│ ←── JSON response ─ │ (CFML) │ ←── OK/FAIL ── │ (IAM) │
|
||||
└──────────┘ └───────────┘ └──────────┘
|
||||
```
|
||||
|
||||
- Токен передаётся как `Authorization: Bearer <JWT>`
|
||||
- deck-api проверяет токен через auth-api (внутренний вызов)
|
||||
- JWT содержит: `iss: "auth-api"`, `token_type: "tech"`, `ClientID: "WZ03709"`
|
||||
- `token_type` обязателен (новое требование API)
|
||||
|
||||
### 3. User-Agent фильтрация DDoS-Guard
|
||||
|
||||
```
|
||||
┌──────────┐ ┌────────────┐ ┌──────────┐
|
||||
│ Client │ ─── HTTP request → │ DDoS-Guard │ ─── if UA ok ───→ │ deck-api │
|
||||
│ │ ←── 403 если UA ── │ 185.178... │ │ │
|
||||
│ │ не браузерный └────────────┘ └──────────┘
|
||||
```
|
||||
|
||||
DDoS-Guard проверяет заголовок `User-Agent`:
|
||||
- `curl/x.y.z` → **403 Forbidden** (блокировка)
|
||||
- `Go-http-client/x.y` → **403 Forbidden** (провайдер блокируется!)
|
||||
- `Mozilla/5.0 ...` → **пропускается**
|
||||
|
||||
Это **критический баг провайдера**: Go HTTP-клиент по умолчанию отправляет `Go-http-client/2.0`, который DDoS-Guard режет.
|
||||
|
||||
### 4. Правильный curl-запрос
|
||||
|
||||
```bash
|
||||
# Рабочий запрос к PROD API:
|
||||
curl -sk --max-time 10 \
|
||||
-H "Authorization: Bearer $(cat secrets/prod.token)" \
|
||||
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
|
||||
"https://deck-api.ngcloud.ru/api/v1/index.cfm/services/90"
|
||||
|
||||
# Аналогично для TEST:
|
||||
curl -sk --max-time 10 \
|
||||
-H "Authorization: Bearer $(cat secrets/test.token.new)" \
|
||||
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
|
||||
"https://deck-api-test.ngcloud.ru/api/v1/index.cfm/services/90"
|
||||
```
|
||||
|
||||
### 5. Живые токены (30.06.2026)
|
||||
|
||||
| Файл | Назначение | Выдан | Истекает | token_type | ClientID |
|
||||
|------|-----------|-------|----------|------------|----------|
|
||||
| `secrets/prod.token` | PROD | 18.02.2026 | 17.08.2026 | ❌ отсутствует | ❌ |
|
||||
| `secrets/dev.token` | DEV | 26.03.2026 | 22.09.2026 | ❌ отсутствует | ❌ |
|
||||
| `secrets/test.token` | TEST | 14.03.2026 | 10.09.2026 | ❌ отсутствует | ❌ |
|
||||
| `secrets/test.token.new` | **TEST** | 30.06.2026 13:37 | ~дек.2026 | ✅ `"tech"` | ✅ `WZ03709` |
|
||||
| `secrets/dev.token.new` (в памяти) | **DEV** | 30.06.2026 13:42 | ~дек.2026 | ✅ `"tech"` | ✅ `WZ03709` |
|
||||
| `secrets/prod.token.new` (в памяти) | **PROD** | 30.06.2026 13:41 | ~дек.2026 | ✅ `"tech"` | ✅ `WZ03709` |
|
||||
|
||||
⚠️ Новые токены ещё не сохранены в файлы `secrets/` — только test сохранён.
|
||||
|
||||
---
|
||||
|
||||
## Что надо исправить в провайдере
|
||||
|
||||
### P0: User-Agent
|
||||
|
||||
В `universal_rebuild/internal/core/client.go` (и `internal/core/client.go` Legacy):
|
||||
|
||||
```go
|
||||
// Сейчас (БАГ):
|
||||
req.Header.Set("User-Agent", "Go-http-client/2.0") // или default
|
||||
|
||||
// Надо:
|
||||
req.Header.Set("User-Agent", "Terraform-Provider-Nubes/5.0") // пропускаемый UA
|
||||
```
|
||||
|
||||
Или в `http.Client.Transport`, или в каждом `doRequest`.
|
||||
|
||||
### Связанное: HTTP/2
|
||||
|
||||
Провайдер форсит HTTP/1.1:
|
||||
```go
|
||||
NextProtos: []string{"http/1.1"},
|
||||
ForceAttemptHTTP2: false,
|
||||
```
|
||||
|
||||
HAR показывает что API работает через HTTP/2. Возможно стоит разрешить HTTP/2 (убрать `ForceAttemptHTTP2: false`) — это ускорит поллинг.
|
||||
|
||||
---
|
||||
|
||||
## Ключевые файлы
|
||||
|
||||
- `/home/naeel/tf_provider/HAR/OK.har` — эталонные запросы (HTTP/2 + браузерный UA)
|
||||
- `/home/naeel/tf_provider/secrets/prod.token` — старый prod-токен (без token_type)
|
||||
- `/home/naeel/tf_provider/secrets/test.token.new` — новый test-токен (рабочий)
|
||||
- `/home/naeel/tf_provider/devops/profiles/*/profile.env` — endpoint'ы стендов
|
||||
- `/home/naeel/tf_provider/universal_rebuild/internal/core/client.go` — `doRequest()` (надо править UA)
|
||||
- `/home/naeel/tf_provider/universal_rebuild/internal/provider/provider.go` — HTTP-транспорт (HTTP/1.1 force)
|
||||
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLWFwaSIsInN1YiI6IjAxOWNjMjY4LTZjNmEtNzgxZS04NjEzLTRiZWQ0ZWM3Y2QyMCIsImV4cCI6MTc5ODM3NzQ3NywiaWF0IjoxNzgyODI1NDc3LCJqdGkiOiI3NjFhZmQ5YS1hM2U1LTRiNTctODcyYi1jNWY4MzRlYTNlYzQiLCJhdXRoX3RpbWUiOjAsInR5cCI6IiIsImF6cCI6IiIsInNlc3Npb25fc3RhdGUiOiIiLCJhY3IiOiIiLCJhbGxvd2VkLW9yaWdpbnMiOm51bGwsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6bnVsbH0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpudWxsfX0sInNjb3BlIjoiIiwic2lkIjoiIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiIiwiQ2xpZW50SUQiOiJXWjAzNzA5IiwiY29tcGFueV9pZCI6IjAxOWNjMjRhLTcyN2UtNzQwZi1iNDA3LWJlYzc5ZGFiNDE2MiIsImNvbXBhbnlfbmFtZSI6Im5hZWVsX3Rlc3QiLCJ0b2tlbl90eXBlIjoidGVjaCIsImlkcF91c3JfdWlkIjoiMDE5Y2MyNjgtNmM2YS03ODFlLTg2MTMtNGJlZDRlYzdjZDIwIiwibG9naW4iOiJ0YXpldGRpbm92bkBnbWFpbC5jb20iLCJmaXJzdG5hbWUiOiLQndCw0LjQu9GMIiwibWlkZGxlbmFtZSI6ItCk0LDRgNC40YLQvtCy0LjRhyIsImxhc3RuYW1lIjoi0KLQsNC30LXRgtC00LjQvdC-0LIiLCJncm91cHMiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6IiIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGF6ZXRkaW5vdm5AZ21haWwuY29tIn0.U6lfbr_GazFGNhqS0jC2lJejAOXKRup5Ay_3shbrdncMivKq_QVpTZin6mBvUv6m1p7YjSTRwyij3SrY1ZLVXME0TiImipcfbp2BIvmvOrudCRTnSkfL0tjHoEXeEs0EgsBs9Gmp6UOv7sBijxQosOSfd13uGR8_Vgk4drP7kbNRT7xX7Qon4fUpF_G7YlCSf8i5B_178SaQaVZosnn4BweSBqGO4gWfHD8m11_VyrDANXVzCu1eprfAtCtMj3vD8gQiua-ATB6Fg0iStN5ZVbqO8ep6zVctxwittwH7sN0fgY4_BfiSCVx2fSJ5-EHlodjjIPHTMIlouQSgGB6LCA
|
||||
Reference in New Issue
Block a user