Forcing all DNS traffic from the LAN to the firewall

Matthias Fischer matthias.fischer at ipfire.org
Mon Nov 23 09:08:14 UTC 2020


Hi,

to keep this discussion going, I'd like to publish some code that is
already running here and ask for some hints. As Jon used to say: "Please
don't hurt me". ;-)

I'm *not* sending this through patchwork - I strongly think this is not
ready yet. Opinions?

The following seems to work as I want, but I'm at a point where I know
what I want the code to do - but he/it has other thoughts. The last step
is not working yet.

As in my previous post I "Slightly shortened, kept the relevant parts",
current patches are based on 'next'.

On 15.11.2020 15:50, Michael Tremer wrote:
>>>> ....
>>>> This would probably go into /etc/init.d/firewall.

Done. Done right?

>> Sorry, but *which* line? I'm really not sure. I suppose somewhere after
>> line 179f which read:
>> ...
>> iptables -t nat -N CUSTOMPREROUTING
>> iptables -t nat -A PREROUTING -j CUSTOMPREROUTING
>> ...
>> 
>> I don't want to mess things up - especially in *this* script!
>> We need an "if"-query to check for ON/OFF there, ok.
>> But the more often I read this script the less sure I am where this code
>> can be inserted best. Where? Hints?
> 
> If we do not go with the generic redirection option, I would suggest to put this before the CAPTIVE_PORTAL chain and create another chain with the redirection rules.

I used REDIRECT, see below. Is it ok this way?

To get a start I first altered 'optionsfw.cgi', so that I got
'[DNS/NTP]_FORCED_ON_[INTERFACE] options in
''/var/ipfire/optionsfw/settings' (see: 01_force_dns_in_optionsfw.patch).

Here, it was important to me that the corresponding options are only
visible if the respective interface is actually available. So if there
is no BLUE interface, you don't see any ON/OFF switches for 'DNS/NTP on
BLUE' or BLUE logging options. Language strings were altered accordingly.

Screenshot examples:
=>
https://community.ipfire.org/t/forcing-all-dns-traffic-from-the-lan-to-the-firewall/3512/91

['Masquerading on BLUE' is not shown because screenshots were made on a
testmachine.]

Then I went to the next problem: how and where to activate?

I used '/etc/rc.d/init.d/firewall' with REDIRECT rules and placed them
just behind the CAPITVE_PORTAL_CHAIN, as Michael mentions. I hope, I got
the right place (see: 02_force_dns_firewall_init.patch).

To avoid creating duplicate rule entries, I used code like 'if !
iptables -t nat -C..." or 'if iptables -t nat -C..." ("Check for the
existence of a rule").
I wanted to be sure that a specific rule would only be created if it
doesn't exist. To reduce output noise I added '>/dev/null 2>&1', where
necessary. Opinions?

All this seemed to work. Manually testing was ok. If I delete just one
rule manually, only the missing rule will be created, I experienced no
duplicates. ON/OFF switches worked as expected. But still I have to do
the necessary firewall restart in a console session. Hm.

Ok, up to next problem.

Restarting the firewall after making changes:
How can I initiate *restarting* the firewall through 'optionsfw.cgi'!?
This implements initiating '/etc/rc.d/init.d/firewall restart' and
starting 'iptables' and this seems to be a point where I'm stuck. Or do
I miss something!?

Like in 'squid', I wanted two buttons: 'Save' and 'Save and restart'.

I found a solution how to code this - perhaps not very professional -
really simple, but works. Nearly.
(see: 03_firewall_restart_in_optionsfw_cgi.patch

In this patch I'm trying to restart the firewall by adding:
...
system("/etc/rc.d/init.d/firewall restart >/dev/null 2>&1 ");
...

Doesn't work.

I tried adding a new prog 'optionsfwctrl' (see: 04_optionsfwctrl.c).
Doesn't work either.

I checked rights ("set user id on execution") and played with 'C' (try
and error), but no chance. I think, I'm missing something important.

Current situation:
If I run 'optionsfwctrl on a root console, it works. Settings are saved.
But the firewall restart does not work. I'm not able to initiate
'/etc/rc.d/init.d/firewall restart' through the web GUI. DNS/NTP rules
won't be applied.

So I got my two buttons, but only ONE is working. The other is only
saving, but doesn't restart.

>> Besides, deactivating these rules would need a complete reboot!? Or do I
>> overlook something?
> 
> Yes, this would be true.
> 
> We could otherwise create a extra script that is only executed when this is enabled like we do with the captive portal.

Tried this(?), but haven't found the right way yet. How to do?

>> Because if this should be the case then on the firewall options page the
>> entries that require a restart should be *marked* to make things easier
>> and more clearly. Otherwise you switch ON <-> OFF or vice versa without
>> *really* realising that your changes "need a reboot". The notice "Some
>> options need a reboot to take effect" is not sufficiently meaningful.
>> "Some options..."!? Which?
> 
> Yes, I find this quite annoying…
> 
> Maybe we should in general move these things to not require a reboot?
> 
> I believe reloading the whole firewall is something we can support right now.

I think, I need a 'restart'. Right? How can this be done?

Please correct me if I'm totally wrong with these patches (but it was
fun coding this...).

Best,
Matthias
-------------- next part --------------
diff -U 3 C:/Compare/force dns/next/html_cgi-bin_optionsfw.cgi C:/Compare/force dns/tuned 01/optionsfw.cgi
--- C:/Compare/force dns/next/html_cgi-bin_optionsfw.cgi	Mon Nov 23 08:52:12 2020
+++ C:/Compare/force dns/tuned 01/optionsfw.cgi	Sun Nov  8 20:17:54 2020
@@ -158,6 +158,18 @@
 $selected{'MASQUERADE_BLUE'}{'off'} = '';
 $selected{'MASQUERADE_BLUE'}{'on'} = '';
 $selected{'MASQUERADE_BLUE'}{$settings{'MASQUERADE_BLUE'}} = 'selected="selected"';
+$checked{'DNS_FORCE_ON_GREEN'}{'off'} = '';
+$checked{'DNS_FORCE_ON_GREEN'}{'on'} = '';
+$checked{'DNS_FORCE_ON_GREEN'}{$settings{'DNS_FORCE_ON_GREEN'}} = "checked='checked'";
+$checked{'DNS_FORCE_ON_BLUE'}{'off'} = '';
+$checked{'DNS_FORCE_ON_BLUE'}{'on'} = '';
+$checked{'DNS_FORCE_ON_BLUE'}{$settings{'DNS_FORCE_ON_BLUE'}} = "checked='checked'";
+$checked{'NTP_FORCE_ON_GREEN'}{'off'} = '';
+$checked{'NTP_FORCE_ON_GREEN'}{'on'} = '';
+$checked{'NTP_FORCE_ON_GREEN'}{$settings{'NTP_FORCE_ON_GREEN'}} = "checked='checked'";
+$checked{'NTP_FORCE_ON_BLUE'}{'off'} = '';
+$checked{'NTP_FORCE_ON_BLUE'}{'on'} = '';
+$checked{'NTP_FORCE_ON_BLUE'}{$settings{'NTP_FORCE_ON_BLUE'}} = "checked='checked'";
 
 &Header::openbox('100%', 'center',);
 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
@@ -207,7 +219,38 @@
 END
 	}
 
-	print <<END
+print <<END;
+	<table width='95%' cellspacing='0'>
+		<tr bgcolor='$color{'color20'}'></tr>
+		<tr> </tr>
+			<td colspan='2' align='left'><b>$Lang::tr{'fw green'}</b></td>
+		</tr>
+		<tr><td align='left' width='60%'>$Lang::tr{'dns force on green'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DNS_FORCE_ON_GREEN' value='on' $checked{'DNS_FORCE_ON_GREEN'}{'on'} />/
+																						<input type='radio' name='DNS_FORCE_ON_GREEN' value='off' $checked{'DNS_FORCE_ON_GREEN'}{'off'} /> $Lang::tr{'off'}</td></tr>
+		<tr><td align='left' width='60%'>$Lang::tr{'ntp force on green'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='NTP_FORCE_ON_GREEN' value='on' $checked{'NTP_FORCE_ON_GREEN'}{'on'} />/
+																						<input type='radio' name='NTP_FORCE_ON_GREEN' value='off' $checked{'NTP_FORCE_ON_GREEN'}{'off'} /> $Lang::tr{'off'}</td></tr>
+END
+
+	if (&Header::blue_used()) {
+		print <<END;
+		<table width='95%' cellspacing='0'>
+		<tr bgcolor='$color{'color20'}'><td colspan='2' align='left'><b>$Lang::tr{'fw blue'}</b></td></tr>
+		<tr> </tr>
+			<tr>
+			<tr><td align='left' width='60%'>$Lang::tr{'dns force on blue'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DNS_FORCE_ON_BLUE' value='on' $checked{'DNS_FORCE_ON_BLUE'}{'on'} />/
+																						<input type='radio' name='DNS_FORCE_ON_BLUE' value='off' $checked{'DNS_FORCE_ON_BLUE'}{'off'} /> $Lang::tr{'off'}</td></tr>
+			<tr><td align='left' width='60%'>$Lang::tr{'ntp force on blue'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='NTP_FORCE_ON_BLUE' value='on' $checked{'NTP_FORCE_ON_BLUE'}{'on'} />/
+																						<input type='radio' name='NTP_FORCE_ON_BLUE' value='off' $checked{'NTP_FORCE_ON_BLUE'}{'off'} /> $Lang::tr{'off'}</td></tr>
+			<tr><td align='left' width='60%'>$Lang::tr{'drop proxy'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPPROXY' value='on' $checked{'DROPPROXY'}{'on'} />/
+																						<input type='radio' name='DROPPROXY' value='off' $checked{'DROPPROXY'}{'off'} /> $Lang::tr{'off'}</td></tr>
+			<tr><td align='left' width='60%'>$Lang::tr{'drop samba'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPSAMBA' value='on' $checked{'DROPSAMBA'}{'on'} />/
+																						<input type='radio' name='DROPSAMBA' value='off' $checked{'DROPSAMBA'}{'off'} /> $Lang::tr{'off'}</td></tr>
+			</td>
+			</tr>
+END
+	}
+
+	print <<END;
 	</table>
 
 	<br>
@@ -224,21 +267,25 @@
 																						<input type='radio' name='DROPOUTGOING' value='off' $checked{'DROPOUTGOING'}{'off'} /> $Lang::tr{'off'}</td></tr>
 <tr><td align='left' width='60%'>$Lang::tr{'drop portscan'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPPORTSCAN' value='on' $checked{'DROPPORTSCAN'}{'on'} />/
 																						<input type='radio' name='DROPPORTSCAN' value='off' $checked{'DROPPORTSCAN'}{'off'} /> $Lang::tr{'off'}</td></tr>
-<tr><td align='left' width='60%'>$Lang::tr{'drop wirelessinput'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPWIRELESSINPUT' value='on' $checked{'DROPWIRELESSINPUT'}{'on'} />/
+END
+
+	if (&Header::blue_used()) {
+		print <<END;
+		<table width='95%' cellspacing='0'>
+			<tr>
+			<tr><td align='left' width='60%'>$Lang::tr{'drop wirelessinput'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPWIRELESSINPUT' value='on' $checked{'DROPWIRELESSINPUT'}{'on'} />/
 																						<input type='radio' name='DROPWIRELESSINPUT' value='off' $checked{'DROPWIRELESSINPUT'}{'off'} /> $Lang::tr{'off'}</td></tr>
-<tr><td align='left' width='60%'>$Lang::tr{'drop wirelessforward'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPWIRELESSFORWARD' value='on' $checked{'DROPWIRELESSFORWARD'}{'on'} />/
+			<tr><td align='left' width='60%'>$Lang::tr{'drop wirelessforward'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPWIRELESSFORWARD' value='on' $checked{'DROPWIRELESSFORWARD'}{'on'} />/
 																						<input type='radio' name='DROPWIRELESSFORWARD' value='off' $checked{'DROPWIRELESSFORWARD'}{'off'} /> $Lang::tr{'off'}</td></tr>
-</table>
-<br/>
+			</tr>
+END
+	}
+
+	print <<END;
+	</table>
+
+	<br/>
 
-<table width='95%' cellspacing='0'>
-<tr bgcolor='$color{'color20'}'><td colspan='2' align='left'><b>$Lang::tr{'fw blue'}</b></td></tr>
-<tr><td align='left' width='60%'>$Lang::tr{'drop proxy'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPPROXY' value='on' $checked{'DROPPROXY'}{'on'} />/
-																						<input type='radio' name='DROPPROXY' value='off' $checked{'DROPPROXY'}{'off'} /> $Lang::tr{'off'}</td></tr>
-<tr><td align='left' width='60%'>$Lang::tr{'drop samba'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='DROPSAMBA' value='on' $checked{'DROPSAMBA'}{'on'} />/
-																						<input type='radio' name='DROPSAMBA' value='off' $checked{'DROPSAMBA'}{'off'} /> $Lang::tr{'off'}</td></tr>
-</table>
-<br>
 <table width='95%' cellspacing='0'>
 <tr bgcolor='$color{'color20'}'><td colspan='2' align='left'><b>$Lang::tr{'fw settings'}</b></td></tr>
 <tr><td align='left' width='60%'>$Lang::tr{'fw settings color'}</td><td align='left'>$Lang::tr{'on'} <input type='radio' name='SHOWCOLORS' value='on' $checked{'SHOWCOLORS'}{'on'} />/
-------------- next part --------------
diff -U 3 C:/Compare/force dns/next/src_initscripts_system_firewall C:/Compare/force dns/tuned 01/firewall_init
--- C:/Compare/force dns/next/src_initscripts_system_firewall	Mon Nov 23 09:09:24 2020
+++ C:/Compare/force dns/tuned 01/firewall_init	Sat Nov 21 01:37:54 2020
@@ -246,6 +246,77 @@
 		iptables -A ${i} -j CAPTIVE_PORTAL
 	done
 
+# Force DNS REDIRECT on GREEN (udp, tcp, 53)
+if [ "$DNS_FORCE_ON_GREEN" == "on" ]; then
+	if ! iptables -t nat -C CUSTOMPREROUTING -i green0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i green0 -p udp -m udp --dport 53 -j REDIRECT
+	fi
+
+	if ! iptables -t nat -C CUSTOMPREROUTING -i green0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i green0 -p tcp -m tcp --dport 53 -j REDIRECT
+	fi
+
+else
+
+	if iptables -t nat -C CUSTOMPREROUTING -i green0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i green0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1
+	fi
+
+	if iptables -t nat -C CUSTOMPREROUTING -i green0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i green0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1
+	fi
+fi
+
+# Force DNS REDIRECT on BLUE (udp, tcp, 53)
+if [ "$DNS_FORCE_ON_BLUE" == "on" ]; then
+	if ! iptables -t nat -C CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 53 -j REDIRECT
+	fi
+
+	if ! iptables -t nat -C CUSTOMPREROUTING -i blue0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i blue0 -p tcp -m tcp --dport 53 -j REDIRECT
+	fi
+
+else
+
+	if iptables -t nat -C CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 53 -j REDIRECT >/dev/null 2>&1
+	fi
+
+	if iptables -t nat -C CUSTOMPREROUTING -i blue0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i blue0 -p tcp -m tcp --dport 53 -j REDIRECT >/dev/null 2>&1
+	fi
+
+fi
+
+# Force NTP REDIRECT on GREEN (udp, 123)
+if [ "$NTP_FORCE_ON_GREEN" == "on" ]; then
+	if ! iptables -t nat -C CUSTOMPREROUTING -i green0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i green0 -p udp -m udp --dport 123 -j REDIRECT
+	fi
+
+else
+
+	if iptables -t nat -C CUSTOMPREROUTING -i green0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i green0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1
+	fi
+
+fi
+
+# Force DNS REDIRECT on BLUE (udp, 123)
+if [ "$NTP_FORCE_ON_BLUE" == "on" ]; then
+	if ! iptables -t nat -C CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -A CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 123 -j REDIRECT
+	fi
+
+else
+
+	if iptables -t nat -C CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1; then
+		iptables -t nat -D CUSTOMPREROUTING -i blue0 -p udp -m udp --dport 123 -j REDIRECT >/dev/null 2>&1
+	fi
+
+fi
+
 	# Accept everything connected
 	for i in INPUT FORWARD OUTPUT; do
 		iptables -A ${i} -j CONNTRACK
-------------- next part --------------
diff -U 3 C:/Compare/force dns/tuned 01/optionsfw.cgi C:/Compare/force dns/tuned 02/optionsfw.cgi
--- C:/Compare/force dns/tuned 01/optionsfw.cgi	Sun Nov  8 20:17:54 2020
+++ C:/Compare/force dns/tuned 02/optionsfw.cgi	Sun Nov 22 20:06:56 2020
@@ -69,6 +69,31 @@
 	&General::readhash($filename, \%settings);             # Load good settings
 }
 
+if ($settings{'ACTION'} eq $Lang::tr{'fw settings save and restart'}) {
+	if ($settings{'defpol'} ne '1'){
+		$errormessage .= $Lang::tr{'new optionsfw later'};
+		&General::writehash($filename, \%settings);             # Save good settings
+		system("/usr/local/bin/firewallctrl");
+	}else{
+		if ($settings{'POLICY'} ne ''){
+			$fwdfwsettings{'POLICY'} = $settings{'POLICY'};
+		}
+		if ($settings{'POLICY1'} ne ''){
+			$fwdfwsettings{'POLICY1'} = $settings{'POLICY1'};
+		}
+		my $MODE = $fwdfwsettings{'POLICY'};
+		my $MODE1 = $fwdfwsettings{'POLICY1'};
+		%fwdfwsettings = ();
+		$fwdfwsettings{'POLICY'} = "$MODE";
+		$fwdfwsettings{'POLICY1'} = "$MODE1";
+		&General::writehash("${General::swroot}/firewall/settings", \%fwdfwsettings);
+		&General::readhash("${General::swroot}/firewall/settings", \%fwdfwsettings);
+		system("/usr/local/bin/firewallctrl");
+		system("/etc/rc.d/init.d/firewall restart >/dev/null 2>&1 ");
+	}
+	&General::readhash($filename, \%settings);             # Load good settings
+}
+
 &Header::openpage($Lang::tr{'options fw'}, 1, '');
 &Header::openbigbox('100%', 'left', '', $errormessage);
 &General::readhash($filename, \%settings);
@@ -370,7 +395,8 @@
 <br />
 <table width='100%' cellspacing='0'>
 <tr><td align='right'><form method='post' action='$ENV{'SCRIPT_NAME'}'>
-<input type='submit' name='ACTION' value=$Lang::tr{'save'} />
+<input type='submit' name='ACTION' value='$Lang::tr{'save'}' />
+<input type='submit' name='ACTION' value='$Lang::tr{'fw settings save and restart'}' />
 </form></td></tr>
 </table>
 </form>
-------------- next part --------------
/* This file is part of the IPFire Firewall.
 *
 * This program is distributed under the terms of the GNU General Public
 * Licence.  See the file COPYING for details.
 *
 */

#include <stdlib.h>
#include "setuid.h"

int main(void)
{
	if (!(initsetuid()))
		exit(1);

	safe_system("/etc/rc.d/init.d/firewall restart >/dev/null 2>&1");

	return 0;
}


More information about the Development mailing list