major refactor of setup procedure

This commit is contained in:
Matthew Tran
2025-02-17 23:15:07 -08:00
parent fff87a07ad
commit be0530cafe
16 changed files with 349 additions and 232 deletions
+14
View File
@@ -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)
+35
View File
@@ -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")
+40
View File
@@ -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...")
+18
View File
@@ -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)
+114
View File
@@ -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",
])
+65
View File
@@ -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")