|
|
0c84e1 |
#!/usr/bin/python2
|
|
|
0c84e1 |
# -*- coding: utf-8 -*-
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
@author: Tomas Hozza <thozza@redhat.com>
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
|
|
|
0c84e1 |
from gi.repository import NMClient
|
|
|
0c84e1 |
import socket
|
|
|
0c84e1 |
import struct
|
|
|
0c84e1 |
import subprocess
|
|
|
0c84e1 |
import os
|
|
|
0c84e1 |
import os.path
|
|
|
0c84e1 |
import syslog
|
|
|
0c84e1 |
import sys
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# DO NOT CHANGE THE VALUE HERE, CHANGE IT IN **DNSSEC_CONF** file
|
|
|
0c84e1 |
DEFAULT_VALIDATE_FORWARD_ZONES = True
|
|
|
0c84e1 |
DEFAULT_ADD_WIFI_PROVIDED_ZONES = False
|
|
|
0c84e1 |
|
|
|
0c84e1 |
STATE_DIR = "/var/run/dnssec-trigger"
|
|
|
0c84e1 |
DNSSEC_CONF = "/etc/dnssec.conf"
|
|
|
0c84e1 |
|
|
|
0c84e1 |
UNBOUND = "/usr/sbin/unbound"
|
|
|
0c84e1 |
UNBOUND_CONTROL = "/usr/sbin/unbound-control"
|
|
|
0c84e1 |
DNSSEC_TRIGGER = "/usr/sbin/dnssec-triggerd"
|
|
|
0c84e1 |
DNSSEC_TRIGGER_CONTROL = "/usr/sbin/dnssec-trigger-control"
|
|
|
0c84e1 |
PIDOF = "/usr/sbin/pidof"
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
class FZonesConfig:
|
|
|
0c84e1 |
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Class representing dnssec-trigger script forward zones behaviour
|
|
|
0c84e1 |
configuration.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def __init__(self):
|
|
|
0c84e1 |
self.validate_fzones = DEFAULT_VALIDATE_FORWARD_ZONES
|
|
|
0c84e1 |
self.add_wifi_zones = DEFAULT_ADD_WIFI_PROVIDED_ZONES
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
class ActiveConnection:
|
|
|
0c84e1 |
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Simple class representing NM Active Connection with information relevant
|
|
|
0c84e1 |
for this script.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
|
|
|
0c84e1 |
TYPE_WIFI = "WIFI"
|
|
|
0c84e1 |
TYPE_VPN = "VPN"
|
|
|
0c84e1 |
TYPE_OTHER = "OTHER"
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def __init__(self):
|
|
|
0c84e1 |
self.type = self.TYPE_OTHER
|
|
|
0c84e1 |
self.is_default = False
|
|
|
0c84e1 |
self.nameservers = []
|
|
|
0c84e1 |
self.domains = []
|
|
|
0c84e1 |
self.uuid = ""
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def __str__(self):
|
|
|
0c84e1 |
string = "UUID: " + self.get_uuid() + "\n"
|
|
|
0c84e1 |
string += "TYPE: " + str(self.get_type()) + "\n"
|
|
|
0c84e1 |
string += "DEFAULT: " + str(self.get_is_default()) + "\n"
|
|
|
0c84e1 |
string += "NS: " + str(self.get_nameservers()) + "\n"
|
|
|
0c84e1 |
string += "DOMAINS: " + str(self.get_domains())
|
|
|
0c84e1 |
return string
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_uuid(self):
|
|
|
0c84e1 |
return self.uuid
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_type(self):
|
|
|
0c84e1 |
return self.type
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_is_default(self):
|
|
|
0c84e1 |
return self.is_default
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_nameservers(self):
|
|
|
0c84e1 |
return self.nameservers
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_domains(self):
|
|
|
0c84e1 |
return self.domains
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def set_uuid(self, uuid=""):
|
|
|
0c84e1 |
self.uuid = uuid
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def set_type(self, conn_type=TYPE_OTHER):
|
|
|
0c84e1 |
if conn_type == self.TYPE_VPN:
|
|
|
0c84e1 |
self.type = self.TYPE_VPN
|
|
|
0c84e1 |
elif conn_type == self.TYPE_WIFI:
|
|
|
0c84e1 |
self.type = self.TYPE_WIFI
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
self.type = self.TYPE_OTHER
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def set_is_default(self, is_default=True):
|
|
|
0c84e1 |
self.is_default = is_default
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def set_nameservers(self, servers=[]):
|
|
|
0c84e1 |
self.nameservers = servers
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def set_domains(self, domains=[]):
|
|
|
0c84e1 |
self.domains = domains
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def ip4_to_str(ip4):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Converts IPv4 address from integer to string.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
return socket.inet_ntop(socket.AF_INET, struct.pack("=I", ip4))
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def ip6_to_str(ip6):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Converts IPv6 address from integer to string.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
addr_struct = ip6
|
|
|
0c84e1 |
return socket.inet_ntop(socket.AF_INET6, addr_struct)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_fzones_settings_from_conf(conf_file=""):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Reads the forward zones behaviour config from file.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
config = FZonesConfig()
|
|
|
0c84e1 |
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
with open(conf_file, "r") as f:
|
|
|
0c84e1 |
lines = [l.strip()
|
|
|
0c84e1 |
for l in f.readlines() if l.strip() and not l.strip().startswith("#")]
|
|
|
0c84e1 |
for line in lines:
|
|
|
0c84e1 |
option_line = line.split("=")
|
|
|
0c84e1 |
if option_line:
|
|
|
0c84e1 |
if option_line[0].strip() == "validate_connection_provided_zones":
|
|
|
0c84e1 |
if option_line[1].strip() == "yes":
|
|
|
0c84e1 |
config.validate_fzones = True
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
config.validate_fzones = False
|
|
|
0c84e1 |
elif option_line[0].strip() == "add_wifi_provided_zones":
|
|
|
0c84e1 |
if option_line[1].strip() == "yes":
|
|
|
0c84e1 |
config.add_wifi_zones = True
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
config.add_wifi_zones = False
|
|
|
0c84e1 |
except IOError:
|
|
|
0c84e1 |
# we don't mind if the config file does not exist
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
|
|
|
0c84e1 |
return config
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_nm_active_connections():
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Process Active Connections from NM and return list of ActiveConnection
|
|
|
0c84e1 |
objects. Active Connections from NM without nameservers are ignored.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
result = []
|
|
|
0c84e1 |
client = NMClient.Client()
|
|
|
0c84e1 |
ac = client.get_active_connections()
|
|
|
0c84e1 |
|
|
|
0c84e1 |
for connection in ac:
|
|
|
0c84e1 |
new_connection = ActiveConnection()
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# get the UUID
|
|
|
0c84e1 |
new_connection.set_uuid(connection.get_uuid())
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Find out if the ActiveConnection is VPN, WIFI or OTHER
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
connection.get_vpn_state()
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# We don't need to change anything
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
new_connection.set_type(ActiveConnection.TYPE_VPN)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# if the connection is NOT VPN, then check if it's WIFI
|
|
|
0c84e1 |
if new_connection.get_type() != ActiveConnection.TYPE_VPN:
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
device_type = connection.get_devices()[
|
|
|
0c84e1 |
0].get_device_type().value_name
|
|
|
0c84e1 |
except IndexError:
|
|
|
0c84e1 |
# if there is no device for a connection, the connection
|
|
|
0c84e1 |
# is going down so ignore it...
|
|
|
0c84e1 |
continue
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# We don't need to change anything
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
if device_type == "NM_DEVICE_TYPE_WIFI":
|
|
|
0c84e1 |
new_connection.set_type(ActiveConnection.TYPE_WIFI)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Finc out if default connection for IP4 or IP6
|
|
|
0c84e1 |
if connection.get_default() or connection.get_default6():
|
|
|
0c84e1 |
new_connection.set_is_default(True)
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
new_connection.set_is_default(False)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Get nameservers (IP4 + IP6)
|
|
|
0c84e1 |
ips = []
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
ips4_int = connection.get_ip4_config().get_nameservers()
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# we don't mind if there are no IP4 nameservers
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
for ip4 in ips4_int:
|
|
|
0c84e1 |
ips.append(ip4_to_str(ip4))
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
num = connection.get_ip6_config().get_num_nameservers()
|
|
|
0c84e1 |
for i in range(0,num):
|
|
|
0c84e1 |
ips.append(ip6_to_str(connection.get_ip6_config().get_nameserver(i)))
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# we don't mind if there are no IP6 nameservers
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
new_connection.set_nameservers(ips)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Get domains (IP4 + IP6)
|
|
|
0c84e1 |
domains = []
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
domains.extend(connection.get_ip4_config().get_domains())
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# we don't mind if there are no IP6 domains
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
domains.extend(connection.get_ip6_config().get_domains())
|
|
|
0c84e1 |
except AttributeError:
|
|
|
0c84e1 |
# we don't mind if there are no IP6 domains
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
new_connection.set_domains(domains)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# If there are no nameservers in the connection, it is useless
|
|
|
0c84e1 |
if new_connection.get_nameservers():
|
|
|
0c84e1 |
result.append(new_connection)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
return result
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def is_running(binary=""):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Checks if the given binary is running.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if binary:
|
|
|
0c84e1 |
sp = subprocess.Popen(PIDOF + " " + binary,
|
|
|
0c84e1 |
stdout=subprocess.PIPE,
|
|
|
0c84e1 |
stderr=open(os.devnull, "wb"),
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
sp.wait()
|
|
|
0c84e1 |
if sp.returncode == 0:
|
|
|
0c84e1 |
# pidof returns "0" if at least one program with the name runs
|
|
|
0c84e1 |
return True
|
|
|
0c84e1 |
return False
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def dnssec_trigger_set_global_ns(servers=[]):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Configures global nameservers into dnssec-trigger.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if servers:
|
|
|
0c84e1 |
servers_list = " ".join(servers)
|
|
|
0c84e1 |
ret = subprocess.call(
|
|
|
0c84e1 |
DNSSEC_TRIGGER_CONTROL + " submit " + servers_list,
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
if ret == 0:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_INFO, "Global forwarders added: " + servers_list)
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_ERR, "Global forwarders NOT added: " + servers_list)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def unbound_add_forward_zone(domain="", servers=[], secure=DEFAULT_VALIDATE_FORWARD_ZONES):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Adds a forward zone into the unbound.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if domain and servers:
|
|
|
0c84e1 |
servers_list = " ".join(servers)
|
|
|
0c84e1 |
# build the command
|
|
|
0c84e1 |
cmd = UNBOUND_CONTROL + " forward_add"
|
|
|
0c84e1 |
if not secure:
|
|
|
0c84e1 |
cmd += " +i"
|
|
|
0c84e1 |
cmd += " " + domain + " " + servers_list
|
|
|
0c84e1 |
# Add the forward zone
|
|
|
0c84e1 |
ret = subprocess.call(cmd,
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
# Flush cache
|
|
|
0c84e1 |
subprocess.call(UNBOUND_CONTROL + " flush_zone " + domain,
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
subprocess.call(UNBOUND_CONTROL + " flush_requestlist",
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
if secure:
|
|
|
0c84e1 |
validated = "(DNSSEC validated)"
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
validated = "(*NOT* DNSSEC validated)"
|
|
|
0c84e1 |
|
|
|
0c84e1 |
if ret == 0:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_INFO, "Added " + validated + " connection provided forward zone '" + domain + "' with NS: " + servers_list)
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_ERR, "NOT added connection provided forward zone '" + domain + "' with NS: " + servers_list)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def unbound_del_forward_zone(domain="", secure=DEFAULT_VALIDATE_FORWARD_ZONES):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Deletes a forward zone from the unbound.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if domain:
|
|
|
0c84e1 |
cmd = UNBOUND_CONTROL + " forward_remove"
|
|
|
0c84e1 |
if not secure:
|
|
|
0c84e1 |
cmd += " +i"
|
|
|
0c84e1 |
cmd += " " + domain
|
|
|
0c84e1 |
# Remove the forward zone
|
|
|
0c84e1 |
ret = subprocess.call(cmd,
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
# Flush cache
|
|
|
0c84e1 |
subprocess.call(UNBOUND_CONTROL + " flush_zone " + domain,
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
subprocess.call(UNBOUND_CONTROL + " flush_requestlist",
|
|
|
0c84e1 |
stdout=open(os.devnull, "wb"),
|
|
|
0c84e1 |
stderr=subprocess.STDOUT,
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
if ret == 0:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_INFO, "Removed connection provided forward zone '" + domain + "'")
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
syslog.syslog(
|
|
|
0c84e1 |
syslog.LOG_ERR, "NOT removed connection provided forward zone '" + domain + "'")
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def unbound_get_forward_zones():
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Returns list of currently configured forward zones from the unbound.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
zones = []
|
|
|
0c84e1 |
# get all configured forward zones
|
|
|
0c84e1 |
sp = subprocess.Popen(UNBOUND_CONTROL + " list_forwards",
|
|
|
0c84e1 |
stdout=subprocess.PIPE,
|
|
|
0c84e1 |
stderr=open(os.devnull, "wb"),
|
|
|
0c84e1 |
shell=True)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
sp.wait()
|
|
|
0c84e1 |
|
|
|
0c84e1 |
if sp.returncode == 0:
|
|
|
0c84e1 |
for line in sp.stdout.readlines():
|
|
|
0c84e1 |
zones.append(line.strip().split(" ")[0][:-1])
|
|
|
0c84e1 |
|
|
|
0c84e1 |
return zones
|
|
|
0c84e1 |
|
|
|
0c84e1 |
##############################################################################
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def append_fzone_to_file(uuid="", zone=""):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Append forward zones from connection with UUID to the disk file.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if uuid and zone:
|
|
|
0c84e1 |
with open(os.path.join(STATE_DIR, uuid), "a") as f:
|
|
|
0c84e1 |
f.write(zone + "\n")
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def write_fzones_to_file(uuid="", zones=[]):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Write forward zones from connection with UUID to the disk file.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if uuid and zones:
|
|
|
0c84e1 |
with open(os.path.join(STATE_DIR, uuid), "w") as f:
|
|
|
0c84e1 |
for zone in zones:
|
|
|
0c84e1 |
f.write(zone + "\n")
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_fzones_from_file(uuid=""):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Gets all zones from a file with specified UUID name din STATE_DIR
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
zones = []
|
|
|
0c84e1 |
if uuid:
|
|
|
0c84e1 |
with open(os.path.join(STATE_DIR, uuid), "r") as f:
|
|
|
0c84e1 |
zones = [line.strip() for line in f.readlines()]
|
|
|
0c84e1 |
return zones
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def get_fzones_from_disk():
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Gets all forward zones from the disk STATE_DIR.
|
|
|
0c84e1 |
Return a dict of "zone" : "connection UUID"
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
zones = {}
|
|
|
0c84e1 |
conn_files = os.listdir(STATE_DIR)
|
|
|
0c84e1 |
for uuid in conn_files:
|
|
|
0c84e1 |
for zone in get_fzones_from_file(uuid):
|
|
|
0c84e1 |
zones[zone] = uuid
|
|
|
0c84e1 |
return zones
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def del_all_fzones_from_file(uuid="", secure=DEFAULT_VALIDATE_FORWARD_ZONES):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Removes all forward zones contained in file with UUID name in STATE_DIR.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if uuid:
|
|
|
0c84e1 |
with open(os.path.join(STATE_DIR, uuid), "r") as f:
|
|
|
0c84e1 |
for line in f.readlines():
|
|
|
0c84e1 |
unbound_del_forward_zone(line.strip(), secure)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def del_fzones_for_nonexisting_conn(ac=[], secure=DEFAULT_VALIDATE_FORWARD_ZONES):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Removes all forward zones contained in file (in STATE_DIR) for non-existing
|
|
|
0c84e1 |
active connections.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
ac_uuid_list = [conn.get_uuid() for conn in ac]
|
|
|
0c84e1 |
conn_files = os.listdir(STATE_DIR)
|
|
|
0c84e1 |
# Remove all non-existing connections zones
|
|
|
0c84e1 |
for uuid in conn_files:
|
|
|
0c84e1 |
if uuid not in ac_uuid_list:
|
|
|
0c84e1 |
# remove all zones from the file
|
|
|
0c84e1 |
del_all_fzones_from_file(uuid, secure)
|
|
|
0c84e1 |
# remove the file
|
|
|
0c84e1 |
os.unlink(os.path.join(STATE_DIR, uuid))
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def del_fzone_from_file(uuid="", zone=""):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Deletes a zone from file and writes changes into it. If there are no zones
|
|
|
0c84e1 |
left, the file is deleted.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
if uuid and zone:
|
|
|
0c84e1 |
zones = get_fzones_from_file(uuid)
|
|
|
0c84e1 |
zones.remove(zone)
|
|
|
0c84e1 |
if zones:
|
|
|
0c84e1 |
write_fzones_to_file(uuid, zones)
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
os.unlink(os.path.join(STATE_DIR, uuid))
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
##############################################################################
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def configure_global_forwarders(active_connections=[]):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Configure global forwarders using dnssec-trigger-control
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
# get only default connections
|
|
|
0c84e1 |
default_conns = filter(lambda x: x.get_is_default(), active_connections)
|
|
|
0c84e1 |
# get forwarders from default connections
|
|
|
0c84e1 |
default_forwarders = []
|
|
|
0c84e1 |
for conn in default_conns:
|
|
|
0c84e1 |
default_forwarders.extend(conn.get_nameservers())
|
|
|
0c84e1 |
|
|
|
0c84e1 |
if default_forwarders:
|
|
|
0c84e1 |
dnssec_trigger_set_global_ns(default_forwarders)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
##############################################################################
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
def configure_forward_zones(active_connections=[], fzones_config=None):
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
Configures forward zones in the unbound using unbound-control.
|
|
|
0c84e1 |
"""
|
|
|
0c84e1 |
# Filter out WIFI connections if desirable
|
|
|
0c84e1 |
if not fzones_config.add_wifi_zones:
|
|
|
0c84e1 |
connections = filter(
|
|
|
0c84e1 |
lambda x: x.get_type() != ActiveConnection.TYPE_WIFI, active_connections)
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
connections = active_connections
|
|
|
0c84e1 |
# If validate forward zones
|
|
|
0c84e1 |
secure = fzones_config.validate_fzones
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Filter active connections with domain(s)
|
|
|
0c84e1 |
conns_with_domains = filter(lambda x: x.get_domains(), connections)
|
|
|
0c84e1 |
fzones_from_ac = {}
|
|
|
0c84e1 |
# Construct dict of domain -> active connection
|
|
|
0c84e1 |
for conn in conns_with_domains:
|
|
|
0c84e1 |
# iterate through all domains in the active connection
|
|
|
0c84e1 |
for domain in conn.get_domains():
|
|
|
0c84e1 |
# if there is already such a domain
|
|
|
0c84e1 |
if domain in fzones_from_ac:
|
|
|
0c84e1 |
# if the "conn" is VPN and the conn for existing domain is not
|
|
|
0c84e1 |
if fzones_from_ac[domain].get_type() != ActiveConnection.TYPE_VPN and conn.get_type() == ActiveConnection.TYPE_VPN:
|
|
|
0c84e1 |
fzones_from_ac[domain] = conn
|
|
|
0c84e1 |
# if none of there connections are VPNs or both are VPNs,
|
|
|
0c84e1 |
# prefer the default one
|
|
|
0c84e1 |
elif not fzones_from_ac[domain].get_is_default() and conn.get_is_default():
|
|
|
0c84e1 |
fzones_from_ac[domain] = conn
|
|
|
0c84e1 |
else:
|
|
|
0c84e1 |
fzones_from_ac[domain] = conn
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Remove all zones which connection UUID does not match any existing AC
|
|
|
0c84e1 |
del_fzones_for_nonexisting_conn(conns_with_domains, secure)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Remove all zones which connection UUID is different than the current AC
|
|
|
0c84e1 |
# UUID for the zone
|
|
|
0c84e1 |
fzones_from_disk = get_fzones_from_disk()
|
|
|
0c84e1 |
for zone, uuid in fzones_from_disk.iteritems():
|
|
|
0c84e1 |
connection = fzones_from_ac[zone]
|
|
|
0c84e1 |
# if the AC UUID is NOT the same as from the disk, remove the zone
|
|
|
0c84e1 |
if connection.get_uuid() != uuid:
|
|
|
0c84e1 |
unbound_del_forward_zone(zone, secure)
|
|
|
0c84e1 |
del_fzone_from_file(uuid, zone)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# get zones from unbound and delete them from fzones_from_ac
|
|
|
0c84e1 |
# there may be zones manually configured in unbound.conf and we
|
|
|
0c84e1 |
# don't want to replace them
|
|
|
0c84e1 |
unbound_zones = unbound_get_forward_zones()
|
|
|
0c84e1 |
for zone in unbound_zones:
|
|
|
0c84e1 |
try:
|
|
|
0c84e1 |
del fzones_from_ac[zone]
|
|
|
0c84e1 |
except KeyError:
|
|
|
0c84e1 |
# we don't mind if there is no such zone
|
|
|
0c84e1 |
pass
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Add forward zones that are not already configured
|
|
|
0c84e1 |
fzones_from_disk = get_fzones_from_disk()
|
|
|
0c84e1 |
for zone, connection in fzones_from_ac.iteritems():
|
|
|
0c84e1 |
if zone not in fzones_from_disk:
|
|
|
0c84e1 |
unbound_add_forward_zone(
|
|
|
0c84e1 |
zone, connection.get_nameservers(), secure)
|
|
|
0c84e1 |
append_fzone_to_file(connection.get_uuid(), zone)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
##############################################################################
|
|
|
0c84e1 |
|
|
|
0c84e1 |
|
|
|
0c84e1 |
if __name__ == "__main__":
|
|
|
0c84e1 |
if not is_running(DNSSEC_TRIGGER):
|
|
|
0c84e1 |
syslog.syslog(syslog.LOG_ERR, "dnssec-triggerd daemon is not running!")
|
|
|
0c84e1 |
sys.exit(1)
|
|
|
0c84e1 |
if not is_running(UNBOUND):
|
|
|
0c84e1 |
syslog.syslog(syslog.LOG_ERR, "unbound server daemon is not running!")
|
|
|
0c84e1 |
sys.exit(1)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
fzones_config = get_fzones_settings_from_conf(DNSSEC_CONF)
|
|
|
0c84e1 |
|
|
|
0c84e1 |
# Get all actove connections from NM
|
|
|
0c84e1 |
ac = get_nm_active_connections()
|
|
|
0c84e1 |
# Configure global forwarders
|
|
|
0c84e1 |
configure_global_forwarders(ac)
|
|
|
0c84e1 |
# Configure forward zones
|
|
|
0c84e1 |
configure_forward_zones(ac, fzones_config)
|