diff --git a/SOURCES/01-dnssec-trigger-hook b/SOURCES/01-dnssec-trigger-hook index f6c7d2a..858af2e 100755 --- a/SOURCES/01-dnssec-trigger-hook +++ b/SOURCES/01-dnssec-trigger-hook @@ -1,98 +1,550 @@ -#!/bin/sh -# -# NetworkManager trigger for in dispatcher.d -# config items -alias unbound-control="/usr/sbin/unbound-control" -alias dnssec-trigger-control="/usr/sbin/dnssec-trigger-control" -alias pidof="/usr/sbin/pidof" -alias nmcli="/usr/bin/nmcli" - -state_dir="/var/run/dnssec-trigger" -validate_forward_zones="no" - -# implementation -ifname="$1" -action="$2" -domains="" -nameservers="" -global_nameservers="" -conn_zones_file="$state_dir/$CONNECTION_UUID" - -################################################################ -# get domains and nameservers if provided by connection going up -case "$action" in - "vpn-up" ) - domains="`echo $VPN_IP4_DOMAINS $VPN_IP6_DOMAINS | tr " " "\n" | sort -u | tr "\n" " " | sed '$s/.$//'`" - nameservers="`echo $VPN_IP4_NAMESERVERS $VPN_IP6_NAMESERVERS`" - ;; - "up" ) - domains="`echo $IP4_DOMAINS $IP6_DOMAINS | tr " " "\n" | sort -u | tr "\n" " " | sed '$s/.$//'`" - nameservers="`echo $IP4_NAMESERVERS $IP6_NAMESERVERS`" - ;; -esac - -######################### -# get global nameservers -if [ -x "`which $nmcli 2>&1`" ]; then - global_nameservers="`$nmcli -f IP4,IP6 dev list | fgrep 'DNS' | awk '{print $2;}'`" -else - global_nameservers="`nm-tool | grep 'DNS:' | awk '{print $2;}'`" -fi -# fix whitespaces -global_nameservers="`echo $global_nameservers`" - - -############################################################ -# configure global nameservers using dnssec-trigger-control -if [ -n "`pidof dnssec-triggerd`" ] ; then - dnssec-trigger-control submit "$global_nameservers" &> /dev/null - logger "dnssec-trigger-hook(networkmanager) $ifname $action added global DNS $global_nameservers" -else - logger "dnssec-trigger-hook(networkmanager) $ifname $action NOT added global DNS - dnssec-triggerd is not running" -fi - -###################################################### -# add forward zones into unbound using unbound-control -if [ -n "`pidof unbound`" ]; then - if [ -r "$conn_zones_file" ]; then - for domain in `cat $conn_zones_file`; do - # Remove forward zone from unbound - if [ "$validate_forward_zones" == "no" ]; then - unbound-control forward_remove +i $domain &> /dev/null - else - unbound-control forward_remove $domain &> /dev/null - fi - unbound-control flush_zone $domain &> /dev/null - unbound-control flush_requestlist &> /dev/null - - logger "dnssec-trigger-hook(networkmanager) $ifname $action removed forward DNS zone $domain" - done - - # Remove file with zones for this connection - rm -f $conn_zones_file &> /dev/null - fi - - if [ "$action" == "vpn-up" ] || [ "$action" == "up" ]; then - if [ -n "$domains" ]; then - for domain in $domains; do - # Add forward zone into unbound - if [ "$validate_forward_zones" == "no" ]; then - unbound-control forward_add +i $domain $nameservers &> /dev/null - else - unbound-control forward_add $domain $nameservers &> /dev/null - fi - unbound-control flush_zone $domain &> /dev/null - unbound-control flush_requestlist &> /dev/null - - # Create zone info file - echo $domain >> $conn_zones_file - - logger "dnssec-trigger-hook(networkmanager) $ifname $action added forward DNS zone $domain $nameservers" - done - fi - fi -else - logger "dnssec-trigger-hook(networkmanager) $ifname $action NOT added forward DNS zone(s) - unbound is not running" -fi - -exit 0 +#!/usr/bin/python2 +# -*- coding: utf-8 -*- +""" +@author: Tomas Hozza +""" + +from gi.repository import NMClient +import socket +import struct +import subprocess +import os +import os.path +import syslog +import sys + + +# DO NOT CHANGE THE VALUE HERE, CHANGE IT IN **DNSSEC_CONF** file +DEFAULT_VALIDATE_FORWARD_ZONES = True +DEFAULT_ADD_WIFI_PROVIDED_ZONES = False + +STATE_DIR = "/var/run/dnssec-trigger" +DNSSEC_CONF = "/etc/dnssec.conf" + +UNBOUND = "/usr/sbin/unbound" +UNBOUND_CONTROL = "/usr/sbin/unbound-control" +DNSSEC_TRIGGER = "/usr/sbin/dnssec-triggerd" +DNSSEC_TRIGGER_CONTROL = "/usr/sbin/dnssec-trigger-control" +PIDOF = "/usr/sbin/pidof" + + +class FZonesConfig: + + """ + Class representing dnssec-trigger script forward zones behaviour + configuration. + """ + + def __init__(self): + self.validate_fzones = DEFAULT_VALIDATE_FORWARD_ZONES + self.add_wifi_zones = DEFAULT_ADD_WIFI_PROVIDED_ZONES + + +class ActiveConnection: + + """ + Simple class representing NM Active Connection with information relevant + for this script. + """ + + TYPE_WIFI = "WIFI" + TYPE_VPN = "VPN" + TYPE_OTHER = "OTHER" + + def __init__(self): + self.type = self.TYPE_OTHER + self.is_default = False + self.nameservers = [] + self.domains = [] + self.uuid = "" + pass + + def __str__(self): + string = "UUID: " + self.get_uuid() + "\n" + string += "TYPE: " + str(self.get_type()) + "\n" + string += "DEFAULT: " + str(self.get_is_default()) + "\n" + string += "NS: " + str(self.get_nameservers()) + "\n" + string += "DOMAINS: " + str(self.get_domains()) + return string + + def get_uuid(self): + return self.uuid + + def get_type(self): + return self.type + + def get_is_default(self): + return self.is_default + + def get_nameservers(self): + return self.nameservers + + def get_domains(self): + return self.domains + + def set_uuid(self, uuid=""): + self.uuid = uuid + + def set_type(self, conn_type=TYPE_OTHER): + if conn_type == self.TYPE_VPN: + self.type = self.TYPE_VPN + elif conn_type == self.TYPE_WIFI: + self.type = self.TYPE_WIFI + else: + self.type = self.TYPE_OTHER + + def set_is_default(self, is_default=True): + self.is_default = is_default + + def set_nameservers(self, servers=[]): + self.nameservers = servers + + def set_domains(self, domains=[]): + self.domains = domains + + +def ip4_to_str(ip4): + """ + Converts IPv4 address from integer to string. + """ + return socket.inet_ntop(socket.AF_INET, struct.pack("=I", ip4)) + + +def ip6_to_str(ip6): + """ + Converts IPv6 address from integer to string. + """ + addr_struct = ip6 + return socket.inet_ntop(socket.AF_INET6, addr_struct) + + +def get_fzones_settings_from_conf(conf_file=""): + """ + Reads the forward zones behaviour config from file. + """ + config = FZonesConfig() + + try: + with open(conf_file, "r") as f: + lines = [l.strip() + for l in f.readlines() if l.strip() and not l.strip().startswith("#")] + for line in lines: + option_line = line.split("=") + if option_line: + if option_line[0].strip() == "validate_connection_provided_zones": + if option_line[1].strip() == "yes": + config.validate_fzones = True + else: + config.validate_fzones = False + elif option_line[0].strip() == "add_wifi_provided_zones": + if option_line[1].strip() == "yes": + config.add_wifi_zones = True + else: + config.add_wifi_zones = False + except IOError: + # we don't mind if the config file does not exist + pass + + return config + + +def get_nm_active_connections(): + """ + Process Active Connections from NM and return list of ActiveConnection + objects. Active Connections from NM without nameservers are ignored. + """ + result = [] + client = NMClient.Client() + ac = client.get_active_connections() + + for connection in ac: + new_connection = ActiveConnection() + + # get the UUID + new_connection.set_uuid(connection.get_uuid()) + + # Find out if the ActiveConnection is VPN, WIFI or OTHER + try: + connection.get_vpn_state() + except AttributeError: + # We don't need to change anything + pass + else: + new_connection.set_type(ActiveConnection.TYPE_VPN) + + # if the connection is NOT VPN, then check if it's WIFI + if new_connection.get_type() != ActiveConnection.TYPE_VPN: + try: + device_type = connection.get_devices()[ + 0].get_device_type().value_name + except IndexError: + # if there is no device for a connection, the connection + # is going down so ignore it... + continue + except AttributeError: + # We don't need to change anything + pass + else: + if device_type == "NM_DEVICE_TYPE_WIFI": + new_connection.set_type(ActiveConnection.TYPE_WIFI) + + # Finc out if default connection for IP4 or IP6 + if connection.get_default() or connection.get_default6(): + new_connection.set_is_default(True) + else: + new_connection.set_is_default(False) + + # Get nameservers (IP4 + IP6) + ips = [] + try: + ips4_int = connection.get_ip4_config().get_nameservers() + except AttributeError: + # we don't mind if there are no IP4 nameservers + pass + else: + for ip4 in ips4_int: + ips.append(ip4_to_str(ip4)) + try: + num = connection.get_ip6_config().get_num_nameservers() + for i in range(0,num): + ips.append(ip6_to_str(connection.get_ip6_config().get_nameserver(i))) + except AttributeError: + # we don't mind if there are no IP6 nameservers + pass + new_connection.set_nameservers(ips) + + # Get domains (IP4 + IP6) + domains = [] + try: + domains.extend(connection.get_ip4_config().get_domains()) + except AttributeError: + # we don't mind if there are no IP6 domains + pass + try: + domains.extend(connection.get_ip6_config().get_domains()) + except AttributeError: + # we don't mind if there are no IP6 domains + pass + new_connection.set_domains(domains) + + # If there are no nameservers in the connection, it is useless + if new_connection.get_nameservers(): + result.append(new_connection) + + return result + + +def is_running(binary=""): + """ + Checks if the given binary is running. + """ + if binary: + sp = subprocess.Popen(PIDOF + " " + binary, + stdout=subprocess.PIPE, + stderr=open(os.devnull, "wb"), + shell=True) + sp.wait() + if sp.returncode == 0: + # pidof returns "0" if at least one program with the name runs + return True + return False + + +def dnssec_trigger_set_global_ns(servers=[]): + """ + Configures global nameservers into dnssec-trigger. + """ + if servers: + servers_list = " ".join(servers) + ret = subprocess.call( + DNSSEC_TRIGGER_CONTROL + " submit " + servers_list, + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + if ret == 0: + syslog.syslog( + syslog.LOG_INFO, "Global forwarders added: " + servers_list) + else: + syslog.syslog( + syslog.LOG_ERR, "Global forwarders NOT added: " + servers_list) + + +def unbound_add_forward_zone(domain="", servers=[], secure=DEFAULT_VALIDATE_FORWARD_ZONES): + """ + Adds a forward zone into the unbound. + """ + if domain and servers: + servers_list = " ".join(servers) + # build the command + cmd = UNBOUND_CONTROL + " forward_add" + if not secure: + cmd += " +i" + cmd += " " + domain + " " + servers_list + # Add the forward zone + ret = subprocess.call(cmd, + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + # Flush cache + subprocess.call(UNBOUND_CONTROL + " flush_zone " + domain, + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + subprocess.call(UNBOUND_CONTROL + " flush_requestlist", + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + + if secure: + validated = "(DNSSEC validated)" + else: + validated = "(*NOT* DNSSEC validated)" + + if ret == 0: + syslog.syslog( + syslog.LOG_INFO, "Added " + validated + " connection provided forward zone '" + domain + "' with NS: " + servers_list) + else: + syslog.syslog( + syslog.LOG_ERR, "NOT added connection provided forward zone '" + domain + "' with NS: " + servers_list) + + +def unbound_del_forward_zone(domain="", secure=DEFAULT_VALIDATE_FORWARD_ZONES): + """ + Deletes a forward zone from the unbound. + """ + if domain: + cmd = UNBOUND_CONTROL + " forward_remove" + if not secure: + cmd += " +i" + cmd += " " + domain + # Remove the forward zone + ret = subprocess.call(cmd, + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + # Flush cache + subprocess.call(UNBOUND_CONTROL + " flush_zone " + domain, + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + subprocess.call(UNBOUND_CONTROL + " flush_requestlist", + stdout=open(os.devnull, "wb"), + stderr=subprocess.STDOUT, + shell=True) + if ret == 0: + syslog.syslog( + syslog.LOG_INFO, "Removed connection provided forward zone '" + domain + "'") + else: + syslog.syslog( + syslog.LOG_ERR, "NOT removed connection provided forward zone '" + domain + "'") + + +def unbound_get_forward_zones(): + """ + Returns list of currently configured forward zones from the unbound. + """ + zones = [] + # get all configured forward zones + sp = subprocess.Popen(UNBOUND_CONTROL + " list_forwards", + stdout=subprocess.PIPE, + stderr=open(os.devnull, "wb"), + shell=True) + + sp.wait() + + if sp.returncode == 0: + for line in sp.stdout.readlines(): + zones.append(line.strip().split(" ")[0][:-1]) + + return zones + +############################################################################## + + +def append_fzone_to_file(uuid="", zone=""): + """ + Append forward zones from connection with UUID to the disk file. + """ + if uuid and zone: + with open(os.path.join(STATE_DIR, uuid), "a") as f: + f.write(zone + "\n") + + +def write_fzones_to_file(uuid="", zones=[]): + """ + Write forward zones from connection with UUID to the disk file. + """ + if uuid and zones: + with open(os.path.join(STATE_DIR, uuid), "w") as f: + for zone in zones: + f.write(zone + "\n") + + +def get_fzones_from_file(uuid=""): + """ + Gets all zones from a file with specified UUID name din STATE_DIR + """ + zones = [] + if uuid: + with open(os.path.join(STATE_DIR, uuid), "r") as f: + zones = [line.strip() for line in f.readlines()] + return zones + + +def get_fzones_from_disk(): + """ + Gets all forward zones from the disk STATE_DIR. + Return a dict of "zone" : "connection UUID" + """ + zones = {} + conn_files = os.listdir(STATE_DIR) + for uuid in conn_files: + for zone in get_fzones_from_file(uuid): + zones[zone] = uuid + return zones + + +def del_all_fzones_from_file(uuid="", secure=DEFAULT_VALIDATE_FORWARD_ZONES): + """ + Removes all forward zones contained in file with UUID name in STATE_DIR. + """ + if uuid: + with open(os.path.join(STATE_DIR, uuid), "r") as f: + for line in f.readlines(): + unbound_del_forward_zone(line.strip(), secure) + + +def del_fzones_for_nonexisting_conn(ac=[], secure=DEFAULT_VALIDATE_FORWARD_ZONES): + """ + Removes all forward zones contained in file (in STATE_DIR) for non-existing + active connections. + """ + ac_uuid_list = [conn.get_uuid() for conn in ac] + conn_files = os.listdir(STATE_DIR) + # Remove all non-existing connections zones + for uuid in conn_files: + if uuid not in ac_uuid_list: + # remove all zones from the file + del_all_fzones_from_file(uuid, secure) + # remove the file + os.unlink(os.path.join(STATE_DIR, uuid)) + + +def del_fzone_from_file(uuid="", zone=""): + """ + Deletes a zone from file and writes changes into it. If there are no zones + left, the file is deleted. + """ + if uuid and zone: + zones = get_fzones_from_file(uuid) + zones.remove(zone) + if zones: + write_fzones_to_file(uuid, zones) + else: + os.unlink(os.path.join(STATE_DIR, uuid)) + + +############################################################################## + + +def configure_global_forwarders(active_connections=[]): + """ + Configure global forwarders using dnssec-trigger-control + """ + # get only default connections + default_conns = filter(lambda x: x.get_is_default(), active_connections) + # get forwarders from default connections + default_forwarders = [] + for conn in default_conns: + default_forwarders.extend(conn.get_nameservers()) + + if default_forwarders: + dnssec_trigger_set_global_ns(default_forwarders) + +############################################################################## + + +def configure_forward_zones(active_connections=[], fzones_config=None): + """ + Configures forward zones in the unbound using unbound-control. + """ + # Filter out WIFI connections if desirable + if not fzones_config.add_wifi_zones: + connections = filter( + lambda x: x.get_type() != ActiveConnection.TYPE_WIFI, active_connections) + else: + connections = active_connections + # If validate forward zones + secure = fzones_config.validate_fzones + + # Filter active connections with domain(s) + conns_with_domains = filter(lambda x: x.get_domains(), connections) + fzones_from_ac = {} + # Construct dict of domain -> active connection + for conn in conns_with_domains: + # iterate through all domains in the active connection + for domain in conn.get_domains(): + # if there is already such a domain + if domain in fzones_from_ac: + # if the "conn" is VPN and the conn for existing domain is not + if fzones_from_ac[domain].get_type() != ActiveConnection.TYPE_VPN and conn.get_type() == ActiveConnection.TYPE_VPN: + fzones_from_ac[domain] = conn + # if none of there connections are VPNs or both are VPNs, + # prefer the default one + elif not fzones_from_ac[domain].get_is_default() and conn.get_is_default(): + fzones_from_ac[domain] = conn + else: + fzones_from_ac[domain] = conn + + # Remove all zones which connection UUID does not match any existing AC + del_fzones_for_nonexisting_conn(conns_with_domains, secure) + + # Remove all zones which connection UUID is different than the current AC + # UUID for the zone + fzones_from_disk = get_fzones_from_disk() + for zone, uuid in fzones_from_disk.iteritems(): + connection = fzones_from_ac[zone] + # if the AC UUID is NOT the same as from the disk, remove the zone + if connection.get_uuid() != uuid: + unbound_del_forward_zone(zone, secure) + del_fzone_from_file(uuid, zone) + + # get zones from unbound and delete them from fzones_from_ac + # there may be zones manually configured in unbound.conf and we + # don't want to replace them + unbound_zones = unbound_get_forward_zones() + for zone in unbound_zones: + try: + del fzones_from_ac[zone] + except KeyError: + # we don't mind if there is no such zone + pass + + # Add forward zones that are not already configured + fzones_from_disk = get_fzones_from_disk() + for zone, connection in fzones_from_ac.iteritems(): + if zone not in fzones_from_disk: + unbound_add_forward_zone( + zone, connection.get_nameservers(), secure) + append_fzone_to_file(connection.get_uuid(), zone) + + +############################################################################## + + +if __name__ == "__main__": + if not is_running(DNSSEC_TRIGGER): + syslog.syslog(syslog.LOG_ERR, "dnssec-triggerd daemon is not running!") + sys.exit(1) + if not is_running(UNBOUND): + syslog.syslog(syslog.LOG_ERR, "unbound server daemon is not running!") + sys.exit(1) + + fzones_config = get_fzones_settings_from_conf(DNSSEC_CONF) + + # Get all actove connections from NM + ac = get_nm_active_connections() + # Configure global forwarders + configure_global_forwarders(ac) + # Configure forward zones + configure_forward_zones(ac, fzones_config) diff --git a/SOURCES/dnssec-trigger-0.11-gui.patch b/SOURCES/dnssec-trigger-0.11-gui.patch deleted file mode 100644 index 7b638e1..0000000 --- a/SOURCES/dnssec-trigger-0.11-gui.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff -Naur dnssec-trigger-0.11-orig/panel/pui.xml dnssec-trigger-0.11/panel/pui.xml ---- dnssec-trigger-0.11-orig/panel/pui.xml 2012-03-29 09:48:23.000000000 -0400 -+++ dnssec-trigger-0.11/panel/pui.xml 2012-06-17 12:07:03.806278004 -0400 -@@ -17,7 +17,9 @@ - True - False - Some networks need insecure signon. After you log in to the --network via its portal page, select <i>Reprobe</i> to get secure again. -+network via its portal page, the network will be secured again -+automatically. You can also select <i>Reprobe</i> to attempt to -+force it to go into secure mode. - - <i>Please, stay safe out there.</i> - True -@@ -81,7 +83,7 @@ - - False - 5 -- No Web Access -+ Web traffic hijacked - dialog - True - -@@ -138,12 +140,12 @@ - - True - False -- There is no web access on this network. Do you have to login for that? -+ The web traffic on this network is being hijacked. Is this a hotspot? - --While you login you are <i>insecure</i>, for backwards compatibility, until --dnssec-trigger can detect web access. -+While you login you are <i>insecure</i>, until the traffic hijacking has -+stopped and dnssec-trigger has detected regular web access. - --<i>Skip</i> this if you do not have to log in on this network. -+<i>Skip</i> if you are not logging into a hotspot right now . - True - - diff --git a/SOURCES/dnssec-trigger-0.11-improve_dialog_texts.patch b/SOURCES/dnssec-trigger-0.11-improve_dialog_texts.patch new file mode 100644 index 0000000..360bfef --- /dev/null +++ b/SOURCES/dnssec-trigger-0.11-improve_dialog_texts.patch @@ -0,0 +1,134 @@ +From d01ec0b07d425580cf3dcf7246ec807dbcf1aa5e Mon Sep 17 00:00:00 2001 +From: Tomas Hozza +Date: Fri, 15 Nov 2013 10:44:45 +0100 +Subject: [PATCH] Improve texts in dialogs to be more clear + +Improve texts in Hotspot sing-on dialog and also +some dialogs labels and pop-up panel button label, +to describe the situation more clearly. + +Changes are proposed and reviewed by Red Hat +Documentation Team. + +Signed-off-by: Tomas Hozza +--- + panel/pui.xml | 48 +++++++++++++++++++++++++++--------------------- + 1 file changed, 27 insertions(+), 21 deletions(-) + +diff --git a/panel/pui.xml b/panel/pui.xml +index 4915d83..f1051b2 100644 +--- a/panel/pui.xml ++++ b/panel/pui.xml +@@ -4,7 +4,7 @@ + + False + 5 +- Hotspot Signon ++ Disable DNSSEC for Hotspot Sign On + dialog + + +@@ -16,10 +16,15 @@ + + True + False +- Some networks need insecure signon. After you log in to the +-network via its portal page, select <i>Reprobe</i> to get secure again. ++ Some networks, such as Hotspots, require you to sign on, or register, ++before allowing full network access. By clicking <i>OK</i>, DNSSEC will be ++disabled to allow you to connect to the captive portal's sign-on ++page. After you have signed on and full network access has been enabled, ++DNSSEC-trigger should detect this and enable DNSSEC again. You can also ++select <i>Reprobe</i> to attempt to establish a secure connection to a DNSSEC ++capable name server. + +-<i>Please, stay safe out there.</i> ++<i>A red exclamation mark in the icon warns you when DNSSEC is disabled.</i> + True + + +@@ -138,10 +143,10 @@ network via its portal page, select <i>Reprobe</i> to get secure aga + + True + False +- There is no web access on this network. Do you have to login for that? ++ There is no access to external websites from this network. Do you have to login for that? + +-While you login you are <i>insecure</i>, for backwards compatibility, until +-dnssec-trigger can detect web access. ++When you select <i>Log in</i>, DNSSEC will be disabled for backwards compatibility reasons, until ++DNSSEC-trigger can detect web access. + + <i>Skip</i> this if you do not have to log in on this network. + True +@@ -162,7 +167,7 @@ dnssec-trigger can detect web access. + + False + 5 +- probe dnssec results ++ Results of DNSSEC probe + 400 + 280 + normal +@@ -251,7 +256,7 @@ dnssec-trigger can detect web access. + True + False + False +- Hotspot signon ++ Hotspot sign-on + True + + +@@ -331,28 +336,29 @@ dnssec-trigger can detect web access. + + True + False +- <b>The Network Fails to Support DNSSEC</b> ++ <b>This Network Fails to Support DNSSEC</b> + +-The network you are connected to does not allow DNSSEC, via +-the provided DNS caches, nor via contacting servers on the +-internet directly (it filters traffic to this end). It is not possible +-to provide DNSSEC security, but you can connect insecurely. ++The network you are connected to does not allow DNS Security ++Extensions (DNSSEC) via the provided DNS caches, nor via contacting ++DNS name servers on the Internet directly (it filters traffic ++to this end). It is not possible to provide DNSSEC, but you can ++connect insecurely. + + Do you want to connect insecurely? + +-* if you choose <b>Disconnect</b> then DNS is disabled. It is safe, +-but there is very little that works. ++* if you choose <b>Disconnect</b> then DNS is disabled. ++It is safe, but there is very little that works. + +-* if you choose <b>Insecure</b> then the DNSSEC security is lost. ++* if you choose <b>Insecure</b> then DNSSEC is disabled and security is lost. + You can connect and work. But there is no safety. The network + interferes with DNSSEC, it may also interfere with other things. + Have caution and work with sensitive personal and financial + things some other time. + +-Some hotspots may work after you have gained access via +-its signon page. Then use <i>Reprobe</i> from the menu to retry. ++Some Hotspots may work after you have gained access via ++its sign-on page. Then use <i>Reprobe</i> from the menu to retry. + +-<i>Stay safe out there!</i> ++<i>A red exclamation mark in the icon warns you when DNSSEC is disabled.</i> + True + + +@@ -383,7 +389,7 @@ its signon page. Then use <i>Reprobe</i> from the menu to retry. + + True + False +- There is a software update available for dnssec-trigger. ++ There is a software update available for DNSSEC-trigger. + Do you wish to install the update? + True + +-- +1.8.3.1 + diff --git a/SOURCES/dnssec-trigger.conf b/SOURCES/dnssec-trigger.conf index 619756d..cf0be7a 100644 --- a/SOURCES/dnssec-trigger.conf +++ b/SOURCES/dnssec-trigger.conf @@ -1,4 +1,4 @@ -# Fedora/EPEL version of dnssec-trigger.conf +# Red Hat Enterprise Linux 7 version of dnssec-trigger.conf # logging detail, 0=only errors, 1=operations, 2=detail, 3,4 debug detail. # verbosity: 1 diff --git a/SOURCES/dnssec-triggerd-resolvconf-handle.service b/SOURCES/dnssec-triggerd-resolvconf-handle.service new file mode 100644 index 0000000..a23760c --- /dev/null +++ b/SOURCES/dnssec-triggerd-resolvconf-handle.service @@ -0,0 +1,11 @@ +[Unit] +Description=Backups and restores /etc/resolv.conf after dnssec-trigger starts/stops +PartOf=dnssec-triggerd.service + + +[Service] +Type=oneshot +RemainAfterExit=yes + +ExecStart=/usr/libexec/dnssec-triggerd-resolvconf-handle.sh backup +ExecStop=/usr/libexec/dnssec-triggerd-resolvconf-handle.sh restore diff --git a/SOURCES/dnssec-triggerd-resolvconf-handle.sh b/SOURCES/dnssec-triggerd-resolvconf-handle.sh new file mode 100755 index 0000000..622df12 --- /dev/null +++ b/SOURCES/dnssec-triggerd-resolvconf-handle.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# dnssec-trigger script handling possible backup and restore of resolv.conf + +SCRIPT_NAME="dnssec-trigger-resolvconf-handle.sh" +STATE_DIR="/var/run/dnssec-trigger" +RESOLV_CONF="/etc/resolv.conf" +RESOLV_CONF_BAK="$STATE_DIR/resolv.conf.bak" +NM_CONFIG="/etc/NetworkManager/NetworkManager.conf" + +usage() +{ + echo + echo "This script backs up or restores /etc/resolv.conf content" + echo "Usage: $SCRIPT_NAME [backup|restore]" +} + +# check number of arguments +if ! [ "$#" -eq 1 ]; then + echo "ERROR: Wrong number of arguments!" + usage + exit 1 +fi + +does_nm_handle_resolv_conf() +{ + grep -x "^dns=none" $NM_CONFIG &> /dev/null + echo "$?" +} + +backup_resolv_conf() +{ + # find out if NM handles the resolv.conf + if [ "`does_nm_handle_resolv_conf`" -eq 0 ]; then + cp -fp $RESOLV_CONF $RESOLV_CONF_BAK + fi +} + +restore_resolv_conf() +{ + # if we have a backup and NM does not handle resolv.conf -> restore it + if [ "`does_nm_handle_resolv_conf`" -eq 0 ] && [ -s $RESOLV_CONF_BAK ]; then + cp -fp $RESOLV_CONF_BAK $RESOLV_CONF + else + # let NM rewrite the resolv.conf + systemctl restart NetworkManager.service + fi +} + +case "$1" in + backup) + backup_resolv_conf + ;; + restore) + restore_resolv_conf + ;; + *) + echo "ERROR: Wrong argument!" + usage + exit 1 +esac + +exit 0 diff --git a/SOURCES/dnssec-triggerd.service b/SOURCES/dnssec-triggerd.service index bc8fcd8..9d55778 100644 --- a/SOURCES/dnssec-triggerd.service +++ b/SOURCES/dnssec-triggerd.service @@ -3,6 +3,8 @@ Description=Reconfigure local DNS(SEC) resolver on network change After=syslog.target network.target After=dnssec-triggerd-keygen.service Wants=dnssec-triggerd-keygen.service +After=dnssec-triggerd-resolvconf-handle.service +Wants=dnssec-triggerd-resolvconf-handle.service After=unbound.service Wants=unbound.service diff --git a/SOURCES/dnssec.conf.sample b/SOURCES/dnssec.conf.sample new file mode 100644 index 0000000..dc1325b --- /dev/null +++ b/SOURCES/dnssec.conf.sample @@ -0,0 +1,54 @@ +# validate_connection_provided_zones: +# ----------------------------------- +# Setts if forward zones added into unbound by dnssec-trigger script +# will be DNSSEC validated or NOT. Note that this setting is global +# for all added forward zones.. +# Possible options are: +# +# validate_connection_provided_zones=yes - All connection provided zones +# configured as forward zones into +# unbound WILL BE DNSSEC validated +# (NOTE: If connection provided DNS +# servers are NOT DNSSEC capable, the +# resolving of provided zones will +# NOT work!) +# +# validate_connection_provided_zones=no - All connection provided zones +# configured as forward zones into +# unbound will NOT be DNSSEC validated +# +# +# NOTICE: if you turn the validation OFF then all forward zones added by +# dnssec-trigger script will NOT be DNSSEC validated. If you turn the +# validation ON, only newly added forward zones will be DNSSEC validated. +# Forward zones added before the change will still NOT be DNSSEC validated. +# To force validation of previously added forward zone you need to restart +# it. For VPNs this can be done by restart NetworkManager. +validate_connection_provided_zones=yes + +# add_wifi_provided_zones: +# ------------------------ +# Setts if domains provided by WiFi connection are configured as forward zones +# into unbound. +# Possible options are: +# +# add_wifi_provided_zones=yes - Domains provided by ANY WiFi connection will +# be configured as forward zones into unbound. +# (NOTE: See the possible security implications +# stated below!) +# +# add_wifi_provided_zones=no - Domains provided by ANY WiFi connection will +# NOT be configured as forward zones into unbound. +# (NOTE: Forward zones will be still configured +# for any other type of connection!) +# +# NOTICE: Turning ON the addition of WiFi provided domains as forward zones +# into unbound may have SECURITY implications such as: +# - A WiFi access point can intentionally provide you a domain via DHCP for +# which it does not have authority and route all your DNS queries to its +# DNS servers. +# - In addition to the previous point, if you have the DNSSEC validation +# of forward zones turned OFF, the WiFi provided DNS servers can spoof +# the IP address for domain names from the provided domain WITHOUT YOU +# KNOWING IT! +add_wifi_provided_zones=no diff --git a/SPECS/dnssec-trigger.spec b/SPECS/dnssec-trigger.spec index 9b893b7..b567eaf 100644 --- a/SPECS/dnssec-trigger.spec +++ b/SPECS/dnssec-trigger.spec @@ -1,33 +1,37 @@ Summary: NetworkManager plugin to update/reconfigure DNSSEC resolving Name: dnssec-trigger Version: 0.11 -Release: 14%{?dist} +Release: 21%{?dist} License: BSD Url: http://www.nlnetlabs.nl/downloads/dnssec-trigger/ Source: http://www.nlnetlabs.nl/downloads/dnssec-trigger/%{name}-%{version}.tar.gz Source1:dnssec-triggerd.service Source2: dnssec-triggerd-keygen.service Source3: dnssec-trigger.conf -# Latest NM dispatcher hook from upstream SVN -# http://www.nlnetlabs.nl/svn/dnssec-trigger/trunk/01-dnssec-trigger-hook.sh.in +# Latest NM dispatcher Python hook from upstream SVN +# http://www.nlnetlabs.nl/svn/dnssec-trigger/trunk/contrib/01-dnssec-trigger-hook-new_nm Source4: 01-dnssec-trigger-hook Source5: dnssec-trigger.tmpfiles.d -Patch1: dnssec-trigger-0.11-gui.patch +Source6: dnssec-triggerd-resolvconf-handle.sh +Source7: dnssec-triggerd-resolvconf-handle.service +# http://www.nlnetlabs.nl/svn/dnssec-trigger/trunk/contrib/dnssec.conf.sample +Source8: dnssec.conf.sample +Patch1: dnssec-trigger-0.11-improve_dialog_texts.patch Patch2: dnssec-trigger-842455.patch # https://www.nlnetlabs.nl/bugs-script/show_bug.cgi?id=489 Patch3: dnssec-trigger-0.11-nl489.patch Patch4: dnssec-trigger-0.11-coverity_scan.patch Requires(postun): initscripts -Requires: ldns >= 1.6.10, NetworkManager, unbound, xdg-utils +Requires: ldns >= 1.6.10, NetworkManager, NetworkManager-glib, unbound, xdg-utils Requires(pre): shadow-utils BuildRequires: desktop-file-utils systemd-units, openssl-devel, ldns-devel BuildRequires: gtk2-devel, NetworkManager-devel -Requires(post): systemd-sysv -Requires(post): systemd-units -Requires(preun): systemd-units -Requires(postun): systemd-units +BuildRequires: systemd +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd %description dnssec-trigger reconfigures the local unbound DNS server. This unbound DNS @@ -41,8 +45,6 @@ dnssec-trigger-applet the option to go with insecure DNS only. # Fixup the name to not include "panel" in the menu item or name sed -i "s/ Panel//" panel/dnssec-trigger-panel.desktop.in sed -i "s/-panel//" panel/dnssec-trigger-panel.desktop.in -# NM has no /usr/sbin in path -sed -i "s/^dnssec-trigger-control/\/usr\/sbin\/dnssec-trigger-control/" 01-dnssec-trigger-hook.sh.in # change some text in the popups %patch1 -p1 %patch2 -p1 @@ -63,10 +65,16 @@ install -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/%{name}d.service install -m 0644 %{SOURCE2} %{buildroot}%{_unitdir}/%{name}d-keygen.service install -m 0644 %{SOURCE3} %{buildroot}%{_sysconfdir}/%{name}/ +mkdir -p %{buildroot}%{_libexecdir} +install -m 0755 %{SOURCE6} %{buildroot}%{_libexecdir}/%{name}d-resolvconf-handle.sh +install -m 0644 %{SOURCE7} %{buildroot}%{_unitdir}/%{name}d-resolvconf-handle.service + desktop-file-install --dir=%{buildroot}%{_datadir}/applications dnssec-trigger-panel.desktop -# overwrite the stock NM hook since there is new one in upstream SVN that has not been released yet -cp -p %{SOURCE4} %{buildroot}/%{_sysconfdir}/NetworkManager/dispatcher.d/01-dnssec-trigger-hook +# overwrite the stock NM hook since there is new one in upstream SVN that is not used by default +install -p -m 0755 %{SOURCE4} %{buildroot}/%{_sysconfdir}/NetworkManager/dispatcher.d/01-dnssec-trigger-hook +#install the /etc/dnssec.conf configuration file +install -p -m 0644 %{SOURCE8} %{buildroot}/%{_sysconfdir}/dnssec.conf # install the configuration for /var/run/dnssec-trigger into tmpfiles.d dir mkdir -p %{buildroot}%{_tmpfilesdir} @@ -93,9 +101,11 @@ rm -rf ${RPM_BUILD_ROOT} %doc README LICENSE %{_unitdir}/%{name}d.service %{_unitdir}/%{name}d-keygen.service +%{_unitdir}/%{name}d-resolvconf-handle.service %attr(0755,root,root) %dir %{_sysconfdir}/%{name} %attr(0755,root,root) %{_sysconfdir}/NetworkManager/dispatcher.d/01-dnssec-trigger-hook +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/dnssec.conf %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/dnssec-trigger.conf %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/xdg/autostart/dnssec-trigger-panel.desktop %dir %{_localstatedir}/run/%{name} @@ -103,6 +113,7 @@ rm -rf ${RPM_BUILD_ROOT} %{_bindir}/dnssec-trigger-panel %{_bindir}/dnssec-trigger %{_sbindir}/dnssec-trigger* +%{_libexecdir}/%{name}d-resolvconf-handle.sh %{_mandir}/*/* %attr(0755,root,root) %dir %{_datadir}/%{name} %attr(0644,root,root) %{_datadir}/%{name}/* @@ -110,26 +121,46 @@ rm -rf ${RPM_BUILD_ROOT} %post -# Enable (but don't start) the units by default - /bin/systemctl enable %{name}d.service >/dev/null 2>&1 || : - /bin/systemctl enable %{name}d-keygen.service >/dev/null 2>&1 || : +%systemd_post %{name}d.service %preun +%systemd_preun %{name}d.service if [ "$1" -eq "0" ] ; then - # Package removal, not upgrade - /bin/systemctl --no-reload disable %{name}d.service > /dev/null 2>&1 || : - /bin/systemctl --no-reload disable %{name}d-keygen.service > /dev/null 2>&1 || : - /bin/systemctl stop %{name}d.service >/dev/null 2>&1 || : - /bin/systemctl stop %{name}d-keygen.service >/dev/null 2>&1 || : # dnssec-triggerd makes /etc/resolv.conf immutable, undo that on removal chattr -i /etc/resolv.conf fi -%postun - /bin/systemctl daemon-reload >/dev/null 2>&1 || : +%postun +%systemd_postun_with_restart %{name}d.service + %changelog +* Tue Feb 11 2014 Tomas Hozza - 0.11-21 +- handle IndexError exception in NM script until NM provides better API (#1063735) +- restart NM when stopping dnssec-trigger daemon instead of handling + resolv.conf by ourself. (#1061370) + +* Wed Jan 29 2014 Tomas Hozza - 0.11-20 +- use systemd macros instead of directly using systemctl (#1058773) +- Replace the "Fedora /EPEL" comment in dnssec-trigger.conf (#1055949) +- Use more newer and more advanced dispatcher script (#1034813) + +* Fri Jan 24 2014 Daniel Mach - 0.11-19 +- Mass rebuild 2014-01-24 + +* Fri Dec 27 2013 Daniel Mach - 0.11-18 +- Mass rebuild 2013-12-27 + +* Tue Nov 26 2013 Tomas Hozza - 0.11-17 +- Add script to backup and restore resolv.conf on dnssec-trigger start/stop (#1031648) + +* Mon Nov 18 2013 Tomas Hozza - 0.11-16 +- Improve GUI dialogs texts (#1029889) + +* Mon Nov 11 2013 Tomas Hozza - 0.11-15 +- Fix the dispatcher script to use new nmcli syntax (#1028003) + * Mon Aug 26 2013 Tomas Hozza - 0.11-14 - Fix errors found by static analysis of source