FTPS TLS session resumption
We are attempting to update some old FTP interfaces to use FTPS and having an issue with an outbound operation to a FileZilla server.
The connection to the the server works, however it fails to transfer files with the error "TLS session of data connection not resumed."
It appears that "TLS session resumption" is no longer optional in FileZilla Server, and I cannot find any details in the IRIS documentation about it.
Does IRIS support TLS session resumption, or know how to set up FTPS to a recent install of FileZilla Server?
Comments
Hi Elisha,
I happened to have worked on a somehow similar request recently, even if not with the use of the FTP adapter within a production, but with the %Net.FtpSession class directly.
The short answer is probably that we do have support for the "new" SSLUseSessionResumption property, starting with the IRIS 2022.1 release.
The matching documentation in the %Net.FtpSession class:
property SSLUseSessionResumption
Using the 2022.1 release, and the %Net.FtpSession class, you could adapt your code to include an extra set on the matching property, like "set ftp.SSLUseSessionResumption = 1"
In order to use the same property within the FTP adapter, it gets a bit more complex:
the matching change will come into the product with the 2022.2+ releases.
I would suggest that you perform a quick test using the %Net.FtpSession class in a 2022.1 installation, just to confirm that the use of the SSLUseSessionResumption property will allow the code to connect to your specific SFTP server.
Once you have that confirmed, you will probably want to open a WRC Support request to see if the change could be made available into your current release.
HTH
Thank you, I'll have a look at it.
I did open a WRC Support request about it, but still waiting to hear back if it can be added to our current release.
It's good to hear this is going to be added. In the meantime we found using explicit ftps got around it but that may depend on how the ftps server is configured.
For explicit ftps I got a very helpful answer in a previous post
Explicit FTPS in Cache | InterSystems Developer Community | Data Import and
Just as a long outstanding note.
I made an extension to EnsLib.FTP.OutboundAdapter that adds handling for Use Session Resumption as a parameter, but it appears as of the 2024 version of IRIS this has been added to the core IRIS code.
We've only recently been able to use it as we had an old OpenSSL version that did not support the flags required, so this has been left and not looked at in a few years.
For reference the extension I wrote is listed below:
Include EnsembleClass Custom.Operations.FTP.OutboundAdapter Extends EnsLib.FTP.OutboundAdapter [ ClassType = "", Inheritance = right, ProcedureBlock, System = 4 ]{Property SSLUseSessionResumption As %Boolean [ InitialExpression = 0 ];/// These properties can be configured or set by the associated Business OperationParameter SETTINGS = "SSLUseSessionResumption:Connection";/// Connect to the FTP server and log in, setting the directory and transfer modeMethod Connect(pTimeout As %Numeric = 30, pInbound As %Boolean = 0) As %Status{Set $ZT="Trap", tSC=$$$OK, tFTPPort=..FTPPortDo {If ..Connected { Do ..TestConnection(pInbound) Quit:..Connected }#; Connect to the FTP serverIf '$IsObject(..%CredentialsObj) Do ..CredentialsSet(..Credentials) If '$IsObject(..%CredentialsObj) { Set tSC=$$$ERROR($$$EnsErrNoCredentials,..Credentials) Quit }Set ..%LastSetFilePath=""#; find FTP type; get a configuration-settings instance, use it to open an FTP Session instanceIf ..%isSFTP {#; Connect using an SFTP objectSet:""=tFTPPort tFTPPort=22Set tIOAddr=..FTPServer_":"_tFTPPort_"/"_..Credentials_"/SSL='"_..SSLConfig_"'/PubKey='"_..SFTPPublicKeyFile_"'/PrivKey='"_..SFTPPrivateKeyFile_"'"$$$catTRACE("connwait","Connecting to "_tIOAddr_"/"_..Credentials_"'/PubKey='"_..SFTPPublicKeyFile_"'/PrivKey='"_..SFTPPrivateKeyFile_"' with timeout="_pTimeout)Set:'$IsObject(..FTP)||'..FTP.%Extends("EnsLib.FTP.CommonSSH") ..FTP=$thisSet ..FTP.SSLUseSessionResumption=..SSLUseSessionResumptionSet t0=$zhSet tSC=..FTP.ConnectSSH(pTimeout, pInbound, tFTPPort)} Else {#; Connect using standard FTP, or FTPS with SSLConfigSet:""=tFTPPort tFTPPort=21Set tIOAddr=..FTPServer_":"_tFTPPort_"/"_..Credentials_"/SSL='"_..SSLConfig$$$catTRACE("connwait","Connecting to "_tIOAddr_"/"_..Credentials_" with timeout="_pTimeout)Set:'$IsObject(..FTP)||'..FTP.%Extends("%Net.FtpSession") ..FTP=##class(%Net.FtpSession).%New()Set ..FTP.SSLUseSessionResumption=..SSLUseSessionResumptionSet t0=$zh, ..FTP.Timeout=pTimeout, ..FTP.UsePASV=..UsePASV, ..FTP.LegacySSL=("*"=$E(..SSLConfig,*)), ..FTP.SSLConfiguration=$S("*"=$E(..SSLConfig,*):$E(..SSLConfig,1,*-1),1:..SSLConfig)If (..FTP.SSLConfiguration'="") {Set ..FTP.SSLCheckServerIdentity = ..SSLCheckServerIdentitySet ..FTP.SSLUseSessionResumption = ..SSLUseSessionResumption}If '..FTP.Connect(..FTPServer,..%CredentialsObj.Username,..%CredentialsObj.Password,tFTPPort) {Set tSC=$$$ERROR($$$EnsErrFTPConnectFailed,tIOAddr_"/"_..Credentials,..FTP.ReturnMessage,..FTP.ReturnCode)}#; Set after connect since FTP class will query server if empty stringSet ..FTP.CommandTranslateTable = ..CommandTranslateTable}If $$$ISERR(tSC) {Set tSC=$S((-1'=pTimeout)&&(t0+pTimeout<=$zh): $$$ADDSC($$$ERROR($$$EnsErrOutConnectExpired,pTimeout,$S(..%isSFTP:"SFTP",1:"FTP"),tIOAddr),tSC), 1: $$$ERROR($$$EnsErrOutConnectFailed,$$$StatusDisplayString(tSC),$S(..%isSFTP:"SFTP",1:"FTP"),tIOAddr))Set ..FTP=$$$NULLOREFQuit}#; Get the system declaration from the FTP serverSet ..%Syst="" Set:..FTP.System(.tSystem) ..%Syst=tSystemIf ""'=..%Syst {Set ..%isVMS = ("VMS " = $E(..%Syst,1,$L("VMS ")))$$$catTRACE("connwait","Detected FTP server system type '"_..%Syst_"'")}#; Set the current directorySet ..%LastSetFilePath=..fixSvrPath(..FilePath,0)If ""=..%LastSetFilePath {$$$catTRACE("connwait","Not setting FTP working directory because FilePath is empty")} Else {If ..FTP.SetDirectory(..%LastSetFilePath) {$$$catTRACE("connwait","Set FTP working directory to "_..%LastSetFilePath)} Else {Set tSC=$$$ERROR($$$EnsErrFTPDirectoryChangeFailed,..%LastSetFilePath,..FTP.ReturnMessage,..FTP.ReturnCode) Set ..%LastSetFilePath=""Quit}}#; Set the transfer modeSet tTable = "RAW"Set csetlc=$ZCVT(..Charset,"L")Set tAscii=$Case($E(csetlc,1,5),"":1,"defau":1,"ascii":1,"latin":1,"iso-8":1,"utf-8":1,:0)If 'tAscii {If '..FTP.Binary() {Set tSC=$$$ERROR($$$EnsErrFTPModeChangeFailed,"Binary",..FTP.ReturnMessage,..FTP.ReturnCode)Set ..FTP.TranslateTable = ""Quit}If "binary"'=csetlc {Set tEnc=..Charset Set:"*"=$E(tEnc) $E(tEnc)=""Set tTable = ##class(%IO.I.TranslationDevice).GetCharEncodingTable(tEnc)Set:tTable="" tTable="RAW"}} Else {If '..FTP.Ascii() {Set tSC=$$$ERROR($$$EnsErrFTPModeChangeFailed,"Ascii",..FTP.ReturnMessage,..FTP.ReturnCode)Quit}If "ascii"'=csetlc {If $Case(csetlc,"":0,"default":0,"native":0,:1) {Set tTable = ##class(%IO.I.TranslationDevice).GetCharEncodingTable(..Charset)} Else { Set tTable = "" }Set:tTable="" tTable=$$DefIO^%NLS(5)}}#; SuccessSet ..FTP.TranslateTable = tTableSet tTxt="Connected to FTP Server '"_tIOAddr_"' at path '"_..%LastSetFilePath_"' using Credentials '"_..Credentials_"'"If ..StayConnected<0 { $$$LOGINFO(tTxt) }Else {If pInbound&&'..StayConnected { $$$catTRACE("connwait",tTxt) }ElseIf ..%logTransfers { $$$LOGINFO(tTxt) }Else { $$$sysTRACE(tTxt) }}Set ..Connected=1$$$ASSERT(..FTP.Connected)If (..BusinessHost.%LastReportedError [ "ERROR <Ens>ErrOutConnect")||(..BusinessHost.%LastReportedError [ ..%LastNetErr) {Set ..BusinessHost.%LastReportedError=""$$$SetHostMonitor(..BusinessHost.%ConfigName,$$$eMonitorStatus,"OK")}Set ..%LastNetErr="%%%%%"Set i%%IOAddr=tIOAddr} While 0ExitIf $$$ISERR(tSC) {$$$ASSERT('..Connected)Do:..FTP.Connected ..FTP.Logout() ; force FTP class into sync in case it made a mistake}Quit tSCTrapSet $ZT="",tSC=$$$EnsSystemErrorSet tSC =$$$ERROR($$$EnsErrOutConnectException,$$$StatusText(tSC),$S(..%isSFTP:"SFTP",1:"FTP"),tIOAddr_"/"_..Credentials)Goto Exit}}