docs: explain the 403 invalid_token case
Build Admin / Build image (push) Successful in 7s

Clients relied on /openrouter/ having no token check; v0.2.0 applies the
guard to every prefix, so the missing header now shows up everywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
vasyanskandClaude Opus 5 committed 2026-09-20 13:46:31 +07:00
1 parent e90d67366a
commit 1a4ad47c77
2 files changed
+89

No files matched your search

+35
View File
@@ -184,6 +184,41 @@ For OpenAI-compatible SDKs set `baseURL` to `https://proxy.bildme.ru/deepseek` a
---
## Troubleshooting
### `403 {"error":"invalid_token"}` on every path
The proxy answered, not the upstream — `X-Proxy-Token` is missing or wrong. The body is the proxy's own format; a real upstream error looks different (DeepSeek: `{"error":{"message":...,"type":...}}`).
The guard applies to **every** prefix. Before v0.2.0 `/openrouter/` was the one route without a token check — that exception is gone, so clients that relied on it must send the header too.
```bash
curl -H "X-Proxy-Token: $PROXY_SECRET" \
-H "Authorization: Bearer $DEEPSEEK_API_KEY" \
https://proxy.bildme.ru/deepseek/user/balance
```
For OpenAI-compatible SDKs put it in the default headers, next to `baseURL`:
```go
option.WithBaseURL("https://proxy.bildme.ru/deepseek"),
option.WithHeader("X-Proxy-Token", os.Getenv("PROXY_SECRET")),
```
### `403 {"error":"ip_not_allowed"}`
The caller's source IP is not in `ALLOWED_CIDR`. Check the effective list:
```bash
docker exec api-proxy cat /etc/caddy/matchers.caddy
```
### Upstream errors pass through unchanged
`401`, `404` or `5xx` with an upstream-shaped JSON body means the proxy did its job and the upstream rejected the request — check the API key, not the proxy.
---
## Monitoring
```bash
@@ -0,0 +1,54 @@
# Переход nginx → Caddy + Let's Encrypt (proxy.bildme.ru)
Дата: 2026-09-20 · Профиль: refactoring · Репозиторий: `pub/elevenlabs.io`
## Задача
Заменить nginx на Caddy, оставить порт 8082 как есть, параллельно открыть 443 с сертификатом Let's Encrypt на `proxy.bildme.ru`. Предыстория — таймаут `api.deepseek.com` у клиента, из-за чего в прокси был добавлен маршрут `/deepseek/`.
## Аудит прода (62.84.98.250)
- 80/443 занимал хостовый nginx 1.18 (systemd): заглушка `/var/www/html` + vhost `tg.bildme.ru`, чей DNS уже указывал на `45.151.70.121`, сертификат истёк 14.07.2026 → потерь нет.
- Контейнер `api-proxy` из `/srv/elevenlabs` (не git), проброс `8082:8080`, образ из `git.realmanual.ru`.
- `docker-compose` 1.29.2 + Docker 28.2.2 → известная несовместимость (`KeyError: 'ContainerConfig'`).
## Изменения в репозитории
| Файл | Что |
|------|-----|
| `Caddyfile` | новый конфиг: сниппеты `(scrub)`, `(api)`, `(routes)`; маршруты в `route {}` — guard'ы отрабатывают до `handle_path` |
| `docker-entrypoint.sh` | генерация `matchers.caddy` / `guard.caddy` из `ALLOWED_CIDR` и `PROXY_SECRET`; обязательные `DOMAIN`, `ACME_EMAIL`; `caddy validate` перед стартом |
| `Dockerfile` | `caddy:2.11.4-alpine`, EXPOSE 80/443/8080 |
| `docker-compose.yml` | порты `80`, `443` (tcp+udp), `8082:8080`; тома `caddy_data`, `caddy_config` |
| `scripts/migrate-host-nginx.sh` | бэкап `/etc/nginx` + `/etc/letsencrypt`, отключение nginx и `certbot.timer`, проверка портов |
| `scripts/deploy.sh` | реальный прод-путь `/srv/elevenlabs`, приоритет `docker compose` v2 |
| `nginx/`, `ssl/` | удалены |
| `VERSION` | 0.2.0 |
Коммиты: `d71001a`, `e90d673`.
## Выполнено на сервере
1. Бэкап хостового nginx → `/root/nginx-backup-20260920-093758` (`nginx-T.dump`, `etc-nginx.tar.gz`, `letsencrypt.tar.gz`).
2. `systemctl disable --now nginx`, `systemctl disable --now certbot.timer`.
3. Установлен `docker-compose-v2` 2.40.3 (v1 ломался на Docker 28).
4. `/srv/elevenlabs/docker-compose.yaml` и `.env` обновлены (бэкапы `.bak-2026-09-20`), добавлены `DOMAIN=proxy.bildme.ru`, `ACME_EMAIL=vasyakrg@gmail.com`.
5. Контейнер пересоздан из образа `0.2.0` / `latest`.
## Проверки (прод)
| Проверка | Результат |
|----------|-----------|
| Сертификат | `CN=proxy.bildme.ru`, issuer Let's Encrypt YE1, до 19.12.2026 |
| `https://proxy.bildme.ru/health` | 200 |
| `http://62.84.98.250:8082/health` | 200 |
| `http://proxy.bildme.ru/health` | 308 → https |
| Без `X-Proxy-Token` | 403 `invalid_token` |
| IP вне `ALLOWED_CIDR` | 403 `ip_not_allowed` |
| `/deepseek/chat/completions` | 401 от DeepSeek (проброс работает, исходный i/o timeout снят) |
| `/telegram/`, `/openrouter/`, `/elevenlabs/` | 401 / 200 / 404 от апстримов — проброс работает |
| Неизвестный префикс | 404 `unknown_upstream` |
| Пересоздание контейнера | сертификат переиспользован из тома, повторного выпуска не было |
| Healthcheck | `healthy` |
## Риски и открытые пункты
- **8082 остаётся plain HTTP** — токен и ключи апстримов идут открытым текстом. Клиентов нужно перевести на `https://proxy.bildme.ru`, после чего порт закрыть (ufw на VM сейчас `inactive`).
- **Том `caddy_data`** содержит сертификаты и ACME-аккаунт. Его удаление = повторный выпуск и риск упереться в лимиты Let's Encrypt.
- **Хостовый nginx остановлен и отключён**, конфиги в бэкапе. Если на VM появятся новые сайты — их конфиг переносится в `Caddyfile`, а не обратно в nginx.
- **`certbot.timer` отключён** — старый серт `tg.bildme.ru` больше не обновляется (домен и так живёт на другом сервере).
- Локальный каталог `ssl/` с `tls.crt`/`tls.key` удалён — эти файлы не были в git и на проде не монтировались.