Безопасное развертывание в Cloud Run

1. Обзор

Вы измените шаги по умолчанию для развертывания службы в Cloud Run, чтобы повысить безопасность, а затем увидите, как получить безопасный доступ к развернутому приложению. Приложение представляет собой «партнерскую службу регистрации» приложения Cymbal Eats, которое используется компаниями, сотрудничающими с Cymbal Eats, для обработки заказов на еду.

Что вы узнаете

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

Затем вы увидите, как авторизовать доступ к приложению и сделать авторизованные запросы.

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

2. Настройка и требования

Самостоятельная настройка среды

  1. Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google. Вы можете обновить его в любое время.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (нельзя изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно он обозначается как PROJECT_ID ). Если вам не нравится сгенерированный идентификатор, вы можете создать другой случайный идентификатор. Кроме того, вы можете попробовать свой собственный и посмотреть, доступен ли он. Его нельзя изменить после этого шага, и он останется в силе на протяжении всего проекта.
  • К вашему сведению, есть третье значение — номер проекта , который используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
  1. Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Чтобы отключить ресурсы и избежать выставления счетов за пределами этого руководства, вы можете удалить созданные вами ресурсы или удалить весь проект. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .

Активировать Cloud Shell

  1. В Cloud Console нажмите «Активировать Cloud Shell». 853e55310c205094.png .

55efc1aaa7a4d3ad.png

Если вы никогда раньше не запускали Cloud Shell, вам будет представлен промежуточный экран (ниже сгиба) с описанием того, что это такое. В этом случае нажмите «Продолжить» (и вы больше никогда этого не увидите). Вот как выглядит этот одноразовый экран:

9c92662c6a846a5c.png

Подготовка и подключение к Cloud Shell займет всего несколько минут.

9f0e51b578fecce5.png

Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью просто браузера или Chromebook.

После подключения к Cloud Shell вы увидите, что вы уже прошли аутентификацию и что для проекта уже установлен ваш идентификатор проекта.

  1. Выполните следующую команду в Cloud Shell, чтобы подтвердить, что вы прошли аутентификацию:
gcloud auth list

Вывод команды

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project

Вывод команды

[core]
project = <PROJECT_ID>

Если это не так, вы можете установить это с помощью этой команды:

gcloud config set project <PROJECT_ID>

Вывод команды

Updated property [core/project].

Настройка среды

В ходе этой лабораторной работы вы будете выполнять команды в командной строке Cloud Shell. Обычно вы можете скопировать команды и вставить их как есть, хотя в некоторых случаях вам придется изменить значения заполнителей на правильные.

  1. Задайте для переменной среды идентификатор проекта для использования в последующих командах:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
  1. Включите API службы Cloud Run, который будет запускать ваше приложение, API Firestore, который будет обеспечивать хранилище данных NoSQL, API Cloud Build, который будет использоваться командой развертывания, и реестр артефактов, который будет использоваться для хранения контейнера приложения при сборке. :
gcloud services enable \
  run.googleapis.com \
  firestore.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  1. Инициализируйте базу данных Firestore в собственном режиме. Эта команда использует API App Engine, поэтому ее необходимо сначала включить.

В команде необходимо указать регион для App Engine, который мы не будем использовать, но должны создать по историческим причинам, а также регион для базы данных. Мы будем использовать us-central для App Engine и nam5 для базы данных. nam5 — это многорегиональное местоположение США. Расположение в нескольких регионах обеспечивает максимальную доступность и надежность базы данных.

gcloud services enable appengine.googleapis.com

gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
  1. Клонируйте репозиторий примера приложения и перейдите в каталог.
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git

cd cymbal-eats/partner-registration-service

3. Просмотрите README.

Откройте редактор и просмотрите файлы, составляющие приложение. Просмотрите README.md , в котором описаны шаги, необходимые для развертывания этого приложения. Некоторые из этих шагов могут включать в себя неявные или явные решения по безопасности, которые следует учитывать. Вы измените некоторые из этих параметров, чтобы повысить безопасность развернутого приложения, как описано здесь:

Шаг 3. Запустите npm install

Важно знать происхождение и целостность любого стороннего программного обеспечения, используемого в приложении. Управление безопасностью цепочки поставок программного обеспечения актуально для создания любого программного обеспечения, а не только приложений, развертываемых в Cloud Run. Эта лабораторная работа посвящена развертыванию, поэтому не затрагивает эту область, но вы можете изучить эту тему отдельно.

Шаги 4 и 5. Отредактируйте и запустите deploy.sh

Эти шаги развернут приложение в Cloud Run, оставив для большинства параметров значения по умолчанию. Вы измените этот шаг, чтобы сделать развертывание более безопасным двумя ключевыми способами:

  1. Не разрешайте неаутентифицированный доступ. Это может быть удобно для тестирования во время исследования, но это веб-сервис для использования коммерческими партнерами, и он всегда должен аутентифицировать своих пользователей.
  2. Укажите, что приложение должно использовать выделенную учетную запись службы, имеющую только необходимые привилегии, а не учетную запись по умолчанию, которая, вероятно, будет иметь больше доступа к API и ресурсам, чем необходимо. Это известно как принцип наименьших привилегий и является фундаментальной концепцией безопасности приложений.

Шаги с 6 по 11. Сделайте образцы веб-запросов, чтобы проверить правильность поведения.

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

4. Безопасное развертывание службы

В сценарии deploy.sh были определены два необходимых изменения: запрет доступа без проверки подлинности и использование выделенной учетной записи службы с минимальными привилегиями.

Сначала вы создадите новую учетную запись службы, затем отредактируете сценарий deploy.sh , чтобы он ссылался на эту учетную запись службы и запретил неаутентифицированный доступ, а затем развернули службу, запустив измененный сценарий, прежде чем мы сможем запустить измененный сценарий Deploy.sh.

Создайте учетную запись службы и предоставьте ей необходимый доступ к Firestore/Datastore.

gcloud iam service-accounts create partner-sa

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:partner-sa@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role=roles/datastore.user

Редактировать deploy.sh

Измените файл deploy.sh , чтобы запретить доступ без проверки подлинности (–no-allow-unauthenticated) и указать новую учетную запись службы (–service-account) для развернутого приложения. Исправьте GOOGLE_PROJECT_ID, чтобы он был идентификатором вашего собственного проекта.

Вы удалите первые две строки и измените три другие строки, как показано ниже.

gcloud run deploy $SERVICE_NAME \
  --source . \
  --platform managed \
  --region ${REGION} \
  --no-allow-unauthenticated \
  --project=$PROJECT_ID \
  --service-account=partner-sa@${PROJECT_ID}.iam.gserviceaccount.com

Развертывание службы

Из командной строки запустите скрипт deploy.sh :

./deploy.sh

После завершения развертывания в последней строке вывода команды будет отображен URL-адрес службы нового приложения. Сохраните URL-адрес в переменной среды:

export SERVICE_URL=<URL from last line of command output>

Теперь попробуйте получить заказ из приложения с помощью инструмента curl :

curl -i -X GET $SERVICE_URL/partners

Флаг -i для команды curl указывает ей включать заголовки ответов в выходные данные. Первая строка вывода должна быть:

HTTP/2 403

Приложение было развернуто с возможностью запретить неаутентифицированные запросы. Эта команда Curl не содержит информации для аутентификации, поэтому Cloud Run отклоняет ее. Фактическое развернутое приложение даже не запускается и не получает никаких данных по этому запросу.

5. Делайте аутентифицированные запросы

Развернутое приложение вызывается путем выполнения веб-запросов, которые теперь должны быть проверены, чтобы Cloud Run разрешил их. Веб-запросы аутентифицируются путем включения заголовка Authorization в форме:

Authorization: Bearer identity-token на предъявителя.

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

Сделайте запрос под своей учетной записью

Инструмент Google Cloud CLI может предоставить токен для пользователя, прошедшего проверку подлинности по умолчанию. Запустите эту команду, чтобы получить токен идентификации для вашей учетной записи и сохраните его в переменной среды ID_TOKEN:

export ID_TOKEN=$(gcloud auth print-identity-token)

По умолчанию идентификационные токены, выданные Google, действительны в течение одного часа. Запустите следующую команду Curl, чтобы выполнить запрос, который ранее был отклонен из-за несанкционированного доступа. Эта команда будет включать необходимый заголовок:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

Вывод команды должен начинаться с HTTP/2 200 , что указывает на то, что запрос приемлем и обрабатывается. (Если вы подождете час и повторите этот запрос, он завершится неудачей, поскольку срок действия токена истечет.) Тело ответа находится в конце вывода, после пустой строки:

{"status":"success","data":[]}

Партнеров пока нет.

Зарегистрируйте партнеров, используя образец данных JSON в каталоге с помощью двух команд curl :

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner.json" \
  $SERVICE_URL/partner

и

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner2.json" \
  $SERVICE_URL/partner

Повторите предыдущий запрос GET, чтобы увидеть всех зарегистрированных партнеров:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

Вы должны увидеть данные JSON с гораздо большим содержанием, предоставляющие информацию о двух зарегистрированных партнерах.

Сделать запрос от имени неавторизованной учетной записи

Аутентифицированный запрос, сделанный на последнем шаге, был выполнен успешно не только потому, что он был аутентифицирован, но и потому, что аутентифицированный пользователь (ваша учетная запись) был авторизован. То есть у учетной записи было разрешение на вызов приложения. Не все аутентифицированные учетные записи будут авторизованы для этого.

Учетная запись по умолчанию, использованная в предыдущем запросе, была авторизована, поскольку именно эта учетная запись создала проект, содержащий приложение, и по умолчанию предоставила ему разрешение на вызов любых приложений Cloud Run в этой учетной записи. При необходимости это разрешение можно отозвать, что желательно в рабочем приложении. Вместо того, чтобы делать это сейчас, вы создадите новую учетную запись службы без назначенных ей привилегий или ролей и будете использовать ее, чтобы попытаться получить доступ к развернутому приложению.

  1. Создайте сервисную учетную запись под названием tester .
gcloud iam service-accounts create tester
  1. Вы получите токен идентификации для этой новой учетной записи почти так же, как вы получили его ранее для своей учетной записи по умолчанию. Однако для этого требуется, чтобы ваша учетная запись по умолчанию имела разрешение выдавать себя за учетные записи служб. Предоставьте своей учетной записи это разрешение.
export USER_EMAIL=$(gcloud config list account --format "value(core.account)")

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$USER_EMAIL" \
  --role=roles/iam.serviceAccountTokenCreator
  1. Теперь выполните следующую команду, чтобы сохранить токен идентификации для этой новой учетной записи в переменной среды TEST_IDENTITY. Если команда показывает сообщение об ошибке, подождите минуту или две и повторите попытку.
export TEST_TOKEN=$( \
  gcloud auth print-identity-token \
    --impersonate-service-account \
    "tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
  1. Сделайте аутентифицированный веб-запрос, как и раньше, но используя этот токен идентификации:
curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

Вывод команды снова начнется с HTTP/2 403 поскольку запрос, хотя и прошел проверку подлинности , не авторизован . У новой учетной записи службы нет разрешения на вызов этого приложения.

Авторизация аккаунта

Учетная запись пользователя или службы должна иметь роль Cloud Run Invoker в службе Cloud Run, чтобы отправлять к ней запросы. Предоставьте эту роль учетной записи службы тестера с помощью команды:

export REGION=us-central1
gcloud run services add-iam-policy-binding ${SERVICE_NAME} \
  --member="serviceAccount:tester@$PROJECT_ID.iam.gserviceaccount.com" \
  --role=roles/run.invoker \
  --region=${REGION}

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

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

Вывод команды теперь начинается с HTTP/1.1 200 OK , а последняя строка содержит ответ JSON. Этот запрос был принят Cloud Run и обработан приложением.

6. Аутентификация программ и аутентификация пользователей

Аутентифицированные запросы, которые вы сделали до сих пор, использовали инструмент командной строки curl . Вместо этого можно использовать другие инструменты и языки программирования. Однако аутентифицированные запросы Cloud Run невозможно выполнить с помощью веб-браузера с простыми веб-страницами. Если пользователь нажимает ссылку или кнопку для отправки формы на веб-странице, браузер не добавит заголовок Authorization , необходимый Cloud Run для аутентифицированных запросов.

Встроенный механизм аутентификации Cloud Run предназначен для использования программами, а не конечными пользователями.

Примечание:

Cloud Run может размещать веб-приложения, ориентированные на пользователя, но такие приложения должны настроить Cloud Run на разрешение неаутентифицированных запросов от веб-браузеров пользователей. Если приложениям требуется аутентификация пользователя, приложение должно обрабатывать ее, а не запрашивать это у Cloud Run. Приложение может делать это так же, как это делают веб-приложения вне Cloud Run. То, как это делается, выходит за рамки данной кодовой лаборатории.

Возможно, вы заметили, что до сих пор ответами на примеры запросов были объекты JSON, а не веб-страницы. Это связано с тем, что эта служба регистрации партнеров предназначена для использования программами, а JSON — это удобная форма для их использования. Далее вы напишете и запустите программу для получения и использования этих данных.

Аутентифицированные запросы от программы Python

Программа может отправлять аутентифицированные запросы к защищенному приложению Cloud Run с помощью стандартных веб-запросов HTTP, но включая заголовок Authorization . Единственная новая задача для этих программ — получить действительный идентификационный токен с неистекшим сроком действия для размещения в этом заголовке. Этот токен будет проверен Cloud Run с использованием Google Cloud Identity and Access Management (IAM), поэтому токен должен быть выпущен и подписан органом, признанным IAM. Существуют клиентские библиотеки, доступные на многих языках, которые программы могут использовать для запроса выдачи такого токена. В этом примере будет использоваться клиентская библиотека Python google.auth . Существует несколько библиотек Python для выполнения веб-запросов в целом; в этом примере используется популярный модуль requests .

Первым шагом является установка двух клиентских библиотек:

pip install google-auth
pip install requests

Код Python для запроса токена идентификации для пользователя по умолчанию :

credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token

Если вы используете командную оболочку, такую ​​как Cloud Shell, или стандартную терминальную оболочку на своем компьютере, пользователем по умолчанию будет тот, кто прошел аутентификацию внутри этой оболочки. В Cloud Shell это обычно пользователь, вошедший в Google. В других случаях это любой пользователь, аутентифицированный с помощью gcloud auth login или другой команды gcloud . Если пользователь никогда не входил в систему, пользователя по умолчанию не будет, и этот код завершится ошибкой.

Для программы, отправляющей запросы к другой программе, вы обычно хотите использовать не личность человека, а личность запрашивающей программы. Для этого и нужны сервисные аккаунты. Вы развернули службу Cloud Run с помощью выделенной учетной записи службы, которая предоставляет удостоверение, которое она использует при отправке запросов API, например к Cloud Firestore. Когда программа запускается на платформе Google Cloud, клиентские библиотеки автоматически используют назначенную ей учетную запись службы в качестве идентификатора по умолчанию, поэтому в обеих ситуациях работает один и тот же программный код.

Код Python для выполнения запроса с добавленным заголовком авторизации:

auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get(url, headers=auth_header)

Следующая полная программа Python отправит аутентифицированный запрос к службе Cloud Run, чтобы получить всех зарегистрированных партнеров, а затем распечатать их имена и назначенные идентификаторы. Скопируйте и выполните команду ниже, чтобы сохранить этот код в файле print_partners.py .

cat > ./print_partners.py << EOF
def print_partners():
    import google.auth
    import google.auth.transport.requests
    import requests

    credentials, _ = google.auth.default()
    credentials.refresh(google.auth.transport.requests.Request())
    identity_token = credentials.id_token

    auth_header = {"Authorization": "Bearer " + identity_token}
    response = requests.get("${SERVICE_URL}/partners", headers=auth_header)

    parsed_response = response.json()
    partners = parsed_response["data"]

    for partner in partners:
        print(f"{partner['partnerId']}: {partner['name']}")


print_partners()
EOF

Вы запустите эту программу с помощью команды оболочки. Сначала вам необходимо будет пройти аутентификацию в качестве пользователя по умолчанию, чтобы программа могла использовать эти учетные данные. Запустите команду gcloud auth ниже:

gcloud auth application-default login

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

python print_partners.py

Вывод будет выглядеть примерно так:

10102: Zippy food delivery
67292: Foodful

Запрос программы достиг службы Cloud Run, поскольку он был подтвержден вашей личностью, а вы являетесь владельцем этого проекта и, следовательно, имеете право запускать его по умолчанию. Чаще всего эта программа запускается под учетной записью службы. При запуске в большинстве продуктов Google Cloud, таких как Cloud Run или App Engine, удостоверением по умолчанию будет учетная запись службы, которая будет использоваться вместо личной учетной записи.

7. Поздравляем!

Поздравляем, вы завершили работу над кодом!

Что дальше:

Ознакомьтесь с другими лабораториями Cymbal Eats:

Очистить

Чтобы избежать списания средств с вашей учетной записи Google Cloud за ресурсы, используемые в этом руководстве, либо удалите проект, содержащий ресурсы, либо сохраните проект и удалите отдельные ресурсы.

Удаление проекта

Самый простой способ избавиться от выставления счетов — удалить проект, созданный вами для этого руководства.