refactor(wiki): bucket sources/ → summaries/ (Karpathy canon, решение 2026-08-24)

This commit is contained in:
2026-08-25 13:43:06 +03:00
parent 15eff6bd01
commit 730f067e13
14 changed files with 14 additions and 12 deletions

0
.wiki/summaries/.gitkeep Normal file
View File

View File

@@ -0,0 +1,111 @@
---
title: books VDS daily backup → kreknin — pipeline standup 2026-05-25
type: source
tags: [backup, books-vds, kreknin, rsync, elasticsearch-snapshot, curl-smtp, cron, ntfy]
ingested: 2026-05-25
raw_path: ../../.tasks/books-vds-backup-daily-kreknin.md
updated: 2026-05-25
---
# books VDS daily backup → kreknin
Поднят ежедневный backup pipeline с [[../entities/books-vds]] (`89.253.255.133` / host4g.ru / CentOS 7) → [[../entities/kreknin-synology]] (`195.19.90.188`) в **06:00 MSK**. Параллель к [[vds-kzntsv-bootstrap-2026-05-20]] §backup и [[ruvds-backup-daily-kreknin-2026-05-24]] (pattern mirror), но source = CentOS 7 с hybrid SSH-compose + Portainer стек.
Источник истины — `.tasks/books-vds-backup-daily-kreknin.md` и `scripts/books-vds-backup-daily-kreknin/`.
## Background — почему отдельно
Прошлые сессии (и `[[../concepts/vds-kzntsv-ssh-access]]` до 2026-05-25) ошибочно утверждали что books-стек живёт на [[../entities/vds-kzntsv]]. **Это не так:** books на отдельном клиентском VDS `89.253.255.133`. User указал на ошибку явно: «books VDS - это совсем другой сервер, клиентский». См. [[../entities/books-vds]] для актуального factsheet.
## Scope
| Path on books VDS | Reason | Approx size |
|---|---|---|
| `/opt/books/{api,job-scheduler,task-runner,ntfy,books-ops-mcp}` | App configs (`config/default.json` overrides) | < 100 MB |
| `/usr/docker/minio/data` | MinIO blobs (S3) — основная масса | ~4.5 GB |
| `/usr/docker/elasticsearch/snapshots` | ES snapshot files (repo `kreknin`) | < 1 MB (read_me index only) |
| `/usr/docker/traefik/{letsencrypt,data}` | acme.json + traefik.yml | < 5 MB |
| `/etc/{ssh,hosts,cron.d}` | system config | < 50 KB |
| `/root/.ssh` | root SSH state | < 5 KB |
| `mariadb.sql.gz` (live dump) | books-db full dump | ~270 MB |
| `mongo.archive.gz` (live dump) | shared mongo (root auth) | ~170 KB |
| `job-scheduler-mongo.archive.gz` (live dump) | scheduler agenda (no auth) | ~3 MB |
Total после initial sync: **4.87 GB** (`du -sh` reported 4.8 GB после rsync).
Не бэкапятся: `/var/lib/docker/`, `/var/log/`, transient buildx volumes.
## Decisions log
- **Tool = bash + rsync + curl** (no rclone, no msmtp). Linux native, no extra installs. Pattern mirror VDS-infra `run.sh`.
- **ES snapshot via REST API** (filesystem repo `kreknin`), не data-dir rsync. Required one-time stack edit `/usr/docker/elasticsearch/docker-compose.yml`: добавлены env `path.repo=/snapshots` + bind `./snapshots:/snapshots`, ES recreated. Снимки атомарны и Lucene-consistent.
- **Email via `curl --ssl-reqd --url smtps://...:465`** (implicit TLS), не msmtp. CentOS 7 + EOL repos = msmtp install painful; curl native + Yandex SMTP works. Initial attempt с `smtp://...:465` + `--ssl-reqd` дала 30-sec timeout (curl попытался STARTTLS на TLS-only-from-start порту) — исправлено переходом на `smtps://`.
- **RFC 5322 CRLF headers + Date + MIME-Version + Content-Type** в email-body. Yandex отвергает без Date. Initial body без них принимался при STARTTLS теста, но for production safety — full headers.
- **`StrictHostKeyChecking=yes`** (НЕ `accept-new`) — CentOS 7 OpenSSH 7.4 не поддерживает `accept-new` (added in 7.6). kreknin host key pre-populated в `/root/.ssh/known_hosts` на bootstrap step.
- **Retention 7 daily snapshots** (matches VDS + RUVDS pattern). ES snapshot pruning отдельно — via REST DELETE.
- **`ntfy.vds.kzntsv.site/vds-backup` topic** (shared канал с VDS + RUVDS + windows-host). Title `BOOKS-VDS backup OK <date>` отличает от других хостов на phone-side.
- **books-job-scheduler-mongo без auth** — `MONGO_INITDB_ROOT_*` env пустой, `mongodump --archive` без `--uri` достаточен. Shared `mongo` — root auth (urircreds).
- **Cron 06:00 MSK** — после VDS-infra (05:00), RUVDS (04:30), windows-host (05:30). Break перед US-EU business start.
- **`books-vds-portainer/full-env` consolidated** → `books-vds/full-env` (parity с `vds-kzntsv/full-env` one-file-per-host pattern).
## Хронология standup'а (2026-05-25)
- **08:30:** user-указание начать `stateful-split-volume-copy` под supervision. Probe `vds-ops` MCP показал что books-db нет на vds-kzntsv (только generic mariadb/mongo/minio). Wiki `[[../concepts/vds-kzntsv-ssh-access]]` (тогда `books-ssh-access.md`) врала что books живёт на vds-kzntsv.
- **08:45:** user: «89.253.255.133 - books VDS, клиентский. Сделаем backup на kreknin». Pivot фокус.
- **08:48:** SSH probe `id_ed25519_books_ops` → ready, root access OK. Inventory: hybrid Portainer + SSH-compose, hosting books-api/web/scheduler/task-runner/db + shared infra.
- **08:55:** Portainer API token request — user создал `claude-code-automation` PAT в Portainer UI (`portainer.kzntsv.site`). Saved в pass `books-vds-portainer/full-env`. API tested OK. But `elasticsearch`/`mongo`/`minio`/`books-db`/`traefik` нет среди Portainer-managed stacks — they're SSH-compose. User: «после backup'а мигрируем максимум под Portainer».
- **08:58:** ES probe — `path.repo` не настроен, snapshot API не работает без edit'а compose.
- **09:00:** User approval на ES stack edit «сейчас, ES никто не использует». Sed edit `docker-compose.yml` + recreate. docker-compose 1.29 → docker 26 incompat (`KeyError: 'ContainerConfig'`) → manual `docker rm` renamed-stuck → `docker-compose up -d` clean → ES ready in 30s. Snapshot repo `kreknin` registered + test snap SUCCESS → deleted.
- **09:14:** Wrote `scripts/books-vds-backup-daily-kreknin/{run.sh,.env.example,README.md}` локально. Built `.env` from pass entries (books-vds + vds-kzntsv NTFY + snolla-smtp SMTP). Deployed `/opt/stacks/backup/{run.sh,.env}` на VDS via scp. Generated `kreknin-key` ed25519 на VDS. Authorized pubkey on kreknin via workstation `id_ed25519_kreknin`. Pre-populated kreknin host key в `/root/.ssh/known_hosts` (CentOS 7 no accept-new).
- **09:15:** Smoke run start. DB dumps OK (mariadb 270MB, mongo 170KB, scheduler-mongo 2.8MB). ES snapshot SUCCESS. rsync started.
- **09:29:** rsync done, 4.87 GB in **13m28s** (7.6 MB/s). ntfy push отправлен. Email **timeout 30s** — bug в email_send.
- **09:30:** Diagnose. SMTP_PORT=465 + `smtp://` + `--ssl-reqd` = curl попытался STARTTLS на implicit-TLS-only порту. Fix: `smtps://` schema. Plus CRLF headers + Date header. Re-tested: SMTP send OK.
- **09:32:** Updated run.sh re-deployed. Cron installed `/etc/cron.d/books-vds-backup` (0 6 * * * root /opt/stacks/backup/run.sh). crond active.
## Pipeline state
- **Live:** cron `books-vds-backup`, daily 06:00 MSK, root user.
- **Verified:** initial sync 4.87 GB end-to-end + ntfy push отправлен.
- **Phone-side ntfy verify:** **pending user confirmation** (был ли push `BOOKS-VDS backup OK 2026-05-25` на ntfy app в общем канале `vds-backup`).
- **Email verify:** sent после fix (см. 09:32 fix). User должен подтвердить получение `[BOOKS-VDS] backup OK <date>` или standalone fix-test message в inbox.
- **Day-2 verify:** automatic at 06:00 MSK 2026-05-26 — first cron-trigger.
- **Retention prune verify:** automatic at day-8 (когда snapshot count > 7).
## Bugs fixed during smoke
- **SMTP timeout 30s** — `smtp://...:465` + `--ssl-reqd` = curl STARTTLS на implicit-TLS-only порту. Server ждёт TLS handshake, curl ждёт plain EHLO, deadlock. Fix: `smtps://` scheme.
- **email RFC 5322** — initial body had no Date/MIME headers, only From/To/Subject. Yandex отвергает без Date. Fix: добавлены Date, MIME-Version, Content-Type CRLF headers.
- **CentOS 7 OpenSSH 7.4 no accept-new** — `StrictHostKeyChecking=accept-new` parse-error. Fix: `StrictHostKeyChecking=yes` + pre-populated known_hosts.
- **docker-compose 1.29 ↔ docker 26 KeyError ContainerConfig** — recreate failed mid-way, container renamed-stuck. Manual `docker rm -f` + clean `up -d` rescued.
## Open follow-ups (не блокер)
- **Migrate SSH-compose стеки под Portainer** (отдельная таска, user-requested после backup live). Targets: elasticsearch, mongo, minio, books-db, traefik, proxy-chain.
- **SSH-аудит books VDS** — отдельный аудит по pattern [[vds-kzntsv-ssh-access]]. На сейчас знаем только root key `id_ed25519_books_ops` под root — других пользователей не enum'ил. Pre-existing keys unknown.
- **kreknin space audit** — quarterly check `du -sh /volume1/NetBackup/*` чтобы не упереться в 5.7T limit.
- **wd40 backup**: рассмотреть второй backup target (B2 / Glacier) для resilience — см. [[../concepts/future-resilient-architecture-goals]].
## Атомарный revert
```bash
# На books VDS:
ssh root@89.253.255.133 'rm /etc/cron.d/books-vds-backup; rm -rf /opt/stacks/backup /var/log/books-vds-backup'
# Revert ES path.repo edit:
ssh root@89.253.255.133 'cd /usr/docker/elasticsearch && \
cp docker-compose.yml.bak-pre-snapshots-2026-05-25 docker-compose.yml && \
docker rm -f elasticsearch && docker-compose up -d'
# На kreknin (через SSH):
ssh vitya@195.19.90.188 'rm -rf /volume1/NetBackup/books-vds'
# Remove the books-vds-backup-20260525 pubkey line from authorized_keys.
```
## Cross-refs
- Source host: [[../entities/books-vds]]
- Backup target: [[../entities/kreknin-synology]]
- Pattern parent: [[vds-kzntsv-bootstrap-2026-05-20]] §backup (msmtp + rsync --link-dest)
- Sibling pipeline: [[ruvds-backup-daily-kreknin-2026-05-24]] (rclone+SFTP)
- Notification: общий ntfy topic `vds-backup` на `ntfy.vds.kzntsv.site`.
- Misnaming legacy: [[../concepts/vds-kzntsv-ssh-access]] (бывший `books-ssh-access` до 2026-05-25).

View File

@@ -0,0 +1,74 @@
---
title: DE VDS 3x-UI — setup session 2026-07-14
type: source
tags: [vless, germany, fornex, 3x-ui, csrf, troubleshooting, session]
ingested: 2026-07-14
raw_path: (live session, no raw file)
sources: []
updated: 2026-07-14
---
# DE VDS 3x-UI — сессия настройки 2026-07-14
Хроника заведения прокси-VDS [`de-vds-3xui`](../entities/de-vds-3xui.md) (Fornex, Германия). Аналог [`nl-vds-3xui`](../entities/nl-vds-3xui.md) — повторены **только proven-working** решения, маскированные протоколы НЕ поднимались.
## Закуп / стартовая точка
Заказ: VPS Custom 1-2-20, Ubuntu 24.04, без панели, Германия, 300 Мбит/с, 1 vCPU / 2 ГБ / 20 ГБ NVMe. Провайдер по факту — **Fornex** (`335555.fornex.cloud`), IP `130.17.17.158`. Свежая машина: только `:22`, ufw inactive, swap 0. Креды → `pass de-vds-3xui/full-env` (host-key `SHA256:1AQ5…cmdE`).
Подход (по урокам NL-сессии [`nl-vds-3xui-setup-2026-06-05`](nl-vds-3xui-setup-2026-06-05.md)):
- только plain VLESS `security=none` (Reality/MTProto/SOCKS5 у боевых клиентов из РФ не заработали);
- проверять на реальном клиенте, не на своих curl-тестах ([`proxy-debugging-test-the-real-client`](../concepts/proxy-debugging-test-the-real-client.md));
- креды сразу в `pass`.
## Что сделано
1. **Recon + `pass`.** SSH через `plink -m <script>` (инлайн-кавычки PowerShell корёжит — тот же урок, что на NL). Креды в `pass de-vds-3xui/full-env`.
2. **Установка 3x-ui v3.5.0** официальным `install.sh` (нативный systemd, плюс автоставка fail2ban `3x-ipl`). v3.5.0 **автогенерит** рандомные panel user/pass/port/webBasePath (`hasDefaultCredential:false`) — лучше старых версий, НО plaintext пароля недоступен (bcrypt).
3. **Panel-креды перевыставлены** на свои (random user/pass) — см. гоча ниже про wrapper.
4. **Plain VLESS 32030** `security=none` создан — см. ниже, не напрямую, а через panel API (борьба с 3.5.0).
5. **Server-side e2e:** xray-клиент на сервере → 32030 → exit = IP сервера (IPv4 `130.17.17.158`, IPv6 `2a02:6b40:2000:3505::1`).
6. **Real-client из РФ — подтверждён:** user подключён через этот узел прямо во время сессии. ✅
## Борьба с 3x-ui 3.5.0 (главное знание сессии)
На NL стояла 3.2.7; тут 3.5.0, и API/DB-модель поменялись. Это и заняло основное время.
### Гоча 1 — login 403 (CSRF, не креды)
`POST {webBasePath}login` возвращал `403 Forbidden` с **пустым телом** + CSP-nonce заголовками. Выглядело как «битые креды», но:
- `/login` → 404 (роут только под `webBasePath`);
- `{BP}login` → 403 (роут есть, но режется middleware);
- x-ui лог молчит (запрос до хендлера не доходит);
- Referer/Origin/UA/X-Requested-With — не помогали.
**Root cause** (найден через source на GitHub): `internal/web/middleware/security.go``CSRFMiddleware` — на всех unsafe-методах (POST) без валидного CSRF-токена → `c.AbortWithStatus(403)` (пустое тело, а SecurityHeaders уже навесил CSP). Логин-хендлер тут **ни при чём** (он даже при неудаче отдаёт 200+JSON). Токен: `GET {webBasePath}csrf-token` (публичный, кладёт `CSRF_TOKEN` в session-cookie) → вернуть тем же заголовком `X-CSRF-Token`.
### Гоча 2 — прямой INSERT в `inbounds` не рендерит инбаунд
Первые две попытки — `INSERT INTO inbounds ...` с валидным JSON (вторая — с эталонным `settings`/`stream_settings`/`sniffing`, **скопированным 1:1 с рабочего nl-vds 32030**). x-ui стартовал, логировал `Normalized sub_sort_index on 1 inbound(s)`, но в `/usr/local/x-ui/bin/config.json` инбаунд **не попадал**, xray `:32030` не слушал. Никакой ошибки в логе.
**Root cause:** в 3.5.0 клиентская модель разнесена по таблицам `clients` / `client_inbounds` / `client_traffics`; эти записи заполняются **только сервисом `AddInbound`** (пути panel API/UI), а не raw-SQL. Инбаунд без клиентских строк x-ui в xray-конфиг не рендерит.
**Лекарство:** panel API `POST {BP}panel/api/inbounds/add` — body = `model.Inbound` (camelCase: `remark,enable,port,protocol,listen,tag,settings,streamSettings,sniffing,shareAddrStrategy,...`), причём `settings`/`streamSettings`/`sniffing`**stringified JSON** (строки, не вложенные объекты). После API-add xray сразу начал слушать 32030 (3.5.0 hot-applies к работающему xray), а после `x-ui restart` — конфиг персистентен.
### Гоча 3 — wrapper `x-ui setting` не применяет креды
`x-ui setting -username … -password …` (shell-wrapper) печатает меню, но `users`-таблицу **не меняет** → логин падает «Invalid username or password». Работает только бинарник напрямую: `systemctl stop x-ui; /usr/local/x-ui/x-ui setting -username … -password …` → «Username and password updated successfully».
### Эталон JSON (снят с рабочего nl-vds 32030)
- `settings` (vless): `{"clients":[{auth,comment,created_at,email,enable,expiryTime,id(uuid),limitIp,password,reset,security:"auto",subId,tgId,totalGB,updated_at}], "decryption":"none","encryption":"none","testseed":[900,500,900,256]}`
- `streamSettings`: `{"network":"tcp","tcpSettings":{"acceptProxyProtocol":false,"header":{"type":"none"}},"security":"none"}`**`tcpSettings`, не `tcp`**, с `header.type:none`.
- `sniffing`: `{"enabled":false}` (простой, без `destOverride`).
## Рабочее решение для друзей
`vless://f3a0dda0-947b-4a11-b3f5-ca973640f843@130.17.17.158:32030?type=tcp&security=none&encryption=none#de-vless-32030` — Hiddify/v2rayN/v2rayNG. При включённом VPN в Telegram прокси не настраивать. Ключи/uuid — `pass de-vds-3xui/full-env`.
## Открытые хвосты
- **Per-friend UUID** (сейчас один — `vitya`): заводить через панель при раздаче, для возможности отзыва.
- **DPI-стойкий канал** (не plain VLESS) — отдельная нерешённая задача (на NL Reality у боевых клиентов так и не поднялся).
- **Panel на http:37601** — random path + fail2ban компенсируют; при желании — SSH-tunnel-only или LE-серт (опция 20 меню).
- **Бэкап `x-ui.db`** в pipeline (как `backup-inventory-2026-06` для nl-vds) — не заведён.

View File

@@ -0,0 +1,275 @@
---
title: IIS Host Migration Session 2026-05-19
type: source
tags: [migration, iis, windows, recovery, traefik]
ingested: 2026-05-19
raw_path: ../../.tasks/iis-on-host-migration.md
updated: 2026-05-21
---
# IIS Host Migration Session 2026-05-19
Перенос основного CMS-сайта (`MoreThenCms.Web`, обслуживает 11 клиентских доменов через Host header) с IIS внутри [[snolla-recovery-vm]] на нативный IIS [[windows-recovery-host]]. Цель — убрать VBox как слой нестабильности.
## Хронология
**Старт сессии** (после [[nas-recovery-session-2026-05-18]]) — VM один раз отвалилась сетью, оживили через `VBoxManage guestcontrol` + `ipconfig /release /renew` (recipe в [[vbox-windows-stability-tuning]] подтверждён ещё раз).
**Phase 1 — discovery (read-only):**
- В VM через SSH (NAT-forward 127.0.0.1:8022, не bridged 192.168.1.15 — это устарело): `appcmd list site/apppool/app/vdir /xml`.
- На хосте: `Get-WindowsOptionalFeature -Online IIS-*` через elevated PS — все нужные фичи (`IIS-WebServer`, `IIS-ASPNET45`, `IIS-NetFxExtensibility45`, `IIS-ISAPIFilter`, `IIS-ManagementConsole`, `IIS-IIS6ManagementCompatibility`, `IIS-Metabase`) уже установлены. `WebAdministration` PS-module грузится из admin-PS.
- В источниках на хосте (`grep`): hardcoded `C:\inetpub\wwwroot\` в коде CMS нет — всё через `ConfigurationManager.AppSettings["sitePath"]`. **НО** в prod Web.config (из backup `vm-sites\`) ключ `sitePath` явно проставлен абсолютным путём — патч в Phase 2 необходим.
**Phase 1 находки (важные расхождения с wiki):**
- `Snolla.IdentityManager` в VM **на :8089, не :80** (вики говорила :80). Sub-apps stostayer.old `/calc`, `/price`, `/price/tireService` существуют с отдельными app pools.
- `C:\stayer\Snolla.IdentityManager\Web.config` указывает на **`SRV-1135520-1\SQLEXPRESS` + Integrated Security** — мёртвый внешний сервер. Сайт точно не работает, deploy-артефакт без живого консьюмера.
- `C:\stayer\MoreThenCms.Web\Web.config` (stostayer) использует **внешний production MSSQL `89.253.219.2,1433`** с user `stostayer` — это **другая инфраструктура**, не наш контейнер.
**Phase 2 — миграция (admin PS):**
- `Default Web Site` остановлен (`autoStart=false`).
- `robocopy` из `C:\nas-recovery\vm-sites\``C:\sites\MoreThenCms.Web` (8.7 GB / 44.7k файлов / 1m13s) и → `C:\stayer\` (2.2 GB / 13.9k файлов / 21s).
- Web.config patch только для `C:\sites\MoreThenCms.Web\Web.config`: `sitePath` `C:\inetpub\wwwroot\MoreThenCms.Web\``C:\sites\MoreThenCms.Web\` + `Data Source=10.0.2.2``Data Source=localhost`. UTF-8 with BOM через `[System.IO.File]::WriteAllText` (паттерн из [[cms-config-rewrite-pattern]]).
- 3 AppPool (`.NET v4.0` Integrated, `ApplicationPoolIdentity`): `MoreThenCms.Web`, `stostayer`, `stostayer.old`.
- 3 IIS-сайта: `MoreThenCms.Web *:80` (catch-all), `stostayer *:8090`, `stostayer.old *:8091`.
- ACL `icacls /grant 'IIS AppPool\<site>:(OI)(CI)M' /T` на каждый physical root.
- Skip: `Snolla.IdentityManager` (по решению — публично не нужен), 3 sub-apps stostayer.old (по решению).
- Smoke test через `http://localhost/` с Host header: все 11 хостов отвечают (большинство 302 redirect — CMS работает). `rimiz.ru` — 404 (CMS-side, не инфра). stostayer/oldstostayer на :8090/:8091 — timeout, **в текущей сессии не разбирались**.
**Phase 3 — traefik switch (file-provider auto-reload):**
- Backup `*.yml.bak-phase3-2026-05-19` для 11 файлов в `C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\`.
- Patch: `host.docker.internal:18080``host.docker.internal:80` (11 yml: snolla, rimiz, labtools, labtoolspro, pilorama98, tandemmebel, emspb, kupimknigi, maljarka, sestech, isc-artmaterials). UTF-8 без BOM.
- **Не трогали** `stostayer.yml` (`:18180`) и `oldstostayer.yml` (`:18181`) — stayer-сайты остаются на VM.
- Public smoke через `https://localhost:4443/` с Host header: **10/11 → HTTP 200**. Только `rimiz.ru` → 404 (как и в локальном тесте).
## Результат
Production-трафик 10 главных доменов теперь идёт **полностью без VM**: client → OpenWRT → traefik → host-IIS native → MSSQL container на хосте. VM продолжает обслуживать только stostayer/oldstostayer через старые NAT port forwards (`:18180/:18181`).
## Открытые вопросы / нюансы
- **`rimiz.ru` отдаёт 404** на host-IIS (и на VM до миграции тоже отдавал?). CMS-routing не знает этот host. Проверить mapping в БД / CMS-сайт-таблице.
- **stostayer + stostayer.old timeout** на host-IIS (`http://localhost:8090/`, `:8091`). Возможно cold-start ASP.NET, возможно реальный bug. Сейчас не разбирали — traefik для них всё ещё на VM.
- **Snolla.IdentityManager не мигрирован** — если что-то внутри CMS дёргает его через `localhost:8089`, может тихо ломаться (пока симптомов не видно).
- **VM можно глушить** только после: либо доделать stostayer на host, либо явное решение не мигрировать stayer на этот хост.
- **Phase 5 не сделан**: URL Rewrite + ARR (фикс `:4443` в редиректах CMS), Application Initialization (warm-start), log rotation `C:\inetpub\logs\`.
## Технические артефакты
- `.tasks/iis-on-host-migration.md` — детальный план фаз, обновлён со статусом Phase 1-3 = done.
- Backup конфигов traefik: `*.yml.bak-phase3-2026-05-19`.
- Скрипты Phase 2 и Phase 3 — в истории чата сессии (не сохранены отдельным файлом, идемпотентны на повторный запуск).
## Phase 4 — реорг `C:\sites\` + перенос stayer'ов (вечер 2026-05-19)
Пользователь возмутился разбросом (`C:\sites\` только для snolla, `C:\stayer\` для stayer). Реорганизация ради единой иерархии:
- `C:\sites\MoreThenCms.Web``C:\sites\snolla` (rename)
- `C:\stayer\MoreThenCms.Web``C:\sites\stostayer` (move)
- `C:\stayer\stostayer.old``C:\sites\stostayer.old` (move)
- `C:\stayer\Snolla.IdentityManager``C:\sites\snolla-identity-manager` (move + kebab-rename)
- Удалены: `C:\stayer\Mis.StoStayer.{Price,TireService,Calculator}.{Api,Web}` (sub-apps не нужны).
- `C:\stayer\` полностью удалён.
IIS sync:
- Site `MoreThenCms.Web` → переименован в `snolla` (`Set-ItemProperty IIS:\Sites\... -Name name`)
- AppPool `MoreThenCms.Web`**rename невозможен in-place**, пересоздан как `snolla` с тем же набором properties (.NET v4.0 Integrated, `ApplicationPoolIdentity`), site rebound, старый pool удалён.
- `physicalPath` всех 3 sites обновлён под `C:\sites\<name>`.
- Web.config sitePath patches:
- `C:\sites\snolla\Web.config`: `C:\sites\MoreThenCms.Web\``C:\sites\snolla\`
- `C:\sites\stostayer\Web.config`: `C:\stayer\MoreThenCms.Web\``C:\sites\stostayer\`
- ACL re-grant + cleanup stale ACE для `IIS AppPool\MoreThenCms.Web`.
## Phase 5 — stostayer.old DB-conn патч (запутанная история conn-string'ов в stayer\)
Локальный smoke `:8091` падал в timeout. Причина: `C:\sites\stostayer.old\web.config` имел `Data Source=10.0.2.2` (адрес VBox NAT gateway, на хосте не резолвится) с user=`snolla` — фактически идентичная нашему MSSQL контейнеру конфигурация, только адрес неправильный. Patch: `10.0.2.2``localhost`. После — `:8091` → 200.
Это **наша ошибка** Phase 2 — пропустили patch этого файла, так как фокус был только на `MoreThenCms.Web\Web.config`.
## Phase 6 — stostayer DB-conn миграция на новый сервер (`www.stostayer.ru`)
Локальный smoke `:8090` тоже timeout — но по другой причине. `C:\sites\stostayer\Web.config` указывал на `89.253.219.2,1433` (внешний production MSSQL), оказался **полностью недоступен**: TCP timeout, ping fail, traceroute затухает на 8-м hop у `139.45.230.171`. На VM (через тот же путь) тоже не работал — пользователь подтвердил.
Пользователь предоставил новые creds: `www.stostayer.ru,1433` / `stayer_site` / `^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF`. Тест из не-elevated PS: TCP reachable, SQL login OK (SQL Server 2022). Patched Web.config.
**Pitfall:** после patch IIS отдавал HTTP 500. Причина — символ `&` в пароле, который **в XML является зарезервированным**. ASP.NET Web.config XML парсер падал на парсинг conn-string. Fix: `&``&amp;` (XML entity escape). См. [[webconfig-password-xml-escape]].
## Phase 7 — отключение traefik routes для stayer'ов
Пользователь решил: stayer'ы наружу светить не нужно вообще (даже после миграции на хост). `stostayer.yml` и `oldstostayer.yml` переименованы в `*.yml.disabled` — traefik file-provider не подхватывает, route gone. Site'ы на хосте `:8090/:8091` остаются для возможного внутреннего использования.
Backup yml для этих изменений: `*.yml.bak-stayer-switch-2026-05-19` (содержит вариант с переключением на `:8090/:8091` — на случай если решим включить обратно).
## Phase 8 — VM savestate
После того как 100% prod-трафика идёт через host-IIS, VM `snolla-recovery` заморожена через `VBoxManage controlvm "snolla-recovery" savestate`. VMState = `saved`. Конфигурация и диски сохранены — resume за 5-10 сек если что-то понадобится. Не удалена.
## Финальное состояние (2026-05-19 конец сессии)
```
C:\sites\
├── snolla\ (was MoreThenCms.Web in C:\inetpub\wwwroot, was in vm-sites\wwwroot\MoreThenCms.Web)
├── stostayer\ (was C:\stayer\MoreThenCms.Web)
├── stostayer.old\ (was C:\stayer\stostayer.old)
└── snolla-identity-manager\ (was C:\stayer\Snolla.IdentityManager — deploy artifact, без живого consumer'а)
IIS sites/pools (имя=имя=пуло):
snolla *:80 → C:\sites\snolla (pool snolla, .NET v4.0 Integrated, AppPoolIdentity)
stostayer *:8090 → C:\sites\stostayer (pool stostayer)
stostayer.old *:8091 → C:\sites\stostayer.old (pool stostayer.old)
Connection strings:
snolla: Data Source=localhost → MSSQL container на хосте (DB MoreThenCms)
stostayer: Data Source=www.stostayer.ru,1433 (user stayer_site) → внешний SQL Server 2022
stostayer.old: Data Source=localhost → MSSQL container на хосте (DB stostayer, user snolla)
Traefik active routes: 10 главных доменов (snolla.com и др.) + rimiz.ru → host:80.
Stayer routes (stostayer.snolla.com, old.stostayer.ru) DISABLED — приватны.
ртвые: disk.yml, dsm.yml (192.168.1.10).
VM snolla-recovery: VMState=saved (frozen, ~92 GB на диске + ~3 GB savestate RAM dump).
```
## Открытые вопросы (что унесли в следующую сессию)
- **`rimiz.ru` отдаёт 404** на host-IIS. CMS-routing не знает этот host — нужно копнуть БД (таблица CMS-сайтов, mapping host header → site).
- **Snolla.IdentityManager** на хосте: папка перенесена, но IIS-сайт не создавался (по решению). Если CMS-код где-то дёргает `localhost:8089` или подобное — будет тихий fail. Симптомов пока нет.
- **Phase 5 (из старого плана)**: URL Rewrite + ARR для фикса `:4443` в редиректах, Application Initialization (warm-start), log rotation `C:\inetpub\logs\`. Не блокеры.
- **VM-cleanup**: если через ~неделю стабильной работы host'а проблем не будет — `VBoxManage unregistervm "snolla-recovery" --delete` освободит ~92 GB. NAT port forwards в OpenWRT (host:18080/18180/18181/18189) можно удалить.
## Связано
[[recovery-architecture-snapshot]] обновлён под полностью-host chain. [[snolla-recovery-vm]] — VMState=saved, все sites unused. [[windows-recovery-host]] — финальный layout `C:\sites\`. [[webconfig-password-xml-escape]] — новый concept про XML entity escape в conn-string'ах. [[cms-config-rewrite-pattern]] — UTF-8 BOM подтверждён ещё много раз.
---
## Phase 9 — RЕVERT (вечер 2026-05-19, после провала)
**Что случилось:** через ~10 минут после моего commit'a "done" пользователь открыл `https://www.pilorama98.ru/``502 Bad Gateway`. Дальше остальные домены показали `TOO_MANY_REDIRECTS`.
**Root cause** (расписан полностью в [[iis-migration-2026-05-19-postmortem]]):
- Phase 3 я patched traefik backend `host.docker.internal:18080``host.docker.internal:80`.
- Внутри traefik-контейнера `host.docker.internal:80` через Docker Desktop NAT резолвится **обратно в сам traefik** на его HTTP entrypoint :80 (potential Docker publish-port loopback gotcha).
- traefik `https.yml` имеет http-catchall middleware `redirect-to-https` → traefik отвечает 301 на свой же запрос → loop.
**Реактивная цепочка ошибок** (~1 час):
- Restart-WebAppPool, nuke workers, docker restart traefik → only sделали хуже (503).
- Patched `host.docker.internal:80 → 192.168.1.143:80` → тот же loop (LAN-IP через NAT тоже возвращается в traefik).
- Patched на `:8088` + добавил IIS binding → нужен `Stop+Start Website` чтобы binding applied, изначально забыл → IIS не listen → 503.
- Думал что 301 от CMS (Pitfall 5 X-Forwarded-Proto), копал CMS код — реальный источник был самим traefik (headers без `Server: Microsoft-IIS` подсказывали).
**Revert (выполнен):**
- `VBoxManage startvm "snolla-recovery"` — VM поднята из savestate (~10s + 90s warmup + recipe `ipconfig /release /renew` для застрявшего network adapter из [[vbox-windows-stability-tuning]]).
- 11 главных yml восстановлены из `*.bak-phase3-2026-05-19` (`host.docker.internal:18080` → VM).
- Stayer yml: `stostayer.yml.disabled / oldstostayer.yml.disabled` удалены, восстановлены из `*.bak-stayer-switch-2026-05-19` (`:18180/18181` → VM).
- `docker restart traefik` (file-provider не подхватил reload автоматом).
- Public smoke через VM-chain → 7/11 хостов отвечают (2 c 200, 5 с CMS-side редиректами на canonical — нормально для CMS-логики); проверено пользователем в браузере → работает.
## Финальное состояние (после revert)
Production снова на VM-chain (как было в начале сессии 2026-05-19). На хосте осталось:
- `C:\sites\{snolla, stostayer, stostayer.old, snolla-identity-manager}` — ~11 GB, **inert** (не на prod-пути)
- IIS sites/pools `snolla, stostayer, stostayer.old` — не получают трафика (traefik backend назад на VM)
- traefik backups: `*.bak-phase3-2026-05-19`, `*.bak-stayer-switch-2026-05-19`, `*.bak-hostip-2026-05-19`
- VM `snolla-recovery` — running, IIS активен, обслуживает 11 главных доменов + 2 stayer публично через port forwards.
**Артефакты для следующей попытки** (НЕ удалять):
- Web.config'и в `C:\sites\` пропатчены правильно (sitePath, conn-strings, stostayer на новый DB) — можно переиспользовать.
- IIS sites/pools уже настроены — переключение требует только traefik backend patch + tested correctly.
- Recipe для правильной миграции — в [[iis-migration-2026-05-19-postmortem]].
---
## Phase 10 — Attempt 2 (вечер 2026-05-19, после прочтения post-mortem)
Повторная попытка миграции, выполненная **по recipe из [[iis-migration-2026-05-19-postmortem]]**. Все 5 главных правил соблюдены, миграция прошла без incidents.
### Что сделано
1. **Read-only sanity (без prod-touches):** VM running confirmed, port-scan на хосте показал `:18090` и `:8089` свободны, traefik dir contains 4 серии bak-stamp'ов сохранённых от attempt 1, IIS sites/pools (`snolla, stostayer, stostayer.old`) живы.
2. **Backend port: `:8089`** (recipe-A — НЕ `:80`, НЕ совпадает с traefik publish `:8000/:4443/:8080`, НЕ совпадает с VM NAT forwards `:18080/:18180/:18181`). Выбор user'а (был кандидат `:18090`, user предпочёл `:8089`).
3. **Atomic revert plan ДО старта** (recipe-E): новая bak-серия `.bak-pre-attempt2-2026-05-19` для всех 13 yml (11 cms + 2 stayer), paste-ready команда восстановления записана в `.tasks/STATUS.md`.
4. **IIS binding** `snolla *:8089` добавлен через elevated PS (`New-WebBinding` + `Stop-Website; Start-Website` — recipe-A note: без restart binding не активируется).
5. **Loop-detect через `docker exec`** (recipe-A + recipe-F): `docker exec traefik wget --spider -S --header="Host: emspb.ru" http://host.docker.internal:8089/` → first response `301 → http://www.emspb.ru/` от `Server: Microsoft-IIS/10.0`. `host.docker.internal` resolves to `192.168.65.254:8089` — это Docker Desktop host gateway, **не traefik publish-port**. NO NAT loop. См. [[docker-host-loopback-detect]] про общую технику.
6. **Smoke test с `-MaximumRedirection 0`** (recipe-B): `curl.exe -k -I --max-redirs 0 -H "Host: emspb.ru" https://localhost:4443/` → first response `301`, `Server: Microsoft-IIS/10.0`, `Location: http://www.emspb.ru/`. Это **expected CMS canonical redirect** (та же логика что была на VM).
7. **Canary atomic** — patched ОДИН yml (`emspb.yml`), file-provider auto-reload, public smoke clean → 📱 **phone-test с мобильного интернета** (recipe-C) → `https://emspb.ru/` открылось → ✅.
8. **Second canary**`labtools.yml`, smoke + phone-test → ✅.
9. **Batch patch** оставшихся 9 cms yml (snolla, rimiz, labtoolspro, pilorama98, tandemmebel, kupimknigi, maljarka, sestech, isc-artmaterials) одним PS-блоком (recipe-G: batch не item-by-item) → smoke 20 hostnames → все `Server: Microsoft-IIS/10.0` ✅.
10. **Stayer routes — disabled** (re-confirmed Phase 7 decision): user подтвердил «stayer'ы локальные, через traefik наружу не светят». Rename `stostayer.yml → stostayer.yml.disabled`, `oldstostayer.yml → oldstostayer.yml.disabled`. `docker restart traefik` (file-provider не подхватил deletion auto-reload). Verify: `stostayer.snolla.com`/`old.stostayer.ru``404 text/plain` (traefik no-route). Host IIS sites `:8090`/`:8091` остаются live для прямого/локального доступа.
11. **VM running parallel** (recipe-D): VM **НЕ savestate'ить** минимум 24h+. Освободит RAM/disk только после подтверждённой стабильности host'а.
### Pitfall found and resolved
- **WinHTTP proxy strips Server header.** Локальный `curl.exe http://localhost:8090/` returned response без `Server: Microsoft-IIS/10.0` и без `X-Powered-By: ASP.NET` (плюс с `Proxy-Connection: keep-alive`) — выглядело как «не IIS отвечает». Это привело к moment'у panic. Но `docker exec traefik wget ...` (real production path) **показал headers корректно**. Вывод: для loop-detect / IIS-confirmation тестов **использовать `docker exec` из traefik container**, не Windows `curl.exe` — последний ходит через Windows-уровневый proxy который headers вырезает.
### My mistake during this session
- Я неверно интерпретировал «мигрировать stayer'ов» как «patch traefik backend → host:8090/8091» (т.е. пускать через traefik наружу). User имел в виду «host IIS уже на :8090/:8091, traefik routes должны быть **DISABLED**». Сделал prod-changing patch → user интервент-stop → revert + rename `.yml.disabled`. См. memory `feedback-migrate-semantics`. Lesson — переспрашивать semantics для internal/low-traffic сервисов перед prod-changing.
### Финальный chain (после attempt 2)
```
Клиент (browser)
→ DNS → 94.19.247.14 (public IP)
→ OpenWRT NAT 443 → 192.168.1.143:4443
→ traefik:4443 (TLS termination)
→ match Host → одно из 11 cms-yml → backend
→ http://host.docker.internal:8089/
↳ Docker Desktop host gateway 192.168.65.254:8089
→ Windows host IIS site `snolla` (*:80 + *:8089 bindings)
→ C:\sites\snolla\, .NET Framework 4.8.1, ApplicationPoolIdentity
→ conn → MSSQL container на host:1433
← HTTP response
```
Stayer chain: traefik routes disabled. `stostayer.snolla.com` / `old.stostayer.ru` → traefik 404. Host IIS sites `:8090`/`:8091` живут для локального доступа.
VM `snolla-recovery`: **running parallel** (24h+ soak), backend в traefik больше не используется — VM NAT port forwards (`:18080/:18180/:18181`) холостые.
### Артефакты этой сессии
- `.tasks/STATUS.md` — статус task'а 🔴 active.
- traefik backups в `data/custom/`: `*.yml.bak-pre-attempt2-2026-05-19` для 13 yml, плюс `stostayer.yml.disabled`, `oldstostayer.yml.disabled`.
- `feedback-migrate-semantics` memory — урок для будущих сессий.
- `concepts/docker-host-loopback-detect.md` — новый concept для loop-detect technique.
## Что осталось open
- **24h+ soak** до savestate VM (минимум до утра 2026-05-20, лучше 48h до 2026-05-21).
- **VM cleanup**: после soak — `savestate` (освободит RAM), позже `unregistervm --delete` (освободит ~92 GB).
- **Port forwards OpenWRT** (`host:18080/18180/18181/18189` → VM) — больше не нужны, удалить позже.
- **rimiz.ru / ics-artmaterials.com 404** — CMS-side routing issues, не инфра. Открытым.
- **X-Forwarded headers** (`:4443` в redirect URL) — известный bug [[traefik-on-windows-docker-desktop]] Pitfall 5, отдельная задача.
## Phase 11 — close-out 2026-05-21 (~36h soak)
Soak window 2026-05-19 22:00 MSK → 2026-05-21 ~08:00 MSK ≈ 36 hours. Verify:
- **Traefik:** `Up 38 hours` (`docker ps`) — zero restarts, zero unscheduled bouncing.
- **w3wp.exe** (IIS worker для `snolla` site): PID 10432, CreationDate 2026-05-20 23:19:21 MSK, uptime 8.8h. Это **scheduled IIS app pool recycle** (default 1740 min ≈ 29h), не crash — нормальное поведение pool'а после ~29h работы. Память 522 MB.
- **HTTPS smoke** 11 заявленных hosts через `docker exec traefik wget --spider --server-response https://<host>/`:
| Host | Code | Server | Verdict |
|---|---|---|---|
| `emspb.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `snolla.com` | 301→on.snolla | Microsoft-IIS/10.0 | ✅ |
| `on.snolla.com` | 200 | Microsoft-IIS/10.0 | ✅ |
| `pilorama98.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `labtools.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `labtools.pro` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `tandemmebel.ru` | 301→www | Microsoft-IIS/10.0 | ✅ |
| `kupimknigi.spb.ru` | 200 | Microsoft-IIS/10.0 | ✅ |
| `maljarka.ru` | — | DNS bad address | ⚠️ off-infra (nslookup 8.8.8.8 → no A record) |
| `sestech.ru` | — | TLS cert `CN=*.domainparking.ru` (expired 2026-05-13) | ⚠️ off-infra (domain parking provider) |
| `ics-artmaterials.com` | 200 | `nginx-reuseport/1.21.1` | ⚠️ off-infra (A→87.236.16.28, мигрирован на сторонний WP-хостинг) |
**Итог:** 8/8 наших sites зеленые. 3 hostname'а из исходного списка task'а оказались **уже не на нашей инфраструктуре** — DNS либо снят, либо переключен на сторонние хостинги. Это не regression миграции; эти domains надо просто убрать из traefik/IIS routing'а как dead.
Decisions:
- iis-host migration close-out: **DONE 2026-05-21**.
- VM `snolla-recovery`: готова к savestate (soak passed, zero rollbacks). User-side savestate решает отдельно.
- 3 off-infra domains: новая задача — `iis-traefik-dead-routes-cleanup` (low prio).
## Артефакты Phase 11
- `.tasks/STATUS.md` — task → 🟢 done.
- (existing) traefik bak-серия `.bak-pre-attempt2-2026-05-19` — оставить ещё неделю (atomic revert на случай unforeseen regression).

View File

@@ -0,0 +1,114 @@
---
title: IIS migration to RUVDS — session 2026-05-23/24
type: source
tags: [iis, ruvds, migration, snolla, cms, ssh, scp, smb, dns, le-certs, sni]
ingested: 2026-05-24
raw_path: ../../.tasks/iis-migration-to-ruvds.md
updated: 2026-05-24
---
# IIS migration to RUVDS — 2026-05-23/24
Перенос IIS-хостинга CMS-сайтов с [[../entities/windows-recovery-host]] (DESKTOP-NSEF0UK, дом, SPOF) на [[../entities/ruvds-iis-host]] (Win Server 2025 Core, RUVDS, 80.64.31.36). Pre-req: MSSQL+MinIO уже мигрированы на [[../entities/vds-kzntsv]] (см. [[../concepts/mssql-on-vds]], [[../concepts/minio-imgproxy-on-vds]]).
Источник истины — `.tasks/iis-migration-to-ruvds.md`. Live state RUVDS-хоста — [[../entities/ruvds-iis-host]].
## Scope finalize (2026-05-24)
Только 1 IIS site `snolla` (catch-all для ~26 hostnames через CMS multi-tenant routing) — 8.66 GB. `stostayer` уже на external MSSQL (не наш scope), `stostayer.old` local-only (defer), `snolla-identity-manager` dead conn-string (defer). `tandemmebel.ru` + www — **остаются на windows-IIS** на неопределённый срок (scope-narrow 2026-05-24). Из 25 HTTPS bindings — 2 хоста уже DNS-flipped, 22 в очереди после снижения TTL.
## Хронология
### Phase 0 — vendor selection (2026-05-22)
Закрыт research-task `windows-hosting-vendor-research` 🟢 — RUVDS выбран. Сравнение: Rusonyx (Linux only для нужного формата), Selectel, FirstVDS, Beget. Critical constraints: RDP-доступ, .NET Framework 4.8 native, IIS 10, external MSSQL/MinIO connectivity (на VDS после Фазы 2).
### Phase 1 — purchase + secrets discipline (2026-05-23 09:37)
RUVDS активирован, RDP creds сохранены изначально plaintext в `.secrets/ruvds-iis.env` в repo tree → нарушение etap-2 secrets-discipline. **Ретроспективно вынесены** в `pass show ruvds-iis/full-env`, `.secrets/` + `*.env` + `*-log.txt` + `*-size.txt` добавлены в `.gitignore`.
### Phase 2 — failed-robocopy → SMB-block discovery (2026-05-23 18:08 → 22:30)
**18:08:** Попытка robocopy через UNC `\\80.64.31.36\sites\snolla\` упала exit 16. Initial diagnostics: SMB share не создан + FW 445 не открыт. Создан concept [[../concepts/windows-server-2025-core-bootstrap]] с default-blockers + transfer-методов матрицей.
**22:30 (session-recovery):** После выполнения bootstrap'а (FW+share созданы) SMB **всё равно** не reachable — TCP/445 не выходит **с home-ISP**. **Real root cause: outbound 445 блокирует ISP** (стандартная анти-worm политика residential провайдеров RU). FW scoping на RUVDS корректен.
> **Решение:** SMB recommendation в bootstrap-концепте **deprecated**. SSH/scp = canonical transfer-метод. Концепт обновлён, см. [[../concepts/windows-server-2025-core-bootstrap]] Update 2026-05-24.
### Phase 3 — SSH/scp transfer (2026-05-23 22:35 → 23:13)
OpenSSH.Server на RUVDS уже был установлен ранее (admin'ом), sshd running. Открыт FW 22 scoped к source IP, deployed ed25519 pubkey в `C:\ProgramData\ssh\administrators_authorized_keys` (правильный ACL — SYSTEM + Administrators only).
`scp -r C:\sites\snolla → C:/sites/`**~25 мин** (8.66 GB / 5 MB/s home uplink), exit 0, count+size MATCH (44725 files / 9 302 398 880 bytes).
### Phase 4 — IIS recreate (2026-05-23 23:18)
Default Web Site удалён. AppPool `snolla` (.NET v4.0, Integrated, ApplicationPoolIdentity), Website `snolla` с physicalPath `C:\sites\snolla` + catch-all binding `*:80:`. ACL — `IIS APPPOOL\snolla` + `IIS_IUSRS` Read на 46150 file entries. Local smoke (loopback + `Host: kupimknigi.spb.ru`) — 200 OK с правильным title.
### Phase 5 — HTTP middlebox surprise (2026-05-23 23:25)
External smoke через home network: все 8 hostnames вернули **`"SNOLLA | Cloud CMS" default`** (28 406 bytes), не per-tenant content. Внутри RUVDS (loopback) — корректно. SSH tunnel localhost:8888→RUVDS:80 → correct. Тест из VDS (другая сеть) → correct.
> **Conclusion: home-side outbound HTTP mutation** — DPI/transparent proxy в OpenWRT/ISP path mangles Host header. Real end-users из других сетей не пострадают. Affected — только local smoke testing.
Зафиксировано в [[../concepts/windows-server-2025-core-bootstrap]] Update 2026-05-24.
### Phase 6 — HTTPS SNI bindings (2026-05-23 23:30)
Извлечены 14 LE certs из traefik `acme.json` (`C:\Users\vitya\projects\docker\diskstation\traefik\letsencrypt\acme.json`):
```
acme.json → openssl pkcs12 -export → PFX → Import-PfxCertificate (RUVDS) →
New-WebBinding *:443:<hostname> SslFlags=1 (SNI) → AddSslCertificate by thumbprint
```
**25 HTTPS bindings live**. Cert chain valid (LE R13, до 2026-07-22). HTTP/2 auto-negotiated. Recipe вынесен в [[../concepts/traefik-acme-json-to-iis-cert-import]].
### Phase 7 — live smoke from VDS (2026-05-23 23:35)
7 prod hostnames → 200 OK + correct content; canonical bare→www 301s (CMS-side). 4 hostnames (`maljarka.tandemmebel.ru`, `rimiz.ru`/www, `rimiz.snolla.com`) → 502/404 — **CMS-internal tenant mismatch** (на source IIS:8089 они возвращают 200 default = тоже degraded pre-existing). Не блокирует cutover.
### Phase 8 — DNS partial-cutover (2026-05-24)
- **~00:00:** user flipped A `kupimknigi.spb.ru` в reg.ru. Сначала ошибочно на `89.253.255.94` (VDS Linux), потом исправлено на `80.64.31.36` (RUVDS). Authoritative `ns1.reg.ru` отдаёт правильное; public resolver-cache (8.8.8.8=6h, 1.1.1.1=24h, Yandex=2h) держат старое до TTL expiry.
- **~12:00:** `emspb.ru` + `www.emspb.ru` flipped (1.1.1.1 + Yandex + reg.ru уже отдают новое, 8.8.8.8 кешировал старое). Smoke `--resolve` + partially-cached real DNS → 200 OK + correct content.
> **TTL=86400 — слишком много.** Lower до 300s в reg.ru для всех hostnames ДО полного DNS swap'а.
### Phase 9 — scope narrow (2026-05-24)
User-decision: `tandemmebel.ru` + `www.tandemmebel.ru` ОСТАЮТСЯ на windows-IIS на неопределённый срок. Cert на RUVDS уже импортирован, HTTPS binding existing — но DNS не свапаем. Source IIS `snolla` site нельзя decommission'ить пока tandemmebel на нём же (catch-all binding). Migration делится: 24 hostnames мигрируют, 2 остаются.
## Decisions log (summary)
- **SMB → SSH/scp pivot** (22:30) — home-ISP блокирует outbound 445, deprecate SMB-метод глобально в bootstrap-концепте.
- **Tariff 2GB / 30GB** — minimum-viable для IIS+1 site catch-all; w3wp ~330 MB cold. Под load monitor.
- **Secrets canonical store** — `pass show ruvds-iis/full-env` после ретроактивной cleanup'а .gitignore.
- **CMS-side degraded** (4 hostnames 502/404) — accept, pre-existing on source, не блокер.
- **TTL не снижен заранее** — ошибка процесса, fix recommendation сделан в pre-swap list.
## Outstanding (post-soak)
- DNS TTL drop до 300s в reg.ru для 22 hostnames.
- Full DNS swap остальных 22 hostnames.
- 1-неделя soak с RUVDS как live prod.
- LE renewal pipeline (recommend: `win-acme` standalone + HTTP-01 после full cutover, ~95 days margin).
- Decommission source IIS:8089 + traefik routes (но не traefik сам).
- Cleanup: temp FW rules `ssh-from-source` + `smb-from-source` + удалить `~/.ssh/ruvds-iis-migration*` keys + `C:\ProgramData\ssh\administrators_authorized_keys`.
- Source-info commit — image-pipeline SPOF осталось (imgproxy на windows-host).
## MinIO/imgproxy caveat (carry-over)
RUVDS IIS coupled к windows-host imgproxy через DNS `imgproxy.kzntsv.site` — SPOF home machine **сохраняется** для image-rendering. HTML rendering работает с RUVDS independent. Verified post-migration: `Test-NetConnection imgproxy.kzntsv.site -Port 443` с RUVDS = OK. См. [[../concepts/iis-cutover-to-vds-services]] для long-term resolution.
## Cross-refs
- Live state: [[../entities/ruvds-iis-host]]
- Source-host (pre-migration): [[../entities/windows-recovery-host]]
- Vendor research: `.tasks/windows-hosting-vendor-research.md` (🟢 closed 2026-05-22)
- Pre-req migrations: [[../concepts/mssql-on-vds]] + [[../concepts/minio-imgproxy-on-vds]]
- Bootstrap recipe (updated 2026-05-24): [[../concepts/windows-server-2025-core-bootstrap]]
- Cert-import recipe: [[../concepts/traefik-acme-json-to-iis-cert-import]]
- Backup pipeline: [[ruvds-backup-daily-kreknin-2026-05-24]]
- Driver (SPOF rationale): [[../concepts/future-resilient-architecture-goals]]
- Pre-existing degraded routes: [[../concepts/iis-migration-2026-05-19-postmortem]] + [[iis-host-migration-2026-05-19]]

View File

@@ -0,0 +1,78 @@
---
title: NAS Recovery Session 2026-05-18/19
type: source
tags: [recovery, nas, synology, virtualbox, traefik, mssql, minio, elasticsearch]
ingested: 2026-05-19
raw_path: ../../.tasks/nas-recovery.md
updated: 2026-05-19
---
# NAS Recovery Session 2026-05-18/19
15-часовая сессия восстановления клиентских сайтов после краха NAS Synology, на котором они хостились. Источник истины — лог переписки восстановления + `.tasks/nas-recovery.md`. Здесь сжатая хронология; конкретные паттерны и решения распилены по `concepts/`, инфраструктурные сущности — по `entities/`.
## Контекст до краха
- **Source NAS (теперь мёртвый):** Synology DiskStation на XPEnology (самосборное x86 железо + DSM через community-loader). На нём:
- VMM (Virtual Machine Manager) → Windows-VM **snolla** с IIS + .NET Framework 4.8 CMS [[snolla-recovery-vm]]
- Container Manager → docker-стек: MSSQL Server 2019, MinIO 2020-07-13, Elasticsearch 7.10.1, imgproxy+nginx, traefik 2.6.6, gitea, и др.
- 11 клиентских сайтов под одной VM (snolla.com + 10 client TLDs: rimiz.ru, labtools.pro/ru, pilorama98.ru, tandemmebel.ru, emspb.ru, kupimknigi.spb.ru, maljarka.tandemmebel.ru, sestech.ru, ics-artmaterials.com)
- **Backup target NAS (живой):** [[kreknin-synology]] на 195.19.90.188 / kreknin.site, держит Hyper Backup репо.
- **Бэкап-задача:** "rsync Server 1", последняя успешная — 2026-05-09 05:06 (за 9 дней до краха).
См. также: [[dead-synology-diskstation]], [[wd40efax-smr-cascade]].
## Хронология
**2026-05-18 ~17:00 MSK:** инцидент — второй из 3 дисков RAID 5 на mёртвой синке вышел из строя. Пул past redundancy. Diagnose см. [[wd40efax-smr-cascade]].
**~17:30:** оценка вариантов. Выбран маршрут "восстановление на локальной Windows-машине пользователя" ([[windows-recovery-host]]) с гибридной архитектурой: VM для CMS (из OVA-экспорта) + docker-контейнеры для backend-сервисов.
**~18:00:** SSH-доступ к [[kreknin-synology]] (изначально по паролю, потом через ssh-ключ). Разбор структуры `.hbk` репо. См. [[hyper-backup-structure-and-recovery]].
**~19:00:** старт Hyper Backup restore через DSM UI на удалённой синке во временную папку `/volume1/restore-tmp/`. Restore инициировал также создание новых шар `backup/`, `docker/`, `work/` на root уровне `/volume1/`. Длительность ~3 часа на 277 GB selective набор.
**~21:00 — параллельно:**
- SFTP-pull `snolla.ova` (42.5 GB) на Windows — через FileZilla (SFTP сервис DSM требовалось включить отдельно, ACL/chroot нюансы).
- Локальная подготовка: установлены IIS, VirtualBox 7.2.8.
- На Windows запущен MSSQL контейнер 2019-latest пустым (для отладки compose).
- v2rayN на Windows: настроены routing rules "Bypass LAN" (`geoip:private` → direct), иначе VPN ловил inbound 80/443 traffic.
**~22:00:** найден `MoreThenCms202605090301.zip` — ежедневный `.sql` дамп БД (101 MB compressed, 547 MB unpacked). Попытка restore через `sqlcmd -i` — упала на строке 457k из-за `$(function(){...})` в данных (jQuery JS в email-template таблицах). Sqlcmd интерпретировал `$()` как переменную. См. [[mssql-restore-pitfalls]].
**~23:00:** повторный sqlcmd с флагом `-x` (disable var substitution). Параллельно pull `/docker/personal/mssql/` (25 GB) как альтернатива — через ACL-fix `chmod -R a+rX`, потом `scp -O` (legacy SCP, обход chrooted SFTP).
**2026-05-19 ночь (Claude автономно):**
- OVA import в VirtualBox завершился (42.7 мин)
- sqlcmd v2 длился ~2.5 часа, тоже падал.
- Решение: **bind-mount проблемы → named volume + `chown -R 10001:0`** через temp alpine container. Это сработало. См. [[mssql-container-data-restore]].
- Имя dead synology в БД-файлах было всё с одинаковым паролем `fXkH4@8O%3pc` (production SA password из старого `docker-compose.yml`). Аккаунт `sa` оказался **disabled**, потребовался `mssql-conf set-sa-password` под `--user 0:0` (root) → re-enable.
**~02-08:00 утра:** VM на VBox не загружалась. Кросс-гипервизорный crash. См. [[vbox-windows-stability-tuning]] — путь к стабильности через SCSI→SATA, отключение Hyper-V driver в Safe Mode, `--paravirtprovider kvm`, OS type Windows10_64, Guest Additions.
**~09:30:** VM стабилизирована. Network drama #1: bridged через WiFi нестабильно (promiscuous mode проблемы у WiFi-адаптера). Switched VM nic1 на NAT + port forwarding в VBoxManage: host:23389→VM:3389, host:8022→VM:22, host:18080→VM:80, host:18180→VM:8080, host:18181→VM:8081, host:18189→VM:8089.
**~10:00:** OpenSSH server установлен внутри VM. SSH-ключ для Claude в `C:\ProgramData\ssh\administrators_authorized_keys` (особая локация для admin-users, локализованная группа `Администраторы` через icacls). Default shell sshd переключен на PowerShell.
**~10:30:** Web.config-патч во всех 4 сайтах VM: `Data Source=192.168.1.10``Data Source=10.0.2.2` (VBox NAT gateway = host). Encoding ловушка: `Set-Content` без `-Encoding utf8` записал UTF-16 LE — IIS вернул 500.19 invalid XML. Fix: `[System.IO.File]::WriteAllText` с `UTF8Encoding($true)` (BOM). См. [[cms-config-rewrite-pattern]].
**~11:00:** Traefik 2.6.6 запущен на хосте. Полный цикл правок: `--configFile=/traefik.yml` явно (не находил автоматом), named volume для `letsencrypt/` (bind-mount показывал `0777` Linux-side, traefik требует `0600`), 13 custom yml файлов пропатчены `192.168.1.15``host.docker.internal:18080`. docker.sock провайдер не работал (Docker Desktop особенности) → minio/imgproxy/elasticsearch traefik-labels переписаны как file-provider в `data/custom/`. См. [[traefik-on-windows-docker-desktop]].
**~11:30:** OpenWRT [[openwrt-router]] на 192.168.1.1: DHCP-резервация Windows-PC на 192.168.1.143 (его MAC 88:66:5A:2F:AA:68), port forwards 80→8000 и 443→4443 (host:8000/4443 ↔ traefik). VM получила старый MAC `02:11:32:2A:7C:B9` из DHCP-резервации `snolla` — IP 192.168.1.15 сохранился для совместимости с `snolla.yml` (но потом перешли на NAT, IP стал внутренним).
**~12:00:** Public test через домен/чужой WiFi: **`https://snolla.com`, `https://pilorama98.ru`, `https://labtools.ru`, `https://labtools.pro`, `https://tandemmebel.ru`, `https://emspb.ru`, `https://kupimknigi.spb.ru`, `https://maljarka.tandemmebel.ru` отвечают `200 OK` end-to-end.** Recovery functionally complete.
## После полного recovery — резервный pull
В фоне на Windows: tar+ssh stream `C:\inetpub\wwwroot\` (8.9 GB) и `C:\stayer\` (2.27 GB) из VM в `C:\nas-recovery\vm-sites\` — как фолбэк если VM снова станет нестабильной (был один случай glitch network — лечился `ipconfig /release /renew` через `VBoxManage guestcontrol`).
## Открытые вопросы / нюансы
- **X-Forwarded-Proto/Host headers** между traefik и CMS не настроены → CMS делает redirect с `:4443` в URL.
- **MinIO / Azure storage** в CMS: connection string использует Azure SDK (AccountName=snolla, AccountKey=...), но в production реально работало с MinIO. Точная схема "не так, как казалось" по словам пользователя — ждёт пояснения.
- **acme.json renewal через HTTP-01** фейлится для доменов с DNS не на нашем IP. Решение — DNS-01 через REGRU (creds в `traefik/docker-compose.yml` env уже, в `traefik.yml` закомментировано).
- **VM long-term stability**: один случай network glitch уже был. Возможна планка scheduled task внутри VM — auto release/renew при детекции downtime.
## Архитектурное замечание
Сохранение работающей конфигурации не равно отказоустойчивости. Текущая инфраструктура [[recovery-architecture-snapshot]] сильнее, чем была (Hyper Backup проверен, восстановление практикой), но **single-point-of-failure всё ещё есть**: один Windows-PC, одна VM, один публичный IP, один роутер. Глобальная задача [[future-resilient-architecture-goals]] — на потом.

View File

@@ -0,0 +1,46 @@
---
title: NL VDS 3x-UI — setup & troubleshooting session 2026-06-05
type: source
tags: [vless, reality, mtproto, hiddify, v2rayn, rkn, troubleshooting, session]
ingested: 2026-06-05
raw_path: (live session, no raw file)
sources: []
updated: 2026-06-05
---
# NL VDS 3x-UI — сессия настройки/диагностики 2026-06-05
Хроника заведения и отладки прокси-VDS [`nl-vds-3xui`](../entities/nl-vds-3xui.md).
## Что сделано
1. **Заведение в вики + ротация кредов.** Узел задокументирован, креды (SSH root, panel, JWT secret) ротированы и положены в `pass nl-vds-3xui/full-env` (оригиналы светились в плейнтексте).
2. **Reality 443 диагностика.** Не коннектился. Root cause: 3x-UI включил ML-DSA-65 (PQ) + dest `www.intel.com` (Akamai, не умеет PQ key-exchange) → handshake не достраивался. Изоляционная матрица на replica-парах. Разбор: [`reality-pq-mldsa65-dest-incompatibility`](../concepts/reality-pq-mldsa65-dest-incompatibility.md).
3. **Fix Reality (server-side):** dest→`www.microsoft.com`, затем снят `mldsa65Seed` (GUI-клиенты не шлют `mldsa65Verify`), затем порт 443→2053.
4. **MTProto:** поднят mtg 2.2.8 (FakeTLS cloudflare) на 8443.
5. **SOCKS5:** инбаунд на 47020 (auth).
6. **Инструкции для друзей** (Hiddify, Win/Mac/Android).
## Чем закончилось (честный итог)
**У реальных клиентов из РФ работает ТОЛЬКО plain VLESS 32030.** Reality (2053), MTProto (8443), SOCKS5 (47020) — **не поднялись** на боевых клиентах:
- **Reality 2053:** в изолированных тестах (отдельный `xray.exe` + `curl -x socks5h` с того же ПК) — проходил, exit NL, 5 МБ/с. Боевой v2rayN на том же ПК и телефон — **нет**. Причина не установлена. Версионная/PQ-несовместимость исключена (PQ снят, flow на месте, конфиг сверен — идентичен рабочему тесту). Вероятная гипотеза (НЕ доказана) — обработка TLS-хендшейка к этому IP в сети пользователя.
- **MTProto 8443:** Telegram (Desktop и телефон) висит «соединение…». tcpdump (телефон изолирован, Desktop закрыт): телефон шлёт 1288-б ClientHello → mtg отвечает 0 байт, телефон долбится десятками повторов. Локальный FakeTLS (`openssl` на сервере) проходит — но это лишь камуфляж-путь, реальную MTProto-сессию mtg здесь не держит. «Доступен (пинг N мс)» в Telegram — пассивная проверка, не равна рабочей сессии.
- **SOCKS5 47020:** на сервере исправен (внешний curl с auth проходил), но у пользователя режется DPI.
**Грубые ошибки в процессе (см. урок ниже):**
- Несколько раз заявлял «работает» по своим коротким curl/standalone-тестам, тогда как боевые клиенты не работали. Тесты были нерепрезентативны (часто шли не через тот путь, что у пользователя; либо через системный прокси v2rayN; либо это был мой отдельный процесс, не v2rayN).
- Влепил **MSS-clamp 1360** на сервер по неверной MTU-гипотезе (пользователь сразу сказал, что дело не в MTU) — clamp **ломал** соединения; после снятия комп-телега через системный прокси заработала.
## Рабочее решение для друзей (раздаётся)
Клиент **Hiddify** (Win/Mac/Android, один на все платформы) либо v2rayN/v2rayNG. Импорт по `vless://`-ссылке или QR, профиль 32030. При включённом VPN — в Telegram прокси не настраивать (моб. «отключить прокси», desktop «системный прокси», кастомные удалить).
Каждому другу — **свой** клиентский UUID (на момент сессии на 32030 один общий `a6fa7965-…`; рекомендовано завести отдельных клиентов под каждого для возможности отзыва — не сделано, ждёт решения user).
## Открытые хвосты
- **Reality/MTProto у реальных клиентов из РФ не работают** — причина не доведена до конца. Если нужен DPI-стойкий канал (не plain VLESS) — отдельная задача (возможно: другой IP/провайдер, CDN-fronting, или клиент с TUN вместо встроенного Telegram-прокси).
- Per-friend UUID на 32030 — завести отдельных клиентов.
- Опционально: убрать неработающие инбаунды (2053 Reality / 47020 SOCKS / 8443 mtg), чтобы не торчали лишние порты.

View File

@@ -0,0 +1,42 @@
---
title: OmniRoute web-комбо — хронология сессии 2026-08-22
type: source
tags: [omniroute, deepseek-web, session-trace]
ingested: 2026-08-22
raw_path: ../raw/omniroute-session-2026-08-22.md
updated: 2026-08-22
---
# OmniRoute web-комбо — хронология сессии 2026-08-22
Сессия настройки omniroute-комбо `deepseek-v4-flash-web` / `deepseek-v4-pro-web` (провайдер deepseek-web). Полный вывод в диалоге pi 2026-08-22; здесь — ключевые точки.
## Контекст
- pi использует omniroute (localhost:20128) как LLM-провайдера, модель `deepseek-v4-flash-web`.
- Веб-чат DeepSeek (chat.deepseek.com) не имеет нативного function calling — omniroute эмулирует через `<tool>`-контракт.
- Симптомы: модель «не видит скилы», агент «вылетает» на вызове тулов, `Upstream response failed quality validation: streaming upstream error`, `503 chat_admission_busy`.
## Ход
1. **MCP-тулы omniroute не работают** из pi-сессии (`Unknown Mcp-Session-Id`) — перешли на REST `http://127.0.0.1:20128/api/*`.
2. **Собрали картину**: 2 комбо, round-robin, 3 аккаунта deepseek-web (megan/yulia/vitya), qwen-web (test=error, DNS), deepseek API + tokenrouter (test=active, но API-доступ «за скобками»).
3. **Метрики** показали дисбаланс: pro-web 81% успешности, yulia 64%/40s, vitya 50%/42s (last=error), megan 90100%.
4. **Найден корень №1**: `system_message` комбо переопределяет системный промпт pi → скилы вырезаются. Убран (`""`).
5. **Найден корень №2**: `tool_filter_regex: ^(bash|computer)$` режет 337 из 339 тулов. Расширен до базовых + `mcp__[a-z0-9_]+`.
6. **Найден корень №3**: search-модель (`think-search`) генерит DEEP_SEARCH-мусор и «Link reading unavailable» при URL. Модель в flash-комбо заменена на `deepseek-v4-flash-think`.
7. **Тесты эмуляции** (1 тул, 25 тулов, stream=true/false) — всё возвращает корректные tool_calls. Вывод: эмуляция исправна; «read("...") текстом» = reasoning_content.
8. **503 chat_admission_busy** — capacity веб-чата DeepSeek. maxRetries 2→4, retryDelayMs 1500→4000.
9. **Идея**: кастомный провайдер на базе вытащенного кода omniroute (executor + translator), без quality validation, persistSession, несколько web-провайдеров отдельными комбо. Обсуждено, не реализовано.
## Итоговое состояние комбо
Оба (`deepseek-v4-flash-web`, `deepseek-v4-pro-web`):
- `system_message`: `""`
- `tool_filter_regex`: `^(bash|read|write|edit|grep|glob|list|describe_image|mcp__[a-z0-9_]+)$`
- `config.maxRetries`: 4, `config.retryDelayMs`: 4000
- flash: модель `deepseek-v4-flash-think` (search убран); pro: `deepseek-v4-pro` (без search и так)
## Ссылки
- [[omniroute-combo-deepseek-web-tuning]] — сводный концепт

View File

@@ -0,0 +1,81 @@
---
title: RUVDS daily backup → kreknin — pipeline standup 2026-05-24
type: source
tags: [backup, ruvds, kreknin, rclone, sftp, ntfy, schedtask]
ingested: 2026-05-24
raw_path: ../../.tasks/ruvds-backup-daily-kreknin.md
updated: 2026-05-24
---
# RUVDS daily backup → kreknin
Поднят ежедневный backup pipeline с [[../entities/ruvds-iis-host]] (`80.64.31.36`, Win Server 2025 Core) → [[../entities/kreknin-synology]] (`195.19.90.188 / kreknin.site`) в **04:30 MSK** (за 30 мин до VDS backup'а в 05:00, чтобы не пересекать uplink). После каждого прохода — ntfy push на общий topic `vds-backup` (одно phone-channel для всех backup events). Параллель [[vds-kzntsv-bootstrap-2026-05-20]] §backup, но source = Windows Core → cron нет (Task Scheduler), rsync нет (rclone), root-cron нет (SYSTEM scheduled task).
Источник истины — `.tasks/ruvds-backup-daily-kreknin.md`.
## Scope
| Path on RUVDS | Reason | Size |
|---|---|---|
| `C:\sites\snolla\` | CMS site content + Web.config + media | 8.66 GB |
| `C:\Windows\System32\inetsrv\config\applicationHost.config` | IIS site/binding/apppool config | <1 MB |
| `Backup-WebConfiguration` snapshot → `%SystemRoot%\System32\inetsrv\backup\` | IIS native config snapshot | <5 MB |
| `C:\ProgramData\ssh\` | sshd_config + authorized_keys | <50 KB |
| Exported PFX из `Cert:\LocalMachine\My` | LE certs для restore без re-extract из traefik | <1 MB |
Не бэкапятся: `C:\Windows\`, `C:\Program Files\`, IIS logs.
## Decisions log
- **Tool = rclone (SFTP)**. Не robocopy-over-SMB (SMB через home-ISP блокируется, см. [[iis-migration-to-ruvds-2026-05-23]] Phase 2), не rsync (нет cygwin/WSL на Core, лишний security surface), не restic (encrypted dedupe overkill пока link trusted; defer like VDS).
- **Run as SYSTEM**, не user account — full доступ к `C:\sites\` + `inetsrv\` + cert store, не требует stored-password в Task Scheduler. Pattern matches VDS root-cron decision.
- **PFX export pass = `pfximport` plaintext в run.ps1** — temporary, migrate to pass-equivalent на Windows (DPAPI или gpg4win+bash-pass) когда migration cutover'нется полностью.
- **Retention 7 daily snapshots, без hardlink dedup** — 8.66 GB × 7 = ~60 GB на kreknin (5.7T free). Per-day dirs `/volume1/NetBackup/ruvds-iis/YYYY-MM-DD/`.
- **ntfy topic `vds-backup`** — reuse общего канала, Title включает source-host чтобы phone-side отличать VDS-backup от RUVDS-backup.
## Хронология standup'а (2026-05-24)
- **~10:18:** rclone v1.74.2 installed на RUVDS (`C:\Program Files\rclone\rclone.exe`).
- **~10:18:** ed25519 SSH key `C:\ProgramData\backup\kreknin-key` сгенерирован, pubkey deployed в kreknin `/var/services/homes/vitya/.ssh/authorized_keys` **через VDS pivot** (source machine не имеет direct SSH к kreknin).
- **~10:19:** Deployed `rclone.conf` (SFTP remote `kreknin`, `disable_hashcheck=true`), `config.env` (ntfy creds), `run.ps1`. ACL = SYSTEM + Administrators.
- **10:19:47:** Зарегистрирован ScheduledTask `RUVDS-Backup-Daily` (daily 04:30 MSK, SYSTEM, 2h timeout).
- **10:21:13 — 11:42:57:** Initial full sync 8.66 GB snolla + 4 small components → kreknin. **~82 мин** из-за home-uplink throttling (production cron не affected — RUVDS uplink direct).
- **11:46:51:** Run #1 incremental smoke ✓ **20 sec** — все 5 components OK, retention prune OK (1 snapshot kept, ничего пока удалять), ntfy success sent.
- Scripts checked-in: `scripts/ruvds-backup-daily-kreknin/` (setup.ps1 + run.ps1 + README).
## Bugs fixed during smoke
- `Backup-WebConfiguration -Force` параметра нет на этом IIS — используем `Get-WebConfigurationBackup + Remove-WebConfigurationBackup` если exists + plain `Backup-WebConfiguration`.
- rclone `--log-file` указывал на тот же путь что и `Start-Transcript` → file lock. Убран `--log-file`, transcript captures всё.
- rclone NOTICE на stderr + `$ErrorActionPreference='Stop'` = PS terminating error даже c `2>$null`. Wrapper `Invoke-Rclone` temporarily switches к `ErrorActionPreference='Continue'`.
- ssh-keyscan'енный `known_hosts` не проходит rclone go-sftp library (key mismatch). Убран `known_hosts_file` из rclone.conf, key-auth достаточно.
## Pipeline state
- **Live**: ScheduledTask `RUVDS-Backup-Daily`, daily 04:30 MSK, SYSTEM.
- **Verified**: run #1 end-to-end (20 sec incremental, ntfy push received).
- **Retention day-8 verify**: pending — retention prune не triggered'илась (только 1 snapshot < 7).
- **Phone-side ntfy verify**: pending — user должен подтвердить что push с title `RUVDS backup OK` приходит на ntfy app (common channel с VDS backup).
## Open follow-ups (не блокер)
- PFX export passphrase в plaintext — migrate to DPAPI/pass-equivalent.
- Backup `C:\sites\snolla` size growth (CMS uploads через админку) — quarterly disk-usage check на kreknin.
## Атомарный revert
```powershell
# На RUVDS:
Unregister-ScheduledTask -TaskName 'RUVDS-Backup-Daily' -Confirm:$false
Remove-Item C:\ProgramData\backup -Recurse -Force
# На kreknin (через SSH):
ssh vitya@195.19.90.188 'rm -rf /volume1/NetBackup/ruvds-iis'
```
## Cross-refs
- Source host: [[../entities/ruvds-iis-host]]
- Backup target: [[../entities/kreknin-synology]]
- Driver: open question "Backup strategy для RUVDS" из [[iis-migration-to-ruvds-2026-05-23]] — closed by этой задачей.
- Parallel pipeline: VDS Ubuntu → kreknin rsync `--link-dest` (см. [[vds-kzntsv-bootstrap-2026-05-20]] §backup).
- Notification: общий ntfy topic `vds-backup` на `ntfy.vds.kzntsv.site` (см. [[../entities/vds-kzntsv]] §стек).

View File

@@ -0,0 +1,121 @@
---
title: VDS kzntsv bootstrap session 2026-05-20
type: source
tags: [vds, bootstrap, rusonyx, traefik, portainer, gitea, verdaccio, registry, migration, kreknin]
ingested: 2026-05-20
raw_path: ../../.tasks/vds-kzntsv-bootstrap.md
updated: 2026-05-20
---
# VDS bootstrap session — 2026-05-20
Одна сессия, ~6 часов: активация Rusonyx VDS → 3-фазная подготовка инфра-стека → миграция трёх сервисов (gitea, verdaccio, registry) с восстановленного backup'а на [`kreknin-synology`](../entities/kreknin-synology.md). Решает первый пункт из [`future-resilient-architecture-goals`](../concepts/future-resilient-architecture-goals.md) — вынос инфра-сервисов с single-point-of-failure хоста. Production CMS остаётся на [`windows-recovery-host`](../entities/windows-recovery-host.md).
Источник истины — `.tasks/vds-kzntsv-bootstrap.md`. Текущий live-state VDS — [`vds-kzntsv`](../entities/vds-kzntsv.md).
## Контекст до сессии
После аварии 2026-05-18 ([`dead-synology-diskstation`](../entities/dead-synology-diskstation.md) развалилась), CMS поднята на [`windows-recovery-host`](../entities/windows-recovery-host.md). Но инфра-сервисы (gitea, verdaccio, docker-registry, owncloud, hermes), которые тоже жили на мёртвой синке, остались только как restored backup на [`kreknin-synology`](../entities/kreknin-synology.md). User не хочет держать их на windows-recovery-host (тот уже перегружен CMS-стеком + это рабочая машина).
Решение — отдельный облачный VDS у Rusonyx. Заказан 2026-05-19. Активирован 2026-05-20 — IP `89.253.255.94`, тариф 160 NVMe (6 vCPU / 8 GB / 160 GB / Ubuntu 24.04).
## Хронология
### Фаза 0 — pre-flight (DNS + кreds)
User проставил DNS records в REGRU **до** активации сервера: `vds.kzntsv.site`, `*.vds.kzntsv.site`, `git.kzntsv.site`, `verdaccio.kzntsv.site`, `registry.kzntsv.site` → 89.253.255.94. DNS-cut решение: сервисы на kreknin остаются как есть, реальный switch произойдёт после standup'а на VDS (DNS уже там).
Initial креды от Rusonyx: root + одноразовый пароль. Сохранены в `~/projects/.common/secrets/vds-kzntsv.env`.
### Rusonyx onboarding pain
VNC консоль изначально не открывалась. Помогла кнопка «Остановить VNC» в Управление сервером → Консоль (force-disconnect stale attachment). После этого VNC заработал.
`apt update && apt upgrade && apt --fix-broken install && apt upgrade` пришлось гонять через VNC (Rusonyx сами рекомендуют: их virt бьёт SSH session, openssh-server restart рвёт connection). По дороге — 5+ dpkg interactive prompts: sshd_config conffile (chose 2 = keep local), cloud.cfg (chose N = keep), grub-pc target disk (1 = /dev/vda whole-disk MBR). Reboot после.
Все эти подробности — в [`rusonyx-vps-onboarding-quirks`](../concepts/rusonyx-vps-onboarding-quirks.md).
### Phase 1 — bootstrap (через SSH с root + pubkey push)
1. Pubkey push через **plink** (PuTTY) с inline `-pw` (OpenSSH for Windows не поддерживает password в флаге). После push — ssh-key-only login.
2. Sudo user `vitya:Pryakhin10~` + group sudo + NOPASSWD грант (для автоматизации).
3. sshd harden через **drop-in `/etc/ssh/sshd_config.d/00-hardening.conf`** (`00-` prefix чтобы выиграть first-match precedence над `50-cloud-init.conf` который форсит `PasswordAuthentication yes`). `PermitRootLogin no` + `PasswordAuthentication no`.
4. ufw default-deny + allow 22/80/443 + DB-порты (5432/3306/27017/6379).
5. fail2ban (default sshd jail).
6. Docker CE 29.5.1 official APT repo + buildx + compose-plugin. vitya в группу docker.
7. Hostname `vds-kzntsv`, timezone Europe/Moscow.
8. Docker networks `proxy` + `shared-dbs` (external).
### Phase 1 (cont) — Traefik v2.11 LTS + Portainer 2.21.5
- Traefik static config: 6 entrypoints (web/websecure/postgres/mariadb/mongo/redis), HTTP-01 LE challenge (DNS уже указан, проходит за 5 sec), dashboard на `traefik.vds.kzntsv.site` + basicAuth middleware из dynamic file provider.
- LE issued cert at first request (5 validators с разных AWS regions → 200 OK).
**Portainer admin init — 4 итерации.** Hard min 12-char policy в Portainer 2.20+ (regression от 2.20.0+), CLI flag `--admin-password` + bcrypt **не bypass'ит** policy и фактически выходит broken (bcrypt сохраняется, но login fails). После проб с `$2y$`/`$2a$` prefix, single-quote escape, YAML list form, docker run direct — оказалось CLI flag тупо не работает в 2.21.5. Финал: голый `docker run`, без `--admin-password`, потом API admin/init с длинным паролем `Pryakhin9-VDS-2026` (18 chars). Деталь — [`portainer-2.21-admin-password-regression`](../concepts/portainer-2.21-admin-password-regression.md).
API key сгенерирован через `/api/users/<id>/tokens`, local docker endpoint создан через `POST /api/endpoints` form-data. Сохранён в `vds-kzntsv.env`.
### Phase 2 — Shared DB park с TLS через traefik
User explicitly: «Я хочу доступ снаружи к базам! Через трафик» — поэтому DBs должны быть accessible from public internet с TLS.
**Решение архитектуры — это main lesson:**
- Initial attempt: traefik TCP routers с `HostSNI('<db>.vds.kzntsv.site')` + `tls.passthrough=true`**работает для Mongo/Redis** (TLS-from-start), **не работает для Postgres/MariaDB** (STARTTLS-protocols, нет SNI в первых байтах).
- Switch на `HostSNI(*)` без `tls.*` (raw TCP forward) — работает для всех 4 DBs, traefik просто маршрутизирует по entrypoint port'у. DBs терминируют TLS сами.
Подробно в [`traefik-tcp-passthrough-vs-starttls`](../concepts/traefik-tcp-passthrough-vs-starttls.md) и [`db-tls-self-signed-via-traefik-raw-tcp`](../concepts/db-tls-self-signed-via-traefik-raw-tcp.md).
Self-signed certs у каждой DB (CN matches `<db>.vds.kzntsv.site`), strong random hex32 passwords. Postgres alpine использует uid 70 — пришлось перейти на не-alpine `postgres:16` (uid 999, matches наш chown). Mongo 7 требует `--tlsCAFile` (chain of trust enforcement) — добавили self-signed как CA. Все 4 DBs верифицированы через openssl s_client + real protocol probe (pg_dumpall connect, mongosh ping, redis-cli PING, mariadb SELECT VERSION()).
### Phase 3.1 — Gitea (миграция от Hyper Backup restored data)
Источник = restored backup на kreknin, **не запущенный контейнер**. Это была ошибочная гипотеза в начале сессии — потеряли время и обиду user'а («ты почему ни хера не читаешь вики?»). Урок зафиксирован в feedback memory `feedback_read_wiki_first.md`.
Pipeline:
1. tar+ssh stream `/volume1/docker/gitea/{data,postgres,docker-compose.yml}` с kreknin → vds (8m19s, ~2.7G total, ~5.4 Mbps). Sudo на kreknin требовал password (`Pryakhin9`) — passed через `echo Pryakhin9 | sudo -S tar c ...` inside ssh quotes.
2. На VDS — `docker run -d --rm postgres:9.6` mounted on restored datadir (postgres uid 999, datadir chown'd, chmod 700). Recovery после non-clean shutdown (last May 9 — последний Hyper Backup snapshot).
3. `pg_dump -U gitea gitea` → /tmp/gitea.sql (23 MB, 22400 lines).
4. Shared postgres 16: `CREATE ROLE gitea LOGIN PASSWORD 'gitea'; CREATE DATABASE gitea OWNER gitea ...;` через `docker exec postgres psql -U postgres -c "..."` (peer auth on Unix socket).
5. Restore `psql -U gitea -d gitea < gitea.sql`. Gitea автоматически мигрирует schema 9.6 → 16.
6. Stop temp pg9.6.
7. **Восстановленная data была вложена на уровень глубже** (`/data/data/git/...` вместо `/data/git/...`) — flatten layout. Gitea на первом старте перезаписал свежий app.ini (env-var driven), нужно было использовать оригинальный app.ini из вложенного `data/gitea/conf/app.ini` который содержал `INSTALL_LOCK=true`, `LFS_JWT_SECRET=...`, `SECRET_KEY=...` оригинала.
8. Patch app.ini под VDS: DOMAIN, SSH_DOMAIN, ROOT_URL → git.kzntsv.site; HOST → postgres:5432; SSH_PORT → 2222.
9. Gitea compose без `GITEA__database__*` env vars (пусть app.ini рулит).
10. Smoke: HTTP 200, `/api/v1/version``{"version":"1.25.5"}`, `/api/v1/repos/search` → 3 first repos (OpeItcLoc03/claude-skills и др.). Final: 132 repos, 4 users.
### Phase 3.2 — Verdaccio (file rsync)
1. rsync `storage/` + `config/` + `plugins/` от kreknin → /opt/stacks/verdaccio/ (~9G transferred, 17m36s, ~8.17 MB/s). Sudo на kreknin не нужен — vitya owns эти файлы.
2. Chown `10001:65533` (verdaccio uid). **Не делать `chown -R` на parent /opt/stacks/verdaccio** — съест permission на root dir, vitya не сможет писать compose. Только sub-dirs.
3. Compose с image `verdaccio/verdaccio:6` + traefik labels → `verdaccio.kzntsv.site`.
4. **Crashloop** — Node 22 в verdaccio:6 требует secret **точно** 32 chars в `/verdaccio/storage/.verdaccio-db.json` (не `.verdaccio-db` как старая версия). Restored secret был 64 chars (старая verdaccio накопила). Fix: `openssl rand -hex 16` = 32 chars, overwrite secret в JSON.
5. User reported: UI пусто. Причина — kreknin'овский config.yaml имеет `access: $authenticated` для всех packages, и анонимный посетитель не видит ничего. Нужно логиниться (`vitya` user в restored htpasswd). User'у объяснил, он залогинился — packages появились.
### Phase 3.3 — Registry (GC + fresh install)
1. Registry GC on kreknin локально (mount restored data, run `registry:2.8.3 garbage-collect`). **Mount должен быть PARENT dir, не sub-dir** — registry ожидает `/var/lib/registry/docker/registry/v2/...`, а не `/var/lib/registry/registry/v2/...`. С правильным mount + `-m` (modify=delete) flag — 99G → **35G** (64G freed).
2. Запущен rsync 35G с kreknin → /opt/stacks/registry/. ETA ~50 min при 2 MB/s.
3. **Mid-flight user reconsiders**: «может зря тащим старые образы? Могу новых наделать». Decision: kill rsync, fresh install. User accepts loss of old images.
4. Fresh registry с htpasswd (vitya/Pryakhin9), `REGISTRY_STORAGE_DELETE_ENABLED=true`, CORS headers для UI.
5. Joxit Registry UI (joxit/docker-registry-ui) на `registry-ui.vds.kzntsv.site` с `DELETE_IMAGES=true` для manual cleanup через GUI.
Подробно про GC: [`registry-gc-mount-and-modify-flag`](../concepts/registry-gc-mount-and-modify-flag.md).
## Decisions log
- **Tariff 160 NVMe Rusonyx** (a не 80 SSD + addon) — operational simplicity overweights ₽1000/мес savings (decision 2026-05-19).
- **Ubuntu 24.04 LTS** vs Debian 12 — LTS support до 2029, docker official APT primary target Ubuntu (decision 2026-05-19).
- **Traefik v2.11 LTS** vs v3 — user explicit choice (совместимость с windows-recovery-host где v2.6.6).
- **DB access from outside через traefik** — user explicit reversal от docker-network-only Q1 answer.
- **DB TLS = self-signed** для starts, LE-cert sidecar deferred — operational simplicity.
- **MongoDB include immediately** — user explicit.
- **Hermes defer** — user explicit.
- **Registry — fresh install, no migration** — user mid-flight reversal (могу новых наделать).
- **`HostSNI(*)` + no tls.* для всех DBs** — uniform config работает для всех protocols (STARTTLS + TLS-from-start).
- **Portainer admin pass `Pryakhin9-VDS-2026` (18 chars)** вместо запрошенного `Pryakhin9` (9 chars) — Portainer 2.21+ hard min 12-char policy, `--admin-password` CLI flag broken в 2.20+. Сохранено в `vds-kzntsv.env`.
## Архитектурное замечание
VDS kzntsv = первый шаг по closing SPOF gap из [`future-resilient-architecture-goals`](../concepts/future-resilient-architecture-goals.md). Раньше всё крутилось на одной физической коробке ([`dead-synology-diskstation`](../entities/dead-synology-diskstation.md) — упала; [`windows-recovery-host`](../entities/windows-recovery-host.md) — тоже SPOF). Теперь инфра-сервисы (git/npm/registry/dbs) живут на отдельном cloud-host'е. Это **не** полное multi-host resilience: VDS сам по себе тоже single host. Но decouples infra и production CMS, что критично.
Backup pipeline VDS → kreknin (см. follow-up task [`vds-backup-rsync-kreknin`](../../.tasks/vds-backup-rsync-kreknin.md)) — следующий шаг.

View File

@@ -0,0 +1,126 @@
---
title: vds-kzntsv outage incident — session chronology 2026-05-28
type: source
tags: [vds, rusonyx, network, dhcp, outage, session-trace]
ingested: 2026-05-28
raw_path: (none — session-live, no external raw)
updated: 2026-05-28
---
# vds-kzntsv incident chronology 2026-05-28
Live-session трасса диагностики и восстановления отказа [[../entities/vds-kzntsv]]. Полный разбор паттерна + recovery runbook — в [[../concepts/vds-kzntsv-dhcp-outage-2026-05-28]].
## Timeline (MSK)
| Время | Событие | Источник |
|---|---|---|
| 2026-05-28 05:25 | Daily backup pipeline `vds-backup-rsync-kreknin` стартует | ntfy-уведомление пользователю |
| 2026-05-28 05:45 | Backup завершён успешно — 19m51s, 69G, 7 snapshots передано на kreknin | ntfy + backup log |
| 2026-05-28 ~06:0008:00 | (окно отказа DHCP — точное время неизвестно) | — |
| 2026-05-28 ~08:15 | User обнаруживает что vds-kzntsv недоступен по сети, пинг падает на хостер-gateway | user message |
| 2026-05-28 08:20 | Сессия с агентом начинается, запрос креды на VDS | conversation |
| 2026-05-28 08:25 | Probe с workstation и с [[../entities/books-vds]] (same hoster) — оба `Destination Host Unreachable` на `89.253.192.40` | bash output |
| 2026-05-28 08:30 | User открывает Rusonyx panel → VNC console, шлёт скриншот журнала | screenshot |
| 2026-05-28 08:3508:42 | Диагностика через VNC: `ip -br link` → eth0 UP без IPv4; `networkctl status` → degraded (configuring), LLDP видит `hw80.rusonyx.ru` | screenshots |
| 2026-05-28 08:43 | Recovery через статику (3 команды + DNS) — IP получен, маршруты добавлены | user confirms "Заработало!" |
| 2026-05-28 08:44 | SSH с workstation работает; uptime 1h (VDS был ребутнут ~07:44, но IP DHCP всё равно не пришёл) | `ssh vitya@89.253.255.94 hostname` |
| 2026-05-28 08:44 | 18 docker контейнеров поднялись через restart policy — gitea, registry, verdaccio, postgres, mariadb, mongo, owncloud, oCIS, modulair-rag стэк и пр. | `docker ps` |
| 2026-05-28 08:50 | Disk cleanup: docker builder prune (10.6G) + image prune (0.3G) + truncate container logs (~3G); disk 89% → 79% | `df -h /` before/after |
| 2026-05-28 09:00 | Тикет в Rusonyx отправлен | user sends |
| 2026-05-28 ~12:20 | Rusonyx отвечает: «возможно потребуется перезагрузка», запрашивают разрешение | helpdesk |
| 2026-05-28 ~12:25 | Даём разрешение с условиями (5-10мин уведомление + готовность VNC к recovery) | helpdesk |
| 2026-05-28 ~13:00 | Rusonyx: «настройте дефолт — `mask netplan + systemd-networkd`, `enable networking`» | helpdesk |
| 2026-05-28 ~13:20 | После уточнений согласовали что они сами пропишут конфиг через свой `start/ipadd` | helpdesk |
| 2026-05-28 ~13:30 | Выполнен `systemctl disable+mask netplan + systemd-networkd.{service,socket,wait-online}` через SSH. IP и SSH остались живые. | session bash |
| 2026-05-28 ~13:52 | Rusonyx ребутает VM с reset-конфига через их provisioning | их сторона |
| 2026-05-28 14:00 | Smoke: SSH + 24 контейнера up + `api.ipify.org → 89.253.255.94`. Сервер полностью восстановлен. | session bash |
## Что сломалось
- DHCP lease для VM `vps534388` (MAC `52:54:00:9c:63:01`) на гипервизоре `hw80.rusonyx.ru` не возобновлялся.
- VM выпала из L3-сети полностью: своя сторона корректна (link UP, networkd шлёт DHCP discover), хостер не отвечает offer'ом.
- Reboot VM не помог.
Подробные подтверждающие сигналы — в [[../concepts/vds-kzntsv-dhcp-outage-2026-05-28]] § Симптомокартина.
## Что починили
Статика на eth0 через 3 ip-команды:
```bash
sudo ip addr add 89.253.255.94/24 dev eth0
sudo ip route add 89.253.192.1 dev eth0
sudo ip route add default via 89.253.192.1
```
DNS: `nameserver 89.253.252.30/.31` (Rusonyx).
**Эфемерно.** Persistence в netplan **отложен** до ответа хостера — если они починят DHCP, persistent статика конфликтнёт.
## Gateway discovery — как нашли 89.253.192.1
Gateway у Rusonyx сидит в /18 надсети `89.253.192.0/18`, а не в нашей /24. Источник — `ip route` на [[../entities/books-vds]] (sibling VPS у того же хостера):
```
$ ssh root@89.253.255.133 'ip route'
default via 89.253.192.1 dev eth0 metric 400
89.253.192.0/18 via 89.253.192.1 dev eth0 src 89.253.255.133 metric 400
89.253.192.1 dev eth0 scope link metric 400
89.253.255.0/24 dev eth0 proto kernel scope link src 89.253.255.133
```
См. также quirk #9 в [[../concepts/rusonyx-vps-onboarding-quirks]].
## Финальная конфигурация (после resolution хостером 13:52 MSK)
`/etc/network/interfaces.d/ifcfg-eth0` (положен их `start/ipadd` procedure):
```
auto eth0
allow-hotplug eth0
iface eth0 inet static
address 89.253.255.94
netmask 255.255.192.0
post-up ip ro add 169.254.0.0/16 dev eth0 metric 400
post-up ip ro add 89.253.192.1 dev eth0 metric 400
post-up ip ro add default via 89.253.192.1 dev eth0 metric 400
post-up ip ro add 89.253.192.0/18 via 89.253.192.1 dev eth0 src 89.253.255.94 metric 400
post-up ip ro del 89.253.192.0/18 dev eth0 proto kernel scope link src 89.253.255.94
```
Изменение по сравнению с тем что было до инцидента:
- **Сетевой стек** — был mixed (netplan + networkd поверх ifupdown), стал чистый ifupdown
- **Netmask** — стал /18 явно (`255.255.192.0`) вместо неявного /24 + link-route trick
- **systemd unit state:** `netplan` masked, `systemd-networkd*` masked, `networking` enabled
Kernel cmdline без изменений: `net.ifnames=0 biosdevname=0` (Rusonyx force eth0 naming).
## Тикет в Rusonyx — итоговый текст
Тикет отправлен через myvm.rusonyx.ru helpdesk, account `21075162`, тема: «VPS 534388 (vps-21075162-534388) — нет DHCP lease ~05:45 28.05.2026 MSK».
Основная часть:
- Подтверждённое окно простоя ~2.5ч (с 05:45 — последний успешный backup — до 08:30 recovery).
- Отсутствие уведомления с их стороны.
- Корневая причина на стороне хостера, с конкретикой (LLDP, networkd journal, отсутствие DHCPv4 offer'ов).
- Диагностика клиентом, не support'ом — что не норма для production-уровня услуги.
- Запрос: (a) технический RCA, (b) SLA-компенсация, (c) procedural followup по их мониторингу VM-уровня, (d) статус DHCP — починен или фиксируем статику постоянно.
Полный текст — в сессионной переписке `~/.claude/projects/C--Users-vitya-projects--admin/` (conversation log).
Ответ хостера ещё не получен (на момент ingest этого source).
## Что обнаружили попутно
1. **Диск 89% → 79% после GC** — освободили 14G (build cache 10.6G + dangling images 0.3G + container logs ~3G). Disk-GC pending давно, см. `vds-kzntsv` § Open issues. Поставлена task [[../../.tasks/vds-kzntsv-disk-gc-followup]] на полный registry-GC (20G возможный reclaim).
2. **`board-viewer-build` unhealthy** — отдельная история, не блокер сейчас.
3. **`ping` не установлен** на VDS — Ubuntu 24.04 base не включает iputils-ping. Использовали curl для проверки сети. Можно установить `apt install iputils-ping` при следующем maintenance.
## Cross-refs
- [[../concepts/vds-kzntsv-dhcp-outage-2026-05-28]] — runbook + RCA-pattern.
- [[../entities/vds-kzntsv]] — host details.
- [[../entities/books-vds]] — sibling, source для gw discovery.
- [[../concepts/rusonyx-vps-onboarding-quirks]] — vendor quirks, дополнен quirk #9.
- [[vds-kzntsv-bootstrap-2026-05-20]] — оригинальный bootstrap.

View File

@@ -0,0 +1,28 @@
---
title: VDSina Outline + 3x-UI — inventory session
type: source
tags: [vds, vdsina, outline, 3x-ui, netherlands, inventory]
date: 2026-06-24
---
# VDSina (46.151.25.64) — inventory 2026-06-24
Пользователь сообщил о ранее недокументированном VDS: «46.151.25.64 — мой публичный api, он же основной VPN; `v1621967.hosted-by-vdsina.ru`; root/Pryakhin10~; там Outline и 3xui — сохрани секреты и собери информацию». Сессия: достал host-key, инвентаризировал по SSH, сохранил в `pass vdsina-outline/full-env`, завёл [[vdsina-outline-3xui]].
## Метод
- Host-key через `ssh-keyscan -t ed25519``SHA256:yRFesrmfNhsIeTSQ0ZWM3MWMjWxO0m3JaZsdbmy6Nx4`.
- SSH через `plink -batch -hostkey ... -pw 'Pryakhin10~' -m <script>` (инлайн с кавычками PowerShell корёжит).
- `sqlite3` на сервере отсутствует → x-ui.db читал через `python3` (модуль sqlite3).
## Что нашли (сырьё)
- **OS:** Ubuntu 20.04.6 LTS (kernel 5.4.0-216), Hyper-V VM, 1 vCPU «Common KVM», 965 MiB RAM, swap 138 MiB, disk 30 G (26% used). Up 17 дней. Provider-панель показывала «Ubuntu 22» — расхождение, реально 20.04.
- **Outline:** docker `shadowbox:stable` + `watchtower` (оба Up 2 недели). `serverId cfb9ef54-…`, created `1710964189814` = **2024-03-20**. data-port 443 (chacha20-ietf-poly1305), mgmt API :8080, путь `/Kv5DD_ZHm9X3VC_InFY2ww`, cert `2CB0FB47…F919D`. **11 access-keys** с именами (vitya/natasha/mi box/router/NFS/alexey/планшет/s/tescha…).
- **3x-UI:** systemd `x-ui` (since 2026-06-06), Xray 25.10.15. Panel webPort 50806, webBasePath `/ecCqrtFVl4zl5kFjdj/`, secret `JQJt1b…`, user `kIqmxpsisp`, пароль — **только bcrypt-хеш** (plaintext недоступен). sub :2096 `/sub/`.
- **Xray inbounds:** id2 VLESS/xhttp :43666 (6 клиентов), id4 VLESS/grpc :46743 (1 клиент), id3 mixed :1420 (local), id1 VLESS :33980 disabled. Все `security=none`.
- **Listen:** 22, 443, 8080, 9090/9091 (prometheus/node, localhost), 9092 (outline-ss localhost), 50806, 2096, 1420, 43666, 46743. ufw inactive.
## Resolved
Пароль 3x-UI панели изначально не извлекался (bcrypt). Пользователь получил его через `x-ui` меню на сервере и передал: `kIqmxpsisp` / `6usTSHFddG`, access URL `http://46.151.25.64:50806/ecCqrtFVl4zl5kFjdj`. Обновлён в `pass vdsina-outline/full-env`. (Строка `Start migrating database...` в выводе — старый мусор, не свежий рестарт.)