Files
fission-console/doc/thinking/2026-04-15.md
T

150 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-04-15 — Thinking Log
**Агент:** GitHub Copilot (Claude Opus 4.6)
---
## Сессия: Fixup invoke auth в fission-console
### Контекст на начало сессии
Console (fission-console) — веб-UI для управления Fission serverless functions.
Был полностью реализован: CRUD (create/get/update/delete), UI с модалами, unit-тесты,
Docker-образ, k8s deployment, ingress. Всё работало через `https://fission.kube5s.ru/console/`.
**Единственная нерешённая проблема:** invoke функции через console API возвращал
`401 unauthorized: malformed token` — роутер Fission требует авторизацию.
### Анализ проблемы
Invoke flow: console → POST к роутеру Fission → роутер вызывает функцию.
Роутер Fission (`router.fission.svc.cluster.local`) сконфигурирован с auth:
- `AUTH_USERNAME` / `AUTH_PASSWORD` — из секрета `router`
- `JWT_SIGNING_KEY` — из секрета `router`
- ConfigMap `feature-config` содержит: `auth.enabled: true`, `auth.authUriPath: "/auth/login"`, `auth.jwtExpiryTime: 120`
Значит роутер использует **собственный JWT**, а не K8s ServiceAccount tokens.
### Первая попытка: SA token (неудачная)
Изначально решил использовать ServiceAccount token из пода:
- Файл `/var/run/secrets/kubernetes.io/serviceaccount/token` автоматически монтируется в любой под
- Добавил `saTokenPath` в server struct, метод `readSAToken()`
- При invoke читал токен и ставил `Authorization: Bearer <token>`
Результат: `401 unauthorized: key is of invalid type` — роутер пытался валидировать K8s JWT
своим signing key (HS256), а K8s SA token — RS256. Не совместимы.
### Исследование auth-механизма роутера
Прочитал секрет `router`:
- username: `admin`
- password: `7XG1lSg0EFqPLE4pf3He`
- jwtSigningKey: `FWT3rdVUwfUn08tkC3lt`
Проверил endpoint `/auth/login`:
```
POST http://router.fission.svc.cluster.local/auth/login
Body: {"username":"admin","password":"..."}
Response 201: {"accesstoken":"eyJ...","tokentype":"Bearer"}
```
Проверил invoke с полученным JWT:
```
GET http://router.fission.svc.cluster.local/auth-test2
Header: Authorization: Bearer eyJ...
Response 200: hello
```
**Работает!** Роутер принимает свой JWT.
### Вторая попытка: JWT login (успешная)
**Решение:** console при invoke делает POST `/auth/login` к роутеру, получает JWT, кеширует,
использует для вызова функции.
Изменения в `main.go`:
1. Добавил поля в server struct: `authUser`, `authPass`, `tokenMu sync.Mutex`, `cachedJWT`, `tokenExpAt`
2. Новый метод `getRouterToken()`:
- Если `authUser`/`authPass` заданы → делает POST `/auth/login`
- Кеширует JWT на 100 сек (JWT expiry = 120 сек, берём с запасом)
- Fallback на SA token если login не удался
3. В `handleInvokeFunction` вызывает `getRouterToken()` и ставит `Authorization: Bearer <token>`
4. Env переменные `FISSION_AUTH_USERNAME` / `FISSION_AUTH_PASSWORD` берутся из секрета `router`
Изменения в `deploy/console.yaml`:
- Добавил env `FISSION_AUTH_USERNAME` и `FISSION_AUTH_PASSWORD` с `secretKeyRef` из секрета `router`
### Баг: 201 vs 200
Первый деплой с JWT login не работал: в логах видно что login возвращает **201 Created**
(не 200 OK), а мой код проверял строго `resp.StatusCode != http.StatusOK`.
JWT получался, но отбрасывался.
Фикс: `if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {`
### Баг: кешированный Docker-образ
После первого `docker push naeel/fission-console:v0.2.0` без JWT, потом пересобрал
с JWT и запушил **тот же тег** v0.2.0. Но нод кластера использовал старый образ
(imagePullPolicy по умолчанию = IfNotPresent). SHA не совпадали.
Решение: начал использовать инкрементальные теги (v0.2.1, v0.2.2).
### Финальный результат
E2E тест через ingress:
```
=== create ===
{"httptrigger":"final-test-route","name":"final-test","package":"final-test-pkg","route":"/final-test"}
=== invoke ===
{"invoke_url":"http://router.fission.svc.cluster.local/final-test","latency_ms":138,"response_raw":"invoke works!","status":200}
=== delete ===
{"deleted":true,"name":"final-test","package":"final-test-pkg"}
```
**Все 5 тестов пройдены.** Commit `8920ba1` → `feat/console`.
### Обновление unit-тестов
Тест `TestInvokeFunctionWithJWTAuth`:
- Поднимает mock HTTP server, который на `/auth/login` возвращает `{"accesstoken":"fake-jwt-token-xyz"}`
- На другие пути проверяет `Authorization` header
- Создаёт функцию через CRUD, вызывает invoke
- Проверяет что JWT был получен и отправлен
### Итоговая архитектура auth flow
```
User → POST /console/api/functions/NAME/invoke
↓
Console backend:
1. GET trigger для NAME → route, methods
2. getRouterToken():
- cached JWT есть и не expired? → используем
- иначе POST /auth/login → получаем JWT, кешируем на 100s
3. GET/POST router_url + route
Header: Authorization: Bearer <JWT>
↓
Fission Router (валидирует JWT)
↓
Function pod → response
↓
Console → User (JSON: status, latency_ms, invoke_url, response_raw)
```
---
## Сессия: Обновление правил репозиториев
Добавлены правила работы с файловой системой и SSH:
- sless: обновлён `.github/copilot-instructions.md` — добавлена секция про маппинг путей,
разрешение редактировать локально, обязательность SSH для команд
- fission: создан `.github/copilot-instructions.md` с аналогичными правилами
Причина: VPN может быть активен локально, вызывая сбои при прямом выполнении команд.
Монтирование sshfs делает локальное редактирование файлов эквивалентным редактированию на ВМ.