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

5.3 KiB
Raw Permalink Blame History

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)

Як відновити (якщо ключ загублено)

# Генерація нового ключа
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'"

Перевірка що пароль не використовується

# Локально (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 не потрібен зовні:

# 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

Перевірка поточних слухаючих портів

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 (більш гнучко):

# /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

Швидка перевірка що порти не відкриті назовні

# Ці команди мають повернути "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 фінальний

  • SSH пароль видалено з .env
  • SSH key-based auth налаштований і перевірений
  • secrets/noda1_id_ed25519 в .gitignore
  • Router (9102) bind на 127.0.0.1
  • Swapper (8890) bind на 127.0.0.1
  • Sofiia-console (8002) bind на 127.0.0.1
  • NATS monitoring (8222) обмежити до 127.0.0.1 (optional)
  • macOS Firewall увімкнений
  • pf rules додані для зовнішніх портів