Obter token de autenticação do AWS EKS usando o AWS Java SDK v2

11

Como posso obter um token de autenticação Kubernetes do AWS EKS usando o AWS Java SDK v2? Um token de autenticação que pode ser usado para autenticar com o Kubernetes usando um Kubernetes SDK. Em outras palavras, desejo obter um token de autenticação do EKS para usar na autenticação com o Kubernetes, para que não seja necessário criar uma "configuração do kube".

Na verdade, eu tenho uma solução trabalhando com o AWS Java SDK v1 (não v2), observando os exemplos de código na seguinte edição aberta . Também há um exemplo de código Python aqui, mas não estou tendo sucesso com o AWS Java SDK v2. Minha tentativa de fazer isso com o 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);
    }
}

Ele gera um token, mas quando uso o token no meu cliente Kubernetes (o SDK oficial do Java Kubernetes) recebo uma resposta "Não autorizada" - por isso estou perdendo algo que não consigo identificar ...

A versão do AWS Java SDK v1 tem a seguinte aparência: (Na edição aberta mencionada anteriormente)

Eu trabalhei, mas estou tentando obter algo semelhante para trabalhar no 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
fonte
Conforme indicado no problema do AWS Java SDK v1, a implementação é sensível à especificação de uma data de validade muito longa. Eu brinquei um pouco com a data de validade, mas isso não resolveu o problema.
NS du Toit
você tentou usar o utilitário aws-iam-authenticator para obter tokens
Umesh Kumhar
Eu usei o aws-iam-authenticator antes, mas preciso gerar tokens a partir do código-fonte Java - sem instalar nada. E consegui que essas coisas funcionassem com o AWS Java SDK v1, apenas tendo problemas com a v2 do SDK.
NS du Toit
Atualmente, estou usando o AWS Java SDK v1 para gerar o token - mas agora tenho que tê-lo no meu caminho de classe :( Assim que eu puder descobrir isso, posso refatorar e remover a v1 do SDK das minhas dependências :)
NS du Toit
Qual versão do Kubernetes você está executando? Onde esse aplicativo deve ser executado (fora do cluster, dentro dele)?
mewa 5/02

Respostas:

2

Ok, finalmente consegui funcionar.

A versão do 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);
    }
}

O problema estava no meu 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);
    }
}

Observe o /no path(terceiro) argumento para o URIobjeto. A versão do AWS Java SDK v1 não criou o URI assim, mas especificou o /outro lugar. Se eu agora imprimir a URIString como recebo https://sts.eu-west-1.amazonaws.com/, enquanto a versão original na pergunta acabou de retornarhttps://sts.eu-west-1.amazonaws.com

Interessante o suficiente - a versão original também gerou um token, mas o token foi rejeitado pelo Kubernetes. Deve-se esperar um comportamento semelhante se a data de validade estiver muito distante no futuro - você receberá um token, mas isso levará a uma Unauthorizedresposta do serviço Kubernetes.

Depois de alterar o ponto final do STS, tudo funcionou, mas fiz mais uma alteração:

Eu adicionei a seguinte linha ao meu Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

Não era necessário, mas o AWS Java SDK v1 original fazia algo com um relógio quando especificado SdkClock.STANDARD, e o ZonedDateTimeque eu uso na versão do AWS Java SDK v2 usa o fuso horário UTC.

NS du Toit
fonte
Então, sim, consegui funcionar, mas não tenho muita percepção do raciocínio por trás disso. Mesmo sem o /eu ainda recebi um token, mas, como indicado, ele simplesmente não funcionou quando comecei a integrar o Kubernetes.
NS du Toit
Apenas mais um aspecto interessante - o problema original do AWS Java SDK v1 indicava que o token durava muito pouco. Assim que tento definir a data de validade para mais de 60 segundos, acontece o mesmo - recebo um token, mas ele leva a uma Unauthorizedresposta.
NS du Toit