FTP script broke on upgrade to Windows Server 2019

Here's the bones of the script that worked on Windows Server 2012.

	logger = system.util.getLogger("ftp_param_report")
	import io
	import csv
	from ftplib import FTP
	
	machine = int(machine)								# '03' -> 3, etc., from viewBanner radio group.
	logger.info('machine: 5 step 1')
	
	try:
		ftp.close()											# This helped during debug if previous attempt didn't clean up.
	except:
		pass
	ftp = FTP()
	ftp.connect(10.11.12.13, 21)
	ftp.login('ftpuser', 'mySecret')					# Login.
	ftp.set_pasv(False)									# Required by the C-More panel for some reason.
	ftp.cwd('/Built-in_Flash/Recipe')					# Change Working Directory
	logger.info('machine: 5 step 2')
		
	# Read the data from the PRODUCT_SKU file.
	with io.BytesIO() as binary_buffer:					# io avoids having to download a file to disk with associated cleanup.
		try:
			# read all of products.csv into a binary buffer
			logger.info('machine: 5 step 3')
			ftp.retrbinary("RETR myFile.csv", binary_buffer.write)							# Read the SKU file.
			logger.info('machine: 5 step 4')

On upgrade to Windows Server 2019 it never gets to step 4.

Running Windows CMD FTP client on the gateway I can see there's a problem.

ftp> ls
200 Port command successful.

But then it hangs there until it times out in 900 s. The directory listing is never returned.
I think the problem is due to Windows not sticking to ports 20 and 21 (which are open for outbound) for the FTP connection and using a high port number which is blocked by Windows Firewall.

Filezilla gateway
The problem can be replicated using Filezilla.

By the way, does anyone know what the 225, 217 means in the PORT command?

Has anyone got any suggestions?

The PORT command for FTP (in active mode) is what defines the port that the server will connect back to the client with for the data channel. In your Filezilla example, it is two bytes that represent the port number:

So you'd have to have port 57817 open through to the Windows Server 2019 machine in order to have that connection established. Unfortunately, that port is typically dynamically chosen from a wide range.

I'm not sure off the top of my head how to prescribe a more constrained dynamic port range for the client to suggest in ftplib...

EDIT: I had the active vs passive backwards in my initial post.. -> :coffee:

1 Like

Thanks, Kevin. Active doesn't seem to fix it.

Filezilla ACTIVE

Filezilla gateway 2

This isn't an Ignition problem anymore. I might ask on StackOverflow.

Yeah, my apologies for a supplementary post that I actually deleted earlier--I had active vs passive backwards in my head. Passive mode actually requests the server to send back an additional port for the client to connect to for the data channel (meaning you wouldn't have to make special firewall exceptions on your Windows Server 2019 side). I see that the PASV (default) actually was rejected by your target, so you were actually in Active mode (same as Ignition) in Filezilla.

So at this point options look like:

  • Open up a wider port range in Windows Firewall (perhaps from that specific host?) for the data channel dynamic port that will be opened by active mode ftp.
  • Find a way to constrict that port range for active mode ftp in python2 ftplib (which I also wasn't having good luck finding) and then following through with the respective fw rule.
  • Use an alternate means for file transfer? :person_shrugging:
1 Like