[2026-05-13] kraken-portainer-access: fix hallucinated token, add proper CF tunnel instructions

This commit is contained in:
Alexey Martemyanov
2026-05-13 14:07:06 +06:00
parent 36df0eac22
commit 6d69f258f3
+177 -101
View File
@@ -1,168 +1,244 @@
# Kraken — Portainer Access & Cloudflared SSH Tunnel # Kraken — Portainer Access & Cloudflared SSH Tunnel
> Статус: ⏳ cloudflared pending deploy (см. инструкции ниже) > Статус: ⏳ cloudflared pending — ждём CF Tunnel token от Alex
## Portainer ## Portainer
| Параметр | Значение | | Параметр | Значение |
|---|---| |---|---|
| URL (локальный) | http://localhost:9000 | | URL | `http://localhost:9000` |
| Auth header | `X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=` | | Auth header | `X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=` |
| Endpoint ID | 1 (local Docker) | | Endpoint ID | уточнить через `GET /api/endpoints` (обычно `1` для local Docker) |
## Cloudflare Tunnel Token ### 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>"
``` ```
eyJhIjoiODFlOGYwYThmMzg5ZTllNzg3NGFmMTE3NGQxYzIxOTIiLCJ0IjoiNjQxMGI0YTYtZWUyMS00MTczLWJhMzItMTcwOGY1MjNlNDMyIiwicyI6Ik9XWmpNVEkxWVRFdE9EWXpZaTAwWlRjNExXSTBaVFl0TkdNeU16Z3daV0pqTW1RMyJ9
### Временный 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` не сработает
--- ---
## Задача для Кракен Hermes ## Cloudflared — деплой
Поднять `cloudflare/cloudflared` в Docker через Portainer API. После запуска Alex добавит SSH-поддомен в Cloudflare Zero Trust (вручную, как делал для `zulip.qentra.top`). ### Шаг 1 — Получить CF Tunnel token (делает Alex вручную)
### Шаг 1 — Проверить Portainer и Docker endpoint 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. Сохранить токен — вписать в эту заметку ниже
```bash **CF Tunnel Token:**
# Получить список endpoints (endpoint ID нужен для всех последующих запросов) ```
curl -s -H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \ <ВСТАВИТЬ СЮДА ТОКЕН ПОСЛЕ СОЗДАНИЯ ТОННЕЛЯ>
http://localhost:9000/api/endpoints | jq '.[].Id'
# Ожидаем: 1
``` ```
### Шаг 2 — Создать контейнер cloudflared через Portainer API ### Шаг 2 — Создать docker-compose.yml через alpine
Используем alpine-контейнер с bind `/:/host` для записи файла:
```bash ```bash
# Создать контейнер (не запускать) BASE=http://localhost:9000
curl -s -X POST \ KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
-H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \ EP=1
-H "Content-Type: application/json" \ TOKEN="<CF_TUNNEL_TOKEN>"
"http://localhost:9000/api/endpoints/1/docker/containers/create?name=cloudflared" \
-d '{ COMPOSE_B64=$(echo "services:
"Image": "cloudflare/cloudflared:latest", cloudflared:
"Cmd": ["tunnel", "--no-autoupdate", "run"], image: cloudflare/cloudflared:latest
"Env": [ container_name: cloudflared
"TUNNEL_TOKEN=eyJhIjoiODFlOGYwYThmMzg5ZTllNzg3NGFmMTE3NGQxYzIxOTIiLCJ0IjoiNjQxMGI0YTYtZWUyMS00MTczLWJhMzItMTcwOGY1MjNlNDMyIiwicyI6Ik9XWmpNVEkxWVRFdE9EWXpZaTAwWlRjNExXSTBaVFl0TkdNeU16Z3daV0pqTW1RMyJ9" restart: unless-stopped
], command: [\"tunnel\", \"--no-autoupdate\", \"run\"]
"HostConfig": { environment:
"RestartPolicy": {"Name": "unless-stopped"}, - TUNNEL_TOKEN=${TOKEN}
"NetworkMode": "host" 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' }" | jq -r '.Id')
# Сохранить полученный container 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 — Запустить контейнер ### Шаг 3 — Поднять cloudflared контейнер через Portainer API
```bash ```bash
CONTAINER_ID="<id из шага 2>" BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=1
TOKEN="<CF_TUNNEL_TOKEN>"
curl -s -X POST \ # Создать контейнер
-H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \ CID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
"http://localhost:9000/api/endpoints/1/docker/containers/${CONTAINER_ID}/start" "$BASE/api/endpoints/$EP/docker/containers/create?name=cloudflared" \
# Ожидаем: пустой ответ или 204 No Content = OK -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 — Проверить логи ### Шаг 4 — Проверить логи
```bash ```bash
curl -s \ BASE=http://localhost:9000
-H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \ KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
"http://localhost:9000/api/endpoints/1/docker/containers/${CONTAINER_ID}/logs?stdout=true&stderr=true&tail=30" \ EP=1
| strings CID=$(curl -s -H "X-API-Key: $KEY" "$BASE/api/endpoints/$EP/docker/containers/json?all=true" | jq -r '.[] | select(.Names[] | contains("cloudflared")) | .Id')
# Ожидаем в логах:
# INF Registered tunnel connection connIndex=0 ... protocol=http2 curl -s -H "X-API-Key: $KEY" \
# INF Connection ... registered connIndex=0..3 "$BASE/api/endpoints/$EP/docker/containers/$CID/logs?stdout=1&stderr=1&tail=30"
``` ```
### Шаг 5 — Создать docker-compose.yml для постоянного управления Ожидаем в логах:
```
Через Alpine-контейнер с доступом к хосту: INF Registered tunnel connection connIndex=0 ... protocol=http2
INF Connection ... registered connIndex=0
```bash INF Connection ... registered connIndex=1
# Создать alpine контейнер, который запишет compose-файл на хост ...
curl -s -X POST \
-H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \
-H "Content-Type: application/json" \
"http://localhost:9000/api/endpoints/1/docker/containers/create?name=alpine-setup" \
-d '{
"Image": "alpine:latest",
"Cmd": ["sh", "-c", "mkdir -p /host/home/kraken/cloudflared && cat > /host/home/kraken/cloudflared/docker-compose.yml << '"'"'EOF'"'"'\nservices:\n cloudflared:\n image: cloudflare/cloudflared:latest\n container_name: cloudflared\n restart: unless-stopped\n command: [\"tunnel\", \"--no-autoupdate\", \"run\"]\n environment:\n - TUNNEL_TOKEN=eyJhIjoiODFlOGYwYThmMzg5ZTllNzg3NGFmMTE3NGQxYzIxOTIiLCJ0IjoiNjQxMGI0YTYtZWUyMS00MTczLWJhMzItMTcwOGY1MjNlNDMyIiwicyI6Ik9XWmpNVEkxWVRFdE9EWXpZaTAwWlRjNExXSTBaVFl0TkdNeU16Z3daV0pqTW1RMyJ9\n network_mode: host\nEOF\necho DONE"],
"HostConfig": {
"Binds": ["/:/host"],
"AutoRemove": true
}
}' | jq -r '.Id'
# Запустить alpine контейнер
curl -s -X POST \
-H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \
"http://localhost:9000/api/endpoints/1/docker/containers/<alpine-id>/start"
``` ```
### Шаг 6 — Обновить Obsidian Если видишь все 4 соединения — туннель активен.
После успешного запуска cloudflaredобновить статус в этой заметке: `Статус: ⏳``Статус: ✅ cloudflared запущен`. ### Шаг 5Обновить статус в этой заметке
Заменить `⏳ cloudflared pending` в заголовке на `✅ cloudflared запущен`.
--- ---
## Что делает Alex вручную (после запуска) ## Что делает Alex после запуска (вручную в CF Zero Trust)
1. Открыть [Cloudflare Zero Trust → Networks → Tunnels](https://one.dash.cloudflare.com/) После того как cloudflared запущен и туннель появился как **Active** в Zero Trust:
2. Найти туннель (должен появиться как активный после запуска контейнера)
3. Нажать **Edit** → вкладка **Public Hostname****Add a public hostname**: 1. Открыть туннель `kraken` → **Edit** → вкладка **Public Hostname** → **Add a public hostname**
2. Добавить поддомен SSH:
- Subdomain: `ssh` - Subdomain: `ssh`
- Domain: `kraken.qentra.top` (или другой по вкусу) - Domain: `kraken.qentra.top` (создать заранее в DNS)
- Service type: **SSH** - Service type: **SSH**
- URL: `localhost:22` - URL: `localhost:22`
4. Сохранить DNS запись создаётся автоматически 3. Сохранить DNS запись создаётся автоматически
> Повторить для других сервисов если нужно: Повторить для других сервисов если нужно:
> - `omv.kraken.qentra.top` → HTTP → `localhost:80` - `omv.kraken.qentra.top` → HTTP → `localhost:80` (OMV Web UI)
> - `portainer.kraken.qentra.top` → HTTP → `localhost:9000` - `portainer.kraken.qentra.top` → HTTP → `localhost:9000`
> ⚠️ Аналогично тому как настраивался `zulip.qentra.top` на Eagle:
> токен получается при создании тоннеля в Zero Trust → вставляется в `TUNNEL_TOKEN`
--- ---
## SSH-доступ через Cloudflare Tunnel (с любой машины) ## SSH-доступ через Cloudflare Tunnel (с Eagle)
После настройки поддомена Alex создаёт скилл на Eagle для доступа к Кракену через тоннель (аналог Таига через alpine+portainer): После настройки поддомена добавить в `~/.ssh/config` на Eagle:
```bash ```
# Один раз настроить ~/.ssh/config на Eagle:
Host ssh.kraken.qentra.top Host ssh.kraken.qentra.top
ProxyCommand cloudflared access ssh --hostname %h ProxyCommand cloudflared access ssh --hostname %h
User kraken User kraken
IdentityFile ~/.ssh/id_rsa IdentityFile ~/.ssh/id_rsa
StrictHostKeyChecking no StrictHostKeyChecking no
```
# Подключиться: Затем:
```bash
ssh ssh.kraken.qentra.top ssh ssh.kraken.qentra.top
``` ```
> ⚠️ `cloudflared` должен быть установлен на клиентской машине (Eagle). > `cloudflared` на Eagle уже есть (используется для `zulip.qentra.top`)
> На Eagle уже есть — он используется для `zulip.qentra.top`.
---
## Проверка что туннель работает
```bash
# На Кракене: посмотреть статус контейнера
docker ps | grep cloudflared
# Логи cloudflared
docker logs cloudflared --tail 20
# С Eagle (после настройки DNS): тест SSH
ssh -o ProxyCommand="cloudflared access ssh --hostname ssh.kraken.qentra.top" \
-i ~/.ssh/id_rsa kraken@ssh.kraken.qentra.top whoami
```
--- ---
## Связанные заметки ## Связанные заметки
- [[openmediavault-rpi5]] — настройка Kraken (Docker, Hermes, структура папок) - [[openmediavault-rpi5]] — настройка Kraken (Docker, Hermes, структура папок)
- [[truenas-infrastructure]] — Taiga (аналогичный подход через Portainer) - [[truenas-infrastructure]] — Taiga (аналогичный Portainer API подход)
- [[obsidian-sync]] — синхронизация vault - [[obsidian-sync]] — синхронизация vault