Идеально защитите свою функцию

Эта статья является частью серии из 4 статей, в которых я даю различные советы по разработке Google Cloud Functions. Эта работа является результатом двухлетней ежедневной практики, развертывания и мониторинга. Некоторые из этих лучших практик взяты непосредственно из официальной документации, другие — из моего опыта, которые оказались наиболее эффективными. Для любой другой точки зрения не стесняйтесь комментировать эту (бесплатную) статью.

Спасибо!

‹‹‹ Рекомендации по использованию облачных функций (1/4): подготовьте среду

‹‹‹ Рекомендации по использованию облачных функций (2/4): Оптимизация облачных функций

Рекомендации по работе с облачными функциями (4/4): отслеживание и регистрация выполнения ›››

Защитите облачные функции

Онлайн-уроки крутые, мои даже супер крутые! ;) С кучей статей каждый может узнать как использовать Secret Manager в облачных функциях Google, как защитить облачные функции Google, как ограничить их скорость, как использовать облачное хранилище, Cloud Pub /Sub и всевозможные инструменты.

Но,

У него есть большой недостаток ⇒ Учебники сделаны для одной, короткой, конкретной темы.

А бизнес-проекты совсем не короткие и конкретные.

Бизнес-проекты сочетают в себе различные виды доступа, содержат конфиденциальные данные, воздействуют на данные реального мира, оказывая серьезное влияние при доступе к ним.

В этой статье я объясняю, как защитить различные конфиденциальные уровни облачных функций.

Конкретные функции = конкретные разрешения

Во многих учебниках (на самом деле, во всех), объясняющих, как создать облачные функции, они сохраняют «учетную запись службы времени выполнения» по умолчанию.

Учетная запись службы выполнения представляет собой учетную запись службы, идентификатор, используемый функцией для вызова, запуска и обмена данными с другими службами GCP.

Плохо. Не хорошо. Ниет.

Функция имеет свою собственную идентичность (🤖), свое поведение и, следовательно, нуждается в собственном доступе.

См. эту структуру:

function_1 получает доступ к службам, к которым не имеет доступа function_2, и наоборот. Используя учетную запись службы выполнения по умолчанию, они оба будут иметь больше разрешений, чем необходимо, тогда как функции function_2 потребуется только доступ к BigQuery и Cloud Vision.

Чуть выше мы видели, что учетная запись службы среды выполнения по умолчанию — это «учетная запись службы App Engine по умолчанию». У этих сервисных аккаунтов по умолчанию есть роль Редактор, которая позволяет функциям получить широкий доступ ко многим сервисам Google Cloud.

Может быть, у вас даже нет роли редактора!

Но если у вас есть роль администратора облачных функций, вы можете делать все, что хотите, в проекте GCP через облачные функции, развернутые с учетной записью службы выполнения по умолчанию (я думаю/надеюсь, что это верно только в том случае, если функция уже развернут кем-то с ролью редактора).

⚠️ И если вы можете что-то сделать, то сможет и любой, у кого есть доступ к облачным функциям.

Чтобы избежать этой ужасной ситуации, присвойте идентификатор каждой облачной функции.

  • Создать сервисный аккаунт
  • Предоставьте ему соответствующие роли в зависимости от того, к каким ресурсам функция должна получить доступ.
  • Свяжите учетную запись службы с вашей функцией. Если функция еще не развернута, перейдите на вкладку «Выполнение, сборка, подключения и параметры безопасности», если она уже развернута, отредактируйте ее и перейдите на ту же вкладку. Обновите учетную запись службы среды выполнения, указав новый SA.
  • Ах, после обновления ваша функция может не работать, если вы забыли разрешения… такое бывает. Просто протестируйте ;)

Чтобы жить счастливо, живи скрытно

В подавляющем большинстве учебных пособий показано, как отправлять электронные письма, отправлять медленные уведомления, готовить кофе или публиковать сообщения в Твиттере с помощью облачных функций с помощью триггера HTTP!

Триггер HTTP удобен для учебных пособий, ввода, вывода, URL-адреса, щелчка, щелчка, проверки, zip, pouf, он в прямом эфире! Не надо говорить про Pub/Sub, создавать доступ, блаблабла.

Новичок, естественно, продолжит работу с этой настройкой.

Он / она будет продолжать иметь облачные функции, доступные всем, и использовать этот удобный HTTPS-адрес для вставки в Postman, для выполнения различных конфиденциальных задач, иногда для доступа к дорогостоящим услугам (иногда даже будет писать сообщение в блоге и делиться этим точным URL-адресом, да , Бывает).

Уважаемый читатель, вы не новичок, а если и были, то это время закончилось:

Облачные функции никогда (никогда!) не должны быть общедоступными!

Облачные функции никогда не должны быть общедоступными

В этой большой части я рассказываю о различных способах скрыть облачные функции.

Перейти фон

Во многих случаях облачные функции могут работать в фоновом режиме, асинхронно реагируя на события.

Примеры случаев:

  • Реагировать после появления нового файла в корзине Cloud Storage
  • Периодически проверяйте наличие новых электронных писем/твитов/сообщений…
  • Каждое утро создавайте список чего-либо из данных BigQuery.
  • Отправлять сообщение каждые X минут, если условие подтверждено
  • Периодически отправлять уведомления

Это простые примеры, но если вы посмотрите онлайн-учебники, большинство из них предлагают создать облачную функцию HTTP.

Правда в том, что если триггеру не нужен HTTP, Функция не обязательно должна быть HTTP.

Это может быть фоновая функция, реагирующая на событие:

  • Новый объект в ведре
  • Триггер облачного планировщика
  • Новый файл на Google Диске
  • И более 100 мероприятий с использованием Eventarc (GCF gen 2)

Если вы посмотрите мою предыдущую статью о лучших практиках. Вы видите следующую предлагаемую структуру:

С правой стороны мы используем фоновые функции для доступа к Gmail, Slack…

Поскольку электронная почта по своей природе асинхронна, уведомления Google Таблиц и Slack не обязательно должны быть активными. И когда я имею в виду живое, я имею в виду настоящее живое, как разговор в чате ⇒ он должен быть живым. При использовании фоновых функций задержка составляет 5 с, что приемлемо во многих случаях.

Функции справа являются внутренними, их могут вызывать только люди и службы с правильным доступом, а не случайный бот или злоумышленник.

Уверяю вас, это уже дает огромную разницу в безопасности (а также делает их многоразовыми, но это другая тема).

Эти фоновые функции могут периодически запускаться через Cloud Scheduler, после события через Eventarc или через другие облачные функции.

Я знаю, что ты думаешь…

В предложенной структуре фоновые функции вызываются функциями HTTPS, это ничего не меняет…

Истинный!

Верно… Во-первых, я просто хотел прояснить тот факт, что облачные функции не обязательно должны быть HTTP, и может быть много случаев, когда фоновой функции достаточно, и к ней никогда не обращаются функцией HTTP. Поэтому 100% скрыто.

Теперь, что, если нам все еще нужна функция http?

Защита функций, запускаемых HTTP — OAuth

Если требуется HTTP-функция (т. е. запрос отправлен и ожидает ответа), то облачная функция должна иметь процесс аутентификации.

Google Cloud предлагает защитить облачные функции с помощью ролей и разрешений IAM, используя учетную запись службы.

Для этого решения требуется токен авторизации носителя для аутентификации, решение простое и безопасное, поскольку токен периодически меняется.

Давайте посмотрим, как это работает:

Сначала создайте Облачную функцию, выберите триггер HTTP и установите флажок Требовать аутентификацию.

Если вы используете gcloud, и вам следует использовать gcloud, потому что вы прочитали Рекомендации по работе с облачными функциями (1/4): подготовьте среду: добавьте --no-allow-unauthenticated в конце командной строки развертывания.

После развертывания функции вставьте указанный URL-адрес в браузер. Вы должны увидеть: Ошибка: Запрещено. У вашего клиента нет разрешения на получение URL-адреса /your_function с этого сервера.

Большой! Как мы можем назвать это тогда?

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

gcloud functions call your_function --region europe-west2

Будет предложен следующий результат:

gcloud functions call your_function --region europe-west2
> executionId: 4fd95u1gge60
> result: HELLO BRO

Согласен, этот способ не так уж и полезен, разве что для тестирования.

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

Получите токен Google Identity с помощью gcloud:

gcloud auth print-identity-token

В Postman вставьте URL-адрес функции и добавьте

Authorization bearer eyJhbGciOiJ…

атрибут с токеном идентификации.

Также полезно для тестирования, теперь давайте вызовем защищенную функцию из другой функции.

Вызывать функцию из другой функции?

Чтобы вызвать защищенную функцию из другой функции, нам необходимо авторизовать и идентифицировать вызывающую сторону.

Основная идея:

  • Разрешить вызов функции_1 из функции_2 с помощью сервисной учетной записи функции_2 (помните часть 1?)
  • В функции function_2 создайте идентификатор, подписанный Google, с помощью библиотек проверки подлинности, как показано ниже, чтобы сгенерировать этот токен.
  • Включите токен ID в заголовок Authorization: Bearer ID_TOKEN в запросе к функции.

Полный код функции_2 для вызова функции_1 (защищено):

Чтобы узнать больше, официальная документация очень понятна: https://cloud.google.com/functions/docs/securing/authenticating#authenticating_function_to_function_calls

Защита функций, запускаемых HTTP — VPC

Эта статья была бы неполной, если бы я не упомянул VPC.

Функция может быть установлена ​​внутри VPC. VPC Service Controls — это набор инструментов, которые вы можете применять для ограничения доступа к сервисам Google в своих проектах GCP.

Это похоже на большую коробку, в которой мы можем найти некоторые сервисы GCP, способные взаимодействовать друг с другом без требований к аутентификации.

Эта коробка недоступна из внешнего мира, что обеспечивает идеальную безопасность.

Процесс немного сложный и требует определенных сетевых навыков, я не буду вдаваться в подробности, потому что эта статья и так слишком длинная, но вы можете найти все шаги в официальной документации.

Защита функций, запускаемых HTTP — ключ API

Для некоторых случаев использования OAuth 2 не является идеальным решением, но мы все равно хотим фильтровать запросы. Например, если функция должна быть доступна внешнему приложению, нам нужен ключ API.

Что ж… Облачные функции Google изначально не поддерживают этот тип аутентификации.

Если ключи API не предлагаются для защиты конечной точки API, это связано с более высоким риском безопасности.

Нам нужно добавить дополнительный уровень: шлюз API!

API Gateway — это прежде всего шлюз. Это означает, что он может управлять нашими API, направлять запросы в соответствующие функции, регистрировать и отслеживать запросы, а также имеет встроенные механизмы, включая аутентификацию и проверку ключа.

Помимо этих плюсов, API Gateway позволяет нам добавить ключ API перед всеми функциями, указанными в конфигурации.

Круто ху?

Для создания шлюза нам потребуется:

  • Облачная функция с --no-allow-unauthenticated ⇒ это защитит нашу функцию.
  • API Gateway включен для проекта GC
  • Файл конфигурации YAML

Файл YAML будет выглядеть так:

И это все!

Это решение не идеально, и его следует использовать только в том случае, если внешний сервис использует API!

Чтобы увидеть весь процесс шаг за шагом, я рекомендую эту широко известную статью:



Ограничение скорости

После того, как вы привыкнете к API Gateway, вы можете захотеть защитить свои маршруты от DDoss-атак и флуда любого рода. Вы можете сделать это, используя ограничение скорости.

Это возможно с облачными функциями только при использовании шлюза API (по состоянию на январь 2023 г.).

Ограничение скорости позволит вам защитить свой маршрут с помощью ключа API, который вы отправите пользователям API. Вы можете давать разные ключи API разным пользователям и ограничивать их количество вызовов в минуту.

Процесс довольно прост и объясняется в этой блестящей статье:



Периодически обновлять

Когда вы развертываете свою функцию, Cloud Functions загружает и устанавливает зависимости, объявленные в requirements.txt, package.json или любом другом файле в зависимости от вашего языка.

Ага! Google Cloud Functions загружает и устанавливает зависимости во время развертывания!

Это означает две вещи:

  • Как только ваша функция развернута, она не обновляет сам пакет.
  • Ставить «^» или «›» для того, чтобы сказать «возьмите последнюю доступную версию» при объявлении требуемых пакетов, рискованно и может привести к сбоям.

Пакеты, используемые в функциях, будут обновляться только тогда, когда будут развернуты функции

Таким образом, если в пакете есть утечка безопасности, исправлены критические ошибки в новой версии, если опубликована более быстрая версия, если случай сбоя обрабатывается более новой версией… Все функции, использующие эти пакеты, будут работать!

Выше я сказал, что ставить «^» или «›», чтобы сказать «эй, возьми последнюю доступную версию», рискованно: представьте случай, когда вы создали функцию в прошлом году, не обновляли ее в течение года (плохо), и вы просто хотите обновить строку из функции, код не изменился, только строка.

Вы говорите своему боссу: «О, это занимает 10 секунд, чтобы обновить это!» и вы развертываете…BAM CRASH

Сбой из-за того, что один из пакетов, использовавшихся в прошлом году, удалил метод или обновил его зависимости. И потому что во время развертывания вы сказали: «Эй, возьми последнюю доступную версию». Теперь у вас новая версия, несовместимая с другими пакетами. Теперь вам нужно копнуть глубже, чтобы найти проблему, исправить и обновить свою функцию.

У меня так случилось в этом году с обновлением от Flask и itsdangerous.

Та же ситуация и с языками выполнения. Google не обновляет автоматически используемый базовый образ для уже развернутых функций.

14 ноября 2022 года команда GCF устранила утечку в системе безопасности. Единственным способом быть в курсе последних событий было повторное развертывание всех GCF.

⇒ Периодически переустанавливайте свои Функции, чтобы они всегда были актуальными. И при необходимости обновите языки выполнения (не пропускайте тестовую часть, даже если код не редактировался).

Секретный менеджер

Не так связано с самими облачными функциями, но все же является важной концепцией безопасности:

  • Установка паролей, API-ключа напрямую в код Cloud Functions — это ужасно
  • Установка паролей, ключ API в переменной env ужасен
  • Установка паролей, ключ API в переменной env с использованием собственной реализации Secret Manager лучше, но не идеально

Если вы используете пароли, ключи API или любые учетные данные в своих облачных функциях, используйте Secret Manager для их хранения. Таким образом, вы можете контролировать доступ и обновлять их в одном месте.

Чтобы использовать Secret Manager в Google Cloud Functions, я настоятельно рекомендую клиентскую библиотеку Secret Manager или нативную реализацию с секретом, смонтированным как том.

Вы можете проверить эту широко читаемую статью, чтобы получить полное сравнение и объяснение методов:



Пока

Я надеюсь, что эта статья выведет вашу разработку облачных функций на более безопасный уровень.

Предыдущие части:

‹‹‹ Рекомендации по использованию облачных функций (1/4): подготовьте среду

‹‹‹ Рекомендации по использованию облачных функций (2/4): Оптимизация облачных функций

Следующая часть:

Рекомендации по работе с облачными функциями (4/4): отслеживание и регистрация выполнения ›››

Спасибо, что читаете, спасибо за поддержку!

Не забудьте изменить мир!