pypxe/PyPXE

Be able to set srcport (firewall friendly) mode of tftp.py through config

Apachez- opened this issue · 6 comments

According to the RFC for tftp its not mandatory to use srcport 69 for the reply traffic back to the client.

The normal behaviour is something like this:

  1. Client connects to tftp server using srcport >1023 (or >49152 depending on os and version) and dstport 69. In this example lets say the client used srcport 12345.

  2. Server replies to the client using srcport >1023 and dstport (in this example) 12345.

The above confuses any firewalls you might have in between.

A workaround is to make the tftp server to use srcport 69 in its replies back to the client, that is something like this:

  1. Client connects to tftp server using srcport >1023 (or >49152 depending on os and version) and dstport 69. In this example lets say the client used srcport 12345.

  2. Server replies to the client using srcport 69 and dstport (in this example) 12345.

One can fix this manually by changing "self.sock.bind((self.ip, 0))" (which exists at two places in tftp.py) into "self.sock.bind((self.ip, 69))" but its a more clean way if this could be made through config.

That is a "firewall friendly" mode of the tftp server (for example firewall_friendly true/false).

icb- commented

From RFC1350:

A requesting host chooses its source TID as described above, and sends its initial request to the known TID 69 decimal (105 octal) on the serving host. The response to the request, under normal operation, uses a TID chosen by the server as its source TID and the TID chosen for the previous message by the requestor as its destination TID. The two chosen TID's are then used for the remainder of the transfer.

In this context, TID is synonymous with UDP port. While that may not expressly forbid the server picking TID 69, using a standard ephemeral port is best practices. All firewalls have problems with TFTP, from any server software, unless the firewall has a layer-4 helper.

Given @icb-'s comments above, I don't think this will be implemented, as we want to stick to the RFC.

As referenced in RFC1350 there is nothing that says that the TID on the server side is not allowed to be 69.

Adding this as an option (firewall friendly / singleport use) will make PyPXE be able to be used in a firewalled environment where one currently is forced to use other software (such as tftpgui who supports using srcport 69 for the replytraffic) simply because PyPXE currently doesnt work.

So I would propose to have this issue reopened :-)

I'll accept a PR for this issue, as long as the default behaviour is not impacted in any way. I don't have the time to work on this myself at the moment.

icb- commented

There's no portable way to specify the ephemeral port range, and even the ways you can are system-wide. You would have to either use a fixed port, limiting you to one in-flight transfer, or loop through a range of ports finding an unused one. Either way you've increased your susceptibility to spoofed traffic.

The proper way to deal with TFTP through a firewall is with a layer-4 helper, like nf_conntrack_tftp in Linux or tftp-proxy in OpenBSD.

I've just had a look at the code again, and whilst we wouldn't explicitly be limited to one transfer, it would bring on a whole host of bugs unless we forced one at a time.

It would also require a lot of changes and for very minimal benefit, especially given the above firewall mitigations. We would essentially be maintaining two tftp main loops, or a complete rewrite.