mirror of
https://github.com/dragonlock2/matthewtran.com.git
synced 2025-10-11 20:17:54 +00:00
further cleanup and securing
This commit is contained in:
parent
be0530cafe
commit
dd072e76d8
15
README.md
15
README.md
@ -15,23 +15,24 @@ Stuff that's deployed on [matthewtran.com](https://matthewtran.com). Currently r
|
|||||||
|
|
||||||
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.
|
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`.
|
- You may need to manually enable IPv6 on the network connection. Use `Automatic` not `Automatic, DHCP only`.
|
||||||
|
- Add an SSH key if you need remote access, setup will disable password authentication.
|
||||||
- Clone this repo and `cd` into it.
|
- Clone this repo and `cd` into it.
|
||||||
2. Set up the server.
|
2. Set up the server.
|
||||||
- `scripts/setup_server.py`
|
- `scripts/setup_server.py`
|
||||||
3. Set up the OpenWrt 24.10 router. Copy SSH keys first to make it easier.
|
3. Set up the OpenWrt 24.10 router. Copy SSH keys first to make it easier. Use a strong root password.
|
||||||
- `scripts/setup_router.py`
|
- `scripts/setup_router.py <interface>`
|
||||||
- For each WireGuard client, run `scripts/setup_peer.py <name>`.
|
4. Reboot the router and server.
|
||||||
- Reboot the router and server.
|
5. Configure, build, and start services.
|
||||||
4. Configure, build, and start services.
|
|
||||||
- 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.
|
||||||
- `scripts/setup_repo.py`
|
- `scripts/setup_repo.py`
|
||||||
- `docker compose build`
|
- `docker compose build`
|
||||||
- `docker compose up -d`
|
- `docker compose up -d`
|
||||||
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.
|
6. 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 <drive> <mount path>`
|
- `scripts/setup_drive.py <drive> <mount path>`
|
||||||
6. Optionally, add the following DNS entries at the registrar.
|
7. Optionally, run `scripts/setup_peer.py <name>` for each WireGuard client.
|
||||||
|
8. Optionally, add the following DNS entries at the registrar.
|
||||||
| hosts | type | data |
|
| hosts | type | data |
|
||||||
| ----------------------- | ------ | ------------------------------------- |
|
| ----------------------- | ------ | ------------------------------------- |
|
||||||
| `@`, `git`, `wg`, `www` | `A` | `<public IPv4>` |
|
| `@`, `git`, `wg`, `www` | `A` | `<public IPv4>` |
|
||||||
|
48
compose.yml
48
compose.yml
@ -1,9 +1,22 @@
|
|||||||
networks:
|
networks:
|
||||||
default6:
|
web:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
ipam:
|
ipam:
|
||||||
config:
|
config:
|
||||||
- subnet: fd3a:138e:8fd0:0001::/64
|
- subnet: "172.20.0.0/16"
|
||||||
|
- subnet: "fd3a:138e:8fd0:0020::/64"
|
||||||
|
monero:
|
||||||
|
enable_ipv6: true
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: "172.21.0.0/16"
|
||||||
|
- subnet: "fd3a:138e:8fd0:0021::/64"
|
||||||
|
game:
|
||||||
|
enable_ipv6: true
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: "172.22.0.0/16"
|
||||||
|
- subnet: "fd3a:138e:8fd0:0022::/64"
|
||||||
services:
|
services:
|
||||||
website:
|
website:
|
||||||
restart: always
|
restart: always
|
||||||
@ -13,21 +26,27 @@ services:
|
|||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
networks:
|
networks:
|
||||||
- default6
|
- web
|
||||||
volumes:
|
volumes:
|
||||||
- ./website/letsencrypt:/etc/letsencrypt
|
- ./website/letsencrypt:/etc/letsencrypt
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
gitea:
|
gitea:
|
||||||
restart: always
|
restart: always
|
||||||
image: gitea/gitea:latest-rootless
|
image: gitea/gitea:latest-rootless
|
||||||
ports:
|
ports:
|
||||||
- "2222:2222"
|
- "2222:2222"
|
||||||
networks:
|
networks:
|
||||||
- default6
|
- web
|
||||||
volumes:
|
volumes:
|
||||||
- ./website/gitea/data:/var/lib/gitea
|
- ./website/gitea/data:/var/lib/gitea
|
||||||
- ./website/gitea/config:/etc/gitea
|
- ./website/gitea/config:/etc/gitea
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
monerod:
|
monerod:
|
||||||
restart: always
|
restart: always
|
||||||
build: monerod/.
|
build: monerod/.
|
||||||
@ -38,9 +57,11 @@ services:
|
|||||||
- "18080:18080"
|
- "18080:18080"
|
||||||
- "18081:18081"
|
- "18081:18081"
|
||||||
networks:
|
networks:
|
||||||
- default6
|
- monero
|
||||||
volumes:
|
volumes:
|
||||||
- ./monerod/.bitmonero:/home/ubuntu/.bitmonero
|
- ./monerod/.bitmonero:/home/ubuntu/.bitmonero
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
p2pool:
|
p2pool:
|
||||||
stop_grace_period: 1m # TODO reduce m_shutdownCountdown to reduce this
|
stop_grace_period: 1m # TODO reduce m_shutdownCountdown to reduce this
|
||||||
restart: always
|
restart: always
|
||||||
@ -53,10 +74,11 @@ services:
|
|||||||
- "37888:37888"
|
- "37888:37888"
|
||||||
- "37889:37889"
|
- "37889:37889"
|
||||||
networks:
|
networks:
|
||||||
- default6
|
- monero
|
||||||
volumes:
|
volumes:
|
||||||
- ./p2pool/cache:/home/ubuntu/cache
|
- ./p2pool/cache:/home/ubuntu/cache
|
||||||
- /dev/hugepages:/dev/hugepages
|
cap_drop:
|
||||||
|
- ALL
|
||||||
minecraft:
|
minecraft:
|
||||||
restart: always
|
restart: always
|
||||||
build: minecraft/.
|
build: minecraft/.
|
||||||
@ -64,9 +86,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "25565:25565"
|
- "25565:25565"
|
||||||
networks:
|
networks:
|
||||||
- default6
|
- game
|
||||||
volumes:
|
volumes:
|
||||||
- ./minecraft/worlds:/home/ubuntu/worlds
|
- ./minecraft/worlds:/home/ubuntu/worlds
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
# minecraft_bedrock:
|
# minecraft_bedrock:
|
||||||
# restart: always
|
# restart: always
|
||||||
# build: minecraft_bedrock/.
|
# build: minecraft_bedrock/.
|
||||||
@ -75,9 +99,11 @@ services:
|
|||||||
# - "19132:19132/udp"
|
# - "19132:19132/udp"
|
||||||
# - "19133:19133/udp"
|
# - "19133:19133/udp"
|
||||||
# networks:
|
# networks:
|
||||||
# - default6
|
# - game
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./minecraft_bedrock/worlds:/home/ubuntu/worlds
|
# - ./minecraft_bedrock/worlds:/home/ubuntu/worlds
|
||||||
|
# cap_drop:
|
||||||
|
# - ALL
|
||||||
# terraria:
|
# terraria:
|
||||||
# restart: always
|
# restart: always
|
||||||
# build: terraria/.
|
# build: terraria/.
|
||||||
@ -85,6 +111,8 @@ services:
|
|||||||
# ports:
|
# ports:
|
||||||
# - "7777:7777"
|
# - "7777:7777"
|
||||||
# networks:
|
# networks:
|
||||||
# - default6
|
# - game
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./terraria/worlds:/home/ubuntu/worlds
|
# - ./terraria/worlds:/home/ubuntu/worlds
|
||||||
|
# cap_drop:
|
||||||
|
# - ALL
|
||||||
|
@ -2,19 +2,22 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
ETH = "enp5s0"
|
DP_LEN = 64 # xfinity delegated prefix length
|
||||||
IPV4 = "192.168.1.69"
|
WRT_ULA = "fd16:8f4d:f516::" # OpenWrt random
|
||||||
|
WG_ULA = "fd32:76a6:ec61:577a::" # WireGuard random
|
||||||
|
IPV4 = "192.168.1.69" # OpenWrt default
|
||||||
IPV6 = "69"
|
IPV6 = "69"
|
||||||
WG_IPV4 = "192.168.2.1"
|
WG_IPV4 = "192.168.2.1" # WireGuard chosen
|
||||||
WG_IPV6 = "fd32:76a6:ec61:577a::1"
|
WG_IPV6 = WG_ULA + "1"
|
||||||
|
|
||||||
def run(cmds):
|
def run(cmds):
|
||||||
# ssh-keygen -t ed25519
|
# ssh-keygen -t ed25519
|
||||||
subprocess.run(["ssh", "root@OpenWrt.lan", ";".join(cmds)], check=True)
|
subprocess.run(["ssh", "root@OpenWrt.lan", ";".join(cmds)], check=True)
|
||||||
|
|
||||||
def mac():
|
def mac(eth):
|
||||||
return open(f"/sys/class/net/{ETH}/address", "r").read().strip()
|
return open(f"/sys/class/net/{eth}/address", "r").read().strip()
|
||||||
|
|
||||||
def duid():
|
def duid():
|
||||||
# adapted from https://github.com/mss/nm-duid
|
# adapted from https://github.com/mss/nm-duid
|
||||||
@ -27,16 +30,21 @@ def key():
|
|||||||
return (pub.decode("utf-8"), priv.decode("utf-8"))
|
return (pub.decode("utf-8"), priv.decode("utf-8"))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# prevent access using WAN addresses
|
ETH = sys.argv[1] # e.g. enp5s0
|
||||||
|
|
||||||
|
# basic setup
|
||||||
run([
|
run([
|
||||||
|
f"uci set network.globals.ula_prefix='{WRT_ULA}/48'"
|
||||||
"uci set dropbear.main.Interface='lan'",
|
"uci set dropbear.main.Interface='lan'",
|
||||||
|
"uci commit network",
|
||||||
|
"uci commit dropbear",
|
||||||
])
|
])
|
||||||
|
|
||||||
# static IP
|
# static IP
|
||||||
run([
|
run([
|
||||||
"uci add dhcp host",
|
"uci add dhcp host",
|
||||||
"uci set dhcp.@host[-1].name='matt-ryzen'",
|
"uci set dhcp.@host[-1].name='matt-ryzen'",
|
||||||
f"uci set dhcp.@host[-1].mac='{mac()}'",
|
f"uci set dhcp.@host[-1].mac='{mac(ETH)}'",
|
||||||
f"uci set dhcp.@host[-1].ip='{IPV4}'",
|
f"uci set dhcp.@host[-1].ip='{IPV4}'",
|
||||||
f"uci set dhcp.@host[-1].duid='{duid()}'",
|
f"uci set dhcp.@host[-1].duid='{duid()}'",
|
||||||
f"uci set dhcp.@host[-1].hostid='{IPV6}'",
|
f"uci set dhcp.@host[-1].hostid='{IPV6}'",
|
||||||
@ -75,7 +83,7 @@ if __name__ == "__main__":
|
|||||||
f"uci set firewall.@rule[-1].name='allow-{name}'",
|
f"uci set firewall.@rule[-1].name='allow-{name}'",
|
||||||
"uci set firewall.@rule[-1].src='wan'",
|
"uci set firewall.@rule[-1].src='wan'",
|
||||||
"uci set firewall.@rule[-1].dest='lan'",
|
"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_ip='::{IPV6}/{DP_LEN-128}'",
|
||||||
f"uci set firewall.@rule[-1].dest_port='{PORTS[name]}'",
|
f"uci set firewall.@rule[-1].dest_port='{PORTS[name]}'",
|
||||||
"uci set firewall.@rule[-1].target='ACCEPT'",
|
"uci set firewall.@rule[-1].target='ACCEPT'",
|
||||||
])
|
])
|
||||||
@ -85,6 +93,7 @@ if __name__ == "__main__":
|
|||||||
])
|
])
|
||||||
|
|
||||||
# wireguard setup
|
# wireguard setup
|
||||||
|
# TODO configure NAT66 to fix tunneling IPv6 traffic
|
||||||
pub, priv = key()
|
pub, priv = key()
|
||||||
run([
|
run([
|
||||||
# install packages
|
# install packages
|
||||||
|
@ -4,7 +4,6 @@ import json
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from setup_router import WG_IPV4, WG_IPV6
|
|
||||||
|
|
||||||
def run(cmd, capture=False):
|
def run(cmd, capture=False):
|
||||||
if capture:
|
if capture:
|
||||||
@ -15,30 +14,60 @@ def run(cmd, capture=False):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# install dependencies and configure
|
# install dependencies and configure
|
||||||
run("apt update")
|
run("apt update")
|
||||||
run("apt upgrade")
|
run("apt upgrade -y")
|
||||||
run("apt install -y avahi-daemon btrfs-progs python-is-python3 python3-pip wireguard zip")
|
run("apt install -y avahi-daemon btrfs-progs openssh-server python-is-python3 python3-pip wireguard zip")
|
||||||
if run("ufw status", capture=True) == b"Status: inactive\n":
|
with open("/etc/sysctl.conf", "a+") as f: # enable huge pages for local mining
|
||||||
run("ufw enable")
|
|
||||||
run("ufw allow OpenSSH")
|
|
||||||
with open("/etc/sysctl.conf", "a+") as f:
|
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
if "vm.nr_hugepages=3072\n" not in f.readlines():
|
if "vm.nr_hugepages=1280\n" not in f.readlines():
|
||||||
f.write("vm.nr_hugepages=3072\n") # enable huge pages
|
f.write("vm.nr_hugepages=1280\n")
|
||||||
|
file = Path("/etc/ssh/sshd_config.d/restrict.conf") # only allow public key login
|
||||||
|
if not file.exists():
|
||||||
|
with file.open("w") as f:
|
||||||
|
f.write("PasswordAuthentication no\n")
|
||||||
|
|
||||||
# install docker and configure
|
# install docker and configure
|
||||||
run("snap install docker")
|
run("snap install docker")
|
||||||
run("addgroup --system docker")
|
run("addgroup --system docker")
|
||||||
run(f"adduser {os.getlogin()} 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:
|
with open("/var/snap/docker/current/config/daemon.json", "r+") as f:
|
||||||
cfg = json.load(f)
|
cfg = json.load(f)
|
||||||
cfg["ipv6"] = True
|
cfg["ipv6"] = True
|
||||||
cfg["fixed-cidr-v6"] = "fd3a:138e:8fd0:0000::/64"
|
cfg["fixed-cidr-v6"] = "fd3a:138e:8fd0:0000::/64" # Docker ULA
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
json.dump(cfg, f, indent=4)
|
json.dump(cfg, f, indent=4)
|
||||||
run("systemctl restart snap.docker.dockerd.service")
|
run("systemctl restart snap.docker.dockerd.service")
|
||||||
|
|
||||||
|
# restrict network access from containers
|
||||||
|
file = Path("/etc/systemd/system/docker-restrict.service")
|
||||||
|
if not file.exists():
|
||||||
|
with file.open("w") as f:
|
||||||
|
f.writelines(s + "\n" for s in [
|
||||||
|
"[Unit]",
|
||||||
|
"Description=Restrict Docker network access",
|
||||||
|
"Before=network.target",
|
||||||
|
"After=network-pre.target",
|
||||||
|
"",
|
||||||
|
"[Service]",
|
||||||
|
"Type=oneshot",
|
||||||
|
"ExecStart=/opt/docker-restrict.sh",
|
||||||
|
"RemainAfterExit=yes",
|
||||||
|
"",
|
||||||
|
"[Install]",
|
||||||
|
"WantedBy=multi-user.target",
|
||||||
|
])
|
||||||
|
file = Path("/opt/docker-restrict.sh")
|
||||||
|
if not file.exists():
|
||||||
|
with file.open("w") as f:
|
||||||
|
f.writelines(s + "\n" for s in [
|
||||||
|
"#!/bin/sh",
|
||||||
|
"iptables -N DOCKER-USER || true",
|
||||||
|
"iptables -I DOCKER-USER -p tcp --dport 22 -j DROP", # SSH
|
||||||
|
"ip6tables -N DOCKER-USER || true",
|
||||||
|
"ip6tables -I DOCKER-USER -p tcp --dport 22 -j DROP", # SSH
|
||||||
|
])
|
||||||
|
file.chmod(0o755)
|
||||||
|
run("systemctl enable docker-restrict.service")
|
||||||
|
|
||||||
# TODO modify /etc/crypttab instead once Ubuntu fixed
|
# TODO modify /etc/crypttab instead once Ubuntu fixed
|
||||||
file = Path("/etc/systemd/system/luks.service")
|
file = Path("/etc/systemd/system/luks.service")
|
||||||
if not file.exists():
|
if not file.exists():
|
||||||
|
@ -11,6 +11,7 @@ RUN pip3 install sendgrid --break-system-packages
|
|||||||
RUN rm /etc/nginx/sites-enabled/default
|
RUN rm /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
# enable site
|
# enable site
|
||||||
|
# TODO make the website code not terrible ;-;
|
||||||
COPY matthewtran.com /etc/nginx/sites-available
|
COPY matthewtran.com /etc/nginx/sites-available
|
||||||
RUN ln -s /etc/nginx/sites-available/matthewtran.com /etc/nginx/sites-enabled/matthewtran.com
|
RUN ln -s /etc/nginx/sites-available/matthewtran.com /etc/nginx/sites-enabled/matthewtran.com
|
||||||
COPY html /var/www/matthewtran.com/html
|
COPY html /var/www/matthewtran.com/html
|
||||||
|
@ -26,11 +26,12 @@ if __name__ == "__main__":
|
|||||||
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>old ipv4: {old_ipv4}</p>"
|
html_content=(
|
||||||
|
f"<p>old ipv4: {old_ipv4}</p>"
|
||||||
f"<p>old ipv6: {old_ipv6}</p>"
|
f"<p>old ipv6: {old_ipv6}</p>"
|
||||||
f"<p>new ipv4: {ipv4}</p>"
|
f"<p>new ipv4: {ipv4}</p>"
|
||||||
f"<p>new ipv6: {ipv6}</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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user