bundles/wireguard: rework metadata.py
Some checks failed
bundlewrap/pipeline/head There was a failure building this commit

This commit is contained in:
Franzi 2021-04-01 16:27:31 +02:00
parent b679f568eb
commit 6e423c24fb
Signed by: kunsi
GPG key ID: 12E3D2136B818350
7 changed files with 155 additions and 64 deletions

View file

@ -1,8 +0,0 @@
[Match]
Name=wg0
[Network]
Address=${my_ip}
[Route]
Destination=${network}

View file

@ -1,21 +1,16 @@
from ipaddress import ip_network
assert node.has_bundle('systemd-networkd') assert node.has_bundle('systemd-networkd')
network = ip_network(node.metadata['wireguard']['my_ip'], strict=False)
files = { files = {
'/etc/systemd/network/99-wg0.netdev': { '/etc/systemd/network/wg0.netdev': {
'source': 'wg0.netdev',
'content_type': 'mako', 'content_type': 'mako',
'context': node.metadata['wireguard'], 'context': {
'needs': { 'network': f'{network.network_address}/{network.prefixlen}',
'pkg_apt:wireguard', **node.metadata['wireguard'],
}, },
'triggers': {
'svc_systemd:systemd-networkd:restart',
},
},
'/etc/systemd/network/99-wg0.network': {
'source': 'wg0.network',
'content_type': 'mako',
'context': node.metadata['wireguard'],
'needs': { 'needs': {
'pkg_apt:wireguard', 'pkg_apt:wireguard',
}, },

View file

@ -1,5 +1,9 @@
from ipaddress import ip_network
from bundlewrap.exceptions import NoSuchNode
from bundlewrap.metadata import atomic from bundlewrap.metadata import atomic
defaults = { defaults = {
'apt': { 'apt': {
'packages': { 'packages': {
@ -16,10 +20,10 @@ defaults = {
}, },
'iptables': { 'iptables': {
'bundle_rules': { 'bundle_rules': {
'wireguard': [ 'wireguard': {
'iptables_both -A FORWARD -i wg0 -j ACCEPT', 'iptables_both -A FORWARD -i wg0 -j ACCEPT',
'iptables_both -A FORWARD -o wg0 -j ACCEPT', 'iptables_both -A FORWARD -o wg0 -j ACCEPT',
], },
}, },
}, },
'wireguard': { 'wireguard': {
@ -29,51 +33,77 @@ defaults = {
@metadata_reactor.provides( @metadata_reactor.provides(
'wireguard/network', 'wireguard/peers',
) )
def get_wireguard_network_from_server(metadata): def peer_psks(metadata):
# FIXME This will break if more than one node sets 'wireguard/network' peers = {}
for rnode in repo.nodes:
if not rnode.has_bundle('wireguard'):
continue
if node.name in rnode.metadata.get('wireguard/peers', {}).keys(): for peer_name in metadata.get('wireguard/peers', {}):
network = rnode.metadata.get('wireguard/network', None) peers[peer_name] = {}
if network: if node.name < peer_name:
return { peers[peer_name] = {
'wireguard': { 'psk': repo.vault.random_bytes_as_base64_for(f'{node.name} wireguard {peer_name}'),
'network': network, }
}, else:
} peers[peer_name] = {
'psk': repo.vault.random_bytes_as_base64_for(f'{peer_name} wireguard {node.name}'),
}
return {} return {
'wireguard': {
'peers': peers,
},
}
@metadata_reactor.provides( @metadata_reactor.provides(
'wireguard/peers', 'wireguard/peers',
) )
def get_my_wireguard_peers(metadata): def peer_pubkeys(metadata):
peers = {} peers = {}
for rnode in repo.nodes: for peer_name in metadata.get('wireguard/peers', {}):
if not rnode.has_bundle('wireguard'): try:
rnode = repo.get_node(peer_name)
except NoSuchNode:
continue continue
if node.name in rnode.metadata.get('wireguard/peers', {}).keys(): peers[peer_name] = {
peers[rnode.name] = { 'pubkey': repo.libs.keys.get_pubkey_from_privkey(
'pubkey': repo.libs.keys.get_pubkey_from_privkey(repo, f'{node.name} wireguard {rnode.name}', rnode.metadata.get('wireguard/privatekey')), repo,
'psk': rnode.metadata.get('wireguard/psk', metadata.get('wireguard/psk', None)), f'{rnode.name} wireguard pubkey',
} rnode.metadata.get('wireguard/privatekey'),
),
}
if not rnode.metadata.get(f'wireguard/peers/{node.name}/do_not_initiate_a_connection_from_your_side', False): return {
peers[rnode.name]['endpoint'] = f'{rnode.hostname}:51820' 'wireguard': {
'peers': peers,
},
}
peers[rnode.name]['ips'] = rnode.metadata.get('wireguard/subnets', set())
your_ip = rnode.metadata.get('wireguard/my_ip', None) @metadata_reactor.provides(
if your_ip: 'wireguard/peers',
peers[rnode.name]['ips'].add(your_ip) )
def peer_ips_and_endpoints(metadata):
peers = {}
for peer_name in metadata.get('wireguard/peers', {}):
try:
rnode = repo.get_node(peer_name)
except NoSuchNode:
continue
ips = rnode.metadata.get('wireguard/subnets', set())
ips.add(rnode.metadata.get('wireguard/my_ip').split('/')[0])
ips = repo.libs.tools.remove_more_specific_subnets(ips)
peers[rnode.name] = {
'endpoint': '{}:51820'.format(rnode.metadata.get('wireguard/external_hostname', rnode.hostname)),
'ips': ips,
}
return { return {
'wireguard': { 'wireguard': {
@ -109,10 +139,63 @@ def icinga2(metadata):
'iptables/port_rules', 'iptables/port_rules',
) )
def iptables(metadata): def iptables(metadata):
sources = set(metadata.get('wireguard/restrict-to', set()))
for peer_name in metadata.get('wireguard/peers'):
try:
rnode = repo.get_node(peer_name)
except NoSuchNode: # roadwarrior
continue
else:
sources.add(peer_name)
return { return {
'iptables': { 'iptables': {
'port_rules': { 'port_rules': {
'51820/udp': atomic(metadata.get('wireguard/restrict-to', set(metadata.get('wireguard/peers', {}).keys()))), '51820/udp': atomic(sources),
},
},
}
@metadata_reactor.provides(
'interfaces/wg0/ips',
)
def interface_ips(metadata):
return {
'interfaces': {
'wg0': {
'ips': {
metadata.get('wireguard/my_ip'),
},
},
},
}
@metadata_reactor.provides(
'interfaces/wg0/routes',
)
def routes(metadata):
network = ip_network(metadata.get('wireguard/my_ip'), strict=False)
ips = {
f'{network.network_address}/{network.prefixlen}',
}
routes = {}
for _, peer_config in metadata.get('wireguard/peers', {}).items():
for ip in peer_config['ips']:
ips.add(ip)
if '0.0.0.0/0' in ips:
ips.remove('0.0.0.0/0')
for ip in repo.libs.tools.remove_more_specific_subnets(ips):
routes[ip] = {}
return {
'interfaces': {
'wg0': {
'routes': routes,
}, },
}, },
} }

View file

@ -55,3 +55,26 @@ def resolve_identifier(repo, identifier):
ip_dict['ipv6'].add(ip) ip_dict['ipv6'].add(ip)
return ip_dict return ip_dict
def remove_more_specific_subnets(input_subnets) -> list:
final_subnets = []
for subnet in sorted(input_subnets):
source = ip_network(subnet)
if not source in final_subnets:
subnet_found = False
for dest_subnet in final_subnets:
if source.subnet_of(dest_subnet):
subnet_found = True
if not subnet_found:
final_subnets.append(source)
out = []
for net in final_subnets:
out.append(str(net))
return out

View file

@ -172,17 +172,15 @@ nodes['home.router'] = {
}, },
}, },
'wireguard': { 'wireguard': {
# TODO autogenerate? 'external_hostname': 'franzi-home.kunbox.net', # Set via DynDNS
'my_ip': '172.19.136.2/32', 'my_ip': '172.19.136.2/22',
'peers': {
'ovh.wireguard': {},
},
'subnets': { 'subnets': {
'172.19.138.0/24', '172.19.138.0/24',
'172.19.139.0/24', '172.19.139.0/24',
}, },
'peers': {
'ovh.wireguard': {
'do_not_initiate_a_connection_from_your_side': True,
},
},
}, },
}, },
} }

View file

@ -113,7 +113,7 @@ nodes['ovh.icinga2'] = {
'service_filter': '"checks_with_sms" in service.groups' 'service_filter': '"checks_with_sms" in service.groups'
}, },
'wireguard': { 'wireguard': {
'my_ip': '172.19.136.3/32', 'my_ip': '172.19.136.3/22',
'peers': { 'peers': {
'ovh.wireguard': {}, 'ovh.wireguard': {},
}, },

View file

@ -24,10 +24,10 @@ nodes['ovh.wireguard'] = {
'ram': 2, 'ram': 2,
}, },
'wireguard': { 'wireguard': {
'network': '172.19.136.0/22', 'my_ip': '172.19.136.1/22',
'my_ip': '172.19.136.1/32',
'psk': vault.random_bytes_as_base64_for('ovh.icinga2 wireguard psk'),
'peers': { 'peers': {
'ovh.icinga2': {},
'home.router': {},
'kunsi-oneplus3': { 'kunsi-oneplus3': {
'ips': { 'ips': {
'172.19.136.100/32', '172.19.136.100/32',