Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ private CloudFoundryContainerKeyManagerFactory(String algorithm, Path certificat
this.privateKey = privateKey;
this.systemKeyManagerFactory = getKeyManagerFactory();

this.logger.fine(String.format("Algorithm: %s", algorithm));
this.logger.fine(String.format("Certificates: %s", certificates));
this.logger.fine(String.format("Private Key: %s", privateKey));
this.logger.fine(() -> String.format("Algorithm: %s", algorithm));
this.logger.fine(() -> String.format("Certificates: %s", certificates));
this.logger.fine(() -> String.format("Private Key: %s", privateKey));
}

@Override
Expand Down Expand Up @@ -105,7 +105,7 @@ private static Path getProperty(String name) {
private FileWatchingX509ExtendedKeyManager getContainerKeyManager() {
synchronized (MONITOR) {
if (CACHED_CONTAINER_KEY_MANAGER == null && this.certificates != null && Files.exists(this.certificates) && this.privateKey != null && Files.exists(this.privateKey)) {
this.logger.info(String.format("Adding Key Manager for %s and %s", this.privateKey, this.certificates));
this.logger.info(() -> String.format("Adding Key Manager for %s and %s", this.privateKey, this.certificates));
CACHED_CONTAINER_KEY_MANAGER = new FileWatchingX509ExtendedKeyManager(this.certificates, this.privateKey, getKeyManagerFactory());
}

Expand All @@ -126,7 +126,7 @@ private X509ExtendedKeyManager getSystemKeyManager() {
if (this.cachedSystemKeyManager == null) {
for (KeyManager candidate : this.systemKeyManagerFactory.getKeyManagers()) {
if (candidate instanceof X509ExtendedKeyManager) {
this.logger.info("Adding System Key Manager");
this.logger.fine("Adding System Key Manager");
this.cachedSystemKeyManager = (X509ExtendedKeyManager) candidate;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ private CloudFoundryContainerTrustManagerFactory(String algorithm, Path certific
this.certificates = certificates;
this.systemTrustManagerFactory = getTrustManagerFactory();

this.logger.fine(String.format("Algorithm: %s", algorithm));
this.logger.fine(String.format("Certificates: %s", certificates));
this.logger.fine(() -> String.format("Algorithm: %s", algorithm));
this.logger.fine(() -> String.format("Certificates: %s", certificates));
}

@Override
Expand Down Expand Up @@ -100,7 +100,7 @@ private static Path getCertificatesLocation() {
private FileWatchingX509ExtendedTrustManager getContainerTrustManager() {
synchronized (MONITOR) {
if (CACHED_CONTAINER_TRUST_MANAGER == null && this.certificates != null && Files.exists(this.certificates)) {
this.logger.info(String.format("Adding TrustManager for %s", this.certificates));
this.logger.info(() -> String.format("Adding TrustManager for %s", this.certificates));
CACHED_CONTAINER_TRUST_MANAGER = new FileWatchingX509ExtendedTrustManager(this.certificates, getTrustManagerFactory());
}

Expand All @@ -113,7 +113,7 @@ private X509ExtendedTrustManager getSystemTrustManager() {
if (this.cachedSystemTrustManager == null) {
for (TrustManager candidate : this.systemTrustManagerFactory.getTrustManagers()) {
if (candidate instanceof X509ExtendedTrustManager) {
this.logger.info("Adding System Trust Manager");
this.logger.fine("Adding System Trust Manager");
this.cachedSystemTrustManager = (X509ExtendedTrustManager) candidate;
break;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/cloudfoundry/security/FileWatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public Thread newThread(Runnable r) {

@Override
public void run() {
this.logger.info(String.format("Start watching %s", this.source));
this.logger.info(() -> String.format("Start watching %s", this.source));

WatchService watchService;
WatchKey expected;
Expand All @@ -81,22 +81,22 @@ public void run() {
WatchKey actual = watchService.take();

if (!actual.equals(expected)) {
this.logger.warning(String.format("Unknown watch key: %s", actual));
this.logger.warning(() -> String.format("Unknown watch key: %s", actual));
continue;
}

for (WatchEvent<?> watchEvent : actual.pollEvents()) {
Path changed = (Path) watchEvent.context();

if (!this.source.getFileName().equals(changed)) {
this.logger.fine(String.format("Discarding unimportant file change: %s", changed));
this.logger.fine(() -> String.format("Discarding unimportant file change: %s", changed));
continue;
}
this.callback.run();
}

if (!actual.reset()) {
this.logger.warning(String.format("Watch key is no longer valid: %s", actual));
this.logger.warning(() -> String.format("Watch key is no longer valid: %s", actual));
break;
}
} catch (InterruptedException e) {
Expand All @@ -106,7 +106,7 @@ public void run() {
}
}

this.logger.info(String.format("Stop watching %s", this.source));
this.logger.info(() -> String.format("Stop watching %s", this.source));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ final class FileWatchingX509ExtendedTrustManager extends X509ExtendedTrustManage
new FileWatcher(this.certificates, new FileWatcherCallback()).watch();

if (this.trustManager.compareAndSet(null, getTrustManager(getKeyStore()))) {
this.logger.info(String.format("Initialized TrustManager for %s", this.certificates));
this.logger.info(() -> String.format("Initialized TrustManager for %s", this.certificates));
}
}

Expand Down Expand Up @@ -123,9 +123,9 @@ private class FileWatcherCallback implements Runnable {
@Override
public void run() {
if (FileWatchingX509ExtendedTrustManager.this.trustManager.getAndSet(getTrustManager(getKeyStore())) == null) {
FileWatchingX509ExtendedTrustManager.this.logger.info(String.format("Initialized TrustManager for %s", FileWatchingX509ExtendedTrustManager.this.certificates));
FileWatchingX509ExtendedTrustManager.this.logger.info(() -> String.format("Initialized TrustManager for %s", FileWatchingX509ExtendedTrustManager.this.certificates));
} else {
FileWatchingX509ExtendedTrustManager.this.logger.info(String.format("Updated TrustManager for %s", FileWatchingX509ExtendedTrustManager.this.certificates));
FileWatchingX509ExtendedTrustManager.this.logger.info(() -> String.format("Updated TrustManager for %s", FileWatchingX509ExtendedTrustManager.this.certificates));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,25 @@
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import static org.assertj.core.api.Assertions.assertThat;

public final class CloudFoundryContainerKeyManagerFactoryTest extends AbstractLoggingTest {

@Test
public void logsSystemKeyManagerAtFine() throws Exception {
List<LogRecord> records = LogRecorder.record(CloudFoundryContainerKeyManagerFactory.SunX509.class, () -> {
CloudFoundryContainerKeyManagerFactory.SunX509 factory = new CloudFoundryContainerKeyManagerFactory.SunX509(null, null);
factory.engineInit(getKeyStore(), new char[0]);
factory.engineGetKeyManagers();
});

assertThat(records).filteredOn(r -> "Adding System Key Manager".equals(r.getMessage())).extracting(LogRecord::getLevel).containsExactly(Level.FINE);
}

@Test
public void customKeyManager() throws NoSuchProviderException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException {
CloudFoundryContainerKeyManagerFactory.SunX509 factory = new CloudFoundryContainerKeyManagerFactory.SunX509(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -51,6 +54,17 @@ public void defaultTrustManagerForNullCertificatesLocation() throws CertificateE
assertThat(((DelegatingX509ExtendedTrustManager) trustManager).size()).isEqualTo(1);
}

@Test
public void logsSystemTrustManagerAtFine() throws Exception {
List<LogRecord> records = LogRecorder.record(CloudFoundryContainerTrustManagerFactory.PKIXFactory.class, () -> {
CloudFoundryContainerTrustManagerFactory.PKIXFactory factory = new CloudFoundryContainerTrustManagerFactory.PKIXFactory(null);
factory.engineInit(getKeyStore());
factory.engineGetTrustManagers();
});

assertThat(records).filteredOn(r -> "Adding System Trust Manager".equals(r.getMessage())).extracting(LogRecord::getLevel).containsExactly(Level.FINE);
}

private KeyStore getKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ public void watchesWatchedFile() throws IOException, InterruptedException, NoSuc
assertThat(trustManager.getAcceptedIssuers()).hasSize(48);

Thread.sleep(5_000);
Files.copy(Paths.get("src/test/resources/server-certificates-173.pem"), watchedCertificates, StandardCopyOption.REPLACE_EXISTING);
// Replace atomically, as a rename, so no update sees a half-written file.
Path replacement = Files.createTempFile(watchedCertificates.getParent(), "certificates-", ".tmp");
Files.copy(Paths.get("src/test/resources/server-certificates-173.pem"), replacement, StandardCopyOption.REPLACE_EXISTING);
Files.move(replacement, watchedCertificates, StandardCopyOption.ATOMIC_MOVE);

long timeout = System.currentTimeMillis() + 300_000;
long timeout = System.currentTimeMillis() + 30_000;
for (; ; ) {
if (System.currentTimeMillis() > timeout) {
fail("Failed to update within timeout");
Expand Down
71 changes: 71 additions & 0 deletions src/test/java/org/cloudfoundry/security/LogRecorder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.cloudfoundry.security;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

final class LogRecorder {

private LogRecorder() {
}

// Records everything logged to the logger named after the given class while the action runs, at all levels.
static List<LogRecord> record(Class<?> loggerClass, Action action) throws Exception {
Logger logger = Logger.getLogger(loggerClass.getName());
List<LogRecord> records = new ArrayList<>();
Handler handler = new Handler() {

@Override
public void publish(LogRecord record) {
records.add(record);
}

@Override
public void flush() {
}

@Override
public void close() {
}

};

Level level = logger.getLevel();
logger.addHandler(handler);
logger.setLevel(Level.ALL);
try {
action.run();
} finally {
logger.removeHandler(handler);
logger.setLevel(level);
}

return records;
}

interface Action {

void run() throws Exception;

}

}
Loading