This commit is contained in:
mwiegand 2021-11-07 16:19:01 +01:00
parent a59c981238
commit 3246f67a81
6 changed files with 49 additions and 83 deletions

View file

@ -1,3 +1,6 @@
from ipaddress import ip_interface
@metadata_reactor.provides(
'dns',
)
@ -20,16 +23,35 @@ def acme_records(metadata):
@metadata_reactor.provides(
'bind/acls/acme',
'bind/keys/acme',
'bind/views/external/zones',
)
def acme_zone(metadata):
allowed_ips = {
str(ip_interface(other_node.metadata.get('network/internal/ipv4')).ip)
for other_node in repo.nodes
if other_node.metadata.get('letsencrypt/domains', {})
}
return {
'bind': {
'acls': {
'acme': {
'key acme',
'!{ !{' + ' '.join(f'{ip};' for ip in sorted(allowed_ips)) + '}; any;}',
},
},
'keys': {
'acme': {},
},
'views': {
'external': {
'zones': {
metadata.get('bind/acme_zone'): {
'dynamic': True,
'allow_update': {
'acme',
},
},
},
},

View file

@ -1,20 +1,16 @@
% for acl_name, acl_content in acls.items():
acl "${acl_name}" {
% for ac in sorted(acl_content, key=lambda e: (not e.startswith('!'), not e.startswith('key'))):
% for ac in sorted(acl_content, key=lambda e: (not e.startswith('!'), not e.startswith('key'), e)):
${ac};
% endfor
};
% endfor
% for view_name, view_conf in views.items():
% for zone_name, zone_conf in view_conf['zones'].items():
% if zone_conf.get('key', False):
key "${view_name}.${zone_name}" {
% for key_name, key_conf in sorted(keys.items()):
key "${key_name}" {
algorithm hmac-sha512;
secret "${zone_conf['key']}";
secret "${key_conf['token']}";
};
% endif
% endfor
% endfor
% for view_name, view_conf in views.items():
@ -46,8 +42,12 @@ view "${view_name}" {
% if type == 'slave':
masters { ${master_ip}; };
% endif
% if type == 'master' and zone_conf.get('key', False):
allow-update { !{ !our-nets; any; }; key "${view_name}.${zone_name}"; };
% if zone_conf.get('allow_update', False):
allow-update {
% for allow_update in zone_conf['allow_update']:
${allow_update};
% endfor
};
% endif
file "/var/lib/bind/${view_name}/db.${zone_name}";
};

View file

@ -81,6 +81,7 @@ files['/etc/bind/named.conf.local'] = {
for view_name, view_conf in master_node.metadata.get('bind/views').items()
},
},
'keys': master_node.metadata.get('bind/keys'),
'views': dict(sorted(
master_node.metadata.get('bind/views').items(),
key=lambda e: (e[1].get('default', False), e[0]),
@ -130,7 +131,7 @@ for view_name, view_conf in node.metadata.get('bind/views').items():
files[f"/var/lib/bind/{view_name}/db.{zone_name}"].update({
'source': 'db',
'content_type': 'mako',
'unless': f"test -f /var/lib/bind/{view_name}/db.{zone_name}" if zone_conf.get('dynamic', False) else 'false',
'unless': f"test -f /var/lib/bind/{view_name}/db.{zone_name}" if zone_conf.get('allow_update', False) else 'false',
'context': {
'serial': datetime.now().strftime('%Y%m%d%H'),
'records': zone_conf['records'],

View file

@ -38,6 +38,7 @@ defaults = {
},
},
'zones': {},
'keys': {},
},
'telegraf': {
'config': {
@ -174,29 +175,23 @@ def slaves(metadata):
@metadata_reactor.provides(
'bind/views',
'bind/keys',
)
def generate_keys(metadata):
return {
'bind': {
'views': {
view_name: {
'zones': {
zone_name: {
'key': repo.libs.hmac.hmac_sha512(
f'{view_name}.{zone_name}',
str(repo.vault.random_bytes_as_base64_for(
f"{metadata.get('id')} bind {view_name} key {zone_name}",
length=32,
)),
)
}
for zone_name, zone_conf in view_conf['zones'].items()
if zone_conf.get('dynamic', False)
}
'keys': {
key: {
'token':repo.libs.hmac.hmac_sha512(
key,
str(repo.vault.random_bytes_as_base64_for(
f"{metadata.get('id')} bind key {key}",
length=32,
)),
)
}
for view_name, view_conf in metadata.get('bind/views').items()
}
for key in metadata.get('bind/keys')
},
}

View file

@ -30,8 +30,8 @@ files = {
'context': {
'server': ip_interface(acme_node.metadata.get('network/external/ipv4')).ip,
'zone': acme_node.metadata.get('bind/acme_zone'),
'acme_key_name': 'external' + acme_node.metadata.get('bind/acme_zone'),
'acme_key': acme_node.metadata.get('bind/views/external/zones/'+acme_node.metadata.get('bind/acme_zone')+'/key'),
'acme_key_name': 'acme',
'acme_key': acme_node.metadata.get('bind/keys/acme/token'),
'domains': node.metadata.get('letsencrypt/domains'),
},
'mode': '0755',

View file

@ -18,55 +18,3 @@ defaults = {
},
},
}
@metadata_reactor.provides(
'systemd-timers/letsencrypt',
'mirror/certs',
)
def renew(metadata):
delegated_node = metadata.get('letsencrypt/delegate_to_node', False)
if delegated_node:
delegated_ip = ip_interface(repo.get_node(delegated_node).metadata.get('network/internal/ipv4')).ip
return {
'mirror': {
'certs': {
'from': f"{delegated_ip}:/var/lib/dehydrated/certs",
'to': '/var/lib/dehydrated',
},
},
}
else:
return {
'systemd-timers': {
'letsencrypt': {
'command': '/bin/bash -c "/usr/bin/dehydrated --cron --accept-terms --challenge dns-01 && /usr/bin/dehydrated --cleanup"',
'when': 'daily',
},
},
}
@metadata_reactor.provides(
'letsencrypt/domains',
'dns',
)
def delegated_domains(metadata):
delegated_domains = {
domain: conf
for other_node in repo.nodes
if other_node.has_bundle('letsencrypt')
and other_node.metadata.get('letsencrypt/delegate_to_node', None) == node.name
for domain, conf in other_node.metadata.get('letsencrypt/domains').items()
}
return {
'letsencrypt': {
'domains': delegated_domains,
},
'dns': {
domain: repo.libs.dns.get_a_records(metadata, internal=False)
for domain in delegated_domains.keys()
},
}