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

Proposal for changes to NAT. #279629

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

gray-heron
Copy link

@gray-heron gray-heron commented Jan 8, 2024

Background

Recently I've been testing NixOS' NAT feature extensively, and found three surprising quirks to its behavior, one of which I find concerning.

I submit a draft of changes to the NAT integration test to cover those quirks, and check against behavior I find unreasonable. If maintainers agree with this proposal, I will proceed to implement changes to the actual NAT module to make this new test pass.

1. NAT affects all interfaces.

As currently implemented, turning on NAT enables indiscriminate traffic forwarding between all interfaces on the nat-enabled machine in all directions. In particular this allows to:

  • reach resources "behind" NAT from the externalNetwork,
  • reach services hosted on uninvolved interfaces from the externalNetwork (uninvolved meaning unspecified at all in the NAT configuration),
  • reach any machine connected to the nat-enabled machine from any network connected to nat-enabled machine.

The new test checks against those in many places, see line 177.

2. DNAT overrides source IP address.

The rules to capture packets intended for loopbackIPs are so wide they capture "normal" traffic from the externalInterface indented for DNAT. In the end, the packets get routed to the correct destination, but because the source IP is being set to externalIP, the listening service cannot identify what is coming from where, which is oftentimes useful for i.e. blacklisting.

See new check client.succeed("check-last-clients-ip ${serverIp}").

A side question: why does the loopbackIP option even exist -- as in why aren't all forwards loopbacked by default? Is there any reasonable use case for routing traffic from the outside differently than from the inside... but only the forwarded ports?

3. DNAT captures traffic on all IPs even if externalIP was specified.

networking.nat.externalIP option allows to specify the exact external address to be used for NAT. This is particularly useful, if there are multiple IP addresses associated with one networking.nat.externalInterface.

However, as currently implmented, even if externalIP was set, packets destined for other IP addresses on the externalInterface would still be captured, making it impossible to host services bound to IP addresses not meant for NAT.

See new check server.succeed('[[ 'curl http://${routerAlternativeExternalIp}:8080' == "router" ]]').

There was a pull request about this #254025, it was closed for... unrelated reasons.

The question was if this isn't too obscure to warrant changes to the configuration semantics. I think if we agree on the points above, this question becomes irrelevant.

I'm looking forward to a feedback.

@samhug @mkg20001 @duament @wegank

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/3697

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/3751

@Luflosi
Copy link
Contributor

Luflosi commented Jul 10, 2024

Does #277016 fix one of these surprising behaviours when using iptables?

JustTNE added a commit to JustTNE/nixpkgs that referenced this pull request Jul 21, 2024
This code is mostly from NixOS#279629, the uninvoled client checks were removed (since they are the same as the direct connection to the client test) and the tests were adjusted to work as intended as well as bugs fixed.
In some cases, some tests are skipped when they do not make sense for the specific configuration that is being tested.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants