Add authentication: admin/agent bearer tokens for the API, login for the dashboard
control-api: every route now carries a mandatory access level (admin / agent / open) in a route table. All /api/v1/admin/* require the admin token; the write calls of validator-agent and prober (self-check, events, results, complete) require a separate static agent token; register, heartbeat and fetching the assignment stay open. Tokens come from env vars, are compared in constant time and never logged. An empty token leaves that level open with a startup warning (backward compatible). validator-agent / prober: apiclient sends the agent token only to control-api. admin-dashboard: login/password (from env) with a stateless HMAC session cookie, Origin-based CSRF check, per-IP brute-force throttle, HX-Redirect for htmx polls, logout in the sidebar; the dashboard calls control-api with the admin token. Login page layout fixed after review. Also: env plumbing in docker-compose/rxprod-compose/systemd/config examples, e2e script with token assertions, tests, docs (API, SETUP, USAGE, DASHBOARD, README), plan and review under docs/changes/, bin/ rebuilt with new SHA256SUMS. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
972ad47d0c
commit
debf2afed2
67 files changed
+2050
-105
No files matched your search
@@ -14,6 +14,19 @@ PROBER_SITE_ID=site-1
|
||||
# --- validator-agent (must match a validator_id in the mounted control-api config) ---
|
||||
VALIDATOR_AGENT_VALIDATOR_ID=validator_01
|
||||
|
||||
# --- authentication (empty = that level stays open; services log a warning) ---
|
||||
# Generate each secret with: openssl rand -hex 32
|
||||
# control-api admin token (protects /api/v1/admin/*) and the dashboard's copy of it:
|
||||
CONTROL_API_ADMIN_TOKEN=
|
||||
ADMIN_DASHBOARD_CONTROL_API_TOKEN=
|
||||
# control-api agent token (protects result/event writes); the same value goes
|
||||
# to prober and validator-agent:
|
||||
CONTROL_API_AGENT_TOKEN=
|
||||
# Dashboard login (both must be set to enable it) and cookie-signing key:
|
||||
ADMIN_DASHBOARD_USERNAME=
|
||||
ADMIN_DASHBOARD_PASSWORD=
|
||||
ADMIN_DASHBOARD_SESSION_SECRET=
|
||||
|
||||
# ADMIN_DASHBOARD_CONTROL_API_URL / PROBER_CONTROL_API_URL /
|
||||
# VALIDATOR_AGENT_CONTROL_API_URL default to http://control-api:8080 in
|
||||
# docker-compose.yml (same Docker network) — only set here to override.
|
||||
@@ -39,3 +39,19 @@ VALIDATOR_AGENT_CONTROL_API_URL=https://control-api.internal.example.com
|
||||
|
||||
# --- admin-dashboard (only needed on a dashboard-profile host) ---
|
||||
ADMIN_DASHBOARD_CONTROL_API_URL=http://control-api:8080
|
||||
|
||||
# --- authentication (fill in only what this host needs; empty = open + warning) ---
|
||||
# Generate each secret with: openssl rand -hex 32
|
||||
# Rollout order without downtime: update all binaries first, then give the
|
||||
# tokens to prober / validator-agent / dashboard, and set the control-api
|
||||
# tokens (and restart it) last.
|
||||
#
|
||||
# control-plane host: tokens that control-api enforces
|
||||
CONTROL_API_ADMIN_TOKEN=
|
||||
CONTROL_API_AGENT_TOKEN=
|
||||
# dashboard host: control-api's admin token, the login, and the cookie-signing key
|
||||
ADMIN_DASHBOARD_CONTROL_API_TOKEN=
|
||||
ADMIN_DASHBOARD_USERNAME=
|
||||
ADMIN_DASHBOARD_PASSWORD=
|
||||
ADMIN_DASHBOARD_SESSION_SECRET=
|
||||
# prober / validator hosts use CONTROL_API_AGENT_TOKEN (same value as above)
|
||||
@@ -25,6 +25,7 @@ docker run -d --platform linux/amd64 --cap-add NET_RAW --name prober \
|
||||
-e PROBER_TCP_TIMEOUT_SECONDS=5 \
|
||||
-e PROBER_ICMP_TIMEOUT_SECONDS=5 \
|
||||
-e PROBER_ICMP_COUNT=3 \
|
||||
-e CONTROL_API_AGENT_TOKEN=<agent_token> \
|
||||
cloud-ip-validator-prober
|
||||
|
||||
Рабочий пример (стенд 62.176.10.113):
|
||||
@@ -42,6 +43,9 @@ docker run -d --platform linux/amd64 --cap-add NET_RAW --name prober \
|
||||
PROBER_TCP_TIMEOUT_SECONDS опциональная, дефолт 5
|
||||
PROBER_ICMP_TIMEOUT_SECONDS опциональная, дефолт 5
|
||||
PROBER_ICMP_COUNT опциональная, дефолт 3
|
||||
CONTROL_API_AGENT_TOKEN опциональная: токен агентов control-api (то же
|
||||
значение, что у control-api); без него результаты
|
||||
проверок будут отклонены, если токен включён
|
||||
|
||||
Примечания:
|
||||
--platform linux/amd64 бинарник в bin/ собран под Linux x86_64
|
||||
@@ -61,6 +65,10 @@ docker run -d --platform linux/amd64 -p 8090:8090 --name admin-dashboard \
|
||||
-e ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS=10 \
|
||||
-e ADMIN_DASHBOARD_LAST_COMPLETED_COUNT=20 \
|
||||
-e ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS=5 \
|
||||
-e ADMIN_DASHBOARD_CONTROL_API_TOKEN=<admin_token> \
|
||||
-e ADMIN_DASHBOARD_USERNAME=<login> \
|
||||
-e ADMIN_DASHBOARD_PASSWORD=<password> \
|
||||
-e ADMIN_DASHBOARD_SESSION_SECRET=<random_hex> \
|
||||
cloud-ip-validator-admin-dashboard
|
||||
|
||||
Рабочий пример (стенд 62.176.10.113):
|
||||
@@ -75,6 +83,14 @@ docker run -d --platform linux/amd64 -p 8090:8090 --name admin-dashboard \
|
||||
ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS опциональная, дефолт 10
|
||||
ADMIN_DASHBOARD_LAST_COMPLETED_COUNT опциональная, дефолт 20
|
||||
ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS опциональная, дефолт 5
|
||||
ADMIN_DASHBOARD_CONTROL_API_TOKEN опциональная: admin-токен control-api
|
||||
ADMIN_DASHBOARD_USERNAME / _PASSWORD опциональные: логин дашборда; если не
|
||||
заданы оба — вход не требуется (в логе
|
||||
предупреждение)
|
||||
ADMIN_DASHBOARD_SESSION_SECRET опциональная: ключ подписи cookie сессии;
|
||||
без неё случайный, сессии сбрасываются
|
||||
рестартом
|
||||
(секреты генерируются так: openssl rand -hex 32)
|
||||
|
||||
Примечания:
|
||||
--platform linux/amd64 бинарник в bin/ собран под Linux x86_64
|
||||
@@ -102,12 +118,18 @@ docker run -d --platform linux/amd64 -p 8080:8080 --name control-api \
|
||||
-e OS_PROJECT_ID=<project_id> \
|
||||
-e OS_REGION_NAME=<region> \
|
||||
-e OS_TOKEN=<token> \
|
||||
-e CONTROL_API_ADMIN_TOKEN=<admin_token> \
|
||||
-e CONTROL_API_AGENT_TOKEN=<agent_token> \
|
||||
cloud-ip-validator-control-api
|
||||
|
||||
(при auth_method: password — вместо OS_TOKEN передать OS_USERNAME,
|
||||
OS_USER_DOMAIN_NAME, OS_PASSWORD; для openstack.mode: mock креденшлы не
|
||||
нужны)
|
||||
|
||||
CONTROL_API_ADMIN_TOKEN закрывает /api/v1/admin/*, CONTROL_API_AGENT_TOKEN —
|
||||
запись результатов/событий агентами и пробером; не заданный токен оставляет
|
||||
соответствующий уровень открытым (в логе предупреждение).
|
||||
|
||||
Примечания:
|
||||
--platform linux/amd64 бинарник в bin/ собран под Linux x86_64
|
||||
-p 8080:8080 проброс порта control-api наружу
|
||||
@@ -132,6 +154,7 @@ docker run -d --platform linux/amd64 --cap-add NET_RAW --name validator-agent \
|
||||
-e VALIDATOR_AGENT_ICMP_COUNT=3 \
|
||||
-e VALIDATOR_AGENT_SSH_ENABLED=false \
|
||||
-e VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS=5 \
|
||||
-e CONTROL_API_AGENT_TOKEN=<agent_token> \
|
||||
cloud-ip-validator-validator-agent
|
||||
|
||||
Переменные окружения:
|
||||
@@ -145,6 +168,8 @@ docker run -d --platform linux/amd64 --cap-add NET_RAW --name validator-agent \
|
||||
VALIDATOR_AGENT_ICMP_COUNT опциональная, дефолт 3
|
||||
VALIDATOR_AGENT_SSH_ENABLED опциональная, дефолт false
|
||||
VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS опциональная, дефолт 5
|
||||
CONTROL_API_AGENT_TOKEN опциональная: токен агентов control-api
|
||||
(то же значение, что у control-api)
|
||||
|
||||
Примечания:
|
||||
--platform linux/amd64 бинарник в bin/ собран под Linux x86_64
|
||||
|
||||
@@ -8,6 +8,10 @@ export ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS="${ADMIN_DASHBOARD_CONTROL_AP
|
||||
export ADMIN_DASHBOARD_LAST_COMPLETED_COUNT="${ADMIN_DASHBOARD_LAST_COMPLETED_COUNT:-20}"
|
||||
export ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS="${ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS:-5}"
|
||||
|
||||
# Tokens/passwords (CONTROL_API_AGENT_TOKEN, ADMIN_DASHBOARD_*) are deliberately
|
||||
# NOT templated into the YAML: the binary reads them straight from this
|
||||
# container's environment (names default in the config loader), so secrets
|
||||
# never land in a file inside the container.
|
||||
envsubst '${ADMIN_DASHBOARD_LISTEN_ADDR} ${ADMIN_DASHBOARD_CONTROL_API_URL} ${ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS} ${ADMIN_DASHBOARD_LAST_COMPLETED_COUNT} ${ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS}' \
|
||||
< /etc/admin-dashboard/admin-dashboard.yaml.tmpl > /etc/admin-dashboard/admin-dashboard.yaml
|
||||
|
||||
|
||||
@@ -17,6 +17,13 @@ database:
|
||||
openstack:
|
||||
mode: "mock"
|
||||
|
||||
# Статические bearer-токены читаются из переменных окружения контейнера с
|
||||
# этими именами (задаются в .env, см. deploy/docker/.env.example). Пусто =
|
||||
# соответствующий уровень API открыт (с предупреждением в логе).
|
||||
auth:
|
||||
admin_token_env: "CONTROL_API_ADMIN_TOKEN" # защищает /api/v1/admin/*
|
||||
agent_token_env: "CONTROL_API_AGENT_TOKEN" # защищает запись результатов агентами/пробером
|
||||
|
||||
orchestrator:
|
||||
poll_interval_seconds: 5
|
||||
self_check_timeout_seconds: 60
|
||||
|
||||
@@ -34,6 +34,11 @@ services:
|
||||
networks: [backend]
|
||||
volumes:
|
||||
- control-api-db:/var/lib/cloud-ip-validator
|
||||
environment:
|
||||
# Static bearer tokens (empty = that API level stays open, with a
|
||||
# warning in the log). Generate with `openssl rand -hex 32`.
|
||||
CONTROL_API_ADMIN_TOKEN: "${CONTROL_API_ADMIN_TOKEN:-}"
|
||||
CONTROL_API_AGENT_TOKEN: "${CONTROL_API_AGENT_TOKEN:-}"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8080/healthz"]
|
||||
interval: 5s
|
||||
@@ -55,6 +60,12 @@ services:
|
||||
ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS: "${ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS:-10}"
|
||||
ADMIN_DASHBOARD_LAST_COMPLETED_COUNT: "${ADMIN_DASHBOARD_LAST_COMPLETED_COUNT:-20}"
|
||||
ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS: "${ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS:-5}"
|
||||
# Admin token of control-api, and the dashboard's own login. Empty
|
||||
# username/password = login disabled (warning in the log).
|
||||
ADMIN_DASHBOARD_CONTROL_API_TOKEN: "${ADMIN_DASHBOARD_CONTROL_API_TOKEN:-}"
|
||||
ADMIN_DASHBOARD_USERNAME: "${ADMIN_DASHBOARD_USERNAME:-}"
|
||||
ADMIN_DASHBOARD_PASSWORD: "${ADMIN_DASHBOARD_PASSWORD:-}"
|
||||
ADMIN_DASHBOARD_SESSION_SECRET: "${ADMIN_DASHBOARD_SESSION_SECRET:-}"
|
||||
|
||||
prober:
|
||||
build:
|
||||
@@ -72,6 +83,8 @@ services:
|
||||
PROBER_TCP_TIMEOUT_SECONDS: "${PROBER_TCP_TIMEOUT_SECONDS:-5}"
|
||||
PROBER_ICMP_TIMEOUT_SECONDS: "${PROBER_ICMP_TIMEOUT_SECONDS:-5}"
|
||||
PROBER_ICMP_COUNT: "${PROBER_ICMP_COUNT:-3}"
|
||||
# Agent token of control-api (same value as on control-api).
|
||||
CONTROL_API_AGENT_TOKEN: "${CONTROL_API_AGENT_TOKEN:-}"
|
||||
|
||||
validator-agent:
|
||||
build:
|
||||
@@ -92,3 +105,5 @@ services:
|
||||
VALIDATOR_AGENT_ICMP_COUNT: "${VALIDATOR_AGENT_ICMP_COUNT:-3}"
|
||||
VALIDATOR_AGENT_SSH_ENABLED: "${VALIDATOR_AGENT_SSH_ENABLED:-false}"
|
||||
VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS: "${VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS:-5}"
|
||||
# Agent token of control-api (same value as on control-api).
|
||||
CONTROL_API_AGENT_TOKEN: "${CONTROL_API_AGENT_TOKEN:-}"
|
||||
@@ -9,6 +9,10 @@ export PROBER_TCP_TIMEOUT_SECONDS="${PROBER_TCP_TIMEOUT_SECONDS:-5}"
|
||||
export PROBER_ICMP_TIMEOUT_SECONDS="${PROBER_ICMP_TIMEOUT_SECONDS:-5}"
|
||||
export PROBER_ICMP_COUNT="${PROBER_ICMP_COUNT:-3}"
|
||||
|
||||
# Tokens/passwords (CONTROL_API_AGENT_TOKEN, ADMIN_DASHBOARD_*) are deliberately
|
||||
# NOT templated into the YAML: the binary reads them straight from this
|
||||
# container's environment (names default in the config loader), so secrets
|
||||
# never land in a file inside the container.
|
||||
envsubst '${PROBER_SITE_ID} ${PROBER_CONTROL_API_URL} ${PROBER_POLL_INTERVAL_SECONDS} ${PROBER_TCP_TIMEOUT_SECONDS} ${PROBER_ICMP_TIMEOUT_SECONDS} ${PROBER_ICMP_COUNT}' \
|
||||
< /etc/prober/prober.yaml.tmpl > /etc/prober/prober.yaml
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ export VALIDATOR_AGENT_ICMP_COUNT="${VALIDATOR_AGENT_ICMP_COUNT:-3}"
|
||||
export VALIDATOR_AGENT_SSH_ENABLED="${VALIDATOR_AGENT_SSH_ENABLED:-false}"
|
||||
export VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS="${VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS:-5}"
|
||||
|
||||
# Tokens/passwords (CONTROL_API_AGENT_TOKEN, ADMIN_DASHBOARD_*) are deliberately
|
||||
# NOT templated into the YAML: the binary reads them straight from this
|
||||
# container's environment (names default in the config loader), so secrets
|
||||
# never land in a file inside the container.
|
||||
envsubst '${VALIDATOR_AGENT_VALIDATOR_ID} ${VALIDATOR_AGENT_CONTROL_API_URL} ${VALIDATOR_AGENT_POLL_INTERVAL_SECONDS} ${VALIDATOR_AGENT_SELF_CHECK_TIMEOUT_SECONDS} ${VALIDATOR_AGENT_HTTPS_TIMEOUT_SECONDS} ${VALIDATOR_AGENT_ICMP_TIMEOUT_SECONDS} ${VALIDATOR_AGENT_ICMP_COUNT} ${VALIDATOR_AGENT_SSH_ENABLED} ${VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS}' \
|
||||
< /etc/validator-agent/validator-agent.yaml.tmpl > /etc/validator-agent/validator-agent.yaml
|
||||
|
||||
|
||||
Reference in new issue
Block a user