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
Altostratus
Sep 12, 2019For 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
}