7.0 KiB
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— из секретаrouterJWT_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:
- Добавил поля в server struct:
authUser,authPass,tokenMu sync.Mutex,cachedJWT,tokenExpAt - Новый метод
getRouterToken():- Если
authUser/authPassзаданы → делает POST/auth/login - Кеширует JWT на 100 сек (JWT expiry = 120 сек, берём с запасом)
- Fallback на SA token если login не удался
- Если
- В
handleInvokeFunctionвызываетgetRouterToken()и ставитAuthorization: Bearer <token> - 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"} - На другие пути проверяет
Authorizationheader - Создаёт функцию через 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 делает локальное редактирование файлов эквивалентным редактированию на ВМ.