Using ipset to allow traffic to specific domains only on an interface that is not allowed WAN access

Is ipset the right tool to allow traffic to a specific set of domains on an interface that does not have WAN access by default?

Here is the relevant setup in /etc/config/firewall:

...
config zone
  option name 'iot'
  option input 'REJECT'
  option output 'ACCEPT'
  option forward 'REJECT'
  list network 'iot'

config rule
  option src 'iot'
  option target 'ACCEPT'
  list proto 'tcp'
  list proto 'udp'
  option dest_port '53 67 68'
  option name 'iot dhcp dns'
  option family 'ipv4'

config rule 'fwd_filter'
	option name 'Filter-IPset-DNS-Forward'
	option src 'iot'
	option dest 'wan'
	option family 'ipv4'
	option ipset 'allowtraffic'
	option target 'ACCEPT'
	list proto 'all'

The ipset definition is present:

# tail /etc/config/dhcp
...
config ipset
	list name 'allowtraffic'
	list domain 'ipinfo.io'
	list domain 'pushx.reolink.com'
	option table_family 'ip'

But devices connected that the iot interface cannot connect to either of the domains defined in the ipset rule.

Where is the ipset definition in /etc/config/firewall?

You can also remove table_family in the dnsmasq dhcp config. It will be inet if you create the set in the fw4 table.

2 Likes

EDIT: Ah! I missed a step. Never ran ipset setup. I will do so and report back shortly.

I created this entry under from Luci: Network>DHCP and DNS>IP Sets, not by using the shell commands on the wiki. Are you certain it should be removed?

Yes, because if you create the set in table inet fw4, the dnsmasq family must also match 4#inet#fw4#allowtraffic.

1 Like

I removed that line from /etc/config/dhcp.

When I ran ipset setup, it errored out.

# ipset setup
ipset v7.17: No command specified: unknown argument setup
Try `ipset help' for more information.

What am I missing?

Are you running an up-to-date version of OpenWRT (with nftables rather than iptables)?

If so, then ipset is no longer a thing. You can either add the relevant definition to the firewall config by hand, or there should be a tab for 'IP Sets' on the firewall page in Luci.

1 Like

Yes, it is a snapshot I built today. I see the section under Network>Firewall>IP Sets. Is this where I define a reference to the config ipset I created in /etc/config/dhcp somehow?

# tail /etc/config/dhcp
...
config ipset
	list name 'allowtraffic'
	list domain 'ipinfo.io'
	list domain 'pushx.reolink.com'

Here is the dialog under Network>Firewall>IP Sets>Add

Yes. As I understand it the two are referenced by name so that looks fine.

Leave the IPs/Networks/MACs blank. That adds static values to the set.

And Packet Field Match should be dest_ip.

If you needed to cover both IPv4 and IPv6 addresses then (afaik) you'd need to add another IP set in the firewall, but the entry in the /etc/config/dhcp can cover both by adding another list name line.

1 Like

I have it setup but am unable to connect to the domain ipinfo.io I setup in /etc/config/dhcp.

Is there some obvious flaw in these entries?

# tail /etc/config/dhcp
...
config ipset
	list name 'allowtraffic'
	list domain 'ipinfo.io'
	list domain 'pushx.reolink.com'

And

# tail /etc/config/firewall
...
config rule 'fwd_filter'
	option name 'Filter-IPset-DNS-Forward'
	option src 'iot'
	option dest 'wan'
	option family 'ipv4'
	option target 'ACCEPT'
	list proto 'tcp'
	option dest_port '80 443'
	option ipset 'allowtraffic'

config ipset
	option name 'allowtraffic'
	option family 'ipv4'
	list match 'dest_ip'

Is the client in the iot zone using the router’s dnsmasq as DNS?

Is the set populating after DNS queries?

nslookup ipinfo.io
nft list set inet fw4 allowtraffic

Yes. Calls to dig on the client work, but calls to curl do not.

On the client on the iot interafce:

% dig ipinfo.io     

; <<>> DiG 9.18.25 <<>> ipinfo.io
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6215
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ipinfo.io.			IN	A

;; ANSWER SECTION:
ipinfo.io.		60	IN	A	34.117.186.192

;; Query time: 52 msec
;; SERVER: 10.9.5.1#53(10.9.5.1) (UDP)
;; WHEN: Sun Apr 07 09:49:01 EDT 2024
;; MSG SIZE  rcvd: 54

But:

% curl https://ipinfo.io        
curl: (7) Failed to connect to ipinfo.io port 443 after 5 ms: Couldn't connect to server

See my edit above to check the set.

Yes, on the client:

% nslookup ipinfo.io
Server:		10.9.5.1
Address:	10.9.5.1#53

Non-authoritative answer:
Name:	ipinfo.io
Address: 34.117.186.192

And on the router:

# nft list set inet fw4 allowtraffic
table inet fw4 {
	set allowtraffic {
		type ipv4_addr
	}
}

How about grep nftset /var/etc/dnsmasq.conf*

There should be entries if dnsmasq is resolving ipinfo.io.

That returns a null...

Have you replaced dnsmasq with dnsmasq-full?

1 Like

head smack
I haven't ... I am building the image now with:

1 Like

In hindsight, this would have been a better article to start from.

https://openwrt.org/docs/guide-user/firewall/filtering_traffic_at_ip_addresses_by_dns

1 Like

I have dnsmasq-full now and that entry is present:

# grep nftset /var/etc/dnsmasq.conf*
nftset=/ipinfo.io/pushx.reolink.com/4#ip#fw4#allowtraffic

I am still unable to connect on the client though...

% curl https://ipinfo.io
curl: (7) Failed to connect to ipinfo.io port 443 after 4 ms: Couldn't connect to server

The ip needs to be inet, as discussed previously (table_family).