Files
obsidian-vault/family/how-to/kraken-portainer-access.md
T

330 lines
12 KiB
Markdown
Raw 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.
# Kraken — Portainer Access & Cloudflared SSH Tunnel
> Статус: ⏳ cloudflared pending — ждём CF Tunnel token от Alex
## Portainer
| Параметр | Значение |
|---|---|
| URL | `http://localhost:9000` |
| Auth header | `X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=` |
| Endpoint ID | уточнить через `GET /api/endpoints` (обычно `1` для local Docker) |
### Portainer API — Quick Reference
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1 # endpoint ID
# Список endpoints (узнать EP)
curl -s -H "X-API-Key: $KEY" $BASE/api/endpoints | jq '[.[].Id]'
# Список контейнеров
curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json?all=true" | jq '[.[] | {name: .Names[0], status: .Status, image: .Image}]'
# Создать контейнер
curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
"$BASE/api/endpoints/$EP/docker/containers/create?name=<NAME>" \
-d '<JSON>'
# Запустить контейнер
curl -s -X POST -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/<ID>/start"
# Логи
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/<ID>/logs?stdout=1&stderr=1&tail=50"
# Удалить контейнер
curl -s -X DELETE -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/<ID>"
```
### Временный Alpine-контейнер (запустить команду на хосте)
Для разовых задач (записать файл, проверить путь и т.д.) — alpine с bind `/:/host`:
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
# 1. Создать
ID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
"$BASE/api/endpoints/$EP/docker/containers/create?name=alpine-tmp-$$" \
-d '{
"Image": "alpine",
"Cmd": ["sh", "-c", "echo hello from host > /host/tmp/test.txt && echo DONE"],
"HostConfig": {
"Binds": ["/:/host"],
"AutoRemove": false
}
}' | jq -r '.Id')
# 2. Запустить
curl -s -X POST -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID/start"
# 3. Подождать завершения (1-2 сек) и забрать логи
sleep 2
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID/logs?stdout=1&stderr=1"
# 4. Удалить
curl -s -X DELETE -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID"
```
> ⚠️ `AutoRemove: false` — обязательно, иначе контейнер удалится до того как получишь логи
> ⚠️ Используй `alpine`, не `alpine/git` и подобные — у них есть ENTRYPOINT, `sh -c` не сработает
---
## Cloudflared — деплой
### Шаг 1 — Получить CF Tunnel token (делает Alex вручную)
1. Открыть [Cloudflare Zero Trust → Networks → Tunnels](https://one.dash.cloudflare.com/)
2. **Create a tunnel** → Cloudflared → назвать `kraken`
3. На шаге "Install connector": скопировать команду вида:
```
cloudflared service install eyJ...
```
Нас интересует только токен — длинная строка после `service install`
4. Сохранить токен — вписать в эту заметку ниже
**CF Tunnel Token:**
```
<ВСТАВИТЬ СЮДА ТОКЕН ПОСЛЕ СОЗДАНИЯ ТОННЕЛЯ>
```
### Шаг 2 — Создать docker-compose.yml через alpine
Используем alpine-контейнер с bind `/:/host` для записи файла:
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
TOKEN="<CF_TUNNEL_TOKEN>"
COMPOSE_B64=$(echo "services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: [\"tunnel\", \"--no-autoupdate\", \"run\"]
environment:
- TUNNEL_TOKEN=${TOKEN}
network_mode: host" | base64 | tr -d '\n')
ID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
"$BASE/api/endpoints/$EP/docker/containers/create?name=alpine-setup-$$" \
-d "{
\"Image\": \"alpine\",
\"Cmd\": [\"sh\", \"-c\", \"mkdir -p /host/home/kraken/cloudflared && echo ${COMPOSE_B64} | base64 -d > /host/home/kraken/cloudflared/docker-compose.yml && echo DONE\"],
\"HostConfig\": {
\"Binds\": [\"/:/host\"],
\"AutoRemove\": false
}
}" | jq -r '.Id')
curl -s -X POST -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID/start"
sleep 2
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID/logs?stdout=1&stderr=1"
curl -s -X DELETE -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$ID"
```
### Шаг 3 — Поднять cloudflared контейнер через Portainer API
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
TOKEN="<CF_TUNNEL_TOKEN>"
# Создать контейнер
CID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
"$BASE/api/endpoints/$EP/docker/containers/create?name=cloudflared" \
-d "{
\"Image\": \"cloudflare/cloudflared:latest\",
\"Cmd\": [\"tunnel\", \"--no-autoupdate\", \"run\"],
\"Env\": [\"TUNNEL_TOKEN=${TOKEN}\"],
\"HostConfig\": {
\"RestartPolicy\": {\"Name\": \"unless-stopped\"},
\"NetworkMode\": \"host\"
}
}" | jq -r '.Id')
# Запустить
curl -s -X POST -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$CID/start"
echo "Container ID: $CID"
```
### Шаг 4 — Проверить логи
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
CID=$(curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json?all=true" | jq -r '.[] | select(.Names[] | contains("cloudflared")) | .Id')
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$CID/logs?stdout=1&stderr=1&tail=30"
```
Ожидаем в логах:
```
INF Registered tunnel connection connIndex=0 ... protocol=http2
INF Connection ... registered connIndex=0
INF Connection ... registered connIndex=1
...
```
Если видишь все 4 соединения — туннель активен.
### Шаг 5 — Обновить статус в этой заметке
Заменить `⏳ cloudflared pending` в заголовке на `✅ cloudflared запущен`.
---
## Что делает Alex после запуска (вручную в CF Zero Trust)
После того как cloudflared запущен и туннель появился как **Active** в Zero Trust:
1. Открыть туннель `kraken` → **Edit** → вкладка **Public Hostname** → **Add a public hostname**
2. Добавить поддомен SSH:
- Subdomain: `ssh`
- Domain: `kraken.qentra.top` (создать заранее в DNS)
- Service type: **SSH**
- URL: `localhost:22`
3. Сохранить — DNS запись создаётся автоматически
Повторить для других сервисов если нужно:
- `omv.kraken.qentra.top` → HTTP → `localhost:80` (OMV Web UI)
- `portainer.kraken.qentra.top` → HTTP → `localhost:9000`
> ⚠️ Аналогично тому как настраивался `zulip.qentra.top` на Eagle:
> токен получается при создании тоннеля в Zero Trust → вставляется в `TUNNEL_TOKEN`
---
## SSH-доступ через Cloudflare Tunnel (с Eagle)
После настройки поддомена добавить в `~/.ssh/config` на Eagle:
```
Host ssh.kraken.qentra.top
ProxyCommand cloudflared access ssh --hostname %h
User kraken
IdentityFile ~/.ssh/id_rsa
StrictHostKeyChecking no
```
Затем:
```bash
ssh ssh.kraken.qentra.top
```
> `cloudflared` на Eagle уже есть (используется для `zulip.qentra.top`)
---
## Шаг 6 — Создать скилл по внешнему доступу (выполняет Кракен)
После того как cloudflared запущен и SSH-поддомен работает — создать скилл чтобы знать как пользоваться внешним доступом в будущем.
Скиллы живут в `/opt/data/skills/` (HDD-volume, переживают rebuild образа). Создать через terminal:
```bash
mkdir -p /opt/data/skills/devops/kraken-external-access
cat > /opt/data/skills/devops/kraken-external-access/SKILL.md << 'EOF'
---
name: kraken-external-access
category: devops
description: Внешний доступ к Kraken через Cloudflare Tunnel — SSH с Eagle, Portainer API, управление cloudflared контейнером.
triggers:
- "доступ к кракену снаружи"
- "ssh к кракену с орла"
- "управление docker на кракене удалённо"
- "cloudflared крякен"
---
# Kraken — External Access via Cloudflare Tunnel
## SSH с Eagle
```bash
# ~/.ssh/config на Eagle (уже настроен):
# Host ssh.kraken.qentra.top
# ProxyCommand cloudflared access ssh --hostname %h
# User kraken
# IdentityFile ~/.ssh/id_rsa
ssh ssh.kraken.qentra.top
```
## Portainer API (локально на Кракене)
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
# Список контейнеров
curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json?all=true" \
| jq '[.[] | {name: .Names[0], status: .Status}]'
```
## Управление cloudflared
```bash
# Статус
curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json" \
| jq '.[] | select(.Names[] | contains("cloudflared")) | {status: .Status}'
# Логи (последние 30 строк)
CID=$(curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json" \
| jq -r '.[] | select(.Names[] | contains("cloudflared")) | .Id')
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$CID/logs?stdout=1&stderr=1&tail=30"
# Рестарт
curl -s -X POST -H "X-API-Key: $KEY" \
"$BASE/api/endpoints/$EP/docker/containers/$CID/restart"
```
## Питфолы
- cloudflared запускается с `network_mode: host` — видит все локальные порты Кракена
- Если туннель отвалился — проверить логи: ищи `ERR` и `failed to`
- `restart: unless-stopped` — при падении поднимается автоматически
- Токен туннеля хранится в env `TUNNEL_TOKEN` контейнера — не менять без пересоздания контейнера
- CF Zero Trust tunnel: проверить статус на https://one.dash.cloudflare.com/ → Networks → Tunnels → kraken
## Связанные заметки в vault
- `family/how-to/kraken-portainer-access.md` — полная инструкция по деплою
- `family/how-to/openmediavault-rpi5.md` — структура Docker на Кракене
EOF
echo "Скилл создан: /opt/data/skills/devops/kraken-external-access/SKILL.md"
```
> ⚠️ `/opt/data/` — это HDD-backed volume, скилл переживёт `docker stop/start` и даже rebuild образа
> ⚠️ Путь внутри контейнера — `/opt/data/skills/`, на хосте это `/srv/dev-disk-by-uuid-49e8f586.../docker/hermes/config/skills/`
---
## Связанные заметки
- [[openmediavault-rpi5]] — настройка Kraken (Docker, Hermes, структура папок)
- [[truenas-infrastructure]] — Taiga (аналогичный Portainer API подход)
- [[obsidian-sync]] — синхронизация vault