Ottieni il token di autenticazione da AWS EKS utilizzando AWS Java SDK v2


11

Come posso ottenere un token di autenticazione Kubernetes da AWS EKS utilizzando AWS Java SDK v2? Un token di autenticazione che può quindi essere utilizzato per l'autenticazione con Kubernetes utilizzando un SDK Kubernetes. In altre parole, voglio ottenere un token di autenticazione da EKS da utilizzare per l'autenticazione con Kubernetes in modo da non dover creare una "configurazione kube".

In realtà ho trovato una soluzione che funziona con AWS Java SDK v1 (non v2) guardando gli esempi di codice nel seguente numero aperto . C'è anche un esempio di codice Python qui, ma non sto avendo alcun successo con AWS Java SDK v2. Il mio tentativo di farlo con 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);
    }
}

Genera un token, ma quando uso il token nel mio client Kubernetes (l'SDK ufficiale di Kubernetes Java) ricevo una risposta "Non autorizzata" - quindi mi manca qualcosa su cui non riesco a mettere il dito ...

La versione di AWS Java SDK v1 è simile alla seguente: (Dalla questione aperta menzionata in precedenza)

Ho funzionato, ma sto lottando per ottenere qualcosa di simile al lavoro in 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;
        }
    }

Come indicato nel problema AWS Java SDK v1, l'implementazione è sensibile alla specifica di una data di scadenza troppo lunga. Ho giocato un po 'con la data di scadenza, ma il problema non è stato risolto.
NS du Toit,

hai provato a usare l'utility aws-iam-authenticator per ottenere i token
Umesh Kumhar

Ho già usato aws-iam-authenticator, ma devo essere in grado di generare token dal codice sorgente Java - senza installare nulla. E ho fatto funzionare queste cose con AWS Java SDK v1, avendo solo problemi con la v2 dell'SDK.
NS du Toit,

Attualmente sto usando AWS Java SDK v1 per generare il token, ma ora devo averlo sul mio percorso di classe :( Non appena riesco a capirlo, posso refactoring e rimuovere v1 dell'SDK dalle mie dipendenze :)
NS du Toit,

Quale versione di Kubernetes stai utilizzando? Dove deve essere eseguita questa app (al di fuori del cluster, al suo interno)?
Mewa,

Risposte:


2

Bene, finalmente l'ho fatto funzionare.

La versione 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);
    }
}

Il problema era nel mio endpoint 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);
    }
}

Notare il /nel path(terzo) argomento per l' URIoggetto. La versione AWS Java SDK v1 non ha creato l'URI in questo modo, ma ha specificato /altrove. Se ora stampo URIcome stringa che ottengo https://sts.eu-west-1.amazonaws.com/, mentre la versione originale nella domanda è appena tornatahttps://sts.eu-west-1.amazonaws.com

Abbastanza interessante: anche la versione originale ha generato un token, ma il token è stato rifiutato da Kubernetes. Uno dovrebbe aspettarsi un comportamento simile se la data di scadenza è troppo lontana nel futuro - otterrai un token, ma porterà a una Unauthorizedrisposta dal servizio Kubernetes.

Dopo aver modificato l'endpoint STS, tutto ha funzionato, ma ho apportato un'altra modifica:

Ho aggiunto la seguente riga al mio Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

Non era necessario, ma l'SDK Java originale v1 di AWS faceva qualcosa con un orologio quando specificato SdkClock.STANDARD, e quello ZonedDateTimeche uso nella versione AWS Java SDK v2 utilizza il fuso orario UTC.


Quindi sì, l'ho fatto funzionare, ma non ho troppe informazioni sul ragionamento alla base. Anche senza l' /ho ancora ottenuto un token, ma come indicato non ha funzionato quando ho iniziato a integrarmi con Kubernetes.
NS du Toit,

Solo un altro aspetto interessante: il problema originale di AWS Java SDK v1 indicava che il token ha una durata molto breve. Non appena provo a impostare la data di scadenza a più di 60 secondi, accade la stessa cosa: ottengo un token, ma porta a una Unauthorizedrisposta.
NS du Toit,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.