This commit is contained in:
2026-02-25 21:01:06 +03:00
parent b0d2746490
commit b532527f32
12 changed files with 719 additions and 42 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# WireGuard mobileconfig contain private keys — do not commit
*.mobileconfig
docs/containers/wireguard-macos-ondemand.mobileconfig
docs/containers/wireguard-ios-ondemand.mobileconfig

View File

@@ -16,7 +16,7 @@
|----------|------------|
| [Контейнер 100](docs/containers/container-100.md) | NPM, Homepage, AdGuard, Wallos, log-dashboard, vpn-route-check |
| [Контейнер 101](docs/containers/container-101.md) | Nextcloud, PostgreSQL, Redis, хранилище «Игры» |
| [Контейнер 103](docs/containers/container-103.md) | Gitea, PostgreSQL, act_runner, CouchDB (Obsidian) |
| [Контейнер 103](docs/containers/container-103.md) | Gitea, PostgreSQL, act_runner, CouchDB (Obsidian), Vaultwarden |
| [Контейнер 104](docs/containers/container-104.md) | Paperless-ngx, PostgreSQL, Redis |
| [Контейнер 105](docs/containers/container-105.md) | RAG API (mini-lm), модели, векторы |
| [Контейнер 107](docs/containers/container-107.md) | Invidious, Companion, PostgreSQL |

BIN
docs/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -29,6 +29,7 @@
| obsidian.katykhin.ru | — |
| share.katykhin.ru | — |
| video.katykhin.ru | Invidious |
| vault.katykhin.ru | Vaultwarden (менеджер паролей) |
| wallos.katykhin.ru | Wallos |
| _acme-challenge.call.katykhin.ru | Служебный (DNS-01 Let's Encrypt) |
@@ -62,14 +63,15 @@ pct create 105 local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
|-----|-------------------------------|----------------|------------|---------------------|
| 100 | NPM, Homepage, AdGuard, Wallos | 192.168.1.100 | 1 core, 2 GB | home.katykhin.ru, wallos.katykhin.ru, adguard.local. → [Контейнер 100 (подробно)](../containers/container-100.md) |
| 101 | Nextcloud | 192.168.1.101 | 2 core, 3 GB | cloud.katykhin.ru. → [Контейнер 101 (подробно)](../containers/container-101.md) |
| 103 | Gitea, сервис Obsidian (5984) | 192.168.1.103 | 1 core, 2 GB | obsidian.katykhin.ru → [Контейнер 103 (подробно)](../containers/container-103.md) |
| 103 | Gitea, CouchDB (Obsidian), Vaultwarden | 192.168.1.103 | 1 core, 2 GB | obsidian.katykhin.ru, vault.katykhin.ru → [Контейнер 103 (подробно)](../containers/container-103.md) |
| 104 | Paperless | 192.168.1.104 | 1 core, 2 GB | docs.katykhin.ru → [Контейнер 104 (подробно)](../containers/container-104.md) |
| 105 | RAG-service | 192.168.1.105 | 1 core, 1 GB | mini-lm.katykhin.ru → [Контейнер 105 (подробно)](../containers/container-105.md) |
| 107 | Invidious (misc) | 192.168.1.107 | 1 core, 2 GB | video.katykhin.ru → [Контейнер 107 (подробно)](../containers/container-107.md) |
| 108 | Galene | 192.168.1.108 | 1 core, 256 MB | call.katykhin.ru → [Контейнер 108 (подробно)](../containers/container-108.md) |
| 109 | WireGuard VPN (local-vpn) | 192.168.1.109 | 1 core, 256 MB | Доступ по VPN (10.10.99.0/24); клиенты получают доступ к vault.katykhin.ru и LAN → [Контейнер 109 (подробно)](../containers/container-109.md) |
| 200 | Immich, immich-pt и др. | 192.168.1.200 | 3 core, 10 GB | immich.katykhin.ru → [ВМ 200 (подробно)](../containers/container-200.md) |
*100108 — LXC-контейнеры (pct), 200 — KVM-ВМ (qm).* Домены api, git, share и др. в NPM при необходимости добавить позже.
*100109 — LXC-контейнеры (pct), 200 — KVM-ВМ (qm). Схема ID = последний октет IP соблюдена.* Домены api, git, share и др. в NPM при необходимости добавить позже.
---
@@ -92,7 +94,7 @@ pct create 105 local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
## Дополнительно
- **Схема сети и зависимости:** полная топология (роутер, Proxmox, контейнеры, VPS), таблица IP/доменов, маршруты NPM, кто от кого зависит, единые точки отказа (SPOF). → [Схема сети и зависимости](../network/network-topology.md).
- **Homepage:** на контейнере 100, конфиг сервисов в `/opt/docker/homepage/config/services.yaml` (ссылки на NPM, Invidious, AdGuard, Immich, Galene и т.д.).
- **Homepage:** на контейнере 100, конфиг сервисов в `/opt/docker/homepage/config/services.yaml` (ссылки на NPM, Invidious, AdGuard, Immich, Galene, Vaultwarden и т.д.).
- **VPN (VPS):** отдельный сервер 185.103.253.99, AmneziaWG для обхода блокировок. → [VPN-сервер (VPS, AmneziaWG)](../vps/vpn-vps-amneziawg.md).
- **Роутер:** Netcraze Speedster, два WireGuard/AmneziaWG (Германия / США), маршрутизация части трафика через VPN. → [Роутер Netcraze Speedster](../network/router-netcraze-speedster.md).
- **VPS Миран (СПБ):** боты (telegram-helper-bot, anonBot), prod-инфраструктура, STUN/TURN для Galene. → [VPS Миран: боты и STUN/TURN](../vps/vps-miran-bots.md).

View File

@@ -225,7 +225,9 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
- [ ] Настроен бэкап `/etc/pve` (скрипт + cron) → `/mnt/backup/proxmox/etc-pve`.
- [ ] Restic: cron на хосте, выгрузка нужных каталогов из `/mnt/backup` в Yandex S3, retention 7/4/6.
- [ ] Yandex: ключи и endpoint зафиксированы, restic успешно пишет в бакет.
- [ ] Vaultwarden развёрнут (CT 107 или 103), секреты перенесены, бэкап данных Vaultwarden входит в restic.
- [x] Vaultwarden развёрнут (CT 103).
- [ ] Секреты перенесены в Vaultwarden.
- [ ] Бэкап данных Vaultwarden (`/opt/docker/vaultwarden/data`) включён в restic (Yandex S3).
- [ ] Выполнено тестовое восстановление одного контейнера (другой VMID), проверена работоспособность.
- [ ] В документации зафиксирована процедура полного восстановления Proxmox «с нуля».
@@ -251,7 +253,7 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
| Где запускать бэкапы | Cron на хосте Proxmox: Backup Job (vzdump) + restic в Yandex. |
| Retention локально | Только в Proxmox Job и в скрипте etc-pve; отдельного restic-репозитория локально не делаем. |
| /etc/pve | Локально и в Yandex (через restic). |
| Пароли | Vaultwarden на CT 107 или 103. |
| Пароли | Vaultwarden на CT 103. |
| Yandex | Бакет создан; ключи и endpoint зафиксировать при настройке restic. |
| MinIO | Не используем; директории + restic (s3 для Yandex). |

View File

@@ -106,7 +106,17 @@ docker restart adguard
**Сеть:** proxy_network.
**Основные файлы конфигурации:**
- `services.yaml` — список сервисов, виджеты (NPM, AdGuard, Proxmox и др.), ссылки, пинги. Пароли и токены виджетов хранятся здесь (не коммитить в открытый репозиторий).
- `services.yaml` — список сервисов, виджеты (NPM, AdGuard, Proxmox и др.), ссылки, пинги. Пароли и токены виджетов хранятся здесь (не коммитить в открытый репозиторий). В список сервисов добавлен **Vaultwarden** (менеджер паролей на контейнере 103): ссылка на `https://vault.katykhin.ru` (если в NPM настроен proxy) или на `http://192.168.1.103:8280` (доступ по LAN по IP). Пример записи в `services.yaml`:
```yaml
- Vaultwarden
description: Менеджер паролей (Bitwarden-совместимый)
icon: vaultwarden.png
href: https://vault.katykhin.ru
target: _blank
```
(При использовании только LAN по IP укажите `href: http://192.168.1.103:8280`. Иконка: в [документации Homepage](https://gethomepage.dev/docs/services/) или локальный файл в `config/images/`.)
- `docker.yaml` — подключение к Docker через dockerproxy (host/port).
- `settings.yaml`, `widgets.yaml`, `bookmarks.yaml`, `custom.css`, `proxmox.yaml`, `kubernetes.yaml` при необходимости.
@@ -254,5 +264,6 @@ docker logs vpn-route-check
## Связь с другими документами
- [Архитектура и подключение](../architecture/architecture.md) — таблица контейнеров, домены, схема сети.
- [Контейнер 103 (Gitea, Vaultwarden)](container-103.md) — сервис Vaultwarden (менеджер паролей), добавляемый на дашборд Homepage.
- [Выпуск сертификата Let's Encrypt (DNS-01)](../network/ssl-letsencrypt-dns01.md) — certbot, Beget API, интеграция с NPM.
- [Роутер Netcraze Speedster](../network/router-netcraze-speedster.md) — VPN и telnet, используемые vpn-route-check.

View File

@@ -1,4 +1,4 @@
# Контейнер 103 (Gitea): Gitea, PostgreSQL, act_runner, CouchDB (Obsidian)
# Контейнер 103 (Gitea): Gitea, PostgreSQL, act_runner, CouchDB (Obsidian), Vaultwarden
Подробное описание LXC-контейнера **103** на Proxmox (192.168.1.103): Git-сервер Gitea с Actions (runner), база PostgreSQL, CouchDB для синхронизации Obsidian (домен obsidian.katykhin.ru).
@@ -21,15 +21,17 @@
- **Debian (CT 103):** логин `root` (пароль — в менеджере паролей или как настраивал при установке).
- **Gitea (веб):** http://192.168.1.103:3000 (или через NPM по домену git.katykhin.ru, если настроен). Учётные записи — пользователи Gitea. Репозитории могут иметь origin на Gitea; при необходимости пуш в GitHub — отдельный remote (например `github`), команда вида `git push github main`.
- **CouchDB (Obsidian sync):** http://192.168.1.103:5984. Админ (пользователь **obsidian**) и пароль заданы в `/opt/docker/couchdb/local.d/local.ini` (секция `[admins]`); клиент Obsidian подключается по URL и своим учётным данным.
- **Vaultwarden (менеджер паролей):** http://192.168.1.103:8280 (только LAN, без домена). Админ-доступ по токену `ADMIN_TOKEN` из `/opt/docker/vaultwarden/.env` (файл только на сервере, не коммитить).
---
## Сервисы (Docker)
Два независимых набора сервисов:
Три набора сервисов:
1. **Gitea (compose)** — в `/opt/gitea/`: Gitea, PostgreSQL, act_runner. Сеть **gitea_default**.
2. **CouchDB** — запущен отдельным контейнером (без compose в репозитории), данные в `/opt/docker/couchdb/`.
3. **Vaultwarden (compose)** — в `/opt/docker/vaultwarden/`: менеджер паролей Vaultwarden (совместим с клиентами Bitwarden).
| Контейнер | Образ | Порты (хост) | Назначение |
|-----------------|--------------------------|------------------|------------|
@@ -37,6 +39,7 @@
| gitea-db-1 | postgres:16-alpine | — | БД Gitea |
| gitea-runner-1 | gitea/act_runner:latest | — | Gitea Actions (CI) |
| couchdb | couchdb:3 | 5984 | Бэкенд синхронизации Obsidian (obsidian.katykhin.ru) |
| vaultwarden | vaultwarden/server:latest | 8280 | Менеджер паролей Vaultwarden (Bitwarden-совместимый), доступ из LAN |
---
@@ -131,6 +134,59 @@ curl -s http://192.168.1.103:5984/
---
## 5. Vaultwarden (менеджер паролей)
**Назначение:** self-hosted менеджер паролей (совместим с официальными клиентами Bitwarden) для хранения всех кредов (Proxmox, контейнеры, БД, API-ключи и т.д.).
**Каталог:** `/opt/docker/vaultwarden/`
**Compose:** `docker-compose.yml`. Запуск/обновление:
```bash
cd /opt/docker/vaultwarden
docker compose pull
docker compose up -d
```
**Образ:** `vaultwarden/server:latest`.
**Порты:**
- 8280 (хост) → 80 (контейнер).
**Доступ в локальной сети:** с любого устройства в LAN (192.168.1.x) Vaultwarden уже доступен по адресу **`http://192.168.1.103:8280`** — дополнительная настройка не нужна. Клиенты Bitwarden на домашних устройствах можно настроить на этот URL.
**Доступ по домену (опционально):** если нужен **https://vault.katykhin.ru** и из LAN, и по VPN, в NPM (контейнер 100) настраивают:
- **Proxy Host:** `vault.katykhin.ru` → upstream `192.168.1.103:8280`, включить SSL (Let's Encrypt или custom).
- **Access List:** создать список, разрешающий только подсети **192.168.1.0/24** (LAN) и **10.10.99.0/24** (WireGuard VPN); для всех остальных — отказ. Эту access list привязать к proxy host `vault.katykhin.ru`. Тогда с интернета без VPN доступ к домену будет закрыт; из дома и по VPN — открыт.
- В compose Vaultwarden при использовании домена задать `DOMAIN=https://vault.katykhin.ru` и перезапустить контейнер.
**Тома:**
- `/opt/docker/vaultwarden/data``/data` (все данные Vaultwarden: база, вложения, и т.п.).
**Переменные окружения (compose):**
- `WEBSOCKET_ENABLED=true` — включает поддержку веб-сокетов.
- `SIGNUPS_ALLOWED=false` — запрещает свободную регистрацию; пользователей создаёт админ.
- `INVITATIONS_ALLOWED=true` — разрешает приглашения.
- `DOMAIN=http://192.168.1.103:8280` — базовый URL (для LAN; при выдаче наружу поменять на `https://<домен>`).
- `ROCKET_PORT=80`, `ROCKET_ADDRESS=0.0.0.0` — HTTP-сервер внутри контейнера.
- `TZ=Europe/Moscow` — часовой пояс.
**Файл `.env` (секреты):**
- Путь: `/opt/docker/vaultwarden/.env`.
- В нём как минимум задаётся `ADMIN_TOKEN=<случайный токен>` для доступа к админке.
- Файл создаётся на хосте (права `600`), **не коммитить** в репозиторий и не копировать в открытые места.
**Проверка работы:**
```bash
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' | grep vaultwarden
curl -s http://127.0.0.1:8280/ | head -c 200
```
После этого интерфейс открывается по **`http://192.168.1.103:8280`** из домашней сети. Клиенты Bitwarden (ПК, телефон в LAN) настраивают на этот URL — сервис уже открыт в локальной сети без NPM. Если позже добавить домен в NPM (см. выше), в клиентах можно перейти на `https://vault.katykhin.ru`.
---
## Порты (сводка на хосте)
| Порт | Сервис / примечание |
@@ -138,6 +194,7 @@ curl -s http://192.168.1.103:5984/
| 3000 | Gitea (веб) |
| 2222 | Gitea (SSH для git) |
| 5984 | CouchDB (Obsidian sync) |
| 8280 | Vaultwarden (менеджер паролей, HTTP по IP из LAN) |
---

View File

@@ -0,0 +1,203 @@
# Контейнер 109 (local-vpn): локальный WireGuard VPN
Подробное описание LXC-контейнера **109** на Proxmox (192.168.1.109): локальный VPN-сервер на WireGuard для доступа к внутренней сети и сервисам (в т.ч. Vaultwarden) только через зашифрованный туннель.
---
## Общие сведения
- **Хостнейм:** local-vpn
- **ID:** 109
- **IP:** 192.168.1.109/24
- **ОС:** Debian 12 (bookworm)
- **Ресурсы:** 1 core, 256 MB RAM, 1 GB диск на `local-lvm`
- **Доступ:** с Proxmox — `ssh root@192.168.1.150``pct exec 109 -- bash`.
Назначение контейнера — поднимать локальный WireGuard, через который:
- клиенты (iOS/macOS) получают доступ к сети `192.168.1.0/24` (LAN),
- DNS внутри VPN идёт через AdGuard на CT 100 (`192.168.1.100`),
- доступ к Vaultwarden (`vault.katykhin.ru` → NPM → 192.168.1.103:8280) можно ограничить **только через этот VPN** (ограничение на уровне NPM/фаервола, см. соответствующую документацию).
---
## Создание контейнера
Контейнер создан на Proxmox командой вида (выполнено на хосте 192.168.1.150):
```bash
pct create 109 local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \
--hostname local-vpn \
--cores 1 \
--memory 256 \
--rootfs local-lvm:1 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.1.109/24,gw=192.168.1.1,firewall=1 \
--features keyctl=1,nesting=1
pct start 109
```
---
## WireGuard (wg0) внутри контейнера
### Пакеты и подготовка
Внутри CT 109:
```bash
apt-get update
apt-get install -y wireguard-tools iptables
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-wg-forward.conf
sysctl -p /etc/sysctl.d/99-wg-forward.conf
```
### Ключи и конфиг
Ключи сервера и клиентов лежат в `/etc/wireguard/` (файлы `server.key/.pub`, `ios.key/.pub`, `macos.key/.pub`).
Основной конфиг интерфейса — `/etc/wireguard/wg0.conf`:
```ini
[Interface]
Address = 10.10.99.1/24
ListenPort = 43123
PrivateKey = <server-private-key>
# Включение форвардинга LAN <-> VPN с NAT
PostUp = iptables -A FORWARD -i wg0 -o eth0 -s 10.10.99.0/24 -d 192.168.1.0/24 -j ACCEPT; \
iptables -A FORWARD -i eth0 -o wg0 -d 10.10.99.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT; \
iptables -t nat -A POSTROUTING -s 10.10.99.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -s 10.10.99.0/24 -d 192.168.1.0/24 -j ACCEPT; \
iptables -D FORWARD -i eth0 -o wg0 -d 10.10.99.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT; \
iptables -t nat -D POSTROUTING -s 10.10.99.0/24 -o eth0 -j MASQUERADE
[Peer]
# iOS
PublicKey = <ios-public-key>
AllowedIPs = 10.10.99.2/32
[Peer]
# macOS
PublicKey = <macos-public-key>
AllowedIPs = 10.10.99.3/32
```
- **Серверный адрес:** `10.10.99.1/24` (подсеть VPN).
- **Порт WireGuard:** `43123/UDP`.
- **NAT:** MASQUERADE на выход в `192.168.1.0/24`, чтобы не настраивать статические маршруты на роутере.
### Запуск и автозапуск
```bash
wg-quick up wg0 # ручной запуск
wg show # проверить состояние
systemctl enable wg-quick@wg0 # автозапуск при старте CT
systemctl start wg-quick@wg0
```
Проверка интерфейса:
```bash
ip a show wg0
```
Ожидаемый адрес: `10.10.99.1/24`.
---
## Конфиги клиентов (split-tunnel + DNS через AdGuard)
Клиентские конфиги удобно хранить в `/etc/wireguard/clients/` внутри CT (и оттуда забирать на устройства).
### Базовые параметры для всех клиентов
- **Адрес клиента в VPN:** `10.10.99.X/32` (уникальный для каждого устройства).
- **DNS:** `192.168.1.100` (AdGuard на CT 100).
- **Endpoint сервера:** `185.35.193.144:43123` (внешний IP + порт, проброшенный на CT 109).
- **Split tunnel:**
- `AllowedIPs = 10.10.99.0/24, 192.168.1.0/24`
→ через VPN идут только:
- сама VPN-подсеть `10.10.99.0/24`,
- внутренняя сеть `192.168.1.0/24` (все домашние сервисы, в т.ч. NPM и Vaultwarden).
Весь остальной интернет идёт **напрямую** у клиента.
Пример для iOS:
```ini
[Interface]
Address = 10.10.99.2/32
PrivateKey = <ios-private-key>
DNS = 192.168.1.100
[Peer]
PublicKey = <server-public-key>
Endpoint = 185.35.193.144:43123
AllowedIPs = 10.10.99.0/24, 192.168.1.0/24
PersistentKeepalive = 25
```
Пример для macOS:
```ini
[Interface]
Address = 10.10.99.3/32
PrivateKey = <macos-private-key>
DNS = 192.168.1.100
[Peer]
PublicKey = <server-public-key>
Endpoint = 185.35.193.144:43123
AllowedIPs = 10.10.99.0/24, 192.168.1.0/24
PersistentKeepalive = 25
```
> **Важно:** реальные ключи (`PrivateKey`, `PublicKey`) хранятся только в контейнере (`/etc/wireguard/*.key/.pub`) и **не попадают** в этот репозиторий. В примерах выше использовать плейсхолдеры и заменять их актуальными значениями при выдаче конфигов на устройства.
---
## Firewall и ограничения доступа к Vaultwarden (общая схема)
Идея ограничения доступа:
- **На роутере (192.168.1.1):**
- открыть только UDP-порт `43123`,
- пробросить его на `192.168.1.109:43123` (CT 109),
- остальные порты для CT 109 снаружи не открывать.
- **На Proxmox firewall (уровень ноды / CT 109):**
- разрешить входящий `UDP 43123` к CT 109,
- разрешить ESTABLISHED/RELATED,
- разрешить исходящий трафик из CT 109 в LAN (`192.168.1.0/24`) и к нужным внешним DNS (если потребуется).
- **На уровне NPM (CT 100) / Vaultwarden (CT 103):**
- создать proxy host `vault.katykhin.ru` → upstream `192.168.1.103:8280`,
- ограничить доступ к этому хосту **только из VPN** — либо по исходному адресу (подсеть WireGuard `10.10.99.0/24`, если используется маршрутизация без NAT), либо по IP сервера VPN (`192.168.1.109`, если используется NAT в CT 109),
- отключить доступ к Vaultwarden из LAN напрямую по домену (и при желании по IP).
Точная реализация правил на роутере и в NPM описывается в соответствующих документах (`router-netcraze-speedster`, `container-100` / NPM); здесь зафиксирована лишь роль контейнера 109 и параметры WireGuard.
---
## On-Demand для iOS/macOS
Для автоматического включения VPN **вне домашнего WiFi** используется профиль `.mobileconfig` с правилами On-Demand. Подробно: [Генерация .mobileconfig для WireGuard VPN (On-Demand)](../network/vpn-mobileconfig-wireguard.md).
- если подключен WiFi с SSID из списка (например `HomeWiFi`) → VPN **отключён**;
- во всех остальных случаях (другой WiFi, сотовые данные) → VPN **включается автоматически**.
**macOS:** профиль `wireguard-macos-ondemand.mobileconfig`. Перед установкой замени `HomeWiFi` на реальный SSID домашней сети (секция `OnDemandRules``SSIDMatch`). Установка: двойной клик по файлу → «Системные настройки» → «Профили» → установить. Туннель появится в приложении WireGuard.
**iOS:** профиль `wireguard-ios-ondemand.mobileconfig`. То же правило: замени `HomeWiFi` на SSID домашней сети. Установка: отправить файл на iPhone (AirDrop, почта, файлы) → открыть → «Установить» в предложенном профиле → при необходимости «Настройки» → «Основные» → «VPN и управление устройством» → установить профиль. Туннель «Local VPN (WireGuard)» появится в приложении WireGuard; включи один раз вручную, далее On-Demand будет управлять подключением.
Оба файла содержат приватные ключи и добавлены в `.gitignore` — в репозиторий не коммитить.
---
## Связь с другими документами
- [Архитектура и подключение](../architecture/architecture.md) — общее описание хостов, IP, доменов (контейнер 109 нужно добавить как исключение ID ↔ IP при необходимости).
- [Контейнер 100 (NPM)](container-100.md) — настройка proxy host `vault.katykhin.ru` и ограничение доступа только через VPN.
- [Контейнер 103 (Vaultwarden)](container-103.md) — сам сервис Vaultwarden и доступ по `192.168.1.103:8280` из LAN/VPN.
- [Роутер Netcraze Speedster](../network/router-netcraze-speedster.md) — проброс UDP 43123 и общие правила фаервола/маршрутизации.

View File

@@ -11,37 +11,38 @@
```mermaid
flowchart TB
subgraph External["Внешний мир"]
DNS["DNS (Beget)\n*.katykhin.ru → IP"]
DNS["DNS (Beget)<br>*.katykhin.ru → IP"]
end
subgraph Edge["Граница сети"]
Router["Роутер\n192.168.1.1\nпроброс 80/443 → .100"]
Router["Роутер<br>192.168.1.1<br>проброс 80/443 → .100"]
end
subgraph Gateway["Единая точка входа"]
NPM["NPM\nCT 100\nтерминация SSL, маршрут по Host"]
NPM["NPM<br>CT 100<br>терминация SSL, маршрут по Host"]
end
subgraph Backends["Сервисы"]
S101["Nextcloud\nCT 101"]
S103["Gitea / CouchDB\nCT 103"]
S104["Paperless\nCT 104"]
S105["RAG API\nCT 105"]
S107["Invidious\nCT 107"]
S108["Galene\nCT 108"]
S200["Immich\nVM 200"]
S101["Nextcloud<br>CT 101"]
S103["Gitea / CouchDB / Vaultwarden<br>CT 103"]
S104["Paperless<br>CT 104"]
S105["RAG API<br>CT 105"]
S107["Invidious<br>CT 107"]
S108["Galene<br>CT 108"]
S109["WireGuard VPN<br>CT 109<br>10.10.99.0/24"]
S200["Immich<br>VM 200"]
end
subgraph Data["Данные"]
D101["PostgreSQL\nRedis\nCT 101"]
D103["PostgreSQL\nCT 103"]
D104["PostgreSQL\nRedis\nCT 104"]
D107["PostgreSQL\nCT 107"]
D200["PostgreSQL\nRedis\nVM 200"]
D101["PostgreSQL<br>Redis<br>CT 101"]
D103["PostgreSQL<br>CT 103"]
D104["PostgreSQL<br>Redis<br>CT 104"]
D107["PostgreSQL<br>CT 107"]
D200["PostgreSQL<br>Redis<br>VM 200"]
end
subgraph External2["Внешние зависимости"]
coTURN["coTURN\nVPS Миран 185.147.80.190"]
coTURN["coTURN<br>VPS Миран 185.147.80.190"]
end
DNS --> Router
@@ -53,6 +54,8 @@ flowchart TB
NPM --> S107
NPM --> S108
NPM --> S200
Router --> S109
S109 -.->|"клиенты VPN<br>доступ к vault, LAN"| NPM
S101 --> D101
S103 --> D103
@@ -87,6 +90,7 @@ flowchart TB
- **Galene (108)** дополнительно зависит от **coTURN (VPS Миран)** для STUN/TURN — на графе это отдельная зависимость от внешнего узла.
- **RAG (105)** без внешней БД в стеке — только NPM → Service.
- **AdGuard, Homepage, Wallos** живут на том же хосте, что и NPM (CT 100), но логически стоят «рядом» с NPM (доступ к ним тоже через роутер и при необходимости через NPM).
- **Vaultwarden (vault.katykhin.ru):** доступ только из LAN (192.168.1.0/24) и из VPN (10.10.99.0/24). Публичный доступ из интернета закрыт; удалённо — только через WireGuard (CT 109). NPM Access List ограничивает приём по подсетям.
---
@@ -102,22 +106,23 @@ flowchart TB
│ VPN: AmneziaWG DE / US │
└───────────────┬───────────────┘
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────────┐ ┌─────────────────┐
│ Proxmox │ │ CT 100 │ │ Остальные CT/VM │
│ 192.168.1.150│ │ 192.168.1.100 │ │ .101 .103 .104 │
│ (гипервизор) │ │ NPM, AdGuard, │ │ .105 .107 .108 │
│ │ │ Homepage, │ │ VM 200 .200 │
│ pct / qm │ │ Wallos, etc. │ │ (бэкенды) │
└──────────────┘ └───────┬────────┘ └────────┬────────┘
│ │ │
│ │ HTTPS по Host │
│ └──────────────────────┘
│ (NPM проксирует на бэкенды)
┌────────────────────┼────────────────────┬────────────────
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Proxmox │ │ CT 100 │ │ Остальные CT/VM │ │ CT 109 │
│ 192.168.1.150│ │ 192.168.1.100 │ │ .101 .103 .104 │ │ 192.168.1.109 │
│ (гипервизор) │ │ NPM, AdGuard, │ │ .105 .107 .108 │ │ WireGuard VPN │
│ │ │ Homepage, │ │ VM 200 .200 │ │ UDP 43123 │
│ pct / qm │ │ Wallos, etc. │ │ (бэкенды) │ │ 10.10.99.0/24 │
└──────────────┘ └───────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ │ HTTPS по Host │ │ клиенты VPN
│ └──────────────────────┘ │ → vault, LAN
│ (NPM проксирует на бэкенды)
│ Туннели VPN (роутер ↔ VPS)
│ + входящий WireGuard на CT 109 (проброс UDP 43123)
├──────────────────────────────────────────────────┐
│ │
@@ -130,6 +135,8 @@ flowchart TB
└──────────────────┘ └──────────────────┘ └──────────────────────┘
```
**Доступ к vault.katykhin.ru:** из LAN (192.168.1.0/24) или через WireGuard (CT 109): клиент подключается к 192.168.1.109:43123, получает IP из 10.10.99.0/24, после чего запросы к vault.katykhin.ru идут через NPM (Access List разрешает обе подсети).
**Поток публичного трафика:**
Запрос из интернета (например `https://video.katykhin.ru`) → роутер (185.35.193.144:443) → проброс на 192.168.1.100:443 → NPM (контейнер 100) → по Host выбирается proxy host → запрос на бэкенд (например 192.168.1.107:3000 для Invidious). Ответ идёт обратно по той же цепочке.
@@ -143,11 +150,12 @@ flowchart TB
| **Proxmox** | 192.168.1.150 | Гипервизор (LXC + KVM) | Управление: `pct`, `qm` |
| **CT 100** | 192.168.1.100 | NPM, Homepage, AdGuard, Wallos, log-dashboard, vpn-route-check | home.katykhin.ru, wallos.katykhin.ru, adguard.local; приём 80/443 |
| **CT 101** | 192.168.1.101 | Nextcloud, PostgreSQL, Redis | cloud.katykhin.ru |
| **CT 103** | 192.168.1.103 | Gitea, PostgreSQL, act_runner, CouchDB (Obsidian) | git.katykhin.ru, obsidian.katykhin.ru |
| **CT 103** | 192.168.1.103 | Gitea, PostgreSQL, act_runner, CouchDB (Obsidian), Vaultwarden | git.katykhin.ru, obsidian.katykhin.ru, vault.katykhin.ru (LAN + VPN) |
| **CT 104** | 192.168.1.104 | Paperless-ngx, PostgreSQL, Redis | docs.katykhin.ru |
| **CT 105** | 192.168.1.105 | RAG API (mini-lm) | mini-lm.katykhin.ru |
| **CT 107** | 192.168.1.107 | Invidious, Companion, PostgreSQL | video.katykhin.ru |
| **CT 108** | 192.168.1.108 | Galene (видеозвонки) | call.katykhin.ru |
| **CT 109** | 192.168.1.109 | WireGuard VPN (local-vpn), UDP 43123 | Клиенты 10.10.99.0/24; доступ к vault, LAN, AdGuard DNS |
| **VM 200** | 192.168.1.200 | Immich, PostgreSQL, Redis, ML, deduper, Power Tools, Public Share | immich.katykhin.ru, immich-pt.katykhin.ru, share.katykhin.ru |
| **VPS DE** | 185.103.253.99 | AmneziaWG (обход блокировок) | Туннель с роутера (10.8.1.x) |
| **VPS US** | 147.45.124.117 | AmneziaWG (второй выход) | Туннель с роутера |
@@ -175,6 +183,7 @@ flowchart TB
| mini-lm.katykhin.ru | 192.168.1.105:8000 | CT 105 |
| git.katykhin.ru | 192.168.1.103:3000 | CT 103 |
| obsidian.katykhin.ru | 192.168.1.103:5984 | CT 103 |
| vault.katykhin.ru | 192.168.1.103:8280 | CT 103 (Vaultwarden; доступ по Access List: LAN + VPN) |
(Точный список proxy host — в NPM на CT 100; при добавлении доменов таблицу обновлять.)
@@ -191,6 +200,7 @@ flowchart TB
4. **DNS (Beget)** — без записей *.katykhin.ru запросы не дойдут до твоего IP.
- **Каждый бэкенд** (101, 103, 104, 105, 107, 108, 200) при доступе снаружи зависит от NPM и роутера; при доступе только из LAN может работать и без NPM (по IP и порту).
- **Vaultwarden (vault.katykhin.ru):** публичный доступ из интернета закрыт; разрешены только LAN (192.168.1.0/24) и клиенты WireGuard VPN (10.10.99.0/24). Удалённый доступ к vault — только через CT 109 (WireGuard).
### Внутри хостов
@@ -201,6 +211,7 @@ flowchart TB
- **Контейнеры 101, 103, 104, 107, 200:** каждый свой стек: приложение зависит от своей БД (PostgreSQL) и при необходимости от Redis. Порядок запуска: сначала БД/Redis, потом приложение (обычно задано в docker-compose через depends_on).
- **Контейнер 105 (RAG):** один контейнер, без внешней БД в стеке.
- **Контейнер 108 (Galene):** не зависит от других контейнеров в LAN; зависит от **внешнего coTURN** (VPS Миран 185.147.80.190) для STUN/TURN — без него видеозвонки могут не устанавливаться за строгим NAT/фаерволом.
- **Контейнер 109 (WireGuard VPN):** самостоятельный; от него зависят удалённые пользователи (доступ к vault.katykhin.ru и LAN через NPM после подключения к VPN). DNS для клиентов — AdGuard (CT 100).
- **Immich (200):** зависит от PostgreSQL, Redis, при необходимости от immich-deduper (сеть и БД). ML-контейнер опционально (GPU).
### Внешние сервисы
@@ -217,10 +228,11 @@ flowchart TB
| **Роутер** | Весь доступ из интернета к домашней сети (в т.ч. все домены). Нет VPN-выхода, если туннели подняты на роутере. | Резервный роутер / запасной канал; доступ к управлению из LAN. |
| **Proxmox (192.168.1.150)** | Все контейнеры и ВМ недоступны (они на нём запущены). | Резервное питание, мониторинг, бэкапы конфигов и данных. |
| **Контейнер 100 (NPM)** | Публичный HTTPS ко всем сервисам: без NPM нет терминации SSL и маршрутизации. Сервисы по IP:порт из LAN могут быть доступны, если порты не закрыты. | Мониторинг NPM и Docker на 100; быстрый перезапуск; бэкап конфигов NPM. |
| **Контейнер 109 (WireGuard)** | Нет удалённого доступа по VPN к vault.katykhin.ru и к внутренним сервисам извне LAN. | Мониторинг сервиса wg-quick@wg0; проброс UDP 43123 на роутере; бэкап /etc/wireguard. |
| **Certbot / Beget API** | Истечение сертификатов → браузеры начнут ругаться. Продление через DNS-01 зависит от Beget. | Следить за продлением (таймер certbot); иметь запасной способ выпуска (другой DNS или ручной сертификат). |
| **DNS (Beget)** | Смена IP или потеря записей — домены перестанут вести на твой хост. | Ведение записей вручную/через API; при смене IP обновить A-записи. |
| **VPS Миран (coTURN)** | Galene: проблемы с установкой видеозвонов за NAT. Остальные сервисы не зависят. | Локальный coTURN на 108 или другом хосте как запасной вариант. |
| **Конкретный бэкенд (101, 103, …)** | Падает только свой сервис (Nextcloud, Gitea, Invidious и т.д.). Остальные работают. | Зависимости внутри стека (БД первым) и мониторинг каждого хоста. |
| **Конкретный бэкенд (101, 103, …)** | Падает только свой сервис (Nextcloud, Gitea, Vaultwarden, Invidious и т.д.). Остальные работают. | Зависимости внутри стека (БД первым) и мониторинг каждого хоста. |
---
@@ -228,6 +240,8 @@ flowchart TB
- [Архитектура и подключение](../architecture/architecture.md) — общее описание, таблица контейнеров, поток запросов.
- [Контейнер 100](../containers/container-100.md) — NPM, AdGuard, Homepage, порядок запуска.
- [Контейнер 109](../containers/container-109.md) — WireGuard VPN (local-vpn), доступ к vault и LAN.
- [Генерация .mobileconfig для WireGuard (On-Demand)](vpn-mobileconfig-wireguard.md) — как собрать профиль для iOS/macOS с автоматическим подключением вне дома.
- [Роутер Netcraze Speedster](router-netcraze-speedster.md) — проброс портов, VPN.
- [VPN-сервер (VPS, AmneziaWG)](../vps/vpn-vps-amneziawg.md) — туннели с роутера.
- [VPS Миран: боты и STUN/TURN](../vps/vps-miran-bots.md) — coTURN для Galene.

View File

@@ -0,0 +1,258 @@
# Генерация .mobileconfig для WireGuard VPN (On-Demand)
Как собрать Apple-профиль конфигурации (`.mobileconfig`) для WireGuard с правилами **On-Demand**: автоматическое подключение VPN вне домашней сети и отключение на домашнем WiFi.
---
## Зачем нужен .mobileconfig
- **Обычный конфиг WireGuard** — пользователь сам включает/выключает туннель.
- **Профиль .mobileconfig** — ставится в «Настройки» → «Профили» (macOS) или «Основные» → «VPN и управление устройством» (iOS). Туннель появляется в приложении WireGuard и может управляться **On-Demand**: система сама поднимает VPN, когда устройство не в доверенной сети (например не дома), и отключает на домашнем WiFi.
В итоге: за пределами дома — всегда VPN, дома — без VPN, без ручного переключения.
---
## Что должно быть готово на сервере
- WireGuard поднят (например на [контейнере 109](../containers/container-109.md)).
- Для каждого клиента (macOS, iOS и т.д.):
- своя пара ключей (приватный + публичный);
- в `wg0.conf` на сервере добавлен `[Peer]` с `PublicKey` и `AllowedIPs` (один адрес из подсети VPN, например `10.10.99.2/32` для iOS, `10.10.99.3/32` для macOS).
- Внешний IP и порт сервера (например `185.35.193.144:43123`), проброс UDP на роутере на CT 109.
Подсеть VPN в примерах: `10.10.99.0/24`; DNS в туннеле — AdGuard на `192.168.1.100`.
---
## Структура профиля
Файл `.mobileconfig` — это **XML plist** (Property List) с корневым словарём:
| Ключ | Назначение |
|------|------------|
| `PayloadContent` | Массив payloadов (в нашем случае один — VPN). |
| `PayloadDisplayName` | Название профиля в списке профилей. |
| `PayloadIdentifier` | Уникальный идентификатор (например `ru.katykhin.wireguard.local-vpn`). |
| `PayloadType` | `Configuration`. |
| `PayloadUUID` | Уникальный UUID профиля. |
| `PayloadVersion` | 1. |
Внутри `PayloadContent` — один элемент типа **VPN**:
| Ключ | Назначение |
|------|------------|
| `PayloadType` | `com.apple.vpn.managed` |
| `VPNSubType` | **macOS:** `com.wireguard.macos`**iOS:** `com.wireguard.ios` |
| `UserDefinedName` | Имя туннеля в приложении WireGuard (например «Local VPN (WireGuard)»). |
| `VendorConfig``WgQuickConfig` | Строка с конфигом WireGuard в формате INI (как в обычном конфиге). |
| `VPN``RemoteAddress` | Адрес сервера (хост:порт). |
| `OnDemandEnabled` | `1` — включить On-Demand. |
| `OnDemandRules` | Массив правил: когда отключать VPN (например на домашнем WiFi) и когда подключать. |
---
## Конфиг WireGuard внутри профиля (WgQuickConfig)
В `WgQuickConfig` вставляется **ровно тот же текст**, что и в обычном клиентском конфиге WireGuard, например:
```ini
[Interface]
Address = 10.10.99.3/32
PrivateKey = <приватный-ключ-этого-клиента>
DNS = 192.168.1.100
[Peer]
PublicKey = <публичный-ключ-сервера>
Endpoint = 185.35.193.144:43123
AllowedIPs = 10.10.99.0/24, 192.168.1.0/24
PersistentKeepalive = 25
```
- `Address` — один адрес из подсети VPN, уникальный для каждого устройства (в `wg0.conf` на сервере в `AllowedIPs` для этого peer должен быть этот же `/32`).
- `PrivateKey` — приватный ключ **клиента** (генерируется один раз и хранится только на устройстве / в профиле).
- `PublicKey` — публичный ключ **сервера** (из `/etc/wireguard/server.pub` на сервере).
- `Endpoint` — внешний IP или домен и порт WireGuard (проброс на роутере).
- `AllowedIPs` — какие сети пускать через VPN (подсеть VPN + LAN, если нужен доступ в домашнюю сеть).
- `PersistentKeepalive` — полезно для мобильных и NAT.
Ключи в профиле — **секретные**; не коммитить `.mobileconfig` с реальными ключами в репозиторий (добавить в `.gitignore`).
---
## Правила On-Demand (OnDemandRules)
Массив словарей. Порядок важен: первое сработавшее правило применяется.
1. **Отключить VPN на домашнем WiFi** — правило с `Action = Disconnect` и условием по WiFi:
- `InterfaceTypeMatch` = `WiFi`;
- `SSIDMatch` = массив SSID (например `["HomeWiFi"]` или `["Netcraze-1882"]`).
- Итог: если устройство в WiFi с этим SSID → VPN отключается.
2. **Во всех остальных случаях — подключать** — правило без условий:
- `Action` = `Connect`.
- Итог: любой другой сеть (другой WiFi, сотовые данные) → VPN включается.
Пример в plist:
```xml
<key>OnDemandRules</key>
<array>
<dict>
<key>Action</key>
<string>Disconnect</string>
<key>InterfaceTypeMatch</key>
<string>WiFi</string>
<key>SSIDMatch</key>
<array>
<string>HomeWiFi</string>
</array>
</dict>
<dict>
<key>Action</key>
<string>Connect</string>
</dict>
</array>
```
Перед установкой профиля **замени** `HomeWiFi` на реальный SSID своей домашней сети.
---
## Различия macOS и iOS
- **VPNSubType:**
- macOS: `com.wireguard.macos`
- iOS: `com.wireguard.ios`
- Остальная структура (WgQuickConfig, OnDemandRules, SSIDMatch) — одинаковая. Обычно делают два файла: `wireguard-macos-ondemand.mobileconfig` и `wireguard-ios-ondemand.mobileconfig`, в каждом свой клиентский ключ и свой `Address` (например `.2/32` для iOS, `.3/32` для macOS), если на сервере заведены два разных peerа.
---
## Шаблон plist (минимум для одного туннеля)
Ниже — скелет без подстановки ключей; в `WgQuickConfig` и в `SSIDMatch` нужно подставить свои значения.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadDisplayName</key>
<string>VPN</string>
<key>PayloadType</key>
<string>com.apple.vpn.managed</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>ru.katykhin.wireguard.local-vpn.tunnel</string>
<key>PayloadUUID</key>
<string>A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D</string>
<key>UserDefinedName</key>
<string>Local VPN (WireGuard)</string>
<key>VPNType</key>
<string>VPN</string>
<key>VPNSubType</key>
<string>com.wireguard.macos</string>
<key>VendorConfig</key>
<dict>
<key>WgQuickConfig</key>
<string>[Interface]
Address = 10.10.99.3/32
PrivateKey = ВСТАВЬРИВАТНЫЙ_КЛЮЧ_КЛИЕНТА
DNS = 192.168.1.100
[Peer]
PublicKey = ВСТАВЬУБЛИЧНЫЙ_КЛЮЧ_СЕРВЕРА
Endpoint = 185.35.193.144:43123
AllowedIPs = 10.10.99.0/24, 192.168.1.0/24
PersistentKeepalive = 25
</string>
</dict>
<key>VPN</key>
<dict>
<key>RemoteAddress</key>
<string>185.35.193.144:43123</string>
<key>AuthenticationMethod</key>
<string>Password</string>
</dict>
<key>OnDemandEnabled</key>
<integer>1</integer>
<key>OnDemandRules</key>
<array>
<dict>
<key>Action</key>
<string>Disconnect</string>
<key>InterfaceTypeMatch</key>
<string>WiFi</string>
<key>SSIDMatch</key>
<array>
<string>HomeWiFi</string>
</array>
</dict>
<dict>
<key>Action</key>
<string>Connect</string>
</dict>
</array>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>WireGuard Local VPN (On-Demand)</string>
<key>PayloadIdentifier</key>
<string>ru.katykhin.wireguard.local-vpn</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>B2C3D4E5-F6A7-5B6C-9D0E-1F2A3B4C5D6E</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
```
Для **iOS** замени `VPNSubType` на `com.wireguard.ios` и в `WgQuickConfig` задай свой `Address` (например `10.10.99.2/32`) и приватный ключ того peerа, который добавлен для этого устройства в `wg0.conf`.
---
## Генерация ключей (на сервере)
На сервере WireGuard (например в CT 109):
```bash
# Приватный ключ
wg genkey | tee client.key | wg pubkey > client.pub
# Вывести для копирования в профиль
cat client.key # → PrivateKey в [Interface]
cat /etc/wireguard/server.pub # → PublicKey в [Peer]
```
Отдельные ключи для каждого устройства (например `ios.key`/`macos.key`) и соответствующие peerы в `wg0.conf` — см. [контейнер 109](../containers/container-109.md).
---
## Безопасность
- В `.mobileconfig` попадают **приватные ключи** клиента. Файл нужно хранить только на доверенных носителях и не коммитить в публичный репозиторий.
- В этом проекте файлы `*.mobileconfig` и конкретно `wireguard-macos-ondemand.mobileconfig`, `wireguard-ios-ondemand.mobileconfig` добавлены в `.gitignore`.
- Передавать профиль на устройство по защищённому каналу (AirDrop, личная почта, зашифрованный диск).
---
## Установка профиля
- **macOS:** двойной клик по `.mobileconfig` → откроются «Системные настройки» → «Профили» → установить. Туннель появится в приложении WireGuard; при необходимости один раз включите его вручную, далее On-Demand будет управлять подключением.
- **iOS:** отправить файл на устройство (AirDrop, почта, «Файлы») → открыть → «Установить» → при необходимости «Настройки» → «Основные» → «VPN и управление устройством» → установить профиль. Туннель появится в WireGuard; включите один раз вручную, затем работает On-Demand.
После смены SSID или ключей — удалить старый профиль и установить новый.
---
## Связь с другими документами
- [Контейнер 109 (WireGuard)](../containers/container-109.md) — настройка сервера, ключи, клиентские конфиги, раздел «On-Demand для iOS/macOS».
- [Архитектура](../architecture/architecture.md) — таблица контейнеров, CT 109.
- [Схема сети](network-topology.md) — место VPN в топологии, доступ к vault.katykhin.ru.

View File

@@ -0,0 +1,110 @@
#!/bin/bash
# Add vault.katykhin.ru → 192.168.1.103:8280 via NPM API + Access List (LAN + VPN only)
# Usage: NPM_EMAIL=j3tears100@gmail.com NPM_PASSWORD=xxx ./npm-add-proxy-vault.sh
# Run from host that can reach NPM, or: ssh root@192.168.1.150 "pct exec 100 -- bash -s" < scripts/npm-add-proxy-vault.sh
# (then set NPM_URL=http://127.0.0.1:81 and NPM_EMAIL/NPM_PASSWORD in env or below)
# NPM credentials: see docs/containers/container-100.md
set -e
NPM_URL="${NPM_URL:-http://192.168.1.100:81}"
API="$NPM_URL/api"
NPM_EMAIL="${NPM_EMAIL:-j3tears100@gmail.com}"
NPM_PASSWORD="${NPM_PASSWORD:-kqEUubVq02DJTS8}"
echo "1. Getting token..."
TOKEN=$(curl -s -X POST "$API/tokens" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"$NPM_EMAIL\",\"secret\":\"$NPM_PASSWORD\"}" \
| jq -r '.token // empty')
if [ -z "$TOKEN" ]; then
echo "Failed to get token"
exit 1
fi
ACCESS_LIST_ID=""
echo "2. Creating access list (LAN + VPN only)..."
# NPM backend may expect 0/1 for booleans; try minimal payload if 500
AL_PAYLOAD=$(jq -n \
--arg name "Vaultwarden LAN+VPN" \
'{ name: $name, satisfy_any: 0, pass_auth: 0 }')
AL_RESP=$(curl -s -w "\n%{http_code}" -X POST "$API/nginx/access-lists" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$AL_PAYLOAD")
AL_HTTP=$(echo "$AL_RESP" | tail -1)
AL_BODY=$(echo "$AL_RESP" | sed '$d')
if [ "$AL_HTTP" = "201" ] || [ "$AL_HTTP" = "200" ]; then
ACCESS_LIST_ID=$(echo "$AL_BODY" | jq -r '.id')
echo " Access list ID: $ACCESS_LIST_ID"
else
echo " Access list creation failed (HTTP $AL_HTTP): $AL_BODY"
echo " Creating proxy without access list; add list in NPM UI and attach to this proxy."
echo " In UI: Access Lists -> create 'Vaultwarden LAN+VPN', add Allow 192.168.1.0/24 and 10.10.99.0/24, then set it on vault.katykhin.ru proxy."
fi
# NPM API may not expose adding IP allow items via API; add 192.168.1.0/24 and 10.10.99.0/24 in UI.
echo "3. Finding certificate for vault.katykhin.ru..."
CERT_ID=$(curl -s -H "Authorization: Bearer $TOKEN" "$API/nginx/certificates" \
| jq -r '.[] | select(.domain_names[]? == "vault.katykhin.ru") | .id' | head -1)
if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then
echo " Certificate not found in NPM. Adding from custom_ssl/npm-18..."
CERT_DIR="/opt/docker/nginx-proxy/data/custom_ssl/npm-18"
if [ -f "$CERT_DIR/fullchain.pem" ] && [ -f "$CERT_DIR/privkey.pem" ]; then
CERT_ESC=$(jq -Rs . < "$CERT_DIR/fullchain.pem")
KEY_ESC=$(jq -Rs . < "$CERT_DIR/privkey.pem")
CERT_RESP=$(curl -s -w "\n%{http_code}" -X POST "$API/nginx/certificates" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"provider\":\"other\",\"domain_names\":[\"vault.katykhin.ru\"],\"nice_name\":\"vault.katykhin.ru\",\"meta\":{\"certificate\":$CERT_ESC,\"certificate_key\":$KEY_ESC}}")
CERT_HTTP=$(echo "$CERT_RESP" | tail -1)
CERT_BODY=$(echo "$CERT_RESP" | sed '$d')
if [ "$CERT_HTTP" = "201" ]; then
CERT_ID=$(echo "$CERT_BODY" | jq -r '.id')
echo " Certificate added, ID: $CERT_ID"
else
echo " Failed to add certificate (HTTP $CERT_HTTP): $CERT_BODY"
CERT_ID=""
fi
else
echo " Skip: no cert files in $CERT_DIR (run from inside CT 100)"
fi
fi
echo "4. Creating proxy host vault.katykhin.ru -> 192.168.1.103:8280..."
PROXY_PAYLOAD=$(jq -n \
--arg cert "$CERT_ID" \
--arg alid "$ACCESS_LIST_ID" \
'{
domain_names: ["vault.katykhin.ru"],
forward_host: "192.168.1.103",
forward_port: "8280",
forward_scheme: "http",
enabled: true,
allow_websocket_upgrade: true,
http2_support: true,
block_exploits: true,
certificate_id: (if $cert != "" and $cert != "null" then ($cert | tonumber) else null end),
ssl_forced: ($cert != "" and $cert != "null"),
access_list_id: (if $alid != "" and $alid != "null" then ($alid | tonumber) else 0 end)
}')
RESP=$(curl -s -w "\n%{http_code}" -X POST "$API/nginx/proxy-hosts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$PROXY_PAYLOAD")
HTTP_CODE=$(echo "$RESP" | tail -1)
BODY=$(echo "$RESP" | sed '$d')
if [ "$HTTP_CODE" = "201" ]; then
echo "Proxy host created: https://vault.katykhin.ru -> 192.168.1.103:8280 (Access List: LAN + VPN)"
echo "$BODY" | jq .
else
echo "Failed (HTTP $HTTP_CODE):"
echo "$BODY" | jq . 2>/dev/null || echo "$BODY"
exit 1
fi

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Fix proxy_host rows where domain_names is stored as string instead of JSON array (causes "e.map is not a function" in NPM UI).
# Run on CT 100 where NPM data lives: ssh root@PROXMOX "pct exec 100 -- bash -s" < scripts/npm-fix-proxy-domain-names.sh
set -e
DB="${NPM_DB:-/opt/docker/nginx-proxy/data/database.sqlite}"
if [ ! -f "$DB" ]; then
echo "Database not found: $DB"
exit 1
fi
# Fix id=10: domain_names was "[mini-lm.katykhin.ru]" (string), must be ["mini-lm.katykhin.ru"] (JSON array)
sqlite3 "$DB" "UPDATE proxy_host SET domain_names = '[\"mini-lm.katykhin.ru\"]' WHERE id = 10;"
echo "Updated proxy_host id=10 domain_names to array."
sqlite3 "$DB" "SELECT id, domain_names FROM proxy_host WHERE id = 10;"