【问题标题】:Get authentication token from AWS EKS using the AWS Java SDK v2使用 AWS Java SDK v2 从 AWS EKS 获取身份验证令牌
【发布时间】:2020-01-22 06:13:20
【问题描述】:

如何使用 AWS Java SDK v2 从 AWS EKS 获取 Kubernetes 身份验证令牌?一个身份验证令牌,然后可用于使用 Kubernetes SDK 对 Kubernetes 进行身份验证。换句话说,我想从 EKS 获取一个身份验证令牌以用于 Kubernetes 身份验证,这样我就不必创建“kube config”。

查看以下open issue 中的代码示例,我实际上得到了一个使用 AWS Java SDK v1(不是 v2)的解决方案。还有一个 Python 代码示例 here 但我在使用 AWS Java SDK v2 时没有任何成功。我尝试使用 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);
    }
}

它会生成一个令牌,但是当我在我的 Kubernetes 客户端(官方 Java Kubernetes SDK)中使用该令牌时,我会收到一个“未经授权”的响应 - 所以我错过了一些我无法理解的东西.. .

AWS Java SDK v1 版本如下所示: (来自前面提到的open issue

我得到了它的工作,但我正在努力获得类似于在 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;
        }
    }

【问题讨论】:

  • 如 AWS Java SDK v1 问题中所述,实施对指定过长的到期日期很敏感。我确实玩弄了一些过期日期,但并没有解决问题。
  • 您是否尝试过使用 aws-iam-authenticator 实用程序获取令牌
  • 我以前使用过 aws-iam-authenticator,但我需要能够从 Java 源代码生成令牌 - 无需安装任何东西。我已经让这些东西与 AWS Java SDK v1 一起使用,只是在 SDK 的 v2 上遇到了问题。
  • 我目前正在使用 AWS Java SDK v1 来生成令牌 - 但现在我必须将它放在我的类路径中 :( 一旦我弄清楚这一点,我就可以重构并删除 v1来自我的依赖项的 SDK :)
  • 您运行的是哪个 Kubernetes 版本?这个应用程序打算在哪里运行(在集群之外,在集群内部)?

标签: amazon-web-services kubernetes aws-sdk amazon-eks aws-java-sdk-2.x


【解决方案1】:

好吧,我终于搞定了。

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

问题出在我的 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);
    }
}

注意URI 对象的path(第三个)参数中的/。 AWS Java SDK v1 版本没有像那样创建 URI,而是在别处指定了 /。如果我现在将URI 打印为字符串,我会得到https://sts.eu-west-1.amazonaws.com/,而问题中的原始版本刚刚返回https://sts.eu-west-1.amazonaws.com

很有趣——原来的版本确实也生成了一个令牌,但是这个令牌被 Kubernetes 拒绝了。如果到期日期太远,人们应该会期待类似的行为 - 你会得到一个令牌,但它会导致来自 Kubernetes 服务的 Unauthorized 响应。

更改 STS 端点后一切正常,但我又做了一项更改:

我在Aws4PresignerParams 中添加了以下行:

.signingClockOverride(Clock.systemUTC())

这不是必需的,但是原始的 AWS Java SDK v1 在指定 SdkClock.STANDARD 时确实使用了时钟,而我在 AWS Java SDK v2 版本中使用的 ZonedDateTime 确实使用了 UTC 时区。

【讨论】:

  • 所以是的,我得到了它的工作,但我对它背后的推理没有太多的了解。即使没有/,我仍然得到了一个令牌,但是当我开始与 Kubernetes 集成时,它就不起作用了。
  • 只是另一个有趣的问题 - 最初的 AWS Java SDK v1 问题确实表明令牌的寿命很短。一旦我尝试将过期日期设置为超过 60 秒,同样的事情就会发生 - 我得到一个令牌,但它会导致 Unauthorized 响应。
【解决方案2】:

为了完整起见,如果您想将令牌与 Kubernetes 客户端 (io.kubernetes:client-java) 一起使用,您可以添加一个拦截器,在每个请求之前生成一个有效令牌:

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

【讨论】:

    猜你喜欢
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 2016-04-11
    • 1970-01-01
    相关资源
    最近更新 更多