Linux Netfilter discussions
 help / color / mirror / Atom feed
* 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