Thanks for your reply. I got it working with the following configuration:
config ipset
option name 'fwd-allow'
option match 'src_ip'
option enabled '1'
option loadfile '/root/fwd-allow.txt'
config redirect
option dest 'lan'
option target 'DNAT'
option name 'fwd'
option src 'wan'
option src_dport 'pppp'
option dest_ip 'zzz.zzz.zzz.zzz'
option dest_port 'qqqq'
config rule
option src 'wan'
option ipset '!fwd-whitelist'
option dest 'lan'
list dest_ip 'zzz.zzz.zzz.zzz'
option target 'REJECT'
option name 'fwd-reject'
However, I do not feel comfortable with that solution. If the REJECT rule gets deleted or disabled accidentally, the service gets exposed to the whole internet.
My previous solution was a custom rule:
config include
option type 'nftables'
option path '/root/chain-post-dstnat_wan.nft'
option position 'chain-post'
option chain 'dstnat_wan'
Content of /root/chain-post-dstnat_wan.nft:
meta nfproto ipv4 ip saddr @fwd-allow tcp dport pppp counter dnat ip to zzz.zzz.zzz.zzz:qqqq comment "fwd-with-ipset"
That way, a single rule contains both the allowed addresses and the forwarding definition. If it is lost, nothing is exposed. This would work perfectly fine, the only problem is the missing reflection which I wanted to solve with a custom entry in chain 'dstnat_lan', similar to the ones generated automatically:
ip saddr xxx.xxx.xxx.0/24 ip daddr yyy.yyy.yyy.yyy tcp dport pppp dnat ip to zzz.zzz.zzz.zzz:qqqq comment "!fw4: fwd (reflection)"
xxx.xxx.xxx -> LAN subnet
yyy.yyy.yyy.yyy -> WAN IP address
zzz.zzz.zzz.zzz -> LAN host IP address to forward to
pppp -> WAN facing port number
qqqq -> service port number on LAN host
As I have a dynamic WAN IP address, I do not know the value of yyy.yyy.yyy.yyy and was hoping there is some kind of a macro/variable containing the current WAN IP which could be used in the definition. Sadly, it seems there is no solution for that.
Thanks anyway for the idea!