# 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 ` - 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)