12 KiB
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
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:
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 вручную)
- Открыть Cloudflare Zero Trust → Networks → Tunnels
- Create a tunnel → Cloudflared → назвать
kraken - На шаге "Install connector": скопировать команду вида:
Нас интересует только токен — длинная строка после
cloudflared service install eyJ...service install - Сохранить токен — вписать в эту заметку ниже
CF Tunnel Token:
<ВСТАВИТЬ СЮДА ТОКЕН ПОСЛЕ СОЗДАНИЯ ТОННЕЛЯ>
Шаг 2 — Создать docker-compose.yml через alpine
Используем alpine-контейнер с bind /:/host для записи файла:
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
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 — Проверить логи
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:
- Открыть туннель
kraken→ Edit → вкладка Public Hostname → Add a public hostname - Добавить поддомен SSH:
- Subdomain:
ssh - Domain:
kraken.qentra.top(создать заранее в DNS) - Service type: SSH
- URL:
localhost:22
- Subdomain:
- Сохранить — 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
Затем:
ssh ssh.kraken.qentra.top
cloudflaredна Eagle уже есть (используется дляzulip.qentra.top)
Шаг 6 — Создать скилл по внешнему доступу (выполняет Кракен)
После того как cloudflared запущен и SSH-поддомен работает — создать скилл чтобы знать как пользоваться внешним доступом в будущем.
Скиллы живут в /opt/data/skills/ (HDD-volume, переживают rebuild образа). Создать через terminal:
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 (локально на Кракене)
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
# Статус
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