STARTTLS Server SMTP with cleartext and STARTTLS client support
Problem this snippet solves: We were looking at our O365 security score and our SMTP scores were pretty sad, so I looked at how I could create a STARTTLS connection up to O365 regardless of the clie...
Published May 02, 2019
Version 1.0Sam_Novak
Altostratus
Joined May 20, 2019
Sam_Novak
Altostratus
Joined May 20, 2019
Sam_Novak
Sep 12, 2019Altostratus
For those interested, here is the iRule I use for client (cleartext) -> Big-IP -> O365 (Starttls)
when CLIENT_ACCEPTED {
# No SSL client side, also check no SSL running already on server side
# Serverside debug mode
set DEBUG_SERVER 1
# Clientside debug mode
set DEBUG_CLIENT 1
# Clientside body capturing
set DEBUG_BODY 1
# Variable to track if we've reached the message body
set BODY_CHECK 0
if { $DEBUG_CLIENT } { log local0. "CLIENT_ACCEPTED" }
SSL::disable serverside
SSL::disable
#TCP::collect
}
when SERVER_CONNECTED {
if { $DEBUG_SERVER } { log local0. "SERVER_CONNECTED" }
TCP::collect
}
when CLIENT_DATA {
set lcpayload [string tolower [TCP::payload]]
if { $DEBUG_BODY and $BODY_CHECK }
{
log local0.debug "CLIENT_DATA - [IP::client_addr] - BODY_PAYLOAD - $lcpayload"
set BODY_CHECK 0
}
if { $lcpayload starts_with "data" } { set BODY_CHECK 1 }
#if { [TCP::payload] starts_with "MAIL FROM:" } { set $CFROM [TCP::payload] }
if { $DEBUG_CLIENT and !$BODY_CHECK } { log local0.debug "CLIENT_DATA - [IP::client_addr] - PAYLOAD - $lcpayload" }
TCP::release
}
when SERVER_DATA {
# Read in responses from remote server into a variable and log to /var/log/ltm
if { $DEBUG_SERVER } { log local0. "server payload: [string tolower [TCP::payload]]" }
set payload [string tolower [TCP::payload]]
if {$payload starts_with "220" and $payload contains "esmtp"}
{
# Listen for remote servers opening 220 and esmtp message
# NOTE the ‘if’ statement above may need to be tweaked to except what message the other
# side is actually sending in reply. Logs should show this.
# Respond with a EHLO to server, most servers require a name after the EHLO as well.
TCP::respond "EHLO F5.yourdomain.com\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
if { $DEBUG_SERVER } { log local0. "responded to server with EHLO" }
serverside {TCP::collect}
}
elseif {$payload contains "250-starttls" }
{
# Check server responds with "250-starttls", if so, respond with a STARTTLS
TCP::respond "STARTTLS\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
if { $DEBUG_SERVER } { log local0. "Sent the server a STARTTLS" }
serverside {TCP::collect}
}
elseif {$payload contains "220 ready for tls" or $payload contains "220 2.0.0 continue" or $payload contains "220 2.0.0 smtp server ready" }
{
# if server gives a 220 response, then start server side ssl profile
# NOTE the ‘if’ statement above may need to be tweaked to except what message the other
# side is actually sending in reply. Logs should show this.
# O365 Edit - O365 returns 220 2.0.0 smtp server ready after enabling TLS
if { $DEBUG_SERVER } { log local0. "server said he is ready for TLS, enable the SSL profile" }
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
serverside {SSL::enable}
# TLS hanshake should now start, which is best seen in wireshark packet captures.
}
}
when SERVERSSL_HANDSHAKE {
# This will only trigger if that is completed successfully.
# ServerSSL profile will need a certificate to match the outbound IP and DNS name,
# and you may want to set the "Server certificate" setting to "require",
# and the "Trusted Certificate Authorities" set to "ca-bundle".
if { $DEBUG_SERVER } { log local0. "SSL handshake completed." }
clientside { TCP::respond "220 SMTP ESMTP Relay F5\r\n" }
SSL::collect
}
when SERVERSSL_DATA {
# Log the SMTP responses to see any errors.
if { $DEBUG_SERVER } { log local0. "server SSL payload: [SSL::payload]" }
SSL::release
clientside { TCP::collect }
SSL::collect
}