init minecraft server configs!
This commit is contained in:
commit
64820d502a
23 changed files with 24719 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.direnv
|
||||||
41
agenix.nix
Normal file
41
agenix.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
age.secrets.discord-bot-token = {
|
||||||
|
file = ./secrets/discord-bot-token.age;
|
||||||
|
owner = "root";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.tailscale-auth-key = {
|
||||||
|
file = ./secrets/tailscale-auth-key.age;
|
||||||
|
owner = "root";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.b2-account-id = {
|
||||||
|
file = ./secrets/b2-account-id.age;
|
||||||
|
owner = "root";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.b2-application-key = {
|
||||||
|
file = ./secrets/b2-application-key.age;
|
||||||
|
owner = "root";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.grafana-admin-password = {
|
||||||
|
file = ./secrets/grafana-admin-password.age;
|
||||||
|
owner = "grafana";
|
||||||
|
group = "grafana";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.grafana-secret-key = {
|
||||||
|
file = ./secrets/grafana-secret-key.age;
|
||||||
|
owner = "grafana";
|
||||||
|
group = "grafana";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
}
|
||||||
3
configs/chunky.yml
Normal file
3
configs/chunky.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
version: 1
|
||||||
|
language: en
|
||||||
|
concurrency: 1
|
||||||
23899
configs/node-exporter-full.json
Normal file
23899
configs/node-exporter-full.json
Normal file
File diff suppressed because it is too large
Load diff
60
configuration.nix
Normal file
60
configuration.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
{ config, pkgs, inputs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./agenix.nix
|
||||||
|
./modules/minecraft.nix
|
||||||
|
./modules/hardening.nix
|
||||||
|
./modules/tailscale.nix
|
||||||
|
./modules/discord.nix
|
||||||
|
./modules/backup.nix
|
||||||
|
./modules/monitoring.nix
|
||||||
|
./modules/caddy.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "24.11";
|
||||||
|
|
||||||
|
boot.loader.grub = {
|
||||||
|
enable = true;
|
||||||
|
efiSupport = true;
|
||||||
|
efiInstallAsRemovable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.hostName = "compsigh-minecraft";
|
||||||
|
|
||||||
|
time.timeZone = "America/Los_Angeles";
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
nix.settings = {
|
||||||
|
experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
auto-optimise-store = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
nix.gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than 14d";
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim
|
||||||
|
git
|
||||||
|
htop
|
||||||
|
tmux
|
||||||
|
rsync
|
||||||
|
|
||||||
|
(writeShellScriptBin "mc-whitelist" ''
|
||||||
|
docker exec minecraft rcon-cli whitelist add "$1"
|
||||||
|
'')
|
||||||
|
(writeShellScriptBin "mc-cmd" ''
|
||||||
|
docker exec minecraft rcon-cli "$@"
|
||||||
|
'')
|
||||||
|
(writeShellScriptBin "mc-logs" ''
|
||||||
|
docker logs --tail ''${1:-100} -f minecraft
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu jetthomaspham@gmail.com"
|
||||||
|
];
|
||||||
|
}
|
||||||
36
disk-config.nix
Normal file
36
disk-config.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
disko.devices = {
|
||||||
|
disk = {
|
||||||
|
main = {
|
||||||
|
type = "disk";
|
||||||
|
device = "/dev/sda"; # adjust for your VPS
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
boot = {
|
||||||
|
size = "1M";
|
||||||
|
type = "EF02"; # BIOS boot partition
|
||||||
|
};
|
||||||
|
esp = {
|
||||||
|
size = "512M";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
130
flake.lock
generated
Normal file
130
flake.lock
generated
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"agenix": {
|
||||||
|
"inputs": {
|
||||||
|
"darwin": "darwin",
|
||||||
|
"home-manager": "home-manager",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770165109,
|
||||||
|
"narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=",
|
||||||
|
"owner": "ryantm",
|
||||||
|
"repo": "agenix",
|
||||||
|
"rev": "b027ee29d959fda4b60b57566d64c98a202e0feb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ryantm",
|
||||||
|
"repo": "agenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"darwin": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"agenix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744478979,
|
||||||
|
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
|
||||||
|
"owner": "lnl7",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "lnl7",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disko": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773025010,
|
||||||
|
"narHash": "sha256-khlHllTsovXgT2GZ0WxT4+RvuMjNeR5OW0UYeEHPYQo=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "disko",
|
||||||
|
"rev": "7b9f7f88ab3b339f8142dc246445abb3c370d3d3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "disko",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"home-manager": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"agenix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1745494811,
|
||||||
|
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773282481,
|
||||||
|
"narHash": "sha256-b/GV2ysM8mKHhinse2wz+uP37epUrSE+sAKXy/xvBY4=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "fe416aaedd397cacb33a610b33d60ff2b431b127",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"agenix": "agenix",
|
||||||
|
"disko": "disko",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
67
flake.nix
Normal file
67
flake.nix
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
description = "compsigh Minecraft server";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
disko = {
|
||||||
|
url = "github:nix-community/disko";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
agenix = {
|
||||||
|
url = "github:ryantm/agenix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, disko, ... }@inputs:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
serverHost = "root@compsigh-minecraft";
|
||||||
|
|
||||||
|
deploy = pkgs.writeShellScriptBin "nhs" ''
|
||||||
|
nh os switch --hostname compsigh-minecraft --target-host root@compsigh-minecraft path:. "$@"
|
||||||
|
'';
|
||||||
|
mcWhitelist = pkgs.writeShellScriptBin "mc-whitelist" ''
|
||||||
|
ssh ${serverHost} "docker exec minecraft rcon-cli whitelist add $1"
|
||||||
|
'';
|
||||||
|
mcCmd = pkgs.writeShellScriptBin "mc-cmd" ''
|
||||||
|
ssh ${serverHost} "docker exec minecraft rcon-cli $*"
|
||||||
|
'';
|
||||||
|
mcLogs = pkgs.writeShellScriptBin "mc-logs" ''
|
||||||
|
ssh ${serverHost} "docker logs --tail ''${1:-100} -f minecraft"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixosConfigurations.compsigh-minecraft = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
specialArgs = { inherit inputs; };
|
||||||
|
modules = [
|
||||||
|
disko.nixosModules.disko
|
||||||
|
inputs.agenix.nixosModules.default
|
||||||
|
./disk-config.nix
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = [
|
||||||
|
deploy
|
||||||
|
mcWhitelist
|
||||||
|
mcCmd
|
||||||
|
mcLogs
|
||||||
|
pkgs.nh
|
||||||
|
inputs.agenix.packages.${system}.default
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "compsigh minecraft server"
|
||||||
|
echo " nhs — deploy to server"
|
||||||
|
echo " mc-whitelist — add a player (mc-whitelist PlayerName)"
|
||||||
|
echo " mc-cmd — run rcon command (mc-cmd whitelist list)"
|
||||||
|
echo " mc-logs — tail server logs"
|
||||||
|
echo " agenix — manage secrets"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
61
modules/backup.nix
Normal file
61
modules/backup.nix
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
mcDataDir = "/srv/minecraft/data";
|
||||||
|
b2Bucket = "compsigh-minecraft-backups"; # TODO: change to your bucket name
|
||||||
|
in
|
||||||
|
{
|
||||||
|
systemd.services.minecraft-backup = {
|
||||||
|
description = "Backup Minecraft world to Backblaze B2";
|
||||||
|
after = [ "docker-minecraft.service" ];
|
||||||
|
path = [ pkgs.docker ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = pkgs.writeShellScript "minecraft-backup" ''
|
||||||
|
TEMP_BACKUP="/tmp/minecraft-world-latest.tar.gz"
|
||||||
|
|
||||||
|
# Check if the container is running
|
||||||
|
if ! docker ps --format '{{.Names}}' | grep -q '^minecraft$'; then
|
||||||
|
echo "Minecraft container not running, skipping backup"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Flush all chunks to disk and pause saving
|
||||||
|
docker exec minecraft rcon-cli save-all flush
|
||||||
|
sleep 2
|
||||||
|
docker exec minecraft rcon-cli save-off
|
||||||
|
|
||||||
|
# Compress world data
|
||||||
|
${pkgs.gnutar}/bin/tar czf "$TEMP_BACKUP" \
|
||||||
|
-C ${mcDataDir} world world_nether world_the_end
|
||||||
|
|
||||||
|
# Re-enable saving immediately
|
||||||
|
docker exec minecraft rcon-cli save-on
|
||||||
|
|
||||||
|
# Upload to B2 (overwrites the single backup file)
|
||||||
|
B2_ACCOUNT=$(cat ${config.age.secrets.b2-account-id.path})
|
||||||
|
B2_KEY=$(cat ${config.age.secrets.b2-application-key.path})
|
||||||
|
|
||||||
|
${pkgs.rclone}/bin/rclone copyto \
|
||||||
|
"$TEMP_BACKUP" \
|
||||||
|
":b2:${b2Bucket}/world-latest.tar.gz" \
|
||||||
|
--b2-account "$B2_ACCOUNT" \
|
||||||
|
--b2-key "$B2_KEY" \
|
||||||
|
--no-check-dest
|
||||||
|
|
||||||
|
# Clean up local temp file
|
||||||
|
rm -f "$TEMP_BACKUP"
|
||||||
|
|
||||||
|
echo "Backup uploaded to Backblaze B2 successfully"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.minecraft-backup = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "*:00,30";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
27
modules/caddy.nix
Normal file
27
modules/caddy.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
domain = "minecraft.compsigh.club";
|
||||||
|
gitRepo = "https://github.com/compsigh/minecraft";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
virtualHosts = {
|
||||||
|
# minecraft.compsigh.club → redirect to git repo
|
||||||
|
"${domain}" = {
|
||||||
|
extraConfig = ''
|
||||||
|
redir ${gitRepo} permanent
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# status.minecraft.compsigh.club → Grafana
|
||||||
|
"status.${domain}" = {
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy localhost:3000
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
36
modules/discord.nix
Normal file
36
modules/discord.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
mcDataDir = "/srv/minecraft/data";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
systemd.services.minecraft-discord-config = {
|
||||||
|
description = "Inject Discord bot token into Discord4Fabric config";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "docker-minecraft.service" ];
|
||||||
|
after = [ "agenix.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = pkgs.writeShellScript "setup-discord4fabric" ''
|
||||||
|
CONFIG_DIR="${mcDataDir}/config/discord4fabric"
|
||||||
|
mkdir -p "$CONFIG_DIR"
|
||||||
|
|
||||||
|
TOKEN=$(cat ${config.age.secrets.discord-bot-token.path})
|
||||||
|
|
||||||
|
cat > "$CONFIG_DIR/config.json" <<EOF
|
||||||
|
{
|
||||||
|
"botToken": "$TOKEN",
|
||||||
|
"guildId": "849685154543960085",
|
||||||
|
"channelId": "1482269524571979899",
|
||||||
|
"chatMessageFormat": "**%player%** > %message%",
|
||||||
|
"joinFormat": "%player% joined the server",
|
||||||
|
"leaveFormat": "%player% left the server",
|
||||||
|
"deathFormat": "%message%",
|
||||||
|
"advancementFormat": "%player% has made the advancement **%advancement%**",
|
||||||
|
"discordToMinecraftFormat": "[Discord] <%user%> %message%"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
87
modules/hardening.nix
Normal file
87
modules/hardening.nix
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# ── Firewall ──
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ 80 443 25565 ]; # Caddy HTTP/HTTPS + Minecraft
|
||||||
|
allowedUDPPorts = [ 24454 ]; # Simple Voice Chat
|
||||||
|
trustedInterfaces = [ "tailscale0" ]; # Full access over Tailscale (SSH, etc.)
|
||||||
|
logRefusedConnections = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── SSH — key-only, hardened ──
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
PermitRootLogin = "prohibit-password";
|
||||||
|
KbdInteractiveAuthentication = false;
|
||||||
|
X11Forwarding = false;
|
||||||
|
MaxAuthTries = 3;
|
||||||
|
LoginGraceTime = 30;
|
||||||
|
AllowAgentForwarding = false;
|
||||||
|
AllowTcpForwarding = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── Fail2ban ──
|
||||||
|
services.fail2ban = {
|
||||||
|
enable = true;
|
||||||
|
bantime = "1h";
|
||||||
|
maxretry = 10;
|
||||||
|
bantime-increment = {
|
||||||
|
enable = true;
|
||||||
|
maxtime = "24h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── Kernel hardening ──
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
# SYN flood protection
|
||||||
|
"net.ipv4.tcp_syncookies" = 1;
|
||||||
|
"net.ipv4.tcp_max_syn_backlog" = 2048;
|
||||||
|
"net.ipv4.tcp_synack_retries" = 2;
|
||||||
|
|
||||||
|
# IP spoofing protection
|
||||||
|
"net.ipv4.conf.all.rp_filter" = 1;
|
||||||
|
"net.ipv4.conf.default.rp_filter" = 1;
|
||||||
|
|
||||||
|
# Disable ICMP redirects
|
||||||
|
"net.ipv4.conf.all.accept_redirects" = 0;
|
||||||
|
"net.ipv4.conf.default.accept_redirects" = 0;
|
||||||
|
"net.ipv6.conf.all.accept_redirects" = 0;
|
||||||
|
"net.ipv4.conf.all.send_redirects" = 0;
|
||||||
|
"net.ipv4.conf.default.send_redirects" = 0;
|
||||||
|
|
||||||
|
# Ignore broadcast pings
|
||||||
|
"net.ipv4.icmp_echo_ignore_broadcasts" = 1;
|
||||||
|
|
||||||
|
# Disable source routing
|
||||||
|
"net.ipv4.conf.all.accept_source_route" = 0;
|
||||||
|
"net.ipv4.conf.default.accept_source_route" = 0;
|
||||||
|
|
||||||
|
# Restrict unprivileged BPF
|
||||||
|
"kernel.unprivileged_bpf_disabled" = 1;
|
||||||
|
|
||||||
|
# Restrict dmesg
|
||||||
|
"kernel.dmesg_restrict" = 1;
|
||||||
|
|
||||||
|
# Restrict kernel pointers
|
||||||
|
"kernel.kptr_restrict" = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── Kernel image protection ──
|
||||||
|
security.protectKernelImage = true;
|
||||||
|
|
||||||
|
# ── Disable unused services ──
|
||||||
|
services.avahi.enable = false;
|
||||||
|
services.printing.enable = false;
|
||||||
|
|
||||||
|
# ── Automatic security updates ──
|
||||||
|
system.autoUpgrade = {
|
||||||
|
enable = true;
|
||||||
|
allowReboot = false;
|
||||||
|
dates = "04:00";
|
||||||
|
};
|
||||||
|
}
|
||||||
142
modules/minecraft.nix
Normal file
142
modules/minecraft.nix
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
mcDataDir = "/srv/minecraft/data";
|
||||||
|
|
||||||
|
modrinthMods = builtins.concatStringsSep "," [
|
||||||
|
# Performance
|
||||||
|
"lithium"
|
||||||
|
"krypton"
|
||||||
|
"ferrite-core"
|
||||||
|
"c2me-fabric"
|
||||||
|
"noisium"
|
||||||
|
"linearly-optimized"
|
||||||
|
"vmp-fabric"
|
||||||
|
"ksyxis"
|
||||||
|
"scalablelux"
|
||||||
|
"lmd"
|
||||||
|
"structure-layout-optimizer"
|
||||||
|
"threadtweak"
|
||||||
|
"crashexploitfixer"
|
||||||
|
|
||||||
|
# Anti-cheat
|
||||||
|
"anti-xray"
|
||||||
|
"grimac"
|
||||||
|
"no-chat-reports"
|
||||||
|
|
||||||
|
# Worldgen — terrain (Stardust Labs suite)
|
||||||
|
"terralith"
|
||||||
|
"tectonic"
|
||||||
|
"amplified-nether"
|
||||||
|
"nullscape"
|
||||||
|
|
||||||
|
# Worldgen — structures (YUNG's suite)
|
||||||
|
"yungs-api"
|
||||||
|
"yungs-better-dungeons"
|
||||||
|
"yungs-better-strongholds"
|
||||||
|
"yungs-better-ocean-monuments"
|
||||||
|
"yungs-better-mineshafts"
|
||||||
|
"yungs-better-desert-temples"
|
||||||
|
"yungs-better-jungle-temples"
|
||||||
|
"yungs-better-witch-huts"
|
||||||
|
"yungs-better-nether-fortresses"
|
||||||
|
"yungs-better-end-island"
|
||||||
|
"yungs-extras"
|
||||||
|
"yungs-bridges"
|
||||||
|
|
||||||
|
# Client mod compatibility (optional on client side)
|
||||||
|
"appleskin"
|
||||||
|
"simple-voice-chat"
|
||||||
|
|
||||||
|
# QoL
|
||||||
|
"oneplayersleep"
|
||||||
|
"netherportalfix"
|
||||||
|
|
||||||
|
# Moderation
|
||||||
|
"luckperms"
|
||||||
|
"banhammer"
|
||||||
|
"ledger"
|
||||||
|
"styled-chat"
|
||||||
|
|
||||||
|
# Discord bridge
|
||||||
|
"discord4fabric"
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
"chunky"
|
||||||
|
];
|
||||||
|
|
||||||
|
jvmFlags = builtins.concatStringsSep " " [
|
||||||
|
"-XX:+UseG1GC"
|
||||||
|
"-XX:+ParallelRefProcEnabled"
|
||||||
|
"-XX:MaxGCPauseMillis=200"
|
||||||
|
"-XX:+UnlockExperimentalVMOptions"
|
||||||
|
"-XX:+DisableExplicitGC"
|
||||||
|
"-XX:G1NewSizePercent=30"
|
||||||
|
"-XX:G1MaxNewSizePercent=40"
|
||||||
|
"-XX:G1HeapRegionSize=8M"
|
||||||
|
"-XX:G1ReservePercent=20"
|
||||||
|
"-XX:G1MixedGCCountTarget=4"
|
||||||
|
"-XX:InitiatingHeapOccupancyPercent=15"
|
||||||
|
"-XX:G1MixedGCLiveThresholdPercent=90"
|
||||||
|
"-XX:SurvivorRatio=32"
|
||||||
|
"-XX:+PerfDisableSharedMem"
|
||||||
|
"-XX:MaxTenuringThreshold=1"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
virtualisation.oci-containers.backend = "docker";
|
||||||
|
virtualisation.oci-containers.containers.minecraft = {
|
||||||
|
image = "itzg/minecraft-server:latest";
|
||||||
|
ports = [
|
||||||
|
"25565:25565" # Minecraft
|
||||||
|
"24454:24454/udp" # Simple Voice Chat
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
"${mcDataDir}:/data"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
EULA = "TRUE";
|
||||||
|
TYPE = "FABRIC";
|
||||||
|
VERSION = "1.21.4";
|
||||||
|
MEMORY = "2G";
|
||||||
|
MAX_PLAYERS = "10";
|
||||||
|
DIFFICULTY = "hard";
|
||||||
|
VIEW_DISTANCE = "10";
|
||||||
|
SIMULATION_DISTANCE = "10";
|
||||||
|
ENABLE_WHITELIST = "TRUE";
|
||||||
|
ENFORCE_WHITELIST = "TRUE";
|
||||||
|
WHITELIST = "player1,player2"; # TODO: add your players
|
||||||
|
MOTD = "compsigh Minecraft"; # TODO: customize
|
||||||
|
MODRINTH_PROJECTS = modrinthMods;
|
||||||
|
JVM_XX_OPTS = jvmFlags;
|
||||||
|
};
|
||||||
|
extraOptions = [
|
||||||
|
"--memory=3g"
|
||||||
|
"--cpus=2"
|
||||||
|
"--restart=unless-stopped"
|
||||||
|
"--pids-limit=256"
|
||||||
|
"--security-opt=no-new-privileges"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Ensure data directory exists
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${mcDataDir} 0755 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Copy Chunky config (concurrency: 1 for background generation)
|
||||||
|
systemd.services.minecraft-mod-configs = {
|
||||||
|
description = "Copy mod configs into Minecraft data volume";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "docker-minecraft.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = pkgs.writeShellScript "setup-mod-configs" ''
|
||||||
|
mkdir -p ${mcDataDir}/plugins/Chunky
|
||||||
|
cp ${../configs/chunky.yml} ${mcDataDir}/plugins/Chunky/config.yml
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
86
modules/monitoring.nix
Normal file
86
modules/monitoring.nix
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.prometheus = {
|
||||||
|
enable = true;
|
||||||
|
port = 9090;
|
||||||
|
retentionTime = "30d";
|
||||||
|
|
||||||
|
exporters = {
|
||||||
|
node = {
|
||||||
|
enable = true;
|
||||||
|
port = 9100;
|
||||||
|
enabledCollectors = [
|
||||||
|
"cpu"
|
||||||
|
"diskstats"
|
||||||
|
"filesystem"
|
||||||
|
"loadavg"
|
||||||
|
"meminfo"
|
||||||
|
"netdev"
|
||||||
|
"netstat"
|
||||||
|
"stat"
|
||||||
|
"time"
|
||||||
|
"vmstat"
|
||||||
|
"systemd"
|
||||||
|
"processes"
|
||||||
|
"interrupts"
|
||||||
|
"conntrack"
|
||||||
|
"tcpstat"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
scrapeConfigs = [
|
||||||
|
{
|
||||||
|
job_name = "node";
|
||||||
|
static_configs = [{
|
||||||
|
targets = [ "localhost:9100" ];
|
||||||
|
labels = { instance = "minecraft-server"; };
|
||||||
|
}];
|
||||||
|
scrape_interval = "15s";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.grafana = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
http_addr = "127.0.0.1";
|
||||||
|
http_port = 3000;
|
||||||
|
domain = "status.minecraft.compsigh.club";
|
||||||
|
root_url = "https://status.minecraft.compsigh.club";
|
||||||
|
};
|
||||||
|
security = {
|
||||||
|
admin_user = "admin";
|
||||||
|
admin_password = "$__file{${config.age.secrets.grafana-admin-password.path}}";
|
||||||
|
secret_key = "$__file{${config.age.secrets.grafana-secret-key.path}}";
|
||||||
|
};
|
||||||
|
"auth.anonymous".enabled = false;
|
||||||
|
users.allow_sign_up = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
provision = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
datasources.settings.datasources = [
|
||||||
|
{
|
||||||
|
name = "Prometheus";
|
||||||
|
type = "prometheus";
|
||||||
|
url = "http://localhost:9090";
|
||||||
|
isDefault = true;
|
||||||
|
editable = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
dashboards.settings.providers = [
|
||||||
|
{
|
||||||
|
name = "Node Exporter Full";
|
||||||
|
options.path = "${pkgs.writeTextDir "dashboards/node-exporter-full.json"
|
||||||
|
(builtins.readFile ../configs/node-exporter-full.json)}/dashboards";
|
||||||
|
options.foldersFromFilesStructure = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
8
modules/tailscale.nix
Normal file
8
modules/tailscale.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
authKeyFile = config.age.secrets.tailscale-auth-key.path;
|
||||||
|
};
|
||||||
|
}
|
||||||
BIN
secrets/b2-account-id.age
Normal file
BIN
secrets/b2-account-id.age
Normal file
Binary file not shown.
7
secrets/b2-application-key.age
Normal file
7
secrets/b2-application-key.age
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 8KhtvA RPJwPm/DkUYfQlYSCa4s6KF5bV8ajDflEj+Gljgs1TI
|
||||||
|
aP54VuO6RYP/tLPIzOHDgRIKs1AirPu3zkYuvVamTjE
|
||||||
|
-> ssh-ed25519 Ziw7aw DHkTEHLtrhSuSoNuv9zS5kgeKth3NtjUj8IVBiBch1k
|
||||||
|
/P9kmTW0z3oTuBtd8wv+tfbBWP4Y1ObhPbwnLeCJO/U
|
||||||
|
--- 6OuUgwkLM+4RkVychG8IVWRb9es4WimS6KI6jxCyPn4
|
||||||
|
¨ü'Ï^xÉ`WAN;öÂzõ<7A>¤V£‡—±¹`“m´7ÐßöG‹7þvc½ó]æ`ÕåM#ª=Öµ<C396>#K
|
||||||
8
secrets/discord-bot-token.age
Normal file
8
secrets/discord-bot-token.age
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 8KhtvA 14QBStvnxOU5fDg/fBWu7Klun03DfblZhFV1vWn/u2w
|
||||||
|
0/rGOHyVCh7AG0HBMCw8XAKkvKmtDXk32zDZFRVI+aE
|
||||||
|
-> ssh-ed25519 Ziw7aw agcXQVvCtH88gSFhJH+g4vJDHFyqfMc7haOmqCWCtBs
|
||||||
|
ULjdXfTM1/3a6fXrqsH7rLPXCLOxT0DBbGnn5Y3znfo
|
||||||
|
--- rGogYk7dpYIqYP9Vg7oRu8ZghB0nYddMCpzz4GKCx0s
|
||||||
|
3b¹0ª-j\>Cs.Ø@‘&,ô%4fC¦Þ¬1ÄEýÕ¢î~Í
ºêÈ<>°€Í³ëì2¤Û³ØúbÁ_2Ñ-åôÇ*õ‚C³ë~LÍœñºª=ûÏ(€ãŽQPþ+²û
|
||||||
|
£6ê
|
||||||
BIN
secrets/grafana-admin-password.age
Normal file
BIN
secrets/grafana-admin-password.age
Normal file
Binary file not shown.
7
secrets/grafana-secret-key.age
Normal file
7
secrets/grafana-secret-key.age
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 8KhtvA HwJeI2xpphSyhSacAqEdmnzbJxNEuRN2sLv+sK0XqQI
|
||||||
|
11GS6HqXTQ0gksTZlJfVDwJ3PaLy1SD5D/J7QstK/bI
|
||||||
|
-> ssh-ed25519 Ziw7aw 5S4mq+b1VAaMrXZrUfYowGfU/wR6aSql3wVOsKnbJEQ
|
||||||
|
c+DAvSndUItpBbFR1ce/SfL2AthJ7fW8Sdq+vja6L+s
|
||||||
|
--- wSQ+PDx7FR4mDl9Cw4ah/CS2JujL7uP+9ZexfD8KfgI
|
||||||
|
ó}†šØ|–ßÖÂdõ‚LXå‘O€R U<>¾UÁ)Ê’'jgPpüšÙámx<>§C¡
BœVŸ@™Ò‘->é6üÔŸ’é¸&`Ñùø“
|
||||||
12
secrets/secrets.nix
Normal file
12
secrets/secrets.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
let
|
||||||
|
server = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP9y+1CHA9RxeeJrkIblRErPvWTeTrqdNlQjxuRgMXN2";
|
||||||
|
admin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"discord-bot-token.age".publicKeys = [ server admin ];
|
||||||
|
"tailscale-auth-key.age".publicKeys = [ server admin ];
|
||||||
|
"b2-account-id.age".publicKeys = [ server admin ];
|
||||||
|
"b2-application-key.age".publicKeys = [ server admin ];
|
||||||
|
"grafana-admin-password.age".publicKeys = [ server admin ];
|
||||||
|
"grafana-secret-key.age".publicKeys = [ server admin ];
|
||||||
|
}
|
||||||
BIN
secrets/tailscale-auth-key.age
Normal file
BIN
secrets/tailscale-auth-key.age
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue