[MRESOLVER-328] SSL insecure mode (#255)
The transport-http now has "insecure" HTTPS mode that simply ignores any kind of SSL validation error (trust, certificate dates, hostnames). This mode is NOT MEANT for production, as it is inherently insecure but may come handy in small shops using self signed certificates. As mode value is string, we can later improve by adding flags, like ignore-hostname-validation, ignore-cert-dates etc.
---
https://issues.apache.org/jira/browse/MRESOLVER-328
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java
index 5c9095b..0cfa9f4 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java
@@ -160,6 +160,30 @@
public static final boolean DEFAULT_HTTP_PREEMPTIVE_AUTH = false;
/**
+ * The mode that sets HTTPS transport "security mode": to ignore any SSL errors (certificate validity checks,
+ * hostname verification). The default value is {@link #HTTPS_SECURITY_MODE_DEFAULT}.
+ *
+ * @see #HTTPS_SECURITY_MODE_DEFAULT
+ * @see #HTTPS_SECURITY_MODE_INSECURE
+ * @since 1.9.6
+ */
+ public static final String HTTPS_SECURITY_MODE = PREFIX_CONNECTOR + "https.securityMode";
+
+ /**
+ * The default HTTPS security mode.
+ *
+ * @since 1.9.6
+ */
+ public static final String HTTPS_SECURITY_MODE_DEFAULT = "default";
+
+ /**
+ * The insecure HTTPS security mode (certificate validation, hostname verification are all ignored).
+ *
+ * @since 1.9.6
+ */
+ public static final String HTTPS_SECURITY_MODE_INSECURE = "insecure";
+
+ /**
* A flag indicating whether checksums which are retrieved during checksum validation should be persisted in the
* local filesystem next to the file they provide the checksum for.
*
diff --git a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
index 4d925aa..1f6d778 100644
--- a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
+++ b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
@@ -33,8 +33,12 @@
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLInitializationException;
+import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.RepositoryCache;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.util.ConfigUtils;
@@ -154,19 +158,42 @@
if (sslConfig == null) {
registryBuilder.register("https", SSLConnectionSocketFactory.getSystemSocketFactory());
} else {
- SSLSocketFactory sslSocketFactory = (sslConfig.context != null)
- ? sslConfig.context.getSocketFactory()
- : (SSLSocketFactory) SSLSocketFactory.getDefault();
-
- HostnameVerifier hostnameVerifier = (sslConfig.verifier != null)
- ? sslConfig.verifier
- : SSLConnectionSocketFactory.getDefaultHostnameVerifier();
+ // config present: use provided, if any, or create (depending on httpsSecurityMode)
+ SSLSocketFactory sslSocketFactory = sslConfig.context != null ? sslConfig.context.getSocketFactory() : null;
+ HostnameVerifier hostnameVerifier = sslConfig.verifier;
+ if (ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(sslConfig.httpsSecurityMode)) {
+ if (sslSocketFactory == null) {
+ sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ }
+ if (hostnameVerifier == null) {
+ hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
+ }
+ } else if (ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(sslConfig.httpsSecurityMode)) {
+ if (sslSocketFactory == null) {
+ try {
+ sslSocketFactory = new SSLContextBuilder()
+ .loadTrustMaterial(null, (chain, auth) -> true)
+ .build()
+ .getSocketFactory();
+ } catch (Exception e) {
+ throw new SSLInitializationException(
+ "Could not configure '" + sslConfig.httpsSecurityMode + "' HTTPS security mode", e);
+ }
+ }
+ if (hostnameVerifier == null) {
+ hostnameVerifier = NoopHostnameVerifier.INSTANCE;
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported '" + sslConfig.httpsSecurityMode + "' HTTPS security mode.");
+ }
registryBuilder.register(
"https",
new SSLConnectionSocketFactory(
sslSocketFactory, sslConfig.protocols, sslConfig.cipherSuites, hostnameVerifier));
}
+
PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(registryBuilder.build());
connMgr.setMaxTotal(100);
connMgr.setDefaultMaxPerRoute(50);
diff --git a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
index 59bbfc0..3e370d1 100644
--- a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
+++ b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
@@ -146,7 +146,12 @@
this.repoAuthContext = AuthenticationContext.forRepository(session, repository);
this.proxyAuthContext = AuthenticationContext.forProxy(session, repository);
- this.state = new LocalState(session, repository, new SslConfig(session, repoAuthContext));
+ String httpsSecurityMode = ConfigUtils.getString(
+ session,
+ ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
+ ConfigurationProperties.HTTPS_SECURITY_MODE + "." + repository.getId(),
+ ConfigurationProperties.HTTPS_SECURITY_MODE);
+ this.state = new LocalState(session, repository, new SslConfig(session, repoAuthContext, httpsSecurityMode));
this.headers = ConfigUtils.getMap(
session,
diff --git a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SslConfig.java b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SslConfig.java
index b08e393..33f0461 100644
--- a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SslConfig.java
+++ b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SslConfig.java
@@ -45,7 +45,9 @@
final String[] protocols;
- SslConfig(RepositorySystemSession session, AuthenticationContext authContext) {
+ final String httpsSecurityMode;
+
+ SslConfig(RepositorySystemSession session, AuthenticationContext authContext, String httpsSecurityMode) {
context = (authContext != null) ? authContext.get(AuthenticationContext.SSL_CONTEXT, SSLContext.class) : null;
verifier = (authContext != null)
? authContext.get(AuthenticationContext.SSL_HOSTNAME_VERIFIER, HostnameVerifier.class)
@@ -53,6 +55,7 @@
cipherSuites = split(get(session, CIPHER_SUITES));
protocols = split(get(session, PROTOCOLS));
+ this.httpsSecurityMode = httpsSecurityMode;
}
private static String get(RepositorySystemSession session, String key) {
diff --git a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java
index 3834096..0c46e31 100644
--- a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java
+++ b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java
@@ -136,13 +136,27 @@
}
public HttpServer addSslConnector() {
+ return addSslConnector(true);
+ }
+
+ public HttpServer addSelfSignedSslConnector() {
+ return addSslConnector(false);
+ }
+
+ private HttpServer addSslConnector(boolean needClientAuth) {
if (httpsConnector == null) {
SslContextFactory.Server ssl = new SslContextFactory.Server();
- ssl.setNeedClientAuth(true);
- ssl.setKeyStorePath(new File("src/test/resources/ssl/server-store").getAbsolutePath());
- ssl.setKeyStorePassword("server-pwd");
- ssl.setTrustStorePath(new File("src/test/resources/ssl/client-store").getAbsolutePath());
- ssl.setTrustStorePassword("client-pwd");
+ if (needClientAuth) {
+ ssl.setNeedClientAuth(true);
+ ssl.setKeyStorePath(new File("src/test/resources/ssl/server-store").getAbsolutePath());
+ ssl.setKeyStorePassword("server-pwd");
+ ssl.setTrustStorePath(new File("src/test/resources/ssl/client-store").getAbsolutePath());
+ ssl.setTrustStorePassword("client-pwd");
+ } else {
+ ssl.setNeedClientAuth(false);
+ ssl.setKeyStorePath(new File("src/test/resources/ssl/server-store-selfsigned").getAbsolutePath());
+ ssl.setKeyStorePassword("server-pwd");
+ }
httpsConnector = new ServerConnector(server, ssl);
server.addConnector(httpsConnector);
try {
diff --git a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
index db00dc6..752640a 100644
--- a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
+++ b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
@@ -396,6 +396,37 @@
}
@Test
+ public void testGet_HTTPS_Unknown_SecurityMode() throws Exception {
+ session.setConfigProperty("aether.connector.https.securityMode", "unknown");
+ httpServer.addSelfSignedSslConnector();
+ try {
+ newTransporter(httpServer.getHttpsUrl());
+ fail("Unsupported security mode");
+ } catch (IllegalArgumentException a) {
+ // good
+ }
+ }
+
+ @Test
+ public void testGet_HTTPS_Insecure_SecurityMode() throws Exception {
+ // here we use alternate server-store-selfigned key (as the key set it static initalizer is probably already
+ // used to init SSLContext/SSLSocketFactory/etc
+ session.setConfigProperty(
+ "aether.connector.https.securityMode", ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE);
+ httpServer.addSelfSignedSslConnector();
+ newTransporter(httpServer.getHttpsUrl());
+ RecordingTransportListener listener = new RecordingTransportListener();
+ GetTask task = new GetTask(URI.create("repo/file.txt")).setListener(listener);
+ transporter.get(task);
+ assertEquals("test", task.getDataString());
+ assertEquals(0L, listener.dataOffset);
+ assertEquals(4L, listener.dataLength);
+ assertEquals(1, listener.startedCount);
+ assertTrue("Count: " + listener.progressedCount, listener.progressedCount > 0);
+ assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
+ }
+
+ @Test
public void testGet_WebDav() throws Exception {
httpServer.setWebDav(true);
RecordingTransportListener listener = new RecordingTransportListener();
diff --git a/maven-resolver-transport-http/src/test/resources/ssl/server-store-selfsigned b/maven-resolver-transport-http/src/test/resources/ssl/server-store-selfsigned
new file mode 100644
index 0000000..caf6e52
--- /dev/null
+++ b/maven-resolver-transport-http/src/test/resources/ssl/server-store-selfsigned
Binary files differ
diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md
index 649cc3c..615b712 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -40,6 +40,7 @@
`aether.connector.http.preemptiveAuth` | boolean | Should HTTP client use preemptive-authentication (works only w/ BASIC) or not. | `false` | yes
`aether.connector.http.retryHandler.count` | int | The maximum number of times a request to a remote HTTP server should be retried in case of an error. | `3` | yes
`aether.connector.https.cipherSuites` | String | Comma-separated list of [Cipher Suites](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#ciphersuites) which are enabled for HTTPS connections. | - (no restriction) | no
+`aether.connector.https.securityMode` | String | Using this flag resolver may set the "security mode" of HTTPS connector. Any other mode than 'default' is NOT MEANT for production, as it is inherently not secure. Accepted values: "default", "insecure" (ignore any kind of certificate validation errors and hostname validation checks). | `"default"` | yes
`aether.connector.https.protocols` | String | Comma-separated list of [Protocols](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames) which are enabled for HTTPS connections. | - (no restriction) | no
`aether.connector.perms.fileMode` | String | [Octal numerical notation of permissions](https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation) to set for newly created files. Only considered by certain Wagon providers. | - | no
`aether.connector.perms.dirMode` | String | [Octal numerical notation of permissions](https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation) to set for newly created directories. Only considered by certain Wagon providers. | - | no