bundlewrap/bundles/powerdns/metadata.py

223 lines
6.3 KiB
Python

from ipaddress import IPv4Address, IPv6Address, ip_address
from bundlewrap.metadata import atomic
defaults = {
'apt': {
'packages': {
'pdns-server': {},
'pdns-tools': {},
'pdns-backend-bind': {},
'pdns-backend-pgsql': {},
},
},
'icinga2_api': {
'powerdns': {
'services': {
'POWERDNS PROCESS': {
'command_on_monitored_host': '/usr/lib/nagios/plugins/check_procs -C pdns_server -c 1:',
'vars.notification.mail': True,
'vars.notification.sms': True,
},
},
},
},
'powerdns': {
'api_key': repo.vault.password_for('{} powerdns api'.format(node.name)),
},
'postgresql': {
'roles': {
'powerdns': {
'password': repo.vault.password_for('{} postgresql powerdns'.format(node.name)),
},
},
'databases': {
'powerdns': {
'owner': 'powerdns',
},
},
},
}
if node.has_bundle('telegraf'):
defaults['telegraf'] = {
'input_plugins': {
'builtin': {
'powerdns': [{
'unix_sockets': [
'/var/run/pdns/pdns.controlsocket',
],
}],
},
},
'additional_groups': {
'pdns',
},
}
@metadata_reactor.provides(
'icinga2_api/powerdns/services',
)
def monitoring_for_primary_nameserver(metadata):
if metadata.get('powerdns/is_secondary', False):
return {}
return {
'icinga2_api': {
'powerdns': {
'services': {
'POWERDNS WEB INTERFACE': {
'command_on_monitored_host': '/usr/local/share/icinga/plugins/check_http_url_for_string http://localhost:8081/ "PowerDNS"',
},
},
},
},
}
@metadata_reactor.provides(
'powerdns/my_secondary_servers',
)
def get_ips_of_secondary_nameservers(metadata):
if metadata.get('powerdns/is_secondary', False):
return {}
ips = set()
for rnode in repo.nodes_in_group('dns'):
if rnode.metadata.get('powerdns/is_secondary', False):
for _, found_ips in repo.libs.tools.resolve_identifier(repo, rnode.name).items():
ips.update({str(ip) for ip in found_ips})
return {
'powerdns': {
'my_secondary_servers': ips,
},
}
@metadata_reactor.provides(
'powerdns/my_primary_servers',
)
def get_ips_of_primary_nameservers(metadata):
if not metadata.get('powerdns/is_secondary', False):
return {}
ips = set()
for rnode in repo.nodes_in_group('dns'):
if not rnode.metadata.get('powerdns/is_secondary', False):
for _, found_ips in repo.libs.tools.resolve_identifier(repo, rnode.name).items():
ips.update({str(ip) for ip in found_ips})
return {
'powerdns': {
'my_primary_servers': ips,
},
}
@metadata_reactor.provides(
'powerdns/bind-zones/kunbox.net/records',
)
def generate_dns_entries_for_nodes(metadata):
results = set()
for rnode in repo.nodes:
node_name_split = rnode.name.split('.')
node_name_split.reverse()
dns_name = '.'.join(node_name_split)
ip4 = None
ip6 = None
found_ips = repo.libs.tools.resolve_identifier(repo, rnode.name)
for ip in sorted(found_ips['ipv4']):
if not ip4 and not ip.is_private:
ip4 = ip
for ip in sorted(found_ips['ipv6']):
if not ip6 and not ip.is_private:
ip6 = ip
if not ip4 and found_ips['ipv4']:
# This node apparently does not have a public IPv4 address.
# We now manually iterate over that nodes interfaces to get
# a IPv4 address which is tied to a physical interface.
# Note we can't use resolve_identifier() here, because we
# only want physical interfaces.
for interface, config in rnode.metadata.get('interfaces', {}).items():
if not (
interface.startswith('bond') or
interface.startswith('br') or
interface.startswith('eno') or
interface.startswith('enp') or
interface.startswith('eth') or
interface == 'default' # dummy nodes use these
):
continue
for ip in sorted(config.get('ips', set())):
if '/' in ip:
addr = ip_address(ip.split('/')[0])
else:
addr = ip_address(ip)
if not ip4 and isinstance(addr, IPv4Address):
ip4 = addr
if ip4:
results.add('{} IN A {}'.format(dns_name, ip4))
if ip6:
results.add('{} IN AAAA {}'.format(dns_name, ip6))
return {
'powerdns': {
'bind-zones': {
'kunbox.net': {
'records': results,
},
},
},
}
@metadata_reactor.provides(
'hosts/entries',
)
def hosts_entries_for_all_dns_servers(metadata):
entries = {}
for rnode in repo.nodes_in_group('dns'):
if rnode.name == node.name:
continue
found_ips = repo.libs.tools.resolve_identifier(repo, rnode.name)
for ip in sorted(found_ips['ipv4']):
if not ip.is_private:
entries[str(ip)] = {
rnode.metadata.get('hostname'),
rnode.name,
}
if rnode.metadata.get('powerdns/my_hostname', None):
entries[str(ip)].add(rnode.metadata.get('powerdns/my_hostname'))
return {
'hosts': {
'entries': entries,
},
}
@metadata_reactor.provides(
'firewall/port_rules',
)
def firewall(metadata):
return {
'firewall': {
'port_rules': {
'53': atomic(metadata.get('powerdns/restrict-to/dns', {'*'})),
'53/udp': atomic(metadata.get('powerdns/restrict-to/dns', {'*'})),
'8081': atomic(metadata.get('powerdns/restrict-to/api', set())),
},
},
}