Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FTPES with TLS Session Reuse for FileZilla Server (need also BouncyCastle and a subclass). #321

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

deguich
Copy link

@deguich deguich commented Jan 15, 2025

Connecting to ftp.example.com on the command port and then to the corresponding IP x.x.x.x on the data port prevents the server from accepting the same TLS session. Therefore, the Filezilla server needs to set peerHost before socket.connect to allow TLS session resumption.

With this new version, FTPeS to the server with TLS session resumption works with this subclass :

package org.mypackage;

import java.io.IOException;
import java.net.Socket;
import java.security.SecureRandom;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.util.TrustManagerUtils;
import org.bouncycastle.jsse.BCExtendedSSLSession;
import org.bouncycastle.jsse.BCSSLSocket;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;

public class FTPSClientSSLSessionReuse extends FTPSClient {

  public FTPSClientSSLSessionReuse (boolean isImplicit) throws Exception {
    super(isImplicit, createSSLContext());
    setEnabledProtocols(new String[] {"TLSv1.2"});
    setUseEPSVwithIPv4(true);
  }

  private static SSLContext createSSLContext() throws Exception {
    SSLContext context = SSLContext.getInstance("TLS", new BouncyCastleJsseProvider());
    context.init(
        null,
        new TrustManager[] {TrustManagerUtils.getValidateServerCertificateTrustManager()},
        new SecureRandom());
    return context;
  }

  @Override
  protected void _prepareDataSocket_(final Socket socket) throws IOException {
	  if (_socket_ instanceof BCSSLSocket sslSocket) {
	      BCExtendedSSLSession bcSession = sslSocket.getBCSession();
	      if (bcSession != null && bcSession.isValid() && socket instanceof BCSSLSocket dataSslSocket) {
	        dataSslSocket.setBCSessionToResume(bcSession); 
	        // Next line could be a solution if this function was called before connect
	        dataSslSocket.setHost(bcSession.getPeerHost());
	      }
	    }
  }
}

…and a subclass).

Connecting to ftp.example.com on the command port and then to the corresponding IP x.x.x.x on the data port prevents the server from accepting the same TLS session.
Therefore, the Filezilla server needs to set peerHost before socket.connect to allow TLS session resumption.
Copy link
Member

@garydgregory garydgregory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @deguich
There are 2 issues with this PR:

  • No tests:
    • This is just an invitation for future changes to inadvertently undo or break the behavior in this PR. A test should fail if the changes to main are not applied.
    • Look at the existing tests for inspiration here and in other Commons components
    • If this is a FileZilla specific change, you might need to run a test that runs FileZilla in Docker using a Docker Maven plugin like https://github.com/fabric8io/docker-maven-plugin
  • You did not follow the instructions in the PR template: You did not run mvn by itself or you would have caught this build failure:
[INFO] --- checkstyle:3.6.0:check (default-cli) @ commons-net ---
[INFO] There are 3 errors reported by Checkstyle 10.21.1 with src/conf/checkstyle.xml ruleset.
[ERROR] src\main\java\org\apache\commons\net\ftp\FTPSClient.java:[315] (regexp) RegexpSingleline: Line has trailing spaces.
[ERROR] src\main\java\org\apache\commons\net\ftp\FTPSClient.java:[834] (regexp) RegexpSingleline: Line has trailing spaces.
[ERROR] src\main\java\org\apache\commons\net\ftp\FTPSClient.java:[876] (regexp) RegexpSingleline: Line has trailing spaces.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

@@ -313,7 +312,7 @@ protected Socket _openDataConnection_(final String command, final String arg) th
*/
protected void _prepareDataSocket_(final Socket socket) throws IOException {
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No extra whitespace please.

@deguich deguich requested a review from garydgregory January 31, 2025 14:21
@deguich
Copy link
Author

deguich commented Jan 31, 2025

You can enable SNI (Server Name Indication) test by using external filezilla server (SSL session reuse always works when using docker on localhost and bouncycastle lib) : src\test\resources\org\apache\commons\net\filezillaserver\filezillaserver.properties
I can PM you the host I used (@deguich).
SSL reuse with SNI is the only problem this MR try to resolve. Without socket peer hostname set, Filezilla server refuse data connections.

Copy link
Member

@garydgregory garydgregory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing and testing... All Javadoc changes in FTPSClient are formatting changes and makes the PR harder to review, this PR should not change style.

<version>1.7.32</version>
<scope>test</scope>
</dependency>
<!--<dependency>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this comment block.

</excludes>
<environmentVariables>
<TRACE_CALLS>${commons.net.trace_calls}</TRACE_CALLS>
<ADD_LISTENER>${commons.net.add_listener}</ADD_LISTENER>
</environmentVariables>
</configuration>
</plugin>
<!-- Plugin Failsafe for integration-tests -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to state what a plugin is.

@garydgregory
Copy link
Member

Hello @deguich
The build fails on the GH CI with:

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.commons.net.ftp.FTPSClientIntegrationTest
Error:  Tests run: 8, Failures: 0, Errors: 4, Skipped: 4, Time elapsed: 0.491 s <<< FAILURE! -- in org.apache.commons.net.ftp.FTPSClientIntegrationTest
Error:  org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaNoTlsResume[endpointCheckingEnabled=false] -- Time elapsed: 0.027 s <<< ERROR!
java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:760)
	at org.apache.commons.net.SocketClient._connect(SocketClient.java:141)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:308)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:290)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientTo(FTPSClientIntegrationTest.java:178)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientToFZ(FTPSClientIntegrationTest.java:218)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaNoTlsResume(FTPSClientIntegrationTest.java:275)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at java.base/java.lang.Thread.run(Thread.java:1575)

Error:  org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaTlsResume[endpointCheckingEnabled=false] -- Time elapsed: 0.393 s <<< ERROR!
java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:760)
	at org.apache.commons.net.SocketClient._connect(SocketClient.java:141)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:308)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:290)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientTo(FTPSClientIntegrationTest.java:178)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientToFZ(FTPSClientIntegrationTest.java:218)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.retrieveFile(FTPSClientIntegrationTest.java:224)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaTlsResume(FTPSClientIntegrationTest.java:264)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at java.base/java.lang.Thread.run(Thread.java:1575)

Error:  org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaNoTlsResume[endpointCheckingEnabled=true] -- Time elapsed: 0.002 s <<< ERROR!
java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:760)
	at org.apache.commons.net.SocketClient._connect(SocketClient.java:141)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:308)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:290)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientTo(FTPSClientIntegrationTest.java:178)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientToFZ(FTPSClientIntegrationTest.java:218)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaNoTlsResume(FTPSClientIntegrationTest.java:275)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at java.base/java.lang.Thread.run(Thread.java:1575)

Error:  org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaTlsResume[endpointCheckingEnabled=true] -- Time elapsed: 0.008 s <<< ERROR!
java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:760)
	at org.apache.commons.net.SocketClient._connect(SocketClient.java:141)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:308)
	at org.apache.commons.net.SocketClient.connect(SocketClient.java:290)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientTo(FTPSClientIntegrationTest.java:178)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.loginClientToFZ(FTPSClientIntegrationTest.java:218)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.retrieveFile(FTPSClientIntegrationTest.java:224)
	at org.apache.commons.net.ftp.FTPSClientIntegrationTest.testFileZillaTlsResume(FTPSClientIntegrationTest.java:264)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at java.base/java.lang.Thread.run(Thread.java:1575)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants