Add self-check via control-api (self_check.methods)
control-api is hosted outside the cloud and validators reach it directly,
so it sees the floating IP as the connection's source address. New open
route GET /api/v1/agents/{id}/observed-ip returns that address (taken only
from the TCP peer; forwarding headers are ignored so a validator cannot
forge it).
The agent gets self_check.methods, a priority-ordered list of ip_echo
(unchanged) and control_api; the default stays [ip_echo]. The self-check
passes when any method confirms the address; the next method is tried on
no answer and on a mismatch. Each method has its own timeout so a hung
first method cannot starve the fallback, and control_api uses a new TCP
connection per call (a connection opened before the floating IP was
attached would keep reporting the old address).
Also: docker agent template/env, example config, docs, plan in
docs/changes, e2e script switch E2E_SELF_CHECK_METHODS, rebuilt
bin/control-api and bin/validator-agent.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
146259cabb
commit
abbee9a08a
23 files changed
+765
-35
No files matched your search
+32
-4
@@ -41,10 +41,10 @@ JSON, базовый префикс прикладных методов — `/ap
|
||||
|---|---|---|
|
||||
| **admin** | `CONTROL_API_ADMIN_TOKEN` | все `/api/v1/admin/*` (очередь, реестр, автоцикл, `config/*`) |
|
||||
| **agent** | `CONTROL_API_AGENT_TOKEN` | запись результатов: `POST /agents/{id}/self-check`, `/events`, `/results`, `/complete` и `POST /probers/{site_id}/results` |
|
||||
| **открыто** | — | `GET /healthz`; `POST /agents/register`, `POST /agents/{id}/heartbeat`, `GET /agents/{id}/assignment`; `POST /probers/register`, `POST /probers/{site_id}/heartbeat`, `GET /probers/{site_id}/assignments` |
|
||||
| **открыто** | — | `GET /healthz`; `POST /agents/register`, `POST /agents/{id}/heartbeat`, `GET /agents/{id}/assignment`, `GET /agents/{id}/observed-ip`; `POST /probers/register`, `POST /probers/{site_id}/heartbeat`, `GET /probers/{site_id}/assignments` |
|
||||
|
||||
- Токены разные: токен администратора **не** подходит для методов агентов, и наоборот.
|
||||
- Валидатор и пробер могут без токена зарегистрироваться, слать heartbeat и забирать задание (настройку); отправка результатов без токена агентов — `401`.
|
||||
- Валидатор и пробер могут без токена зарегистрироваться, слать heartbeat и забирать задание (настройку); валидатор также может спросить, с какого адреса его видит control-api (`observed-ip`); отправка результатов без токена агентов — `401`.
|
||||
- Имена переменных меняются в секции `auth` конфига control-api (`admin_token_env`, `agent_token_env`); сами значения в YAML не хранятся.
|
||||
- Ответ при отказе: `401 {"error": "unauthorized"}` с заголовком `WWW-Authenticate: Bearer`.
|
||||
- Токен не задан (пустая переменная) — уровень открыт; в логе control-api при старте предупреждение. Токены нужно генерировать случайными: `openssl rand -hex 32`.
|
||||
@@ -145,6 +145,30 @@ curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://<control-api>:8080/api/v1
|
||||
`check_config` — уже развёрнутая конфигурация проверок (тип + список
|
||||
целей), агенту не нужно самому сопоставлять группы целей.
|
||||
|
||||
### `GET /api/v1/agents/{id}/observed-ip`
|
||||
|
||||
С какого адреса control-api видит соединение валидатора. Используется
|
||||
self-check способом `control_api` (`self_check.methods` в
|
||||
`validator-agent.yaml`) как альтернатива внешнему IP-echo сервису. Уровень
|
||||
доступа — открыто: отдаётся только адрес самого вызывающего.
|
||||
|
||||
Ответ `200`:
|
||||
```json
|
||||
{"ip": "203.0.113.10", "source": "remote_addr"}
|
||||
```
|
||||
|
||||
- Адрес берётся только из адреса TCP-соединения (`RemoteAddr`), приведённого
|
||||
к каноничному виду (`::ffff:1.2.3.4` → `1.2.3.4`). Заголовки
|
||||
`X-Forwarded-For` / `X-Real-IP` **не учитываются**: иначе валидатор мог бы
|
||||
подделать адрес и пройти проверку. Метод рассчитан на прямое подключение
|
||||
без обратного прокси.
|
||||
- `404`, если `validator_id` не зарегистрирован.
|
||||
- Способ корректен, только если соединение выходит через внешнюю сеть
|
||||
(SNAT Floating IP). Если control-api достижим из облака по внутренней
|
||||
сети, он увидит приватный адрес валидатора. Если порт control-api
|
||||
опубликован через Docker, проверьте, что ручка показывает внешний адрес
|
||||
клиента, а не адрес шлюза Docker.
|
||||
|
||||
### `POST /api/v1/agents/{id}/self-check`
|
||||
|
||||
Отчёт о результате self-check — подтверждение, что исходящий трафик
|
||||
@@ -152,7 +176,11 @@ curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://<control-api>:8080/api/v1
|
||||
определяет это **сам**, обращаясь к внешнему (снаружи облака) IP-echo
|
||||
сервису (`self_check.ip_echo_urls` в `validator-agent.yaml`, например
|
||||
`api.ipify.org`) и сравнивая ответ с `ip_address` из задания — control-api
|
||||
в этом определении не участвует. Важно, что ресурс должен быть именно
|
||||
в этом определении не участвует. Дополнительно можно включить способ
|
||||
`control_api` (`self_check.methods`): агент спрашивает у control-api через
|
||||
`GET /agents/{id}/observed-ip`, с какого адреса тот его видит. Способы
|
||||
пробуются по приоритету, достаточно подтверждения любым из них; без
|
||||
настройки работает только IP-echo. Важно, что ресурс должен быть именно
|
||||
внешним: OpenStack применяет SNAT через Floating IP только к трафику,
|
||||
уходящему через внешнюю сеть, поэтому обращение к чему-либо внутри
|
||||
проекта (в том числе к самому control-api, если он в той же внутренней
|
||||
@@ -165,7 +193,7 @@ curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://<control-api>:8080/api/v1
|
||||
"ip_id": 42,
|
||||
"detected_egress_ip": "203.0.113.10",
|
||||
"success": true,
|
||||
"detail": "matched"
|
||||
"detail": "matched (control_api)"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user