From 7f998d522248e6ee57b574f3dfd7ebe19b54316c Mon Sep 17 00:00:00 2001 From: vitya Date: Thu, 11 Jun 2026 16:28:31 +0300 Subject: [PATCH] =?UTF-8?q?wiki(ingest):=20concepts/verdaccio-token-lifecy?= =?UTF-8?q?cle=20=E2=80=94=20restart=20trap=20+=20JWT=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Постмортем утреннего фикса verdaccio: ephemeral secret (нет `secret:` в конфиге) → инвалидация всех токенов при каждом рестарте; max_users:-1 + pnpm login → 409; web-UI login как обход; JWT config fix (пинуем secret, api.jwt 900d). Recipe рефреша для yarn classic vs berry. Co-Authored-By: Claude Opus 4.8 (1M context) --- .wiki/concepts/verdaccio-token-lifecycle.md | 114 ++++++++++++++++++++ .wiki/index.md | 1 + .wiki/log.md | 1 + 3 files changed, 116 insertions(+) create mode 100644 .wiki/concepts/verdaccio-token-lifecycle.md diff --git a/.wiki/concepts/verdaccio-token-lifecycle.md b/.wiki/concepts/verdaccio-token-lifecycle.md new file mode 100644 index 0000000..3e7122f --- /dev/null +++ b/.wiki/concepts/verdaccio-token-lifecycle.md @@ -0,0 +1,114 @@ +--- +title: Verdaccio token lifecycle — restart trap + JWT fix +type: concept +tags: [verdaccio, npm, pnpm, yarn, auth, jwt, gotcha, postmortem] +sources: [] +updated: 2026-06-11 +--- + +# Verdaccio token lifecycle — restart trap + JWT fix + +## Root cause: auto-generated secret + +Verdaccio без явного `secret:` в конфиге генерирует случайный секрет **при каждом запуске**. Все токены (legacy opaque и JWT) подписаны этим секретом — перезапуск контейнера = мгновенная инвалидация всех выданных токенов у всех клиентов. + +**Симптом:** `401 Unauthorized` после рестарта VDS/контейнера, несмотря на то что токен в `.npmrc` визуально «есть». + +**Фикс (применён 2026-06-11):** явный `secret:` в `config.yaml` → токены переживают рестарты. + +## `max_users: -1` + pnpm login → 409 + +Стандартный workaround «перелогинься» (`pnpm login --registry ...`) не работает при `max_users: -1`. + +Verdaccio htpasswd plugin v6 проверяет `max_users` **первым** в `adduser()`, до проверки существования пользователя: + +``` +if (max_users === -1) → return 409 "user registration disabled" +``` + +`pnpm login` отправляет `PUT /-/user/org.couchdb.user:` — это registration-path. Verdaccio блокирует даже для существующих юзеров, даже если пароль верный. + +Изменение `max_users` на положительное число решает проблему, но открывает регистрацию всем — нежелательно. + +## Правильный способ получить токен при `max_users: -1` + +Использовать **web-UI login endpoint**, который не проходит через `adduser()`: + +```powershell +$body = '{"username":"vitya","password":"..."}' +$r = Invoke-RestMethod -Uri "https://verdaccio.kzntsv.site/-/verdaccio/sec/login" ` + -Method POST -Body $body -ContentType "application/json" +$r.token # JWT, 7d expiry +``` + +Полученный токен — в `~/.npmrc`: + +```ini +//verdaccio.kzntsv.site/:_authToken=eyJ... +``` + +## Конфиг-фикс (2026-06-11) + +Добавлено в `/opt/stacks/verdaccio/config/config.yaml` на [vds-kzntsv](../entities/vds-kzntsv.md): + +```yaml +secret: <32-byte-hex-permanent> + +security: + api: + jwt: + sign: + expiresIn: 30d + notBefore: 0 + web: + sign: + expiresIn: 7d + verify: {} +``` + +До фикса: нет `secret` и нет `security` → legacy tokens, привязанные к ephemeral secret. +После: web-UI и npm-API используют один JWT формат, подписанный постоянным секретом. + +> **Примечание:** `security.api.jwt` меняет формат API-токенов с legacy opaque (`5a3X...==`) на JWT (`eyJ...`). Старые legacy-токены в `.npmrc` у всех клиентов перестают работать после смены конфига — нужен refresh. + +## Срок жизни токенов + +| Endpoint | Expiry | Используется для | +|---|---|---| +| `/-/verdaccio/sec/login` | 7d (web.sign) | Ручной refresh, агенты | +| npm API (pnpm/yarn/npm login) | 30d (api.jwt.sign) | После решения `max_users` проблемы | + +## Клиенты: где хранится токен + +### npm / pnpm / yarn classic (1.x) + +Читают `~/.npmrc` (user-level) или `.npmrc` в корне проекта: + +```ini +//verdaccio.kzntsv.site/:_authToken=eyJ... +@snollajs:registry=https://verdaccio.kzntsv.site/ +``` + +Yarn classic читает тот же `~/.npmrc` — update токена в одном файле покрывает всех. + +### yarn berry (2.x+) + +Не читает `~/.npmrc`. Конфиг в `.yarnrc.yml`: + +```yaml +npmRegistries: + "https://verdaccio.kzntsv.site": + npmAuthToken: "eyJ..." + npmAlwaysAuth: true + +npmScopes: + snollajs: + npmRegistryServer: "https://verdaccio.kzntsv.site" +``` + +Refresh токена = обновить `npmAuthToken` в `.yarnrc.yml` каждого проекта, либо использовать `yarn npm login --scope snollajs` (тоже обходит `max_users: -1` через web UI path). + +## Связанные страницы + +- [verdaccio-prune-semantics](verdaccio-prune-semantics.md) — storage layout и GC +- [vds-kzntsv](../entities/vds-kzntsv.md) — хост где крутится инстанс diff --git a/.wiki/index.md b/.wiki/index.md index dd0fdbc..bd43afe 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -50,6 +50,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever - [vbox-windows-stability-tuning](concepts/vbox-windows-stability-tuning.md) — VirtualBox + Windows-гость — нюансы стабильности cross-hypervisor миграции - [vds-kzntsv-dhcp-outage-2026-05-28](concepts/vds-kzntsv-dhcp-outage-2026-05-28.md) — DHCP outage 2026-05-28 на vds-kzntsv — диагностика + recovery runbook (link UP но без IPv4 → статика + тикет хостеру) - [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published +- [verdaccio-token-lifecycle](concepts/verdaccio-token-lifecycle.md) — restart trap (ephemeral secret) + max_users:-1 pnpm 409 + JWT fix + yarn clients - [winacme-iis-owin-catchall-http01](concepts/winacme-iis-owin-catchall-http01.md) — win-acme HTTP-01 авто-renewal LE на IIS под OWIN-catch-all CMS — challenge в отдельном No-Managed-Code приложении + патч шаблона; заменяет ручной PFX-импорт - [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18 - [windows-server-2025-core-bootstrap](concepts/windows-server-2025-core-bootstrap.md) — Win Server 2025 Core (RUVDS) — bootstrap для IIS-хоста: default-blockers + transfer-методов матрица diff --git a/.wiki/log.md b/.wiki/log.md index fe33dcd..d643f88 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -2,6 +2,7 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations). +## [2026-06-11] ingest | verdaccio-token-lifecycle — restart trap (ephemeral secret → 401), max_users:-1 + pnpm 409 постмортем, JWT config fix, yarn classic vs berry token refresh recipe. ## [2026-06-11] update | MSSQL backup gap closed — `/opt/stacks/backup/scripts/run.sh` расширен: 5 prod DBs через `docker exec mssql sqlcmd BACKUP DATABASE ... WITH COPY_ONLY, INIT` (Express, без COMPRESSION); .bak в bind-mount → mv в DUMP_DIR → rsync kreknin. Обновлён [[concepts/mssql-on-vds]] (TODO→реализовано). ## [2026-06-11] lint | 10 issues found, 8 fixed inline