#!/usr/bin/python3 import json import subprocess import sys from ipaddress import ip_address, ip_network from itertools import islice from pathlib import Path wg_dir = Path('/etc/wireguard') wg_json = wg_dir / 'wg0.json' wg_conf = wg_dir / 'wg0.conf' iface = 'enp3s0' ipv4_prefix = '/24' ipv6_prefix = '/64' def genkey(): return subprocess.check_output(['wg', 'genkey']).strip().decode('utf-8') def pubkey(key): return subprocess.run(['wg', 'pubkey'], input=key, encoding='utf-8', capture_output=True).stdout.strip() def ipv4(cfg): taken = [ip_address(cfg['ipv4'])] + [ip_address(c['ipv4']) for c in cfg['clients']] if cfg else [] for ip in ip_network('192.168.0.0' + ipv4_prefix).hosts(): if ip not in taken: return str(ip) raise Exception('no ipv4 left') def ipv6(cfg): taken = [ip_address(cfg['ipv6'])] + [ip_address(c['ipv6']) for c in cfg['clients']] if cfg else [] for ip in ip_network('fd32:76a6:ec61:577a::0' + ipv6_prefix).hosts(): if ip not in taken: return str(ip) raise Exception('no ipv6 left') def clientconf(cfg, key): c = cfg['clients'][-1] return ( f'[Interface]\n' f'Address = {c["ipv4"] + ipv4_prefix}\n' f'Address = {c["ipv6"] + ipv6_prefix}\n' f'DNS = 8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844\n' # Google DNS servers f'PrivateKey = {key}\n' f'\n' f'[Peer]\n' f'Endpoint = matthewtran.com:51820\n' f'AllowedIPs = 0.0.0.0/0, ::/0\n' f'PublicKey = {pubkey(cfg["key"])}\n' f'PersistentKeepalive = 15\n' ) def serverconf(cfg): conf = ( f'[Interface]\n' f'Address = {cfg["ipv4"] + ipv4_prefix}\n' f'Address = {cfg["ipv6"] + ipv6_prefix}\n' f'ListenPort = 51820\n' f'PrivateKey = {cfg["key"]}\n' f'PostUp = sysctl -w net.ipv4.ip_forward=1\n' f'PostUp = sysctl -w net.ipv6.conf.all.forwarding=1\n' f'PostUp = iptables -A FORWARD -i wg0 -j ACCEPT\n' f'PostUp = iptables -t nat -A POSTROUTING -o {iface} -j MASQUERADE\n' f'PostUp = ip6tables -A FORWARD -i wg0 -j ACCEPT\n' f'PostUp = ip6tables -t nat -A POSTROUTING -o {iface} -j MASQUERADE\n' f'PostUp = ufw reload\n' f'PostDown = sysctl -w net.ipv4.ip_forward=0\n' f'PostDown = sysctl -w net.ipv6.conf.all.forwarding=0\n' f'PostDown = iptables -D FORWARD -i wg0 -j ACCEPT\n' f'PostDown = iptables -t nat -D POSTROUTING -o {iface} -j MASQUERADE\n' f'PostDown = ip6tables -D FORWARD -i wg0 -j ACCEPT\n' f'PostDown = ip6tables -t nat -D POSTROUTING -o {iface} -j MASQUERADE\n' f'PostDown = ufw reload\n' f'\n' ) for c in cfg['clients']: conf += ( f'[Peer]\n' f'AllowedIPs = {c["ipv4"] + "/32"}\n' f'AllowedIPs = {c["ipv6"] + "/128"}\n' f'PublicKey = {c["pubkey"]}\n' f'\n' ) return conf if __name__ == '__main__': # create initial config if doesn't exist if not wg_json.is_file(): with wg_json.open('w') as file: json.dump({ 'ipv4': ipv4(None), 'ipv6': ipv6(None), 'key' : genkey(), 'clients': [] }, file, indent=4) file.write('\n') # read config with wg_json.open('r') as file: cfg = json.load(file) # add additional clients for c in sys.argv[1:]: key = genkey() cfg['clients'].append({ 'ipv4' : ipv4(cfg), 'ipv6' : ipv6(cfg), 'pubkey': pubkey(key), }) with open(c + '.conf', 'w') as file: file.write(clientconf(cfg, key)) # qrencode -t ansiutf8 < # generate files with wg_json.open('w') as file: json.dump(cfg, file, indent=4) file.write('\n') with wg_conf.open('w') as file: file.write(serverconf(cfg)) # reload new configs subprocess.check_output(['systemctl', 'reload', 'wg-quick@wg0.service'])