]> git.ipfire.org Git - people/ummeegge/ipfire-2.x.git/commitdiff
DNS-over-TLS: Integration into web user interface DoT
authorErik Kapfer <ummeegge@ipfire.org>
Fri, 1 Nov 2019 13:38:57 +0000 (14:38 +0100)
committerErik Kapfer <ummeegge@ipfire.org>
Fri, 1 Nov 2019 13:38:57 +0000 (14:38 +0100)
- unbound init has been modified and do not uses 'update_forwarders' function if DoT has been detected.
- DoT works with forward.conf which includes also 'qname-minimization strict'.
- dnsovertls.cgi can be used to configure DoT connections but do also accepts other ports (e.g. 53).
- dot-indexCGI-check checks if and how the connections has been established and delivers the result
    to /var/ipfire/red/dot which will be used by index.cgi to deliver the status via color codes.
    color codes: red = server is off ; orange = DNSsec do not works but certificate is trustworthy and crypto works ; green = all is working.
- check_connections.sh is a shell script which checks all configured connections also with the above color codes.
- test_tls.sh is also a shell script which check also all configured connections but with the raw output of kdig.
- language file has only be adapted for english.

Signed-off-by: Erik Kapfer <ummeegge@ipfire.org>
config/dot/EX-dnsovertls.menu [new file with mode: 0644]
config/dot/check_connection.sh [new file with mode: 0644]
config/dot/dot-indexCGI-check [new file with mode: 0755]
config/dot/test_tls.sh [new file with mode: 0644]
html/cgi-bin/dnsovertls.cgi [new file with mode: 0644]
html/cgi-bin/index.cgi
langs/en/cgi-bin/en.pl
src/initscripts/system/unbound

diff --git a/config/dot/EX-dnsovertls.menu b/config/dot/EX-dnsovertls.menu
new file mode 100644 (file)
index 0000000..3282666
--- /dev/null
@@ -0,0 +1,6 @@
+$subipfire->{'56.dnsovertls'} = {
+       'caption' => $Lang::tr{'dnsovertls'},
+       'uri' => '/cgi-bin/dnsovertls.cgi',
+       'title' => $Lang::tr{'dnsovertls'},
+       'enabled' => 1,
+};
\ No newline at end of file
diff --git a/config/dot/check_connection.sh b/config/dot/check_connection.sh
new file mode 100644 (file)
index 0000000..9b28e45
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/bash -
+
+# Text and formatting
+COLUMNS="$(tput cols)";
+R=$(tput setaf 1);
+G=$(tput setaf 2);
+O=$(tput setaf 166)
+N=$(tput sgr0);
+seperator(){ printf -v _hr "%*s" ${COLUMNS} && echo ${_hr// /${1-=}}; }
+WELCOME="${b}${O}Will check now your configured DNS-over-TLS servers from '/etc/unbound/forward.conf'${N}"
+
+# Check if kdig is presant
+command -v kdig >/dev/null 2>&1 || { echo >&2 "kdig is required but it's not installed.  Aborting."; exit 1; }
+
+# Paths
+FORWARDCONF="/etc/unbound/forward.conf"
+BUNDLE="/etc/ssl/certs/ca-bundle.crt"
+
+# Check for DoT forwarder
+if ! grep -ERw "forward-addr: ([0-9.]+){4}@853#[a-zA-Z0-9]+([-.]?[a-zA-Z0-9]+)*.[a-zA-Z]+$" ${FORWARDCONF} > /dev/null 2>&1; then
+       echo "Haven´t found usable DoT forwarders in ${FORWARDCONF}. Need to quit... "
+       exit 1
+fi
+
+# Get DoT data and write it to files
+DOT="/tmp/DOT"
+HOST="/tmp/host.in"
+IP="/tmp/ip.in"
+grep -hERo "([0-9.]+){4}@853#.*" ${FORWARDCONF} > ${DOT}
+awk -F'#' '{ print $2 }' ${DOT} > ${HOST}
+awk -F'@' '{ print $1 }' ${DOT} > ${IP}
+
+
+# Mainpart
+clear
+seperator "$@"
+echo
+printf "%*s\n" $(((${#WELCOME}+COLUMNS)/2)) "${WELCOME}";
+echo
+while read -u 3 -r ip && read -u 4 -r host; do
+       echo
+       seperator "$@"
+       echo "From Host: ${O}${host}${N} ---- With IP: ${O}${ip}${N} ---- Date: ${O}$(date)${N}"
+       echo
+       IFS=$'\n'
+       check_array=( $(kdig -d @"${ip}" +dnssec +tls-ca=${BUNDLE} +tls-host="${host}" www.isoc.org | \
+               grep -oE 'The certificate.*|TLS session.*|ra ad;|\(TCP\) in .*' | sed -e 's/;//g' -e 's/(TCP) //g') )
+       for element in "${check_array[@]}"; do
+               echo "${element}" | sed -e '/The certificate is trusted./d' -e '/TLS session.*/d' -e '/ra.*/d'
+       done
+       echo
+
+       # Check TLS
+       if echo "${check_array[1]}" | grep -q 'TLS session'; then
+               ENC=$(echo "${check_array[1]}" | awk '{ print $3 }' | sed -e 's/(//g' -e 's/)//g')
+               echo "${G}The encryption is OK and works with: ${ENC}${N}"
+       else
+               echo "${R}Encryption do not works, this server seems to be OFF${N}"
+               continue
+
+       fi
+       echo
+
+       # Check certificate trust
+       if [ "${check_array[0]}" = "The certificate is trusted. " ]; then
+               echo "${G}The certificate is trusted and OK${N}"
+       else
+               echo "${R}The certificate is NOT Trusted and NOT OK${N}"
+       fi
+       echo
+
+       # Check DNSSEC
+       if [ "${check_array[2]}" = "ra ad" ]; then
+               echo "${G}The DNSSEC validation works and is OK${N}"
+       else
+               echo "${R}The DNSSEC validation do NOT works and is NOT OK${N}"
+       fi
+
+done 3<${IP} 4<${HOST}
+
+echo
+seperator
+echo
+echo "All DNS-over-TLS servers from /etc/unbound/forward.conf has been tested."
+
+# Clean up
+rm -rf ${DOT} ${IP} ${HOST}
+
+# EOF
diff --git a/config/dot/dot-indexCGI-check b/config/dot/dot-indexCGI-check
new file mode 100755 (executable)
index 0000000..0c3b3c1
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/bash -
+
+# Script checks if DoT server certificate is trusted and if DNSSec works.
+# If the certificate is trusted and DNSsec works, the server IP will be
+# displayed in color green.
+# If the certificate is trusted but DNSsec do NOT works, the IP will be
+# displayed in orange.
+# If the certificate is NOT trusted, the server IP will be displayed in red.
+#
+# Script uses HTML font color attributes and pipes them to /var/ipfire/red/dns
+# so the index.cgi can it display in IPFire home WUI site.
+#
+# ummeegge ipfire.org ; 29.09.2019
+##############################################################################
+#
+
+## Formatting
+# Certificate is trusted, DNSSEC works
+G="<font color='green'>"
+# Certificate is trusted but DNSSEC does NOT work
+O="<font color='orange'>"
+# Server certificate is not trusted and should NOT be used
+R="<font color='red'>"
+N="</font>"
+
+# Paths
+FORWARDCONF="/etc/unbound/forward.conf"
+BUNDLE="/etc/ssl/certs/ca-bundle.crt"
+# Get DoT data and write it to files
+DOT="/tmp/DOT"
+HOST="/tmp/host.in"
+IP="/tmp/ip.in"
+RES="/tmp/result"
+DNS="/var/ipfire/red/dot"
+grep -hERo "([0-9.]+){4}@853#.*" ${FORWARDCONF} > ${DOT}
+awk -F'#' '{ print $2 }' ${DOT} > ${HOST}
+awk -F'@' '{ print $1 }' ${DOT} > ${IP}
+
+# Check for DoT forwarder
+if ! grep -ERw "forward-addr: ([0-9.]+){4}@853#[a-zA-Z0-9]+([-.]?[a-zA-Z0-9]+)*.[a-zA-Z]+$" ${FORWARDCONF} > /dev/null 2>&1; then
+       logger -t DNS-over-TLS "DoT: Haven´t found usable DoT forwarders in ${FORWARDCONF}. '/etc/fcron.hourly/dot-indexCGI-color.sh' Need to quit... "
+       exit 1
+else
+       echo > ${DNS}
+fi
+
+search_func() {
+       if [[ "${check_array[0]}" = "The certificate is trusted. " ]] && [[ "${check_array[2]}" = "ra ad" ]]; then
+               echo "${G}${ip}${N}"
+       elif [[ "${check_array[0]}" = "The certificate is trusted. " ]] && [[ "${check_array[2]}" != "ra ad" ]]; then
+               echo "${O}${ip}${N}"
+       else
+               echo "${R}${ip}${N}"
+               continue
+       fi
+}
+
+# Mainpart
+
+while read -u 3 -r ip && read -u 4 -r host; do
+       IFS=$'\n'
+       check_array=( $(kdig -d @"${ip}" +dnssec +tls-ca=${BUNDLE} +tls-host="${host}" www.isoc.org | \
+               grep -oE 'The certificate.*|TLS session.*|ra ad;|\(TCP\) in .*' | sed -e 's/;//g' -e 's/(TCP) //g') )
+       for element in "${check_array[@]}"; do
+               echo "${element}" | sed -e '/The certificate is trusted./d' -e '/TLS session.*/d' -e '/ra.*/d' -e '/in /d'
+       done
+
+       search_func >> ${RES}
+
+done 3<${IP} 4<${HOST} 2> /dev/null
+
+cat ${RES} | sed ':a;N;s/\n/ | /;ta' > ${DNS}
+
+# Clean up
+rm -rf ${DOT} ${IP} ${HOST} ${RES}
+
+# EOF
+
diff --git a/config/dot/test_tls.sh b/config/dot/test_tls.sh
new file mode 100644 (file)
index 0000000..97711e9
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash -
+
+#
+# Test if DoT certificates are trustworthy.
+#
+# ummeegge 08.12.2018
+###########################################
+#
+
+# Formatting
+COLUMNS="$(tput cols)";
+seperator(){ printf -v _hr "%*s" ${COLUMNS} && echo ${_hr// /${1-=}}; }
+
+# Check if kdig is presant
+command -v kdig >/dev/null 2>&1 || { echo >&2 "kdig is required but it's not installed.  Aborting."; exit 1; }
+
+# Paths
+FORWARDCONF="/etc/unbound/forward.conf"
+BUNDLE="/etc/ssl/certs/ca-bundle.crt"
+
+# Check for DoT forwarder
+if ! grep -ERw "forward-addr: ([0-9.]+){4}@853#[a-zA-Z0-9]+([-.]?[a-zA-Z0-9]+)*.[a-zA-Z]+$" ${FORWARDCONF} > /dev/null 2>&1; then
+       echo "Haven´t found usable DoT forwarders in ${FORWARDCONF}. Need to quit... "
+       exit 1
+fi
+
+# Get DoT data and write it to files
+DOT="/tmp/DOT"
+HOST="/tmp/host.in"
+IP="/tmp/ip.in"
+grep -hERo "([0-9.]+){4}@853#.*" ${FORWARDCONF} > ${DOT}
+awk -F'#' '{ print $2 }' ${DOT} > ${HOST}
+awk -F'@' '{ print $1 }' ${DOT} > ${IP}
+
+# Mainpart
+echo
+seperator
+while read -u 3 -r ip && read -u 4 -r host; do
+       echo
+       kdig -d @"${ip}" +dnssec +tls-ca="${BUNDLE}" +tls-host="${host}" www.isoc.org; exit=$?
+       echo
+       echo "Exit status: $exit"
+       echo
+       seperator
+       sleep 5
+done 3<${IP} 4<${HOST}
+
+# Clean up
+rm -rf ${DOT} ${IP} ${HOST}
+
+
+# EOF
diff --git a/html/cgi-bin/dnsovertls.cgi b/html/cgi-bin/dnsovertls.cgi
new file mode 100644 (file)
index 0000000..dc22f1b
--- /dev/null
@@ -0,0 +1,398 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2013  IPFire Development Team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+use strict;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+require "${General::swroot}/header.pl";
+
+#workaround to suppress a warning when a variable is used only once
+my @dummy = ( ${Header::colouryellow} );
+undef (@dummy);
+
+my %cgiparams=();
+my %checked=();
+my %selected=();
+my $errormessage = '';
+my $tlsconfig = "${General::swroot}/dns/tlsconfig";
+my $changed = 'no';
+my %color = ();
+my %mainsettings = ();
+&General::readhash("${General::swroot}/main/settings", \%mainsettings);
+&General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color);
+&Header::showhttpheaders();
+
+$cgiparams{'ENABLED'} = 'off';
+$cgiparams{'ACTION'} = '';
+$cgiparams{'HOSTNAME'} = '';
+$cgiparams{'SERVER_IP'} = '';
+$cgiparams{'PORT'} = '853';
+$cgiparams{'REMARK'} ='';
+&Header::getcgihash(\%cgiparams);
+open(FILE, ">>$tlsconfig") or die 'Unable to create file.';
+open(FILE, $tlsconfig) or die 'Unable to open config file.';
+my @current = <FILE>;
+close(FILE);
+
+###
+# Add / Edit entries.
+#
+if ($cgiparams{'ACTION'} eq $Lang::tr{'add'})
+{
+
+       # Check if the entered domainname is valid.
+       unless(&General::validfqdn($cgiparams{'HOSTNAME'})) {
+               $errormessage = $Lang::tr{'invalid domain name'};
+               goto ERROR;
+       }
+
+       # Check if the settings for the forward server are valid.
+       unless(&General::validip($cgiparams{'SERVER_IP'})) {
+               $errormessage = $Lang::tr{'invalid ip'};
+               goto ERROR;
+       }
+
+       # Check if the settings for the port are valid.
+       unless(&General::validport($cgiparams{'PORT'})) {
+               $errormessage = $Lang::tr{'invalid port'};
+               goto ERROR;
+       }
+
+       # Go further if there was no error.
+       if ( ! $errormessage)
+       {
+           # Check if a remark has been entered.
+           $cgiparams{'REMARK'} = &Header::cleanhtml($cgiparams{'REMARK'});
+
+               # Check if we want to edit an existing or add a new entry.
+               if($cgiparams{'EDITING'} eq 'no') {
+                       # Check in 'ADD' mode if IP is already in usage
+                       foreach my $line (@current) {
+                               chomp($line);
+                               my @temp=split(/\,/,$line);
+                               if ($temp[2] eq $cgiparams{'SERVER_IP'}) {
+                                       $errormessage = $Lang::tr{'fwhost err ipcheck'};
+                                       goto ERROR;
+                               }
+                       }
+
+                       open(FILE,">>$tlsconfig") or die 'Unable to open config file.';
+                       flock FILE, 2;
+                       print FILE "$cgiparams{'ENABLED'},$cgiparams{'HOSTNAME'},$cgiparams{'SERVER_IP'},$cgiparams{'PORT'},$cgiparams{'REMARK'}\n";
+               } else {
+                       open(FILE, ">$tlsconfig") or die 'Unable to open config file.';
+                       flock FILE, 2;
+                       my $id = 0;
+                       foreach my $line (@current)
+                       {
+                               $id++;
+                               if ($cgiparams{'EDITING'} eq $id) {
+                                       print FILE "$cgiparams{'ENABLED'},$cgiparams{'HOSTNAME'},$cgiparams{'SERVER_IP'},$cgiparams{'PORT'},$cgiparams{'REMARK'}\n";
+                               } else { print FILE "$line"; }
+                       }
+               }
+
+               close(FILE);
+               undef %cgiparams;
+               $changed = 'yes';
+       } else {
+               # stay on edit mode if an error occur
+               if ($cgiparams{'EDITING'} ne 'no')
+               {
+                       $cgiparams{'ACTION'} = $Lang::tr{'edit'};
+                       $cgiparams{'ID'} = $cgiparams{'EDITING'};
+               }
+       }
+       # Restart unbound
+       system('/usr/local/bin/unboundctrl restart >/dev/null');
+}
+
+ERROR:
+
+
+###
+# Remove existing entries.
+#
+if ($cgiparams{'ACTION'} eq $Lang::tr{'delete'})
+{
+       my $id = 0;
+       open(FILE, ">$tlsconfig") or die 'Unable to open config file.';
+       flock FILE, 2;
+       foreach my $line (@current)
+       {
+               $id++;
+               unless ($cgiparams{'ID'} eq $id) { print FILE "$line"; }
+       }
+       close(FILE);
+       # Restart unbound.
+       system('/usr/local/bin/unboundctrl restart >/dev/null');
+}
+
+###
+# Toggle Enable/Disable for entries.
+#
+if ($cgiparams{'ACTION'} eq $Lang::tr{'toggle enable disable'})
+{
+       open(FILE, ">$tlsconfig") or die 'Unable to open config file.';
+       flock FILE, 2;
+       my $id = 0;
+       foreach my $line (@current)
+       {
+               $id++;
+               unless ($cgiparams{'ID'} eq $id) { print FILE "$line"; }
+               else
+               {
+                       chomp($line);
+                       my @temp = split(/\,/,$line);
+                       print FILE "$cgiparams{'ENABLE'},$temp[1],$temp[2],$temp[3],$temp[4]\n";
+               }
+       }
+       close(FILE);
+       # Restart unbound.
+       system('/usr/local/bin/unboundctrl restart >/dev/null');
+}
+
+###
+# Read items for edit mode.
+#
+if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'})
+{
+       my $id = 0;
+       foreach my $line (@current)
+       {
+               $id++;
+               if ($cgiparams{'ID'} eq $id)
+               {
+                       chomp($line);
+                       my @temp = split(/\,/,$line);
+                       $cgiparams{'ENABLED'} = $temp[0];
+                       $cgiparams{'HOSTNAME'} = $temp[1];
+                       $cgiparams{'SERVER_IP'} = $temp[2];
+                       $cgiparams{'PORT'} = $temp[3];
+                       $cgiparams{'REMARK'} = $temp[4];
+               }
+       }
+}
+
+$checked{'ENABLED'}{'off'} = '';
+$checked{'ENABLED'}{'on'} = '';
+$checked{'ENABLED'}{$cgiparams{'ENABLED'}} = "checked='checked'";
+
+&Header::openpage($Lang::tr{'dnsovertls configuration'}, 1, '');
+
+&Header::openbigbox('100%', 'left', '', $errormessage);
+
+
+###
+# Error messages layout.
+#
+if ($errormessage) {
+       &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
+       print "<class name='base'>$errormessage\n";
+       print "&nbsp;</class>\n";
+       &Header::closebox();
+}
+
+print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>\n";
+
+my $buttontext = $Lang::tr{'add'};
+if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}) {
+       &Header::openbox('100%', 'left', $Lang::tr{'dnsforward edit an entry'});
+       $buttontext = $Lang::tr{'update'};
+} else {
+       &Header::openbox('100%', 'left', $Lang::tr{'dnsforward add a new entry'});
+}
+
+###
+# Content of the main page.
+#
+print <<END
+<table width='100%'>
+       <tr>
+               <td width='20%' class='base'>$Lang::tr{'hostname'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
+               <td><input type='text' name='HOSTNAME' value='$cgiparams{'HOSTNAME'}' size='24' /></td>
+               <td width='30%' class='base'>$Lang::tr{'enabled'}<input type='checkbox' name='ENABLED' $checked{'ENABLED'}{'on'} /></td>
+       </tr>
+
+       <tr>
+               <td width='20%' class='base'>$Lang::tr{'ip address'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
+               <td><input type='text' name='SERVER_IP' value='$cgiparams{'SERVER_IP'}' size='24' /></td>
+       </tr>
+
+       <tr>
+               <td width='12%' class='base'>$Lang::tr{'port'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
+               <td><input type='text' name='PORT' value='$cgiparams{'PORT'}' size='6' /></td>
+</table>
+
+<table width='100%'>
+       <tr>
+               <td width ='20%' class='base'>$Lang::tr{'remark'}:</td>
+               <td><input type='text' name='REMARK' value='$cgiparams{'REMARK'}' size='40' maxlength='50' /></td>
+       </tr>
+</table>
+<br>
+<hr>
+
+<table width='100%'>
+       <tr>
+               <td class='base' width='55%'><img src='/blob.gif' alt ='*' align='top' />&nbsp;$Lang::tr{'required field'}</td>
+               <td width='40%' align='right'>
+                       <input type='hidden' name='ACTION' value='$Lang::tr{'add'}' />
+                       <input type='submit' name='SUBMIT' value='$buttontext' />
+               </td>
+       </tr>
+</table>
+END
+;
+if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}) {
+       print "<input type='hidden' name='EDITING' value='$cgiparams{'ID'}' />\n";
+} else {
+       print "<input type='hidden' name='EDITING' value='no' />\n";
+}
+
+&Header::closebox();
+print "</form>\n";
+
+###
+# Existing rules.
+#
+&Header::openbox('100%', 'left', $Lang::tr{'dnsovertls entries'});
+print <<END
+<table width='100%' cellpadding='0' class='tbl'>
+       <tr>
+               <th width='30%' class='boldbase' align='center'><b>$Lang::tr{'hostname'}</b></th>
+               <th width='25%' class='boldbase' align='center'><b>$Lang::tr{'ip address'}</b></th>
+               <th width='15%' class='boldbase' align='center'><b>$Lang::tr{'port'}</b></th>
+               <th width='35%' class='boldbase' align='center'><b>$Lang::tr{'remark'}</b></th>
+               <th width='10%' class='boldbase' colspan='7' align='center'><b>$Lang::tr{'action'}</b></th>
+       </tr>
+END
+;
+
+# If something has happened re-read config
+if($cgiparams{'ACTION'} ne '' or $changed ne 'no')
+{
+       open(FILE, $tlsconfig) or die 'Unable to open config file.';
+       @current = <FILE>;
+       close(FILE);
+       # Restart unbound.
+       system('/usr/local/bin/unboundctrl restart >/dev/null');
+}
+
+###
+# Re-read entries and highlight selected item for editing.
+#
+my $id = 0;
+my $col="";
+foreach my $line (@current)
+{
+       $id++;
+       chomp($line);
+       my @temp = split(/\,/,$line);
+       my $toggle = '';
+       my $gif = '';
+       my $gdesc = '';
+       my $toggle = '';
+       
+       if($cgiparams{'ACTION'} eq $Lang::tr{'edit'} && $cgiparams{'ID'} eq $id) {
+               print "<tr>";
+               $col="bgcolor='${Header::colouryellow}'"; }
+       elsif ($id % 2) {
+               print "<tr>";
+               $col="bgcolor='$color{'color22'}'"; }
+       else {
+               print "<tr>";
+               $col="bgcolor='$color{'color20'}'"; }
+
+       if ($temp[0] eq 'on') { $gif='on.gif'; $toggle='off'; $gdesc=$Lang::tr{'click to disable'};}
+       else { $gif='off.gif'; $toggle='on'; $gdesc=$Lang::tr{'click to enable'}; }
+
+###
+# Display edit page.
+#
+print <<END
+       <td align='center' $col>$temp[1]</td>
+       <td align='center' $col>$temp[2]</td>
+       <td align='center' $col>$temp[3]</td>
+       <td align='center' $col>$temp[4]</td>
+       <td align='center' $col>
+               <form method='post' name='frma$id' action='$ENV{'SCRIPT_NAME'}'>
+                       <input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' title='$gdesc' alt='$gdesc' />
+                       <input type='hidden' name='ID' value='$id' />
+                       <input type='hidden' name='ENABLE' value='$toggle' />
+                       <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
+               </form>
+       </td>
+       <td align='center' $col>
+               <form method='post' name='frmb$id' action='$ENV{'SCRIPT_NAME'}'>
+                       <input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' title='$Lang::tr{'edit'}' alt='$Lang::tr{'edit'}' />
+                       <input type='hidden' name='ID' value='$id' />
+                       <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
+               </form>
+       </td>
+       <td align='center' $col>
+               <form method='post' name='frmc$id' action='$ENV{'SCRIPT_NAME'}'>
+                       <input type='image' name='$Lang::tr{'delete'}' src='/images/delete.gif' title='$Lang::tr{'delete'}' alt='$Lang::tr{'delete'}' onclick="return confirm('$Lang::tr{'delete item'}');" />
+                       <input type='hidden' name='ID' value='$id' />
+                       <input type='hidden' name='ACTION' value='$Lang::tr{'delete'}' />
+               </form>
+       </td>
+</tr>
+END
+       ;
+}
+print "</table>\n";
+
+###
+# Print the legend at the bottom if there are any configured entries.
+#
+# Check if the file size is zero - no existing entries.
+if ( ! -z "$tlsconfig") {
+print <<END
+<table>
+       <tr>
+               <td class='boldbase'>&nbsp; <b>$Lang::tr{'legend'}:</b></td>
+               <td>&nbsp; <img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
+               <td class='base'>$Lang::tr{'click to disable'}</td>
+               <td>&nbsp; &nbsp; <img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
+               <td class='base'>$Lang::tr{'click to enable'}</td>
+               <td>&nbsp; &nbsp; <img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
+               <td class='base'>$Lang::tr{'edit'}</td>
+               <td>&nbsp; &nbsp; <img src='/images/delete.gif' alt='$Lang::tr{'delete'}' onclick="return confirm('$Lang::tr{'delete item'}');" /></td>
+               <td class='base'>$Lang::tr{'delete'}</td>
+       </tr>
+</table>
+END
+;
+}
+
+&Header::closebox();
+
+&Header::closebigbox();
+
+&Header::closepage();
+
+
index c7bf9f323242092642d4f23434b538fbf1589351..9116fb0f740c89661bf9415b53e5b3dc090fd593 100644 (file)
@@ -213,12 +213,33 @@ END
                                <b><a href="netexternal.cgi">$Lang::tr{'dns servers'}</a>:</b>
                        </td>
                        <td style='text-align:center;'>
+                       <br>
                                $dns_servers
                        </td>
                        <td></td>
                </tr>
 END
 
+       my $dot_servers;
+       if ( -e "${General::swroot}/red/dot" ) {
+               open (TMP, "<${General::swroot}/red/dot");
+               $dot_servers = <TMP>;
+               chomp($dot_servers);
+               close TMP;
+       }
+       print <<END;
+               <tr>
+                       <td>
+                               <b><a href="dnsovertls.cgi">$Lang::tr{'dnsovertls'}</a>:</b>
+                       </td>
+                       <td style='text-align:center;'>
+                       <br>
+                               $dot_servers
+                       </td>
+                       <td></td>
+               </tr>
+END
+
        if (&General::RedIsWireless()) {
                my $iface = $netsettings{"RED_DEV"} || "red0";
 
index 8b7e63cb868c0e8ab465da0f4331d91ecbecca5c..2f812cf7b54ef593a8ff241fdc537906373d183c 100644 (file)
 'dnsforward entries' => 'Current entries',
 'dnsforward forward_servers' => 'Nameservers',
 'dnsforward zone' => 'Zone',
+'dnsovertls' => 'DNS-over-TLS',
+'dnsovertls configuration' => 'DNS-over-TLS configuration',
+'dnsovertls entries' => 'DNS-over-TLS entries',
 'dnssec aware' => 'DNSSEC Aware',
 'dnssec disabled warning' => 'WARNING: DNSSEC has been disabled',
 'dnssec information' => 'DNSSEC Information',
 'zoneconf val zoneslave amount error' => 'A zone that is not in bridge mode can\'t have more than one NIC assigned',
 );
 
-#EOF
+#EOF
\ No newline at end of file
index d195fd3256dc695cef32dd36615ff99a78359c8f..7164346389445958b91a29e319ccc2987c5a3740 100644 (file)
@@ -23,6 +23,10 @@ LOCAL_TTL=60
 # EDNS buffer size
 EDNS_DEFAULT_BUFFER_SIZE=4096
 
+# Path to other DNS configurations
+DOT="/var/ipfire/dns/tlsconfig"
+DNSFORWARD="/var/ipfire/dnsforward/config"
+
 # Load optional configuration
 [ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
 
@@ -48,6 +52,15 @@ read_name_servers() {
        done 2>/dev/null | xargs echo
 }
 
+read_dot_servers() {
+       if [ -f "/usr/bin/dot-indexCGI-check" ]; then
+               bash /usr/bin/dot-indexCGI-check
+       else
+               dot=$(unbound-control list_forwards | grep -Eo "([0-9.]+){4}" | tr '\n' ' ')
+               echo "${dot}" > /var/ipfire/red/dns
+       fi
+}
+
 check_red_has_carrier_and_ip() {
        # Interface configured ?
        [ ! -e "/var/ipfire/red/iface" ] && return 0;
@@ -264,7 +277,7 @@ write_forward_conf() {
                                        echo
                                        ;;
                        esac
-               done < /var/ipfire/dnsforward/config
+               done < ${DNSFORWARD}
 
                if [ -n "${insecure_zones}" ]; then
                        echo "server:"
@@ -273,6 +286,33 @@ write_forward_conf() {
                                echo "  domain-insecure: ${zone}"
                        done
                fi
+
+               # Add DNS-over-TLS forwarder configuration
+               if grep -q '^on,' ${DOT}; then
+                       echo "# DNS-over-TLS configuration block"
+                       echo "server:"
+                       echo "    tls-cert-bundle: /etc/ssl/certs/ca-bundle.crt"
+                       echo "    qname-minimisation-strict: yes"
+                       echo
+                       echo "forward-zone:"
+                       echo "    name: \".\""
+                       echo "    forward-tls-upstream: yes"
+                       echo
+                       read_dot_servers
+               else
+                       echo "# DNS forward configuration block"
+                       echo "forward-zone:"
+                       echo "    name: \".\""
+                       echo
+                       read_name_servers
+               fi
+
+               local enabled domain ip port remark
+               while IFS="," read -r enabled domain ip port remark; do
+                       # Line must be enabled
+                       [ "${enabled}" = "on"  ] || continue
+                       echo "    forward-addr: ${ip}@${port}#${domain}"
+               done < ${DOT}
        ) > /etc/unbound/forward.conf
 }
 
@@ -450,7 +490,7 @@ ns_supports_tcp() {
        shift
 
        # If TCP is forced we know by now if the server responds to it
-       if [ "${FORCE_TCP}" = "on" ]; then
+       if [ "${FORCE_TCP}" = "on" ]; then
                return 0
        fi
 
@@ -805,7 +845,6 @@ case "$1" in
 
                # Update configuration files
                write_tuning_conf
-               write_forward_conf
                write_safe_search_conf
 
                boot_mesg "Starting Unbound DNS Proxy..."
@@ -814,12 +853,20 @@ case "$1" in
                # Make own hostname resolveable
                own_hostname
 
-               # Update any known forwarding name servers
-               update_forwarders
+               # Use update_forwards only in case if DoT or dns forwarding is disabled
+               # otherwise use write_forward_conf
+               if ! grep -q 'on' ${DOT} && ! grep -q 'on' ${DNSFORWARD}; then
+                       update_forwarders
+               else
+                       read_dot_servers        
+               fi
 
                # Update hosts
                update_hosts
 
+               # Write forward conf
+               write_forward_conf
+
                fix_time_if_dns_fail
                ;;
 
@@ -844,7 +891,14 @@ case "$1" in
                        exit 0
                fi
 
-               update_forwarders
+               # Use update_forwards only in case if DoT or dns forwarding is disabled
+               # otherwise use write_forward_conf
+               if ! grep -q 'on' ${DOT} && ! grep -q 'on' ${DNSFORWARD}; then
+                       update_forwarders
+               else
+                       write_forward_conf
+                       read_dot_servers
+               fi
 
                unbound-control flush_negative > /dev/null
                unbound-control flush_bogus > /dev/null