Отримайте маркер аутентифікації від AWS EKS за допомогою AWS Java SDK v2


11

Як я можу отримати маркер аутентифікації Kubernetes від AWS EKS за допомогою AWS Java SDK v2? Маркер аутентифікації, який потім може бути використаний для аутентифікації з Kubernetes за допомогою SDK Kubernetes. Іншими словами, я хочу отримати маркер автентифікації від EKS, щоб використовувати його для аутентифікації з Kubernetes, щоб мені не довелося створювати "kube config".

Я фактично отримав рішення, працюючи з 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 (офіційний SDK Java Kubernetes), я отримую відповідь "Несанкціонована" відповідь - тому мені не вистачає чогось, на що я не можу покласти пальцем ...

Версія 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;
        }
    }

Як зазначено у випуску AWS Java SDK v1, реалізація є чутливою до визначення занадто довгої дати закінчення терміну придатності. Я трохи пограв із терміном придатності, але це не вирішило проблему.
NS du Toit

ви намагалися скористатися утилітою
aws

Раніше я використовував aws-iam-автентифікатор, але мені потрібно вміти генерувати жетони з вихідного коду Java - не встановлюючи нічого. І я змусив цю роботу працювати з AWS Java SDK v1, просто маючи проблеми з v2 SDK.
NS du Toit

Наразі я використовую AWS Java SDK v1 для генерування маркера - але тепер я маю це мати на своєму classpath :( Як тільки я можу це зрозуміти, я можу рефактор і видалити v1 з SDK з моїх залежностей :)
Н.С. du Toit

Яку версію 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як String, який я отримую https://sts.eu-west-1.amazonaws.com/, тоді як оригінальний варіант у питанні просто повернувсяhttps://sts.eu-west-1.amazonaws.com

Досить цікаво - оригінальна версія також генерувала маркер, але Kubernetes відкинув маркер. Слід очікувати подібної поведінки, якщо термін придатності занадто далеко в майбутньому - ви отримаєте жетон, але це призведе до Unauthorizedвідповіді від служби Kubernetes.

Після зміни кінцевої точки STS все працювало, але я вніс ще одну зміну:

Я додав наступний рядок до свого Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

Це не було потрібно, але оригінальний AWS Java SDK v1 робив щось із годинником, коли він вказав SdkClock.STANDARD, і те, ZonedDateTimeщо я використовую у версії AWS Java SDK v2, використовує часовий пояс UTC.


Так, так, у мене це працює, але я не надто розуміюсь, як міркувати за цим. Навіть без цього /я все-таки отримав жетон, але, як було зазначено, він просто не спрацював, коли я почав інтегруватися з Kubernetes.
NS du Toit

Ще один цікавий бік - оригінальний номер AWS Java SDK v1 вказував на те, що маркер дуже короткочасний. Як тільки я намагаюся встановити термін придатності більше 60 секунд, відбувається те саме - я отримую маркер, але це призводить до Unauthorizedвідповіді.
NS du Toit
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.