This commit is contained in:
mwiegand 2021-11-07 14:28:09 +01:00
parent 0a0c7cb859
commit 46c12fd05f
3 changed files with 85 additions and 124 deletions

View file

@ -20,16 +20,18 @@ def acme_records(metadata):
@metadata_reactor.provides( @metadata_reactor.provides(
'bind/zones', 'bind/views/external/zones',
) )
def acme_zone(metadata): def acme_zone(metadata):
return { return {
'bind': { 'bind': {
'zones': { 'views': {
metadata.get('bind/acme_zone'): { 'external': {
'dynamic': True, 'zones': {
'views': ['external'], metadata.get('bind/acme_zone'): {
'records': set(), 'dynamic': True,
},
},
}, },
}, },
}, },

View file

@ -1,18 +1,6 @@
from ipaddress import ip_address, ip_interface from ipaddress import ip_address, ip_interface
from datetime import datetime from datetime import datetime
if node.metadata.get('bind/type') == 'master':
zones = node.metadata.get('bind/zones')
master_ip = None
slave_ips = [
ip_interface(repo.get_node(slave).metadata.get('network/external/ipv4')).ip
for slave in node.metadata.get('bind/slaves')
]
else:
zones = repo.get_node(node.metadata.get('bind/master_node')).metadata.get('bind/zones')
master_ip = ip_interface(repo.get_node(node.metadata.get('bind/master_node')).metadata.get('network/external/ipv4')).ip
slave_ips = []
directories[f'/var/lib/bind'] = { directories[f'/var/lib/bind'] = {
'owner': 'bind', 'owner': 'bind',
'group': 'bind', 'group': 'bind',
@ -48,6 +36,19 @@ files['/etc/bind/named.conf'] = {
'svc_systemd:bind9:restart', 'svc_systemd:bind9:restart',
], ],
} }
if node.metadata.get('bind/type') == 'master':
master_node = node
master_ip = None
slave_ips = [
ip_interface(repo.get_node(slave).metadata.get('network/external/ipv4')).ip
for slave in node.metadata.get('bind/slaves')
]
else:
master_node = repo.get_node(node.metadata.get('bind/master_node'))
master_ip = ip_interface(repo.get_node(node.metadata.get('bind/master_node')).metadata.get('network/external/ipv4')).ip
slave_ips = []
files['/etc/bind/named.conf.options'] = { files['/etc/bind/named.conf.options'] = {
'content_type': 'mako', 'content_type': 'mako',
'context': { 'context': {
@ -74,12 +75,11 @@ files['/etc/bind/named.conf.local'] = {
'type': node.metadata.get('bind/type'), 'type': node.metadata.get('bind/type'),
'master_ip': master_ip, 'master_ip': master_ip,
'views': dict(sorted( 'views': dict(sorted(
node.metadata.get('bind/views').items(), master_node.metadata.get('bind/hostname'),
key=lambda e: (e[1].get('default', False), e[0]), key=lambda e: (e[1].get('default', False), e[0]),
)), )),
'zones': zones, 'zones': zones,
'hostname': node.metadata.get('bind/hostname'), 'hostname': node.metadata.get('bind/hostname'),
'keys': node.metadata.get('bind/keys'),
}, },
'owner': 'root', 'owner': 'root',
'group': 'bind', 'group': 'bind',
@ -94,24 +94,6 @@ files['/etc/bind/named.conf.local'] = {
], ],
} }
def record_matches_view(record, records, view):
if record['type'] in ['A', 'AAAA']:
if view == 'external':
# no internal addresses in external view
if ip_address(record['value']).is_private:
return False
elif view == 'internal':
# external addresses in internal view only, if no internal exists
if ip_address(record['value']).is_global:
for other_record in records:
if (
record['name'] == other_record['name'] and
record['type'] == other_record['type'] and
ip_address(other_record['value']).is_private
):
return False
return True
for view_name, view_conf in node.metadata.get('bind/views').items(): for view_name, view_conf in node.metadata.get('bind/views').items():
directories[f"/var/lib/bind/{view_name}"] = { directories[f"/var/lib/bind/{view_name}"] = {
'owner': 'bind', 'owner': 'bind',
@ -125,18 +107,7 @@ for view_name, view_conf in node.metadata.get('bind/views').items():
], ],
} }
for zone, conf in zones.items(): for zone, conf in view_conf['zones'].items():
if view_name not in conf.get('views', ['internal', 'external']):
continue
records = conf['records']
unique_records = [
dict(record_tuple)
for record_tuple in set(
tuple(record.items()) for record in records
)
]
files[f"/var/lib/bind/{view_name}/db.{zone}"] = { files[f"/var/lib/bind/{view_name}/db.{zone}"] = {
'owner': 'bind', 'owner': 'bind',
'group': 'bind', 'group': 'bind',
@ -150,23 +121,22 @@ for view_name, view_conf in node.metadata.get('bind/views').items():
'svc_systemd:bind9:restart', 'svc_systemd:bind9:restart',
], ],
} }
if True or node.metadata.get('bind/type') == 'master': #FIXME: slave doesnt get updated if db doesnt get rewritten on each apply #FIXME: slave doesnt get updated if db doesnt get rewritten on each apply
files[f"/var/lib/bind/{view_name}/db.{zone}"].update({ files[f"/var/lib/bind/{view_name}/db.{zone}"].update({
'source': 'db', 'source': 'db',
'content_type': 'mako', 'content_type': 'mako',
'unless': f"test -f /var/lib/bind/{view_name}/db.{zone}" if conf.get('dynamic', False) else 'false', 'unless': f"test -f /var/lib/bind/{view_name}/db.{zone}" if conf.get('dynamic', False) else 'false',
'context': { 'context': {
'view': view_name, 'serial': datetime.now().strftime('%Y%m%d%H'),
'serial': datetime.now().strftime('%Y%m%d%H'), 'records': list(filter(
'records': list(filter( lambda record: record_matches_view(record, records, view_name),
lambda record: record_matches_view(record, records, view_name), unique_records
unique_records )),
)), 'hostname': node.metadata.get('bind/hostname'),
'hostname': node.metadata.get('bind/hostname'), 'type': node.metadata.get('bind/type'),
'type': node.metadata.get('bind/type'), 'keys': node.metadata.get('bind/keys'),
'keys': node.metadata.get('bind/keys'), },
}, })
})
svc_systemd['bind9'] = {} svc_systemd['bind9'] = {}

View file

@ -21,24 +21,18 @@ defaults = {
'172.16.0.0/12', '172.16.0.0/12',
'192.168.0.0/16', '192.168.0.0/16',
}, },
'keys': {},
'zones': {}, 'zones': {},
}, },
'external': { 'external': {
'default': True, 'default': True,
'name': 'external',
'is_internal': False, 'is_internal': False,
'acl': { 'acl': {
'any', 'any',
}, },
'keys': {},
'zones': {}, 'zones': {},
}, },
}, },
'keys': { 'zones': {},
'internal': {},
'external': {},
},
}, },
'telegraf': { 'telegraf': {
'config': { 'config': {
@ -77,7 +71,7 @@ def dns(metadata):
@metadata_reactor.provides( @metadata_reactor.provides(
'bind/zones', 'bind/views',
) )
def collect_records(metadata): def collect_records(metadata):
if metadata.get('bind/type') == 'slave': if metadata.get('bind/type') == 'slave':
@ -137,15 +131,20 @@ def ns_records(metadata):
] ]
return { return {
'bind': { 'bind': {
'zones': { 'views': {
zone: { view_name: {
'records': { 'zones': {
# FIXME: bw currently cant handle lists of dicts :( zone_name: {
h({'name': '@', 'type': 'NS', 'value': f"{nameserver}."}) 'records': {
for nameserver in nameservers # FIXME: bw currently cant handle lists of dicts :(
} h({'name': '@', 'type': 'NS', 'value': f"{nameserver}."})
for nameserver in nameservers
}
}
for zone_name, zone_conf in view_conf['zones'].items()
}
} }
for zone in metadata.get('bind/zones').keys() for view_name, view_conf in metadata.get('bind/views').items()
}, },
}, },
} }
@ -176,21 +175,22 @@ def generate_keys(metadata):
return { return {
'bind': { 'bind': {
'views': { 'views': {
view: { view_name: {
'keys': { 'zones': {
f'{view}.{zone}': repo.libs.hmac.hmac_sha512( zone_name: {
zone, 'key': repo.libs.hmac.hmac_sha512(
str(repo.vault.random_bytes_as_base64_for( f'{view_name}.{zone_name}',
f"{metadata.get('id')} bind {view} key {zone}", str(repo.vault.random_bytes_as_base64_for(
length=32, f"{metadata.get('id')} bind {view_name} key {zone_name}",
)), length=32,
) )),
for zone, conf in metadata.get('bind/zones').items() )
if conf.get('dynamic', False) }
and view in conf.get('views', metadata.get('bind/views').keys()) for zone_name, zone_conf in view_conf['zones'].items()
if zone_conf.get('dynamic', False)
} }
} }
for view in metadata.get('bind/views') for view_name, view_conf in metadata.get('bind/views').items()
} }
}, },
} }
@ -199,39 +199,28 @@ def generate_keys(metadata):
@metadata_reactor.provides( @metadata_reactor.provides(
'bind/views', 'bind/views',
) )
def allow_keys_in_acl(metadata): def generate_acl_entries_for_keys(metadata):
return { return {
'bind': { 'bind': {
'views': { 'views': {
view: { view_name: {
'acl': { 'acl': {
f'key {key}' *{
for key in conf['keys'] f'key {view_name}.{zone_name}'
for zone_name, zone_conf in view_conf['zones'].items()
if zone_conf.get('key', False)
},
*{
f'! key {other_view_name}.{zone_name}'
for other_view_name, other_view_conf in metadata.get('bind/views').items()
if other_view_name != view_name
for zone_name, zone_conf in other_view_conf['zones'].items()
if zone_conf.get('key', False)
}
} }
} }
for view, conf in metadata.get('bind/views').items() for view_name, view_conf in metadata.get('bind/views').items()
} if not view_conf.get('default')
}, },
}
@metadata_reactor.provides(
'bind/views',
)
def reject_keys_from_other_views(metadata):
return {
'bind': {
'views': {
view: {
'acl': {
f'! key {key}'
for other_view, other_conf in metadata.get('bind/views').items()
if other_view != view
for key in other_conf['keys']
}
}
for view, conf in metadata.get('bind/views').items()
if not conf.get('default')
}
}, },
} }