diff --git a/README.md b/README.md index edf5e797..0ca3f729 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ and how it advertises itself to a Cryostat server instance. Properties that requ - [ ] `cryostat.agent.webclient.tls.version` [`String`]: the version of TLS used for the Agent's client SSL context. Default `TLSv1.2`. - [ ] `cryostat.agent.webclient.tls.trust-all` [`boolean`]: control whether the agent trusts all certificates presented by the Cryostat server. Default `false`. This should only be overridden for development and testing purposes, never in production. - [ ] `cryostat.agent.webclient.tls.verify-hostname` [`boolean`]: control whether the agent verifies hostnames on certificates presented by the Cryostat server. Default `true`. This should only be overridden for development and testing purposes, never in production. +- [ ] `cryostat.agent.webclient.tls.required` [`boolean`]: Specify whether the agent should require TLS on Cryostat server connections, expecting the base URI to be an https connection with a certificate it trusts. Defaults to `true`. Should only be disabled for testing/prototyping purposes. - [ ] `cryostat.agent.webclient.tls.trustore.cert` [`list`]: the list of truststoreConfig objects with alias, path, and type properties for certificates to be stored in the agent's truststore. For example, 'cryostat.agent.webclient.tls.truststore.cert[0].type' would be the type of the first certificate in this list. A truststoreConfig object must contain all three properties to be a valid certificate entry. - [ ] `cryostat.agent.webclient.tls.truststore.type` [`String`]: the type of truststore used for the agent's client truststore. Default `JKS`. - [ ] `cryostat.agent.webclient.tls.truststore.path` [`String`]: the filepath to the agent's webclient truststore. This takes precedence over `cryostat.agent.webclient.tls.truststore.cert` and must be configured with the truststore's pass with `cryostat.agent.webclient.tls.truststore.pass.file` or `cryostat.agent.webclient.tls.truststore.pass`. diff --git a/src/main/java/io/cryostat/agent/ConfigModule.java b/src/main/java/io/cryostat/agent/ConfigModule.java index c09fa4c3..0ae940e3 100644 --- a/src/main/java/io/cryostat/agent/ConfigModule.java +++ b/src/main/java/io/cryostat/agent/ConfigModule.java @@ -137,6 +137,9 @@ public abstract class ConfigModule { public static final String CRYOSTAT_AGENT_WEBCLIENT_RESPONSE_RETRY_COUNT = "cryostat.agent.webclient.response.retry-count"; + public static final String CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED = + "cryostat.agent.webclient.tls.required"; + public static final String CRYOSTAT_AGENT_WEBSERVER_HOST = "cryostat.agent.webserver.host"; public static final String CRYOSTAT_AGENT_WEBSERVER_PORT = "cryostat.agent.webserver.port"; public static final String CRYOSTAT_AGENT_WEBSERVER_TLS_VERSION = @@ -959,6 +962,13 @@ public static long provideCryostatSmartTriggerEvaluationPeriodMs(Config config) return config.getValue(CRYOSTAT_AGENT_SMART_TRIGGER_EVALUATION_PERIOD_MS, long.class); } + @Provides + @Singleton + @Named(CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED) + public static boolean provideCryostatAgentTlsEnabled(Config config) { + return config.getValue(CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED, boolean.class); + } + public enum URIRange { LOOPBACK(u -> check(u, u2 -> true, InetAddress::isLoopbackAddress)), LINK_LOCAL( diff --git a/src/main/java/io/cryostat/agent/MainModule.java b/src/main/java/io/cryostat/agent/MainModule.java index 803bce87..6bd4ea17 100644 --- a/src/main/java/io/cryostat/agent/MainModule.java +++ b/src/main/java/io/cryostat/agent/MainModule.java @@ -86,7 +86,6 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; @@ -236,9 +235,19 @@ public static SSLContext provideClientSslContext( @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_TLS_CLIENT_AUTH_KEY_PASS_CHARSET) String clientAuthKeyPassFileCharset, @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_TLS_CLIENT_AUTH_KEY_MANAGER_TYPE) - String clientAuthKeyManagerType) { + String clientAuthKeyManagerType, + @Named(ConfigModule.CRYOSTAT_AGENT_BASEURI) URI baseUri, + @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED) boolean tlsRequired) { try { KeyManager[] keyManagers = null; + if (tlsRequired && !baseUri.getScheme().equals("https")) { + throw new IllegalArgumentException( + String.format( + "If TLS is enabled via the (%s) property, the base URI (%s)" + + " must be an https connection.", + ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED, + ConfigModule.CRYOSTAT_AGENT_BASEURI)); + } if (clientAuthCertPath.isPresent() && clientAuthKeyPath.isPresent()) { KeyStore ks = KeyStore.getInstance(clientAuthKeystoreType); Optional keystorePass = @@ -610,20 +619,23 @@ public static HttpClient provideHttpClient( boolean verifyHostname, @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_CONNECT_TIMEOUT_MS) int connectTimeout, @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_RESPONSE_TIMEOUT_MS) int responseTimeout, - @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_RESPONSE_RETRY_COUNT) int retryCount) { + @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_RESPONSE_RETRY_COUNT) int retryCount, + @Named(ConfigModule.CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED) boolean tlsRequired) { SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, verifyHostname ? SSLConnectionSocketFactory.getDefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE); - Registry socketFactoryRegistry = + + RegistryBuilder socketFactoryRegistryBuilder = RegistryBuilder.create() - .register("https", sslSocketFactory) - .register("http", new PlainConnectionSocketFactory()) - .build(); + .register("https", sslSocketFactory); + if (!tlsRequired) { + socketFactoryRegistryBuilder.register("http", new PlainConnectionSocketFactory()); + } HttpClientConnectionManager connMan = - new BasicHttpClientConnectionManager(socketFactoryRegistry); + new BasicHttpClientConnectionManager(socketFactoryRegistryBuilder.build()); return HttpClients.custom() .setSSLContext(sslContext) .setSSLSocketFactory(sslSocketFactory) diff --git a/src/main/resources/META-INF/microprofile-config.properties b/src/main/resources/META-INF/microprofile-config.properties index a2a3748b..7fa3a563 100644 --- a/src/main/resources/META-INF/microprofile-config.properties +++ b/src/main/resources/META-INF/microprofile-config.properties @@ -16,6 +16,7 @@ cryostat.agent.webclient.response.retry-count=3 cryostat.agent.webclient.tls.version=TLSv1.2 cryostat.agent.webclient.tls.trust-all=false cryostat.agent.webclient.tls.verify-hostname=true +cryostat.agent.webclient.tls.required=true cryostat.agent.webclient.tls.truststore.type=JKS cryostat.agent.webclient.tls.truststore.pass-charset=utf-8 @@ -82,3 +83,4 @@ cryostat.agent.harvester.max-size-b=0 cryostat.agent.smart-trigger.definitions= cryostat.agent.smart-trigger.evaluation.period-ms=1000 +