-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
reload service account token after expiry #967
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package io.kubernetes.client.util.credentials; | ||
|
||
import io.kubernetes.client.openapi.ApiClient; | ||
import java.io.IOException; | ||
import java.nio.charset.Charset; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.time.Instant; | ||
import okhttp3.Interceptor; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.Request; | ||
import okhttp3.Response; | ||
|
||
// TODO: prefer OpenAPI backed Auentication once it is available. see details in | ||
// https://github.com/OpenAPITools/openapi-generator/pull/6036. currently, the | ||
// workaround is to hijack the http request. | ||
public class TokenFileAuthentication implements Authentication, Interceptor { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i presume using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the current openapi doesn't have reloading logic in we need something similar to this change OpenAPITools/openapi-generator#6036 (it might take much longer time to merge) to implement reloading. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
convinced, can you leave a TODO comment to reference OpenAPITools/openapi-generator#6036 so that we can converge in the future? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, done |
||
private String file; | ||
private String token; | ||
private Instant expiry; | ||
|
||
public TokenFileAuthentication(String file) { | ||
this.expiry = Instant.MIN; | ||
this.file = file; | ||
} | ||
|
||
private String getToken() { | ||
if (Instant.now().isAfter(this.expiry)) { | ||
try { | ||
this.token = | ||
new String(Files.readAllBytes(Paths.get(this.file)), Charset.defaultCharset()).trim(); | ||
expiry = Instant.now().plusSeconds(60); | ||
} catch (IOException ie) { | ||
throw new RuntimeException("Cannot read file: " + this.file); | ||
} | ||
} | ||
|
||
return this.token; | ||
} | ||
|
||
public void setExpiry(Instant expiry) { | ||
this.expiry = expiry; | ||
} | ||
|
||
public void setFile(String file) { | ||
this.file = file; | ||
} | ||
|
||
@Override | ||
public void provide(ApiClient client) { | ||
OkHttpClient withInterceptor = client.getHttpClient().newBuilder().addInterceptor(this).build(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the # of interceptors seems to be growing unboundly upon every There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, the assumption is made that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yea, that's true |
||
client.setHttpClient(withInterceptor); | ||
} | ||
|
||
@Override | ||
public Response intercept(Interceptor.Chain chain) throws IOException { | ||
Request request = chain.request(); | ||
Request newRequest; | ||
newRequest = request.newBuilder().header("Authorization", "Bearer " + getToken()).build(); | ||
return chain.proceed(newRequest); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package io.kubernetes.client.util.credentials; | ||
|
||
import static com.github.tomakehurst.wiremock.client.WireMock.*; | ||
|
||
import com.github.tomakehurst.wiremock.client.WireMock; | ||
import com.github.tomakehurst.wiremock.junit.WireMockRule; | ||
import com.google.common.io.Resources; | ||
import io.kubernetes.client.openapi.ApiClient; | ||
import io.kubernetes.client.openapi.ApiException; | ||
import io.kubernetes.client.openapi.Configuration; | ||
import io.kubernetes.client.openapi.apis.CoreV1Api; | ||
import java.io.IOException; | ||
import java.time.Instant; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
|
||
public class TokenFileAuthenticationTest { | ||
private static final String SERVICEACCOUNT_TOKEN1_PATH = | ||
Resources.getResource("token1").getPath(); | ||
private static final String SERVICEACCOUNT_TOKEN2_PATH = | ||
Resources.getResource("token2").getPath(); | ||
private static final int PORT = 8089; | ||
private TokenFileAuthentication auth; | ||
|
||
@Rule public WireMockRule wireMockRule = new WireMockRule(PORT); | ||
|
||
@Before | ||
public void setup() throws IOException { | ||
final ApiClient client = new ApiClient(); | ||
client.setBasePath("http://localhost:" + PORT); | ||
this.auth = new TokenFileAuthentication(SERVICEACCOUNT_TOKEN1_PATH); | ||
this.auth.provide(client); | ||
Configuration.setDefaultApiClient(client); | ||
} | ||
|
||
@Test | ||
public void testTokenProvided() throws IOException, ApiException { | ||
stubFor( | ||
get(urlPathEqualTo("/api/v1/pods")).willReturn(okForContentType("application/json", "{}"))); | ||
CoreV1Api api = new CoreV1Api(); | ||
|
||
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null); | ||
WireMock.verify( | ||
1, | ||
getRequestedFor(urlPathEqualTo("/api/v1/pods")) | ||
.withHeader("Authorization", equalTo("Bearer token1"))); | ||
|
||
this.auth.setFile(SERVICEACCOUNT_TOKEN2_PATH); | ||
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null); | ||
WireMock.verify( | ||
2, | ||
getRequestedFor(urlPathEqualTo("/api/v1/pods")) | ||
.withHeader("Authorization", equalTo("Bearer token1"))); | ||
|
||
this.auth.setExpiry(Instant.now().minusSeconds(1)); | ||
api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null); | ||
WireMock.verify( | ||
1, | ||
getRequestedFor(urlPathEqualTo("/api/v1/pods")) | ||
.withHeader("Authorization", equalTo("Bearer token2"))); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
token1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
token2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to keep backward compatibility and users are expected to opt-in to the change. wdyt add a constructor w/ parameter
reloadInterval
and factor the originalcluster()
to adopt an infinite (or zero) interval?NOTE: even tho minute-ish reloading SA is always recommended and users are even supposed to be not aware of the change, i suppose it will take a release or two for users to get onboard to the SA rotation..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cluster()
with reloading should be the default so that users dont have to do anything to migrate and add aoldCluster
as the fail safe.update the InClusterClusterExample to something like:
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea we should also reflect how to opt-in SA reloading in the example. note that there's two aspects of change the pull is involving:
the first is good while i'm leaning on doing the second in another release. i will defer to @brendandburns for thoughts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just do the whole thing. We haven't cut a
9.0.0
release yet so I think it's ok to make this change in the9.0.0
release.