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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
### Deprecations
* `read...Credentials()` methods for specific database mounts (#92)

### Features
* Support PEM certificate string from `VAULT_CACERT` environment variable (#93)

### Dependencies
* Updated Jackson to 2.18.3 (#90)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -326,7 +328,11 @@ public HTTPVaultConnectorBuilder fromEnv() throws VaultConnectorException {

/* Parse certificate, if set */
if (System.getenv(ENV_VAULT_CACERT) != null && !System.getenv(ENV_VAULT_CACERT).isBlank()) {
return withTrustedCA(Paths.get(System.getenv(ENV_VAULT_CACERT)));
X509Certificate cert = certificateFromString(System.getenv(ENV_VAULT_CACERT));
if (cert == null) {
cert = certificateFromFile(Paths.get(System.getenv(ENV_VAULT_CACERT)));
}
return withTrustedCA(cert);
}
return this;
}
Expand Down Expand Up @@ -398,6 +404,28 @@ public HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
return con;
}

/**
* Read given certificate file to X.509 certificate.
*
* @param cert Certificate string (optionally PEM)
* @return X.509 Certificate object if parseable, else {@code null}
* @throws TlsException on error
* @since 1.5.0
*/
private X509Certificate certificateFromString(final String cert) throws TlsException {
// Check if PEM header is present in given string
if (cert.contains("-BEGIN ") && cert.contains("-END")) {
try (var is = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
} catch (IOException | CertificateException e) {
throw new TlsException("Unable to read certificate.", e);
}
}

// Not am PEM string, skip
return null;
}

/**
* Read given certificate file to X.509 certificate.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
import java.io.File;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;

import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;
import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -128,19 +131,6 @@ void testFromEnv() throws Exception {
return null;
});

// Provide CA certificate.
String vaultCacert = tempDir.toString() + "/doesnotexist";
withVaultEnv(VAULT_ADDR, vaultCacert, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
TlsException e = assertThrows(
TlsException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Creation with unknown cert path failed"
);
assertEquals(vaultCacert, assertInstanceOf(NoSuchFileException.class, e.getCause()).getFile());

return null;
});

// Automatic authentication.
withVaultEnv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
Expand All @@ -164,6 +154,59 @@ void testFromEnv() throws Exception {
});
}

/**
* Test CA certificate handling from environment variables
*/
@Test
void testCertificateFromEnv() throws Exception {
// From direct PEM content
String pem = Files.readString(Paths.get(getClass().getResource("/tls/ca.pem").toURI()));
AtomicReference<Object> certFromPem = new AtomicReference<>();
withVaultEnv(VAULT_ADDR, pem, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Builder with PEM certificate from environment failed"
);
HTTPVaultConnector connector = builder.build();

certFromPem.set(getRequestHelperPrivate(connector, "trustedCaCert"));
assertNotNull(certFromPem.get(), "Trusted CA cert from PEM not set");

return null;
});

// From file path
String file = Paths.get(getClass().getResource("/tls/ca.pem").toURI()).toString();
AtomicReference<Object> certFromFile = new AtomicReference<>();
withVaultEnv(VAULT_ADDR, file, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Builder with certificate path from environment failed"
);
HTTPVaultConnector connector = builder.build();

certFromFile.set(getRequestHelperPrivate(connector, "trustedCaCert"));
assertNotNull(certFromFile.get(), "Trusted CA cert from file not set");

return null;
});

assertEquals(certFromPem.get(), certFromFile.get(), "Certificates from PEM and file should be equal");

// Non-existing path CA certificate path
String doesNotExist = tempDir.toString() + "/doesnotexist";
withVaultEnv(VAULT_ADDR, doesNotExist, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
TlsException e = assertThrows(
TlsException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Creation with unknown cert path failed"
);
assertEquals(doesNotExist, assertInstanceOf(NoSuchFileException.class, e.getCause()).getFile());

return null;
});
}

private SystemLambda.WithEnvironmentVariables withVaultEnv(String vaultAddr, String vaultCacert, String vaultMaxRetries, String vaultToken) {
return withEnvironmentVariable("VAULT_ADDR", vaultAddr)
.and("VAULT_CACERT", vaultCacert)
Expand Down