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.
|
||||
- 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.
|
||||
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 <name>`.
|
||||
- Reboot the router and server.
|
||||
4. Configure, build, and start services.
|
||||
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 <interface>`
|
||||
4. Reboot the router and server.
|
||||
5. 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.
|
||||
- `scripts/setup_repo.py`
|
||||
- `docker compose build`
|
||||
- `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>`
|
||||
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 |
|
||||
| ----------------------- | ------ | ------------------------------------- |
|
||||
| `@`, `git`, `wg`, `www` | `A` | `<public IPv4>` |
|
||||
|
48
compose.yml
48
compose.yml
@ -1,9 +1,22 @@
|
||||
networks:
|
||||
default6:
|
||||
web:
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
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:
|
||||
website:
|
||||
restart: always
|
||||
@ -13,21 +26,27 @@ services:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
networks:
|
||||
- default6
|
||||
- web
|
||||
volumes:
|
||||
- ./website/letsencrypt:/etc/letsencrypt
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
gitea:
|
||||
restart: always
|
||||
image: gitea/gitea:latest-rootless
|
||||
ports:
|
||||
- "2222:2222"
|
||||
networks:
|
||||
- default6
|
||||
- web
|
||||
volumes:
|
||||
- ./website/gitea/data:/var/lib/gitea
|
||||
- ./website/gitea/config:/etc/gitea
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
cap_drop:
|
||||
- ALL
|
||||
monerod:
|
||||
restart: always
|
||||
build: monerod/.
|
||||
@ -38,9 +57,11 @@ services:
|
||||
- "18080:18080"
|
||||
- "18081:18081"
|
||||
networks:
|
||||
- default6
|
||||
- monero
|
||||
volumes:
|
||||
- ./monerod/.bitmonero:/home/ubuntu/.bitmonero
|
||||
cap_drop:
|
||||
- ALL
|
||||
p2pool:
|
||||
stop_grace_period: 1m # TODO reduce m_shutdownCountdown to reduce this
|
||||
restart: always
|
||||
@ -53,10 +74,11 @@ services:
|
||||
- "37888:37888"
|
||||
- "37889:37889"
|
||||
networks:
|
||||
- default6
|
||||
- monero
|
||||
volumes:
|
||||
- ./p2pool/cache:/home/ubuntu/cache
|
||||
- /dev/hugepages:/dev/hugepages
|
||||
cap_drop:
|
||||
- ALL
|
||||
minecraft:
|
||||
restart: always
|
||||
build: minecraft/.
|
||||
@ -64,9 +86,11 @@ services:
|
||||
ports:
|
||||
- "25565:25565"
|
||||
networks:
|
||||
- default6
|
||||
- game
|
||||
volumes:
|
||||
- ./minecraft/worlds:/home/ubuntu/worlds
|
||||
cap_drop:
|
||||
- ALL
|
||||
# minecraft_bedrock:
|
||||
# restart: always
|
||||
# build: minecraft_bedrock/.
|
||||
@ -75,9 +99,11 @@ services:
|
||||
# - "19132:19132/udp"
|
||||
# - "19133:19133/udp"
|
||||
# networks:
|
||||
# - default6
|
||||
# - game
|
||||
# volumes:
|
||||
# - ./minecraft_bedrock/worlds:/home/ubuntu/worlds
|
||||
# cap_drop:
|
||||
# - ALL
|
||||
# terraria:
|
||||
# restart: always
|
||||
# build: terraria/.
|
||||
@ -85,6 +111,8 @@ services:
|
||||
# ports:
|
||||
# - "7777:7777"
|
||||
# networks:
|
||||
# - default6
|
||||
# - game
|
||||
# volumes:
|
||||
# - ./terraria/worlds:/home/ubuntu/worlds
|
||||
# cap_drop:
|
||||
# - ALL
|
||||
|
@ -2,19 +2,22 @@
|
||||
|
||||
import hashlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ETH = "enp5s0"
|
||||
IPV4 = "192.168.1.69"
|
||||
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"
|
||||
WG_IPV6 = "fd32:76a6:ec61:577a::1"
|
||||
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():
|
||||
return open(f"/sys/class/net/{ETH}/address", "r").read().strip()
|
||||
def mac(eth):
|
||||
return open(f"/sys/class/net/{eth}/address", "r").read().strip()
|
||||
|
||||
def duid():
|
||||
# adapted from https://github.com/mss/nm-duid
|
||||
@ -27,16 +30,21 @@ def key():
|
||||
return (pub.decode("utf-8"), priv.decode("utf-8"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
# prevent access using WAN addresses
|
||||
ETH = sys.argv[1] # e.g. enp5s0
|
||||
|
||||
# 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",
|
||||
"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].duid='{duid()}'",
|
||||
f"uci set dhcp.@host[-1].hostid='{IPV6}'",
|
||||
@ -75,7 +83,7 @@ if __name__ == "__main__":
|
||||
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_ip='::{IPV6}/{DP_LEN-128}'",
|
||||
f"uci set firewall.@rule[-1].dest_port='{PORTS[name]}'",
|
||||
"uci set firewall.@rule[-1].target='ACCEPT'",
|
||||
])
|
||||
@ -85,6 +93,7 @@ if __name__ == "__main__":
|
||||
])
|
||||
|
||||
# wireguard setup
|
||||
# TODO configure NAT66 to fix tunneling IPv6 traffic
|
||||
pub, priv = key()
|
||||
run([
|
||||
# install packages
|
||||
|
@ -4,7 +4,6 @@ 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:
|
||||
@ -15,30 +14,60 @@ def run(cmd, capture=False):
|
||||
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:
|
||||
run("apt upgrade -y")
|
||||
run("apt install -y avahi-daemon btrfs-progs openssh-server python-is-python3 python3-pip wireguard zip")
|
||||
with open("/etc/sysctl.conf", "a+") as f: # enable huge pages for local mining
|
||||
f.seek(0)
|
||||
if "vm.nr_hugepages=3072\n" not in f.readlines():
|
||||
f.write("vm.nr_hugepages=3072\n") # enable huge pages
|
||||
if "vm.nr_hugepages=1280\n" not in f.readlines():
|
||||
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
|
||||
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"
|
||||
cfg["fixed-cidr-v6"] = "fd3a:138e:8fd0:0000::/64" # Docker ULA
|
||||
f.seek(0)
|
||||
json.dump(cfg, f, indent=4)
|
||||
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
|
||||
file = Path("/etc/systemd/system/luks.service")
|
||||
if not file.exists():
|
||||
|
@ -11,6 +11,7 @@ RUN pip3 install sendgrid --break-system-packages
|
||||
RUN rm /etc/nginx/sites-enabled/default
|
||||
|
||||
# enable site
|
||||
# TODO make the website code not terrible ;-;
|
||||
COPY matthewtran.com /etc/nginx/sites-available
|
||||
RUN ln -s /etc/nginx/sites-available/matthewtran.com /etc/nginx/sites-enabled/matthewtran.com
|
||||
COPY html /var/www/matthewtran.com/html
|
||||
|
@ -26,11 +26,12 @@ if __name__ == "__main__":
|
||||
from_email="mtran319@gmail.com",
|
||||
to_emails="mtran319@gmail.com",
|
||||
subject="pls update ip",
|
||||
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>"
|
||||
)
|
||||
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:
|
||||
print(f"IP changed to {ipv4} and {ipv6}")
|
||||
resp = sg.send(msg)
|
||||
|
Loading…
x
Reference in New Issue
Block a user