DNSMasq: adding dynamically DNS server to query

Hello,

I'm configuring an IPSEC client with StrongSwan.
When connecting the client, StrongSwan edits /etc/resolv.conf to add the preferred DNS server of the VPN server.

nameserver fd0f:ee:b0::1   # by strongSwan
nameserver 212.27.38.253   # by strongSwan
search lan
nameserver 127.0.0.1
nameserver ::1

however /tmp/resolv.conf.d/resolv.conf.auto is not changed
and the result is that dnsmasq cannot relay the DNS query (strangely it does not even try, as I understand netfilter's traces - no request is relayed at all)

# Interface wan6
nameserver fd17:b4b5:1c96::1
# Interface wan
nameserver 192.168.10.1
search lan

I can resolve names perfectly from the router,or from the client if I set the DNS manually to one of the two proposed by IPSEC.

when I edit this file to add the VPN nameserver

nameserver fd0f:ee:b0::1
nameserver 212.27.38.253

and restart dnsmasq
the client can then query via the router DNSMasq...
but after a short time, the config is reverted and the client cannot query anymore...

I've seen how to configure DNS servers statically in Luci, but here it's dynamically pulled from the IPSEC server.
Is there a clean way to add/replace DNS server that DNSMasq query, from a script ?
I have an updown script that could do the job...

I've seen in LUCI the "DHCP and DNS""resolv and hosts file">Resolv file" property that set the /tmp/resolv.conf.d/resolv.conf.auto filename, but I need it to have the defaul values when IPSEC is not started?

I do not use Strongswan but the same problem emerges when using OpenVPN or Wireguard.
For some DNS background see my personal notes: https://github.com/egc112/OpenWRT-egc-add-on/blob/main/stop-dns-leak/README.md

I have scripts for OpenVPN and WireGuard to deal with this: https://github.com/egc112/OpenWRT-egc-add-on/tree/main/stop-dns-leak

The OpenVPN script is specific for OpenVPN, but the WireGuard script is using ifup/ifdown.
Maybe that gives you some inspiration to make a strongswan script

1 Like

Maybe this can help.
https://docs.strongswan.org/docs/5.9/plugins/resolve.html

4 Likes

It worked,

I had to edit the plugin file
(editing the swanctl config does not work)

/etc/strongswan.d/charon/resolve.conf

resolve {
# File where to add DNS server entries if not using resolvconf(8).
# file = /etc/resolv.conf
file = /tmp/swanctl/resolv-swanctl.conf
# Whether to load the plugin. Can also be an integer to increase the
# priority of this plugin.
load = yes
resolvconf {
# Interface name/protocol sent to resolvconf(8).
# iface = lo.ipsec
# Path/command for resolvconf(8).
# path = /sbin/resolvconf
}
}

In fact the /etc/resolv.conf was not to edit, Strongwan dit it erroneouly. It typically calls dnsmasq, so the file to edit is the one of dnsmasq:/tmp/resolv.conf.d/resolv.conf.auto .

I had to enrich my ipsec.user which already was adding nft rules for SNAT
(see IPSEC with StrongSwan/swanctl to connect to home freebox IPSEC )

it parses the generated /tmp/swanctl/resolv-swanctl.confand add each server at the beginning of /tmp/resolv.conf.d/resolv.conf.auto , if not already in .

finally I generated a new file, because without any call to the iface or dhcp hotplug, the resolv.conf.auto is modified
I change the DNS "Resolv file" via Luci to resolv.conf.merge and my script merge resolv.conf.auto with the resolve-swanctl.conf ...

Too bad, I did not find the kind of hotplug that was called

/etc/ipsec.user

NFTDIR=/tmp/swanctl
DNAT_IPSEC_CHAIN="srcnat_ipsec_${PLUTO_CONNECTION}"
DNAT_IPSEC6_CHAIN="srcnat_ipsec6_${PLUTO_CONNECTION}"
NFTFILE="${NFTDIR}/client-${PLUTO_CONNECTION}.nft"
NFTFILE6="${NFTDIR}/client6-${PLUTO_CONNECTION}.nft"

save_resolv() {
auto_resolv=/tmp/resolv.conf.d/resolv.conf.auto
merge_resolv=/tmp/resolv.conf.d/resolv.conf.merge
ipsec_resolv=/tmp/swanctl/resolv-swanctl.conf

ADDED=""
if test -r $ipsec_resolv ; then 
	ADDED=$(awk '/^nameserver/ { print $2;}'<$ipsec_resolv)
fi

if test -n "$ADDED" ; then
	(
		for s in $ADDED ; do 
			grep -qF "nameserver $s" <$auto_resolv ||echo "nameserver $s"
		done
		cat $auto_resolv
	) > $merge_resolv
	cat $merge_resolv>$auto_resolv 
else 
	cat $auto_resolv>$merge_resolv
fi

}

case "$PLUTO_VERB:$1" in
up-host:)
# connection to me coming up
# If you are doing a custom version, firewall commands go here.
;;
down-host:)
# connection to me going down
# If you are doing a custom version, firewall commands go here.
;;
up-client:)
# connection to my client subnet coming up
# If you are doing a custom version, firewall commands go here.
/usr/bin/logger -t ipsecuser "$PLUTO_VERB": "${PLUTO_CONNECTION} ${PLUTO_MY_SOURCEIP4_1} ${PLUTO_INTERFACE}"
cat >"${NFTFILE}" <<EOF
chain ${DNAT_IPSEC_CHAIN} {
type nat hook postrouting priority srcnat; policy accept;
oifname ${PLUTO_INTERFACE} meta nfproto ipv4 meta mark & 0x00080000 != 0x00080000 counter snat ip to ${PLUTO_MY_SOURCEIP4_1}
}
EOF

#nft flush chain inet fw4 ${DNAT_IPSEC_CHAIN}
nft delete chain inet fw4 ${DNAT_IPSEC_CHAIN} 2>/dev/null
nft add chain inet fw4  ${DNAT_IPSEC_CHAIN} "{type nat hook postrouting priority srcnat; policy accept;}"
nft add rule inet fw4  ${DNAT_IPSEC_CHAIN} " oifname ${PLUTO_INTERFACE}  meta nfproto ipv4 meta mark & 0x00080000 != 0x00080000 counter snat ip to ${PLUTO_MY_SOURCEIP4_1}"
save_resolv
;;

down-client:)
# connection to my client subnet going down
# If you are doing a custom version, firewall commands go here.
/usr/bin/logger -t ipsecuser "$PLUTO_VERB": "${PLUTO_CONNECTION} ${PLUTO_MY_SOURCEIP4_1} ${PLUTO_INTERFACE}"
nft delete chain inet fw4 "${DNAT_IPSEC_CHAIN}"
rm "${NFTFILE}"
save_resolv
;;

IPv6

up-host-v6:)
# connection to me coming up
# If you are doing a custom version, firewall commands go here.
;;
down-host-v6:)
# connection to me going down
# If you are doing a custom version, firewall commands go here.
;;
up-client-v6:)
# connection to my client subnet coming up
# If you are doing a custom version, firewall commands go here.
/usr/bin/logger -t ipsecuser "$PLUTO_VERB": "${PLUTO_CONNECTION} ${PLUTO_MY_SOURCEIP6_1} ${PLUTO_INTERFACE}"
cat >"${NFTFILE6}" <<EOF
chain ${DNAT_IPSEC6_CHAIN} {
type nat hook postrouting priority srcnat; policy accept;
oifname ${PLUTO_INTERFACE} meta nfproto ipv6 meta mark & 0x00080000 != 0x00080000 counter snat ip6 to ${PLUTO_MY_SOURCEIP6_1}
}
EOF

#nft flush chain inet fw4 ${DNAT_IPSEC6_CHAIN}
nft delete chain inet fw4 ${DNAT_IPSEC6_CHAIN} 2>/dev/null
nft add chain inet fw4  ${DNAT_IPSEC6_CHAIN} "{type nat hook postrouting priority srcnat; policy accept;}"
nft add rule inet fw4  ${DNAT_IPSEC6_CHAIN} " oifname ${PLUTO_INTERFACE}  meta nfproto ipv6 meta mark & 0x00080000 != 0x00080000 counter snat ip6 to ${PLUTO_MY_SOURCEIP6_1}"
save_resolv
;;

down-client-v6:)
# connection to my client subnet going down
# If you are doing a custom version, firewall commands go here.
/usr/bin/logger -t ipsecuser "$PLUTO_VERB": "${PLUTO_CONNECTION} ${PLUTO_MY_SOURCEIP6_1} ${PLUTO_INTERFACE}"
nft delete chain inet fw4 "${DNAT_IPSEC6_CHAIN}"
rm "${NFTFILE6}"
save_resolv
;;
*) echo "$0: unknown verb `$PLUTO_VERB' or parameter `$1'" >&2
exit 1
;;

esac

To react to change in DNS serveurs on the WAN&all, I created 2 hotplugs on iface and dhcp

/etc/hotplug.d/iface/99-resolvfile-merge

save_resolv() {
auto_resolv=/tmp/resolv.conf.d/resolv.conf.auto
merge_resolv=/tmp/resolv.conf.d/resolv.conf.merge
ipsec_resolv=/tmp/swanctl/resolv-swanctl.conf

ADDED=""
if test -r $ipsec_resolv ; then 
	ADDED=$(awk '/^nameserver/ { print $2;}'<$ipsec_resolv)
fi

if test -n "$ADDED" ; then
	(
		for s in $ADDED ; do 
			grep -qF "nameserver $s" <$auto_resolv ||echo "nameserver $s"
		done
		cat $auto_resolv
	) > $merge_resolv
	cat $merge_resolv>$auto_resolv 
else 
	cat $auto_resolv>$merge_resolv
fi

}

save_resolv

I used the same script for DHCP hotplug
/etc/hotplug.d/dhcp/99-resolvfile-merge

and evenNET hotplug
/etc/hotplug.d/net/99-resolvfile-merge
(what is it for ?)

My impression is that Strongwan is not yet well integrated in OpenWRT...
The UCI support is incomplete and obsolete, the basic configuration is not working .
I'l not enough competent to make something clean as needed for a release, but it's working...

Hope this helps...

1 Like

There is a mystery I don't understand,
when the link have some state change, no hotplug is called, and the resolv.conf.auto is modified, and then dnsmasq reload it...

Hopefully it now use the old file resolv.conf.merge, but if the DNS server change because of those network event, the DNS will not change.

I have this trace with the time matchin the modification of the file

Sun May 12 16:39:05 2024 daemon.info ipsec: 03[KNL] flags changed for fd17:b4b5:1c96:0:9683:c4ff:fe49:fd4 on phy0-sta0
Sun May 12 16:39:05 2024 daemon.info dnsmasq[1]: reading /tmp/resolv.conf.d/resolv.conf.merge
Sun May 12 16:39:05 2024 daemon.info dnsmasq[1]: using nameserver fd17:b4b5:1c96::1#53
...

It's not far

I improved the script...
In fact if there is no IPSEC, the change in
/tmp/resolv.conf.d/resolv.conf.auto
are very important, so I create a symbolic link to this dynamic file from
/tmp/resolv.conf.d/resolv.conf.merge
that is cited in the configuration...
When IPSEC client start up, some entries are created in
/tmp/swanctl/resolv-swanctl.conf
and thus I merge the two files, and if the resolv.conf.auto is changed silently, it is not so serious. the IPSEC DNS entries have priority, and whan the IPSEC came up, the DNS of the WAN was working correctly ...
It's not perfect, but not too risky.

If you have better Ideas...

code to maintain /tmp/resolv.conf.d/resolv.conf.merge

save_resolv() {
auto_resolv=/tmp/resolv.conf.d/resolv.conf.auto
merge_resolv=/tmp/resolv.conf.d/resolv.conf.merge
ipsec_resolv=/tmp/swanctl/resolv-swanctl.conf

ADDED=""
if test -r $ipsec_resolv ; then 
	ADDED=$(awk '/^nameserver/ { print $2;}'<$ipsec_resolv)
fi

if test -n "$ADDED" ; then
	rm -f $merge_resolv
	(
		for s in $ADDED ; do 
			grep -qF "nameserver $s" <$auto_resolv ||echo "nameserver $s"
		done
		cat $auto_resolv
	) > $merge_resolv
	cat $merge_resolv>$auto_resolv 
else 
	rm -f $merge_resolv
	ln -s $auto_resolv $merge_resolv
fi

}

save_resolv

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.