530 Must issue a STARTTLS Command First

I have an issue sending emails from my production server.

I do not use server-wide settings and pass the server, port, useTLS, userame, and password in the following code:

var msg = new mail(argumentCollection=args);

I have verified that useTLS is set to true, however, when I send a message, I get the following error:

image

Stack info:

I saw this was reported about 5 years ago but none of the proposed solutions pertain to my situation.

I am able to send emails without issue from my local machine running the same code on the same Version of Lucee.

Local stack:

I should note that both of these instances are launched using CommandBox version 5.4.2+00453

You need to install a working cert for your “SERVER” or java instance

STORE=/path/to/JRE/cacerts
keytool -import -trustcacerts -keystore $STORE -storepass changeit -noprompt -file mymailserver.pem -alias mymailserve

I am not sure I understand why that is necessary (or where the files referenced woudl come from).

I did not have to do this locally, nor did I have to do it on the old production machine.

Everything I see online says that error happens when you try to make a non-secure connection to a server which requires TLS. I see you said you confirmed the useTLS flag was true. Out of curiosity, what if you just set up a very simple hard-coded CFMail example on a page? Does that still have the same error? Also, perhaps you can run a packet sniffer to prove if the connection is using TLS.

Running this code on a test page on the production server produces the same error.

Running this same exact code locally works as expected.

I am not sure I feel comfortable running a port sniffer on my production server.

I should also note that the mail server config information is the same on production and my local machine.

Thanks for running the standalone test. I’m afraid I’m out of ideas for the moment. The screenshots in your original post show your local and prod server are basically the exact same. So unless there’s some sort of network routing difference, I can’t image what would be different. And just to confirm-- your local and production examples are connecting to the exact same remote SMTP server?

Same SMTP server using the same credentials.

I would think routing would be simpler because the SMTP server is part of Oracle Cloud Infrastructure (OCI) and the server is an OCI Compute instance.

A friend at Oracle suggested maybe it’s an issue when the Arm architecture. I do not know enough to know if that is even feasible

Maybe try the below code. Many have disabled TLSv1 and TLSv1.1.

<cfscript>
javaSystem = createObject("java", "javax.net.ssl.SSLContext");
currentTLS = javaSystem.getDefault().getSupportedSSLParameters().getProtocols();
dump(currentTLS);
</cfscript>

Here is the result of the dump
image

If it helps, here is the full stack trace

lucee.runtime.exp.NativeException: 530 Must issue a STARTTLS command first
 
  at com.sun.mail.smtp.SMTPTransport.issueCommand(SMTPTransport.java:2324)
  at com.sun.mail.smtp.SMTPTransport.helo(SMTPTransport.java:1678)
  at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:746)
  at javax.mail.Service.connect(Service.java:366)
  at javax.mail.Service.connect(Service.java:246)
  at javax.mail.Service.connect(Service.java:195)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:566)
  at lucee.runtime.reflection.pairs.MethodInstance.invoke(MethodInstance.java:56)
  at lucee.runtime.reflection.Reflector.callMethod(Reflector.java:877)
  at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:831)
  at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1747)
  at scratch.test_cfm$cf.call(/scratch/test.cfm:48)
  at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:1034)
  at lucee.runtime.PageContextImpl._doInclude(PageContextImpl.java:926)
  at lucee.runtime.listener.ModernAppListener._onRequest(ModernAppListener.java:217)
  at lucee.runtime.listener.MixedAppListener.onRequest(MixedAppListener.java:44)
  at lucee.runtime.PageContextImpl.execute(PageContextImpl.java:2460)
  at lucee.runtime.PageContextImpl._execute(PageContextImpl.java:2450)
  at lucee.runtime.PageContextImpl.executeCFML(PageContextImpl.java:2421)
  at lucee.runtime.engine.Request.exe(Request.java:45)
  at lucee.runtime.engine.CFMLEngineImpl._service(CFMLEngineImpl.java:1179)
  at lucee.runtime.engine.CFMLEngineImpl.serviceCFML(CFMLEngineImpl.java:1125)
  at lucee.loader.engine.CFMLEngineWrapper.serviceCFML(CFMLEngineWrapper.java:102)
  at lucee.loader.servlet.CFMLServlet.service(CFMLServlet.java:51)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
  at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
  at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
  at org.cfmlprojects.regexpathinfofilter.RegexPathInfoFilter.doFilter(RegexPathInfoFilter.java:47)
  at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
  at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
  at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176)
  at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
  at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
  at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:405)
  at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
  at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
  at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
  at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
  at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
  at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
  at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
  at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
  at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
  at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
  at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
  at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
  at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
  at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
  at runwar.Server$1.handleRequest(Server.java:510)
  at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
  at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
  at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:280)
  at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:79)
  at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:134)
  at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:131)
  at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
  at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
  at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:260)
  at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:79)
  at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:100)
  at io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
  at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:852)
  at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
  at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)
  at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)
  at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449)
  at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
  at java.base/java.lang.Thread.run(Thread.java:834)
 Caused by: javax.mail.MessagingException: 530 Must issue a STARTTLS command first

How about

<cfscript>
javaSystem = createObject("java", "javax.net.ssl.SSLSocketFactory");
factory = javaSystem.getDefault();
soc = factory.createSocket();
currentTLS = soc.getEnabledProtocols();
dump(currentTLS);
</cfscript>

Your java instance needs the TLS keystore so it can communicate effectively with your upstream server. Without the certificate in the keystore, you’ll get errors.

using openssl you can do the following to download the certificate

echo -n | openssl s_client -connect $HOST:$PORTNUMBER -servername $SERVERNAME \
    | openssl x509 > /tmp/$SERVERNAME.cert

then you would import it to your jvm’s keystore

STORE=/path/to/JRE/cacerts
keytool -import -trustcacerts -keystore $STORE -storepass changeit -noprompt -file mymailserver.pem -alias mymailserver

if you are not sure of where your java is installed
on windows
echo %path%

on *NIX
env | more

As for why this all of the suddenly happened

Microsoft last year stated they where doing away with startTLS 1.0. Many other vendors have followed their recommendation as well as many sell their services which ride ontop of azure.

Isn’t it just possible that this is some sort of gmail “security setting”… that gmail is just allowing the connection from your dev machine, maybe because you have a general access to the same gmail account from your actual local machines IP (which would then be whitelisted)? I’ve read somewhere, that some users experience this error SMTP 530, if they haven’t set their account to allow “insecure apps to send emails”? I’d quickly check and try that setting and test it. Email providers have very strickt and unpredictable rules and sometime odd SMTP return codes. One strong signal maybe your IP address. You are probaly using your local IP from an whitelisted IP, while your prod machine may be comming from a cloud that google may target differently because of spam prevention.

https://support.google.com/accounts/answer/6010255

And also see this SO answer:

Here is the dump of this script
image

I am not trying to connect to Gmail.

I have never done this on my local machine, and I am able to send emails from there without issue using the same version of Java and Lucee.

Proof I need more coffee…

Try

useTLS=“yes”

No dice. Same issue.

First off,

you do not need the full email address for username for gmail, so change that to just the begining part of the email.

second login to your gmail account, you should see multiple notifications that state

Less secure app blocked

Google blocked the app you were trying to use because it doesn’t meet our security standards.

Some apps and devices use less secure sign-in technology, which makes your account more vulnerable. You can turn off access for these apps, which we recommend, or turn on access if you want to use them despite the risks. Google will automatically turn this setting OFF if it’s not being used.

Learn more