Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Provodox Lite

Демонстрация: как заставить Google Apps Script (и любую другую среду, не доверяющую корню Минцифры) работать с MAX Bot API. Разворачивается в вашем облаке за десять минут.

Прокси рабочий и полный: проходит весь MAX Bot API, а не отдельные методы, и обходится нулевой конфигурацией. Когда задача перерастает личный контур, добавляются контроль доступа, свой домен и произвольные апстримы — это Provodox.

Проблема

platform-api2.max.ru подписан корнем Минцифры. Ряд сред исполнения этому корню не доверяет и не даёт его добавить — сертификаты там зашиты в платформу. В Google Apps Script прямой вызов падает на TLS-handshake с SSL Error.

Обойти на стороне клиента нечем: своего CA-бандла в такой среде нет, а отключение проверки не работает — в Apps Script флаг validateHttpsCertificates: false не помогает. Код правильный, токен правильный, не работает сам TLS-handshake.

Решение

Между клиентом и MAX ставится прокси на Yandex Cloud Functions:

Apps Script  ──TLS от GlobalSign──▶  API Gateway ──▶ функция ──▶ platform-api2.max.ru
                                                                (Минцифры)

Домен шлюза *.apigw.yandexcloud.net выдан GlobalSign — ему доверяют все. Функция форвардит запрос на MAX, намеренно не проверяя цепочку сертификата апстрима: именно её клиент и не может проверить сам. Ответ возвращается как есть — статус, заголовки, тело.

Токен бота прокси не хранит и не видит смысла в нём: заголовок Authorization пробрасывается насквозь, авторизует по-прежнему MAX.

Развернуть

Нужен аккаунт Yandex Cloud и yc CLI. Пошагово — в deploy.md, кратко:

git clone https://github.com/contributor-pw/provodox-lite.git
cd provodox-lite

PKG=$(mktemp -d); mkdir -p "$PKG/src"
cp src/index.js "$PKG/src/index.js"; cp package.json "$PKG/package.json"

yc serverless function create --name provodox-lite
yc serverless function version create --function-name provodox-lite \
  --runtime nodejs18 --entrypoint src/index.handler \
  --memory 128MB --execution-timeout 120s --source-path "$PKG"
yc serverless function allow-unauthenticated-invoke --name provodox-lite
# далее — API Gateway, см. deploy.md

Настраивать нечего: апстрим зашит в код, переменных окружения нет.

Проверка, что всё живо:

curl -s "https://<GATEWAY_ID>.apigw.yandexcloud.net/me"
# 401 {"code":"verify.token","message":"No access token"}

Этот 401 пришёл от MAX — значит запрос прошёл цепочку целиком. Добавьте токен и получите 200.

Использование

Клиент берёт свой код под MAX Bot API и меняет одну вещь — базовый URL:

https://platform-api2.max.ru  →  https://<GATEWAY_ID>.apigw.yandexcloud.net

Путь, query-параметры, тело, заголовки — без изменений. Токен передаётся в Authorization: <токен>; передача через query-параметр access_token в MAX больше не поддерживается.

Google Apps Script

Основной сценарий. Через прокси — обычный UrlFetchApp, без плясок с сертификатами:

const props = PropertiesService.getScriptProperties();
const BASE = 'https://<GATEWAY_ID>.apigw.yandexcloud.net';
const TOKEN = props.getProperty('MAX_TOKEN'); // токен бота MAX, проверяет апстрим

function getMe() {
  const res = UrlFetchApp.fetch(`${BASE}/me`, {
    headers: { Authorization: TOKEN },
    muteHttpExceptions: true,
  });
  return JSON.parse(res.getContentText());
}

function sendMessage(chatId, text) {
  const res = UrlFetchApp.fetch(`${BASE}/messages?chat_id=${chatId}`, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: TOKEN },
    payload: JSON.stringify({ text }),
    muteHttpExceptions: true,
  });
  return JSON.parse(res.getContentText());
}

curl

BASE="https://<GATEWAY_ID>.apigw.yandexcloud.net"

curl "$BASE/me" -H "Authorization: $MAX_TOKEN"

curl -X POST "$BASE/messages?chat_id=$CHAT_ID" \
  -H "Authorization: $MAX_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"text":"привет"}'

Вложения

Загрузка файла идёт в три шага, и через прокси нужен только первый и третий:

  1. POST /uploads?type=... — через прокси, вернёт upload-URL;
  2. загрузка на выданный URL (fu.oneme.ru, iu.oneme.ru, vu.okcdn.ru) — напрямую, эти хосты подписаны публично доверенными CA, проксировать их не нужно;
  3. POST /messages с attachments — через прокси.

Куда это растёт

Lite решает исходную задачу целиком: клиент, не доверяющий Минцифре, работает с MAX. Для личного бота, прототипа и внутренней автоматизации этого достаточно, и настраивать там нечего — что и есть главное его удобство.

Дальше начинаются задачи не про TLS, а про эксплуатацию. Их закрывает Provodox:

Provodox Lite Provodox
Доступ по адресу шлюза ключ на каждого клиента, отзыв по одному
Домен служебный *.apigw.yandexcloud.net свой домен и сертификат
Апстрим MAX, задан в коде любой, меняется без передеплоя
Секреты не требуются Lockbox, сервис-аккаунт с минимальной ролью
Эксплуатация ваше облако и ваш контроль развёрнут и обслуживается

Первая строка — та, ради которой стоит дочитать. Lite не проверяет вызывающего: адрес шлюза и есть весь доступ. Пока адрес знаете только вы, модель работает. Как только он попадёт к кому-то ещё, запросы к MAX пойдут за ваш счёт и с вашего IP — а свободный лимит API Gateway составляет 100 000 запросов в месяц, дальше тарификация. Поэтому адрес не публикуют, а стенд не оставляют включённым без надобности. Там, где адрес нельзя удержать в секрете, нужен контроль доступа.

Как это устроено

Весь прокси — один файл на ~140 строк без зависимостей (src/index.js), читается за пять минут.

Что стоит знать:

  • API Gateway обязателен. Голая функция вызывается по https://functions.yandexcloud.net/<id>, и такой вызов не передаёт путь: /me в этом URL платформа считает частью идентификатора функции и отвечает 400 invalid functionID. Пути маршрутизирует шлюз через {path+}.
  • Ответ буферизуется целиком и возвращается в base64. Это корректно для JSON, gzip и вложений. Долгие запросы (long polling GET /updates) работают: тело приходит один раз, в конце. → Можно усилить: потоковая передача ответа откроет SSE и стриминг.
  • Таймаут — 120 секунд, с запасом на самый долгий вызов MAX (/updates принимает timeout до 90 с). → Можно усилить: это не предел платформы, функция допускает до 10 минут — поднимается одним флагом при деплое.
  • Проверка сертификата апстрима отключена (rejectUnauthorized: false) — сознательно, это и есть смысл прокси. Апстрим при этом ровно один и задан в коде, так что открытым релеем в интернет прокси не становится. → Можно усилить: закрепить апстрим списком разрешённых хостов, если апстрим станет настраиваемым.

Лицензия

MIT — см. LICENSE.

About

Демонстрационный reverse-proxy к MAX Bot API для сред, не доверяющих корню Минцифры (Google Apps Script и др.)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages