Details
-
Improvement
-
Status: Closed
-
Major
-
Resolution: Fixed
-
None
-
None
-
None
Description
Description
As an administrator I wish to secure my outgoing emails using SSL (SMTPS on port 465), and default to SMTP on port 25 (where STARTTLS can be used opportunistically).
This need is a recurring one for Gitter users (often asked, known for years to be buggy).
Setting up test James servers
I did write a simple docker-compose.yml file to experiment this:
version: '3'
services:
james1:
image: apache/james:memory-latest
container_name: james1
hostname: james1
volumes:
- $PWD/keystore:/root/conf/keystore
james2:
image: apache/james:memory-latest
container_name: james2
hostname: james2
volumes:
- $PWD/keystore:/root/conf/keystore
I do embed two RcptHooks to diagnose where remoteDelivery connects on james2:
package org.apache.james; import org.apache.james.core.MailAddress; import org.apache.james.core.MaybeSender; import org.apache.james.protocols.smtp.SMTPSession; import org.apache.james.protocols.smtp.hook.HookResult; import org.apache.james.protocols.smtp.hook.RcptHook; public class SoutRcptHook implements RcptHook { @Override public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt) { System.out.println(" <---> SSL activated: " + sender.asPrettyString() + " sends a message securely to " + rcpt.asString()); return HookResult.DECLINED; }
And
package org.apache.james; import org.apache.james.core.MailAddress; import org.apache.james.core.MaybeSender; import org.apache.james.protocols.smtp.SMTPSession; import org.apache.james.protocols.smtp.hook.HookResult; import org.apache.james.protocols.smtp.hook.RcptHook; public class NoSSLRcptHook implements RcptHook { @Override public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt) { System.out.println(" <---> NO SSL: " + sender.asPrettyString() + " sends a message securely to " + rcpt.asString()); return HookResult.DECLINED; } }
Which then ellows configuring the SMTP server:
<smtpservers> <smtpserver enabled="true"> <jmxName>smtpserver-global</jmxName> <bind>0.0.0.0:25</bind> <tls socketTLS="false" startTLS="true"> <keystore>file://conf/keystore</keystore> <secret>james72laBalle</secret> <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> <algorithm>SunX509</algorithm> </tls> <smtpGreeting>Apache JAMES awesome SMTP Server</smtpGreeting> <handlerchain> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> <handler class="org.apache.james.NoSSLRcptHook"/> </handlerchain> </smtpserver> <smtpserver enabled="true"> <jmxName>smtpserver-TLS</jmxName> <bind>0.0.0.0:465</bind> <tls socketTLS="true" startTLS="false"> <keystore>file://conf/keystore</keystore> <secret>james72laBalle</secret> <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> <algorithm>SunX509</algorithm> </tls> <smtpGreeting>Apache JAMES awesome SMTP Server</smtpGreeting> <handlerchain> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> <handler class="org.apache.james.SoutRcptHook"/> </handlerchain> </smtpserver> </smtpservers>
We then can review logs to know which port was eventually used to receive emails (smtp logs also tells us if STARTTLS is used).
I expect `sslEnable` parameter to govern this opportunistic SSL connection. Thus RemoteDelivery configuration looks like this:
<processor state="relay" enableJmx="true"> <mailet match="All" class="RemoteDelivery"> <outgoingQueue>outgoing</outgoingQueue> <bounceProcessor>bounces</bounceProcessor> <debug>true</debug> <mail.smtp.ssl.trust>*</mail.smtp.ssl.trust> <sslEnable>true</sslEnable> <startTLS>true</startTLS> </mailet> </processor>
We then can create some test data and send a mail from james1 to james2:
docker-compose up -d docker exec james1 james-cli adddomain james1 docker exec james2 james-cli adddomain james2 docker exec james1 james-cli adduser bob@james1 123456 docker exec james2 james-cli adduser bob@james2 123456 docker inspect james1 telnet [james1] 25 auth login Ym9iQGphbWVzMQ== MTIzNDU2 ehlo james1 mail from: <bob@james1> rcpt to: <bob@james2> data Subject: rueooerwbwerb veriobwerobwerbr rwebeberber .
Will generate james1 to remote-deliver a mail to james2, and we can in the process diagnose the remote delivery behaviour between the two.
Actual behaviour
james1 attepts an SSL connection on port 25:
james1 | 06:56:43.405 [DEBUG] o.a.j.t.m.r.d.MailDelivrer - Could not connect to SMTP host: 172.30.0.2, port: 25 james1 | javax.net.ssl.SSLException: Unsupported or unrecognized SSL message james1 | at java.base/sun.security.ssl.SSLSocketInputRecord.handleUnknownRecord(SSLSocketInputRecord.java:451) james1 | at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:175) james1 | at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:110) james1 | at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1418) james1 | at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1324) james1 | at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440) james1 | at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:411) james1 | at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:626) james1 | at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:400) james1 | at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:238) james1 | at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2175) james1 | at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:740)
And james2 do not recognizes the commands:
james2 | 06:56:43.410 [DEBUG] o.a.j.p.a.h.CommandDispatcher - org.apache.james.protocols.api.handler.CommandDispatcher received: ZVVF�������E�F�8T�E'F��LO��#��� james2 | 06:56:43.411 [DEBUG] o.a.j.p.a.h.CommandDispatcher - Lookup command handler for command: ZVVF�������E�F�8T�E'F��LO��#��� james2 | 06:56:43.437 [DEBUG] o.a.j.p.a.h.CommandHandlerResultLogger - org.apache.james.protocols.smtp.core.UnknownCmdHandler: [500 5.5.1 Command ZVVF�������E�F�8T�E'F��LO��#��� unrecognized.] james2 | 06:56:43.441 [DEBUG] o.a.j.p.a.h.CommandDispatcher - org.apache.james.protocols.api.handler.CommandDispatcher received: �5��98�#�'<�%�)G@� �/��32�� james2 | 06:56:43.442 [DEBUG] o.a.j.p.a.h.CommandDispatcher - Lookup command handler for command: �5��98�#�'<�%�)G@� �/��32�� james2 | 06:56:43.442 [DEBUG] o.a.j.p.a.h.CommandHandlerResultLogger - org.apache.james.protocols.smtp.core.UnknownCmdHandler: [500 5.5.1 Command �5��98�#�'<�%�)G@� �/��32�� unrecognized.] james2 | 06:56:43.443 [DEBUG] o.a.j.p.a.h.CommandDispatcher - org.apache.james.protocols.api.handler.CommandDispatcher received: ,* james2 | 06:56:43.443 [DEBUG] o.a.j.p.a.h.CommandDispatcher - Lookup command handler for command: ,*
Expected behaviour
We expect the mail to be sent over SSL.
We expect that if the target host do not support SSL on port 465 that we fallback to SMTP on port 25, and use STARTTLS.
Interesting notice
- MXHostAddressIterator is adding the protocol (smtp VS smtps) and the port (25 VS 465). While setting these values according to SMTPS & 465 SSL is correctly performed.
The management of the fallback behavior however requires a fallback host address to be generated (SMTP + 25 after the SMTPS one), and also requires handling down the line in MailDelivrerToHost (use of two sessions, one for SMTP, one for SMTPS).
- The use of self signed certificates for the distant server when using remoteDelivery :
<jvmFlag>-Djavax.net.ssl.trustStore=/root/conf/keystore</jvmFlag>
- Fallback can be tested by disactivating the SSL SMTP port:
<smtpservers> <smtpserver enabled="true"> <jmxName>smtpserver-global</jmxName> <bind>0.0.0.0:25</bind> <tls socketTLS="false" startTLS="true"> <keystore>file://conf/keystore</keystore> <secret>james72laBalle</secret> <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> <algorithm>SunX509</algorithm> </tls> <smtpGreeting>Apache JAMES awesome SMTP Server</smtpGreeting> <handlerchain> <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> <handler class="org.apache.james.NoSSLRcptHook"/> </handlerchain> </smtpserver> <smtpserver enabled="false"> <jmxName>smtpserver-TLS</jmxName> <bind>0.0.0.0:465</bind> <tls socketTLS="true" startTLS="false"> <keystore>file://conf/keystore</keystore> <secret>james72laBalle</secret> <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> <algorithm>SunX509</algorithm> </tls> <smtpGreeting>Apache JAMES awesome SMTP Server</smtpGreeting> <handlerchain> <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> <handler class="org.apache.james.SoutRcptHook"/> </handlerchain> </smtpserver> </smtpservers>
Attachments
Issue Links
- links to