Межклиентское хранилище BLOB-объектов Azure SAS завершается сбоем с сообщением «Подпись не соответствует» с использованием UserDelegatedCredential и azure-sdk-for-go

Моя настройка инфраструктуры выглядит следующим образом:

Есть два арендатора – арендатор А и арендатор Б.

В Тенанте А у меня есть группа ресурсов с кластером k8s, управляемым удостоверением, учетной записью хранения с контейнером и другими ресурсами. В арендаторе Б у меня есть группа ресурсов только с управляемым удостоверением и учетной записью хранения с контейнером.

Я могу успешно писать из приложения, работающего в кластере k8s в Тенанте А, в контейнер хранилища в Тенанте Б. Я могу это сделать, потому что в Тенанте Б я назначил роли Storage Account Contributor и Storage Blob Data Owner управляемому удостоверению в той же группе ресурсов. Затем я создал федеративные учетные данные, которые позволяют кластеру k8s в арендаторе A выполнять аутентификацию управляемого удостоверения в арендаторе B, используя следующую команду:

az identity federated-credential create \
  --name fc-x-tenant-test \
  --identity-name tenantB-managedIdentity \
  --resource-group tenantB-rg \
  --issuer <Tenant A AKS Cluster OIDC Provider URL> \
  --subject system:serviceaccount:default:<Tenant A Service Account name>

(Я подписался на https://paulyu.dev/article/cross-tenant-workload-identity-on-aks/ , чтобы настроить это - спасибо https://stackoverflow.com/users/21008480/pauldotyu !)

Затем, используя azure-sdk-for-go, я создаю клиент (проверяю err != nil, но для простоты опускаю здесь)

url := fmt.Sprintf("https://%s.blob.core.windows.net", accountName)
opts := &azidentity.WorkloadIdentityCredentialOptions{
    TenantID: *tenantBID,
    ClientID: *tenantBmanagedIdentityClientID,
}
cred, err := azidentity.NewWorkloadIdentityCredential(opts)
client, err := azblob.NewClient(url, cred, nil)
serviceClient := client.ServiceClient()
containerClient := serviceClient.NewContainerClient(containerName)

Оттуда я могу успешно читать и писать в контейнер. Примечание. При создании учетных данных мне необходимо указать идентификатор клиента и идентификатор клиента управляемого удостоверения из клиента B, иначе SDK попытается пройти проверку подлинности с помощью клиента A — клиента, в котором находится кластер AKS!


Однако когда я пытаюсь создать предварительно подписанный URL-адрес, чтобы клиент мог помещать объекты в этот контейнер, это не работает. Подписанный URL-адрес выглядит так https://<Tenant B Storage Account Name>.blob.core.windows.net/<Tenant B Container Name>/path/to/object/metadata.json?se=2024-05-03T01%3A33%3A10Z&sig=KL3t1EGRODo4%2FUUu5Irtaw11vXwIEpBkO2sYTqrTg5o%3D&sp=w&spr=https&sr=b&sv=2023-11-03. И я получаю ошибку 403 с

ErrorCode:AuthenticationFailed
authenticationerrordetail:Signature did not match

Я создаю предварительно подписанный URL-адрес с помощью следующего кода (используя client, созданный, как показано выше, с идентификатором арендатора B и идентификатором клиента управляемой идентификации).

serviceClient := client.ServiceClient()
now := time.Now().UTC().Add(-10 * time.Second)
expiry := now.Add(24 * time.Hour)
info := azblobService.KeyInfo{
    Start:  to.Ptr(now.UTC().Format(sas.TimeFormat)),
    Expiry: to.Ptr(expiry.UTC().Format(sas.TimeFormat)),
}
udc, err := serviceClient.GetUserDelegationCredential(ctx, info, nil)
sigVals := sas.BlobSignatureValues{
    Protocol:      sas.ProtocolHTTPS,
    ExpiryTime:    time.Now().Add(24 * time.Hour).UTC(),
    Permissions:   perm.String(),
    ContainerName: containerName,
    BlobName:      blobName,
}
query, err := sigVals.SignWithUserDelegation(udc)

Затем я вызываю query.Encode(), генерируя подписанный URL-адрес.

Мне удалось записать значение query, оно находится здесь ниже. Два поля с отредактированы, но в описании описано, какие значения там были.

Query {
    version:2023-11-03 
    services: 
    resourceTypes: 
    protocol:https 
    startTime:{wall:0 ext:0 loc:<nil>} 
    expiryTime:{wall:151009920 ext:63850466819 loc:<nil>} 
    snapshotTime:{wall:0 ext:0 loc:<nil>} 
    ipRange:{Start:[] End:[]} 
    identifier: 
    resource:b 
    permissions:w 
    signature:bDcn0MCs1kGakVX9OOtKt/ApBllv68AJLZnwZK4g+5g= 
    cacheControl: contentDisposition: contentEncoding: contentLanguage: contentType: 
    signedOID:<Tenant B Managed Identity Object ID> 
    signedTID:<Tenant B Tenant ID> 
    signedStart:{wall:0 ext:63850380409 loc:<nil>} 
    signedService:b 
    signedExpiry:{wall:0 ext:63850466809 loc:<nil>} 
    signedVersion:2023-11-03 
    signedDirectoryDepth: authorizedObjectID: unauthorizedObjectID: correlationID: encryptionScope: stTimeFormat: seTimeFormat:
}

Здесь я заметил, что сроки истечения выглядят странно. Тем не менее, я подтвердил, что с expiryTime все в порядке, потому что мне удалось заставить SAS работать в арендаторе A, используя общие ключи доступа, и его expiryTime было установлено таким образом.

Подписанное время начала/окончания срока тоже поначалу выглядело не так. Итак, я записал значения, и они выглядели правильно. Затем я попытался посмотреть, что произойдет, если я сделаю так, чтобы signedStart было равно {wall:0 ext:0 loc:<nil>}, и проблема не исчезла. (Вы можете увидеть, что означают все эти значения, в разделе Создание пользовательской делегации SAS. .)

Я также включил журналы для целевой учетной записи хранения в Tenant B и могу видеть успешные вызовы PutBlob, GetBlob и DeleteBlob в контейнере $logs (для операций чтения/записи между арендаторами, которые не используют SAS). Однако я не вижу журналов неудачных запросов SAS PUT, которые продолжают отклоняться.

Обновлено: обновление, теперь я воспроизвел ту же проблему со всем в одном арендаторе.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
108
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема заключалась в том, что я не мог указать BlobName в BlobSignatureValues при подписании с помощью UserDelegationCredential.

Однако вы можете указать там BlobName, когда я использую SharedKeyCredential и подписываюсь с помощью SignWithSharedKey

Другие вопросы по теме

Использование Azure DataLakeServiceClient для загрузки файла получило запрещенный ответ после нескольких минут успеха
Проблема с созданием URL-адреса SAS хранилища BLOB-объектов Azure с управляемым удостоверением
IngestionTimestamp содержит символ %3A вместо точки с запятой
Невозможно отправить большой двоичный объект в Azure – отсутствует исключение HTTP-заголовка
Неверный запрос – пустое сообщение – нулевая ошибка из ADF – копирование конвейера данных
Функция Azure v2 (python) для фрагментации данных в цикле for не выводит данные в хранилище BLOB-объектов
Тема системы сетки событий Azure не реагирует на загрузку нового большого двоичного объекта
Не удалось выполнить правильные действия по копированию данных контейнера BLOB-объектов в ADF в двух разных подписках в одном клиенте
Объедините триггер Blob, ввод и вывод в 1 функцию Azure v2 (Python)
Несколько учетных записей хранения и контейнеры Terraform