pypxe / PyPXE

Pure Python PXE (DHCP-(Proxy)/TFTP/HTTP/NBD) Server
MIT License
547 stars 125 forks source link

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

Closed Apachez- closed 7 years ago

Apachez- commented 7 years ago

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 7 years ago

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.

psychomario commented 7 years ago

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

Apachez- commented 7 years ago

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 :-)

psychomario commented 7 years ago

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 7 years ago

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.

psychomario commented 7 years ago

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.