This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 8c9fa4657e [ENHANCEMENT] Allow to trust all certificates with S3
blobstore (#1846)
8c9fa4657e is described below
commit 8c9fa4657e3ddbd85013bc6d57fa690b07e3f8fe
Author: Benoit TELLIER <[email protected]>
AuthorDate: Mon Dec 11 07:38:35 2023 +0100
[ENHANCEMENT] Allow to trust all certificates with S3 blobstore (#1846)
---
.../modules/ROOT/pages/configure/blobstore.adoc | 4 ++
.../objectstorage/aws/AwsS3AuthConfiguration.java | 30 ++++++--
.../blob/objectstorage/aws/S3BlobStoreDAO.java | 23 +++++++
.../aws/s3/AwsS3ConfigurationReader.java | 2 +
.../aws/s3/AwsS3ConfigurationReaderTest.java | 79 ++++++++++++++++++++++
src/site/xdoc/server/config-blobstore.xml | 4 ++
6 files changed, 138 insertions(+), 4 deletions(-)
diff --git
a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/blobstore.adoc
b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/blobstore.adoc
index 1b5f267f21..709325a76c 100644
---
a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/blobstore.adoc
+++
b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/blobstore.adoc
@@ -143,6 +143,10 @@ Maximum size of stored objects expressed in bytes.
| objectstorage.s3.truststore.algorithm
| optional: Use this specific trust store algorithm; default SunX509
+| objectstorage.s3.trustall
+| optional: boolean. Defaults to false. Cannot be set to true with other
trustore options. Wether James should validate
+S3 endpoint SSL certificates.
+
| objectstorage.s3.read.timeout
| optional: HTTP read timeout. duration, default value being second. Leaving
it empty relies on S3 driver defaults.
diff --git
a/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/AwsS3AuthConfiguration.java
b/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/AwsS3AuthConfiguration.java
index ecc4014027..70975d73d7 100644
---
a/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/AwsS3AuthConfiguration.java
+++
b/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/AwsS3AuthConfiguration.java
@@ -53,6 +53,7 @@ public class AwsS3AuthConfiguration {
private final URI endpoint;
private final String accessKeyId;
private final String secretKey;
+ private Optional<Boolean> trustAll;
private Optional<String> trustStorePath;
private Optional<String> trustStoreType;
@@ -67,6 +68,7 @@ public class AwsS3AuthConfiguration {
this.trustStoreType = Optional.empty();
this.trustStoreSecret = Optional.empty();
this.trustStoreAlgorithm = Optional.empty();
+ this.trustAll = Optional.empty();
}
public ReadyToBuild trustStorePath(Optional<String>
trustStorePath) {
@@ -87,6 +89,11 @@ public class AwsS3AuthConfiguration {
return trustStoreType(Optional.ofNullable(trustStoreType));
}
+ public ReadyToBuild trustAll(boolean trustAll) {
+ this.trustAll = Optional.of(trustAll);
+ return this;
+ }
+
public ReadyToBuild trustStoreSecret(Optional<String>
trustStoreSecret) {
this.trustStoreSecret = trustStoreSecret;
return this;
@@ -114,8 +121,13 @@ public class AwsS3AuthConfiguration {
Preconditions.checkNotNull(secretKey, "'secretKey' is
mandatory");
Preconditions.checkArgument(!secretKey.isEmpty(), "'secretKey'
is mandatory");
+ boolean trustAll = this.trustAll.orElse(false);
+ Preconditions.checkState(!(trustAll &&
trustStoreType.isPresent()), "Cannot specify 'trustAll' and 'trustStoreType'
simultaneously");
+ Preconditions.checkState(!(trustAll &&
trustStorePath.isPresent()), "Cannot specify 'trustAll' and 'trustStorePath'
simultaneously");
+ Preconditions.checkState(!(trustAll &&
trustStoreSecret.isPresent()), "Cannot specify 'trustAll' and
'trustStoreSecret' simultaneously");
+
return new AwsS3AuthConfiguration(endpoint, accessKeyId,
secretKey,
- trustStorePath, trustStoreType, trustStoreSecret,
trustStoreAlgorithm);
+ trustStorePath, trustStoreType, trustStoreSecret,
trustStoreAlgorithm, trustAll);
}
}
}
@@ -128,6 +140,7 @@ public class AwsS3AuthConfiguration {
private final Optional<String> trustStoreType;
private final Optional<String> trustStoreSecret;
private final Optional<String> trustStoreAlgorithm;
+ private final boolean trustAll;
private AwsS3AuthConfiguration(URI endpoint,
String accessKeyId,
@@ -135,7 +148,8 @@ public class AwsS3AuthConfiguration {
Optional<String> trustStorePath,
Optional<String> trustStoreType,
Optional<String> trustStoreSecret,
- Optional<String> trustStoreAlgorithm) {
+ Optional<String> trustStoreAlgorithm,
+ boolean trustAll) {
this.endpoint = endpoint;
this.accessKeyId = accessKeyId;
this.secretKey = secretKey;
@@ -143,6 +157,7 @@ public class AwsS3AuthConfiguration {
this.trustStoreType = trustStoreType;
this.trustStoreSecret = trustStoreSecret;
this.trustStoreAlgorithm = trustStoreAlgorithm;
+ this.trustAll = trustAll;
}
public URI getEndpoint() {
@@ -173,6 +188,10 @@ public class AwsS3AuthConfiguration {
return trustStoreAlgorithm;
}
+ public boolean isTrustAll() {
+ return trustAll;
+ }
+
@Override
public final boolean equals(Object o) {
if (o instanceof AwsS3AuthConfiguration) {
@@ -183,7 +202,8 @@ public class AwsS3AuthConfiguration {
Objects.equal(trustStorePath, that.trustStorePath) &&
Objects.equal(trustStoreType, that.trustStoreType) &&
Objects.equal(trustStoreSecret, that.trustStoreSecret) &&
- Objects.equal(trustStoreAlgorithm, that.trustStoreAlgorithm);
+ Objects.equal(trustStoreAlgorithm, that.trustStoreAlgorithm) &&
+ Objects.equal(trustAll, that.trustAll);
}
return false;
}
@@ -191,7 +211,8 @@ public class AwsS3AuthConfiguration {
@Override
public final int hashCode() {
return Objects.hashCode(endpoint, accessKeyId, secretKey,
- trustStorePath, trustStoreType, trustStoreSecret,
trustStoreAlgorithm);
+ trustStorePath, trustStoreType, trustStoreSecret,
trustStoreAlgorithm,
+ trustAll);
}
@Override
@@ -203,6 +224,7 @@ public class AwsS3AuthConfiguration {
.add("trustStorePath", trustStorePath)
.add("trustStoreSecret", trustStoreSecret)
.add("trustStoreAlgorithm", trustStoreAlgorithm)
+ .add("trustAll", trustAll)
.toString();
}
}
diff --git
a/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/S3BlobStoreDAO.java
b/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/S3BlobStoreDAO.java
index 725cf8dcd8..b24460176f 100644
---
a/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/S3BlobStoreDAO.java
+++
b/server/blob/blob-s3/src/main/java/org/apache/james/blob/objectstorage/aws/S3BlobStoreDAO.java
@@ -28,6 +28,7 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
+import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
@@ -35,7 +36,9 @@ import java.util.concurrent.CompletableFuture;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
+import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
import org.apache.commons.io.IOUtils;
import org.apache.james.blob.api.BlobId;
@@ -83,6 +86,23 @@ import
software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Object;
public class S3BlobStoreDAO implements BlobStoreDAO, Startable, Closeable {
+ private static final TrustManager DUMMY_TRUST_MANAGER = new
X509TrustManager() {
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String
authType) {
+ // Always trust
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String
authType) {
+ // Always trust
+ }
+ };
+
private static class FileBackedOutputStreamByteSource extends ByteSource {
private final FileBackedOutputStream stream;
private final long size;
@@ -158,6 +178,9 @@ public class S3BlobStoreDAO implements BlobStoreDAO,
Startable, Closeable {
}
private TlsTrustManagersProvider
getTrustManagerProvider(AwsS3AuthConfiguration configuration) {
+ if (configuration.isTrustAll()) {
+ return () -> ImmutableList.of(DUMMY_TRUST_MANAGER).toArray(new
TrustManager[0]);
+ }
try {
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(
configuration.getTrustStoreAlgorithm().orElse(TrustManagerFactory.getDefaultAlgorithm()));
diff --git
a/server/container/guice/blob/s3/src/main/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReader.java
b/server/container/guice/blob/s3/src/main/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReader.java
index 50ba125a59..a7618eee5c 100644
---
a/server/container/guice/blob/s3/src/main/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReader.java
+++
b/server/container/guice/blob/s3/src/main/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReader.java
@@ -34,6 +34,7 @@ public class AwsS3ConfigurationReader {
static final String OBJECTSTORAGE_TRUSTSTORE_TYPE =
"objectstorage.s3.truststore.type";
static final String OBJECTSTORAGE_TRUSTSTORE_SECRET =
"objectstorage.s3.truststore.secret";
static final String OBJECTSTORAGE_TRUSTSTORE_ALGORITHM =
"objectstorage.s3.truststore.algorithm";
+ static final String OBJECTSTORAGE_TRUSTALL = "objectstorage.s3.trustall";
public static AwsS3AuthConfiguration from(Configuration configuration) {
String endpoint = configuration.getString(OBJECTSTORAGE_ENDPOINT);
@@ -49,6 +50,7 @@ public class AwsS3ConfigurationReader {
.trustStoreType(configuration.getString(OBJECTSTORAGE_TRUSTSTORE_TYPE))
.trustStoreSecret(configuration.getString(OBJECTSTORAGE_TRUSTSTORE_SECRET))
.trustStoreAlgorithm(configuration.getString(OBJECTSTORAGE_TRUSTSTORE_ALGORITHM))
+ .trustAll(configuration.getBoolean(OBJECTSTORAGE_TRUSTALL,
false))
.build();
}
}
diff --git
a/server/container/guice/blob/s3/src/test/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReaderTest.java
b/server/container/guice/blob/s3/src/test/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReaderTest.java
index 21edf20283..2d675a86bc 100644
---
a/server/container/guice/blob/s3/src/test/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReaderTest.java
+++
b/server/container/guice/blob/s3/src/test/java/org/apache/james/modules/objectstorage/aws/s3/AwsS3ConfigurationReaderTest.java
@@ -20,6 +20,7 @@
package org.apache.james.modules.objectstorage.aws.s3;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.net.URI;
@@ -89,6 +90,84 @@ class AwsS3ConfigurationReaderTest {
assertThat(authConfiguration).isEqualTo(expected);
}
+
+ @Test
+ void trustAllAndTrustStoreShouldBeIncompatible() {
+ Configuration configuration = new PropertiesConfiguration();
+ URI endpoint = URI.create("http://myEndpoint");
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ENDPOINT,
endpoint);
+ String accessKeyId = "myAccessKeyId";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ACCESKEYID,
accessKeyId);
+ String secretKey = "mySecretKey";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_SECRETKEY,
secretKey);
+ String trustStorePath = "/some/where/truststore.p12";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_PATH,
trustStorePath);
+ String trustStoreType = "PKCS12";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_TYPE,
trustStoreType);
+ String trustStoreSecret = "myTrustStoreSecret";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_SECRET,
trustStoreSecret);
+ String trustStoreAlgorithm = "myTrustStoreAlgorithm";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_ALGORITHM,
trustStoreAlgorithm);
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTALL,
true);
+
+ assertThatThrownBy(() ->
AwsS3ConfigurationReader.from(configuration)).isInstanceOf(IllegalStateException.class);
+ }
+
+
+ @Test
+ void trustNotAllAndTrustStoreShouldBeCompatible() {
+ Configuration configuration = new PropertiesConfiguration();
+ URI endpoint = URI.create("http://myEndpoint");
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ENDPOINT,
endpoint);
+ String accessKeyId = "myAccessKeyId";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ACCESKEYID,
accessKeyId);
+ String secretKey = "mySecretKey";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_SECRETKEY,
secretKey);
+ String trustStorePath = "/some/where/truststore.p12";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_PATH,
trustStorePath);
+ String trustStoreType = "PKCS12";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_TYPE,
trustStoreType);
+ String trustStoreSecret = "myTrustStoreSecret";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_SECRET,
trustStoreSecret);
+ String trustStoreAlgorithm = "myTrustStoreAlgorithm";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTSTORE_ALGORITHM,
trustStoreAlgorithm);
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTALL,
false);
+
+ assertThatCode(() ->
AwsS3ConfigurationReader.from(configuration)).doesNotThrowAnyException();
+ }
+
+ @Test
+ void trustAllShouldBeFalseByDefault() {
+ Configuration configuration = new PropertiesConfiguration();
+ URI endpoint = URI.create("http://myEndpoint");
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ENDPOINT,
endpoint);
+ String accessKeyId = "myAccessKeyId";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ACCESKEYID,
accessKeyId);
+ String secretKey = "mySecretKey";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_SECRETKEY,
secretKey);
+
+ AwsS3AuthConfiguration testee =
AwsS3ConfigurationReader.from(configuration);
+
+ assertThat(testee.isTrustAll()).isFalse();
+ }
+
+
+ @Test
+ void trustAllShouldBeTrueWhenEnabled() {
+ Configuration configuration = new PropertiesConfiguration();
+ URI endpoint = URI.create("http://myEndpoint");
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ENDPOINT,
endpoint);
+ String accessKeyId = "myAccessKeyId";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_ACCESKEYID,
accessKeyId);
+ String secretKey = "mySecretKey";
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_SECRETKEY,
secretKey);
+
configuration.addProperty(AwsS3ConfigurationReader.OBJECTSTORAGE_TRUSTALL,
true);
+
+ AwsS3AuthConfiguration testee =
AwsS3ConfigurationReader.from(configuration);
+
+ assertThat(testee.isTrustAll()).isTrue();
+ }
+
@Test
void fromShouldWorkWithoutOptionals() {
Configuration configuration = new PropertiesConfiguration();
diff --git a/src/site/xdoc/server/config-blobstore.xml
b/src/site/xdoc/server/config-blobstore.xml
index 4ff8da2850..7583ba493e 100644
--- a/src/site/xdoc/server/config-blobstore.xml
+++ b/src/site/xdoc/server/config-blobstore.xml
@@ -184,6 +184,10 @@ generate salt with : openssl rand -hex 16
<dt><strong>objectstorage.s3.truststore.algorithm</strong></dt>
<dd><i>optional:</i> Use this specific trust store
algorithm; default SunX509</dd>
+ <dt><strong>objectstorage.s3.trustall</strong></dt>
+ <dd><i>optional:</i> boolean. Defaults to false.
Cannot be set to true with other trustore options. Wether James should validate
+ S3 endpoint SSL certificates.</dd>
+
<dt><strong>objectstorage.s3.read.timeout</strong></dt>
<dd><i>optional:</i> HTTP read timeout. duration,
default value being second. Leaving it empty relies on S3 driver defaults.</dd>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]