diff --git a/README.md b/README.md index 0ab3cac..f3cb97b 100644 --- a/README.md +++ b/README.md @@ -5,62 +5,39 @@ Stuff that's deployed on [matthewtran.com](https://matthewtran.com). Currently r - website - gitea ([git.matthewtran.com](https://git.matthewtran.com)) - monerod -- p2pool (`xmrig -o matthewtran.com:3333`) -- minecraft -- minecraft bedrock -- ~~terraria~~ +- p2pool (`xmrig -o matthewtran.com`) - wireguard +- minecraft +- ~~minecraft bedrock~~ +- ~~terraria~~ ## setup -1. Install [Ubuntu Server 24.04.1 LTS](https://ubuntu.com/download/server). - - Add OpenSSH Server and Docker during the process. - - Expand the root partition if needed. - - `lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv` - - `resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv` - - Enable huge pages. - - `echo "vm.nr_hugepages=3072" | sudo tee -a /etc/sysctl.conf` - - Give yourself Docker access if needed. - - `groupadd docker` - - `usermod -aG docker $USER` - - Enable UFW and allow the following. - - `ufw enable` - - `ufw allow OpenSSH` - - `ufw allow 51820/udp` - - Reboot. -2. Forward the following ports. Set a static IP if needed. - - website - `80`, `443` - - gitea - `2222` - - monerod - `18080`, `18081` - - p2pool - `3333`, `37888`, `37889` - - minecraft - `25565` - - minecraft bedrock - `19132`, `19133` - - terraria - `7777` - - wireguard - `51820` -3. Install dependencies and clone. - - `apt install avahi-daemon git python3 python-is-python3 qrencode wireguard zip` - - `git clone https://github.com/dragonlock2/matthewtran.com` - - `cd matthewtran.com` -4. Set up WireGuard. - - `systemctl enable wg-quick@wg0.service` - - `python wireguard/setup.py` - - `systemctl start wg-quick@wg0.service` -5. Enable IPv6 for Docker. - - Add the following to `/var/snap/docker/current/config/daemon.json`. - - `"ipv6": true` - - `"fixed-cidr-v6": "fd3a:138e:8fd0:0000::/64"` - - `systemctl restart snap.docker.dockerd.service` -6. Set up the repo. - - Run `./volumes.sh`, allowing the containers to access the binded volumes since you have the same UID/GID by default. +1. Install [Ubuntu Desktop 24.04.1 LTS](https://ubuntu.com/download/desktop) with TPM-backed FDE. Server currently has a [bug](https://bugs.launchpad.net/ubuntu/+source/cryptsetup/+bug/1980018) that makes TPM-backed FDE hard. + - You may need to manually enable IPv6 on the network connection. Use `Automatic` not `Automatic, DHCP only`. + - Clone this repo and `cd` into it. +2. Set up the server. + - `scripts/setup_server.py` +3. Set up the OpenWrt 24.10 router. Copy SSH keys first to make it easier. + - `scripts/setup_router.py` + - For each WireGuard client, run `scripts/setup_peer.py `. + - Reboot the router and server. +4. Configure, build, and start services. - Create `website/sendgrid.key` with a [SendGrid API key](https://app.sendgrid.com/settings/api_keys). - Create `terraria/password.txt` if needed. - Restore backups if needed. -7. Build and start the services. + - `scripts/setup_repo.py` - `docker compose build` - `docker compose up -d` -8. If first start, some services need configuring. - - Gitea +5. Optionally, add additional drives. This script formats the drive as LUKS/BTRFS with the key file stored in `/opt/luks` and auto-mounts on boot. Make sure to backup the key file elsewhere. + - `scripts/setup_drive.py ` +6. Optionally, add the following DNS entries at the registrar. + | hosts | type | data | + | ----------------------- | ------ | ------------------------------------- | + | `@`, `git`, `wg`, `www` | `A` | `` | + | `@`, `git`, `www` | `AAAA` | `::` | + | `wg` | `AAAA` | `::1` | ## backup -Run `./backup.sh` and save the resultant `data.zip` somewhere. I should probably automate this. +Run `scripts/backup.py` and save the resultant `data.zip` somewhere. I should probably automate this. diff --git a/backup.sh b/backup.sh deleted file mode 100755 index 0fa55ce..0000000 --- a/backup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -zip -FS -r data.zip \ - minecraft/world* \ - minecraft_bedrock/worlds \ - terraria/worlds \ - terraria/password.txt \ - website/gitea \ - website/letsencrypt \ - website/sendgrid.key diff --git a/compose.yml b/compose.yml index 02ee98f..98c87ba 100644 --- a/compose.yml +++ b/compose.yml @@ -66,18 +66,18 @@ services: networks: - default6 volumes: - - ./minecraft/world:/home/ubuntu/world - minecraft_bedrock: - restart: always - build: minecraft_bedrock/. - entrypoint: ["/bin/sh", "/home/ubuntu/entry.sh"] - ports: - - "19132:19132/udp" - - "19133:19133/udp" - networks: - - default6 - volumes: - - ./minecraft_bedrock/worlds:/home/ubuntu/worlds + - ./minecraft/worlds:/home/ubuntu/worlds + # minecraft_bedrock: + # restart: always + # build: minecraft_bedrock/. + # entrypoint: ["/bin/sh", "/home/ubuntu/entry.sh"] + # ports: + # - "19132:19132/udp" + # - "19133:19133/udp" + # networks: + # - default6 + # volumes: + # - ./minecraft_bedrock/worlds:/home/ubuntu/worlds # terraria: # restart: always # build: terraria/. diff --git a/minecraft/server.properties b/minecraft/server.properties index 0192d7a..99bb9e6 100644 --- a/minecraft/server.properties +++ b/minecraft/server.properties @@ -20,7 +20,7 @@ hardcore=true hide-online-players=false initial-disabled-packs= initial-enabled-packs=vanilla -level-name=world +level-name=worlds/main level-seed= level-type=minecraft\:normal max-chained-neighbor-updates=1000000 diff --git a/scripts/backup.py b/scripts/backup.py new file mode 100755 index 0000000..84126f0 --- /dev/null +++ b/scripts/backup.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import subprocess + +if __name__ == "__main__": + subprocess.run(["zip", "-FS", "-r", "data.zip", + "minecraft/worlds", + "minecraft_bedrock/worlds", + "terraria/worlds", + "terraria/password.txt", + "website/gitea", + "website/letsencrypt", + "website/sendgrid.key", + ], check=True) diff --git a/scripts/setup_drive.py b/scripts/setup_drive.py new file mode 100755 index 0000000..5e2e01f --- /dev/null +++ b/scripts/setup_drive.py @@ -0,0 +1,35 @@ +#!/usr/bin/sudo /usr/bin/python3 + +import subprocess +import sys +from pathlib import Path + +KEY_DIR = Path("/opt/luks") + +def run(cmd): + subprocess.run(cmd.split(), check=True) + +if __name__ == "__main__": + drive = sys.argv[1] + mount = Path(sys.argv[2]) + key = KEY_DIR / f"{drive}.key" + assert(Path(f"/dev/{drive}").exists()) + assert(not key.exists()) + + # create directories and key + KEY_DIR.mkdir(exist_ok=True) + mount.mkdir(exist_ok=True) + run(f"dd if=/dev/random bs=32 count=1 of={key}") + key.chmod(0o400) + + # format and mount drive + run(f"cryptsetup luksFormat --key-file={key} /dev/{drive}") + run(f"cryptsetup luksOpen --key-file={key} /dev/{drive} {drive}_luks") + run(f"mkfs.btrfs /dev/mapper/{drive}_luks") + run(f"mount /dev/mapper/{drive}_luks {mount}") + mount.chmod(0o777) + + # TODO modify /etc/crypttab instead once Ubuntu fixed + with open("/opt/luks.sh", "a") as f: + f.write(f"systemd-cryptsetup attach {drive}_luks /dev/{drive} {key} luks\n") + f.write(f"mount /dev/mapper/{drive}_luks {mount}\n") diff --git a/scripts/setup_peer.py b/scripts/setup_peer.py new file mode 100755 index 0000000..d96c982 --- /dev/null +++ b/scripts/setup_peer.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +from ipaddress import ip_address, ip_network +from setup_router import WG_IPV4, WG_IPV6, run, key + +def ips(): + try: + ret = subprocess.check_output(["ssh", "root@OpenWrt.lan", "uci get network.@wireguard_wg0[-1].allowed_ips"]).decode("utf-8") + ipv4, ipv6 = ret.split() + ipv4, ipv6 = ipv4.split("/")[0], ipv6.split("/")[0] + except subprocess.CalledProcessError: + ipv4, ipv6 = WG_IPV4, WG_IPV6 + net4, net6 = ip_network(ipv4 + "/24", strict=False), ip_network(ipv6 + "/64", strict=False) + ipv4, ipv6 = ip_address(ipv4), ip_address(ipv6) + if (ipv4 + 1) in net4 and (ipv6 + 1) in net6: + return str(ipv4 + 1), str(ipv6 + 1) + raise Exception("no ips left") + +if __name__ == "__main__": + name = sys.argv[1] + ipv4, ipv6 = ips() + pub, priv = key() + run([ + "uci add network wireguard_wg0", + f"uci set network.@wireguard_wg0[-1].description='{name}'", + f"uci set network.@wireguard_wg0[-1].public_key='{pub}'", + f"uci set network.@wireguard_wg0[-1].private_key='{priv}'", + f"uci add_list network.@wireguard_wg0[-1].allowed_ips='{ipv4}/32'", + f"uci add_list network.@wireguard_wg0[-1].allowed_ips='{ipv6}/128'", + "uci set network.@wireguard_wg0[-1].endpoint_host='wg.matthewtran.com'", + "uci set network.@wireguard_wg0[-1].endpoint_port='51820'", + "uci set network.@wireguard_wg0[-1].persistent_keepalive='25'", + "uci commit network", + "ifdown wg0", + "ifup wg0", + ]) + print("Go to the following to generate configuration, double check endpoint:") + print(f"Network > Interfaces > wg0 (Edit) > Peers > {name} (Edit) > Generate configuration...") diff --git a/scripts/setup_repo.py b/scripts/setup_repo.py new file mode 100755 index 0000000..d64ebb8 --- /dev/null +++ b/scripts/setup_repo.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +from pathlib import Path + +if __name__ == "__main__": + # create folders with same UID/GID as user so containers have access + PATHS = [ + "website/letsencrypt", + "website/gitea/config", + "website/gitea/data", + "monerod/.bitmonero", + "p2pool/cache", + "minecraft/worlds", + "minecraft_bedrock/worlds", + "terraria/worlds", + ] + for p in PATHS: + Path(p).mkdir(parents=True, exist_ok=True) diff --git a/scripts/setup_router.py b/scripts/setup_router.py new file mode 100755 index 0000000..2b9cfc7 --- /dev/null +++ b/scripts/setup_router.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +import hashlib +import subprocess + +ETH = "enp5s0" +IPV4 = "192.168.1.69" +IPV6 = "69" +WG_IPV4 = "192.168.2.1" +WG_IPV6 = "fd32:76a6:ec61:577a::1" + +def run(cmds): + # ssh-keygen -t ed25519 + subprocess.run(["ssh", "root@OpenWrt.lan", ";".join(cmds)], check=True) + +def mac(): + return open(f"/sys/class/net/{ETH}/address", "r").read().strip() + +def duid(): + # adapted from https://github.com/mss/nm-duid + id = bytes.fromhex(open("/etc/machine-id", "r").read().strip()) + return "0004" + hashlib.sha256(id).digest()[:16].hex() + +def key(): + priv = subprocess.check_output(["wg", "genkey"]).strip() + pub = subprocess.check_output(["wg", "pubkey"], input=priv).strip() + return (pub.decode("utf-8"), priv.decode("utf-8")) + +if __name__ == "__main__": + # prevent access using WAN addresses + run([ + "uci set dropbear.main.Interface='lan'", + ]) + + # static IP + run([ + "uci add dhcp host", + "uci set dhcp.@host[-1].name='matt-ryzen'", + f"uci set dhcp.@host[-1].mac='{mac()}'", + f"uci set dhcp.@host[-1].ip='{IPV4}'", + f"uci set dhcp.@host[-1].duid='{duid()}'", + f"uci set dhcp.@host[-1].hostid='{IPV6}'", + "uci commit dhcp", + "service dnsmasq restart", + "service odhcpd restart", + ]) + + # forward traffic + PORTS = { + "http" : "80", + "https" : "443", + "git" : "2222", + "monerod" : "18080-18081", + "p2pool" : "3333", + "p2pool2" : "37888-37889", + "minecraft" : "25565", + "minecraft_be": "19132-19133", + "terraria" : "7777", + } + for name in PORTS: + run([ + # IPv4 port forward + "uci add firewall redirect", + f"uci set firewall.@redirect[-1].name='{name}'", + "uci set firewall.@redirect[-1].target='DNAT'", + "uci set firewall.@redirect[-1].family='IPv4'", + "uci set firewall.@redirect[-1].src='wan'", + f"uci set firewall.@redirect[-1].src_dport='{PORTS[name]}'", + "uci set firewall.@redirect[-1].dest='lan'", + f"uci set firewall.@redirect[-1].dest_ip='{IPV4}'", + f"uci set firewall.@redirect[-1].dest_port='{PORTS[name]}'", + + # IPv6 traffic rules + "uci add firewall rule", + f"uci set firewall.@rule[-1].name='allow-{name}'", + "uci set firewall.@rule[-1].src='wan'", + "uci set firewall.@rule[-1].dest='lan'", + f"uci set firewall.@rule[-1].dest_ip='::{IPV6}/-64'", # xfinity provides /64 => /-64 match + f"uci set firewall.@rule[-1].dest_port='{PORTS[name]}'", + "uci set firewall.@rule[-1].target='ACCEPT'", + ]) + run([ + "uci commit firewall", + "service firewall restart", + ]) + + # wireguard setup + pub, priv = key() + run([ + # install packages + "opkg update", + "opkg install luci-proto-wireguard", + + # create interface + "uci set network.wg0=interface", + "uci set network.wg0.proto='wireguard'", + f"uci set network.wg0.private_key='{priv}'", + "uci set network.wg0.listen_port='51820'", + f"uci add_list network.wg0.addresses='{WG_IPV4}/24'", + f"uci add_list network.wg0.addresses='{WG_IPV6}/64'", + "uci commit network", + + # allow traffic + "uci del firewall.@zone[0].network", + "uci add_list firewall.@zone[0].network='lan'", + "uci add_list firewall.@zone[0].network='wg0'", + "uci add firewall rule", + "uci set firewall.@rule[-1].name='allow-wireguard'", + "uci add_list firewall.@rule[-1].proto='udp'", + "uci set firewall.@rule[-1].src='wan'", + "uci set firewall.@rule[-1].dest_port='51820'", + "uci set firewall.@rule[-1].target='ACCEPT'", + "uci commit firewall", + ]) diff --git a/scripts/setup_server.py b/scripts/setup_server.py new file mode 100755 index 0000000..1982b7f --- /dev/null +++ b/scripts/setup_server.py @@ -0,0 +1,65 @@ +#!/usr/bin/sudo /usr/bin/python3 + +import json +import os +import subprocess +from pathlib import Path +from setup_router import WG_IPV4, WG_IPV6 + +def run(cmd, capture=False): + if capture: + return subprocess.check_output(cmd.split()) + else: + subprocess.run(cmd.split(), check=True) + +if __name__ == "__main__": + # install dependencies and configure + run("apt update") + run("apt upgrade") + run("apt install -y avahi-daemon btrfs-progs python-is-python3 python3-pip wireguard zip") + if run("ufw status", capture=True) == b"Status: inactive\n": + run("ufw enable") + run("ufw allow OpenSSH") + with open("/etc/sysctl.conf", "a+") as f: + f.seek(0) + if "vm.nr_hugepages=3072\n" not in f.readlines(): + f.write("vm.nr_hugepages=3072\n") # enable huge pages + + # install docker and configure + run("snap install docker") + run("addgroup --system docker") + run(f"adduser {os.getlogin()} docker") + run("snap disable docker") + run("snap enable docker") + with open("/var/snap/docker/current/config/daemon.json", "r+") as f: + cfg = json.load(f) + cfg["ipv6"] = True + cfg["fixed-cidr-v6"] = "fd3a:138e:8fd0:0000::/64" + f.seek(0) + json.dump(cfg, f, indent=4) + run("systemctl restart snap.docker.dockerd.service") + + # TODO modify /etc/crypttab instead once Ubuntu fixed + file = Path("/etc/systemd/system/luks.service") + if not file.exists(): + with file.open("w") as f: + f.writelines(s + "\n" for s in [ + "[Unit]", + "Description=Mount more LUKS drives", + "After=local-fs.target", + "Requires=local-fs.target", + "", + "[Service]", + "Type=oneshot", + "ExecStart=/opt/luks.sh", + "RemainAfterExit=yes", + "", + "[Install]", + "WantedBy=multi-user.target", + ]) + file = Path("/opt/luks.sh") + if not file.exists(): + with file.open("w") as f: + f.write("#!/bin/sh\n") + file.chmod(0o755) + run("systemctl enable luks.service") diff --git a/volumes.sh b/volumes.sh deleted file mode 100755 index 8ca35d4..0000000 --- a/volumes.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -mkdir website/letsencrypt -mkdir -p website/gitea/config -mkdir -p website/gitea/data -mkdir monerod/.bitmonero -mkdir p2pool/cache -mkdir minecraft/world -mkdir minecraft_bedrock/worlds -mkdir terraria/worlds diff --git a/website/Dockerfile b/website/Dockerfile index 79cc89e..f0927f2 100644 --- a/website/Dockerfile +++ b/website/Dockerfile @@ -18,5 +18,4 @@ COPY html /var/www/matthewtran.com/html # start script WORKDIR /root COPY sendgrid.ke[y] ip_update.py ./ -COPY cert_update.py ./ COPY entry.sh ./ diff --git a/website/cert_update.py b/website/cert_update.py deleted file mode 100644 index 8ddfcd5..0000000 --- a/website/cert_update.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import time - -if __name__ == '__main__': - while True: - # try renew once a day - subprocess.run(['certbot', 'renew', '--quiet']) - time.sleep(86400) diff --git a/website/entry.sh b/website/entry.sh index af2bfcd..b273834 100644 --- a/website/entry.sh +++ b/website/entry.sh @@ -1,26 +1,32 @@ #!/bin/sh -# server needs to be up to grab certificates +# server needs to be up to get certs nginx while [ ! -f /var/run/nginx.pid ] do sleep 1 done +# get certs if needed certbot --nginx \ --webroot-path /var/www/matthewtran.com \ --non-interactive --agree-tos -m matthewlamtran@berkeley.edu \ -d matthewtran.com \ -d www.matthewtran.com \ -d git.matthewtran.com - nginx -s reload -python3 ip_update.py & -python3 cert_update.py & +# background process to renew certs and check ip changes +update() { + certbot renew --quiet + sleep 86400 +} +update & +./ip_update.py & + +# wait for termination cleanup() { echo "stopping..." } trap 'cleanup' TERM - wait $! # wait SIGTERM, other processes can just be killed diff --git a/website/ip_update.py b/website/ip_update.py old mode 100644 new mode 100755 index 86bbd90..50327ba --- a/website/ip_update.py +++ b/website/ip_update.py @@ -2,19 +2,20 @@ import time import urllib.request +from ipaddress import ip_network from pathlib import Path from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail -if __name__ == '__main__': - sg = SendGridAPIClient(Path('sendgrid.key').read_text()) +if __name__ == "__main__": + sg = SendGridAPIClient(Path("sendgrid.key").read_text()) old_ipv4, old_ipv6 = None, None while True: try: - ipv4 = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8') - # ipv6 = urllib.request.urlopen('https://v6.ident.me').read().decode('utf8') - ipv6 = None + ipv4 = urllib.request.urlopen("https://v4.ident.me").read().decode("utf8") + ipv6 = urllib.request.urlopen("https://v6.ident.me").read().decode("utf8") + ipv6 = str(ip_network(ipv6 + "/64", strict=False).network_address) # xfinity gives /64 except Exception as e: print(e) time.sleep(60) @@ -22,17 +23,20 @@ if __name__ == '__main__': if ipv4 != old_ipv4 or ipv6 != old_ipv6: msg = Mail( - from_email='mtran319@gmail.com', - to_emails='mtran319@gmail.com', - subject='pls update ip', - html_content=f'

ipv4: {ipv4}

ipv6: {ipv6}

' + from_email="mtran319@gmail.com", + to_emails="mtran319@gmail.com", + subject="pls update ip", + html_content=f"

old ipv4: {old_ipv4}

" + f"

old ipv6: {old_ipv6}

" + f"

new ipv4: {ipv4}

" + f"

new ipv6: {ipv6}

" ) try: - print(f'IP changed to {ipv4} and {ipv6}') + print(f"IP changed to {ipv4} and {ipv6}") resp = sg.send(msg) except Exception as e: print(e) - sg = SendGridAPIClient(Path('sendgrid.key').read_text()) + sg = SendGridAPIClient(Path("sendgrid.key").read_text()) time.sleep(60) continue diff --git a/wireguard/setup.py b/wireguard/setup.py deleted file mode 100755 index 9bec587..0000000 --- a/wireguard/setup.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/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'])