diff --git a/README.md b/README.md index 5c3913b..d5632fe 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ contain extension jars) for the `otel.javaagent.extensions` value. ## Network communication The extension contains a client, who is able to communicate with other servers via HTTPS. -To configure TLS +You can set the server url via system or environmental properties. +You have the option to use a keystore for TLS: 1. Provide a local keystore, which contains the certificate of your server 2. Modify the startup command to add the path to the keystore as well as the password. For example: @@ -38,6 +39,7 @@ To configure TLS java -javaagent:path/to/opentelemetry-javaagent.jar \ -Dotel.javaagent.extensions=build/libs/opentelemetry-javaagent.jar \ -Dotel.service.name="my-service" \ + -Dinspectit.config.http.url="https://{server-host:port}/api/v1/connections" \ -Djavax.net.ssl.trustStore="path\to\keystore\agent-keystore.jks" \ -Djavax.net.ssl.trustStorePassword="password" -jar myapp.jar @@ -50,7 +52,7 @@ The repository was build upon this example project: https://github.com/open-tele ### Why Gepard? Gepard is the German name for the animal cheetah as well as an acronym for: -"**G**anzheitliche, **e**ffizienz-orientierte, **P**erformance **A**nwendungsüberwachung mit **R**eporting und **D**iagnose" +"**G**anzheitliche, **e**ffizienz-orientierte, **P**erformance **A**nwendungsüberwachung mit **R**eporting und **D**iagnose", -meaning: holistic, efficiency-orientated, performance application monitoring with reporting and diagnostics. +which means: holistic, efficiency-orientated, performance application monitoring with reporting and diagnostics. diff --git a/build.gradle b/build.gradle index 33e1a89..b4562b6 100644 --- a/build.gradle +++ b/build.gradle @@ -107,6 +107,8 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") + // configure environmental variables + testImplementation("com.github.stefanbirkner:system-lambda:1.2.1") // Otel Java instrumentation that we use and extend during integration tests otel("io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}") @@ -145,6 +147,11 @@ tasks { inputs.files(layout.files(tasks.shadowJar)) inputs.files(layout.files(tasks.extendedAgent)) + // Necessary to use system-lambda in tests + // See: https://github.com/stefanbirkner/system-lambda/issues/10 + jvmArgs '--add-opens', 'java.base/java.util=ALL-UNNAMED' + jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED' + systemProperty 'io.opentelemetry.smoketest.agentPath', configurations.otel.singleFile.absolutePath systemProperty 'io.opentelemetry.smoketest.extendedAgentPath', tasks.extendedAgent.archiveFile.get().asFile.absolutePath systemProperty 'io.opentelemetry.smoketest.extensionPath', tasks.shadowJar.archiveFile.get().asFile.absolutePath @@ -156,9 +163,7 @@ tasks { assemble.dependsOn(shadowJar) - dockerPrepare { - dependsOn extendedAgent - } + dockerPrepare.dependsOn(extendedAgent) docker { Provider agentJar = layout.buildDirectory.dir("/libs/opentelemetry-javaagent.jar") diff --git a/src/main/java/rocks/inspectit/gepard/agent/InspectitAgentExtension.java b/src/main/java/rocks/inspectit/gepard/agent/InspectitAgentExtension.java index 772060d..ce03e19 100644 --- a/src/main/java/rocks/inspectit/gepard/agent/InspectitAgentExtension.java +++ b/src/main/java/rocks/inspectit/gepard/agent/InspectitAgentExtension.java @@ -6,6 +6,7 @@ import net.bytebuddy.agent.builder.AgentBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import rocks.inspectit.gepard.agent.config.ConfigurationResolver; import rocks.inspectit.gepard.agent.notify.NotificationManager; @SuppressWarnings("unused") @@ -13,9 +14,6 @@ public class InspectitAgentExtension implements AgentExtension { private static final Logger log = LoggerFactory.getLogger(InspectitAgentExtension.class); - /** Hard coded server url, which should configurable be in the future */ - private static final String SERVER_URL = "https://localhost:8080/api/v1/connections"; - /** * Entrypoint for the inspectIT gepard extension * @@ -28,9 +26,15 @@ public class InspectitAgentExtension implements AgentExtension { public AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config) { log.info("Starting inspectIT Gepard agent extension ..."); - boolean successful = NotificationManager.sendStartNotification(SERVER_URL); - if (successful) log.info("Successfully notified configuration server about start"); - else log.info("Could not notify configuration server about start"); + String url = ConfigurationResolver.getServerUrl(); + if (url.isEmpty()) log.info("No configuration server url was provided"); + else { + log.info("Sending start notification to configuration server with url: {}", url); + boolean successful = NotificationManager.sendStartNotification(url); + + if (successful) log.info("Successfully notified configuration server about start"); + else log.warn("Could not notify configuration server about start"); + } return agentBuilder; } diff --git a/src/main/java/rocks/inspectit/gepard/agent/config/ConfigurationResolver.java b/src/main/java/rocks/inspectit/gepard/agent/config/ConfigurationResolver.java new file mode 100644 index 0000000..9bc177a --- /dev/null +++ b/src/main/java/rocks/inspectit/gepard/agent/config/ConfigurationResolver.java @@ -0,0 +1,31 @@ +package rocks.inspectit.gepard.agent.config; + +import java.util.Objects; + +/** + * This resolver provides the configured configuration server url. Currently, it is possible to + * configure the url via system properties or environmental properties. System properties are higher + * prioritized than environmental properties. + */ +public class ConfigurationResolver { + + private static final String SERVER_URL_SYSTEM_PROPERTY = "inspectit.config.http.url"; + + private static final String SERVER_URL_ENV_PROPERTY = "INSPECTIT_CONFIG_HTTP_URL"; + + private ConfigurationResolver() {} + + /** + * Get the configured configuration server url. If no url was configured, an empty string will be + * returned. + * + * @return the configured configuration server url + */ + public static String getServerUrl() { + String serverUrlSystemProperty = System.getProperty(SERVER_URL_SYSTEM_PROPERTY); + if (Objects.nonNull(serverUrlSystemProperty)) return serverUrlSystemProperty; + + String serverUrlEnvProperty = System.getenv(SERVER_URL_ENV_PROPERTY); + return Objects.nonNull(serverUrlEnvProperty) ? serverUrlEnvProperty : ""; + } +} diff --git a/src/main/java/rocks/inspectit/gepard/agent/notify/http/HttpClientHolder.java b/src/main/java/rocks/inspectit/gepard/agent/notify/http/HttpClientHolder.java index 4c04113..4c212ad 100644 --- a/src/main/java/rocks/inspectit/gepard/agent/notify/http/HttpClientHolder.java +++ b/src/main/java/rocks/inspectit/gepard/agent/notify/http/HttpClientHolder.java @@ -1,8 +1,10 @@ package rocks.inspectit.gepard.agent.notify.http; +import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; -import java.security.cert.X509Certificate; +import java.security.KeyStore; +import java.util.Objects; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.config.RequestConfig; @@ -13,7 +15,6 @@ import org.apache.hc.client5.http.ssl.*; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.ssl.SSLContexts; -import org.apache.hc.core5.ssl.TrustStrategy; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,15 +76,20 @@ private static RequestConfig getRequestConfig() { */ private static AsyncClientConnectionManager getConnectionManager() throws GeneralSecurityException, IOException { - TrustStrategy trustStrategy = - (chain, authType) -> { - final X509Certificate certificate = chain[0]; - // Later the CN should be configurable - return "CN=localhost".equalsIgnoreCase(certificate.getSubjectX500Principal().getName()); - }; - SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStrategy).build(); - HostnameVerifier hostnameVerifier = HttpsSupport.getDefaultHostnameVerifier(); + String keystorePath = System.getProperty("javax.net.ssl.trustStore"); + String keystorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); + SSLContext sslContext; + + if (Objects.nonNull(keystorePath) || Objects.nonNull(keystorePassword)) { + KeyStore keyStore = + KeyStore.getInstance(new File(keystorePath), keystorePassword.toCharArray()); + sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, null).build(); + } else { + log.info("No keystore found. Using default SSL context"); + sslContext = SSLContexts.custom().loadTrustMaterial(TrustAllStrategy.INSTANCE).build(); + } + HostnameVerifier hostnameVerifier = HttpsSupport.getDefaultHostnameVerifier(); TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create() .setSslContext(sslContext) diff --git a/src/main/java/rocks/inspectit/gepard/agent/notify/http/exception/ClientConfigurationException.java b/src/main/java/rocks/inspectit/gepard/agent/notify/http/exception/ClientConfigurationException.java index f392f77..c21ce75 100644 --- a/src/main/java/rocks/inspectit/gepard/agent/notify/http/exception/ClientConfigurationException.java +++ b/src/main/java/rocks/inspectit/gepard/agent/notify/http/exception/ClientConfigurationException.java @@ -1,11 +1,9 @@ package rocks.inspectit.gepard.agent.notify.http.exception; -/** - * Exception, for errors in the configuration of the HTTP client - */ +/** Exception, for errors in the configuration of the HTTP client */ public class ClientConfigurationException extends RuntimeException { - public ClientConfigurationException(String message, Throwable cause) { - super(message, cause); - } + public ClientConfigurationException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/test/java/rocks/inspectit/gepard/agent/config/ConfigurationResolverTest.java b/src/test/java/rocks/inspectit/gepard/agent/config/ConfigurationResolverTest.java new file mode 100644 index 0000000..db3a6b0 --- /dev/null +++ b/src/test/java/rocks/inspectit/gepard/agent/config/ConfigurationResolverTest.java @@ -0,0 +1,60 @@ +package rocks.inspectit.gepard.agent.config; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties; +import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ConfigurationResolverTest { + + private static final String SERVER_URL_SYSTEM_PROPERTY = "inspectit.config.http.url"; + + private static final String SERVER_URL_ENV_PROPERTY = "INSPECTIT_CONFIG_HTTP_URL"; + + private static final String TEST_URL = "https://inspectit.rocks/"; + + @Test + void resolverReturnsUrlIfSystemPropertyExists() throws Exception { + restoreSystemProperties( + () -> { + System.setProperty(SERVER_URL_SYSTEM_PROPERTY, TEST_URL); + + String url = ConfigurationResolver.getServerUrl(); + + assertEquals(TEST_URL, url); + }); + } + + @Test + void resolverReturnsUrlIfEnvironmentPropertyExists() throws Exception { + String url = + withEnvironmentVariable(SERVER_URL_ENV_PROPERTY, TEST_URL) + .execute(ConfigurationResolver::getServerUrl); + + assertEquals(TEST_URL, url); + } + + @Test + void resolverReturnsSystemPropertyIfSystemAndEnvPropertyExist() throws Exception { + String envTestUrl = TEST_URL + "1"; + restoreSystemProperties( + () -> { + System.setProperty(SERVER_URL_SYSTEM_PROPERTY, TEST_URL); + + String url = + withEnvironmentVariable(SERVER_URL_ENV_PROPERTY, envTestUrl) + .execute(ConfigurationResolver::getServerUrl); + + assertEquals(TEST_URL, url); + }); + } + + @Test + void resolverReturnsEmptyStringIfNoPropertyExists() { + String url = ConfigurationResolver.getServerUrl(); + + assertTrue(url.isEmpty()); + } +}