Получите токен аутентификации от AWS EKS, используя AWS Java SDK v2

11

Как получить токен аутентификации Kubernetes от AWS EKS с помощью AWS Java SDK v2? Токен аутентификации, который затем можно использовать для аутентификации в Kubernetes с использованием Kubernetes SDK. Другими словами, я хочу получить токен аутентификации от EKS, чтобы использовать его для аутентификации с Kubernetes, чтобы мне не нужно было создавать «конфигурацию kube».

На самом деле я получил решение, работающее с AWS Java SDK v1 (не v2), рассматривая примеры кода в следующем открытом выпуске . Существует также код Python пример здесь , но я не имея каких - либо успехов с AWS Java SDK v2. Моя попытка сделать это с помощью AWS Java SDK v2:

public static String getAuthenticationToken(AwsCredentialsProvider awsAuth, Region awsRegion, String clusterName) {
    try {
        SdkHttpFullRequest requestToSign = SdkHttpFullRequest
                .builder()
                .method(SdkHttpMethod.GET)
                .uri(new URI("https", String.format("sts.%s.amazonaws.com", awsRegion.id()), null, null))
                .appendHeader("x-k8s-aws-id", clusterName)
                .appendRawQueryParameter("Action", "GetCallerIdentity")
                .appendRawQueryParameter("Version", "2011-06-15")
                .build();

        ZonedDateTime expirationDate = DateUtil.addSeconds(DateUtil.now(), 60);
        Aws4PresignerParams presignerParams = Aws4PresignerParams.builder()
                .awsCredentials(awsAuth.resolveCredentials())
                .expirationTime(expirationDate.toInstant())
                .signingName("sts")
                .signingRegion(awsRegion)
                .build();

        SdkHttpFullRequest signedRequest = Aws4Signer.create().presign(requestToSign, presignerParams);

        String encodedUrl = Base64.getUrlEncoder().withoutPadding().encodeToString(signedRequest.getUri().toString().getBytes(CharSet.UTF_8.getCharset()));
        return ("k8s-aws-v1." + encodedUrl);
    } catch (Exception e) {
        String errorMessage = "A problem occurred generating an Eks token";
        logger.error(errorMessage, e);
        throw new RuntimeException(errorMessage, e);
    }
}

Он генерирует токен, но когда я использую токен в моем клиенте Kubernetes (официальном Java Kubernetes SDK), я получаю ответ «Несанкционированный» - так что мне не хватает того, на что я не могу положить палец ...

Версия AWS Java SDK v1 выглядит примерно так: (Из упомянутой ранее открытой проблемы )

Я получил это работает, но я изо всех сил пытаюсь получить что-то похожее на работу в AWS Java SDK v2.

private String generateToken(String clusterName,
                                 Date expirationDate,
                                 String serviceName,
                                 String region,
                                 AWSSecurityTokenServiceClient awsSecurityTokenServiceClient,
                                 AWSCredentialsProvider credentialsProvider,
                                 String scheme,
                                 String host) throws URISyntaxException {
        try {
            DefaultRequest<GetCallerIdentityRequest> callerIdentityRequestDefaultRequest = new DefaultRequest<>(new GetCallerIdentityRequest(), serviceName);
            URI uri = new URI(scheme, host, null, null);
            callerIdentityRequestDefaultRequest.setResourcePath("/");
            callerIdentityRequestDefaultRequest.setEndpoint(uri);
            callerIdentityRequestDefaultRequest.setHttpMethod(HttpMethodName.GET);
            callerIdentityRequestDefaultRequest.addParameter("Action", "GetCallerIdentity");
            callerIdentityRequestDefaultRequest.addParameter("Version", "2011-06-15");
            callerIdentityRequestDefaultRequest.addHeader("x-k8s-aws-id", clusterName);

            Signer signer = SignerFactory.createSigner(SignerFactory.VERSION_FOUR_SIGNER, new SignerParams(serviceName, region));
            SignerProvider signerProvider = new DefaultSignerProvider(awsSecurityTokenServiceClient, signer);
            PresignerParams presignerParams = new PresignerParams(uri,
                    credentialsProvider,
                    signerProvider,
                    SdkClock.STANDARD);

            PresignerFacade presignerFacade = new PresignerFacade(presignerParams);
            URL url = presignerFacade.presign(callerIdentityRequestDefaultRequest, expirationDate);
            String encodedUrl = Base64.getUrlEncoder().withoutPadding().encodeToString(url.toString().getBytes());
            log.info("Token [{}]", encodedUrl);
            return "k8s-aws-v1." + encodedUrl;
        } catch (URISyntaxException e) {
            log.error("could not generate token", e);
            throw e;
        }
    }
NS du Toit
источник
Как указано в выпуске AWS Java SDK v1, реализация чувствительна к указанию слишком длинной даты истечения срока действия. Я немного поигрался с датой истечения срока, но это не решило проблему.
NS du Toit
вы пытались использовать утилиту aws-iam-authenticator для получения токенов
Umesh Kumhar
Ранее я использовал aws-iam-authenticator, но мне нужно иметь возможность генерировать токены из исходного кода Java - без установки чего-либо. И я получил этот материал для работы с AWS Java SDK v1, просто возникли проблемы с v2 SDK.
NS du Toit
В настоящее время я использую AWS Java SDK v1 для генерации токена, но теперь мне нужно иметь его в своем classpath :( Как только я смогу это выяснить, я смогу реорганизовать и удалить v1 SDK из моих зависимостей :)
NS дю Туа
Какую версию Kubernetes вы используете? Где это приложение предназначено для запуска (вне кластера, внутри него)?
Mewa

Ответы:

2

Ладно, наконец-то все заработало.

Версия AWS Java SDK v2:

public static String getAuthenticationToken(AwsCredentialsProvider awsAuth, Region awsRegion, String clusterName) {
    try {    
        SdkHttpFullRequest requestToSign = SdkHttpFullRequest
                .builder()
                .method(SdkHttpMethod.GET)
                .uri(StsUtil.getStsRegionalEndpointUri(awsRegion))
                .appendHeader("x-k8s-aws-id", clusterName)
                .appendRawQueryParameter("Action", "GetCallerIdentity")
                .appendRawQueryParameter("Version", "2011-06-15")
                .build();

        ZonedDateTime expirationDate = DateUtil.addSeconds(DateUtil.now(), 60);
        Aws4PresignerParams presignerParams = Aws4PresignerParams.builder()
                .awsCredentials(awsAuth.resolveCredentials())
                .signingRegion(awsRegion)
                .signingName("sts")
                .signingClockOverride(Clock.systemUTC())
                .expirationTime(expirationDate.toInstant())
                .build();

        SdkHttpFullRequest signedRequest = Aws4Signer.create().presign(requestToSign, presignerParams);

        String encodedUrl = Base64.getUrlEncoder().withoutPadding().encodeToString(signedRequest.getUri().toString().getBytes(CharSet.UTF_8.getCharset()));
        return ("k8s-aws-v1." + encodedUrl);
    } catch (Exception e) {
        String errorMessage = "A problem occurred generating an Eks authentication token for cluster: " + clusterName;
        logger.error(errorMessage, e);
        throw new RuntimeException(errorMessage, e);
    }
}

Проблема была в моей конечной точке STS Uri:

public static URI getStsRegionalEndpointUri(Region awsRegion) {
    try {
        return new URI("https", String.format("sts.%s.amazonaws.com", awsRegion.id()), "/", null);
    } catch (URISyntaxException shouldNotHappen) {
        String errorMessage = "An error occurred creating the STS regional endpoint Uri";
        logger.error(errorMessage, shouldNotHappen);
        throw new RuntimeException(errorMessage, shouldNotHappen);
    }
}

Обратите внимание , что /в path(третьем) аргументе для URIобъекта. Версия AWS Java SDK v1 не создавала подобный URI, а указала в /другом месте. Если я сейчас распечатаю полученную URIстроку https://sts.eu-west-1.amazonaws.com/, в то время как оригинальная версия в вопросе только что вернуласьhttps://sts.eu-west-1.amazonaws.com

Интересно, что оригинальная версия также генерировала токен, но он был отклонен Кубернетом. Следует ожидать аналогичного поведения, если срок действия истекает слишком далеко в будущем - вы получите токен, но это приведет к Unauthorizedответу от службы Kubernetes.

После изменения конечной точки STS все заработало, но я сделал еще одно изменение:

Я добавил следующую строку в мой Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

В этом не было необходимости, но оригинальный AWS Java SDK v1 действительно что-то делал с часами, когда они были указаны SdkClock.STANDARD, а то, ZonedDateTimeчто я использую в версии AWS Java SDK v2, действительно использует часовой пояс UTC.

NS du Toit
источник
Так что да, у меня все получилось, но я не слишком разбираюсь в причинах этого. Даже без /я все еще получил токен, но, как указано, он просто не работал, когда я начал интегрироваться с Kubernetes.
NS du Toit
Еще один интересный момент - первоначальная проблема AWS Java SDK v1 действительно указывала на то, что токен очень недолговечен. Как только я пытаюсь установить срок действия более 60 секунд, происходит то же самое - я получаю токен, но это приводит к Unauthorizedответу.
NS du Toit