Skip to content

Commit 61c28c8

Browse files
authored
Merge pull request #2806 from yue9944882/eks-authentication
EKS authentication based on AWS Sigv4
2 parents 4e97f33 + a51debb commit 61c28c8

15 files changed

Lines changed: 813 additions & 12 deletions

File tree

examples/examples-release-19/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
<artifactId>spring-boot-starter-actuator</artifactId>
8989
<version>${spring.boot.version}</version>
9090
</dependency>
91+
<dependency>
92+
<groupId>com.amazonaws</groupId>
93+
<artifactId>aws-java-sdk-sts</artifactId>
94+
</dependency>
9195

9296
</dependencies>
9397

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.examples;
14+
15+
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
16+
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
17+
import io.kubernetes.client.openapi.ApiClient;
18+
import io.kubernetes.client.openapi.ApiException;
19+
import io.kubernetes.client.openapi.models.VersionInfo;
20+
import io.kubernetes.client.util.ClientBuilder;
21+
import io.kubernetes.client.util.credentials.EKSAuthentication;
22+
import io.kubernetes.client.util.version.Version;
23+
24+
import java.io.IOException;
25+
26+
public class EKSAuthenticationExample {
27+
public static void main(String[] args) throws IOException, ApiException {
28+
29+
// Connecting an EKS cluster using {@link io.kubernetes.client.util.credentials.EKSAuthentication }
30+
31+
// This role should have access to the EKS cluster.
32+
String roleArn = "arn:aws:iam::123456789:role/TestRole";
33+
// Arbitrary role session name.
34+
String roleSessionName = "test";
35+
// Region where the EKS cluster at.
36+
String region = "us-west-2";
37+
// EKS cluster name.
38+
String clusterName = "test-2";
39+
40+
STSAssumeRoleSessionCredentialsProvider credProvider = new STSAssumeRoleSessionCredentialsProvider(
41+
new DefaultAWSCredentialsProviderChain().getCredentials(),
42+
roleArn,
43+
roleSessionName);
44+
45+
ApiClient apiClient = ClientBuilder.standard()
46+
.setAuthentication(new EKSAuthentication(credProvider, region, clusterName))
47+
.build();
48+
49+
Version version = new Version(apiClient);
50+
VersionInfo versionInfo = version.getVersion();
51+
System.out.println(versionInfo);
52+
}
53+
}

pom.xml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@
139139
<version>1.6.7</version>
140140
<optional>true</optional>
141141
</dependency>
142+
<dependency>
143+
<groupId>com.amazonaws</groupId>
144+
<artifactId>aws-java-sdk-sts</artifactId>
145+
<version>1.12.560</version>
146+
<optional>true</optional>
147+
</dependency>
142148
<dependency>
143149
<groupId>com.google.protobuf</groupId>
144150
<artifactId>protobuf-java</artifactId>
@@ -233,7 +239,12 @@
233239
<version>1.20.0</version>
234240
<optional>true</optional>
235241
</dependency>
236-
242+
<dependency>
243+
<groupId>com.fasterxml.jackson.core</groupId>
244+
<artifactId>jackson-annotations</artifactId>
245+
<version>2.15.2</version>
246+
<optional>true</optional>
247+
</dependency>
237248

238249
<!-- tests -->
239250
<dependency>
@@ -307,12 +318,6 @@
307318
<version>3.24.2</version>
308319
<scope>test</scope>
309320
</dependency>
310-
<dependency>
311-
<groupId>com.fasterxml.jackson.core</groupId>
312-
<artifactId>jackson-annotations</artifactId>
313-
<version>2.15.2</version>
314-
<scope>test</scope>
315-
</dependency>
316321
</dependencies>
317322
</dependencyManagement>
318323

util/pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@
6161
<dependency>
6262
<groupId>com.microsoft.azure</groupId>
6363
<artifactId>adal4j</artifactId>
64-
<optional>true</optional>
64+
</dependency>
65+
<dependency>
66+
<groupId>com.amazonaws</groupId>
67+
<artifactId>aws-java-sdk-sts</artifactId>
6568
</dependency>
6669
<dependency>
6770
<groupId>ch.qos.logback</groupId>

util/src/main/java/io/kubernetes/client/util/ConfigPersister.java renamed to util/src/main/java/io/kubernetes/client/persister/ConfigPersister.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
See the License for the specific language governing permissions and
1111
limitations under the License.
1212
*/
13-
package io.kubernetes.client.util;
13+
package io.kubernetes.client.persister;
1414

1515
import java.io.IOException;
1616
import java.util.ArrayList;

util/src/main/java/io/kubernetes/client/util/FilePersister.java renamed to util/src/main/java/io/kubernetes/client/persister/FilePersister.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
See the License for the specific language governing permissions and
1111
limitations under the License.
1212
*/
13-
package io.kubernetes.client.util;
13+
package io.kubernetes.client.persister;
1414

1515
import java.io.File;
1616
import java.io.FileWriter;
1717
import java.io.IOException;
1818
import java.util.ArrayList;
1919
import java.util.HashMap;
20+
21+
import io.kubernetes.client.persister.ConfigPersister;
2022
import org.yaml.snakeyaml.LoaderOptions;
2123
import org.yaml.snakeyaml.Yaml;
2224
import org.yaml.snakeyaml.constructor.SafeConstructor;

util/src/main/java/io/kubernetes/client/util/ClientBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.kubernetes.client.openapi.ApiClient;
2525
import io.kubernetes.client.openapi.ApiException;
2626
import io.kubernetes.client.openapi.models.V1CertificateSigningRequest;
27+
import io.kubernetes.client.persister.FilePersister;
2728
import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
2829
import io.kubernetes.client.util.credentials.Authentication;
2930
import io.kubernetes.client.util.credentials.ClientCertificateAuthentication;

util/src/main/java/io/kubernetes/client/util/KubeConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.google.gson.JsonObject;
1717
import com.google.gson.JsonParseException;
1818
import com.google.gson.JsonParser;
19+
import io.kubernetes.client.persister.ConfigPersister;
1920
import io.kubernetes.client.util.authenticators.Authenticator;
2021
import io.kubernetes.client.util.authenticators.AzureActiveDirectoryAuthenticator;
2122
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
@@ -235,7 +236,7 @@ public Map<String, String> getCredentials() {
235236
}
236237
}
237238
Map<String, String> credentialsViaExecCredential =
238-
credentialsViaExecCredential((Map<String, Object>) currentUser.get("exec"));
239+
getCredentialsViaExecCredential((Map<String, Object>) currentUser.get("exec"));
239240
if (credentialsViaExecCredential != null) {
240241
return credentialsViaExecCredential;
241242
}
@@ -265,7 +266,7 @@ public Map<String, String> getCredentials() {
265266
* Authenticating » client-go credential plugins</a>
266267
*/
267268
@SuppressWarnings("unchecked")
268-
private Map<String, String> credentialsViaExecCredential(Map<String, Object> execMap) {
269+
private Map<String, String> getCredentialsViaExecCredential(Map<String, Object> execMap) {
269270
if (execMap == null) {
270271
return null;
271272
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.util.credentials;
14+
15+
import com.amazonaws.auth.AWSSessionCredentials;
16+
import com.amazonaws.auth.AWSSessionCredentialsProvider;
17+
import io.kubernetes.client.openapi.ApiClient;
18+
import io.kubernetes.client.util.eks.AWS4STSSigner;
19+
import io.kubernetes.client.util.eks.AWS4SignerBase;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import java.net.MalformedURLException;
24+
import java.net.URI;
25+
import java.net.URISyntaxException;
26+
import java.time.Instant;
27+
import java.time.temporal.ChronoUnit;
28+
import java.util.Base64;
29+
import java.util.HashMap;
30+
31+
/**
32+
* EKS cluster authentication which generates a bearer token from AWS AK/SK. It doesn't require an "aws"
33+
* command line tool in the $PATH.
34+
*/
35+
public class EKSAuthentication implements Authentication {
36+
37+
private static final Logger log = LoggerFactory.getLogger(EKSAuthentication.class);
38+
39+
/**
40+
* Instantiates a new Eks authentication.
41+
*
42+
* @param provider the AWS credential provider
43+
* @param region the region where EKS cluster at
44+
* @param clusterName the EKS cluster name
45+
*/
46+
public EKSAuthentication(AWSSessionCredentialsProvider provider, String region, String clusterName) {
47+
this(provider, region, clusterName, MAX_EXPIRY_SECONDS);
48+
}
49+
50+
public EKSAuthentication(AWSSessionCredentialsProvider provider, String region, String clusterName, int expirySeconds) {
51+
this.provider = provider;
52+
this.region = region;
53+
this.clusterName = clusterName;
54+
if (expirySeconds > MAX_EXPIRY_SECONDS) {
55+
expirySeconds = MAX_EXPIRY_SECONDS;
56+
}
57+
this.expirySeconds = expirySeconds;
58+
}
59+
60+
private static final int MAX_EXPIRY_SECONDS = 60 * 15;
61+
private final AWSSessionCredentialsProvider provider;
62+
private final String region;
63+
private final String clusterName;
64+
65+
private final int expirySeconds;
66+
67+
@Override
68+
public void provide(ApiClient client) {
69+
URI uri = URI.create("https://sts." + this.region + ".amazonaws.com/");
70+
AWSSessionCredentials cred = provider.getCredentials();
71+
try {
72+
AWS4STSSigner signer = new AWS4STSSigner(
73+
uri.toURL(),
74+
"GET",
75+
"sts",
76+
this.region);
77+
String token = "k8s-aws-v1." + Base64.getEncoder().withoutPadding().encodeToString(signer.computeSignature(
78+
uri,
79+
new HashMap<String, String>() {{
80+
put("x-k8s-aws-id", clusterName);
81+
82+
}},
83+
new HashMap<String, String>() {{
84+
put("Action", "GetCallerIdentity");
85+
put("Version", "2011-06-15");
86+
}},
87+
expirySeconds,
88+
AWS4SignerBase.EMPTY_BODY_SHA256,
89+
cred.getAWSAccessKeyId(),
90+
cred.getAWSSecretKey(),
91+
cred.getSessionToken()).getBytes());
92+
client.setApiKeyPrefix("Bearer");
93+
client.setApiKey(token);
94+
log.info("Generated BEARER token for ApiClient, expiring at {}", Instant.now().plus(expirySeconds, ChronoUnit.SECONDS));
95+
} catch (MalformedURLException | URISyntaxException e) {
96+
throw new RuntimeException(e);
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)