<p>I'm using Rancher's nginx ingress controller, <code>rancher/nginx-ingress-controller:0.21.0-rancher3</code>, which should be based on <a href="https://github.com/kubernetes/ingress-nginx" rel="nofollow noreferrer">https://github.com/kubernetes/ingress-nginx</a> AFAIK.</p>

<p>My udp-services is configured as:</p>

<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: udp-services
  namespace: ingress-nginx
data:
  69: "default/tftp:69"
  8881: "default/test:8881"
</code></pre>

<p>Running <code>nc -l -u -p 8881</code> on <code>default/test</code> can communicate with an out-of-cluster client just fine. That should mean that the udp proxying works, at least for some cases. However tftp requests to <code>default/tftp</code> timeout consistently.</p>

<p>Roughly, a TFTP read should work as <a href="https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol" rel="nofollow noreferrer">below</a>:</p>

<ol>
<li>Client port A => Server port 69 (request)</li>
<li>Server port B => Client port A (send data, and note it's a <em>new</em> port B)</li>
<li>Client port A => Server port B (acknowledgement)</li>
</ol>

<p><code>tcpdump</code> running on the tftp server shows the communication is like:</p>

<ol>
<li>Host port A => Server port 69 (request a file)</li>
<li>Server port B => Host port A (sending data back to port A)</li>
<li>Host => Server, ICMP port unreachable (but port A is unreachable)</li>
</ol>

<p>At the same time the ingress logs something like:</p>

<pre><code>TIMESTAMP [error] ... upstream timed out (110: Connection timed out) while proxying connection, udp client: ::1, server: [::]:69, upstream: "...:69", bytes from/to client:..., bytes from/to upstream:...
</code></pre>

<p>TFTP requests from another in-cluster container work just fine. This should mean that the TFTP server itself is not the direct source of problem. And the issue is how the ingress controller handles the requests.</p>

<p>I found <a href="https://manpages.ubuntu.com/manpages/precise/man8/tftpd.8.html" rel="nofollow noreferrer">tftpd</a> has a <code>--port-range</code> argument which can pin which ports tftpd can use to respond. I tried to pin it to port 8881 (<code>--port-range 8881:8881</code>), but the requests are still being dropped.</p>

<p>My guess is that the ingress does not redirect the packet back to the client since the reply is not from port 69, but port B.</p>

<p>Did anyone succeed to expose a TFTP service within a Kubernetes cluster?</p>

<p>It is not a 100% solution but i found a workaround for the exact same thing.  The problem is that tftp creates a new outbound UDP connection that isn't known in the host's state table.  Thus it treats it like an outgoing request rather than a reply.  I will also note that TFTP client apps handle this fine, but PXE drivers (at least intel ones) do not.</p>

<p>If you are using Calico as your CNI, you can disable "natOutgoing" on the IPPool.  if you need NAT, you can create a second IPPool without NAT.</p>

<p><a href="https://docs.projectcalico.org/networking/assign-ip-addresses-topology#features" rel="nofollow noreferrer">https://docs.projectcalico.org/networking/assign-ip-addresses-topology#features</a></p>

<p>I disabled it for the default by <code>calicoctl get ippool -oyaml | sed 's/natOutgoing: true/natOutgoing: false/g' | calicoctl apply -f -</code></p>

<p>I am sure other CNI plugins may have a similar workaround </p>
