bundlewrap/bundles/wireguard/metadata.py

212 lines
4.9 KiB
Python

from ipaddress import ip_network
from bundlewrap.exceptions import NoSuchNode
from bundlewrap.metadata import atomic
defaults = {
'apt': {
'packages': {
'wireguard': {},
},
'repos': {
'backports': {
'install_gpg_key': False, # default debian signing key
'items': [
'deb http://deb.debian.org/debian {os_release}-backports main',
],
},
},
},
'iptables': {
'bundle_rules': {
'wireguard': [
'iptables_both -A FORWARD -i wg0 -j ACCEPT',
'iptables_both -A FORWARD -o wg0 -j ACCEPT',
],
},
},
'wireguard': {
'privatekey': repo.libs.keys.gen_privkey(repo, f'{node.name} wireguard privatekey'),
},
}
if node.has_bundle('telegraf'):
defaults['telegraf'] = {
'input_plugins': {
'builtin': {
'wireguard': [{}],
},
},
}
@metadata_reactor.provides(
'wireguard/peers',
)
def peer_psks(metadata):
peers = {}
for peer_name in metadata.get('wireguard/peers', {}):
peers[peer_name] = {}
if node.name < peer_name:
peers[peer_name] = {
'psk': repo.vault.random_bytes_as_base64_for(f'{node.name} wireguard {peer_name}'),
}
else:
peers[peer_name] = {
'psk': repo.vault.random_bytes_as_base64_for(f'{peer_name} wireguard {node.name}'),
}
return {
'wireguard': {
'peers': peers,
},
}
@metadata_reactor.provides(
'wireguard/peers',
)
def peer_pubkeys(metadata):
peers = {}
for peer_name in metadata.get('wireguard/peers', {}):
try:
rnode = repo.get_node(peer_name)
except NoSuchNode:
continue
peers[peer_name] = {
'pubkey': repo.libs.keys.get_pubkey_from_privkey(
repo,
f'{rnode.name} wireguard pubkey',
rnode.metadata.get('wireguard/privatekey'),
),
}
return {
'wireguard': {
'peers': peers,
},
}
@metadata_reactor.provides(
'wireguard/peers',
)
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 {
'wireguard': {
'peers': peers,
},
}
@metadata_reactor.provides(
'icinga2_api/wireguard/services',
)
def icinga2(metadata):
services = {}
for peer, config in metadata.get('wireguard/peers', {}).items():
if config.get('exclude_from_monitoring', False):
continue
services[f'WIREGUARD CONNECTION {peer}'] = {
'command_on_monitored_host': config['pubkey'].format_into('sudo /usr/local/share/icinga/plugins/check_wireguard_connected wg0 {}'),
}
return {
'icinga2_api': {
'wireguard': {
'services': services,
},
},
}
@metadata_reactor.provides(
'iptables/port_rules',
)
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 {
'iptables': {
'port_rules': {
'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,
},
},
}