Skip to content

Commit

Permalink
Issue #8973 - Rework KeyStoreScanner handling for symlink related cha…
Browse files Browse the repository at this point in the history
…nges

+ Removed changes from #8786 and #8787
+ More test cases

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
  • Loading branch information
joakime committed Dec 6, 2022
1 parent e923123 commit 9047d11
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
Expand Down Expand Up @@ -43,11 +44,6 @@ public class KeyStoreScanner extends ContainerLifeCycle implements Scanner.Discr
private final Scanner _scanner;

public KeyStoreScanner(SslContextFactory sslContextFactory)
{
this(sslContextFactory, true);
}

public KeyStoreScanner(SslContextFactory sslContextFactory, boolean followLinks)
{
this.sslContextFactory = sslContextFactory;
try
Expand All @@ -59,12 +55,6 @@ public KeyStoreScanner(SslContextFactory sslContextFactory, boolean followLinks)
if (monitoredFile.isDirectory())
throw new IllegalArgumentException("expected keystore file not directory");

if (followLinks && keystoreResource.isAlias())
{
// This resource has an alias, so monitor the target of the alias.
monitoredFile = new File(keystoreResource.getAlias());
}

keystoreFile = monitoredFile;
if (LOG.isDebugEnabled())
LOG.debug("Monitored Keystore File: {}", monitoredFile);
Expand All @@ -78,7 +68,7 @@ public KeyStoreScanner(SslContextFactory sslContextFactory, boolean followLinks)
if (!parentFile.exists() || !parentFile.isDirectory())
throw new IllegalArgumentException("error obtaining keystore dir");

_scanner = new Scanner(null, followLinks);
_scanner = new Scanner(null, false);
_scanner.addDirectory(parentFile.toPath());
_scanner.setScanInterval(1);
_scanner.setReportDirs(false);
Expand All @@ -88,11 +78,23 @@ public KeyStoreScanner(SslContextFactory sslContextFactory, boolean followLinks)
addBean(_scanner);
}

private Path getRealKeyStorePath()
{
try
{
return keystoreFile.toPath().toRealPath();
}
catch (IOException e)
{
return keystoreFile.toPath();
}
}

@Override
public void fileAdded(String filename)
{
if (LOG.isDebugEnabled())
LOG.debug("added {}", filename);
LOG.debug("fileAdded {} - keystoreFile.toReal {}", filename, getRealKeyStorePath());

if (keystoreFile.toPath().toString().equals(filename))
reload();
Expand All @@ -102,7 +104,7 @@ public void fileAdded(String filename)
public void fileChanged(String filename)
{
if (LOG.isDebugEnabled())
LOG.debug("changed {}", filename);
LOG.debug("fileChanged {} - keystoreFile.toReal {}", filename, getRealKeyStorePath());

if (keystoreFile.toPath().toString().equals(filename))
reload();
Expand All @@ -112,7 +114,7 @@ public void fileChanged(String filename)
public void fileRemoved(String filename)
{
if (LOG.isDebugEnabled())
LOG.debug("removed {}", filename);
LOG.debug("fileRemoved {} - keystoreFile.toReal {}", filename, getRealKeyStorePath());

if (keystoreFile.toPath().toString().equals(filename))
reload();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -47,6 +48,8 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.hamcrest.MatcherAssert.assertThat;
Expand Down Expand Up @@ -105,7 +108,7 @@ public void start(Configuration configuration, boolean resolveAlias) throws Exce
server.addConnector(connector);

// Configure Keystore Reload.
keyStoreScanner = new KeyStoreScanner(sslContextFactory, resolveAlias);
keyStoreScanner = new KeyStoreScanner(sslContextFactory);
keyStoreScanner.setScanInterval(0);
server.addBean(keyStoreScanner);

Expand Down Expand Up @@ -245,6 +248,109 @@ public void testReloadChangingTargetOfSymbolicLink() throws Exception
assertThat(getExpiryYear(cert2), is(2020));
}

@Test
public void testReloadChangingLinkTargetOfSymbolicLink() throws Exception
{
assumeFileSystemSupportsSymlink();
Path oldKeyStoreSrc = MavenTestingUtils.getTestResourcePathFile("oldKeyStore");
Path newKeyStoreSrc = MavenTestingUtils.getTestResourcePathFile("newKeyStore");

Path sslDir = keystoreDir.resolve("ssl");
Path optDir = keystoreDir.resolve("opt");
Path optKeystoreLink = optDir.resolve("keystore");
Path optKeystore1 = optDir.resolve("keystore.1");
Path optKeystore2 = optDir.resolve("keystore.2");
Path keystoreFile = sslDir.resolve("keystore");

start(sslContextFactory ->
{
FS.ensureEmpty(sslDir);
FS.ensureEmpty(optDir);
Files.copy(oldKeyStoreSrc, optKeystore1);
Files.createSymbolicLink(optKeystoreLink, optKeystore1);
Files.createSymbolicLink(keystoreFile, optKeystoreLink);

sslContextFactory.setKeyStorePath(keystoreFile.toString());
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
});

// Check the original certificate expiry.
X509Certificate cert1 = getCertificateFromServer();
assertThat(getExpiryYear(cert1), is(2015));

// Create a new keystore file
Files.copy(newKeyStoreSrc, optKeystore2);
// Change (middle) link to new keystore
Files.delete(optKeystoreLink);
Files.createSymbolicLink(optKeystoreLink, optKeystore2);
System.err.println("### Triggering scan");
keyStoreScanner.scan(5000);

// The scanner should have detected the updated keystore, expiry should be renewed.
X509Certificate cert2 = getCertificateFromServer();
assertThat(getExpiryYear(cert2), is(2020));
}

/**
* Test a doubly-linked keystore, and refreshing by only modifying the middle symlink.
*/
@Test
public void testDoublySymlinked() throws Exception
{
assumeFileSystemSupportsSymlink();
Path oldKeyStoreSrc = MavenTestingUtils.getTestResourcePathFile("oldKeyStore");
Path newKeyStoreSrc = MavenTestingUtils.getTestResourcePathFile("newKeyStore");

Path sslDir = keystoreDir.resolve("ssl");
Path dataDir = sslDir.resolve("data");
Path timestampNovDir = sslDir.resolve("2022-11");
Path timestampDecDir = sslDir.resolve("2022-12");
Path targetNov = timestampNovDir.resolve("keystore.p12");
Path targetDec = timestampDecDir.resolve("keystore.p12");

boolean followLinks = false; // follow keystore links

start(sslContextFactory ->
{
// What we want is ..
// ssl/keystore.p12 -> data/keystore.p12
// ssl/data/ -> 2022-11/
// ssl/2022-11/keystore.p12 (file)

FS.ensureEmpty(sslDir);
FS.ensureEmpty(timestampNovDir);
FS.ensureEmpty(timestampDecDir);
Files.copy(oldKeyStoreSrc, targetNov);
Files.copy(newKeyStoreSrc, targetDec);

// Create symlink of data/ to 2022-11/
Files.createSymbolicLink(dataDir, timestampNovDir.getFileName());

// Create symlink of keystore.p12 to data/keystore.p12
Path keystoreLink = sslDir.resolve("keystore.p12");
Files.createSymbolicLink(keystoreLink, Paths.get("data/keystore.p12"));

sslContextFactory.setKeyStorePath(keystoreLink.toString());
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
}, followLinks);

// Check the original certificate expiry.
X509Certificate cert1 = getCertificateFromServer();
assertThat(getExpiryYear(cert1), is(2015));

// Change the data/ symlink to 2022-12/ which points to a newer certificate
Files.delete(dataDir);
Files.createSymbolicLink(dataDir, timestampDecDir.getFileName());
System.err.println("### Triggering scan");
keyStoreScanner.scan(5000);

// The scanner should have detected the updated keystore, expiry should be renewed.
X509Certificate cert2 = getCertificateFromServer();
assertThat(getExpiryYear(cert2), is(2020));
}

public Path useKeystore(String keystoreToUse, String keystorePath) throws Exception
{
return useKeystore(MavenTestingUtils.getTestResourcePath(keystoreToUse), keystoreDir.resolve(keystorePath));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.websocket.LEVEL=DEBUG
#org.eclipse.jetty.util.ssl.KeyStoreScanner.LEVEL=DEBUG
#org.eclipse.jetty.util.Scanner.LEVEL=DEBUG

0 comments on commit 9047d11

Please sign in to comment.