mirror of
https://github.com/dragonlock2/matthewtran.com.git
synced 2025-10-11 20:17:54 +00:00
major refactor of setup procedure
This commit is contained in:
parent
fff87a07ad
commit
be0530cafe
71
README.md
71
README.md
@ -5,62 +5,39 @@ Stuff that's deployed on [matthewtran.com](https://matthewtran.com). Currently r
|
|||||||
- website
|
- website
|
||||||
- gitea ([git.matthewtran.com](https://git.matthewtran.com))
|
- gitea ([git.matthewtran.com](https://git.matthewtran.com))
|
||||||
- monerod
|
- monerod
|
||||||
- p2pool (`xmrig -o matthewtran.com:3333`)
|
- p2pool (`xmrig -o matthewtran.com`)
|
||||||
- minecraft
|
|
||||||
- minecraft bedrock
|
|
||||||
- ~~terraria~~
|
|
||||||
- wireguard
|
- wireguard
|
||||||
|
- minecraft
|
||||||
|
- ~~minecraft bedrock~~
|
||||||
|
- ~~terraria~~
|
||||||
|
|
||||||
## setup
|
## setup
|
||||||
|
|
||||||
1. Install [Ubuntu Server 24.04.1 LTS](https://ubuntu.com/download/server).
|
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.
|
||||||
- Add OpenSSH Server and Docker during the process.
|
- You may need to manually enable IPv6 on the network connection. Use `Automatic` not `Automatic, DHCP only`.
|
||||||
- Expand the root partition if needed.
|
- Clone this repo and `cd` into it.
|
||||||
- `lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv`
|
2. Set up the server.
|
||||||
- `resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv`
|
- `scripts/setup_server.py`
|
||||||
- Enable huge pages.
|
3. Set up the OpenWrt 24.10 router. Copy SSH keys first to make it easier.
|
||||||
- `echo "vm.nr_hugepages=3072" | sudo tee -a /etc/sysctl.conf`
|
- `scripts/setup_router.py`
|
||||||
- Give yourself Docker access if needed.
|
- For each WireGuard client, run `scripts/setup_peer.py <name>`.
|
||||||
- `groupadd docker`
|
- Reboot the router and server.
|
||||||
- `usermod -aG docker $USER`
|
4. Configure, build, and start services.
|
||||||
- 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.
|
|
||||||
- Create `website/sendgrid.key` with a [SendGrid API key](https://app.sendgrid.com/settings/api_keys).
|
- Create `website/sendgrid.key` with a [SendGrid API key](https://app.sendgrid.com/settings/api_keys).
|
||||||
- Create `terraria/password.txt` if needed.
|
- Create `terraria/password.txt` if needed.
|
||||||
- Restore backups if needed.
|
- Restore backups if needed.
|
||||||
7. Build and start the services.
|
- `scripts/setup_repo.py`
|
||||||
- `docker compose build`
|
- `docker compose build`
|
||||||
- `docker compose up -d`
|
- `docker compose up -d`
|
||||||
8. If first start, some services need configuring.
|
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.
|
||||||
- Gitea
|
- `scripts/setup_drive.py <drive> <mount path>`
|
||||||
|
6. Optionally, add the following DNS entries at the registrar.
|
||||||
|
| hosts | type | data |
|
||||||
|
| ----------------------- | ------ | ------------------------------------- |
|
||||||
|
| `@`, `git`, `wg`, `www` | `A` | `<public IPv4>` |
|
||||||
|
| `@`, `git`, `www` | `AAAA` | `<delegated prefix>::<server suffix>` |
|
||||||
|
| `wg` | `AAAA` | `<delegated prefix>::1` |
|
||||||
|
|
||||||
## backup
|
## 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.
|
||||||
|
10
backup.sh
10
backup.sh
@ -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
|
|
24
compose.yml
24
compose.yml
@ -66,18 +66,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- default6
|
- default6
|
||||||
volumes:
|
volumes:
|
||||||
- ./minecraft/world:/home/ubuntu/world
|
- ./minecraft/worlds:/home/ubuntu/worlds
|
||||||
minecraft_bedrock:
|
# minecraft_bedrock:
|
||||||
restart: always
|
# restart: always
|
||||||
build: minecraft_bedrock/.
|
# build: minecraft_bedrock/.
|
||||||
entrypoint: ["/bin/sh", "/home/ubuntu/entry.sh"]
|
# entrypoint: ["/bin/sh", "/home/ubuntu/entry.sh"]
|
||||||
ports:
|
# ports:
|
||||||
- "19132:19132/udp"
|
# - "19132:19132/udp"
|
||||||
- "19133:19133/udp"
|
# - "19133:19133/udp"
|
||||||
networks:
|
# networks:
|
||||||
- default6
|
# - default6
|
||||||
volumes:
|
# volumes:
|
||||||
- ./minecraft_bedrock/worlds:/home/ubuntu/worlds
|
# - ./minecraft_bedrock/worlds:/home/ubuntu/worlds
|
||||||
# terraria:
|
# terraria:
|
||||||
# restart: always
|
# restart: always
|
||||||
# build: terraria/.
|
# build: terraria/.
|
||||||
|
@ -20,7 +20,7 @@ hardcore=true
|
|||||||
hide-online-players=false
|
hide-online-players=false
|
||||||
initial-disabled-packs=
|
initial-disabled-packs=
|
||||||
initial-enabled-packs=vanilla
|
initial-enabled-packs=vanilla
|
||||||
level-name=world
|
level-name=worlds/main
|
||||||
level-seed=
|
level-seed=
|
||||||
level-type=minecraft\:normal
|
level-type=minecraft\:normal
|
||||||
max-chained-neighbor-updates=1000000
|
max-chained-neighbor-updates=1000000
|
||||||
|
14
scripts/backup.py
Executable file
14
scripts/backup.py
Executable 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
scripts/setup_drive.py
Executable file
35
scripts/setup_drive.py
Executable 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
scripts/setup_peer.py
Executable file
40
scripts/setup_peer.py
Executable 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
scripts/setup_repo.py
Executable file
18
scripts/setup_repo.py
Executable 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
scripts/setup_router.py
Executable file
114
scripts/setup_router.py
Executable 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
scripts/setup_server.py
Executable file
65
scripts/setup_server.py
Executable 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")
|
10
volumes.sh
10
volumes.sh
@ -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
|
|
@ -18,5 +18,4 @@ COPY html /var/www/matthewtran.com/html
|
|||||||
# start script
|
# start script
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
COPY sendgrid.ke[y] ip_update.py ./
|
COPY sendgrid.ke[y] ip_update.py ./
|
||||||
COPY cert_update.py ./
|
|
||||||
COPY entry.sh ./
|
COPY entry.sh ./
|
||||||
|
@ -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)
|
|
@ -1,26 +1,32 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# server needs to be up to grab certificates
|
# server needs to be up to get certs
|
||||||
nginx
|
nginx
|
||||||
while [ ! -f /var/run/nginx.pid ]
|
while [ ! -f /var/run/nginx.pid ]
|
||||||
do
|
do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# get certs if needed
|
||||||
certbot --nginx \
|
certbot --nginx \
|
||||||
--webroot-path /var/www/matthewtran.com \
|
--webroot-path /var/www/matthewtran.com \
|
||||||
--non-interactive --agree-tos -m matthewlamtran@berkeley.edu \
|
--non-interactive --agree-tos -m matthewlamtran@berkeley.edu \
|
||||||
-d matthewtran.com \
|
-d matthewtran.com \
|
||||||
-d www.matthewtran.com \
|
-d www.matthewtran.com \
|
||||||
-d git.matthewtran.com
|
-d git.matthewtran.com
|
||||||
|
|
||||||
nginx -s reload
|
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() {
|
cleanup() {
|
||||||
echo "stopping..."
|
echo "stopping..."
|
||||||
}
|
}
|
||||||
trap 'cleanup' TERM
|
trap 'cleanup' TERM
|
||||||
|
|
||||||
wait $! # wait SIGTERM, other processes can just be killed
|
wait $! # wait SIGTERM, other processes can just be killed
|
||||||
|
26
website/ip_update.py
Normal file → Executable file
26
website/ip_update.py
Normal file → Executable file
@ -2,19 +2,20 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
from ipaddress import ip_network
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from sendgrid import SendGridAPIClient
|
from sendgrid import SendGridAPIClient
|
||||||
from sendgrid.helpers.mail import Mail
|
from sendgrid.helpers.mail import Mail
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
sg = SendGridAPIClient(Path('sendgrid.key').read_text())
|
sg = SendGridAPIClient(Path("sendgrid.key").read_text())
|
||||||
|
|
||||||
old_ipv4, old_ipv6 = None, None
|
old_ipv4, old_ipv6 = None, None
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
ipv4 = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8')
|
ipv4 = urllib.request.urlopen("https://v4.ident.me").read().decode("utf8")
|
||||||
# ipv6 = urllib.request.urlopen('https://v6.ident.me').read().decode('utf8')
|
ipv6 = urllib.request.urlopen("https://v6.ident.me").read().decode("utf8")
|
||||||
ipv6 = None
|
ipv6 = str(ip_network(ipv6 + "/64", strict=False).network_address) # xfinity gives /64
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
time.sleep(60)
|
time.sleep(60)
|
||||||
@ -22,17 +23,20 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
if ipv4 != old_ipv4 or ipv6 != old_ipv6:
|
if ipv4 != old_ipv4 or ipv6 != old_ipv6:
|
||||||
msg = Mail(
|
msg = Mail(
|
||||||
from_email='mtran319@gmail.com',
|
from_email="mtran319@gmail.com",
|
||||||
to_emails='mtran319@gmail.com',
|
to_emails="mtran319@gmail.com",
|
||||||
subject='pls update ip',
|
subject="pls update ip",
|
||||||
html_content=f'<p>ipv4: {ipv4}</p><p>ipv6: {ipv6}</p>'
|
html_content=f"<p>old ipv4: {old_ipv4}</p>"
|
||||||
|
f"<p>old ipv6: {old_ipv6}</p>"
|
||||||
|
f"<p>new ipv4: {ipv4}</p>"
|
||||||
|
f"<p>new ipv6: {ipv6}</p>"
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
print(f'IP changed to {ipv4} and {ipv6}')
|
print(f"IP changed to {ipv4} and {ipv6}")
|
||||||
resp = sg.send(msg)
|
resp = sg.send(msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
sg = SendGridAPIClient(Path('sendgrid.key').read_text())
|
sg = SendGridAPIClient(Path("sendgrid.key").read_text())
|
||||||
time.sleep(60)
|
time.sleep(60)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -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 < <conf>
|
|
||||||
|
|
||||||
# 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'])
|
|
Loading…
x
Reference in New Issue
Block a user