> Статус: ✅ 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//logs?stdout=1&stderr=1&tail=50" # Удалить контейнер curl -s -X DELETE -H "X-API-Key: $KEY" \ "$BASE/api/endpoints/$EP/docker/containers/" ``` ### Временный 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]] — синхронизация хранилища.