使用 AWS Java SDK v2 从 AWS EKS 获取身份验证令牌
Get authentication token from AWS EKS using the AWS Java SDK v2
如何使用 AWS Java SDK v2 从 AWS EKS 获取 Kubernetes 身份验证令牌?一个身份验证令牌,然后可用于使用 Kubernetes SDK 对 Kubernetes 进行身份验证。换句话说,我想从 EKS 获取身份验证令牌以用于 Kubernetes 的身份验证,这样我就不必创建 "kube config".
我实际上得到了一个使用 AWS Java SDK v1(不是 v2)的解决方案,查看下面的代码示例 open issue. There is also a Python code example 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)中使用该令牌时,我得到了一个 "Unauthorized" 响应 - 所以我遗漏了一些我可以'把我的手指放在...
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 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
有意思——原来的版本也确实生成了一个token,但是这个token被Kubernetes拒绝了。如果过期日期离未来太远,应该会出现类似的行为——你会得到一个令牌,但它会导致来自 Kubernetes 服务的 Unauthorized
响应。
更改 STS 端点后一切正常,但我又做了一项更改:
我在 Aws4PresignerParams
中添加了以下行:
.signingClockOverride(Clock.systemUTC())
这不是必需的,但原始 AWS Java SDK v1 在指定 SdkClock.STANDARD
时确实对时钟做了一些事情,而我在 AWS 中使用的 ZonedDateTime
Java SDK v2 版本使用 UTC 时区。
为了完整起见,如果您想将令牌与 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();
}
如何使用 AWS Java SDK v2 从 AWS EKS 获取 Kubernetes 身份验证令牌?一个身份验证令牌,然后可用于使用 Kubernetes SDK 对 Kubernetes 进行身份验证。换句话说,我想从 EKS 获取身份验证令牌以用于 Kubernetes 的身份验证,这样我就不必创建 "kube config".
我实际上得到了一个使用 AWS Java SDK v1(不是 v2)的解决方案,查看下面的代码示例 open issue. There is also a Python code example 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)中使用该令牌时,我得到了一个 "Unauthorized" 响应 - 所以我遗漏了一些我可以'把我的手指放在...
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 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
有意思——原来的版本也确实生成了一个token,但是这个token被Kubernetes拒绝了。如果过期日期离未来太远,应该会出现类似的行为——你会得到一个令牌,但它会导致来自 Kubernetes 服务的 Unauthorized
响应。
更改 STS 端点后一切正常,但我又做了一项更改:
我在 Aws4PresignerParams
中添加了以下行:
.signingClockOverride(Clock.systemUTC())
这不是必需的,但原始 AWS Java SDK v1 在指定 SdkClock.STANDARD
时确实对时钟做了一些事情,而我在 AWS 中使用的 ZonedDateTime
Java SDK v2 版本使用 UTC 时区。
为了完整起见,如果您想将令牌与 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();
}