Uzyskaj token uwierzytelniający z AWS EKS przy użyciu AWS Java SDK v2

11

Jak mogę uzyskać token uwierzytelniania Kubernetes z AWS EKS przy użyciu AWS Java SDK v2? Token uwierzytelniania, którego można następnie użyć do uwierzytelnienia w Kubernetes za pomocą zestawu SDK Kubernetes. Innymi słowy, chcę uzyskać token uwierzytelniający od EKS, który będzie używany do uwierzytelniania w Kubernetes, aby nie musiałem tworzyć „konfiguracji kube”.

Właściwie mam rozwiązanie współpracujące z AWS Java SDK v1 (nie v2), patrząc na przykłady kodu w następującym otwartym problemie . Jest tu również przykład kodu w języku Python , ALE nie odnoszę żadnych sukcesów z AWS Java SDK v2. Moja próba zrobienia tego z 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);
    }
}

Generuje token, ale kiedy używam go w moim kliencie Kubernetes (oficjalny pakiet Java Kubernetes SDK), otrzymuję odpowiedź „Nieautoryzowane” - więc brakuje mi czegoś, czego nie mogę dotknąć ...

Wersja AWS Java SDK v1 wygląda mniej więcej tak: (Z otwartego problemu wspomnianego wcześniej)

Mam to działa, ale staram się uzyskać coś podobnego do pracy w 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
źródło
Jak wskazano w numerze AWS Java SDK v1, implementacja jest wrażliwa na określenie zbyt długiej daty ważności. Grałem trochę z datą wygaśnięcia, ale to nie rozwiązało problemu.
NS du Toit
czy próbowałeś użyć narzędzia aws-iam
-hentator
Wcześniej używałem uwierzytelniacza aws-iam, ale muszę być w stanie generować tokeny z kodu źródłowego Java - bez instalowania czegokolwiek. Dostałem te rzeczy do pracy z AWS Java SDK v1, po prostu mam problemy z v2 SDK.
NS du Toit
Obecnie używam AWS Java SDK v1 do generowania tokena - ale teraz muszę go mieć na mojej ścieżce klasy :( Jak tylko mogę to rozgryźć, mogę refaktoryzować i usunąć v1 SDK z moich zależności :)
NS du Toit
Z jakiej wersji Kubernetes korzystasz? Gdzie ta aplikacja ma działać (poza klastrem, wewnątrz niego)?
mewa

Odpowiedzi:

2

Okej, w końcu udało mi się.

Wersja 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);
    }
}

Problem dotyczył mojego punktu końcowego 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);
    }
}

Zauważ, że /w path(trzeciej) argument dla URIobiektu. Wersja AWS Java SDK v1 nie utworzyła takiego identyfikatora URI, ale określiła /gdzie indziej. Jeśli teraz wydrukuję URIjako ciąg, otrzymam https://sts.eu-west-1.amazonaws.com/, a oryginalna wersja pytania właśnie wróciłahttps://sts.eu-west-1.amazonaws.com

Co ciekawe - oryginalna wersja również wygenerowała token, ale token został odrzucony przez Kubernetes. Należy się spodziewać podobnego zachowania, jeśli data wygaśnięcia jest zbyt daleko w przyszłość - dostaniesz token, ale doprowadzi to do Unauthorizedodpowiedzi z usługi Kubernetes.

Po zmianie punktu końcowego STS wszystko działało, ale wprowadziłem jeszcze jedną zmianę:

Dodałem następujący wiersz do mojego Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

Nie było to wymagane, ale oryginalny AWS Java SDK v1 zrobił coś z zegarem, gdy został określony SdkClock.STANDARD, a ten ZonedDateTime, którego używam w wersji AWS Java SDK v2, korzysta ze strefy czasowej UTC.

NS du Toit
źródło
Tak, mam to działa, ale nie mam zbyt wiele wglądu w uzasadnienie. Nawet bez niego /wciąż otrzymałem token, ale jak wskazano, po prostu nie działał, kiedy zacząłem integrować się z Kubernetes.
NS du Toit
Jeszcze jedna interesująca sprawa - oryginalny problem AWS Java SDK v1 wskazał, że token jest bardzo krótkotrwały. Gdy tylko próbuję ustawić datę ważności na ponad 60 sekund, dzieje się to samo - otrzymuję token, ale prowadzi to do Unauthorizedodpowiedzi.
NS du Toit