Compare commits
3 Commits
f957830033
...
68706d0bee
| Author | SHA1 | Date | |
|---|---|---|---|
| 68706d0bee | |||
| 74cf219533 | |||
| c46e569908 |
@@ -1,5 +1,5 @@
|
|||||||
# Admin Task Board
|
# Admin Task Board
|
||||||
_Updated: 2026-05-21 (admin-infra-project-review closed 🟢 — no new findings; F1-F3 remain documented in [verify-migration] close-note)_
|
_Updated: 2026-05-22 (owncloud-vds-deploy 🟢 — 25 044 объектов / 26 GB live, все 6 large-file холдауты залиты VDS-side curl loopback'ом; 60s upload-timeout finding закреплён в [`ocis-on-vds-deploy-recipe`](../.wiki/concepts/ocis-on-vds-deploy-recipe.md) §Gotcha 5)_
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Status legend:
|
Status legend:
|
||||||
@@ -10,6 +10,23 @@ _Updated: 2026-05-21 (admin-infra-project-review closed 🟢 — no new findings
|
|||||||
🔵 Blocked — waiting on external input
|
🔵 Blocked — waiting on external input
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 🟢 [owncloud-vds-deploy] — oCIS 7.1.0 на vds-kzntsv live, 25 044 объектов / 26 GB
|
||||||
|
|
||||||
|
**Closed:** 2026-05-22 — import complete. 99.97% (22.2 GB / 25 038 objs) залито rclone'ом за ~2.5 ч (3.35 MiB/s uplink). Оставшиеся 6 файлов (4.04 GB) — 4× .pat ~221 MB + 2× .seospider 1.55+1.7 GB — упали с 502 (later 500) на каждой попытке rclone из-за 60-секундного timeout где-то в HTTP stack (Go http.Client.Timeout в reva v2.27 datagateway, **не** traefik). Workaround: throwaway sftp-only ssh-key → агент scp'ит в `/tmp/oc-import/` на VDS → c VDS curl PUT loopback'ом через traefik в правильные oCIS пути (local network 142 MB/s — все 6 файлов залиты за 28 секунд total). Все PROPFIND size match. Throwaway-key revoked, staging cleaned.
|
||||||
|
|
||||||
|
**Finding закреплён** в [`ocis-on-vds-deploy-recipe`](../.wiki/concepts/ocis-on-vds-deploy-recipe.md) §Gotcha 5 (60s timeout cap × upload speed = ~200 MB max single PUT на 3 MiB/s uplink; recipe для VDS-side workaround). Traefik buffering middleware **не починил** — сам выдаёт 500 на 60s mark (likely bug в `vulcand/oxy` buffer); подходит для других кейсов с медленными backend'ами, но не для этого 60s cascade.
|
||||||
|
|
||||||
|
**Detail:** [owncloud-vds-deploy.md](owncloud-vds-deploy.md). Backup integration: `/opt/stacks/owncloud` в rsync sources [[vds-backup-rsync-kreknin]] (next nightly snapshot 2026-05-22 05:00 MSK захватит full 26 GB).
|
||||||
|
|
||||||
|
**Open follow-ups:**
|
||||||
|
- 2× DipTrace `.exe` (~2 MB total) — заблокированы Windows Defender на агентовой машине, не баг oCIS. User-side issue, не блокер.
|
||||||
|
- Portainer API key (vds-kzntsv/full-env `PORTAINER_API_KEY`) — 401 Unauthorized, требует regen через Portainer UI + pass-store update. Не блокировало deploy (admin user/pass работали). Отдельная chore-task если нужна.
|
||||||
|
- oCIS image upgrade на 7.2.x / 8.x потенциально подъёмлет 60s timeout (newer reva имеет configurable timeout). Defer пока 4.04 GB workaround сработал.
|
||||||
|
|
||||||
|
**Branch:** master
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🟢 [admin-infra-project-pointers] — Bootstrap-pointers для design admin-infra-project. Pre-fills target's `.wiki/CLAUDE.md` Domain conventions ссылками на спецификацию. Дизайн не лежит в этом репо — только pointer-stub. Без этой таски следующий агент попадёт в дыру: where_stopped one-liner + пустой Domain conventions stub = угадывание порогов / pipeline-этапов вместо чтения готовых решений.
|
## 🟢 [admin-infra-project-pointers] — Bootstrap-pointers для design admin-infra-project. Pre-fills target's `.wiki/CLAUDE.md` Domain conventions ссылками на спецификацию. Дизайн не лежит в этом репо — только pointer-stub. Без этой таски следующий агент попадёт в дыру: where_stopped one-liner + пустой Domain conventions stub = угадывание порогов / pipeline-этапов вместо чтения готовых решений.
|
||||||
|
|
||||||
**Кто делает:** любой следующий агент в этом проекте. Это первая по приоритету таска промоушена — все остальные импл-таски ссылаются на pointers через .wiki/CLAUDE.md.
|
**Кто делает:** любой следующий агент в этом проекте. Это первая по приоритету таска промоушена — все остальные импл-таски ссылаются на pointers через .wiki/CLAUDE.md.
|
||||||
|
|||||||
136
.tasks/owncloud-vds-deploy.md
Normal file
136
.tasks/owncloud-vds-deploy.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# owncloud-vds-deploy
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Поднять [[owncloud-infinite-scale]] (oCIS 7.1.0) на vds-kzntsv в качестве замены мёртвого ownCloud сервера на Synology NAS (см. [[nas-recovery]]). После восстановления — импортировать 24.7 GB из off-VDS backup-копии. Сервис на `owncloud.kzntsv.site`, files-only (calendar/contacts не нужны).
|
||||||
|
|
||||||
|
## Decisions log
|
||||||
|
|
||||||
|
- **2026-05-21 — oCIS vs Seafile vs Nextcloud:** oCIS выбран. Reasons: (1) drop-in миграция — decomposedfs storage по сути plain-files на диске (24.7 GB просто кладутся); (2) lightweight — single Go binary, не PHP-monstr classic ownCloud 10; (3) workflow continuity — user уже знаком с ownCloud UX; (4) тривиальный backup (tar `data/` + конфиг); (5) future-proof — oCIS = main line от ownCloud GmbH, classic OC10 deprecating. Seafile отвергнут: block-storage = нет drop-in import, медленнее migration. Nextcloud отвергнут: только files нужны — calendar/contacts overhead не оправдан.
|
||||||
|
|
||||||
|
- **2026-05-21 — `user: "1001:1001"` override:** image owncloud/ocis runs as UID 1000 (ocis-user inside container) by default, но host vitya = UID 1001. Mount-target dirs (./data, ./config) owned by vitya → container не мог писать. Fix: добавил `user: "1001:1001"` в compose — container runs as host UID, owns mounted dirs. Альтернатива (`chown 1000` хост-dirs) отвергнута — создаёт orphan UID на хосте.
|
||||||
|
|
||||||
|
- **2026-05-21 — `PROXY_ENABLE_BASIC_AUTH=true`:** дефолтно oCIS basic auth выключен (OIDC-only). Для WebDAV-клиентов (rclone, ownCloud desktop) и LibreGraph API admin-операций basic auth нужен → enabled. Trade-off: чуть слабее security (basic auth не имеет MFA/refresh tokens), но threat model personal use acceptable.
|
||||||
|
|
||||||
|
- **2026-05-21 — `PROXY_TLS=false`:** docs прямо разрешают, когда TLS термируется на reverse proxy (traefik certresolver letsEncrypt). Альтернатива — собственный self-signed cert внутри контейнера + traefik passthrough — overhead без выгоды.
|
||||||
|
|
||||||
|
- **2026-05-21 — admin/user-vitya passwords в pass-store:** `owncloud/admin-password` (32 chars random) + `owncloud/user-vitya` (32 chars random). Encrypted-at-rest, cross-machine sync через `OpeItcLoc03/password-store-private`. Закрывает риски [[secrets-manager-adopt]].
|
||||||
|
|
||||||
|
- **2026-05-21 — DNS surprise:** ожидали, что user будет создавать A-запись `owncloud.kzntsv.site → 89.253.255.94` вручную. Reality: запись уже была (видимо ранее создана при wildcard cleanup / dns prep), сразу резолвилась на VDS_IP. Wildcard `*.kzntsv.site → 94.19.247.14` (NAS recovery host) overlay-ится specific записями.
|
||||||
|
|
||||||
|
- **2026-05-21 — backup integration:** добавил `/opt/stacks/owncloud` в rsync source list `/opt/stacks/backup/scripts/run.sh` ([[vds-backup-rsync-kreknin]]). После import 24.7GB первый full backup snapshot будет +24.7GB на kreknin, далее ~hardlink incremental. kreknin 5.6 TB free — comfortably fits.
|
||||||
|
|
||||||
|
## Implementation sketch
|
||||||
|
|
||||||
|
`/opt/stacks/owncloud/docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
ocis:
|
||||||
|
image: owncloud/ocis:7.1.0
|
||||||
|
container_name: owncloud
|
||||||
|
restart: unless-stopped
|
||||||
|
user: "1001:1001"
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command: ["-c", "ocis init || true; exec ocis server"]
|
||||||
|
environment:
|
||||||
|
OCIS_URL: https://owncloud.kzntsv.site
|
||||||
|
OCIS_LOG_LEVEL: info
|
||||||
|
OCIS_LOG_COLOR: "false"
|
||||||
|
PROXY_TLS: "false"
|
||||||
|
PROXY_ENABLE_BASIC_AUTH: "true"
|
||||||
|
OCIS_INSECURE: "false"
|
||||||
|
IDM_CREATE_DEMO_USERS: "false"
|
||||||
|
IDM_ADMIN_PASSWORD: ${OCIS_ADMIN_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/ocis
|
||||||
|
- ./config:/etc/ocis
|
||||||
|
networks: [proxy]
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- "traefik.http.routers.owncloud.rule=Host(`owncloud.kzntsv.site`)"
|
||||||
|
- traefik.http.routers.owncloud.entrypoints=websecure
|
||||||
|
- traefik.http.routers.owncloud.tls.certresolver=letsEncrypt
|
||||||
|
- traefik.http.services.owncloud.loadbalancer.server.port=9200
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy: { external: true }
|
||||||
|
```
|
||||||
|
|
||||||
|
`/opt/stacks/owncloud/.env` (chmod 600):
|
||||||
|
```
|
||||||
|
OCIS_ADMIN_PASSWORD=<32-char-random — стор в pass owncloud/admin-password>
|
||||||
|
```
|
||||||
|
|
||||||
|
User creation via LibreGraph API (`POST /graph/v1.0/users`, basic auth as admin).
|
||||||
|
|
||||||
|
## Completed steps
|
||||||
|
|
||||||
|
- [x] **Compose** написан, image `owncloud/ocis:7.1.0` pulled (digest `sha256:c75af880...b717b`)
|
||||||
|
- [x] **Dirs:** `/opt/stacks/owncloud/{data,config}` (vitya:vitya 775)
|
||||||
|
- [x] **Secrets:** `OCIS_ADMIN_PASSWORD` в `.env` (chmod 600), originals в pass-store: `owncloud/admin-password` + `owncloud/user-vitya` (pushed to OpeItcLoc03/password-store-private)
|
||||||
|
- [x] **Deploy:** `docker compose up -d` через ssh (limited stack — Portainer auto-detects)
|
||||||
|
- [x] **UID fix:** `user: "1001:1001"` после первого "permission denied" на `/etc/ocis/ocis.yaml`
|
||||||
|
- [x] **Basic auth fix:** `PROXY_ENABLE_BASIC_AUTH=true` после 401 на LibreGraph API
|
||||||
|
- [x] **Traefik integration:** labels подхвачены, cert via letsEncrypt valid, HTTPS 200 OK
|
||||||
|
- [x] **User `vitya` created** через LibreGraph API (`POST /graph/v1.0/users`, id `58110e99-5e92-43f8-a077-9f2ec7f5562e`)
|
||||||
|
- [x] **WebDAV verified:** `PROPFIND /dav/files/vitya/` returns 200 multistatus с personal space "Vitya" + system space "Shares"
|
||||||
|
- [x] **Backup integration:** `/opt/stacks/owncloud` добавлен в rsync source list `/opt/stacks/backup/scripts/run.sh` (между `/opt/stacks/backup` и `/etc/ssh`). `bash -n` syntax OK. Next nightly run 05:00 MSK захватит config + (post-import) data.
|
||||||
|
|
||||||
|
## Closed 2026-05-22 — import complete
|
||||||
|
|
||||||
|
25 044 объектов / **26 GB** на vitya space. Path: 22.2 GB rclone (3.35 MiB/s uplink, ~2.5h) + 4.04 GB VDS-side curl loopback (6 файлов с 502/500 from 60s timeout — см. §Closed-out finding ниже).
|
||||||
|
|
||||||
|
Все PROPFIND size match. Backup integration: `/opt/stacks/owncloud` в `vds-backup-rsync-kreknin` rsync sources — next snapshot 2026-05-22 05:00 MSK захватит 26 GB.
|
||||||
|
|
||||||
|
### Closed-out finding: 60s HTTP upload timeout
|
||||||
|
|
||||||
|
При rclone uplink ~3.35 MiB/s упали все файлы ≥221 MB (4× .pat 220-222 MB, 508 MB zip, 1.55 + 1.7 GB seospider). Logs traefik показывали ровно `60000ms` duration на 502. oCIS error log: `Put "http://localhost:9158/data/simple/...": context canceled`, `time_ns:59944479090` (59.94s) — internal HTTP client между oCIS services вырубает request на 60s. Hardcoded в Go `http.Client.Timeout` где-то в reva v2.27 datagateway.go:200. Env-var fix отсутствует.
|
||||||
|
|
||||||
|
**Traefik buffering middleware попробован, не помог:** `traefik.http.middlewares.oc-buffering.buffering.maxRequestBodyBytes=0` + `memRequestBodyBytes=1048576` через docker labels. Traefik сам стал выдавать 500 на 60s mark (backend_url="-" в access log = до oCIS не доходило). Likely bug в `vulcand/oxy` buffer на больших телах. Откатил.
|
||||||
|
|
||||||
|
**Workaround:** VDS-side direct curl PUT loopback'ом через traefik. Пользовательский агент:
|
||||||
|
1. Throwaway ed25519 key сгенерирован на VDS, append'нут как `restrict,command="internal-sftp"` в `~/.ssh/authorized_keys` (sftp-only, no shell).
|
||||||
|
2. Агент `sftp -i <key> -b <batch> vitya@VDS` залил 6 файлов в `/tmp/oc-import/` (~20 мин на агентовом uplink).
|
||||||
|
3. С VDS — `for SRC ...; curl -T $SRC <ocis-url-with-urlencode>` через localhost → traefik → owncloud. Local network 142 MB/s — все 4.04 GB за 28 секунд (1.7 GB файл за 10s).
|
||||||
|
4. Verify PROPFIND `<oc:size>` match `stat -c %s`. All 6 OK.
|
||||||
|
5. Cleanup: `rm -rf /tmp/oc-import/`, throwaway-key revoked из authorized_keys.
|
||||||
|
|
||||||
|
Pattern закреплён в [`.wiki/concepts/ocis-on-vds-deploy-recipe.md`](../.wiki/concepts/ocis-on-vds-deploy-recipe.md) §Gotcha 5.
|
||||||
|
|
||||||
|
## Skipped / not-our-bug
|
||||||
|
|
||||||
|
- 2× DipTrace `.exe` (~2 MB total) — заблокированы Windows Defender на агентовой машине при copy в staging. Не баг oCIS. Не блокер.
|
||||||
|
- Portainer API key invalid (401) — не блокировало deploy (admin user/pass работали), требует regen через UI + pass-store update если кто будет использовать API.
|
||||||
|
|
||||||
|
## Open (legacy section) — DEPRECATED, см. closed-out выше
|
||||||
|
|
||||||
|
- [x] ~~Import 24.7 GB~~ — done 2026-05-22 (rclone + VDS-side curl)
|
||||||
|
```
|
||||||
|
rclone copy "<src>" :webdav: \
|
||||||
|
--webdav-url https://owncloud.kzntsv.site/dav/files/vitya \
|
||||||
|
--webdav-vendor owncloud \
|
||||||
|
--webdav-user vitya \
|
||||||
|
--webdav-pass $(rclone obscure "$(pass show owncloud/user-vitya)") \
|
||||||
|
--progress --transfers 4 --checkers 4 --tpslimit 8 \
|
||||||
|
--retries 5 --low-level-retries 10 --stats 30s
|
||||||
|
```
|
||||||
|
Resumable. Прогресс мониторится с VDS: `ssh vitya@VDS 'du -sh /opt/stacks/owncloud/data/spaces/'`.
|
||||||
|
|
||||||
|
- [ ] **Post-import verify:** size match (≈24.7 GB на data/) + spot-check файлов через web UI.
|
||||||
|
|
||||||
|
- [ ] **Pin image version** — сейчас `7.1.0` явно (good). Consider tracking 7.x patch releases — но не auto-update на 8.x без manual verify (breaking config schema changes возможны).
|
||||||
|
|
||||||
|
- [ ] **API-key rotate?** Portainer api-key в `vds-kzntsv/full-env` оказался invalid (401 "Invalid JWT token"). Не блокирует — admin user/pass работают. Follow-up: regenerate API key через Portainer UI + update pass-store. Отдельная chore-task если нужно.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Architecture:** request flow → DNS owncloud.kzntsv.site → 89.253.255.94 → ufw 80/443 → traefik websecure → owncloud (172.18.0.14:9200) → decomposedfs storage в `/var/lib/ocis` mount → host `/opt/stacks/owncloud/data/`.
|
||||||
|
- **Storage layout:** oCIS использует decomposedfs — каждый файл = node с metadata. Spaces (personal + Shares) живут в `data/spaces/<space-uuid>/`. Backup-friendly: plain files на диске, не block storage.
|
||||||
|
- **Atomic revert:**
|
||||||
|
```
|
||||||
|
ssh vitya@89.253.255.94 'cd /opt/stacks/owncloud && docker compose down -v && cd .. && rm -rf owncloud'
|
||||||
|
# + remove /opt/stacks/owncloud line из run.sh
|
||||||
|
# + pass rm owncloud/admin-password owncloud/user-vitya && pass git push
|
||||||
|
```
|
||||||
|
- **No external DB** — oCIS использует internal KV-store (`data/storage-users/`, `data/idm/`) для metadata. Postgres/MariaDB на VDS не задействованы. Это снимает зависимость от shared-dbs network — owncloud только в `proxy`.
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
- [x] ~~**Что бэкапить:**~~ Resolved 2026-05-20. Sources в `/opt/stacks/backup/scripts/run.sh`:
|
- [x] ~~**Что бэкапить:**~~ Resolved 2026-05-20. Sources в `/opt/stacks/backup/scripts/run.sh`:
|
||||||
- `/opt/stacks/{gitea,verdaccio/storage,verdaccio/config,registry,traefik,portainer,ntfy,backup}`
|
- `/opt/stacks/{gitea,verdaccio/storage,verdaccio/config,registry,traefik,portainer,ntfy,backup}`
|
||||||
|
- `/opt/stacks/owncloud` (added 2026-05-21 для [[owncloud-vds-deploy]] — oCIS data + config + .env + compose; post-import будет ~25GB, hardlink-incremental после первой ночи)
|
||||||
- `/etc/{ssh,ufw,hosts,docker}`
|
- `/etc/{ssh,ufw,hosts,docker}`
|
||||||
- `/home/vitya/.ssh`
|
- `/home/vitya/.ssh`
|
||||||
- DB dumps в `$DUMP_DIR` (pg/maria/mongo/redis) — добавляются в rsync source list.
|
- DB dumps в `$DUMP_DIR` (pg/maria/mongo/redis) — добавляются в rsync source list.
|
||||||
|
|||||||
247
.wiki/concepts/ocis-on-vds-deploy-recipe.md
Normal file
247
.wiki/concepts/ocis-on-vds-deploy-recipe.md
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
---
|
||||||
|
title: oCIS на VDS — deploy recipe + non-obvious gotchas
|
||||||
|
type: concept
|
||||||
|
tags: [recipe, owncloud, ocis, docker, traefik, webdav, libregraph, decomposedfs]
|
||||||
|
sources: [../../.tasks/owncloud-vds-deploy.md]
|
||||||
|
updated: 2026-05-21
|
||||||
|
---
|
||||||
|
|
||||||
|
# oCIS на VDS — deploy recipe + non-obvious gotchas
|
||||||
|
|
||||||
|
Recipe для разворачивания **ownCloud Infinite Scale** (oCIS) — Go-based замены classic ownCloud Server 10 — в Docker за traefik с LetsEncrypt. Применимо на [[vds-kzntsv]] и других хостах с тем же pattern'ом (`/opt/stacks/<name>/`, traefik network `proxy`, LE certresolver `letsEncrypt`).
|
||||||
|
|
||||||
|
Validated 2026-05-21 на image `owncloud/ocis:7.1.0`. Container запустился, traefik routes 200 OK, LibreGraph API + WebDAV работают, init создаёт admin + idm.json в `data/idm/`.
|
||||||
|
|
||||||
|
## Когда применять
|
||||||
|
|
||||||
|
- Личный personal-cloud для одного пользователя (single binary, no external DB)
|
||||||
|
- Replace мёртвого / legacy ownCloud Server 10 (oCIS = main line от ownCloud GmbH, OC10 deprecating)
|
||||||
|
- Files-only use-case (calendar/contacts не нужны — для тех нужен Nextcloud)
|
||||||
|
- На хосте с уже работающим traefik + LE certresolver (recipe рассчитан на termination upstream)
|
||||||
|
|
||||||
|
## Когда **не** применять
|
||||||
|
|
||||||
|
- Multi-tenant корпоративная инсталляция → нужны external IDP, OIDC chain, не личный setup
|
||||||
|
- Нужны calendar / contacts / mail / collabora → Nextcloud, не oCIS
|
||||||
|
- Block-level dedup критичен → Seafile (oCIS использует file-level decomposedfs)
|
||||||
|
- Host UID = 1000 (image uid 1000 matches → `user:` override **не** нужен)
|
||||||
|
|
||||||
|
## Compose recipe
|
||||||
|
|
||||||
|
`/opt/stacks/owncloud/docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
ocis:
|
||||||
|
image: owncloud/ocis:7.1.0
|
||||||
|
container_name: owncloud
|
||||||
|
restart: unless-stopped
|
||||||
|
user: "1001:1001" # ← gotcha 1, см. ниже
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command: ["-c", "ocis init || true; exec ocis server"]
|
||||||
|
environment:
|
||||||
|
OCIS_URL: https://owncloud.kzntsv.site
|
||||||
|
OCIS_LOG_LEVEL: info
|
||||||
|
OCIS_LOG_COLOR: "false"
|
||||||
|
PROXY_TLS: "false" # ← gotcha 2
|
||||||
|
PROXY_ENABLE_BASIC_AUTH: "true" # ← gotcha 3
|
||||||
|
OCIS_INSECURE: "false"
|
||||||
|
IDM_CREATE_DEMO_USERS: "false"
|
||||||
|
IDM_ADMIN_PASSWORD: ${OCIS_ADMIN_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/ocis
|
||||||
|
- ./config:/etc/ocis
|
||||||
|
networks: [proxy]
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- "traefik.http.routers.owncloud.rule=Host(`owncloud.kzntsv.site`)"
|
||||||
|
- traefik.http.routers.owncloud.entrypoints=websecure
|
||||||
|
- traefik.http.routers.owncloud.tls.certresolver=letsEncrypt
|
||||||
|
- traefik.http.services.owncloud.loadbalancer.server.port=9200
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy: { external: true }
|
||||||
|
```
|
||||||
|
|
||||||
|
`/opt/stacks/owncloud/.env` (chmod 600):
|
||||||
|
```
|
||||||
|
OCIS_ADMIN_PASSWORD=<32+ random chars, store в pass owncloud/admin-password>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gotcha 1: UID mismatch image vs host
|
||||||
|
|
||||||
|
Image `owncloud/ocis` runs as **UID 1000** (`ocis-user` внутри контейнера). Если host-side user-owner mounted dirs ≠ 1000 — контейнер падает в restart-loop с:
|
||||||
|
|
||||||
|
```
|
||||||
|
Could not create config: open /etc/ocis/ocis.yaml: permission denied
|
||||||
|
The jwt_secret has not been set properly in your config for ocis.
|
||||||
|
```
|
||||||
|
|
||||||
|
«jwt_secret has not been set» — **обманка**, симптом сидит на ровном месте perm-deny при попытке записать ocis.yaml на первом `ocis init` пробеге.
|
||||||
|
|
||||||
|
**Fix:** `user: "<host-uid>:<host-gid>"` override в compose. На vds-kzntsv host user `vitya` = UID 1001 → `user: "1001:1001"`. Альтернатива (`chown 1000` host-dirs) **отвергнута**: создаёт orphan UID на хосте, путает auditing + backup tools.
|
||||||
|
|
||||||
|
**Diagnose UID:** `stat -c "%u:%g %U:%G" /opt/stacks/owncloud/config`.
|
||||||
|
|
||||||
|
## Gotcha 2: `PROXY_TLS=false` за reverse proxy
|
||||||
|
|
||||||
|
Дефолт oCIS — `PROXY_TLS=true` (HTTPS на proxy port 9200 self-signed). За traefik, который сам терминирует HTTPS, это даёт двойную TLS-обёртку — traefik пытается сделать backend TLS, не получается.
|
||||||
|
|
||||||
|
Per ownCloud docs: «If a reverse proxy is used to terminate HTTPS, `PROXY_TLS` can be set to false, though this means communication between the proxy and Infinite Scale will be unencrypted.» На том же хосте, в bridge network — acceptable.
|
||||||
|
|
||||||
|
## Gotcha 3: `PROXY_ENABLE_BASIC_AUTH=true` для WebDAV + admin API
|
||||||
|
|
||||||
|
Дефолтно oCIS — OIDC-only (browser-based login). Для **rclone / ownCloud-desktop / curl LibreGraph API** нужен basic auth — иначе 401 везде, включая `GET /graph/v1.0/me`.
|
||||||
|
|
||||||
|
**Trade-off:** basic auth не имеет MFA/refresh-tokens. Для personal use acceptable. Для shared deployment — обернуть LDAP / OIDC отдельно.
|
||||||
|
|
||||||
|
## Gotcha 4: User-create via LibreGraph API (нет CLI)
|
||||||
|
|
||||||
|
oCIS **не** имеет `ocis idm user add` или подобной CLI команды. Создание users — только через:
|
||||||
|
|
||||||
|
1. Web UI как admin → Settings → Users
|
||||||
|
2. LibreGraph API: `POST /graph/v1.0/users` с basic auth admin'а
|
||||||
|
|
||||||
|
API recipe:
|
||||||
|
```bash
|
||||||
|
curl -u "admin:$ADMIN_PWD" -X POST \
|
||||||
|
"https://owncloud.kzntsv.site/graph/v1.0/users" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"displayName": "Vitya",
|
||||||
|
"onPremisesSamAccountName": "vitya",
|
||||||
|
"mail": "vitya@kzntsv.site",
|
||||||
|
"passwordProfile": {"password": "<random-32>"},
|
||||||
|
"accountEnabled": true
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
→ HTTP 201 с user JSON (с `id` UUID).
|
||||||
|
|
||||||
|
List users: `GET /graph/v1.0/users`.
|
||||||
|
|
||||||
|
## Storage layout (decomposedfs)
|
||||||
|
|
||||||
|
После `ocis init` data dir выглядит так:
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/stacks/owncloud/data/
|
||||||
|
├── idm/ — internal IDM (LDAP-style) state
|
||||||
|
├── idp/ — OIDC IdP private keys (private-key.pem etc)
|
||||||
|
├── nats/ — internal pub-sub messaging
|
||||||
|
├── search/ — indexer state (bleve)
|
||||||
|
└── storage/
|
||||||
|
├── metadata/spaces/<2>/<rest>/ — space metadata
|
||||||
|
└── users/spaces/<2>/<rest>/ — actual user files (decomposedfs nodes)
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый user space = `<2-char-prefix>/<26-char-rest-of-uuid>/` (split UUID для FS spread). Files внутри хранятся как **plain bytes** + sidecar metadata (xattrs / .meta.json). Backup-friendly: tar+rsync захватит всё корректно, восстановление = просто разархивировать обратно.
|
||||||
|
|
||||||
|
Internal KV-store (`storage/users/spaces/.../metadata/`, `idm/idm.json`, etc.) — **JSON files**, не БД. Это и есть «no external DB needed» — oCIS использует filesystem + NATS вместо Postgres/Redis.
|
||||||
|
|
||||||
|
## Backup integration
|
||||||
|
|
||||||
|
`/opt/stacks/owncloud` (whole dir) добавлен в rsync source list [[vds-backup-rsync-kreknin]]:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
/opt/stacks/backup \
|
||||||
|
+ /opt/stacks/owncloud \
|
||||||
|
/etc/ssh \
|
||||||
|
```
|
||||||
|
|
||||||
|
Live container во время rsync — acceptable trade-off (decomposedfs использует atomic rename, отдельные node-files не corrupt'ятся). Если требуется strictly consistent snapshot — `docker stop owncloud` → rsync → `docker start` (downtime ~30s).
|
||||||
|
|
||||||
|
Первый full backup snapshot после import → +~25 GB на kreknin. Далее hardlink-incremental ≈ 1 GB/day.
|
||||||
|
|
||||||
|
## Connection chain
|
||||||
|
|
||||||
|
```
|
||||||
|
DNS owncloud.kzntsv.site → 89.253.255.94 (VDS_IP)
|
||||||
|
→ ufw 443
|
||||||
|
→ traefik websecure (LE cert via letsEncrypt resolver)
|
||||||
|
→ docker network proxy (172.18.0.0/16)
|
||||||
|
→ container owncloud:9200 (oCIS proxy service)
|
||||||
|
→ internal services: idp / idm / users / groups / proxy / storage-system / sharing / search ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Atomic revert
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh vitya@89.253.255.94 'cd /opt/stacks/owncloud && docker compose down -v && cd .. && rm -rf owncloud'
|
||||||
|
# rsync source list cleanup (revert):
|
||||||
|
ssh vitya@89.253.255.94 "sed -i '\\|/opt/stacks/owncloud|d' /opt/stacks/backup/scripts/run.sh"
|
||||||
|
# secrets cleanup:
|
||||||
|
pass rm owncloud/admin-password owncloud/user-vitya && pass git push
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gotcha 5: 60-секундный timeout на uploads через slow uplink
|
||||||
|
|
||||||
|
**Симптом:** rclone PUT любого файла, который не успевает залиться за 60 секунд через
|
||||||
|
клиентский uplink, падает с 502 (no buffering) или 500 (с buffering middleware).
|
||||||
|
Пороги при разной скорости uplink:
|
||||||
|
|
||||||
|
| Uplink | Cap |
|
||||||
|
|---|---|
|
||||||
|
| 3 MiB/s | ~180 MB |
|
||||||
|
| 10 MiB/s | ~600 MB |
|
||||||
|
| 50 MiB/s | ~3 GB |
|
||||||
|
|
||||||
|
В нашем кейсе (rclone 3.35 MiB/s) — упали все файлы ≥221 MB (4× .pat по 221-222 MB,
|
||||||
|
508 MB zip, 1.55 GB + 1.7 GB seospider).
|
||||||
|
|
||||||
|
**Где сидит 60s:** не traefik (default unlimited). Не на oCIS layer per env-var
|
||||||
|
(нет конфигурируемого ключа). Hardcoded где-то в HTTP stack — кандидаты:
|
||||||
|
|
||||||
|
- Go `http.Client.Timeout` в reva v2.27 datagateway.go:200 (видно в логах
|
||||||
|
oCIS: `Put "http://localhost:9158/data/simple/...": context canceled`,
|
||||||
|
`time_ns:59944479090` = 59.94s)
|
||||||
|
- rclone-side `http.Client.Timeout` (default Go = no timeout, но в библиотеке
|
||||||
|
WebDAV может быть явно установлен)
|
||||||
|
- HTTP/2 default stream timeout
|
||||||
|
|
||||||
|
Bumped via env-var не починить — закопано в коде reva. Apgrade oCIS image
|
||||||
|
до новой версии может помочь (newer reva имеет настраиваемый timeout) — defer.
|
||||||
|
|
||||||
|
**Что НЕ помогло:**
|
||||||
|
- `traefik.http.middlewares.<name>.buffering.maxRequestBodyBytes=0` +
|
||||||
|
`memRequestBodyBytes=1048576` — буферинг сам выдаёт 500 на 60s mark
|
||||||
|
(`backend_url="-"` в access log = traefik так и не доходит до backend).
|
||||||
|
Возможно ошибка в `vulcand/oxy` buffer на больших телах. Не разбирался —
|
||||||
|
workaround ниже проще.
|
||||||
|
|
||||||
|
**Workaround: VDS-side direct upload.**
|
||||||
|
|
||||||
|
Когда у нескольких файлов upload-time > 60s через клиентский uplink:
|
||||||
|
|
||||||
|
1. **scp с throwaway-key с restricted authorized_keys на VDS:** генерим одноразовый
|
||||||
|
ed25519 на VDS, append'им как `restrict,command="internal-sftp" <pub>`
|
||||||
|
в `~vitya/.ssh/authorized_keys` (sftp-only, no shell, no port forwards), private
|
||||||
|
отдаём агенту на slow-uplink машине. Cleanup ключа после.
|
||||||
|
|
||||||
|
2. **Агент `sftp -i <key> -b <batch>` пушит файлы в `/tmp/oc-import/`** на VDS.
|
||||||
|
SFTP через ssh не уязвим к 60s timeout — длинная сессия с keep-alive.
|
||||||
|
|
||||||
|
3. **C VDS-стороны: `curl -T <local-file> <ocis-url>` loopback'ом.**
|
||||||
|
Local network = >100 MB/s → 1.7 GB файл за 10 секунд, ≪ 60s timeout window.
|
||||||
|
PUT через traefik websecure (https://owncloud.kzntsv.site/dav/files/<user>/<path>)
|
||||||
|
с basic auth admin/user. URL-encode Cyrillic / spaces в path. Pattern:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ENC=$(python3 -c "import urllib.parse,sys; print('/'.join(urllib.parse.quote(p) for p in sys.argv[1].split('/')))" "$REMOTE_PATH")
|
||||||
|
curl -sS -u "vitya:$PWD" -T "$SRC" "https://owncloud.kzntsv.site/dav/files/vitya/${ENC}"
|
||||||
|
```
|
||||||
|
→ HTTP 201 Created.
|
||||||
|
|
||||||
|
4. **Verify через PROPFIND** — `<oc:size>` или `<d:getcontentlength>` должен
|
||||||
|
match'ить `stat -c %s` source.
|
||||||
|
|
||||||
|
5. **Cleanup:** `rm -rf /tmp/oc-import/`, remove throwaway-key line из
|
||||||
|
authorized_keys.
|
||||||
|
|
||||||
|
В нашем кейсе для 6 файлов / 4.04 GB local PUT cycle занял ~28 секунд total
|
||||||
|
(0.5-10s per file). vs rclone failed после ~5 минут на каждый retry × 5 retries × 6 файлов = безуспешно вечность.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[admin-infra-project]] — общий context для admin-infra repo
|
||||||
|
- [[vds-kzntsv]] — host entity (owncloud стек добавлен в его software stack)
|
||||||
|
- [[future-resilient-architecture-goals]] — RTO/RPO targets (oCIS — single-user, RTO/RPO defer)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: VDS kzntsv — Rusonyx 160 NVMe cloud server
|
title: VDS kzntsv — Rusonyx 160 NVMe cloud server
|
||||||
type: entity
|
type: entity
|
||||||
tags: [hardware, vds, cloud, rusonyx, infrastructure, gitea, verdaccio, registry, postgres, mariadb, mongo, redis]
|
tags: [hardware, vds, cloud, rusonyx, infrastructure, gitea, verdaccio, registry, postgres, mariadb, mongo, redis, owncloud, ocis]
|
||||||
sources: [../sources/vds-kzntsv-bootstrap-2026-05-20.md]
|
sources: [../sources/vds-kzntsv-bootstrap-2026-05-20.md]
|
||||||
updated: 2026-05-20
|
updated: 2026-05-21
|
||||||
---
|
---
|
||||||
|
|
||||||
# VDS kzntsv
|
# VDS kzntsv
|
||||||
@@ -51,6 +51,7 @@ Production CMS (MoreThenCms) **остаётся на** [`windows-recovery-host`]
|
|||||||
| Git | gitea | 1.25.5 | `/opt/stacks/gitea/` |
|
| Git | gitea | 1.25.5 | `/opt/stacks/gitea/` |
|
||||||
| NPM | verdaccio | 6 | `/opt/stacks/verdaccio/` |
|
| NPM | verdaccio | 6 | `/opt/stacks/verdaccio/` |
|
||||||
| Docker registry | registry | 2.8.3 + joxit UI | `/opt/stacks/registry/` |
|
| Docker registry | registry | 2.8.3 + joxit UI | `/opt/stacks/registry/` |
|
||||||
|
| Personal cloud | ownCloud Infinite Scale (oCIS) | 7.1.0 | `/opt/stacks/owncloud/` |
|
||||||
|
|
||||||
## Docker networks (external)
|
## Docker networks (external)
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ Production CMS (MoreThenCms) **остаётся на** [`windows-recovery-host`]
|
|||||||
| `verdaccio.kzntsv.site` | Verdaccio | kreknin htpasswd (vitya) | Private npm |
|
| `verdaccio.kzntsv.site` | Verdaccio | kreknin htpasswd (vitya) | Private npm |
|
||||||
| `registry.kzntsv.site` | Docker Registry | vitya / Pryakhin9 (htpasswd) | Docker images |
|
| `registry.kzntsv.site` | Docker Registry | vitya / Pryakhin9 (htpasswd) | Docker images |
|
||||||
| `registry-ui.vds.kzntsv.site` | Joxit Registry UI | (proxied к registry, та же auth) | GUI cleanup |
|
| `registry-ui.vds.kzntsv.site` | Joxit Registry UI | (proxied к registry, та же auth) | GUI cleanup |
|
||||||
|
| `owncloud.kzntsv.site` | oCIS (ownCloud Infinite Scale) | admin + vitya (pass `owncloud/*`); basic auth enabled для WebDAV/LibreGraph | Personal cloud, replace мёртвой Synology ownCloud (см. [`ocis-on-vds-deploy-recipe`](../concepts/ocis-on-vds-deploy-recipe.md)) |
|
||||||
| `postgres.vds.kzntsv.site:5432` | Postgres TLS | postgres / hex32 | Shared DB |
|
| `postgres.vds.kzntsv.site:5432` | Postgres TLS | postgres / hex32 | Shared DB |
|
||||||
| `mariadb.vds.kzntsv.site:3306` | MariaDB TLS | root / hex32 | Shared DB |
|
| `mariadb.vds.kzntsv.site:3306` | MariaDB TLS | root / hex32 | Shared DB |
|
||||||
| `mongo.vds.kzntsv.site:27017` | MongoDB TLS | root / hex32 | Shared DB |
|
| `mongo.vds.kzntsv.site:27017` | MongoDB TLS | root / hex32 | Shared DB |
|
||||||
@@ -102,10 +104,15 @@ DB TLS: self-signed certs (CN matches hostname), клиент с `verify-none` /
|
|||||||
│ ├── config/{config.yaml,htpasswd}
|
│ ├── config/{config.yaml,htpasswd}
|
||||||
│ ├── plugins/
|
│ ├── plugins/
|
||||||
│ └── docker-compose.yml
|
│ └── docker-compose.yml
|
||||||
└── registry/
|
├── registry/
|
||||||
├── docker/ (registry blobs storage)
|
│ ├── docker/ (registry blobs storage)
|
||||||
├── auth/htpasswd
|
│ ├── auth/htpasswd
|
||||||
└── docker-compose.yml (registry + registry-ui в одном compose)
|
│ └── docker-compose.yml (registry + registry-ui в одном compose)
|
||||||
|
└── owncloud/
|
||||||
|
├── data/ (decomposedfs: idm/, idp/, nats/, search/, storage/{metadata,users}/spaces/...)
|
||||||
|
├── config/ (ocis.yaml generated by `ocis init`)
|
||||||
|
├── .env (chmod 600 — OCIS_ADMIN_PASSWORD)
|
||||||
|
└── docker-compose.yml (oCIS 7.1.0 + traefik labels — recipe: [`ocis-on-vds-deploy-recipe`](../concepts/ocis-on-vds-deploy-recipe.md))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Что входит в backup pipeline (planned)
|
## Что входит в backup pipeline (planned)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
|||||||
- [hyper-backup-structure-and-recovery](concepts/hyper-backup-structure-and-recovery.md) — Hyper Backup — структура репо и стратегия восстановления
|
- [hyper-backup-structure-and-recovery](concepts/hyper-backup-structure-and-recovery.md) — Hyper Backup — структура репо и стратегия восстановления
|
||||||
- [iis-migration-2026-05-19-postmortem](concepts/iis-migration-2026-05-19-postmortem.md) — post-mortem миграции CMS на нативный IIS, 2026-05-19
|
- [iis-migration-2026-05-19-postmortem](concepts/iis-migration-2026-05-19-postmortem.md) — post-mortem миграции CMS на нативный IIS, 2026-05-19
|
||||||
- [mssql-container-data-restore](concepts/mssql-container-data-restore.md) — MSSQL контейнер с восстановленными production data — паттерн
|
- [mssql-container-data-restore](concepts/mssql-container-data-restore.md) — MSSQL контейнер с восстановленными production data — паттерн
|
||||||
|
- [ocis-on-vds-deploy-recipe](concepts/ocis-on-vds-deploy-recipe.md) — oCIS на VDS — deploy recipe + non-obvious gotchas (UID 1001 vs 1000, basic auth, LibreGraph user-create)
|
||||||
- [portainer-2.21-admin-password-regression](concepts/portainer-2.21-admin-password-regression.md) — Portainer 2.21 `--admin-password` regression + min 12-char policy
|
- [portainer-2.21-admin-password-regression](concepts/portainer-2.21-admin-password-regression.md) — Portainer 2.21 `--admin-password` regression + min 12-char policy
|
||||||
- [recovery-architecture-snapshot](concepts/recovery-architecture-snapshot.md) — текущая recovery architecture (2026-05-19/21, attempt 2)
|
- [recovery-architecture-snapshot](concepts/recovery-architecture-snapshot.md) — текущая recovery architecture (2026-05-19/21, attempt 2)
|
||||||
- [registry-gc-mount-and-modify-flag](concepts/registry-gc-mount-and-modify-flag.md) — Docker Registry GC mount layout + `-m` flag
|
- [registry-gc-mount-and-modify-flag](concepts/registry-gc-mount-and-modify-flag.md) — Docker Registry GC mount layout + `-m` flag
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations).
|
|||||||
## [2026-05-21] migrate | subtree-import 6 entities + 17 concepts + 3 sources from MoreThenCms (history preserved via subtree-split + temp-prefix merge)
|
## [2026-05-21] migrate | subtree-import 6 entities + 17 concepts + 3 sources from MoreThenCms (history preserved via subtree-split + temp-prefix merge)
|
||||||
|
|
||||||
## [2026-05-21] regen | index.md catalog refresh after subtree import
|
## [2026-05-21] regen | index.md catalog refresh after subtree import
|
||||||
|
|
||||||
|
## [2026-05-21] ingest | concepts/ocis-on-vds-deploy-recipe — oCIS deploy recipe + gotchas (UID mismatch, PROXY_TLS, PROXY_ENABLE_BASIC_AUTH, LibreGraph user-create); updated entities/vds-kzntsv (added owncloud row to stack/hostnames/file-layout); source: .tasks/owncloud-vds-deploy.md
|
||||||
|
|
||||||
|
## [2026-05-22] update | concepts/ocis-on-vds-deploy-recipe — Gotcha 5: 60s HTTP timeout caps slow-uplink uploads at ~200MB on 3 MiB/s link; traefik buffering NOT the fix (500 from oxy buffer); workaround = VDS-side curl PUT loopback via throwaway sftp key. Source: .tasks/owncloud-vds-deploy.md close-note.
|
||||||
|
|||||||
Reference in New Issue
Block a user