All of lore.kernel.org
 help / color / mirror / Atom feed
* Improvements to the Home Router Wiki page
@ 2021-10-30 21:39 Timothy Ham
  2021-11-02 11:31 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 3+ messages in thread
From: Timothy Ham @ 2021-10-30 21:39 UTC (permalink / raw)
  To: netfilter

Hello,

I have benefited from the wiki page "Simple ruleset for a home router" found here:

https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_home_router

Still, it doesn't cover port forwarding and hairpin NAT. So after several hours of trying different things, I've finally figured out how to do them, and improved the example config. Could someone in charge please update the example on the wiki? Specifically the example config given in the section "Simple router using ppp interface". I've kept everything the same except the few lines I've added.

If my config can be improved further, please suggest more changes as well.

Thank you.

==== begin ====
flush ruleset

define DEV_PRIVATE = eth1
define DEV_WORLD = ppp0
define NET_PRIVATE = 192.168.0.0/16
define IP_WAN = x.x.x.x
define IP_ROUTER = 192.168.0.1
define IP_SERVER = 192.168.0.10

table ip global {
    chain prerouting {
          type nat hook prerouting priority dstnat;

          # Hairpin NAT part 1/2. WAN IP address must be known
          ip daddr $IP_WAN tcp dport 443 dnat to $IP_SERVER:8443
          ip daddr $IP_WAN tcp dport 80 dnat to $IP_SERVER:8080

          # Port forwarding part 1/2
          iifname $DEV_WORLD tcp dport 443 dnat to $IP_SERVER:8443
          iifname $DEV_WORLD tcp dport 80 dnat to $IP_SERVER:8080

    }

    chain inbound_world {
        # accepting ping (icmp-echo-request) for diagnostic purposes.
        # However, it also lets probes discover this host is alive.
        # This sample accepts them within a certain rate limit:
        #
        # icmp type echo-request limit rate 5/second accept

        # allow SSH connections from some well-known internet host
        ip saddr 81.209.165.42 tcp dport ssh accept
    }

    chain inbound_private {
        # accepting ping (icmp-echo-request) for diagnostic purposes.
        icmp type echo-request limit rate 5/second accept

        # allow DHCP, DNS and SSH from the private network
        ip protocol . th dport vmap { tcp . 22 : accept, udp . 53 : accept, tcp . 53 : accept, udp . 67 : accept}
    }

    chain inbound {
        type filter hook input priority 0; policy drop;

        # Allow traffic from established and related packets, drop invalid
        ct state vmap { established : accept, related : accept, invalid : drop }

        # allow loopback traffic, anything else jump to chain for further evaluation
        iifname vmap { lo : accept, $DEV_WORLD : jump inbound_world, $DEV_PRIVATE : jump inbound_private }

        # the rest is dropped by the above policy
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # port forwarding part 2/2
        meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8443 ct state new accept
        meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8080 ct state new accept

        # Allow traffic from established and related packets, drop invalid
        ct state vmap { established : accept, related : accept, invalid : drop }

        # connections from the internal net to the internet or to other
        # internal nets are allowed
        iifname $DEV_PRIVATE accept

        # the rest is dropped by the above policy
    }

    chain postrouting {
        type nat hook postrouting priority 100; policy accept;

        # hairpin part 2/2
        ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8443 snat $ROUTER
        ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8080 snat $ROUTER

        # masquerade private IP addresses
        ip saddr $NET_PRIVATE oifname $DEV_WORLD masquerade
    }
}
==== end ====



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Improvements to the Home Router Wiki page
  2021-10-30 21:39 Improvements to the Home Router Wiki page Timothy Ham
@ 2021-11-02 11:31 ` Pablo Neira Ayuso
  2021-11-03  7:16   ` Timothy Ham
  0 siblings, 1 reply; 3+ messages in thread
From: Pablo Neira Ayuso @ 2021-11-02 11:31 UTC (permalink / raw)
  To: Timothy Ham; +Cc: netfilter

On Sat, Oct 30, 2021 at 09:39:07PM +0000, Timothy Ham wrote:
> Hello,
> 
> I have benefited from the wiki page "Simple ruleset for a home router" found here:
> 
> https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_home_router
> 
> Still, it doesn't cover port forwarding and hairpin NAT. So after several hours of trying different things, I've finally figured out how to do them, and improved the example config. Could someone in charge please update the example on the wiki? Specifically the example config given in the section "Simple router using ppp interface". I've kept everything the same except the few lines I've added.
> 
> If my config can be improved further, please suggest more changes as well.

I'm assuming nftables 1.0.0 for the comments below.

> Thank you.
> 
> ==== begin ====
> flush ruleset
> 
> define DEV_PRIVATE = eth1
> define DEV_WORLD = ppp0
> define NET_PRIVATE = 192.168.0.0/16
> define IP_WAN = x.x.x.x
> define IP_ROUTER = 192.168.0.1
> define IP_SERVER = 192.168.0.10
> 
> table ip global {
>     chain prerouting {
>           type nat hook prerouting priority dstnat;
> 
>           # Hairpin NAT part 1/2. WAN IP address must be known
>           ip daddr $IP_WAN tcp dport 443 dnat to $IP_SERVER:8443
>           ip daddr $IP_WAN tcp dport 80 dnat to $IP_SERVER:8080

Consolidate this rule using a map:

            ip daddr $IP_WAN dnat to tcp dport map { 443 : $IP_SERVER . 8443, 80 : $IP_SERVER . 8080 }

>           # Port forwarding part 1/2
>           iifname $DEV_WORLD tcp dport 443 dnat to $IP_SERVER:8443
>           iifname $DEV_WORLD tcp dport 80 dnat to $IP_SERVER:8080

Is this rule is redundant? is $IP_WAN the IP address of $DEV_WORLD?

>     }
> 
>     chain inbound_world {
>         # accepting ping (icmp-echo-request) for diagnostic purposes.
>         # However, it also lets probes discover this host is alive.
>         # This sample accepts them within a certain rate limit:
>         #
>         # icmp type echo-request limit rate 5/second accept
> 
>         # allow SSH connections from some well-known internet host
>         ip saddr 81.209.165.42 tcp dport ssh accept
>     }
> 
>     chain inbound_private {
>         # accepting ping (icmp-echo-request) for diagnostic purposes.
>         icmp type echo-request limit rate 5/second accept
> 
>         # allow DHCP, DNS and SSH from the private network
>         ip protocol . th dport vmap { tcp . 22 : accept, udp . 53 : accept, tcp . 53 : accept, udp . 67 : accept}
>     }
> 
>     chain inbound {
>         type filter hook input priority 0; policy drop;
> 
>         # Allow traffic from established and related packets, drop invalid
>         ct state vmap { established : accept, related : accept, invalid : drop }
> 
>         # allow loopback traffic, anything else jump to chain for further evaluation
>         iifname vmap { lo : accept, $DEV_WORLD : jump inbound_world, $DEV_PRIVATE : jump inbound_private }
> 
>         # the rest is dropped by the above policy
>     }
> 
>     chain forward {
>         type filter hook forward priority 0; policy drop;

You could move these rules below the verdict map...

>         # port forwarding part 2/2
>         meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8443 ct state new accept
>         meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8080 ct state new accept
> 
>         # Allow traffic from established and related packets, drop invalid
>         ct state vmap { established : accept, related : accept, invalid : drop }

... 'ct state new,untracked' is implicitly assumed after vmap, so you
could move (and consolidate) this rule:

          meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport { 8443, 8080 } accept

>         # connections from the internal net to the internet or to other
>         # internal nets are allowed
>         iifname $DEV_PRIVATE accept
> 
>         # the rest is dropped by the above policy
>     }
> 
>     chain postrouting {
>         type nat hook postrouting priority 100; policy accept;
> 
>         # hairpin part 2/2
>         ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8443 snat $ROUTER
>         ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8080 snat $ROUTER

Consolidate rule:

          ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport { 8080, 8443 } snat $ROUTER

however, if the routing table allows to reach your $IP_SERVER from
$NET_PRIVATE, then you do not need this NAT rule.

>         # masquerade private IP addresses
>         ip saddr $NET_PRIVATE oifname $DEV_WORLD masquerade
>     }
> }
> ==== end ====
> 
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Improvements to the Home Router Wiki page
  2021-11-02 11:31 ` Pablo Neira Ayuso
@ 2021-11-03  7:16   ` Timothy Ham
  0 siblings, 0 replies; 3+ messages in thread
From: Timothy Ham @ 2021-11-03  7:16 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter

Thanks for the reply. My responses are inlined below.


On Tuesday, November 2nd, 2021 at 4:31 AM, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Sat, Oct 30, 2021 at 09:39:07PM +0000, Timothy Ham wrote:
>
> > Hello,
> >
> > I have benefited from the wiki page "Simple ruleset for a home router" found here:
> >
> > https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_home_router
> >
> > Still, it doesn't cover port forwarding and hairpin NAT. So after several hours of trying different things, I've finally figured out how to do them, and improved the example config. Could someone in charge please update the example on the wiki? Specifically the example config given in the section "Simple router using ppp interface". I've kept everything the same except the few lines I've added.
> >
> > If my config can be improved further, please suggest more changes as well.
>
> I'm assuming nftables 1.0.0 for the comments below.

I'm actually using 0.9.8-3.1 on Debian 11. I hope there are no big differences.

>
> > Thank you.
> >
> > ==== begin ====
> >
> > flush ruleset
> >
> > define DEV_PRIVATE = eth1
> >
> > define DEV_WORLD = ppp0
> >
> > define NET_PRIVATE = 192.168.0.0/16
> >
> > define IP_WAN = x.x.x.x
> >
> > define IP_ROUTER = 192.168.0.1
> >
> > define IP_SERVER = 192.168.0.10
> >
> > table ip global {
> >
> > chain prerouting {
> >
> > type nat hook prerouting priority dstnat;
> >
> >           # Hairpin NAT part 1/2. WAN IP address must be known
> >           ip daddr $IP_WAN tcp dport 443 dnat to $IP_SERVER:8443
> >           ip daddr $IP_WAN tcp dport 80 dnat to $IP_SERVER:8080
> >
>
> Consolidate this rule using a map:
>
> ip daddr $IP_WAN dnat to tcp dport map { 443 : $IP_SERVER . 8443, 80 : $IP_SERVER . 8080 }
>
> >           # Port forwarding part 1/2
> >           iifname $DEV_WORLD tcp dport 443 dnat to $IP_SERVER:8443
> >           iifname $DEV_WORLD tcp dport 80 dnat to $IP_SERVER:8080
> >
>
> Is this rule is redundant? is $IP_WAN the IP address of $DEV_WORLD?

Yes, these rules are functional duplicates. However....

If one is only using port forwarding, the second set of rules are much more preferable because the IP address of the WAN does not need to be specified. However, when using hairpin NAT, the WAN's IP address must be specified, as far as I know. So with this configuration, I need an external script to update the $WAN_IP whenever that changes. If there is a way to do this in nftables without knowing the $WAN_IP for hairpin NAT, that would be great. I was able to get OpenBSD PF to perform hairpin NAT without specifying the WAN_IP in the config file, but have been unable to do it with nftables.

Anyways, I thought most people would use this example script as a template, and comment out what they don't need. As such, since hairpin NAT is not that common, I wanted to present it in a way that a novice can comment out independent bits.

> >     }
> >
> >     chain inbound_world {
> >         # accepting ping (icmp-echo-request) for diagnostic purposes.
> >         # However, it also lets probes discover this host is alive.
> >         # This sample accepts them within a certain rate limit:
> >         #
> >         # icmp type echo-request limit rate 5/second accept
> >
> >         # allow SSH connections from some well-known internet host
> >         ip saddr 81.209.165.42 tcp dport ssh accept
> >     }
> >
> >     chain inbound_private {
> >         # accepting ping (icmp-echo-request) for diagnostic purposes.
> >         icmp type echo-request limit rate 5/second accept
> >
> >         # allow DHCP, DNS and SSH from the private network
> >         ip protocol . th dport vmap { tcp . 22 : accept, udp . 53 : accept, tcp . 53 : accept, udp . 67 : accept}
> >     }
> >
> >     chain inbound {
> >         type filter hook input priority 0; policy drop;
> >
> >         # Allow traffic from established and related packets, drop invalid
> >         ct state vmap { established : accept, related : accept, invalid : drop }
> >
> >         # allow loopback traffic, anything else jump to chain for further evaluation
> >         iifname vmap { lo : accept, $DEV_WORLD : jump inbound_world, $DEV_PRIVATE : jump inbound_private }
> >
> >         # the rest is dropped by the above policy
> >     }
> >
> >     chain forward {
> >         type filter hook forward priority 0; policy drop;
> >
>
> You could move these rules below the verdict map...
>
> >         # port forwarding part 2/2
> >         meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8443 ct state new accept
> >         meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport 8080 ct state new accept
> >
> >         # Allow traffic from established and related packets, drop invalid
> >         ct state vmap { established : accept, related : accept, invalid : drop }
> >
>
> ... 'ct state new,untracked' is implicitly assumed after vmap, so you
>
> could move (and consolidate) this rule:
>
> meta iifname $DEV_WORLD ip daddr $IP_SERVER tcp dport { 8443, 8080 } accept
>

Yes. Again, I wanted to be able for people to just comment out what they don't need for this template.

> >         # connections from the internal net to the internet or to other
> >         # internal nets are allowed
> >         iifname $DEV_PRIVATE accept
> >
> >         # the rest is dropped by the above policy
> >     }
> >
> >     chain postrouting {
> >         type nat hook postrouting priority 100; policy accept;
> >
> >         # hairpin part 2/2
> >         ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8443 snat $ROUTER
> >         ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport 8080 snat $ROUTER
> >
>
> Consolidate rule:
>
> ip saddr $NET_PRIVATE ip daddr $IP_SERVER tcp dport { 8080, 8443 } snat $ROUTER
>
> however, if the routing table allows to reach your $IP_SERVER from
>
> $NET_PRIVATE, then you do not need this NAT rule.

Actually, hairpin NAT does not work without this rule. I believe the reason is that the reply packet from the $IP_SERVER would have $ROUTER as its daddr, and that packet needs to be NATed back to the requesting client inside $NET_PRIVATE.

> >         # masquerade private IP addresses
> >         ip saddr $NET_PRIVATE oifname $DEV_WORLD masquerade
> >     }
> >
> >
> > }
> >
> > ==== end ====

Thank you much for your feedback!

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-11-03  7:16 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-30 21:39 Improvements to the Home Router Wiki page Timothy Ham
2021-11-02 11:31 ` Pablo Neira Ayuso
2021-11-03  7:16   ` Timothy Ham

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.