Skip to content

Commit f4125c5

Browse files
committed
xds: Reload cert/key even if only one of them changes
1 parent f90b881 commit f4125c5

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

xds/src/main/java/io/grpc/xds/internal/security/certprovider/FileWatcherCertificateProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ void checkAndReloadCertificates() {
116116
FileTime currentCertTime = Files.getLastModifiedTime(certFile);
117117
FileTime currentKeyTime = Files.getLastModifiedTime(keyFile);
118118
if (!currentCertTime.equals(lastModifiedTimeCert)
119-
&& !currentKeyTime.equals(lastModifiedTimeKey)) {
119+
|| !currentKeyTime.equals(lastModifiedTimeKey)) {
120120
byte[] certFileContents = Files.readAllBytes(certFile);
121121
byte[] keyFileContents = Files.readAllBytes(keyFile);
122122
FileTime currentCertTime2 = Files.getLastModifiedTime(certFile);

xds/src/test/java/io/grpc/xds/internal/security/certprovider/FileWatcherCertificateProviderTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,59 @@ public void certAndKeyFileUpdateOnly()
261261
verifyTimeServiceAndScheduledFuture();
262262
}
263263

264+
@Test
265+
public void certFileUpdateOnly()
266+
throws IOException, CertificateException, InterruptedException {
267+
TestScheduledFuture<?> scheduledFuture =
268+
new TestScheduledFuture<>();
269+
doReturn(scheduledFuture)
270+
.when(timeService)
271+
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
272+
// Ideally we'd use a matching cert/key pair here, but we don't actually have any ready-made.
273+
// The test doesn't notice they don't match though.
274+
populateTarget(
275+
CLIENT_PEM_FILE, SERVER_0_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
276+
provider.checkAndReloadCertificates();
277+
278+
reset(mockWatcher, timeService);
279+
doReturn(scheduledFuture)
280+
.when(timeService)
281+
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
282+
timeProvider.forwardTime(1, TimeUnit.SECONDS);
283+
// It's normal to get a newer cert while continuing to use the same private key
284+
populateTarget(SERVER_0_PEM_FILE, null, null, null, false, false, false, false);
285+
provider.checkAndReloadCertificates();
286+
verifyWatcherUpdates(SERVER_0_PEM_FILE, null, null);
287+
verifyTimeServiceAndScheduledFuture();
288+
}
289+
290+
@Test
291+
public void keyFileUpdateOnly()
292+
throws IOException, CertificateException, InterruptedException {
293+
TestScheduledFuture<?> scheduledFuture =
294+
new TestScheduledFuture<>();
295+
doReturn(scheduledFuture)
296+
.when(timeService)
297+
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
298+
// Assume the key/cert is not updated atomically and we see a tear between them. Or maybe this
299+
// was just a bug.
300+
populateTarget(
301+
SERVER_0_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
302+
provider.checkAndReloadCertificates();
303+
304+
reset(mockWatcher, timeService);
305+
doReturn(scheduledFuture)
306+
.when(timeService)
307+
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
308+
timeProvider.forwardTime(1, TimeUnit.SECONDS);
309+
// Even though it is strange the key updated without a cert update, we do still want to use the
310+
// new files, as this recovers from the earlier tear.
311+
populateTarget(null, SERVER_0_KEY_FILE, null, null, false, false, false, false);
312+
provider.checkAndReloadCertificates();
313+
verifyWatcherUpdates(SERVER_0_PEM_FILE, null, null);
314+
verifyTimeServiceAndScheduledFuture();
315+
}
316+
264317
@Test
265318
public void spiffeTrustMapFileUpdateOnly() throws Exception {
266319
provider = new FileWatcherCertificateProvider(watcher, true, certFile, keyFile, null,

0 commit comments

Comments
 (0)