Files
microdao-daarion/ops/security_hardening_node2.md
Apple 7b8499dd8a node2: P0 vision restore + P1 security hardening + node-specific router config
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
2026-02-27 01:27:38 -08:00

141 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 додані для зовнішніх портів