Некоторое время назад я начал писать прокси с поддержкой идентификации (IAP) для защиты двоичного файла с помощью аутентификации. Однако то, что начиналось как минимальный уровень аутентификации, расширилось за счет функций. Я пришел к выводу, что обратный прокси-сервер - отличный уровень для решения множества сквозных задач, таких как аутентификация, хотя бы один раз доставка и адаптация. Кроме того, я обнаружил, что OpenResty обеспечивает потрясающую производительность и гибкость, И он почти идеально подходит для бессерверной парадигмы.
Конкретно я работал над расширением IAP для приема и изменения сигналов от Slack и Zapier, туннелирования их через журнал предварительной записи (WAL) и проверки их подлинности до того, как они попадут в двоичный файл нашего приложения. Оказывается, такая интеграция на уровне прокси дает огромные технические преимущества.
Первая победа для прокси - это универсальный адаптер. Часто требуется изменить форму JSON, которыми обмениваются независимо разработанные службы. Учитывая полезность обертывания сервисов общим слоем аутентификации, имеет смысл, что это также удобный момент для сопоставления доменов. С OpenResty вы можете сделать это в высокопроизводительном двоичном файле.
Вторая победа заключалась в использовании прокси для минимизации задержки ответа. Slack требует, чтобы боты ответили в течение 3 секунд. Если восходящий поток является бессерверным процессом JVM, вы можете легко истечь тайм-аут, когда восходящий поток холодный. Мы решили эту проблему на уровне прокси, буферизовав входящие запросы в управляемую очередь, что-то вроде журнала предзаписи (WAL). Это означало, что мы могли снизить задержку, отвечая на веб-перехватчик Slack, как только очередь подтверждала запись. Поскольку OpenResty - это c + lua, запускается так быстро, что мы делаем все, что в наших силах, в бессерверной среде.
С WAL мы получаем семантику доставки хотя бы один раз. Размещение WAL на уровне прокси может решить массу проблем с надежностью восходящего потока. Это означает, что, пока восходящий поток идемпотентен, вам не нужна логика повторных попыток восходящего потока. Это упрощает разработку приложений и расширяет выбор стека в восходящем направлении. Конкретно для нас это означало, что двоичный файл JVM с медленным запуском не нужно переписывать для развертывания на бессерверной основе.
Наконец, мы могли быстро проверять подлинность входящих сообщений, чтобы остановить потенциальные атаки до того, как потребуются более дорогие ресурсы в восходящем направлении. Опять же, OpenResty, вероятно, будет быстрее (и, следовательно, дешевле), чем сервер приложений в этой механической задаче. Мы обнаружили, что относительно безболезненно хранить секреты в secretmanager и получать их через прокси.
В целом мы обнаружили, что OpenResty - почти идеальная технология для Serverless. Это дает вам производительность C (задержка отклика до 5 мс), быстрое время запуска (400 мс холодный запуск в Cloud Run) и готовность Ngnix к производству, и все это дает вам гибкость для настройки и добавления функций к границе вашей инфраструктуры с помощью Скрипты Lua.
Стоит отметить, что Cloud Run масштабируется до нуля (в отличие от Fargate) и поддерживает параллельные операции (в отличие от Lambda и Google Cloud Functions). OpenResty + Cloud Run позволит вам обслуживать тонны одновременного трафика на одном экземпляре, и поэтому я ожидаю, что это будет наиболее рентабельным из возможных вариантов. Хотя его холодный запуск выше, чем, скажем, Лямбда (мы получаем 400 мс против 200 мс), поскольку он требует меньше событий масштабирования, я ожидаю, что инциденты холодного запуска будут менее частыми для большинства развертываний.
Благодаря тому, что прокси обрабатывает больше вариантов использования (например, логика повторных попыток), затраты переносятся из двоичных файлов приложения в самую удобную часть инфраструктуры. Чтобы воспользоваться всеми этими преимуществами, вам не нужен кластер Kubernetes, но вы можете развернуть его в кластере, если хотите. Нам удалось объединить все наши функции в единую небольшую бессерверную службу, развернутую Terraform по лицензии MIT.
Теперь я расскажу подробнее о том, как проходила наша конкретная разработка, и как мы решали различные тактические проблемы разработки. Вероятно, это будет слишком подробным для большинства читателей, но я попытался отсортировать их по обобщаемости.
Локальная разработка с Terraform и Docker-compose
Медленность развертываний Cloud Run препятствовала разработке сложных функций прокси, поэтому это первая проблема, которая решила проблему локальной разработки. Поскольку Cloud Run в конечном итоге развертывает файлы докеров, мы использовали docker-compose, чтобы запустить наш двоичный файл вместе с эмулятором метаданных GCE. Наш файл докеров создается из шаблонов Terraform, но вы можете попросить Terraform создать этот локальный файл, без развертывания всего проекта, с флагом -target
. Таким образом, мы можем создать полуприличный цикл разработки, упорядочивая генерацию артефактов терраформ и docker-compose в цикле, и не нужно переписывать рецепт Terraform для поддержки локальной разработки!
В приведенном выше сценарии оболочки, когда вы нажимаете CTRL + C в оболочке, двоичный файл обновится и перезапустится. Сложность - выйти из этого цикла! Если вы вызовете его с помощью «/ bin / bash test / dev.sh», он будет называться bash, поэтому вы можете выйти с помощью «killall bash». Redirect_uri Oauth 2.0 не будет работать с localhost, поэтому вам нужно будет скопировать токены prod с / login? Token = true из развертывания prod.
Добавление журнала упреждающей записи с помощью Pub / Sub
Чтобы иметь возможность быстро и уверенно отвечать на входящие запросы, к прокси-серверу было добавлено внутреннее расположение общего назначения /wal/...
. Предполагалось, что любой запрос, направленный на /wal/<PATH>
, будет адресован UPSTREAM/<PATH>
, но будет перемещаться через тему Pub / Sub и подписку. Это выгружает постоянное хранилище буфера в выделенную службу с большой задержкой и гарантиями надежности.
Каждое сообщение WAL, по сути, инкапсулирует HTTP-запрос. Итак, заголовки, uri, method и body были помещены в конверт и отправлены в PubSub. Тело запроса было сопоставлено с полем данных Pub / Sub в кодировке base64, и мы использовали атрибуты Pub / Sub для хранения остальных.
Вызов Pub / Sub - это простой POST-запрос к теме, предоставленной Terraform.
Предусмотрена подписка Pub / Sub, которая отправит конверты обратно на прокси-сервер в расположение /wal-playback
. Указав oidc_token
, Pub / Sub добавит токен идентификатора, который можно проверить на прокси-сервере.
В конфигурации OpenResty мы открываем /wal-playback
для Интернета, но мы проверяем входящий токен перед распаковкой конверта и отправкой в восходящем направлении.
В нашем случае наш апстрим тоже был размещен в Cloud Run. Если ответом восходящего потока был код состояния 429 (слишком много запросов), это означает, что контейнер увеличивается и его следует повторить. Точно так же код состояния 500 означает, что восходящий поток прерван и запрос следует повторить. Для этих кодов ответа прокси-сервер возвращает статус 500 в Pub / Sub, который запускает его поведение повторной попытки, что приводит к семантике доставки хотя бы один раз.
В нашем развертывании интеграции Zapier и Slack использовали прокси /wal/
.
Интеграция Slack
Мы хотели повысить внутреннюю продуктивность, добавив настраиваемые «команды с косой чертой» в наш внутренний механизм бизнес-процессов. Создать внутреннего бота и зарегистрировать новую команду очень просто, вам просто нужно предоставить общедоступную конечную точку.
Slack отправляет исходящие x-www-form-urlencoded
веб-перехватчиков. Конечно, наш апстрим использует JSON, но конвертировать его с помощью пакета resty-reqargs несложно.
Поскольку это общедоступная конечная точка, нам необходимо обеспечить подлинность запроса. Slack использует общий симметричный ключ подписи. Поскольку нам не нужны секреты рядом с Terraform. Копируем вручную ключ в Google Secret Manager.
Тогда нам нужно только сохранить идентификатор ресурса ключа в Terraform. Кстати, Секретный менеджер отличный! Вы можете ссылаться на последнюю версию, чтобы вы могли менять секреты, не беспокоя Terraform.
В конфигурации OpenResty мы получаем секрет с помощью аутентифицированного GET и декодирования base64. Мы храним секрет в глобальной переменной для использования во всех запросах.
Документация Slack довольно хорошо объясняет, как проверить запрос. При использовании пакета resty.hmac было всего несколько строк lua:
Конечно, настоящая трудность со Slack заключается в требовании тайм-аута в 3 секунды, поэтому входящие команды slack перенаправлялись в WAL для быстрого ответа с семантикой доставки хотя бы один раз.
Интеграция Zapier
Zapier - еще одна отличная интеграция с выгодной ценой. Если у вас есть Identity Aware Proxy, легко создать внутреннее приложение, которое может вызывать ваши API.
После создания приложения Zapier вам необходимо добавить Zapier в качестве авторизованного URL-адреса перенаправления.
Чтобы авторизация работала бессрочно с Google Auth, вам необходимо добавить параметры в запрос токена, чтобы включить автономный доступ.
Вам нужен только адрес электронной почты:
А чтобы конечная точка токена обновления работала правильно, вам нужно добавить идентификатор клиента и секрет:
Чтобы отправить сигнал от Zapier нашему внутреннему программному обеспечению, мы создали действие с именем signal, у которого был ключ имени плюс строка, словарь строк переменных. Это похоже на минимальную, но гибкую схему.
Его очень хорошо Zapier работает с Oauth 2.0, и это помогло проверить правильность нашей собственной реализации идентификации.
Выучить больше
Наш внутренний механизм рабочего процесса разрабатывается полностью с открытым исходным кодом и лицензируется MIT. Узнайте больше о нашем видении автоматизации цифровых процессов на основе OpenAPI.
Следите за развитием на Linkedin или Medium
Первоначально опубликовано на https://futurice.com.