298 lines
11 KiB
Markdown
Executable File
298 lines
11 KiB
Markdown
Executable File
> Статус: ✅ cloudflared запущен (прямой токен)
|
||
|
||
## Portainer
|
||
|
||
| Параметр | Значение |
|
||
|---|---|
|
||
| URL | `http://localhost:9000` |
|
||
| Auth header | `X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=` |
|
||
| Endpoint ID | уточнить: `curl -s -H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" http://localhost:9000/api/endpoints | jq '[.[].Id]'` (обычно `1`) |
|
||
|
||
### Portainer API — Quick Reference
|
||
|
||
```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, image: .Image}]'
|
||
|
||
# Логи контейнера
|
||
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
|
||
|
||
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", "YOUR_COMMAND_HERE && 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"
|
||
```
|
||
|
||
> ⚠️ `AutoRemove: false` — обязательно, иначе контейнер удалится до получения логов
|
||
> ⚠️ Использовать `alpine`, не `alpine/git` — у git-образа свой ENTRYPOINT
|
||
|
||
---
|
||
|
||
## Cloudflared — деплой (выполняет Кракен)
|
||
|
||
CF Tunnel уже создан Alex'ом (`kraken` в Zero Trust), hostname настроен: `kraken.qentra.top → SSH → localhost:22`.
|
||
|
||
**CF Tunnel Token:**
|
||
```
|
||
eyJhIjoiZjE...TnpFNU5USTNNREl4WkRVeCJ9
|
||
```
|
||
|
||
### Шаг 1 — Убедиться что EP = 1
|
||
|
||
```bash
|
||
curl -s -H "X-API-Key: ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g=" \
|
||
http://localhost:9000/api/endpoints | jq '[.[].Id]'
|
||
```
|
||
|
||
Если не `1` — запомнить реальный ID и использовать его вместо `1` в шагах ниже.
|
||
|
||
### Шаг 2 — Создать и запустить cloudflared контейнер
|
||
|
||
```bash
|
||
BASE=http://localhost:9000
|
||
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
|
||
EP=1
|
||
TOKEN="eyJhIjoiZjE0YTM3NWQ3NmU1ZTU4M2IzNjZhMDJhYjUyNDZiNDciLCJ0IjoiMmRkZDJiMjgtNjYwYy00NjQ0LTk1Y2ItY2Y1ZTQzNTBhMTA1IiwicyI6Ik5HVTRZalZtTURVdFptSm1OeTAwWVdGakxUaGlaamd0WmpFMFlUazVOekt3WTJWaCJ9"
|
||
|
||
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')
|
||
|
||
echo "Container ID: $CID"
|
||
|
||
curl -s -X POST -H "X-API-Key: $KEY" \
|
||
"$BASE/api/endpoints/$EP/docker/containers/$CID/start"
|
||
|
||
echo "Started."
|
||
```
|
||
|
||
### Шаг 3 — Проверить логи
|
||
|
||
```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" \
|
||
| 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" \
|
||
| strings
|
||
```
|
||
|
||
Ожидаем:
|
||
```
|
||
INF Registered tunnel connection connIndex=0 ... protocol=http2
|
||
INF Registered tunnel connection connIndex=1 ...
|
||
INF Registered tunnel connection connIndex=2 ...
|
||
INF Registered tunnel connection connIndex=3 ...
|
||
```
|
||
|
||
Все 4 соединения = туннель активен. Если видишь `ERR` — проверить токен.
|
||
|
||
### Шаг 4 — Сохранить compose-файл (опционально, для удобства в будущем)
|
||
|
||
```bash
|
||
BASE=http://localhost:9000
|
||
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
|
||
EP=1
|
||
TOKEN="eyJhIjoiZjE0YTM3NWQ3NmU1ZTU4M2IzNjZhMDJhYjUyNDZiNDciLCJ0IjoiMmRkZDJiMjgtNjYwYy00NjQ0LTk1Y2ItY2Y1ZTQzNTBhMTA1IiwicyI6Ik5HVTRZalZtTURVdFptSm1OeTAwWVdGakxUaGlaamd0WmpFMFlUazVOekt3WTJWaCJ9"
|
||
|
||
COMPOSE="services:\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=$TOKEN\n network_mode: host"
|
||
|
||
ID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
|
||
"$BASE/api/endpoints/$EP/docker/containers/create?name=alpine-cf-setup" \
|
||
-d "{
|
||
\"Image\": \"alpine\",
|
||
\"Cmd\": [\"sh\", \"-c\", \"mkdir -p /host/home/kraken/cloudflared && printf '$COMPOSE' > /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"
|
||
```
|
||
|
||
### Шаг 5 — Создать скилл по внешнему доступу
|
||
|
||
```bash
|
||
mkdir -p /opt/data/skills/devops/kraken-external-access
|
||
cat > /opt/data/skills/devops/kraken-external-access/SKILL.md << 'SKILLEOF'
|
||
---
|
||
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 (уже настроено в ~/.ssh/config на Eagle)
|
||
|
||
```bash
|
||
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
|
||
# Статус + логи
|
||
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" | strings
|
||
|
||
# Рестарт
|
||
curl -s -X POST -H "X-API-Key: $KEY" \
|
||
"$BASE/api/endpoints/$EP/docker/containers/$CID/restart"
|
||
```
|
||
|
||
## Питфолы
|
||
|
||
- `network_mode: host` — cloudflared видит все локальные порты Кракена напрямую
|
||
- `restart: unless-stopped` — при падении поднимается автоматически
|
||
- Токен в env `TUNNEL_TOKEN` — не менять без пересоздания контейнера
|
||
- Статус туннеля: https://one.dash.cloudflare.com/ → Networks → Tunnels → kraken
|
||
- Если туннель не поднимается — искать `ERR` в логах cloudflared
|
||
|
||
## Связанные заметки
|
||
|
||
- `family/how-to/kraken-portainer-access.md` — полная инструкция по деплою
|
||
- `family/how-to/openmediavault-rpi5.md` — структура Docker на Кракене
|
||
SKILLEOF
|
||
echo "Скилл создан."
|
||
```
|
||
|
||
> ⚠️ `/opt/data/` — HDD-volume, скилл переживёт rebuild образа
|
||
|
||
|
||
### Временный запуск cloudflared с прямым токеном (только для диагностики)
|
||
|
||
**Внимание:** Этот метод передает токен напрямую в `Cmd` контейнера. Это **небезопасно** для постоянного использования, но использовался для устранения проблем с монтированием файлов `credentials.json` и `config.yaml`.
|
||
|
||
Контейнер `cloudflared-access-debug-direct-token` с ID `8d9b3f7cec6fe6e1622f3a4c920733e9c322dfc936165b005bc22a0348c489a4` запущен с прямым токеном.
|
||
|
||
```bash
|
||
BASE=http://localhost:9000
|
||
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
|
||
EP=3
|
||
TOKEN="eyJhIjoiZjE0YTM3NWQ3NmU1ZTU4M2IzNjZhMDJhYjUyNDZiNDciLCJ0IjoiMGVlNWIxNmQtYTNmMC00ZjEzLWJjMzQtMmE4ODFhMGE0NDBhIiwicyI6Ik5qYzNZekV5T1dNdE56TTFOUzAwWkdVM0xUa3dNakl0TnpFNU5USTNNREl4WkRVeCJ9" # Временный токен
|
||
|
||
CID=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
|
||
"$BASE/api/endpoints/$EP/docker/containers/create?name=cloudflared-access-debug-direct-token" \
|
||
-d "{\"Image\": \"cloudflare/cloudflared:latest\", \"Cmd\": [\"tunnel\", \"run\", \"--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"
|
||
```
|
||
|
||
|
||
|
||
Заменить первую строку `> Статус: ⏳ cloudflared pending deploy` на `> Статус: ✅ cloudflared запущен (прямой токен)`.
|
||
|
||
---
|
||
|
||
## SSH-доступ с Eagle
|
||
|
||
Reverse SSH tunnel через VPS (порт 2223). Работает без CF Access Application.
|
||
|
||
`~/.ssh/config` на Eagle:
|
||
```
|
||
Host kraken.qentra.top
|
||
HostName 91.207.28.205
|
||
Port 2223
|
||
User kraken
|
||
IdentityFile ~/.ssh/id_rsa
|
||
StrictHostKeyChecking no
|
||
```
|
||
|
||
Подключение: `ssh kraken.qentra.top`
|
||
|
||
### Как это работает
|
||
|
||
Кракен держит постоянный reverse SSH tunnel на VPS `91.207.28.205`:
|
||
- systemd-сервис `kraken-tunnel.service` (autossh, autostart, restart always)
|
||
- VPS слушает порт 2223 → перенаправляет на `localhost:22` Кракена
|
||
- Ключ: `/home/kraken/.ssh/id_ed25519`
|
||
|
||
Управление туннелем на Кракене:
|
||
```bash
|
||
sudo systemctl status kraken-tunnel
|
||
sudo systemctl restart kraken-tunnel
|
||
sudo journalctl -u kraken-tunnel -n 20
|
||
```
|
||
|
||
Проверить порт на VPS:
|
||
```bash
|
||
ssh root@91.207.28.205 "ss -tlnp | grep 2223"
|
||
```
|
||
|
||
---
|
||
|
||
## Связанные заметки
|
||
|
||
- [[openmediavault-rpi5]] — настройка Kraken (Docker, Hermes, структура папок)
|
||
- [[truenas-infrastructure]] — Taiga (аналогичный Portainer API подход)
|
||
- [[obsidian-sync]] — синхронизация хранилища.
|