P0 — Vision: - swapper_config_node2.yaml: add llava-13b as vision model (vision:true) /vision/models now returns non-empty list; inference verified ~3.5s - ollama.url fixed to host.docker.internal:11434 (was localhost, broken in Docker) P1 — Security: - Remove NODES_NODA1_SSH_PASSWORD from .env and docker-compose.node2-sofiia.yml - SSH ED25519 key generated, authorized on NODA1, mounted as /run/secrets/noda1_ssh_key - sofiia-console reads key via NODES_NODA1_SSH_PRIVATE_KEY env var - secrets/noda1_id_ed25519 added to .gitignore P1 — Router: - services/router/router-config.node2.yml: new node2-specific config replaces all 172.17.0.1:11434 → host.docker.internal:11434 - docker-compose.node2-sofiia.yml: mount router-config.node2.yml (not root config) P1 — Ports: - router (9102), swapper (8890), sofiia-console (8002): bind to 127.0.0.1 - gateway (9300): keep 0.0.0.0 (Telegram webhook requires public access) Artifacts: - ops/patch_node2_P0P1_20260227.md — change log - ops/validation_node2_P0P1_20260227.md — all checks PASS - ops/node2.env.example — safe env template (no secrets) - ops/security_hardening_node2.md — SSH key migration guide + firewall - ops/node2_models_pull.sh — model pull script for P0/P1 Made-with: Cursor
141 lines
5.3 KiB
Markdown
141 lines
5.3 KiB
Markdown
# NODA2 Security Hardening Guide
|
||
**MacBook Pro M4 Max — мінімальний безпечний пакет**
|
||
**Дата:** 2026-02-27
|
||
|
||
---
|
||
|
||
## 1. SSH — перехід з пароля на ключ (ED25519)
|
||
|
||
### Статус: ВИКОНАНО (2026-02-27)
|
||
|
||
#### Що зроблено
|
||
- Згенеровано ED25519 ключ: `~/.ssh/noda1_ed25519`
|
||
- Public key доданий на NODA1 (`/root/.ssh/authorized_keys`)
|
||
- Private key: `secrets/noda1_id_ed25519` (chmod 600)
|
||
- `NODES_NODA1_SSH_PASSWORD` видалено з `.env`
|
||
- `sofiia-console` читає ключ через `/run/secrets/noda1_ssh_key` (file mount)
|
||
|
||
#### Як відновити (якщо ключ загублено)
|
||
|
||
```bash
|
||
# Генерація нового ключа
|
||
ssh-keygen -t ed25519 -C "sofiia-console@noda2" -f ./secrets/noda1_id_ed25519 -N ""
|
||
chmod 600 ./secrets/noda1_id_ed25519
|
||
|
||
# Додати public key на NODA1
|
||
cat ./secrets/noda1_id_ed25519.pub | ssh root@144.76.224.179 "cat >> ~/.ssh/authorized_keys"
|
||
|
||
# Перевірка
|
||
ssh -i ./secrets/noda1_id_ed25519 root@144.76.224.179 "echo 'key auth works'"
|
||
```
|
||
|
||
#### Перевірка що пароль не використовується
|
||
```bash
|
||
# Локально (repo)
|
||
grep -r 'SSH_PASSWORD' . --include='*.yml' --include='*.yaml' --include='*.env' --include='*.py' 2>/dev/null | grep -v '.git\|venv\|__pycache__\|ops/\|docs/'
|
||
|
||
# В запущеному контейнері
|
||
docker inspect sofiia-console --format '{{range .Config.Env}}{{println .}}{{end}}' | grep SSH_PASSWORD
|
||
# Очікуємо: 0 результатів
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Port Exposure — bind на 127.0.0.1
|
||
|
||
### Статус: ВИКОНАНО (2026-02-27)
|
||
|
||
| Порт | Сервіс | Bind | Причина |
|
||
|------|--------|------|---------|
|
||
| 9102 | dagi-router-node2 | 127.0.0.1 | тільки локальне використання |
|
||
| 8890 | swapper-service-node2 | 127.0.0.1 | internal inference, не зовні |
|
||
| 8002 | sofiia-console | 127.0.0.1 | web UI тільки локально |
|
||
| 9300 | dagi-gateway-node2 | 0.0.0.0 | Telegram webhook потребує зовнішній доступ |
|
||
| 4222 | dagi-nats-node2 | 0.0.0.0 | NATS leafnode від NODA1 |
|
||
| 8222 | dagi-nats-node2 | 0.0.0.0 | NATS monitoring (обмежити якщо не потрібно) |
|
||
|
||
#### Рекомендація: обмежити NATS HTTP monitoring
|
||
Якщо 8222 не потрібен зовні:
|
||
```yaml
|
||
# docker-compose.node2-sofiia.yml — dagi-nats:
|
||
ports:
|
||
- "0.0.0.0:4222:4222" # leafnode — потрібен зовні
|
||
- "127.0.0.1:8222:8222" # monitoring — тільки локально
|
||
```
|
||
|
||
---
|
||
|
||
## 3. macOS Application Firewall
|
||
|
||
### Перевірка поточних слухаючих портів
|
||
```bash
|
||
lsof -iTCP -sTCP:LISTEN -P | grep -E '(LISTEN)' | awk '{print $9, $1}' | sort
|
||
```
|
||
|
||
### Мінімальний macOS pf firewall (Application Firewall)
|
||
|
||
Увімкнення через System Preferences → Security → Firewall:
|
||
1. Firewall → Turn On
|
||
2. Firewall Options → Block all incoming connections (крім Telegram і NATS)
|
||
|
||
Або через pfctl (більш гнучко):
|
||
|
||
```bash
|
||
# /etc/pf.conf (додати правила)
|
||
# Дозволити тільки NATS (4222) і Telegram gateway (9300) зовні
|
||
# Решту блокувати
|
||
pass in on en0 proto tcp to any port 4222 # NATS leafnode
|
||
pass in on en0 proto tcp to any port 9300 # Telegram gateway
|
||
block in on en0 proto tcp to any port 8890 # swapper — тільки localhost
|
||
block in on en0 proto tcp to any port 9102 # router — тільки localhost
|
||
block in on en0 proto tcp to any port 8002 # sofiia-console — тільки localhost
|
||
block in on en0 proto tcp to any port 8222 # nats monitoring — тільки localhost
|
||
|
||
# Apply:
|
||
sudo pfctl -f /etc/pf.conf -e
|
||
```
|
||
|
||
### Швидка перевірка що порти не відкриті назовні
|
||
```bash
|
||
# Ці команди мають повернути "refused" від зовнішньої машини:
|
||
# router, swapper, sofiia-console
|
||
nc -zv <NODA2_IP> 9102 # має бути Connection refused
|
||
nc -zv <NODA2_IP> 8890 # має бути Connection refused
|
||
nc -zv <NODA2_IP> 8002 # має бути Connection refused
|
||
|
||
# Ці мають бути доступні:
|
||
nc -zv <NODA2_IP> 4222 # NATS
|
||
nc -zv <NODA2_IP> 9300 # Gateway
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Secrets Directory
|
||
|
||
`secrets/` — локальна директорія для чутливих файлів (НЕ в git):
|
||
|
||
```
|
||
secrets/
|
||
├── noda1_id_ed25519 # SSH private key → NODA1 (chmod 600)
|
||
└── README.md # опис (ок в git)
|
||
```
|
||
|
||
Правила:
|
||
- `secrets/noda1_id_ed25519` і `secrets/*.key` — в `.gitignore`
|
||
- Всі файли `chmod 600`
|
||
- НЕ зберігати паролі в env docker-compose — тільки через file mount
|
||
|
||
---
|
||
|
||
## 5. Checklist фінальний
|
||
|
||
- [x] SSH пароль видалено з `.env`
|
||
- [x] SSH key-based auth налаштований і перевірений
|
||
- [x] `secrets/noda1_id_ed25519` в `.gitignore`
|
||
- [x] Router (9102) bind на 127.0.0.1
|
||
- [x] Swapper (8890) bind на 127.0.0.1
|
||
- [x] Sofiia-console (8002) bind на 127.0.0.1
|
||
- [ ] NATS monitoring (8222) обмежити до 127.0.0.1 (optional)
|
||
- [ ] macOS Firewall увімкнений
|
||
- [ ] pf rules додані для зовнішніх портів
|