greenmail-mail-test / greenmail

Official master for the Greenmail project
http://greenmail-mail-test.github.io/greenmail/
Apache License 2.0
640 stars 184 forks source link

Is the Docker image supposed to work out-of-the-box with SSL/TLS? #448

Open CrispinStichart opened 2 years ago

CrispinStichart commented 2 years ago

According to Greenmail's website's FAQ, Greenmail should work out-of-the box with SSL/TLS. However, since it makes mention of Java's keystore, I'm wondering if the same applies to the stand-alone docker image.

I'm trying to set up integration testing for a simple email client I'm writing in Rust (with rust-imap). Attempting to connect to greenmail gives me an error (on my client side) about an untrusted root certificate.

I don't know much about security stuff, so I've spent the last five-plus hours trying to figure out what's going on.

From what I've learned, it seems like it's not expected to work out-of-the-box when doing integration testing, since the connection has to go through the OS, and the OS will reject any untrusted certificates.

So next I attempted to create my own greenmail.p12, add it as a "Trusted Root Certificate Authority", and insert it into the java classpath when running the docker image. I must be doing something wrong, because it's not using mine. I'm running this command:

docker run -v greenmail.p12:/jks/ -t -i -e JAVA_OPTS='-classpath /jks/' -e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose' -p 3025:3025 -p 3110:3110 -p 3143:3143 -p 3465:3465 -p 3993:3993 -p 3995:3995 greenmail/standalone:1.6.7

Next I tried extracting the greenmail.p12 keystore that ships with the 1.6.7 JAR and adding that as a Trusted Root Certificate Authority, and I got a bit further: now I'm getting an error about the CN not matching.

Looking into this error, it seems like it's because the FQDM of the certificate needs to match the domain I'm connecting to, which in this case is localhost.

Before I spend more hours on this, I just want to ask: am I doing something dumb? Is this supposed to be easy? The fact that the rust-imap tests take the route of configuring a TLS connector to accept invalid certificates doesn't fill me with hope. And I mean yeah, I could do that in this case, but I'm also trying to learn something here, especially in case I ever want to use greenmail in a different context where I can't do that.

At the very least, are there any good examples of setting up and using greenmail with integration tests outside of Java? Everything I can find on google is Java-related.

marcelmay commented 2 years ago

I agree the out-of-the-box might not cover the docker image, unfortunately, due to the exposed IP.

Regarding adding you own certificate:

Relates to #284 .

CrispinStichart commented 2 years ago

For GreenMail 1.6.x, you need to actually add it to the JAR in the container image (so that it is found first in the class path, before the shipped default greenmail.p12). Not very user friendly.

Interesting. I don't know much about Java, but I was under the impression that you could override resources by putting their location in the classpath. In fact, this is what the comment in DummySSLServerSocketFactory.java says:

GreenMail provides the keystore resource. For customization, place your greenmail.p12 before greenmail JAR in the classpath.

Perhaps I'm misreading that, and/or Java classpath/resource loading doesn't work like I think it does.

Looking at your journey, the provided/shipped greenmail.p12 should be provided by default parallel to the greenmail-standalone.jar and configured by default (easier to override via eg COPY or bind-mount).

Yeah, that'd be nice.

Anyway, I finally got it working, with the following steps, listed here for the benefit of anyone else struggling with a similar problem:


1: Create a certificate and key:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
 -keyout greenmail.key -out greenmail.crt -subj "/CN=localhost" \
 -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

The subjectAltName is the critical bit; a lot of places on the internet imply that it'll work as long as the CN is set to localhost. However, support for that has been apparently deprecated for almost two decades. Had to learn that from a random post on the python bug tracker. 🙄

2: Generate the .p12 keystore:

 openssl pkcs12 -export -out greenmail.p12 -inkey greenmail.key -in greenmail.crt

When it asks for a password, use changeit.

3: Add certificate as a Trusted Root Certificate Authority

In Linux:

sudo cp greenmail.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

In Windows, use the graphical Certificate Manager (just search "certificate" and it should come up).

4: Add the keystore to the JAR

Get the greenmail-standalone.jar file, either from the Docker image (not sure how, but I think it can be done) or just downloading it from maven.

Add the keystore: jar uvf greenmail-standalone.jar greenmail.p12 (or just open the jar in 7zip or whatever).

5: Bind mount the JAR when running docker:

docker run -v <full path to greenmail-standalone.jar>:/home/greenmail/greenmail-standalone.jar -t -i -e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -D-Dgreenmail.verbose' -p 3025:3025 -p 3110:3110 -p 3143:3143 -p 3465:3465 -p 3993:3993 -p 3995:3995 greenmail/standalone:1.6.7

Note that I had to use the full path, not relative, of the local JAR file to get it working.

After all that, I can successfully establish a secure connection with the greenmail server.