245 lines
8.4 KiB
Markdown
245 lines
8.4 KiB
Markdown
# 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`)
|
||
|
||
---
|
||
|
||
## Связанные заметки
|
||
|
||
- [[openmediavault-rpi5]] — настройка Kraken (Docker, Hermes, структура папок)
|
||
- [[truenas-infrastructure]] — Taiga (аналогичный Portainer API подход)
|
||
- [[obsidian-sync]] — синхронизация vault
|