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

forwarding rules #236

Open
anarcat opened this issue Jan 25, 2024 · 8 comments
Open

forwarding rules #236

anarcat opened this issue Jan 25, 2024 · 8 comments

Comments

@anarcat
Copy link
Contributor

anarcat commented Jan 25, 2024

Hello!

I've been working on a home router setup, and one of the things i have painfully figured out is the exact incantations to get not only port forwarding right (that's relatively easy), but "reflection" or "hairpinning" which allows users inside the NAT to access services as if they were on the outside.

I think this might be a worthwhile addition here. I'm still in the testing phase of this here, i have a handful of new forwards to deploy soon that I haven't tested yet, but when I did, i'd submit a PR for that... The code, right now, lives in a profile of mine:

# forward a service from the outside to internal services
#
# This forwards an external port to an internal machine using DNAT. It
# also sets up reflection so that internal hosts can access those
# services transparently by using the same external IP address.
#
# Example:
#
#     profile::network::forward {
#       default:
#         lan_cidr => '192.168.0.0/24',
#         wan_if   => 'eth0',
#         wan_cidr => '10.0.0.1/32',
#       'http':
#         target_addr => '192.168.0.10',
#         target_port => '80',
#         wan_port    => '8080',
#       'https':
#         target_addr => '192.168.0.10',
#         target_port => '443',
#         wan_port    => '8443',
#     }
#
# The above forwards ports 8080 and 8443 to the machine 192.168.0.10's
# regular http (80) and (8443) internal ports. The internal network is
# 192.168.0.0/24 and the external, public IP address is 10.0.0.1. The
# external interface is eth0.
#
# @param target_addr IP address to forward to
# @param lan_addr internal address of the router
# @param lan_cidr internal IP address range
# @param target_port port number of the forward target
# @param wan_port port number on the external interface
# @param wan_if external interface, public network
# @param wan_cidr publicly available IP address range
define profile::network::forward(
  Stdlib::IP::Address::V4::Nosubnet $target_addr,
  Stdlib::IP::Address::V4::Nosubnet $lan_addr,
  Stdlib::IP::Address::V4::CIDR $lan_cidr,
  Stdlib::Port $target_port,
  Stdlib::Port $wan_port,
  String[1] $wan_if,
  Stdlib::IP::Address::V4::CIDR $wan_cidr,
) {
  nftables::rule {
    "PREROUTING-dnat_${title}":
      table   => "ip-${nftables::nat_table_name}",
      order   => '41',
      content => "iifname ${wan_if} tcp dport ${wan_port} dnat to ${target_addr}:${target_port}";
    # reflection
    "PREROUTING-dnat_${title}reflect":
      table   => "ip-${nftables::nat_table_name}",
      order   => '40',
      content => "tcp dport ${wan_port} ip saddr ${lan_cidr} ip daddr ${wan_cidr} dnat to ${target_addr}:${target_port}";
    "POSTROUTING-dnat_${title}reflect":
      table   => "ip-${nftables::nat_table_name}",
      order   => '40',
      content => "tcp dport ${target_port} ip saddr ${lan_cidr} ip daddr ${target_addr}/32 snat to ${lan_addr}";
  }
}

How does that look?

@anarcat
Copy link
Contributor Author

anarcat commented Jan 25, 2024

i actually renamed the define and pushed the code here:

https://gitlab.com/anarcat/puppet/-/blob/c9635ec8e8a7f319874ca63c4074de9e731941aa/site-modules/profile/manifests/nftables/rules/forward.pp

may be updated with time, naturally.

also, i just remembered we have dnat4 and snat4 rules in there, but for me those are really opaque and undocumented, and don't do all of the above. maybe the above should be rewritten to use the dnat/snat stuff, but i'm not sure i understand them well enough to tell.

@kenyon
Copy link
Member

kenyon commented Jan 25, 2024

Unrelated to your code, but it sure would be simpler to use IPv6 rather than port forwarding. Being able to eliminate this kind of legacy IP complexity on my home network is what got me into IPv6 many years ago.

@anarcat
Copy link
Contributor Author

anarcat commented Jan 25, 2024 via email

@kenyon
Copy link
Member

kenyon commented Jan 25, 2024

Not necessarily obvious, everyone has to learn at some point. Maybe also obvious, but tunnels are often a good workaround for lack of native IPv6, but I guess some ISPs make even that difficult.

@anarcat
Copy link
Contributor Author

anarcat commented Jan 25, 2024

🤷

i understand where you're coming from, but i would assume someone that comes in with relatively advanced knowledge in firewall rules and Puppet would know about other ways to do what i'm trying to here. I wouldn't try to teach them the myriad of other ways they could do what they want to do.

Heck, you could have told me about ipsec, tailscale, ngrok and god knows how else I might manage this. I'm just trying to keep things simple for now. :)

@anarcat
Copy link
Contributor Author

anarcat commented Jan 25, 2024

or to frame this another way:

do you fundamentally object to adding such a NAT forwarding rule here, in favor of telling people they should setup IPv6 tunnels instead?

@duritong
Copy link
Collaborator

We do have have a simple wrapper that includes hairpinning here: https://code.immerda.ch/immerda/ibox/puppet-modules/-/blob/main/ib_nftables/manifests/simple_dnat.pp?ref_type=heads

It seems to me that we did it slightly different, but I can assure you we have it in use for quite a while (longer than the commits are).

I intended to contribute that wrapper eventually into the nftables module itself, but lacked a bit of time to properly clean it up and write tests.

@anarcat
Copy link
Contributor Author

anarcat commented Jan 26, 2024

interesting! that leads to a 500 error here, but i'd be really curious to read it! i'd be happy to collaborate towards a final version, but i must admit i suck at unit tests, so i'm not sure i could help with that. :p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants