mirror of
https://github.com/dragonlock2/matthewtran.com.git
synced 2026-06-28 01:58:34 +00:00
migrate to coreos
This commit is contained in:
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
BACKUPS = {
|
||||
"web": [
|
||||
"gitea",
|
||||
],
|
||||
"game": [
|
||||
"minecraft",
|
||||
"minecraft_bedrock",
|
||||
"terraria",
|
||||
],
|
||||
}
|
||||
|
||||
def run(cmds):
|
||||
subprocess.run(["ssh", remote, ";".join(cmds)], check=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--restore", action="store_true", help="restore zip instead of saving to it")
|
||||
parser.add_argument("file", type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
cfg = json.load(open("config/server.json"))
|
||||
remote = f"core@{cfg["core"]["hostname"]}.local"
|
||||
if args.restore:
|
||||
# stop needed containers
|
||||
run([
|
||||
f"sudo systemctl --machine={user}@.host --user stop " + " ".join(BACKUPS[user])
|
||||
for user in BACKUPS
|
||||
])
|
||||
|
||||
# restore backup
|
||||
subprocess.run(["scp", args.file, f"{remote}:{cfg["core"]["data_dir"]}/data.zip"], check=True)
|
||||
run([
|
||||
f"cd {cfg["core"]["data_dir"]}",
|
||||
"sudo rm -rf " + " ".join([
|
||||
" ".join([f"{user}/{img}" for img in BACKUPS[user]])
|
||||
for user in BACKUPS
|
||||
]),
|
||||
"unzip data.zip",
|
||||
"rm data.zip",
|
||||
])
|
||||
|
||||
# fix permissions
|
||||
run([f"cd {cfg["core"]["data_dir"]}"] + [
|
||||
f"sudo chown -R {user}:{user} " + " ".join([f"{user}/{img}" for img in BACKUPS[user]])
|
||||
for user in BACKUPS
|
||||
])
|
||||
|
||||
# restart needed containers
|
||||
run([
|
||||
f"sudo systemctl --machine={user}@.host --user restart {user}-pod " + " ".join(BACKUPS[user])
|
||||
for user in BACKUPS
|
||||
])
|
||||
else:
|
||||
run([
|
||||
f"cd {cfg["core"]["data_dir"]}",
|
||||
f"sudo zip -FS -r /var/mnt/stash/data.zip " + " ".join([
|
||||
" ".join([f"{user}/{img}" for img in BACKUPS[user]])
|
||||
for user in BACKUPS
|
||||
]),
|
||||
])
|
||||
subprocess.run(["scp", f"{remote}:/var/mnt/stash/data.zip", args.file], check=True)
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from ipaddress import ip_address, ip_network
|
||||
from 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...")
|
||||
Executable
+398
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import base64
|
||||
import json
|
||||
import http.server
|
||||
import os
|
||||
import secrets
|
||||
import socket
|
||||
import socketserver
|
||||
import subprocess
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from update import SOURCE_DIR, IMAGES, generate
|
||||
|
||||
UIDS = {
|
||||
"web" : 1001,
|
||||
"monero" : 1002,
|
||||
"game" : 1003,
|
||||
"nas" : 1004,
|
||||
}
|
||||
|
||||
PORTS = {
|
||||
"web": [
|
||||
"80:80", # website
|
||||
"443:443",
|
||||
"2222:22", # gitea
|
||||
],
|
||||
"monero": [
|
||||
"18080:18080", # monerod
|
||||
"18081:18081",
|
||||
"3333:3333", # p2pool
|
||||
"37888:37888",
|
||||
"37889:37889",
|
||||
],
|
||||
"game": [
|
||||
"25565:25565", # minecraft
|
||||
"19132:19132/udp", # minecraft_bedrock
|
||||
"19133:19133/udp",
|
||||
"7777:7777", # terraria
|
||||
],
|
||||
"nas": [
|
||||
"445:445", # nas
|
||||
],
|
||||
}
|
||||
|
||||
def check_keys():
|
||||
if "stash_key" not in cfg["core"]:
|
||||
print(f'cfg["core"]["stash_key"] doesn\'t exist, try "{base64.b64encode(secrets.token_bytes(64)).decode("utf-8")}"')
|
||||
exit(1)
|
||||
for i, d in enumerate(cfg["drives"]):
|
||||
if "key" not in d:
|
||||
print(f'cfg["drives"][{i}]["key"] doesn\'t exist, try "{base64.b64encode(secrets.token_bytes(64)).decode("utf-8")}"')
|
||||
exit(1)
|
||||
|
||||
def add_root_drive():
|
||||
but["storage"] = {
|
||||
"disks": [
|
||||
{
|
||||
"device": "/dev/disk/by-id/coreos-boot-disk",
|
||||
"wipe_table": False,
|
||||
"partitions": [
|
||||
{
|
||||
"number": 4,
|
||||
"label": "root",
|
||||
"size_mib": 65536,
|
||||
"resize": True,
|
||||
},
|
||||
{
|
||||
"label": "stash",
|
||||
"size_mib": 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"raid": [],
|
||||
"luks": [
|
||||
{
|
||||
"name": "root",
|
||||
"label": "luks-root",
|
||||
"device": "/dev/disk/by-partlabel/root",
|
||||
"wipe_volume": True,
|
||||
"clevis": { "tpm2": True },
|
||||
},
|
||||
{
|
||||
"name": "stash",
|
||||
"device": "/dev/disk/by-partlabel/stash",
|
||||
"wipe_volume": cfg["core"]["stash_wipe"],
|
||||
"key_file": { "inline": base64.b64decode(cfg["core"]["stash_key"]) },
|
||||
},
|
||||
],
|
||||
"filesystems": [
|
||||
{
|
||||
"device": "/dev/mapper/root",
|
||||
"format": "xfs",
|
||||
"wipe_filesystem": True,
|
||||
"label": "root",
|
||||
},
|
||||
{
|
||||
"path": "/var/mnt/stash",
|
||||
"device": "/dev/mapper/stash",
|
||||
"format": "ext4",
|
||||
"wipe_filesystem": cfg["core"]["stash_wipe"],
|
||||
"with_mount_unit": True,
|
||||
},
|
||||
],
|
||||
"directories": [
|
||||
{
|
||||
"path": f"/var/mnt/stash",
|
||||
"user": { "name": "core" },
|
||||
"group": { "name": "core" },
|
||||
},
|
||||
],
|
||||
"files": [],
|
||||
}
|
||||
|
||||
def add_more_drive():
|
||||
for d in cfg["drives"]:
|
||||
raid = len(d["devices"]) > 1
|
||||
if raid:
|
||||
but["storage"]["raid"].append({
|
||||
"name": d["name"],
|
||||
"level": "raid1",
|
||||
"devices": d["devices"],
|
||||
})
|
||||
but["storage"]["luks"].append({
|
||||
"name": d["name"],
|
||||
"device": f"/dev/md/{d["name"]}" if raid else d["devices"][0],
|
||||
"wipe_volume": d["wipe"],
|
||||
"key_file": { "inline": base64.b64decode(d["key"]) },
|
||||
})
|
||||
but["storage"]["filesystems"].append({
|
||||
"path": f"/var/mnt/{d["name"]}",
|
||||
"device": f"/dev/mapper/{d["name"]}",
|
||||
"format": "ext4",
|
||||
"wipe_filesystem": d["wipe"],
|
||||
"with_mount_unit": True,
|
||||
})
|
||||
but["storage"]["directories"].append({
|
||||
"path": f"/var/mnt/{d["name"]}",
|
||||
"user": { "name": "core" },
|
||||
"group": { "name": "core" },
|
||||
})
|
||||
|
||||
def add_packages():
|
||||
# TODO update once done https://github.com/coreos/fedora-coreos-tracker/issues/681
|
||||
but["systemd"] = {
|
||||
"units": [
|
||||
{
|
||||
"name": "rpm-ostree-install.service",
|
||||
"enabled": True,
|
||||
"contents": "\n".join([
|
||||
"[Unit]",
|
||||
"Description=Install packages",
|
||||
"Wants=network-online.target",
|
||||
"After=network-online.target",
|
||||
"Before=zincati.service",
|
||||
"ConditionPathExists=!/etc/rpm/%N.stamp",
|
||||
"[Service]",
|
||||
"Type=oneshot",
|
||||
"RemainAfterExit=yes",
|
||||
f"ExecStart=/usr/bin/usermod -a -G {",".join(UIDS.keys())} core",
|
||||
"ExecStart=/usr/bin/rpm-ostree install -y --allow-inactive " + " ".join([
|
||||
"avahi",
|
||||
"htop",
|
||||
"python3",
|
||||
"vim",
|
||||
"zip",
|
||||
]),
|
||||
"ExecStart=/bin/touch /etc/rpm/%N.stamp",
|
||||
"ExecStart=/bin/systemctl --no-block reboot",
|
||||
"[Install]",
|
||||
"WantedBy=multi-user.target",
|
||||
]),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def add_ssh_keys():
|
||||
assert(len(cfg["core"]["ssh_keys"]) > 0)
|
||||
but["passwd"] = {
|
||||
"users": [
|
||||
{
|
||||
"name": "core",
|
||||
"ssh_authorized_keys": cfg["core"]["ssh_keys"],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def set_hostname():
|
||||
but["storage"]["files"].append({
|
||||
"path": "/etc/hostname",
|
||||
"mode": 0o644,
|
||||
"contents": { "inline": cfg["core"]["hostname"] },
|
||||
})
|
||||
|
||||
def allow_port_access():
|
||||
but["storage"]["files"].append({
|
||||
"path": "/etc/sysctl.d/99-unprivileged-ports.conf",
|
||||
"mode": 0o644,
|
||||
"contents": { "inline": "net.ipv4.ip_unprivileged_port_start=80" },
|
||||
})
|
||||
|
||||
def add_users():
|
||||
for user in UIDS:
|
||||
but["passwd"]["users"].append({
|
||||
"name": user,
|
||||
"uid": UIDS[user],
|
||||
"ssh_authorized_keys": cfg["core"]["ssh_keys"],
|
||||
})
|
||||
but["storage"]["files"].append({
|
||||
"path": f"/var/lib/systemd/linger/{user}",
|
||||
"contents": { "inline": "" },
|
||||
})
|
||||
|
||||
def copy_source():
|
||||
but["storage"]["directories"].append({
|
||||
"path": SOURCE_DIR,
|
||||
"user": { "name": "core" },
|
||||
"group": { "name": "core" },
|
||||
})
|
||||
for user in IMAGES:
|
||||
for img in IMAGES[user]:
|
||||
but["storage"]["directories"].append({
|
||||
"path": str(Path(SOURCE_DIR) / img),
|
||||
"mode": 0o770,
|
||||
"user": { "name": user },
|
||||
"group": { "name": user },
|
||||
})
|
||||
for f in Path(img).glob("**/*"):
|
||||
if f.is_dir():
|
||||
but["storage"]["directories"].append({
|
||||
"path": str(Path(SOURCE_DIR) / f),
|
||||
"user": { "name": user },
|
||||
"group": { "name": user },
|
||||
})
|
||||
else:
|
||||
but["storage"]["files"].append({
|
||||
"path": str(Path(SOURCE_DIR) / f),
|
||||
"contents": { "inline": open(f, "rb").read() },
|
||||
"user": { "name": user },
|
||||
"group": { "name": user },
|
||||
})
|
||||
but["storage"]["files"].append({
|
||||
"path": "/var/opt/router.py",
|
||||
"mode": 0o755,
|
||||
"contents": { "inline": open("config/router.py", "rb").read() },
|
||||
})
|
||||
|
||||
def build_images():
|
||||
but["storage"]["directories"].append({ "path": "/etc/containers/systemd/users" })
|
||||
for user in IMAGES:
|
||||
but["storage"]["directories"].append({ "path": f"/etc/containers/systemd/users/{UIDS[user]}" })
|
||||
for img in IMAGES[user]:
|
||||
but["storage"]["files"].append({
|
||||
"path": f"/etc/containers/systemd/users/{UIDS[user]}/{img}.build",
|
||||
"contents": { "inline": "\n".join([
|
||||
"[Build]",
|
||||
f"ImageTag={img}",
|
||||
f"SetWorkingDirectory={SOURCE_DIR}/{img}",
|
||||
])}
|
||||
})
|
||||
|
||||
def create_pods():
|
||||
for user in IMAGES:
|
||||
but["storage"]["files"].append({
|
||||
"path": f"/etc/containers/systemd/users/{UIDS[user]}/{user}.pod",
|
||||
"contents": { "inline": "\n".join([
|
||||
"[Pod]",
|
||||
*[f"PublishPort={p}" for p in PORTS[user]],
|
||||
])},
|
||||
})
|
||||
|
||||
def create_folders():
|
||||
but["storage"]["directories"].append({
|
||||
"path": cfg["core"]["data_dir"],
|
||||
"user": { "name": "core" },
|
||||
"group": { "name": "core" },
|
||||
})
|
||||
for user in IMAGES:
|
||||
but["storage"]["directories"].append({
|
||||
"path": str(Path(cfg["core"]["data_dir"]) / user),
|
||||
"mode": 0o770,
|
||||
"user": { "name": user },
|
||||
"group": { "name": user },
|
||||
})
|
||||
for img in IMAGES[user]:
|
||||
but["storage"]["directories"].append({
|
||||
"path": str(Path(cfg["core"]["data_dir"]) / user / img),
|
||||
"user": { "name": user },
|
||||
"group": { "name": user },
|
||||
})
|
||||
for mnt in cfg["nas"]["mounts"]:
|
||||
but["storage"]["directories"].append({
|
||||
"path": str(Path(cfg["nas"]["mounts"][mnt]) / "share"),
|
||||
"mode": 0o770,
|
||||
"user": { "name": "nas" },
|
||||
"group": { "name": "nas" },
|
||||
})
|
||||
|
||||
def run_containers():
|
||||
for user in IMAGES:
|
||||
for img in IMAGES[user]:
|
||||
env = []
|
||||
if img == "gitea":
|
||||
env.extend([
|
||||
"Environment=GITEA__server__SSH_PORT=2222",
|
||||
"Environment=GITEA__service__DISABLE_REGISTRATION=true",
|
||||
"Environment=GITEA__openid__ENABLE_OPENID_SIGNIN=false",
|
||||
"Environment=GITEA__openid__ENABLE_OPENID_SIGNUP=false",
|
||||
])
|
||||
|
||||
vols = [f"Volume={str(Path(cfg["core"]["data_dir"]) / user / img)}:/data:z"]
|
||||
if user == "nas":
|
||||
vols.extend([
|
||||
f"Volume={str(Path(cfg["nas"]["mounts"][mnt]) / "share")}:/mnt/{mnt}:z"
|
||||
for mnt in cfg["nas"]["mounts"]
|
||||
])
|
||||
|
||||
but["storage"]["files"].append({
|
||||
"path": f"/etc/containers/systemd/users/{UIDS[user]}/{img}.container",
|
||||
"contents": { "inline": "\n".join([
|
||||
"[Container]",
|
||||
f"ContainerName={img}",
|
||||
f"Image={img}.build",
|
||||
f"Pod={user}.pod",
|
||||
*env,
|
||||
*vols,
|
||||
"[Install]",
|
||||
"WantedBy=default.target",
|
||||
])}
|
||||
})
|
||||
|
||||
def advertise_services():
|
||||
but["storage"]["directories"].extend([
|
||||
{ "path": "/etc/avahi" },
|
||||
{ "path": "/etc/avahi/services" },
|
||||
])
|
||||
for mnt in cfg["nas"]["mounts"]:
|
||||
but["storage"]["files"].append({
|
||||
"path": f"/etc/avahi/services/nas-{mnt}.service",
|
||||
"contents": { "inline": "\n".join([
|
||||
"<?xml version=\"1.0\" standalone='no'?>",
|
||||
"<!DOCTYPE service-group SYSTEM \"avahi-service.dtd\">",
|
||||
"<service-group>",
|
||||
f" <name replace-wildcards=\"yes\">%h - {mnt}</name>",
|
||||
" <service>",
|
||||
" <type>_smb._tcp</type>",
|
||||
" <port>445</port>",
|
||||
" </service>",
|
||||
" <service>",
|
||||
" <type>_adisk._tcp</type>",
|
||||
f" <txt-record>dk0=adVN={mnt},adVF=0x82</txt-record>",
|
||||
" <txt-record>sys=waMa=0,adVF=0x100</txt-record>",
|
||||
" </service>",
|
||||
"</service-group>",
|
||||
])}
|
||||
})
|
||||
|
||||
if __name__ == "__main__":
|
||||
cfg = json.load(open("config/server.json"))
|
||||
but = {
|
||||
"variant": "fcos",
|
||||
"version": "1.6.0",
|
||||
}
|
||||
|
||||
# core setup
|
||||
check_keys()
|
||||
add_root_drive()
|
||||
add_more_drive()
|
||||
add_packages()
|
||||
add_ssh_keys()
|
||||
set_hostname()
|
||||
allow_port_access()
|
||||
|
||||
# server setup
|
||||
add_users()
|
||||
generate(cfg)
|
||||
copy_source()
|
||||
build_images()
|
||||
create_pods()
|
||||
create_folders()
|
||||
run_containers()
|
||||
advertise_services()
|
||||
|
||||
# generate ignition file
|
||||
with open("config/server.bu", "w") as f:
|
||||
f.write(yaml.dump(but, sort_keys=False))
|
||||
subprocess.check_output(["butane", "-p", "-s", "-o", "config/server.ign", "config/server.bu"])
|
||||
|
||||
# host ignition file
|
||||
ip = socket.gethostbyname(socket.gethostname())
|
||||
print("WARNING - Using unencrypted connections without authentication, ensure LAN is secure!")
|
||||
print("NOTE - TPM may need to be cleared after enough provisions.")
|
||||
print(f"NOTE - Run \"sudo coreos-installer install /dev/<boot drive> --ignition-url http://{ip}/server.ign --insecure-ignition\"")
|
||||
print("NOTE - Starting HTTP server, ctrl-c to exit...")
|
||||
os.chdir("config")
|
||||
with socketserver.TCPServer(("", 80), http.server.SimpleHTTPRequestHandler) as httpd:
|
||||
httpd.serve_forever()
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import subprocess
|
||||
|
||||
DP_LEN = 64 # xfinity delegated prefix length
|
||||
WRT_ULA = "fd16:8f4d:f516::" # OpenWrt random
|
||||
WG_ULA = "fd32:76a6:ec61:577a::" # WireGuard random
|
||||
IPV4 = "192.168.1.69" # OpenWrt default
|
||||
IPV6 = "69"
|
||||
WG_IPV4 = "192.168.2.1" # WireGuard chosen
|
||||
WG_IPV6 = WG_ULA + "1"
|
||||
|
||||
def run(cmds):
|
||||
# ssh-keygen -t ed25519
|
||||
subprocess.run(["ssh", "root@OpenWrt.lan", ";".join(cmds)], check=True)
|
||||
|
||||
def mac(eth):
|
||||
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__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--provision", type=str, help="network interface to set static IP for, e.g. enp5s0")
|
||||
args = parser.parse_args()
|
||||
|
||||
# update static IP
|
||||
if args.provision is None:
|
||||
run([
|
||||
f"uci set dhcp.@host[0].duid='{duid()}'",
|
||||
"uci commit dhcp",
|
||||
"service dnsmasq restart",
|
||||
"service odhcpd restart",
|
||||
])
|
||||
exit(0)
|
||||
|
||||
# basic setup
|
||||
run([
|
||||
f"uci set network.globals.ula_prefix='{WRT_ULA}/48'",
|
||||
"uci set dropbear.main.Interface='lan'",
|
||||
"uci commit network",
|
||||
"uci commit dropbear",
|
||||
])
|
||||
|
||||
# static IP
|
||||
run([
|
||||
"uci add dhcp host",
|
||||
f"uci set dhcp.@host[-1].mac='{mac(args.provision)}'",
|
||||
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].family='ipv6'",
|
||||
"uci set firewall.@rule[-1].src='wan'",
|
||||
"uci set firewall.@rule[-1].dest='lan'",
|
||||
f"uci set firewall.@rule[-1].dest_ip='::{IPV6}/{DP_LEN-128}'",
|
||||
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
|
||||
# TODO configure NAT66 to fix tunneling IPv6 traffic
|
||||
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",
|
||||
])
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"core": {
|
||||
"hostname": "<server name>",
|
||||
"ssh_keys": [
|
||||
"ssh-ed25519 AAAA..."
|
||||
],
|
||||
"stash_key": "<LUKS key>",
|
||||
"stash_wipe": false,
|
||||
"data_dir": "/var/home/core/matthewtrancom_data"
|
||||
},
|
||||
"drives": [
|
||||
{
|
||||
"devices": ["/dev/sda"],
|
||||
"key": "<LUKS key>",
|
||||
"name": "stuff",
|
||||
"wipe": false
|
||||
}
|
||||
],
|
||||
"website": {
|
||||
"sendgrid_key": "<SendGrid API key from https://app.sendgrid.com/settings/api_keys>"
|
||||
},
|
||||
"minecraft": {
|
||||
"world": "main"
|
||||
},
|
||||
"minecraft_bedrock": {
|
||||
"world": "main"
|
||||
},
|
||||
"terraria": {
|
||||
"password": "password",
|
||||
"world": "main",
|
||||
"autogen": {
|
||||
"size": 3,
|
||||
"difficulty": 2
|
||||
}
|
||||
},
|
||||
"nas": {
|
||||
"users": {
|
||||
"matt": "password"
|
||||
},
|
||||
"mounts": {
|
||||
"stuff": "/var/mnt/stuff"
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
SOURCE_DIR = "/var/source"
|
||||
|
||||
IMAGES = {
|
||||
"web": [
|
||||
"website",
|
||||
"gitea",
|
||||
],
|
||||
"monero": [
|
||||
"monerod",
|
||||
"p2pool",
|
||||
],
|
||||
"game": [
|
||||
"minecraft",
|
||||
"minecraft_bedrock",
|
||||
"terraria",
|
||||
],
|
||||
"nas": [
|
||||
"nas",
|
||||
],
|
||||
}
|
||||
|
||||
def generate(cfg):
|
||||
# website
|
||||
with open("website/sendgrid.key", "w") as f:
|
||||
f.write(cfg["website"]["sendgrid_key"])
|
||||
|
||||
# minecraft
|
||||
shutil.copy("minecraft/server.default", "minecraft/server.properties")
|
||||
with open("minecraft/server.properties", "a") as f:
|
||||
f.write(f"level-name=/data/{cfg["minecraft"]["world"]}\n")
|
||||
|
||||
# minecraft_bedrock
|
||||
shutil.copy("minecraft_bedrock/server.default", "minecraft_bedrock/server.properties")
|
||||
with open("minecraft_bedrock/server.properties", "a") as f:
|
||||
f.write(f"level-name={cfg["minecraft_bedrock"]["world"]}\n")
|
||||
|
||||
# terraria
|
||||
shutil.copy("terraria/config.default", "terraria/config.txt")
|
||||
with open("terraria/config.txt", "a") as f:
|
||||
f.write(f"world=/data/worlds/{cfg["terraria"]["world"]}.wld\n")
|
||||
f.write(f"autocreate={cfg["terraria"]["autogen"]["size"]}\n") # 1=small, 2=medium, 3=large
|
||||
f.write(f"difficulty={cfg["terraria"]["autogen"]["difficulty"]}\n") # 0=normal, 1=expert, 2=master, 3=journey
|
||||
with open("terraria/password.txt", "w") as f:
|
||||
f.write(cfg["terraria"]["password"])
|
||||
|
||||
# nas
|
||||
shutil.copy("nas/Dockerfile.template", "nas/Dockerfile")
|
||||
shutil.copy("nas/smb.conf.template", "nas/smb.conf")
|
||||
with open("nas/Dockerfile", "a") as f:
|
||||
for user in cfg["nas"]["users"]:
|
||||
p = cfg["nas"]["users"][user]
|
||||
f.write(f"RUN useradd -M -s /bin/false {user}\n")
|
||||
f.write(f"RUN echo \"{p}\\n{p}\\n\" | pdbedit -s smb.conf -a {user}\n")
|
||||
with open("nas/smb.conf", "a") as f:
|
||||
for mnt in cfg["nas"]["mounts"]:
|
||||
f.write(f"[{mnt}]\n")
|
||||
f.write(f"path = /mnt/{mnt}\n\n")
|
||||
|
||||
def run(cmds, user="core"):
|
||||
try:
|
||||
subprocess.check_output(["ssh", f"{user}@{cfg["core"]["hostname"]}.local", ";".join(cmds)], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("\033[31m", end="")
|
||||
print(e.output.decode())
|
||||
print("\033[0m", end="")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
cfg = json.load(open("config/server.json"))
|
||||
|
||||
# generate helper files
|
||||
generate(cfg)
|
||||
|
||||
# copy files
|
||||
for user in IMAGES:
|
||||
for img in IMAGES[user]:
|
||||
subprocess.run(["scp", "-r", img, f"{user}@{cfg["core"]["hostname"]}.local:{SOURCE_DIR}"], check=True)
|
||||
run([f"chmod 770 {SOURCE_DIR}/{img}"], user=user)
|
||||
|
||||
# run builds
|
||||
for user in IMAGES:
|
||||
print(f"building images for {user}...")
|
||||
run([f"cd {SOURCE_DIR}"] + [
|
||||
f"sudo -u {user} podman build --tag {i} {SOURCE_DIR}/{i}"
|
||||
for i in IMAGES[user]
|
||||
])
|
||||
|
||||
# restart pods
|
||||
for user in IMAGES:
|
||||
print(f"restarting pod for {user}...")
|
||||
run([
|
||||
f"cd {SOURCE_DIR}",
|
||||
f"sudo systemctl --machine={user}@.host --user restart {user}-pod " + " ".join(IMAGES[user]),
|
||||
])
|
||||
Reference in New Issue
Block a user