Как я могу использовать AWS для аутентификации в EKS Kubernetes API с помощью .NET, когда моя программа работает вне кластера?

Я хотел бы разработать программу .NET, которая обращается к API Kubernetes для выполнения некоторых административных задач. Наш кластер Kubernetes — это EKS, поэтому я хотел бы использовать собственный метод аутентификации AWS для создания временных учетных данных и доступа к API, поскольку моя программа должна работать вне Kubernetes по архитектурным причинам. Я хотел бы сопоставить роль AWS с ролью Kubernetes, а затем использовать права, предоставленные этой роли, для доступа к API и выполнения заданных задач.

Я увидел, что в интерфейсе командной строки AWS есть команда под названием aws eks get-token , которая является рекомендуемым методом для получения токена доступа в Terraform, поэтому я установил AWSSDK.EKS, но, к сожалению, обнаружил, что есть нет такого метода в варианте библиотеки .NET при просмотре методов на IAmazonEks.

Просматривая исходный код команды aws eks get-token, я вижу, что мы используем STS для создания предварительно подписанного URL:

def _get_presigned_url(self, k8s_aws_id):
    return self._sts_client.generate_presigned_url(
        'get_caller_identity',
        Params = {K8S_AWS_ID_HEADER: k8s_aws_id},
        ExpiresIn=URL_TIMEOUT,
        HttpMethod='GET',
    )

Изучив вывод aws eks get-token, я вижу, что токен действительно представляет собой URL-адрес в кодировке base 64, который, предположительно, будет вызываться кластером для получения идентификатора вызывающего абонента и попытки сопоставить его с ролью перед предоставлением доступа — довольно хороший трюк. Действительно, обращение к этому URL-адресу дает идентификацию вызывающего абонента, как и ожидалось. Для справки, вот как вы его вызываете:

GET https://sts.eu-west-1.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=....&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Security-Token=...
Host: sts.eu-west-1.amazonaws.com
x-k8s-aws-id: my-cluster-id

Однако, к сожалению, в AWS.SecurityToken также не существует C#-эквивалента generate_presigned_url()!

Итак, как мне сгенерировать токен безопасности EKS для использования с клиентской библиотекой .NET Kubernetes, не обращаясь к интерфейсу командной строки AWS?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
143
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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

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

После того, как я понял формат вывода aws eks get-token, я понял, что этот предварительно подписанный URL-адрес очень похож на предварительно подписанный URL-адрес, используемый в S3. Я смог использовать ту же технику, чтобы создать предварительно подписанный URL-адрес для GetCallerIdentity. В AmazonS3Client.GetPresignedUrl есть много кода для обратной совместимости, который я не полностью понимаю, поэтому это может не работать для каждого отдельного варианта использования.

Однако в этом фрагменте кода показано, как создать токен и пройти аутентификацию в кластере Kubernetes, работающем на EKS:

// for reference, these are the using statements. For simplicity however, all code is inline.
using Amazon;
using Amazon.Runtime;
using Amazon.Runtime.Internal;
using Amazon.Runtime.Internal.Auth;
using Amazon.Runtime.Internal.Util;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Internal;
using Amazon.SecurityToken.Model;
using Amazon.SecurityToken.Model.Internal.MarshallTransformations;
using k8s;
using System.Security.Cryptography.X509Certificates;


// Configuration:
const string clusterId = "my-eks-cluster";
const string clusterUrl = "https://0000000.xx.eu-west-1.eks.amazonaws.com";
const string certificateAuthority = "dGhpcyBpcyBub3QgYWN0dWFsbHkgYSBDQQ==...";
const string region = "eu-west-1";


// 60s is what aws eks get-token uses and seems appropriate because it's not too expensive to make a new token.
// I haven't tested to see if there's an upper limit here.
const int credentialAge = 60;

// It's best to retrieve credentials from your local instance profile, profile or wherever:
var credentials = await FallbackCredentialsFactory.GetCredentials().GetCredentialsAsync();


// We don't use the STS client directly, but we stil need some of its variables and internals:
var sts = new AmazonSecurityTokenServiceClient(new AmazonSecurityTokenServiceConfig
{
    AuthenticationRegion = region,
    RegionEndpoint = RegionEndpoint.GetBySystemName(region),
    StsRegionalEndpoints = StsRegionalEndpointsValue.Regional
});
var signer = new AWS4PreSignedUrlSigner();

// All AWS requests in the .NET SDK are turned into an IRequest object, which is the base object
// that is sent to the REST client.
var request = GetCallerIdentityRequestMarshaller.Instance.Marshall(new GetCallerIdentityRequest());
request.Headers["x-k8s-aws-id"] = clusterId;
request.HttpMethod = "GET";
request.OverrideSigningServiceName = "sts";

if (credentials.Token != null)
    request.Parameters["X-Amz-Security-Token"] = credentials.Token;

request.Parameters["X-Amz-Expires"] = Convert.ToString(credentialAge);


// We will now prepare the request as if we were to send it so that we can set other parameters. We only
// seem to set the host and endpoint field but there is a great deal of logic behind these methods so
// possibly some important edge cases are covered.
var endpointResolver = new AmazonSecurityTokenServiceEndpointResolver();
endpointResolver.ProcessRequestHandlers(new Amazon.Runtime.Internal.ExecutionContext(new Amazon.Runtime.Internal.RequestContext(true, new NullSigner())
{
    Request = request,
    ClientConfig = sts.Config
}, null));

// We get a signature for the request using a built-in AWS utility - this is the same thing that we
// do when sending a real REST request.
var result = signer.SignRequest(request, sts.Config, new RequestMetrics(), credentials.AccessKey, credentials.SecretKey);

// We can't append result.ForQueryParameters to the URL like the AWS S3 client, as EKS
// authorisation expects that the results will be URL-encoded:
request.Parameters["X-Amz-Credential"] = $"{result.AccessKeyId}/{result.Scope}";
request.Parameters["X-Amz-Algorithm"] = "AWS4-HMAC-SHA256";
request.Parameters["X-Amz-Date"] = result.ISO8601DateTime;
request.Parameters["X-Amz-SignedHeaders"] = result.SignedHeaders;
request.Parameters["X-Amz-Signature"] = result.Signature;

// Finally we have a signed URL - this can be called like so if you would like to test that it works:
// GET {signedUrl}
// Host: sts.{region}.amazonaws.com
// x-k8s-aws-id: {clusterId}
var signedUrl = AmazonServiceClient.ComposeUrl(request).ToString();

// Now, we just need to format it how EKS expects it:
var encodedUrl = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedUrl));
var eksToken = "k8s-aws-v1." + encodedUrl;


// Now, with our new token we can go ahead and connect to EKS:
var clientConfig = new KubernetesClientConfiguration
{
    AccessToken = eksToken,
    Host = clusterUrl,
    SslCaCerts = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(certificateAuthority)))
};

// If your credentials have the right permissions, you should be able to get a list of your namespaces:
var kubernetesClient = new Kubernetes(clientConfig);

foreach (var ns in kubernetesClient.CoreV1.ListNamespace().Items)
{
    Console.WriteLine(ns.Metadata.Name);
}

Я надеюсь, что это представляет собой полезную альтернативу, если вам нужно сделать что-то более сложное или добавить функциональность Kubernetes к существующему инструменту .NET.

Кроме того, большое количество результатов поиска связано с созданием предварительно подписанных URL-адресов для S3, и я не думаю, что общеизвестно, что вы можете создавать предварительно подписанные URL-адреса для других конечных точек AWS, поэтому, надеюсь, это поможет решить эту конкретную проблему и породит некоторые другие идеи.

Альтернатива: используйте локальную конфигурацию

Было бы упущением с моей стороны не упомянуть гораздо более простую альтернативу — просто создать клиент Kubernetes, используя вашу локальную конфигурацию Kubernetes. Однако:

  1. Это напрямую вызывает awscli, а это означает, что вы должны установить эту и другие зависимости на свой сервер и поддерживать их в актуальном состоянии.
  2. При использовании этого в разработке в Windows окно AWS всплывает на секунду, перехватывая фокус, что раздражает при работе над долгоживущим процессом.
  3. Если вам нужно программно настроить одновременно неизвестное количество кластеров Kubernetes, вам не нужно добавлять их в файл конфигурации Kubernetes.
  4. При обработке пользовательского ввода существует повышенный риск безопасности при передаче пользовательского ввода внешним процессам. Я предпочитаю не делать этого, если это возможно.
  5. Существует также небольшое снижение производительности при обращении к внешнему процессу.

Однако я не могу отрицать простоту, поэтому этот вариант доступен вам, если вы можете установить awscli в своей целевой среде и настроить Kubernetes:

var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
var kubernetesClient = new Kubernetes(config);

foreach (var ns in kubernetesClient.CoreV1.ListNamespace().Items)
{
    Console.WriteLine(ns.Metadata.Name);
}

Несколько раз протестировав решение Стива Рукутса, кажется, что токен иногда не авторизован.

Немного покопавшись в github aws cli, кажется, что encodedUrl обрезано для '=' в конце.

Итак, после изменения кода на:

var encodedUrl = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedUrl)).TrimEnd('=');

Вроде решил проблему

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