Get authentication token from AWS EKS using the AWS Java SDK v2 Get authentication token from AWS EKS using the AWS Java SDK v2 kubernetes kubernetes

Get authentication token from AWS EKS using the AWS Java SDK v2


Okay, I finally got it working.

The AWS Java SDK v2 version:

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

The problem was in my STS endpoint 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);    }}

Note the / in the path (third) argument for the URI object. The AWS Java SDK v1 version didn't create the URI like that, but specified the / elsewhere. If I now print out the URI as a String I get https://sts.eu-west-1.amazonaws.com/, while the original version in the question just returned https://sts.eu-west-1.amazonaws.com

Interesting enough - the original version did also generate a token, but the token was rejected by Kubernetes. One should expect similar behavior if the expiration date is too far into the future - you'll get a token, but it will lead to an Unauthorized response from the Kubernetes service.

After changing the STS endpoint everything worked, but I made one more change:

I added the following line to my Aws4PresignerParams:

.signingClockOverride(Clock.systemUTC())

It wasn't required, but the original AWS Java SDK v1 did do something with a clock when it specified SdkClock.STANDARD, and the ZonedDateTime that I use in the AWS Java SDK v2 version does use the UTC timezone.


Just for completeness sake, in case you want to use the token with the Kubernetes client (io.kubernetes:client-java), you can add an interceptor that generates a valid token before every request:

  private ApiClient createClient(AwsCredentialsProvider awsAuth, Region awsRegion, String clusterName) {    ApiClient client = Config.fromUrl(eksEndpoint, false);    OkHttpClient httpClient = createHttpClient(client.getHttpClient(), awsAuth, awsRegion, clusterName);    client.setHttpClient(httpClient);    return client;  }  @NotNull  private OkHttpClient createHttpClient(      OkHttpClient baseHttpClient, AwsCredentialsProvider awsAuth, Region awsRegion, String clusterName  ) {    OkHttpClient.Builder builder = new OkHttpClient.Builder(baseHttpClient);    builder.addInterceptor(chain -> {      Request request = chain.request();      String token = getAuthenticationToken(awsAuth, awsRegion, clusterName);      Request newRequest = request          .newBuilder()          .header("Authorization", "Bearer " + token)          .build();      return chain.proceed(newRequest);    });    return builder.build();  }