Google Cloud Tasks не может пройти аутентификацию в Cloud Run

Я пытаюсь вызвать службу Cloud Run с помощью Cloud Tasks, как описано в здесь.

У меня работает служба Cloud Run. Если я сделаю службу общедоступной, она будет вести себя так, как ожидалось.

Я создал облачную очередь и планирую облачную задачу с помощью локального сценария. Этот использует мою учетную запись. Скрипт выглядит так

from google.cloud import tasks_v2

client = tasks_v2.CloudTasksClient()

project = 'my-project'
queue = 'my-queue'
location = 'europe-west1'
url = 'https://url_to_my_service'

parent = client.queue_path(project, location, queue)

task = {
        'http_request': {
            'http_method': 'GET',
            'url': url,
            'oidc_token': {
               'service_account_email': '[email protected]'
            }
        }
}

response = client.create_task(parent, task)
print('Created task {}'.format(response.name))

Я вижу, что задача появляется в очереди, но она терпит неудачу и немедленно повторяет попытку. Причина этого (путем проверки журналов) заключается в том, что служба Cloud Run возвращает ответ 401.

У моего собственного пользователя есть роли Создатель токена учетной записи службы и Пользователь учетной записи службы. В нем явно нет очереди облачных задач, но, поскольку я могу создать задачу в очереди, я полагаю, что унаследовал необходимые разрешения. Учетная запись службы [email protected] (которую я использую в задаче для получения токена OIDC) имеет, среди прочего, следующие роли:

  • Cloud Tasks Enqueuer (хотя я не думаю, что он ему нужен, поскольку я создаю задачу со своей учетной записью)
  • Средство выполнения задач Cloud Tasks
  • Наблюдатель облачных задач
  • Создатель токена учетной записи службы (я не уверен, следует ли ее добавлять в мою учетную запись - ту, которая планирует задачу, - или в учетную запись службы, которая должна выполнять вызов Cloud Run)
  • Пользователь учетной записи службы (здесь же)
  • Cloud Run Invoker

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

curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" https://url_to_my_service

Это работает! (Кстати, он также работает, когда я снова переключаюсь на свою учетную запись)

Заключительные тесты: если я уберу oidc_token из задачи при ее создании, я получу ответ 403 от Cloud Run! Не 401 ... Если я удалю роль Cloud Run Invoker из учетной записи службы и попробую снова локально с помощью curl, я также получу 403 вместо 401.

Если я наконец сделаю сервис Cloud Run общедоступным, все заработает.

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

Что мне не хватает?


person bartaelterman    schedule 09.04.2020    source источник
comment
Я тоже ... внимательно следил за документами: https://cloud.google.com/tasks/docs/creating-http-target-tasks, но я получаю 401 ответ от целевой службы. Учетной записи службы, помещающей задачу в очередь, должны быть только разрешения 1. Cloud Tasks Enqueuer 2. Service Account User 3. Cloud Run Invoker (Или вызывающий объект для той службы Google, на которую вы нацеливаетесь). Адрес электронной почты учетной записи службы, добавленной в очередь, добавляется к задаче перед ее постановкой в ​​очередь, чтобы очередь облачных задач могла использовать ее для генерации токена ... Я собираюсь посмотреть, разрешится ли эта проблема в течение 24 часов, как это сделала ваша. Это очень неприятно   -  person teaMonkeyFruit    schedule 04.05.2020
comment
Возможно, также стоит отметить: недавно я также получил 401 ответ при попытке запустить Cloud Run из Cloud Scheduler. Я также использовал токен OIDC, и оказалось, что я установил неправильный URL в аудитории. Для облачных задач кажется, что получение токена OIDC происходит за кулисами, но теперь у меня такое чувство, что что-то здесь не так.   -  person bartaelterman    schedule 05.05.2020
comment
Я понял. И меня расстраивает то, что мне приходилось решать эту проблему раньше ... Если вы явно не заполняете поле audience для oidc_token, тогда используется целевой URL-адрес из задачи, в вашем примере выше: https://url_to_my_service. Проблема здесь в том, что если вы используете Cloud Run с пользовательскими доменами (вместо домена, сгенерированного облачным запуском), вы получите сообщение об ошибке, потому что OIDC audience не поддерживает пользовательские домены. Мое исправление заключалось в том, чтобы явно заполнить аудиторию URL-адресом, сгенерированным Cloud Run, и тогда это сработало.   -  person teaMonkeyFruit    schedule 15.05.2020


Ответы (4)


У меня была такая же проблема, вот и мое исправление:

Диагностика. Создание токенов OIDC в ​​настоящее время не поддерживает пользовательские домены в параметре audience. Я использовал личный домен для своей облачной службы (https://my-service.my-domain.com) вместо URL-адреса, сгенерированного облачным запуском (находится на панели управления облачной службы), который выглядит следующим образом: https://XXXXXX.run.app

Поведение при маскировке: если задача ставится в очередь в облачные задачи, если поле audience для oidc_token не задано явно, то целевой URL из задачи используется для установки audience в запросе на токен OIDC. .

В моем случае это означало, что постановка задачи в очередь для отправки целевой https://my-service.my-domain.com/resource аудитории для генерации токена OIDC была установлена ​​в мой личный домен https://my-service.my-domain.com/resource. Поскольку пользовательские домены не поддерживаются при генерации токенов OIDC, я получал 401 not authorized ответов от целевой службы.

Мое исправление: явным образом заполните аудиторию URL-адресом, созданным Cloud Run, чтобы выдать действительный токен. В моем клиенте я смог глобально настроить аудиторию для всех задач, нацеленных на данную службу, с базовым URL: 'audience' : 'https://XXXXXX.run.app'. Это сгенерировало действительный токен. Мне не нужно было менять URL-адрес самого целевого ресурса. Ресурс остался прежним: 'url' : 'https://my-service.my-domain.com/resource'

Дополнительная информация. Я уже сталкивался с этой проблемой раньше при настройке межсетевой аутентификации: Google Cloud Run Authentication Service-to-Service

person teaMonkeyFruit    schedule 15.05.2020
comment
Спасибо за эту разбивку, это помогло мне понять, что моя проблема связана с аудиторией. В частности, используя облачную функцию в качестве обработчика, но с суффиксом пути, мне нужно было явно установить суффикс пути без указания аудитории, чтобы он работал. - person darnmason; 14.08.2020

1. С помощью этого кода я создал службу запуска частного облака. :

import os

from flask import Flask
from flask import request


app = Flask(__name__)

@app.route('/index', methods=['GET', 'POST'])
def hello_world():
    target = os.environ.get('TARGET', 'World')
    print(target)
    return str(request.data)

if __name__ == "__main__":
    app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
   

2. Я создал учетную запись службы с --role=roles/run.invoker, которую я свяжу с облачной задачей.

 gcloud iam service-accounts create SERVICE-ACCOUNT_NAME \
 --display-name "DISPLAYED-SERVICE-ACCOUNT_NAME"  
 gcloud iam service-accounts list

 gcloud run services add-iam-policy-binding SERVICE \
 --member=serviceAccount:[email protected] \ 
 --role=roles/run.invoker 

3.Я создал очередь

gcloud tasks queues create my-queue

4. Я создаю test.py

from google.cloud import tasks_v2
from google.protobuf import timestamp_pb2
import datetime

# Create a client.
client = tasks_v2.CloudTasksClient()

# TODO(developer): Uncomment these lines and replace with your values.
project = 'your-project'
queue = 'your-queue'
location = 'europe-west2' # app engine locations
url = 'https://helloworld/index'
payload = 'Hello from the Cloud Task'

# Construct the fully qualified queue name.
parent = client.queue_path(project, location, queue)

# Construct the request body.
task = {
        'http_request': {  # Specify the type of request.
            'http_method': 'POST',
            'url': url,  # The full url path that the task will be sent to.
            'oidc_token': {
                'service_account_email': "your-service-account"
            },
             'headers' : {
             'Content-Type': 'application/json',
           }
        }
}

# Convert "seconds from now" into an rfc3339 datetime string.
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=60)

# Create Timestamp protobuf.
timestamp = timestamp_pb2.Timestamp()
timestamp.FromDatetime(d)

# Add the timestamp to the tasks.
task['schedule_time'] = timestamp
task['name'] = 'projects/your-project/locations/app-engine-loacation/queues/your-queue/tasks/your-task'


converted_payload = payload.encode()

# Add the payload to the request.
task['http_request']['body'] = converted_payload


# Use the client to build and send the task.
response = client.create_task(parent, task)

print('Created task {}'.format(response.name))
#return response

5. Я запускаю код в Google Cloud Shell с моей учетной записью пользователя с ролью владельца.

6. Полученный ответ имеет вид:

Created task projects/your-project/locations/app-engine-loacation/queues/your-queue/tasks/your-task

7.Проверьте логи, успех

введите описание изображения здесь

person marian.vladoi    schedule 10.04.2020
comment
Спасибо, что поделились этим. Единственная разница, которую я вижу в том, что я делаю, - это способ привязки роли к учетной записи службы. Я использовал интерфейс консоли, пока вы используете команду gcloud. Будет ли это иметь значение? - person bartaelterman; 10.04.2020

На следующий день я больше не могу воспроизвести эту проблему. Я могу воспроизвести 403 ответа, удалив роль Cloud Run Invoker, но я больше не получаю 401 ответ с точно таким же кодом, как вчера. Полагаю, это была временная проблема со стороны Google?

Кроме того, я заметил, что до того, как обновленные политики вступят в силу, требуется некоторое время (1-2 минуты).

person bartaelterman    schedule 10.04.2020

Для тех, кто, как я, борется с документацией и stackoverflow при наличии непрерывных UNAUTHORIZED ответов на HTTP-запросы Cloud Tasks:

Как было написано в ветке, вам лучше предоставить audience для oidcToken, которые вы отправляете в CloudTasks. Убедитесь, что запрошенный URL-адрес точно соответствует вашему ресурсу.

Например, если у вас есть облачная функция с именем my-awesome-cloud-function, а URL-адрес запроса задачи - https://REGION-PROJECT-ID.cloudfunctions.net/my-awesome-cloud-function/api/v1/hello, вам необходимо убедиться, что вы установили сам URL-адрес функции.

{ 
  serviceAccountEmail: [email protected],
  audience: https://REGION-PROJECT-ID.cloudfunctions.net/my-awesome-cloud-function 
}

В противном случае кажется, что используется полный URL-адрес, что приводит к ошибке.

person Kostiantyn Levytskyi    schedule 09.07.2021