Obtenez le jeton d'authentification d'AWS EKS à l'aide du kit AWS Java SDK v2

11

Comment puis-je obtenir un jeton d'authentification Kubernetes auprès d'AWS EKS à l'aide d'AWS Java SDK v2? Un jeton d'authentification qui peut ensuite être utilisé pour s'authentifier auprès de Kubernetes à l'aide d'un SDK Kubernetes. En d'autres termes, je veux obtenir un jeton d'authentification d'EKS à utiliser pour l'authentification avec Kubernetes afin de ne pas avoir à créer une "configuration de kube".

J'ai en fait obtenu une solution fonctionnant avec AWS Java SDK v1 (pas v2) en regardant les exemples de code dans le problème ouvert suivant . Il y a aussi un exemple de code Python ici MAIS je n'ai aucun succès avec AWS Java SDK v2. Ma tentative de le faire avec 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);
    }
}

Il génère un jeton, mais lorsque j'utilise le jeton dans mon client Kubernetes (le SDK Java Kubernetes officiel), je reçois une réponse "Non autorisé" - donc il me manque quelque chose sur lequel je ne peux pas mettre le doigt ...

La version AWS Java SDK v1 ressemble à ceci: (D'après le problème ouvert mentionné précédemment)

Je l'ai fait fonctionner, mais j'ai du mal à obtenir quelque chose de similaire à travailler dans 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
la source
Comme indiqué dans le problème AWS Java SDK v1, l'implémentation est sensible à la spécification d'une date d'expiration trop longue. J'ai joué un peu avec la date d'expiration, mais cela n'a pas résolu le problème.
NS du Toit
avez-vous essayé d'utiliser l'utilitaire aws-iam-authentifier pour obtenir des jetons
Umesh Kumhar
J'ai déjà utilisé l'authentificateur aws-iam, mais je dois pouvoir générer des jetons à partir du code source Java - sans rien installer. Et j'ai réussi à faire fonctionner ce truc avec AWS Java SDK v1, j'ai juste des problèmes avec la v2 du SDK.
NS du Toit
J'utilise actuellement l'AWS Java SDK v1 pour générer le jeton - mais maintenant je dois l'avoir sur mon chemin de classe :( Dès que je peux comprendre cela, je peux refactoriser et supprimer la v1 du SDK de mes dépendances :)
NS du Toit
Quelle version de Kubernetes utilisez-vous? Où cette application est-elle censée s'exécuter (en dehors du cluster, à l'intérieur de celui-ci)?
mewa

Réponses:

2

D'accord, je l'ai finalement fait fonctionner.

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

Le problème était dans mon point de terminaison 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);
    }
}

Notez le /dans le path(troisième) argument de l' URIobjet. La version AWS Java SDK v1 n'a pas créé l'URI comme ça, mais a spécifié /ailleurs. Si j'imprime maintenant le en URItant que chaîne que j'obtiens https://sts.eu-west-1.amazonaws.com/, alors que la version originale dans la question vient de revenirhttps://sts.eu-west-1.amazonaws.com

Assez intéressant - la version originale a également généré un jeton, mais le jeton a été rejeté par Kubernetes. On devrait s'attendre à un comportement similaire si la date d'expiration est trop éloignée dans le futur - vous obtiendrez un jeton, mais cela conduira à une Unauthorizedréponse du service Kubernetes.

Après avoir changé le point de terminaison STS, tout a fonctionné, mais j'ai fait un autre changement:

J'ai ajouté la ligne suivante à mon Aws4PresignerParams :

.signingClockOverride(Clock.systemUTC())

Ce n'était pas nécessaire, mais l'AWS Java SDK v1 d'origine a fait quelque chose avec une horloge quand il l'a spécifié SdkClock.STANDARD, et ZonedDateTimecelui que j'utilise dans la version AWS Java SDK v2 utilise le fuseau horaire UTC.

NS du Toit
la source
Alors oui, je l'ai fait fonctionner, mais je n'ai pas trop de connaissances sur le raisonnement derrière cela. Même sans le, /j'ai toujours un jeton, mais comme indiqué, cela n'a tout simplement pas fonctionné lorsque j'ai commencé à m'intégrer à Kubernetes.
NS du Toit
Juste un autre côté intéressant - le problème original d'AWS Java SDK v1 a indiqué que le jeton est de très courte durée. Dès que j'essaie de définir la date d'expiration à plus de 60 secondes, la même chose se produit - j'obtiens un jeton, mais cela conduit à une Unauthorizedréponse.
NS du Toit