Passwordless Journey
В документе будут записаны всякие демки и примеры как мы пытались понять сможем ли мы сделать эдакий passwordless общение между своими сервисами
На выходе получиться что то типа такого
Но перед тем как запрыгивать во всю эту историю нужно понять как работает более простой сетап
История 1: S3
Сценарий такой - мы хотим S3 только ажурный
Для этого создаем в azure storage account
Выбираем любую из подписок, создаем свою ресурсную группу, название и дальше все настройки можно оставить по умолчанию
Примечание: здесь и далее желательно себе выписывать что создается и меняется, что бы затем можно было почистить за собой, так же очень удобно, везде где можно использовать одну и ту же ресурсную группу - можно будет из одного места все грохнуть
Так, создал я сам storage account, но теперь внутри него нужно создать так называемый container
Опять таки все по умолчанию, нам особо не интересует что там приватное, что публичное и вот это все
Ну и следом туда руками закинуть какой то файлик
Далее идем в раздел Access keys и забираем свои ключи доступа
Гуглим как это дело правильно готовить со стороны dotnet и попадаем на вот эту статью
У меня получилось что то типа такого:
using Azure.Storage.Blobs;
// dotnet add package Azure.Storage.Blobs
var client = new BlobServiceClient("DefaultEndpointsProtocol=https;AccountName=mactemp;AccountKey=xxxxxxxxxxxxx;EndpointSuffix=core.windows.net");
var container = client.GetBlobContainerClient("demo");
foreach(var blob in container.GetBlobs()) {
Console.WriteLine(blob.Name);
}Короче не ракето-строение и на деле такое же простое как S3
Дальше начинается самое интересное и по сути наше первое знакомство с passwordless подходами
Всем этим делом будет заправлять пакет Azure.Identity вот пример кода
using Azure.Identity;
using Azure.Storage.Blobs;
// dotnet add package Azure.Storage.Blobs
// dotnet add package Azure.Identity
var client = new BlobServiceClient(new Uri("https://mactemp.blob.core.windows.net"), new DefaultAzureCredential());
var container = client.GetBlobContainerClient("demo");
foreach(var blob in container.GetBlobs()) {
Console.WriteLine(blob.Name);
}Вот эта штука DefaultAzureCredential под капотом перебирает все возможные сценарии и пытается разобраться как подключиться к ажуру
Один из сценариев это az cli, который уже у всех стоит и в котором есть смысл выполнить az login, что бы освежить его токен
Но, есть еще один момент, в ключами доступа у нас уже были все все права на S3, а тут то мы логинимся как некий alexandrm@rabota.ua, соотв вопрос - а у него должны быть доступы вообще к этому делу или нет 🤔
Идем в наш S3 и в настройках IAM выдаем права
После чего наша приложенька радостно заработала
Примечание: что важно, у нас уже нет никаких паролей которые нужно было бы куда то в appsettings.json прописывать, затем в octopus deploy, затем их подменять на разных окружениях, как то прятать и ротировать
Как эта штука работает - под капотом там перебрались все возможные варианты и оно увидело что мы залогинены в az cli и за не имением ничего лучшего с этим пользователем и попробовало пойти в s3, а по скольку пользователю мы только что выдали права - все сработало
На деле ж там все работает через эти access token'ы которые jwt
К сожалению примеры с curl для storage - проще застрелиться, потому привожу два других примера что бы было нагляднее понятно что там под капотом делается
Например если мы хотим сходить в azure api
az account get-access-token --query accessToken -o tsvМы можем посмотреть что токен выписан для management.core.windows.net, и для меня
А значит, я с этим токеном могу ломиться в это api
curl -s -H "Authorization: Bearer $(az account get-access-token --query accessToken -o tsv)" 'https://management.azure.com/subscriptions?api-version=2022-09-01' | jq ".value"Ок, с локальной разработкой разобрались, все четко и красиво, но как быть с кубером 🤔
Дальше начинает интересная многоходовочка, мы создадим сервисныю учетку и в ажуре и в кубере и как бы свяжем их между собой, тем самым сделаем так что бы наше приложение бежало из под этой сервисной учетки, выдадим ей такие же права как выдавали себе, и если все сделано правильно оно один в один так же само заработает в кубере, без необходимости что либо менять в коде, appsettings, octopus, переменных окружения и прочем - просто из коробки все будет работать
Для этого идем в раздел managed identity в портале и создаем свою первую сервисную учетку.
Потрібно врахувати що є обмеження в назві. Вона валідується на стороні Кубера\октопуса.
Для валідного імені облікового запису служби дозволено використовувати лише символи нижнього регістру, цифри, '-' або '.', причому ім'я повинно починатися і закінчуватися буквою або цифрою. Наприклад, допустимі імена: my-service-account, user-service-account-1.
Как не сложно догадаться, подписка, ресурсная группа и регион те же самые, ну и дальше next, next, finish
Вуаля, готово
Далее нам понадобяться client id с этого экрана, в моем случае это
client_id: 5063dd9c-e304-4544-9e0a-d2c6651da05fИ tenant id, с ним проще, он один общий, одинаковый на все все все (условно это id нашей орг)
tenant_id: 695e64b5-2d13-4ea8-bb11-a6fda2d60c41Теперь нам нужно создать сервисную учетку в кубере, можно делать yaml, а можно вот так:
kubectl -n dev create serviceaccount mactemp
kubectl -n dev label serviceaccount mactemp "azure.workload.identity/use=true"
kubectl -n dev annotate --overwrite serviceaccount mactemp "azure.workload.identity/client-id=5063dd9c-e304-4544-9e0a-d2c6651da05f" "azure.workload.identity/tenant-id=695e64b5-2d13-4ea8-bb11-a6fda2d60c41"ну или для yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: mactemp
namespace: dev
annotations:
azure.workload.identity/client-id: 5063dd9c-e304-4544-9e0a-d2c6651da05f
azure.workload.identity/tenant-id: 695e64b5-2d13-4ea8-bb11-a6fda2d60c41
labels:
azure.workload.identity/use: "true"
imagePullSecrets:
- name: gcr.ioПримечания:
- если все ок, то
kubectl get serviceaccount mactempего покажет - оба tenant_id и client_id - не секреты, а id'ки организации и сервисной учетки в ажуре соотв.
- лейбочка
azure.workload.identity/use: "true"нужна для тулы которая уже установлена в кластере и которая будет делать магию
Многоходовка на этом еще не заканчивается, теперь идем назад в azure в свою сервисную учетку в раздел federated credentials и добавляем новые с такими параметрами
cluster issuer url: https://northeurope.oic.prod-aks.azure.com/695e64b5-2d13-4ea8-bb11-a6fda2d60c41/839148b7-1a9a-4c39-9bbb-01d35db879f4/Теперь самое время собрать и опубликовать образ нашего приложения и попробовать запустить его в кубере
Но перед этим нужно внести две правки:
spect.template.metadata.labels добавить azure.workload.identity/use: "true"
spect.template.spec.serviceAccountName указать mactemp
Полный yaml не привожу, т.к. там ничего интересного не будет, но можно запуститься даже без него командой по типу такой (тут наглядно видно что нужно поменять в yaml):
kubectl run -it --rm mactemp --image=TODO_IMAGE_URL_HERE --overrides='{"spec": { "serviceAccountName": "mactemp", "nodeSelector": {"kubernetes.io/os": "linux"}}, "metadata":{"labels":{"azure.workload.identity/use":"true"},"annotations":{"kubeforce":"skip"}}}'И оно не сработает - потому что?
Мы не выдали права, откуда ажуру знать, что некой сервисной учетке mactemp можно ходить в s3? Соотв по аналогии с тем как мы выдавали права сами себе, нужно сделать то же самое, только за вместо пользователей выбрать managed identities и нашу учетку - и вот уже после этого оно заработает
Примечание: после всех экспериментов не забыть удалить свой образ из container registry, бо там и так дофига всего и потом, спустя время будем ломать себе голову, а нужно оно или нет и можно ли удалить
Чертова магия в действии, у нас вобще нет никаких секретов вообще нигде, а как следствие нечего прятать\ротировать\отзывать и при этом все работает
Если локально мы уже разобрались что под капотом оно использует аккаунт пользователя с которым мы залогинились в az cli, то в кубере происходит следующее:
- В отдельном нейспейсе бежит спец деплоймент, который следит за кубером и как только видит что будет запускаться под с лейбочками
"azure.workload.identity/use":"true"делает две вещи:- генерит jwt токен
- подсовывает этот токен в под в папку /var/run/secrets/azure
- подсовывает в под переменные окружения о котороых знает DefaultAzureCredentials
- При запуске приложения наш DefaultAzureCredentials видит эти переменные и файлик с токеном, берет все это дело и идет в Azure что бы обменять их на access token к нашему S3
- Azure в свою очередь видит что к нему ломиться нечто, выдающее себя за
client_id: XXX(наша сервисная учетка) и утверждающее что это действительно оно прикрепив токен - Azure видит что действительно есть такая сервисная учетка, у нее есть федеративные креды,
[sub]jectсовпадают,[iss]uerсовпадают - Далее Azure верифицирует токен у issuer'а (кубера) и если все ок, генерит и отдает в ответ access token
- Profit
Примечания:
- токен внутри кубера в /var/run/secrets/azure сгенерен самим кубером, с этим токеном не выйдет сходить в s3, или другие сервисы, в нем явно указана
[aud]ience- api://AzureTokenExchange, этот токен можно использовать в azure api для обмена его на access token к целевому ресурсу - токен внутри кубере подписан секретом кубера, о котором azure ничего не знает (условно у нас кубер вообще может где то на нашем сервере работать) именно потому ажур верифицирует его
- хоть наш кубер и внутри ажура, был бы логичным вопрос - почему бы сразу не выписать access token - ответ прост - access token куда? в s3, или sql или что то еще - на перед же система не знает, плюс не стоит забывать что access token это короткоживущий токен, так что было бы странно если бы нам его подсунули, а условно через час он уже протух
- вся прелесть этого подхода с обменом токенов в том что он не завязан на ажур и может работать в любых комбинациях, условно у нас может быть свой oidc сервер, и мы бы в федеративные креды добавили бы его, а не кубер и тем самым могли бы авторизироваться под своими учетками, та же история с aws, github, и многими другими
- вторая прелесть - это то что у каждой системы есть свои секреты, которыми подписываются токены, но при этом никто о них не знает
- ну и последнее и самое важное - в нашем приложении вообще никаких секретов нет, хоть в публичный докер хаб можно отправлять, но самое главное - больше никакой истории о том что секреты нужно ротировать и вот это все что безопасники любят
- что бы можно было нормально провалиться в приложение - есть смысл из него сделать web api что бы оно не сразу же закрывалось, затем провалиться внутрь через kubectl exec, очень рекомендую посмотреть в jwt.io что там за токен и его aud, iss и вот это все
- подробнее о том какие есть опции обмена токанами описано тут
App Registrations
По мимо сервисных учеток в Azure, есть так называемые App Registrations, можно найти в разделе Active Directory
Примечания:
- tenant_id - который одинаковый для всего на свете - это по сути id нашей орг, и по сути id нашей active directory, и одниаковый во всех подписках и окружениях
- app registration - глобально регистрируемое во всем мире приложение имеющее свой уникальный client_id
- enterprise applications - это установленное конретно в нашей active directory экземпляр этого приложения - по сути это и есть его сервисная учетка, в отличии от app registrations, если какая то другая компания поставит у себя наше приложение, то в их active directory в разделе enterprise applications - появиться наша апка - то есть тем самым они как бы содают сервисную учетку от имени нашего приложения в своем active directory. В процессе создания app registration, enterprise application создается автоматом и тут ничего делать не нужно
- Так же само как и с сервисными учетками, у приложений, в разрезе секретов есть раздел federated credentials которые один в один можно настроить точно так же
Use Case: API to API
For this to work we gonna need an app registration, nothing fancy here, just create one with default settings, no need to change anything
Copy your client id and tenant id, we will need them later
Click “Add an Application ID URI”
At the very top click "Set"
Accept default
One more prerequisite
If you leave everything as is, client will fail with error
Unhandled exception.
Azure.Identity.AuthenticationFailedException:
Azure CLI authentication failed due to an unknown error.
See the troubleshooting guide for more information.
https://aka.ms/azsdk/net/identity/azclicredential/troubleshoot
ERROR: AADSTS65001:
The user or administrator has not consented to use the application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named 'Microsoft Azure CLI'.
Send an interactive authorization request for this user and resource.To fix this issue go to exposed api, add some scope and add client with ID mentioned in error
Now create an API
mkdir myapi
cd myapi
dotnet new web --exclude-launch-settings --no-https
dotnet add package Microsoft.Identity.WebAnd our Program.cs
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Authority = "https://sts.windows.net/695e64b5-2d13-4ea8-bb11-a6fda2d60c41";
options.Audience = "api://fa220c3a-cd6d-416a-ac4f-25a3e14ba38f";
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", (ClaimsPrincipal user) => user.Claims.Select(c => new KeyValuePair<string, string>(c.Type, c.Value)).ToList());
app.Run();Now we need client, to simplify things I will create console client
mkdir client
cd client
dotnet new console
dotnet add package Azure.IdentityAnd here is the code
using System.Net.Http.Headers;
using Azure.Core;
using Azure.Identity;
var credentials = new DefaultAzureCredential();
var tokenResult = await credentials.GetTokenAsync(new TokenRequestContext(new[] { "api://fa220c3a-cd6d-416a-ac4f-25a3e14ba38f/.default" }), CancellationToken.None);
Console.WriteLine(tokenResult.Token);
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenResult.Token);
var claims = await client.GetStringAsync("http://localhost:5000/");
Console.WriteLine(claims);If everything were done correctly you should receive your access token, which then will be used to authenticate against our API
The next step is to check how can we receive such token for managed identity, let’s pretend we already have one configured
At moment it does not work, I was able to acquire access token for managed identity, it is much smaller but from the logs of API it seems to be valid, but at the very end API marks user as not logged in
The easiest way to get an access token created from kubernetes will be:
# note: we are using preexisting "ats" service account, so no need to deal with it
kubectl run -it --rm mactemp --image=ubuntu --overrides='{"spec": { "serviceAccountName": "ats", "nodeSelector": {"kubernetes.io/os": "linux"}}, "metadata":{"labels":{"azure.workload.identity/use":"true"},"annotations":{"kubeforce":"skip"}}}'
apt -qq update && apt install -y curl jq
# check that you have token
env | grep AZURE
# exchange tokens (make sure to replace "fa220c3a-cd6d-416a-ac4f-25a3e14ba38f" with your application registration client id)
curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "scope=api%3A%2F%2Ffa220c3a-cd6d-416a-ac4f-25a3e14ba38f%2F.default&client_id=$AZURE_CLIENT_ID&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$(cat $AZURE_FEDERATED_TOKEN_FILE)&grant_type=client_credentials" "https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/v2.0/token"Then copy received token an locally call your api like so:
curl -H "Authorization: Bearer XXXXX" http://localhost:5000/myapi
I have created mactemp_myapi
In its expose api section I have set default api://328df3d8-186c-4e8c-b51e-bb60790599e9 app uri, added “Hello” scope, and added 04b07795-8ddb-461a-bbee-02f9e1bf7b46 Azure CLI as allowed client
In properties of enterprise app i have enabled assigment required feature, and added my self to users
Did tried to remove my self at 12:55, still can receive an token, will try later to see how much time is needed for change to propagate. In 14:16 i can not receive a token anymore
console
Created mactemp_console
For easier experiments between user token and console token, have created an client credentials secret
Как дебажить джобу
Сценарий: у нас есть некая крон джоба которая по какой то не ведомой причине перестала работать
Мы хотить перепроверить что происходит с токенами и валидны ли они
В нашем случае речь о доступе к azure storage но сам подход применим ко всему
Запускаем наш контейнер
kubectl run -it --rm mactemp --image=ubuntu --overrides='{"spec": { "serviceAccountName": "ats", "nodeSelector": {"kubernetes.io/os": "linux"}}, "metadata":{"labels":{"azure.workload.identity/use":"true"},"annotations":{"kubeforce":"skip"}}}' bashПримечания:
- нужно поменять
--image=ubuntuна образ своей джобы - нужно поменять
serviceAccountNameна тот для которого проводим тест bashв самом конце добавляем что бы запустилась не наша джоба, а терминал
Если нужно доставляем софт
apt update
apt install -y curl jqТеперь начинаем сами проверки
Смотрим наличие переменных окружения
env | grep AZUREВ моем случае все ок, вывело
AZURE_TENANT_ID=695e64b5-2d13-4ea8-bb11-a6fda2d60c41
AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token
AZURE_AUTHORITY_HOST=https://login.microsoftonline.com/
AZURE_CLIENT_ID=92b87306-6e31-4fa0-bf0c-fa84e64072f2Это значит что все сработало как надо и нашему поду подсунули токен
Дальше нам остаеться только проверить валидный ли это токен
cat $AZURE_FEDERATED_TOKEN_FILEДля начала смотрим его в jwt.io
Должно быть что то типа такого, в частности aud должен быть проставлен в AzureADTokenExchange, а sub в нашу сервисную учетку
Естественно с таким токеном ходить куда либо не получиться
curl -s -H "Authorization: Bearer $(cat $AZURE_FEDERATED_TOKEN_FILE)" 'https://management.azure.com/subscriptions?api-version=2022-09-01' | jqВыдаст
{
"error": {
"code": "AuthenticationFailed",
"message": "Authentication failed."
}
}Для того что бы работало, нам нужно провернуть “магию” то что происходит под капотом и обменять токен
Делается следующим образом
curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "scope=https%3A%2F%2Fatsrobsad.blob.core.windows.net%2F.default&client_id=$AZURE_CLIENT_ID&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$(cat $AZURE_FEDERATED_TOKEN_FILE)&grant_type=client_credentials" "https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/v2.0/token" | jq -r ".access_token"Примечания:
- в этом запросе самое главное это
scope- ресурс к которому мы собираемся стучаться, в моем примере этоhttps://atsrobsad.blob.core.windows.net/.default - токен выписывается вот прямо на конретный storage account, а не на все на свете
- слеши меняем на
%2F, двоеточие на%3A - если стучимся к своему API то scope будет по типу такого:
api://111111-222222-33333-44444, где вот эта простыня это id нашего приложения
Теперь если сходим с полученным токеном в jwt.io должны будем там увидеть в aud наш Azure Storage
A sub теперь равен client id сервисной учетке из под которой мы запустили наш контейнер
Примечание: в кубере называется
atsдля удобства, на делеkubectl describe serviceaccount atsи там будет аннотацияazure.workload.identity/client-idс этим id
Так ну и теперь финальный запрос к искому сервису
access_token=$(curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "scope=https%3A%2F%2Fatsrobsad.blob.core.windows.net%2F.default&client_id=$AZURE_CLIENT_ID&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$(cat $AZURE_FEDERATED_TOKEN_FILE)&grant_type=client_credentials" "https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/v2.0/token" | jq -r ".access_token")
curl -H "x-ms-version: 2021-08-06" -H 'accept: application/json' -H "Authorization: Bearer $access_token" 'https://atsrobsad.blob.core.windows.net/?comp=list'Выведет список контейнеров (папок), что то типа:
<?xml version="1.0" encoding="utf-8"?>
<EnumerationResults ServiceEndpoint="https://atsrobsad.blob.core.windows.net/">
<Containers>
<Container>
<Name>olx-resume-files</Name>
<Properties>
<Last-Modified>Mon, 26 Jun 2023 12:14:35 GMT</Last-Modified>
<Etag>"0x8DB763EE7D2E9BF"</Etag>
<LeaseStatus>unlocked</LeaseStatus>
<LeaseState>available</LeaseState>
<PublicAccess>blob</PublicAccess>
<DefaultEncryptionScope>$account-encryption-key</DefaultEncryptionScope>
<DenyEncryptionScopeOverride>false</DenyEncryptionScopeOverride>
<HasImmutabilityPolicy>false</HasImmutabilityPolicy>
<HasLegalHold>false</HasLegalHold>
<ImmutableStorageWithVersioningEnabled>false</ImmutableStorageWithVersioningEnabled>
</Properties>
</Container>
<Container>
<Name>work-resume-files</Name>
<Properties>
<Last-Modified>Thu, 23 Mar 2023 10:35:41 GMT</Last-Modified>
<Etag>"0x8DB2B8A59E410DB"</Etag>
<LeaseStatus>unlocked</LeaseStatus>
<LeaseState>available</LeaseState>
<PublicAccess>blob</PublicAccess>
<DefaultEncryptionScope>$account-encryption-key</DefaultEncryptionScope>
<DenyEncryptionScopeOverride>false</DenyEncryptionScopeOverride>
<HasImmutabilityPolicy>false</HasImmutabilityPolicy>
<HasLegalHold>false</HasLegalHold>
<ImmutableStorageWithVersioningEnabled>false</ImmutableStorageWithVersioningEnabled>
</Properties>
</Container>
<Container>
<Name>work-resume-photos</Name>
<Properties>
<Last-Modified>Thu, 23 Mar 2023 10:35:41 GMT</Last-Modified>
<Etag>"0x8DB2B8A59DAC363"</Etag>
<LeaseStatus>unlocked</LeaseStatus>
<LeaseState>available</LeaseState>
<PublicAccess>blob</PublicAccess>
<DefaultEncryptionScope>$account-encryption-key</DefaultEncryptionScope>
<DenyEncryptionScopeOverride>false</DenyEncryptionScopeOverride>
<HasImmutabilityPolicy>false</HasImmutabilityPolicy>
<HasLegalHold>false</HasLegalHold>
<ImmutableStorageWithVersioningEnabled>false</ImmutableStorageWithVersioningEnabled>
</Properties>
</Container>
</Containers>
<NextMarker />
</EnumerationResults>Таким образом мы можем проверить полный цикл и убедиться что все работает
Под капотом AzureDefaultCredentials видит переменные окружения и делает такой вот обмен токена
Альтернативный вариант, если нет какого то конретного сервиса или не понятно как к нему достучаться - это сходить в апи самого ажура
access_token=$(curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "scope=https%3A%2F%2Fmanagement.azure.com%2F.default&client_id=$AZURE_CLIENT_ID&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$(cat $AZURE_FEDERATED_TOKEN_FILE)&grant_type=client_credentials" "https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/v2.0/token" | jq -r ".access_token")
curl -s -H "Authorization: Bearer $access_token" 'https://management.azure.com/subscriptions?api-version=2022-09-01' | jq ".value"Тут мы запрашиваем список подписок, в ответ придет что то типа такого:
[
{
"id": "/subscriptions/9e9a4757-68f9-4e55-a5c7-1e6666f9539d",
"authorizationSource": "RoleBased",
"managedByTenants": [],
"tags": {
"organization": "robota",
"environment": "dev"
},
"subscriptionId": "9e9a4757-68f9-4e55-a5c7-1e6666f9539d",
"tenantId": "695e64b5-2d13-4ea8-bb11-a6fda2d60c41",
"displayName": "ROBOTA - Dev2",
"state": "Enabled",
"subscriptionPolicies": {
"locationPlacementId": "Public_2014-09-01",
"quotaId": "CSP_2015-05-01",
"spendingLimit": "Off"
}
}
]В нашел сулчае все оказалось до боли смешно и нелепо, а ответ был у нас все это время перед глазами
Что бы вся эта штука работала нужно собслюсти 2 момента:
- пода должна бежать из под service account'а созданного на манер описания в самом верху этого документа
- пода должна иметь лейбочку
azure.workload.identity/use=true
И действительно, если так подумать - с какого перепугу поду должны подсовываться токены, если он бежит из под сервисной учетки, у нас и так куча разных деплойментов, которые бегут из под своих учеток и слухом не слыхивали об ажуре и токенах и оно им не нужно, или к примеру, а если у нас под будет ходить в амазон, именно наличие аннотации запускает всю эту историю
Хоть мы и выяснили что с токенами все ок, приложение все так же не фурыкало, пока суть да дело пробуем все то же самое сделать только из donet, скидываю что бы была заготовка для копипасты
using System.Net.Http.Headers;
using Azure.Core;
using Azure.Identity;
// dotnet add package Azure.Identity
var credentials = new DefaultAzureCredential();
var tokenResult = await credentials.GetTokenAsync(new TokenRequestContext(new[] { "https://atsrobsad.blob.core.windows.net/.default" }), CancellationToken.None);
Console.WriteLine(tokenResult.Token);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-ms-version", "2021-08-06");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenResult.Token);
var claims = await client.GetStringAsync("https://atsrobsad.blob.core.windows.net/?comp=list");
Console.WriteLine(claims);Для быстрой проверки гипотез и таких кусков код можно сделать следующий финт ушами
kubectl run -it --rm mactemp --image=mcr.microsoft.com/dotnet/sdk:7.0 --overrides='{"spec": { "serviceAccountName": "ats", "nodeSelector": {"kubernetes.io/os": "linux"}}, "metadata":{"labels":{"azure.workload.identity/use":"true"},"annotations":{"kubeforce":"skip"}}}' bashЗатем внутри
cd ~
mkdir foo
cd foo
dotnet new console
dotnet add package Azure.Identity
apt update
apt install -y vim
vim Program.csИ дальше уже как обычно dotnet run