mirror of
https://github.com/dragonlock2/matthewtran.com.git
synced 2025-10-11 12:07:56 +00:00
migrate to coreos
This commit is contained in:
parent
12039fb862
commit
8bd3def755
32
.gitignore
vendored
32
.gitignore
vendored
@ -1,34 +1,26 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
__pycache__
|
__pycache__
|
||||||
compose.override.yml
|
*.bak
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# config
|
||||||
|
config/server.json
|
||||||
|
config/*.bu
|
||||||
|
config/*.ign
|
||||||
|
config/*.iso
|
||||||
|
|
||||||
# website
|
# website
|
||||||
website/gitea
|
|
||||||
website/certbot
|
|
||||||
website/sendgrid.key
|
website/sendgrid.key
|
||||||
|
|
||||||
# monerod
|
|
||||||
monerod/.bitmonero
|
|
||||||
|
|
||||||
# p2pool
|
|
||||||
p2pool/cache
|
|
||||||
|
|
||||||
# minecraft
|
# minecraft
|
||||||
minecraft/world*
|
minecraft/server.properties
|
||||||
|
|
||||||
# minecraft_bedrock
|
# minecraft_bedrock
|
||||||
minecraft_bedrock/worlds*
|
minecraft_bedrock/server.properties
|
||||||
|
|
||||||
# terraria
|
# terraria
|
||||||
terraria/worlds
|
terraria/*.txt
|
||||||
terraria/mods
|
|
||||||
terraria/config.txt
|
|
||||||
terraria/password.txt
|
|
||||||
|
|
||||||
# nas
|
# nas
|
||||||
nas/*.json
|
nas/Dockerfile
|
||||||
nas/smb.conf
|
nas/smb.conf
|
||||||
nas/users.sh
|
|
||||||
|
|
||||||
# backup
|
|
||||||
data.zip
|
|
||||||
|
44
README.md
44
README.md
@ -7,49 +7,31 @@ Services deployed on [matthewtran.com](https://matthewtran.com).
|
|||||||
- monerod
|
- monerod
|
||||||
- p2pool (`xmrig -o matthewtran.com`)
|
- p2pool (`xmrig -o matthewtran.com`)
|
||||||
- minecraft
|
- minecraft
|
||||||
- ~~minecraft bedrock~~
|
- minecraft bedrock
|
||||||
- terraria
|
- terraria
|
||||||
- nas (`<server>/<name>` on LAN)
|
- nas (`<server>/<name>` on LAN)
|
||||||
- wireguard
|
- wireguard
|
||||||
|
|
||||||
## setup
|
## setup
|
||||||
|
|
||||||
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. Create `config/server.json` and run `config/provision.py`.
|
||||||
- You may need to manually enable IPv6 on the network connection. Use `Automatic` not `Automatic, DHCP only`.
|
2. On the server to be provisioned, boot a [Fedora CoreOS installation media](https://fedoraproject.org/coreos/download?stream=stable) and run the install command.
|
||||||
- Add an SSH key if you need remote access, setup will disable password authentication.
|
3. To configure the OpenWrt router, run `/opt/router.py --provision <interface>` on the server. Then reboot the router and server.
|
||||||
- Clone this repo and `cd` into it.
|
4. Add the following DNS entries at the registrar.
|
||||||
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. 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/config.txt` and `terraria/password.txt` if needed.
|
|
||||||
- Create `nas/mounts.json` which contains a list of `"<name>":"<directory>"` for the SMB share.
|
|
||||||
- Create `nas/users.json` which contains a list of `"<user>":"<password>"` for the SMB share.
|
|
||||||
- `scripts/setup_repo.py`
|
|
||||||
- Restore backups if needed. Make sure to set correct ownership. For example, `chown -R 2000:2000 website/gitea`.
|
|
||||||
- `docker compose build`
|
|
||||||
- `docker compose up -d`
|
|
||||||
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>`
|
|
||||||
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>` |
|
||||||
| `@`, `git`, `www` | `AAAA` | `<delegated prefix>::69` |
|
| `@`, `git`, `www` | `AAAA` | `<delegated prefix>::69` |
|
||||||
| `wg` | `AAAA` | `<delegated prefix>::1` |
|
| `wg` | `AAAA` | `<delegated prefix>::1` |
|
||||||
|
5. Optionally, run `config/peer.py` for each WireGuard client.
|
||||||
|
|
||||||
## backup
|
## development
|
||||||
|
|
||||||
Run `scripts/backup.py` and save the resultant `data.zip` somewhere. Also run the following commands for BTRFS maintenance. I should probably automate this.
|
- For quick iteration, run `config/update.py`. This copies over sources, rebuilds images, and restarts containers.
|
||||||
```
|
- After development, it's best to reprovision (see above) with `wipe=false` for drives you want to keep. Then run `/opt/router.py` on the server and reboot.
|
||||||
btrfs device stats <mount>
|
|
||||||
btrfs scrub start -B <mount>
|
|
||||||
```
|
|
||||||
|
|
||||||
## security
|
## maintenance
|
||||||
|
|
||||||
To protect against vulnerabilities, all services run as non-root users inside containers that are on separate networks by function and have all capabilities dropped. These non-root users have a UID that doesn't exist on the host and a GID that maps to their function. Hopefully, even in the event of a full container compromise and root escalation, there is little damage an attacker can do. The main security hole left is containers accessing the LAN and host, AppArmor might help with this.
|
- Run `config/backup.py <name>.zip` to back up critical files.
|
||||||
|
- Run `config/backup.py --restore <name>.zip` to restore those files.
|
||||||
|
- Run `sudo mdadm -D /dev/md/<name>` on the server to check RAID status.
|
||||||
|
134
compose.yml
134
compose.yml
@ -1,134 +0,0 @@
|
|||||||
networks:
|
|
||||||
web:
|
|
||||||
enable_ipv6: true
|
|
||||||
ipam:
|
|
||||||
config:
|
|
||||||
- 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"
|
|
||||||
nas:
|
|
||||||
enable_ipv6: true
|
|
||||||
ipam:
|
|
||||||
config:
|
|
||||||
- subnet: "172.23.0.0/16"
|
|
||||||
- subnet: "fd3a:138e:8fd0:0023::/64"
|
|
||||||
services:
|
|
||||||
website:
|
|
||||||
restart: always
|
|
||||||
build: website/.
|
|
||||||
entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
ports:
|
|
||||||
- "80:8080"
|
|
||||||
- "443:8443"
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
volumes:
|
|
||||||
- ./website/certbot:/home/me/certbot
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
gitea:
|
|
||||||
restart: always
|
|
||||||
image: gitea/gitea:latest-rootless
|
|
||||||
user: "2000:2000"
|
|
||||||
ports:
|
|
||||||
- "2222:2222"
|
|
||||||
networks:
|
|
||||||
- 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/.
|
|
||||||
entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
ports:
|
|
||||||
- "18080:18080"
|
|
||||||
- "18081:18081"
|
|
||||||
networks:
|
|
||||||
- monero
|
|
||||||
volumes:
|
|
||||||
- ./monerod/.bitmonero:/home/me/.bitmonero
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
p2pool:
|
|
||||||
stop_grace_period: 1m # TODO reduce m_shutdownCountdown to reduce this
|
|
||||||
restart: always
|
|
||||||
build: p2pool/.
|
|
||||||
entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
ports:
|
|
||||||
- "3333:3333"
|
|
||||||
- "37888:37888"
|
|
||||||
- "37889:37889"
|
|
||||||
networks:
|
|
||||||
- monero
|
|
||||||
volumes:
|
|
||||||
- ./p2pool/cache:/home/me/cache
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
minecraft:
|
|
||||||
restart: always
|
|
||||||
build: minecraft/.
|
|
||||||
entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
ports:
|
|
||||||
- "25565:25565"
|
|
||||||
networks:
|
|
||||||
- game
|
|
||||||
volumes:
|
|
||||||
- ./minecraft/worlds:/home/me/worlds
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
# minecraft_bedrock:
|
|
||||||
# restart: always
|
|
||||||
# build: minecraft_bedrock/.
|
|
||||||
# entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
# ports:
|
|
||||||
# - "19132:19132/udp"
|
|
||||||
# - "19133:19133/udp"
|
|
||||||
# networks:
|
|
||||||
# - game
|
|
||||||
# volumes:
|
|
||||||
# - ./minecraft_bedrock/worlds:/home/me/worlds
|
|
||||||
# cap_drop:
|
|
||||||
# - ALL
|
|
||||||
terraria:
|
|
||||||
restart: always
|
|
||||||
build: terraria/.
|
|
||||||
entrypoint: ["/usr/bin/python3", "/home/me/entry.py"]
|
|
||||||
ports:
|
|
||||||
- "7777:7777"
|
|
||||||
networks:
|
|
||||||
- game
|
|
||||||
volumes:
|
|
||||||
- ./terraria/worlds:/home/me/worlds
|
|
||||||
- ./terraria/mods:/home/me/mods
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
||||||
nas:
|
|
||||||
restart: always
|
|
||||||
build: nas/.
|
|
||||||
entrypoint: ["/bin/sh", "/home/me/entry.sh"]
|
|
||||||
ports:
|
|
||||||
- "445:8445"
|
|
||||||
networks:
|
|
||||||
- nas
|
|
||||||
cap_drop:
|
|
||||||
- ALL
|
|
67
config/backup.py
Executable file
67
config/backup.py
Executable file
@ -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)
|
@ -3,7 +3,7 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from ipaddress import ip_address, ip_network
|
from ipaddress import ip_address, ip_network
|
||||||
from setup_router import WG_IPV4, WG_IPV6, run, key
|
from router import WG_IPV4, WG_IPV6, run, key
|
||||||
|
|
||||||
def ips():
|
def ips():
|
||||||
try:
|
try:
|
398
config/provision.py
Executable file
398
config/provision.py
Executable file
@ -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()
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
|
|
||||||
DP_LEN = 64 # xfinity delegated prefix length
|
DP_LEN = 64 # xfinity delegated prefix length
|
||||||
WRT_ULA = "fd16:8f4d:f516::" # OpenWrt random
|
WRT_ULA = "fd16:8f4d:f516::" # OpenWrt random
|
||||||
@ -30,7 +30,19 @@ 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__":
|
||||||
ETH = sys.argv[1] # e.g. enp5s0
|
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
|
# basic setup
|
||||||
run([
|
run([
|
||||||
@ -43,8 +55,7 @@ if __name__ == "__main__":
|
|||||||
# static IP
|
# static IP
|
||||||
run([
|
run([
|
||||||
"uci add dhcp host",
|
"uci add dhcp host",
|
||||||
"uci set dhcp.@host[-1].name='matt-ryzen'",
|
f"uci set dhcp.@host[-1].mac='{mac(args.provision)}'",
|
||||||
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}'",
|
44
config/server.example
Normal file
44
config/server.example
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
config/update.py
Executable file
100
config/update.py
Executable file
@ -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]),
|
||||||
|
])
|
1
gitea/Dockerfile
Normal file
1
gitea/Dockerfile
Normal file
@ -0,0 +1 @@
|
|||||||
|
FROM docker.io/gitea/gitea:1.23.7
|
@ -1 +0,0 @@
|
|||||||
worlds/
|
|
@ -3,18 +3,18 @@ FROM ubuntu:24.04
|
|||||||
RUN apt-get update && apt-get -y upgrade
|
RUN apt-get update && apt-get -y upgrade
|
||||||
RUN apt-get install -y wget openjdk-21-jre
|
RUN apt-get install -y wget openjdk-21-jre
|
||||||
|
|
||||||
RUN groupadd -g 2002 me && useradd -u 2002 -g 2002 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
|
|
||||||
# from https://github.com/itzg/rcon-cli
|
# from https://github.com/itzg/rcon-cli
|
||||||
RUN wget -O rcon-cli.tar.gz https://github.com/itzg/rcon-cli/releases/download/1.6.9/rcon-cli_1.6.9_linux_amd64.tar.gz
|
RUN wget -O rcon-cli.tar.gz https://github.com/itzg/rcon-cli/releases/download/1.7.0/rcon-cli_1.7.0_linux_amd64.tar.gz
|
||||||
RUN tar xvf rcon-cli.tar.gz && rm rcon-cli.tar.gz
|
RUN tar xvf rcon-cli.tar.gz && rm rcon-cli.tar.gz
|
||||||
|
|
||||||
# from https://www.minecraft.net/en-us/download/server (currently 1.21.4)
|
# from https://www.minecraft.net/en-us/download/server (currently 1.21.5)
|
||||||
RUN wget https://piston-data.mojang.com/v1/objects/4707d00eb834b446575d89a61a11b5d548d8c001/server.jar
|
RUN wget https://piston-data.mojang.com/v1/objects/e6ec2f64e6080b9b5d9b471b291c33cc7f509733/server.jar
|
||||||
|
|
||||||
COPY --chown=me:me eula.txt ./
|
COPY entry.sh ./
|
||||||
COPY --chown=me:me entry.sh ./
|
COPY eula.txt ./
|
||||||
COPY --chown=me:me server.properties ./
|
COPY ops.json ./
|
||||||
COPY --chown=me:me ops.json ./
|
COPY server.properties ./
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
@ -4,7 +4,7 @@ cleanup() {
|
|||||||
./rcon-cli --password password stop
|
./rcon-cli --password password stop
|
||||||
}
|
}
|
||||||
|
|
||||||
trap 'cleanup' TERM
|
trap 'cleanup' SIGTERM SIGINT
|
||||||
|
|
||||||
java -Xmx1024M -Xms1024M -jar server.jar nogui &
|
java -Xmx1024M -Xms1024M -jar server.jar nogui &
|
||||||
wait $! # wait for SIGTERM
|
wait $! # wait for SIGTERM
|
||||||
|
@ -20,7 +20,6 @@ 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=worlds/main
|
|
||||||
level-seed=
|
level-seed=
|
||||||
level-type=minecraft\:normal
|
level-type=minecraft\:normal
|
||||||
max-chained-neighbor-updates=1000000
|
max-chained-neighbor-updates=1000000
|
@ -1 +0,0 @@
|
|||||||
worlds/
|
|
@ -3,14 +3,15 @@ FROM ubuntu:24.04
|
|||||||
RUN apt-get update && apt-get -y upgrade
|
RUN apt-get update && apt-get -y upgrade
|
||||||
RUN apt-get install -y wget unzip curl tmux
|
RUN apt-get install -y wget unzip curl tmux
|
||||||
|
|
||||||
RUN groupadd -g 2002 me && useradd -u 2002 -g 2002 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
|
|
||||||
# from https://www.minecraft.net/en-us/download/server/bedrock (currently 1.21.61.01)
|
# from https://www.minecraft.net/en-us/download/server/bedrock (currently 1.21.73.01)
|
||||||
RUN wget -O server.zip --user-agent "Mozilla/5.0" https://www.minecraft.net/bedrockdedicatedserver/bin-linux/bedrock-server-1.21.61.01.zip
|
RUN wget -O server.zip --user-agent "Mozilla/5.0" https://www.minecraft.net/bedrockdedicatedserver/bin-linux/bedrock-server-1.21.73.01.zip
|
||||||
RUN unzip server.zip && rm server.zip
|
RUN unzip server.zip && rm server.zip
|
||||||
|
|
||||||
COPY --chown=me:me entry.sh ./
|
COPY entry.sh ./
|
||||||
COPY --chown=me:me server.properties ./
|
COPY permissions.json ./
|
||||||
COPY --chown=me:me permissions.json ./
|
COPY server.properties ./
|
||||||
|
|
||||||
|
RUN ln -s /data /root/worlds
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
@ -4,9 +4,8 @@ cleanup() {
|
|||||||
tmux send-keys stop Enter
|
tmux send-keys stop Enter
|
||||||
}
|
}
|
||||||
|
|
||||||
trap 'cleanup' TERM
|
trap 'cleanup' SIGTERM SIGINT
|
||||||
|
|
||||||
rm log
|
|
||||||
mkfifo log
|
mkfifo log
|
||||||
tmux new -d 'LD_LIBRARY_PATH=. ./bedrock_server > log'
|
tmux new -d 'LD_LIBRARY_PATH=. ./bedrock_server > log'
|
||||||
cat log &
|
cat log &
|
||||||
|
@ -13,7 +13,6 @@ view-distance=32
|
|||||||
tick-distance=4
|
tick-distance=4
|
||||||
player-idle-timeout=0
|
player-idle-timeout=0
|
||||||
max-threads=4
|
max-threads=4
|
||||||
level-name=test
|
|
||||||
level-seed=
|
level-seed=
|
||||||
default-player-permission-level=visitor
|
default-player-permission-level=visitor
|
||||||
texturepack-required=false
|
texturepack-required=false
|
@ -1 +0,0 @@
|
|||||||
.bitmonero
|
|
@ -3,12 +3,12 @@ FROM ubuntu:24.04
|
|||||||
RUN apt-get update && apt-get -y upgrade
|
RUN apt-get update && apt-get -y upgrade
|
||||||
RUN apt-get install -y wget bzip2
|
RUN apt-get install -y wget bzip2
|
||||||
|
|
||||||
RUN groupadd -g 2001 me && useradd -u 2001 -g 2001 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
|
|
||||||
RUN wget https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2 -O monerod.tar.bz2
|
RUN wget https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.4.0.tar.bz2 -O monerod.tar.bz2
|
||||||
RUN tar xvf monerod.tar.bz2 && rm monerod.tar.bz2
|
RUN tar xvf monerod.tar.bz2 && rm monerod.tar.bz2
|
||||||
RUN mv monero-x86_64-linux-gnu-v0.18.3.4 monero
|
RUN mv monero-x86_64-linux-gnu-v0.18.4.0 monero
|
||||||
|
|
||||||
COPY --chown=me:me entry.sh ./
|
COPY entry.sh ./
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
# check bitmonero.log for log
|
# check bitmonero.log for log
|
||||||
monero/monerod \
|
monero/monerod \
|
||||||
--prune-blockchain \
|
--prune-blockchain \
|
||||||
|
--data-dir /data \
|
||||||
--rpc-bind-port 18089 \
|
--rpc-bind-port 18089 \
|
||||||
--rpc-restricted-bind-ip 0.0.0.0 \
|
--rpc-restricted-bind-ip 0.0.0.0 \
|
||||||
--rpc-restricted-bind-port 18081 \
|
--rpc-restricted-bind-port 18081 \
|
||||||
@ -17,6 +18,6 @@ monero/monerod \
|
|||||||
cleanup() {
|
cleanup() {
|
||||||
monero/monerod exit --rpc-bind-port 18089
|
monero/monerod exit --rpc-bind-port 18089
|
||||||
}
|
}
|
||||||
trap 'cleanup' TERM
|
trap 'cleanup' SIGTERM SIGINT
|
||||||
tail -f /dev/null &
|
tail -f /dev/null &
|
||||||
wait $!
|
wait $!
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
ENV TZ=America/Los_Angeles
|
|
||||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y upgrade
|
|
||||||
RUN apt-get install -y samba
|
|
||||||
|
|
||||||
# create required files and user
|
|
||||||
RUN groupadd -g 2003 me && useradd -u 2003 -g 2003 -m me
|
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
RUN mkdir share samba samba/log samba/lock samba/state samba/cache samba/pid samba/private samba/ncalrpc
|
|
||||||
COPY --chown=me:me smb.conf entry.sh ./
|
|
||||||
|
|
||||||
# create additional users
|
|
||||||
USER root
|
|
||||||
COPY users.sh ./
|
|
||||||
RUN /bin/sh users.sh && rm users.sh
|
|
||||||
USER me
|
|
15
nas/Dockerfile.template
Normal file
15
nas/Dockerfile.template
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV TZ=America/Los_Angeles
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get -y upgrade
|
||||||
|
RUN apt-get install -y samba
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
|
||||||
|
COPY smb.conf entry.sh ./
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
|
||||||
|
# create users
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
smbd -s smb.conf -l=/home/me/samba/log
|
smbd -s smb.conf
|
||||||
trap 'echo "stopping smbd..."' TERM
|
trap 'echo "stopping smbd..."' SIGTERM SIGINT
|
||||||
tail -f /dev/null &
|
tail -f /dev/null &
|
||||||
wait $!
|
wait $!
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[global]
|
[global]
|
||||||
workgroup = WORKGROUP
|
workgroup = WORKGROUP
|
||||||
smb ports = 8445
|
smb ports = 445
|
||||||
load printers = no
|
load printers = no
|
||||||
disable spoolss = yes
|
disable spoolss = yes
|
||||||
|
|
||||||
@ -22,19 +22,12 @@ client smb3 signing algorithms = AES-128-GMAC AES-128-CMAC HMAC-SHA256
|
|||||||
client signing = required
|
client signing = required
|
||||||
client ipc signing = required
|
client ipc signing = required
|
||||||
|
|
||||||
lock directory = /home/me/samba/lock
|
|
||||||
state directory = /home/me/samba/state
|
|
||||||
cache directory = /home/me/samba/cache
|
|
||||||
pid directory = /home/me/samba/pid
|
|
||||||
private dir = /home/me/samba/private
|
|
||||||
ncalrpc dir = /home/me/samba/ncalrpc
|
|
||||||
|
|
||||||
browseable = yes
|
browseable = yes
|
||||||
writable = yes
|
writable = yes
|
||||||
create mask = 0660
|
create mask = 0660
|
||||||
directory mask = 0770
|
directory mask = 0770
|
||||||
force user = me
|
force user = root
|
||||||
force group = me
|
force group = root
|
||||||
|
|
||||||
vfs objects = fruit streams_xattr
|
vfs objects = fruit streams_xattr
|
||||||
fruit:metadata = stream
|
fruit:metadata = stream
|
@ -1 +0,0 @@
|
|||||||
cache
|
|
@ -3,13 +3,13 @@ FROM ubuntu:24.04
|
|||||||
RUN apt-get update && apt-get -y upgrade
|
RUN apt-get update && apt-get -y upgrade
|
||||||
RUN apt-get install -y wget
|
RUN apt-get install -y wget
|
||||||
|
|
||||||
RUN groupadd -g 2001 me && useradd -u 2001 -g 2001 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
|
|
||||||
# currently v4.4
|
# currently v4.5
|
||||||
RUN wget https://github.com/SChernykh/p2pool/releases/download/v4.4/p2pool-v4.4-linux-x64.tar.gz -O p2pool.tar.gz
|
RUN wget https://github.com/SChernykh/p2pool/releases/download/v4.5/p2pool-v4.5-linux-x64.tar.gz -O p2pool.tar.gz
|
||||||
RUN tar xvf p2pool.tar.gz && rm p2pool.tar.gz
|
RUN tar xvf p2pool.tar.gz && rm p2pool.tar.gz
|
||||||
RUN mv p2pool-v4.4-linux-x64/p2pool ./p2pool
|
RUN mv p2pool-v4.5-linux-x64/p2pool ./p2pool
|
||||||
|
|
||||||
COPY --chown=me:me entry.sh ./
|
COPY entry.sh ./
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
cd cache
|
cd /data
|
||||||
exec ~/p2pool \
|
exec ~/p2pool \
|
||||||
--mini \
|
--mini \
|
||||||
--host monerod \
|
--host 127.0.0.1 \
|
||||||
--wallet 42j7pyNRf8WE96D1xb6pjPWCwaDaYYevwZSPpELbTJjnXiKp7Lhtahbhb5Gc3p2BVxgMB3FEGNPUcbST1oZds6nBERA4jrQ
|
--wallet 42j7pyNRf8WE96D1xb6pjPWCwaDaYYevwZSPpELbTJjnXiKp7Lhtahbhb5Gc3p2BVxgMB3FEGNPUcbST1oZds6nBERA4jrQ
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/sudo /usr/bin/python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
out = "data.zip"
|
|
||||||
subprocess.run(["zip", "-FS", "-r", out,
|
|
||||||
"minecraft/worlds",
|
|
||||||
"minecraft_bedrock/worlds",
|
|
||||||
"terraria/worlds",
|
|
||||||
"terraria/mods",
|
|
||||||
"website/gitea",
|
|
||||||
], check=True)
|
|
||||||
shutil.chown(out, os.getlogin(), os.getlogin())
|
|
@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/sudo /usr/bin/python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
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}")
|
|
||||||
shutil.chown(mount, os.getlogin(), "nas")
|
|
||||||
mount.chmod(0o770)
|
|
||||||
|
|
||||||
# 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")
|
|
@ -1,95 +0,0 @@
|
|||||||
#!/usr/bin/sudo /usr/bin/python3
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import yaml
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
override = {}
|
|
||||||
|
|
||||||
# create folders so containers have access
|
|
||||||
PATHS = {
|
|
||||||
"web": [
|
|
||||||
"website/certbot",
|
|
||||||
"website/gitea/config",
|
|
||||||
"website/gitea/data",
|
|
||||||
],
|
|
||||||
"monero": [
|
|
||||||
"monerod/.bitmonero",
|
|
||||||
"p2pool/cache",
|
|
||||||
],
|
|
||||||
"game": [
|
|
||||||
"minecraft/worlds",
|
|
||||||
"minecraft_bedrock/worlds",
|
|
||||||
"terraria/worlds",
|
|
||||||
"terraria/mods",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
for group in PATHS:
|
|
||||||
for p in PATHS[group]:
|
|
||||||
Path(p).mkdir(parents=True, exist_ok=True)
|
|
||||||
Path(p).chmod(0o775)
|
|
||||||
shutil.chown(p, user=os.getlogin(), group=group)
|
|
||||||
|
|
||||||
# add users to nas
|
|
||||||
file = Path("nas/users.json")
|
|
||||||
script = Path("nas/users.sh")
|
|
||||||
with script.open("w") as f:
|
|
||||||
if file.exists():
|
|
||||||
users = json.load(file.open())
|
|
||||||
for id, user in enumerate(users):
|
|
||||||
id = 3000 + id
|
|
||||||
f.writelines(s + "\n" for s in [
|
|
||||||
f"groupadd -g {id} {user}",
|
|
||||||
f"useradd -M -s /bin/false -u {id} -g {id} {user}",
|
|
||||||
f"su - me -c 'echo \"{users[user]}\\n{users[user]}\\n\" | pdbedit -s smb.conf -a {user}'",
|
|
||||||
])
|
|
||||||
shutil.chown(script, user=os.getlogin(), group=os.getlogin())
|
|
||||||
|
|
||||||
# add volumes to nas
|
|
||||||
file = Path("nas/mounts.json")
|
|
||||||
serv = Path("/etc/avahi/services")
|
|
||||||
conf = Path("nas/smb.conf")
|
|
||||||
shutil.copyfile("nas/base.conf", conf)
|
|
||||||
shutil.chown(conf, user=os.getlogin(), group=os.getlogin())
|
|
||||||
for f in serv.glob("nas-*.service"):
|
|
||||||
f.unlink()
|
|
||||||
if file.exists():
|
|
||||||
mounts = json.load(file.open())
|
|
||||||
with open("nas/smb.conf", "a") as f:
|
|
||||||
for m in mounts:
|
|
||||||
f.write(f"[{m}]\n")
|
|
||||||
f.write(f"path = /home/me/share/{m}\n")
|
|
||||||
f.write("\n")
|
|
||||||
override.setdefault("services", {})["nas"] = {"volumes": [f"{mounts[m]}:/home/me/share/{m}" for m in mounts]}
|
|
||||||
for m in mounts:
|
|
||||||
with (serv / f"nas-{m}.service").open("w") as f:
|
|
||||||
f.writelines(s + "\n" for s in [
|
|
||||||
"<?xml version=\"1.0\" standalone='no'?>",
|
|
||||||
"<!DOCTYPE service-group SYSTEM \"avahi-service.dtd\">",
|
|
||||||
"<service-group>",
|
|
||||||
f" <name replace-wildcards=\"yes\">%h - {m}</name>",
|
|
||||||
" <service>",
|
|
||||||
" <type>_smb._tcp</type>",
|
|
||||||
" <port>445</port>",
|
|
||||||
" </service>",
|
|
||||||
" <service>",
|
|
||||||
" <type>_adisk._tcp</type>",
|
|
||||||
f" <txt-record>dk0=adVN={m},adVF=0x82</txt-record>",
|
|
||||||
" <txt-record>sys=waMa=0,adVF=0x100</txt-record>",
|
|
||||||
" </service>",
|
|
||||||
"</service-group>",
|
|
||||||
])
|
|
||||||
subprocess.run(["systemctl", "restart", "avahi-daemon"], check=True)
|
|
||||||
|
|
||||||
# generate compose override
|
|
||||||
file = Path("compose.override.yml")
|
|
||||||
if override:
|
|
||||||
with file.open("w") as f:
|
|
||||||
yaml.dump(override, f)
|
|
||||||
shutil.chown(file, user=os.getlogin(), group=os.getlogin())
|
|
||||||
else:
|
|
||||||
file.unlink(True)
|
|
@ -1,107 +0,0 @@
|
|||||||
#!/usr/bin/sudo /usr/bin/python3
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
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 -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=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")
|
|
||||||
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" # Docker ULA
|
|
||||||
f.seek(0)
|
|
||||||
json.dump(cfg, f, indent=4)
|
|
||||||
run("systemctl restart snap.docker.dockerd.service")
|
|
||||||
|
|
||||||
try:
|
|
||||||
run("addgroup --gid 2000 web")
|
|
||||||
run("addgroup --gid 2001 monero")
|
|
||||||
run("addgroup --gid 2002 game")
|
|
||||||
run("addgroup --gid 2003 nas")
|
|
||||||
run(f"adduser {os.getlogin()} web")
|
|
||||||
run(f"adduser {os.getlogin()} monero")
|
|
||||||
run(f"adduser {os.getlogin()} game")
|
|
||||||
run(f"adduser {os.getlogin()} nas")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 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 -d 10.0.0.0/8 -j DROP", # xfinity gateway
|
|
||||||
"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():
|
|
||||||
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")
|
|
@ -1 +0,0 @@
|
|||||||
worlds/
|
|
@ -3,21 +3,21 @@ FROM ubuntu:24.04
|
|||||||
RUN apt-get update && apt-get -y upgrade
|
RUN apt-get update && apt-get -y upgrade
|
||||||
RUN apt-get install -y wget unzip python3 iproute2 dotnet-runtime-8.0
|
RUN apt-get install -y wget unzip python3 iproute2 dotnet-runtime-8.0
|
||||||
|
|
||||||
RUN groupadd -g 2002 me && useradd -u 2002 -g 2002 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
|
|
||||||
# from https://github.com/tModLoader/tModLoader/releases (currently v2025.02.3.2)
|
# from https://github.com/tModLoader/tModLoader/releases (currently v2025.03.3.1)
|
||||||
RUN wget https://github.com/tModLoader/tModLoader/releases/download/v2025.02.3.2/tModLoader.zip
|
RUN wget https://github.com/tModLoader/tModLoader/releases/download/v2025.03.3.1/tModLoader.zip
|
||||||
RUN unzip tModLoader.zip -d server && rm tModLoader.zip
|
RUN unzip tModLoader.zip -d server && rm tModLoader.zip
|
||||||
|
|
||||||
RUN chmod +x server/start-tModLoaderServer.sh
|
RUN chmod +x server/start-tModLoaderServer.sh
|
||||||
RUN mkdir server/tModLoader-Logs && touch server/tModLoader-Logs/server.log
|
RUN mkdir server/tModLoader-Logs && touch server/tModLoader-Logs/server.log
|
||||||
RUN echo "" > server/LaunchUtils/InstallDotNet.sh
|
RUN echo "" > server/LaunchUtils/InstallDotNet.sh
|
||||||
COPY --chown=me:me entry.py ./
|
|
||||||
COPY --chown=me:me config.default ./config.txt
|
COPY config.txt ./
|
||||||
COPY --chown=me:me password.default ./password.txt
|
COPY entry.py ./
|
||||||
COPY --chown=me:me config.tx[t] password.tx[t] ./
|
COPY password.txt ./
|
||||||
|
|
||||||
|
CMD ["/usr/bin/python3", "/root/entry.py"]
|
||||||
|
|
||||||
# To add mods, install them on the client and copy over the .tmod files to mods/
|
# To add mods, install them on the client and copy over the .tmod files to mods/
|
||||||
# Then modify/create mods/enabled.json and add the desired mods to enable
|
# Then modify/create mods/enabled.json and add the desired mods to enable
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
# world file
|
|
||||||
world=/home/me/worlds/master.wld
|
|
||||||
|
|
||||||
# default options if no world
|
# default options if no world
|
||||||
autocreate=3
|
|
||||||
worldname=poopy
|
worldname=poopy
|
||||||
difficulty=2
|
|
||||||
|
|
||||||
# server options
|
# server options
|
||||||
motd=poopy
|
motd=poopy
|
||||||
worldpath=/home/me/worlds
|
worldpath=/data/worlds
|
||||||
secure=1
|
secure=1
|
||||||
|
|
||||||
# tmodloader options
|
# tmodloader options
|
||||||
modpath=/home/me/mods
|
modpath=/data/mods
|
||||||
|
|
||||||
|
# generated options
|
||||||
|
@ -27,13 +27,13 @@ class Runner:
|
|||||||
logging.info(f"attempted connection from {addr}, starting server...")
|
logging.info(f"attempted connection from {addr}, starting server...")
|
||||||
|
|
||||||
# start server
|
# start server
|
||||||
with open("/home/me/password.txt", "r") as f:
|
with open("/root/password.txt", "r") as f:
|
||||||
password = f.read()
|
password = f.read()
|
||||||
self.server = subprocess.Popen([
|
self.server = subprocess.Popen([
|
||||||
"/bin/bash",
|
"/bin/bash",
|
||||||
"/home/me/server/start-tModLoaderServer.sh",
|
"/root/server/start-tModLoaderServer.sh",
|
||||||
"-nosteam",
|
"-nosteam",
|
||||||
"-config", "/home/me/config.txt",
|
"-config", "/root/config.txt",
|
||||||
"-pass", f"{password}",
|
"-pass", f"{password}",
|
||||||
], stdin=subprocess.PIPE, start_new_session=True)
|
], stdin=subprocess.PIPE, start_new_session=True)
|
||||||
while not self.started():
|
while not self.started():
|
||||||
|
@ -1 +0,0 @@
|
|||||||
password
|
|
@ -1,2 +0,0 @@
|
|||||||
gitea
|
|
||||||
certbot
|
|
@ -8,12 +8,11 @@ RUN apt-get update && apt-get -y upgrade
|
|||||||
RUN apt-get install -y nginx certbot python3-pip
|
RUN apt-get install -y nginx certbot python3-pip
|
||||||
RUN pip3 install sendgrid --break-system-packages
|
RUN pip3 install sendgrid --break-system-packages
|
||||||
|
|
||||||
RUN groupadd -g 2000 me && useradd -u 2000 -g 2000 -m me
|
WORKDIR /root
|
||||||
USER me
|
|
||||||
WORKDIR /home/me
|
|
||||||
RUN mkdir nginx certbot
|
|
||||||
|
|
||||||
# TODO make the website code not terrible ;-;
|
# TODO make the website code not terrible ;-;
|
||||||
COPY --chown=me:me html ./html
|
COPY html /var/www/html
|
||||||
COPY --chown=me:me sendgrid.ke[y] ip_update.py ./
|
COPY sendgrid.key ip.py ./
|
||||||
COPY --chown=me:me server.conf entry.sh ./
|
COPY server.conf entry.sh ./
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "/root/entry.sh"]
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
# get certs if needed
|
# get certs if needed
|
||||||
certbot certonly --standalone \
|
certbot certonly --standalone \
|
||||||
--http-01-port 8080 \
|
--config-dir /data \
|
||||||
--config-dir ~/certbot \
|
--work-dir /data/work \
|
||||||
--work-dir ~/certbot/work \
|
--logs-dir /data/logs \
|
||||||
--logs-dir ~/certbot/logs \
|
|
||||||
--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 \
|
||||||
@ -14,16 +13,16 @@ certbot certonly --standalone \
|
|||||||
# background process to renew certs and check ip changes
|
# background process to renew certs and check ip changes
|
||||||
update() {
|
update() {
|
||||||
certbot renew --quiet \
|
certbot renew --quiet \
|
||||||
--config-dir ~/certbot \
|
--config-dir /data \
|
||||||
--work-dir ~/certbot/work \
|
--work-dir /data/work \
|
||||||
--logs-dir ~/certbot/logs
|
--logs-dir /data/logs
|
||||||
sleep 86400
|
sleep 86400
|
||||||
}
|
}
|
||||||
update &
|
update &
|
||||||
./ip_update.py &
|
python3 ip.py &
|
||||||
|
|
||||||
# run server
|
# run server
|
||||||
nginx -c ~/server.conf
|
nginx -c ~/server.conf
|
||||||
trap 'echo "stopping website..."' TERM
|
trap 'echo "stopping website..."' SIGTERM SIGINT
|
||||||
tail -f /dev/null &
|
tail -f /dev/null &
|
||||||
wait $!
|
wait $!
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# adapted from /etc/nginx/nginx.conf
|
# adapted from /etc/nginx/nginx.conf
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
pid /home/me/nginx/site.pid;
|
|
||||||
error_log /dev/stderr;
|
error_log /dev/stderr;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
@ -15,42 +14,37 @@ http {
|
|||||||
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
ssl_certificate /home/me/certbot/live/matthewtran.com/fullchain.pem;
|
ssl_certificate /data/live/matthewtran.com/fullchain.pem;
|
||||||
ssl_certificate_key /home/me/certbot/live/matthewtran.com/privkey.pem;
|
ssl_certificate_key /data/live/matthewtran.com/privkey.pem;
|
||||||
|
|
||||||
include /etc/nginx/mime.types;
|
include /etc/nginx/mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
|
||||||
access_log /dev/stdout;
|
access_log /dev/stdout;
|
||||||
client_body_temp_path /home/me/nginx/body;
|
|
||||||
proxy_temp_path /home/me/nginx/proxy;
|
|
||||||
fastcgi_temp_path /home/me/nginx/fastcgi;
|
|
||||||
uwsgi_temp_path /home/me/nginx/uwsgi;
|
|
||||||
scgi_temp_path /home/me/nginx/scgi;
|
|
||||||
|
|
||||||
# SSL redirect
|
# SSL redirect
|
||||||
server {
|
server {
|
||||||
listen 8080 default_server;
|
listen 80 default_server;
|
||||||
listen [::]:8080 default_server;
|
listen [::]:80 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://$host$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
# default
|
# default
|
||||||
server {
|
server {
|
||||||
listen 8443 ssl default_server;
|
listen 443 ssl default_server;
|
||||||
listen [::]:8443 ssl default_server;
|
listen [::]:443 ssl default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
return 404;
|
return 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
# website
|
# website
|
||||||
server {
|
server {
|
||||||
listen 8443 ssl;
|
listen 443 ssl;
|
||||||
listen [::]:8443 ssl;
|
listen [::]:443 ssl;
|
||||||
server_name matthewtran.com www.matthewtran.com;
|
server_name matthewtran.com www.matthewtran.com;
|
||||||
|
|
||||||
root /home/me/html;
|
root /var/www/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
@ -59,13 +53,13 @@ http {
|
|||||||
|
|
||||||
# gitea
|
# gitea
|
||||||
server {
|
server {
|
||||||
listen 8443 ssl;
|
listen 443 ssl;
|
||||||
listen [::]:8443 ssl;
|
listen [::]:443 ssl;
|
||||||
server_name git.matthewtran.com;
|
server_name git.matthewtran.com;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
client_max_body_size 512M;
|
client_max_body_size 512M;
|
||||||
proxy_pass http://gitea:3000;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user