Compare commits
15 commits
811a3caf98
...
42fc425173
Author | SHA1 | Date | |
---|---|---|---|
![]() |
42fc425173 | ||
![]() |
f596f6b833 | ||
![]() |
7c72fbb044 | ||
![]() |
d87c77b441 | ||
![]() |
316d7db89d | ||
![]() |
c9eef4fc79 | ||
![]() |
1c9c4e0902 | ||
![]() |
cbaded9f8a | ||
![]() |
7c3c1cabf5 | ||
![]() |
ce7b3a0fc7 | ||
![]() |
cf54948abb | ||
![]() |
e35cdb98bb | ||
![]() |
d5a7a8c8cb | ||
![]() |
5f85594b32 | ||
![]() |
3eee733daf |
11 changed files with 126 additions and 84 deletions
36
bin/wireguard_client_config
Executable file
36
bin/wireguard_client_config
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from bundlewrap.repo import Repository
|
||||||
|
from os.path import realpath, dirname
|
||||||
|
from sys import argv
|
||||||
|
from ipaddress import ip_network, ip_interface
|
||||||
|
|
||||||
|
repo = Repository(dirname(dirname(realpath(__file__))))
|
||||||
|
|
||||||
|
server_node = repo.get_node('htz.mails')
|
||||||
|
data = server_node.metadata.get(f'wireguard/clients/{argv[1]}')
|
||||||
|
|
||||||
|
vpn_network = ip_interface(server_node.metadata.get('wireguard/my_ip')).network
|
||||||
|
allowed_ips = [
|
||||||
|
vpn_network,
|
||||||
|
ip_interface(server_node.metadata.get('network/internal/ipv4')).network,
|
||||||
|
]
|
||||||
|
for peer in server_node.metadata.get('wireguard/s2s').values():
|
||||||
|
for network in peer['allowed_ips']:
|
||||||
|
if not ip_network(network).subnet_of(vpn_network):
|
||||||
|
allowed_ips.append(ip_network(network))
|
||||||
|
|
||||||
|
print(
|
||||||
|
f'''[Interface]
|
||||||
|
PrivateKey = {repo.libs.wireguard.privkey(data['peer_id'])}
|
||||||
|
ListenPort = 51820
|
||||||
|
Address = {data['peer_ip']}
|
||||||
|
DNS = 8.8.8.8
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {repo.libs.wireguard.pubkey(server_node.metadata.get('id'))}
|
||||||
|
PresharedKey = {repo.libs.wireguard.psk(data['peer_id'], server_node.metadata.get('id'))}
|
||||||
|
AllowedIPs = {', '.join(str(client_route) for client_route in sorted(allowed_ips))}
|
||||||
|
Endpoint = {ip_interface(server_node.metadata.get('network/external/ipv4')).ip}:51820
|
||||||
|
PersistentKeepalive = 10'''
|
||||||
|
)
|
2
bundles/wireguard/README.md
Normal file
2
bundles/wireguard/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
|
||||||
|
dmesg -wT | grep wireguard
|
|
@ -1,3 +1 @@
|
||||||
from ipaddress import ip_network
|
|
||||||
|
|
||||||
repo.libs.tools.require_bundle(node, 'systemd-networkd')
|
repo.libs.tools.require_bundle(node, 'systemd-networkd')
|
||||||
|
|
|
@ -3,16 +3,14 @@ from ipaddress import ip_network, ip_interface
|
||||||
from bundlewrap.exceptions import NoSuchNode
|
from bundlewrap.exceptions import NoSuchNode
|
||||||
from bundlewrap.metadata import atomic
|
from bundlewrap.metadata import atomic
|
||||||
|
|
||||||
|
repo.libs.wireguard.repo = repo
|
||||||
|
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
'apt': {
|
'apt': {
|
||||||
'packages': {
|
'packages': {
|
||||||
# 'linux-headers-amd64': {},
|
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'backports': node.os_version < (11,),
|
'backports': node.os_version < (11,),
|
||||||
# 'needs': [
|
|
||||||
# 'pkg_apt:linux-headers-amd64',
|
|
||||||
# ],
|
|
||||||
'triggers': [
|
'triggers': [
|
||||||
'svc_systemd:systemd-networkd:restart',
|
'svc_systemd:systemd-networkd:restart',
|
||||||
],
|
],
|
||||||
|
@ -20,11 +18,43 @@ defaults = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'privatekey': repo.vault.random_bytes_as_base64_for(f'{node.name} wireguard privatekey'),
|
's2s': {},
|
||||||
|
'clients': {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@metadata_reactor.provides(
|
||||||
|
'wireguard/s2s',
|
||||||
|
'wireguard/clients',
|
||||||
|
)
|
||||||
|
def s2s_peer_specific(metadata):
|
||||||
|
return {
|
||||||
|
'wireguard': {
|
||||||
|
's2s': {
|
||||||
|
s2s: {
|
||||||
|
'peer_id': repo.get_node(s2s).metadata.get(f'id'),
|
||||||
|
'peer_ip': repo.get_node(s2s).metadata.get(f'wireguard/my_ip'),
|
||||||
|
'endpoint': f'{repo.get_node(s2s).hostname}:51820',
|
||||||
|
'allowed_ips': [
|
||||||
|
str(ip_interface(repo.get_node(s2s).metadata.get(f'wireguard/my_ip')).network),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for s2s in metadata.get('wireguard/s2s')
|
||||||
|
},
|
||||||
|
'clients': {
|
||||||
|
client: {
|
||||||
|
'peer_id': client,
|
||||||
|
'allowed_ips': [
|
||||||
|
str(ip_interface(conf['peer_ip']).network),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for client, conf in metadata.get('wireguard/clients').items()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@metadata_reactor.provides(
|
@metadata_reactor.provides(
|
||||||
'systemd/units',
|
'systemd/units',
|
||||||
)
|
)
|
||||||
|
@ -36,11 +66,6 @@ def systemd_networkd_networks(metadata):
|
||||||
'Address': {
|
'Address': {
|
||||||
'Address': metadata.get('wireguard/my_ip'),
|
'Address': metadata.get('wireguard/my_ip'),
|
||||||
},
|
},
|
||||||
'Route': {
|
|
||||||
'Destination': str(ip_interface(metadata.get('wireguard/my_ip')).network),
|
|
||||||
'GatewayOnlink': 'yes',
|
|
||||||
'PreferredSource': str(ip_interface(metadata.get('network/internal/ipv4')).ip),
|
|
||||||
},
|
|
||||||
'Network': {
|
'Network': {
|
||||||
'DHCP': 'no',
|
'DHCP': 'no',
|
||||||
'IPForward': 'yes',
|
'IPForward': 'yes',
|
||||||
|
@ -48,14 +73,12 @@ def systemd_networkd_networks(metadata):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for peer, config in metadata.get('wireguard/peers').items():
|
for peer, config in metadata.get('wireguard/s2s').items():
|
||||||
for route in config.get('route', []):
|
for route in config.get('allowed_ips', []):
|
||||||
network.update({
|
network.update({
|
||||||
f'Route#{peer}_{route}': {
|
f'Route#{peer}_{route}': {
|
||||||
'Destination': route,
|
'Destination': route,
|
||||||
'Gateway': str(ip_interface(repo.get_node(peer).metadata.get(f'wireguard/my_ip')).ip),
|
'Gateway': str(ip_interface(metadata.get('wireguard/my_ip')).ip),
|
||||||
'GatewayOnlink': 'yes',
|
|
||||||
'PreferredSource': str(ip_interface(metadata.get('network/internal/ipv4')).ip),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -79,24 +102,25 @@ def systemd_networkd_netdevs(metadata):
|
||||||
'Description': 'WireGuard server',
|
'Description': 'WireGuard server',
|
||||||
},
|
},
|
||||||
'WireGuard': {
|
'WireGuard': {
|
||||||
'PrivateKey': metadata.get('wireguard/privatekey'),
|
'PrivateKey': repo.libs.wireguard.privkey(metadata.get('id')),
|
||||||
'ListenPort': 51820,
|
'ListenPort': 51820,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for peer, config in metadata.get('wireguard/peers').items():
|
for peer, config in {
|
||||||
|
**metadata.get('wireguard/s2s'),
|
||||||
|
**metadata.get('wireguard/clients'),
|
||||||
|
}.items():
|
||||||
netdev.update({
|
netdev.update({
|
||||||
f'WireGuardPeer#{peer}': {
|
f'WireGuardPeer#{peer}': {
|
||||||
'Endpoint': config['endpoint'],
|
'PublicKey': repo.libs.wireguard.pubkey(config['peer_id']),
|
||||||
'PublicKey': config['pubkey'],
|
'PresharedKey': repo.libs.wireguard.psk(config['peer_id'], metadata.get('id')),
|
||||||
'PresharedKey': config['psk'],
|
'AllowedIPs': ', '.join(config.get('allowed_ips', [])),
|
||||||
'AllowedIPs': ', '.join([
|
|
||||||
str(ip_interface(repo.get_node(peer).metadata.get(f'wireguard/my_ip')).ip),
|
|
||||||
*config.get('route', []),
|
|
||||||
]), # FIXME
|
|
||||||
'PersistentKeepalive': 30,
|
'PersistentKeepalive': 30,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if config.get('endpoint'):
|
||||||
|
netdev[f'WireGuardPeer#{peer}']['Endpoint'] = config['endpoint']
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'systemd': {
|
'systemd': {
|
||||||
|
@ -105,33 +129,3 @@ def systemd_networkd_netdevs(metadata):
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@metadata_reactor.provides(
|
|
||||||
'wireguard/peers',
|
|
||||||
)
|
|
||||||
def peer_keys(metadata):
|
|
||||||
peers = {}
|
|
||||||
|
|
||||||
for peer_name in metadata.get('wireguard/peers', {}):
|
|
||||||
peer_node = repo.get_node(peer_name)
|
|
||||||
|
|
||||||
first, second = sorted([node.metadata.get('id'), peer_node.metadata.get('id')])
|
|
||||||
psk = repo.vault.random_bytes_as_base64_for(f'{first} wireguard {second}')
|
|
||||||
|
|
||||||
pubkey = repo.libs.keys.get_pubkey_from_privkey(
|
|
||||||
f'{peer_name} wireguard pubkey',
|
|
||||||
peer_node.metadata.get('wireguard/privatekey'),
|
|
||||||
)
|
|
||||||
|
|
||||||
peers[peer_name] = {
|
|
||||||
'psk': psk,
|
|
||||||
'pubkey': pubkey,
|
|
||||||
'endpoint': f'{peer_node.hostname}:51820',
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'wireguard': {
|
|
||||||
'peers': peers,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
15
libs/keys.py
15
libs/keys.py
|
@ -1,15 +0,0 @@
|
||||||
import base64
|
|
||||||
from nacl.public import PrivateKey
|
|
||||||
from nacl.encoding import Base64Encoder
|
|
||||||
from bundlewrap.utils import Fault
|
|
||||||
|
|
||||||
def gen_privkey(repo, identifier):
|
|
||||||
return repo.vault.random_bytes_as_base64_for(identifier)
|
|
||||||
|
|
||||||
def get_pubkey_from_privkey(identifier, privkey):
|
|
||||||
# FIXME this assumes the privkey is always a base64 encoded string
|
|
||||||
def derive_pubkey():
|
|
||||||
pub_key = PrivateKey(base64.b64decode(str(privkey))).public_key
|
|
||||||
return pub_key.encode(encoder=Base64Encoder).decode('ascii')
|
|
||||||
|
|
||||||
return Fault(f'pubkey from privkey {identifier}', derive_pubkey)
|
|
16
libs/wireguard.py
Normal file
16
libs/wireguard.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import base64
|
||||||
|
from nacl.public import PrivateKey
|
||||||
|
from nacl.encoding import Base64Encoder
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def privkey(id):
|
||||||
|
return str(repo.vault.random_bytes_as_base64_for(f"wireguard privkey {id}"))
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def pubkey(id):
|
||||||
|
return PrivateKey(base64.b64decode(privkey(id))).public_key.encode(encoder=Base64Encoder).decode('ascii')
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def psk(id1, id2):
|
||||||
|
return repo.vault.random_bytes_as_base64_for(f"wireguard psk {' '.join(sorted([id1, id2]))}")
|
|
@ -58,10 +58,10 @@
|
||||||
'ram': 16192,
|
'ram': 16192,
|
||||||
},
|
},
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'my_ip': '172.30.0.2/24',
|
'my_ip': '172.30.0.2/32',
|
||||||
'peers': {
|
's2s': {
|
||||||
'htz.mails': {
|
'htz.mails': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'10.0.10.0/24',
|
'10.0.10.0/24',
|
||||||
'10.0.11.0/24',
|
'10.0.11.0/24',
|
||||||
'10.0.20.0/24',
|
'10.0.20.0/24',
|
||||||
|
|
|
@ -141,25 +141,36 @@
|
||||||
},
|
},
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'my_ip': '172.30.0.1/24',
|
'my_ip': '172.30.0.1/24',
|
||||||
'peers': {
|
's2s': {
|
||||||
'home.server': {
|
'home.server': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'10.0.0.0/24',
|
'10.0.0.0/24',
|
||||||
'10.0.2.0/24',
|
'10.0.2.0/24',
|
||||||
'10.0.9.0/24',
|
'10.0.9.0/24',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'netcup.secondary': {
|
'netcup.secondary': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'10.0.11.0/24',
|
'10.0.11.0/24',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'wb.offsite-backups': {
|
'wb.offsite-backups': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'192.168.178.0/24',
|
'192.168.178.0/24',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'clients': {
|
||||||
|
'macbook': {
|
||||||
|
'peer_ip': '172.30.0.100/32',
|
||||||
|
},
|
||||||
|
'phone': {
|
||||||
|
'peer_ip': '172.30.0.101/32',
|
||||||
|
},
|
||||||
|
'ipad': {
|
||||||
|
'peer_ip': '172.30.0.102/32',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'zfs': {
|
'zfs': {
|
||||||
'pools': {
|
'pools': {
|
||||||
|
|
|
@ -31,10 +31,10 @@
|
||||||
# 'hostname': 'mail2.sublimity.de',
|
# 'hostname': 'mail2.sublimity.de',
|
||||||
# },
|
# },
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'my_ip': '172.30.0.3/24',
|
'my_ip': '172.30.0.3/32',
|
||||||
'peers': {
|
's2s': {
|
||||||
'htz.mails': {
|
'htz.mails': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'10.0.0.0/24',
|
'10.0.0.0/24',
|
||||||
'10.0.2.0/24',
|
'10.0.2.0/24',
|
||||||
'10.0.9.0/24',
|
'10.0.9.0/24',
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'wireguard': {
|
'wireguard': {
|
||||||
'my_ip': '172.30.0.4/24',
|
'my_ip': '172.30.0.4/32',
|
||||||
'peers': {
|
's2s': {
|
||||||
'htz.mails': {
|
'htz.mails': {
|
||||||
'route': [
|
'allowed_ips': [
|
||||||
'10.0.0.0/24',
|
'10.0.0.0/24',
|
||||||
'10.0.2.0/24',
|
'10.0.2.0/24',
|
||||||
'10.0.9.0/24',
|
'10.0.9.0/24',
|
||||||
|
|
Loading…
Reference in a new issue