* Simplifying DNAT Rules using Maps
@ 2020-06-03 16:08 Max Ehrlich
2020-06-07 21:40 ` Pablo Neira Ayuso
0 siblings, 1 reply; 5+ messages in thread
From: Max Ehrlich @ 2020-06-03 16:08 UTC (permalink / raw)
To: netfilter
Hi,
I'm switching from iptables to nftables, specifically from a high
level translator (awall) to using nftables directly since the
scripting environment is so expressive.
I have quite a few ipv4 DNAT rules that I need to translate, and they
all have a similar form like the following for a web service:
table ip nat {
chain prerouting {
ip daddr != 10.0.0.0/8 fib daddr type local tcp dport http dnat
10.1.1.112:8080
}
chain postrouting {
ip saddr 10.0.0.0/8 ip daddr 10.1.1.112 tcp dport 8080 masquerade
}
}
table ip filter {
chain forward {
ip daddr 10.1.1.112 tcp dport 8080 accept
}
}
I want to simplify this using a map so that I can add services to the
map instead of having to copy all three rules every time. Something
like this
table ip nat {
map dnat_services {
type inet_service: ipv4_addr . inet_service
elements = {
http: 10.1.1.112 . 8080
}
}
chain prerouting {
ip daddr != 10.0.0.0/8 fib daddr type local dnat tcp dport map
@dnat_services
}
...
would be great but it seems like the dnat target doesnt accept
concatenations. I get that this can be done with two maps but it makes
it quite ugly to write although there are performance benefits. Also I
have no idea what to do about the filter and masquerade rules. For
example
chain postrouting {
ip saddr 10.0.0.0/8 ip daddr tcp dport map @dnat_services masquerade
}
doesn't parse (my assumption was this would have been that the ip
daddr would be the result of looking up the tcp dport in the given
map, it matches the dnat syntax)
So is there a cleaner way to write these rules using maps?
thanks,
Max
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Simplifying DNAT Rules using Maps
2020-06-03 16:08 Simplifying DNAT Rules using Maps Max Ehrlich
@ 2020-06-07 21:40 ` Pablo Neira Ayuso
2020-06-08 13:42 ` Max Ehrlich
0 siblings, 1 reply; 5+ messages in thread
From: Pablo Neira Ayuso @ 2020-06-07 21:40 UTC (permalink / raw)
To: Max Ehrlich; +Cc: netfilter
On Wed, Jun 03, 2020 at 12:08:50PM -0400, Max Ehrlich wrote:
> Hi,
>
> I'm switching from iptables to nftables, specifically from a high
> level translator (awall) to using nftables directly since the
> scripting environment is so expressive.
>
> I have quite a few ipv4 DNAT rules that I need to translate, and they
> all have a similar form like the following for a web service:
>
> table ip nat {
> chain prerouting {
> ip daddr != 10.0.0.0/8 fib daddr type local tcp dport http dnat
> 10.1.1.112:8080
> }
>
> chain postrouting {
> ip saddr 10.0.0.0/8 ip daddr 10.1.1.112 tcp dport 8080 masquerade
> }
> }
>
> table ip filter {
> chain forward {
> ip daddr 10.1.1.112 tcp dport 8080 accept
> }
> }
>
> I want to simplify this using a map so that I can add services to the
> map instead of having to copy all three rules every time. Something
> like this
>
> table ip nat {
> map dnat_services {
> type inet_service: ipv4_addr . inet_service
> elements = {
> http: 10.1.1.112 . 8080
> }
> }
>
> chain prerouting {
> ip daddr != 10.0.0.0/8 fib daddr type local dnat tcp dport map
> @dnat_services
> }
> ...
>
> would be great but it seems like the dnat target doesnt accept
> concatenations. I get that this can be done with two maps but it makes
> it quite ugly to write although there are performance benefits. Also I
> have no idea what to do about the filter and masquerade rules. For
> example
>
> chain postrouting {
> ip saddr 10.0.0.0/8 ip daddr tcp dport map @dnat_services masquerade
> }
>
> doesn't parse (my assumption was this would have been that the ip
> daddr would be the result of looking up the tcp dport in the given
> map, it matches the dnat syntax)
>
> So is there a cleaner way to write these rules using maps?
This is supported since nftables >= 0.9.4
# cat ruleset.nft
table ip nat {
map destinations {
type ipv4_addr . inet_service : ipv4_addr . inet_service
}
chain f {
type nat hook postrouting priority srcnat; policy accept;
snat ip addr . port to ip daddr . tcp dport map @destinations
}
}
# nft -f ruleset.nft
Then, you can add elements to the `destinations' map that contains the
mapping.
nft add element ip nat destinations { 1.1.1.1 . 80 : 2.2.2.2 . 443 }
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Simplifying DNAT Rules using Maps
2020-06-07 21:40 ` Pablo Neira Ayuso
@ 2020-06-08 13:42 ` Max Ehrlich
2020-06-08 14:13 ` Max Ehrlich
0 siblings, 1 reply; 5+ messages in thread
From: Max Ehrlich @ 2020-06-08 13:42 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter
Thanks a lot Pablo this is working great for the dnat rule:
```
ip daddr != 10.0.0.0/8 fib daddr type local dnat ip addr . port to tcp
dport map @dnat_destinations
```
Do you know how I can use that map to write the masquerade rule as
well? And I'm assuming I'll need another rule for the filter table
rule since I can't reference a map in another table correct?
On Sun, Jun 7, 2020 at 5:40 PM Pablo Neira Ayuso <pablo@netfilter.org> wrote:
>
> On Wed, Jun 03, 2020 at 12:08:50PM -0400, Max Ehrlich wrote:
> > Hi,
> >
> > I'm switching from iptables to nftables, specifically from a high
> > level translator (awall) to using nftables directly since the
> > scripting environment is so expressive.
> >
> > I have quite a few ipv4 DNAT rules that I need to translate, and they
> > all have a similar form like the following for a web service:
> >
> > table ip nat {
> > chain prerouting {
> > ip daddr != 10.0.0.0/8 fib daddr type local tcp dport http dnat
> > 10.1.1.112:8080
> > }
> >
> > chain postrouting {
> > ip saddr 10.0.0.0/8 ip daddr 10.1.1.112 tcp dport 8080 masquerade
> > }
> > }
> >
> > table ip filter {
> > chain forward {
> > ip daddr 10.1.1.112 tcp dport 8080 accept
> > }
> > }
> >
> > I want to simplify this using a map so that I can add services to the
> > map instead of having to copy all three rules every time. Something
> > like this
> >
> > table ip nat {
> > map dnat_services {
> > type inet_service: ipv4_addr . inet_service
> > elements = {
> > http: 10.1.1.112 . 8080
> > }
> > }
> >
> > chain prerouting {
> > ip daddr != 10.0.0.0/8 fib daddr type local dnat tcp dport map
> > @dnat_services
> > }
> > ...
> >
> > would be great but it seems like the dnat target doesnt accept
> > concatenations. I get that this can be done with two maps but it makes
> > it quite ugly to write although there are performance benefits. Also I
> > have no idea what to do about the filter and masquerade rules. For
> > example
> >
> > chain postrouting {
> > ip saddr 10.0.0.0/8 ip daddr tcp dport map @dnat_services masquerade
> > }
> >
> > doesn't parse (my assumption was this would have been that the ip
> > daddr would be the result of looking up the tcp dport in the given
> > map, it matches the dnat syntax)
> >
> > So is there a cleaner way to write these rules using maps?
>
> This is supported since nftables >= 0.9.4
>
> # cat ruleset.nft
> table ip nat {
> map destinations {
> type ipv4_addr . inet_service : ipv4_addr . inet_service
> }
>
> chain f {
> type nat hook postrouting priority srcnat; policy accept;
> snat ip addr . port to ip daddr . tcp dport map @destinations
> }
> }
> # nft -f ruleset.nft
>
> Then, you can add elements to the `destinations' map that contains the
> mapping.
>
> nft add element ip nat destinations { 1.1.1.1 . 80 : 2.2.2.2 . 443 }
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Simplifying DNAT Rules using Maps
2020-06-08 13:42 ` Max Ehrlich
@ 2020-06-08 14:13 ` Max Ehrlich
2020-06-11 6:43 ` Trent W. Buck
0 siblings, 1 reply; 5+ messages in thread
From: Max Ehrlich @ 2020-06-08 14:13 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter
Just to put some more context, I was able to do this using a map and a
set as follows:
```
define dnat_targets = {
80 : 10.0.10.1 . 8080,
25565 : 10.0.10.8 . 25565
}
define dnat_allowed = {
10.0.10.1 . 8080,
10.0.10.8 . 25565
}
table inet nat {
map dnat_destinations {
type inet_service : ipv4_addr . inet_service
elements = $dnat_targets
}
set dnat_masq {
type ipv4_addr . inet_service
elements = $dnat_allowed
}
chain prerouting {
ip daddr != 10.0.0.0/8 fib daddr type local dnat ip addr . port to tcp
dport map @dnat_destinations
}
chain postrouting {
ip saddr 10.0.0.0/8 ip daddr . tcp dport @dnat_masq masquerade
}
}
table inet filter {
set dnat_allowed {
type ipv4_addr . inet_service
elements = $dnat_allowed
}
chain forward {
ip daddr . tcp dport @dnat_allowed accept
}
}
```
however note that values of the map `dnat_targets` is the same as the
set `dnat_allowed`, I wonder if there is a way to do this with only
the map `dnat_targets`? Something like using only the values of the
map as a set?
On Mon, Jun 8, 2020 at 9:42 AM Max Ehrlich <max.ehr@gmail.com> wrote:
>
> Thanks a lot Pablo this is working great for the dnat rule:
>
> ```
> ip daddr != 10.0.0.0/8 fib daddr type local dnat ip addr . port to tcp
> dport map @dnat_destinations
> ```
>
> Do you know how I can use that map to write the masquerade rule as
> well? And I'm assuming I'll need another rule for the filter table
> rule since I can't reference a map in another table correct?
>
> On Sun, Jun 7, 2020 at 5:40 PM Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> >
> > On Wed, Jun 03, 2020 at 12:08:50PM -0400, Max Ehrlich wrote:
> > > Hi,
> > >
> > > I'm switching from iptables to nftables, specifically from a high
> > > level translator (awall) to using nftables directly since the
> > > scripting environment is so expressive.
> > >
> > > I have quite a few ipv4 DNAT rules that I need to translate, and they
> > > all have a similar form like the following for a web service:
> > >
> > > table ip nat {
> > > chain prerouting {
> > > ip daddr != 10.0.0.0/8 fib daddr type local tcp dport http dnat
> > > 10.1.1.112:8080
> > > }
> > >
> > > chain postrouting {
> > > ip saddr 10.0.0.0/8 ip daddr 10.1.1.112 tcp dport 8080 masquerade
> > > }
> > > }
> > >
> > > table ip filter {
> > > chain forward {
> > > ip daddr 10.1.1.112 tcp dport 8080 accept
> > > }
> > > }
> > >
> > > I want to simplify this using a map so that I can add services to the
> > > map instead of having to copy all three rules every time. Something
> > > like this
> > >
> > > table ip nat {
> > > map dnat_services {
> > > type inet_service: ipv4_addr . inet_service
> > > elements = {
> > > http: 10.1.1.112 . 8080
> > > }
> > > }
> > >
> > > chain prerouting {
> > > ip daddr != 10.0.0.0/8 fib daddr type local dnat tcp dport map
> > > @dnat_services
> > > }
> > > ...
> > >
> > > would be great but it seems like the dnat target doesnt accept
> > > concatenations. I get that this can be done with two maps but it makes
> > > it quite ugly to write although there are performance benefits. Also I
> > > have no idea what to do about the filter and masquerade rules. For
> > > example
> > >
> > > chain postrouting {
> > > ip saddr 10.0.0.0/8 ip daddr tcp dport map @dnat_services masquerade
> > > }
> > >
> > > doesn't parse (my assumption was this would have been that the ip
> > > daddr would be the result of looking up the tcp dport in the given
> > > map, it matches the dnat syntax)
> > >
> > > So is there a cleaner way to write these rules using maps?
> >
> > This is supported since nftables >= 0.9.4
> >
> > # cat ruleset.nft
> > table ip nat {
> > map destinations {
> > type ipv4_addr . inet_service : ipv4_addr . inet_service
> > }
> >
> > chain f {
> > type nat hook postrouting priority srcnat; policy accept;
> > snat ip addr . port to ip daddr . tcp dport map @destinations
> > }
> > }
> > # nft -f ruleset.nft
> >
> > Then, you can add elements to the `destinations' map that contains the
> > mapping.
> >
> > nft add element ip nat destinations { 1.1.1.1 . 80 : 2.2.2.2 . 443 }
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Simplifying DNAT Rules using Maps
2020-06-08 14:13 ` Max Ehrlich
@ 2020-06-11 6:43 ` Trent W. Buck
0 siblings, 0 replies; 5+ messages in thread
From: Trent W. Buck @ 2020-06-11 6:43 UTC (permalink / raw)
To: netfilter
[-- Attachment #1: Type: text/plain, Size: 1124 bytes --]
Max Ehrlich <max.ehr@gmail.com> writes:
> Just to put some more context, I was able to do this using a map and a
> set as follows:
>
> ```
> define dnat_targets = {
> 80 : 10.0.10.1 . 8080,
> 25565 : 10.0.10.8 . 25565
> }
>
> define dnat_allowed = {
> 10.0.10.1 . 8080,
> 10.0.10.8 . 25565
> }
>
> [...]
>
> table inet filter {
> set dnat_allowed {
> type ipv4_addr . inet_service
> elements = $dnat_allowed
> }
>
> chain forward {
> ip daddr . tcp dport @dnat_allowed accept
> }
> }
> ```
>
> however note that values of the map `dnat_targets` is the same as the
> set `dnat_allowed`, I wonder if there is a way to do this with only
> the map `dnat_targets`? Something like using only the values of the
> map as a set?
FWIW in filter you can just say "allow anything I already DNATted":
# xtables, annoying explicit way
-A FORWARD -p tcp --dports http,https -d www -j ACCEPT
-A FORWARD -p tcp --dports imaps,submission -d mail -j ACCEPT
...
# xtables, easy way
-A FORWARD --ctstate DNAT -j ACCEPT
# nft, easy way
ct status dnat accept
A full ruleset might look like this (attached):
[-- Attachment #2: tmp.nft --]
[-- Type: text/plain, Size: 1500 bytes --]
#!/usr/sbin/nft --file
flush ruleset
table inet my_filter {
chain my_input {
type filter hook input priority filter
policy drop
jump my_prologue comment "deal with boring conntrack/loopback/ICMP/ICMPv6"
tcp dport ssh accept
jump my_epilogue
}
chain my_forward {
type filter hook forward priority filter
policy drop
jump my_prologue comment "deal with boring conntrack/loopback/ICMP/ICMPv6"
jump my_epilogue
}
chain my_prologue {
ct state vmap { established: accept, related: accept, invalid: drop }
ct status dnat accept
iiftype loopback accept
icmp type echo-request accept
icmpv6 type { echo-request, nd-neighbor-solicit } accept
}
chain my_epilogue {
iiftype != ppp reject comment "be polite (reject, not drop) to local networks"
}
}
table ip my_nat {
chain my_postrouting {
type nat hook postrouting priority srcnat
policy accept
oiftype ppp masquerade
}
chain my_prerouting {
type nat hook prerouting priority dstnat
policy accept
iiftype != ppp return comment "port forwards are only relevant from the internet"
define www.example.com = 127.1.2.3
define mail.example.com = 127.254.253.252
tcp dport { http, https } dnat to $www.example.com
tcp dport { smtp, submission, imaps } dnat to $mail.example.com
}
}
list ruleset
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2020-06-11 6:43 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-06-03 16:08 Simplifying DNAT Rules using Maps Max Ehrlich
2020-06-07 21:40 ` Pablo Neira Ayuso
2020-06-08 13:42 ` Max Ehrlich
2020-06-08 14:13 ` Max Ehrlich
2020-06-11 6:43 ` Trent W. Buck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox