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

Support for Hairpinning #53

Closed
CodeZombieCH opened this issue Jul 2, 2020 · 10 comments
Closed

Support for Hairpinning #53

CodeZombieCH opened this issue Jul 2, 2020 · 10 comments
Labels
enhancement New feature or request good first issue Good for newcomers good-for-stream https://www.twitch.tv/stapelberg help wanted Extra attention is needed

Comments

@CodeZombieCH
Copy link
Contributor

I noticed that I'm unable to connect to a server inside my local network using the router7 public IP and the forwarded port.

Example
Local machine: 192.168.0.100
Public IP: 12.34.56.78
Port forwarding rule: :22 -> 192.168.0.100:22

Result: connection refused

Connecting from outside of router7 works perfectly fine.

To ensure we can connect from the local network using the public IP, it would be nice if support for Hairpinning (see Hairpinning and Hairpin-NAT) could be added. This could be achieved by adding additional nftables rules.

(thanks for @stapelberg for helping me investigating this issue)

@stapelberg stapelberg added enhancement New feature or request good first issue Good for newcomers good-for-stream https://www.twitch.tv/stapelberg help wanted Extra attention is needed labels Jul 2, 2020
@stapelberg
Copy link
Contributor

Because it came up recently on twitter, here are a few additional notes:

I suppose the next step would be prototyping some nft rules that do what Dave outlined in the twitter thread.

@stapelberg
Copy link
Contributor

More insight from Dave: https://twitter.com/dave_universetf/status/1298100264927289349

I guess we just wait another few days until he explains how things ought to really work in Linux :)

@neingeist
Copy link

neingeist commented Feb 8, 2022

This comes up as 2nd hit when googling for nftables hairpinning, so I thought I share my experience. I'm not running router7 though.

For hairpinning NAT of a portforward from e.g. TCP port 16085 on the outer interface $out_if (using the address $out_ip) to local 192.168.122.2:22 on $lan_if (with the $lan_net IP range), you need to add

  • a prerouting NAT rule iif $lan_if ip daddr $out_ip tcp dport 16085 dnat to 192.168.122.2:22
    (this rewrites the destination)
  • a forward filter rule iif $lan_if oif $lan_if ip daddr 192.168.122.2 tcp dport 22 accept
    (this allows forwardng the NATted connection)
  • a postrouting NAT rule ip saddr $lan_net ip daddr 192.168.122.2 tcp dport 22 counter masquerade
    (this rewrites the source)

And, if you want to make local connections from the router to the service work:

  • an output NAT rule (!) ip daddr $out_ip tcp dport 16085 dnat to 192.168.122.2:22
    (this rewrites the "outgoing" connection)
  • (probably another output filter rule, but I generally allow outgoing connections, so I have this covered)

The rules might be a bit narrow, but I go down the careful route (pun intended) with this to avoid accidental NATting.

@neingeist
Copy link

neingeist commented Feb 8, 2022

  • a forward filter rule iif $lan_if oif $lan_if ip daddr 192.168.122.2 tcp dport 22 accept
    (this allows forwardng the NATted connection)
  • (probably another output filter rule, but I generally allow outgoing connections, so I have this covered)

The forward filter rules can apparently be replaced by one universal ct status dnat counter accept.

Same goes for the postrouting NAT rules - one iifname $lan_if oifname $lan_if ct status dnat counter masquerade suffices.

@neingeist
Copy link

Here's a full ruleset, configured as above but reduced to a handful of rules by using a map of port forwards (requires nftables >= 0.9.4).

https://gist.github.com/neingeist/c97b488f2511bb5ca07c8a07213eccbe

@stapelberg
Copy link
Contributor

Thanks for sharing this! I’m pretty busy the next few days, but will take a closer look and try it out next week hopefully :)

@neingeist
Copy link

There's no rush ;)

@stapelberg
Copy link
Contributor

Yeah, on router7 indeed only 2 modifications are needed: the prerouting dnat rule needs to be expressed without using iifname "uplink0", and the postrouting needs a masquerade rule for lan0→lan0 traffic:

# nft add rule ip nat prerouting ip daddr 212.51.xx.0/24 tcp dport 8096 dnat to 10.0.0.252:8096
# nft add rule ip nat postrouting iifname "lan0" oifname "lan0" ct status dnat counter masquerade

I think we can express the rule without using hard-coded addresses (which means they would need to change whenever the public IP address changes) by using ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type local, which would match all locally-configured IP addresses (fib daddr type local) except for the loopback and LAN IP addresses.

Unfortunately, the router7 kernel is built without CONFIG_NFT_FIB_IPV4. I’ll enable that when I get a chance.

stapelberg added a commit to rtr7/kernel that referenced this issue Mar 7, 2022
@stapelberg
Copy link
Contributor

Yep, as expected, these rules work, too:

# nft add rule ip nat prerouting ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type local tcp dport 8096 dnat to 10.0.0.252:8096
# nft add rule ip nat postrouting iifname "lan0" oifname "lan0" ct status dnat counter masquerade

Next up is changing the router7 netconfig to apply these rules.

@neingeist
Copy link

I think we can express the rule without using hard-coded addresses (which means they would need to change whenever the public IP address changes) by using ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type local, which would match all locally-configured IP addresses (fib daddr type local) except for the loopback and LAN IP addresses.

Nice, I didn't know about that one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers good-for-stream https://www.twitch.tv/stapelberg help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants