Compare commits
2 Commits
d15ec9fe0b
...
a23b3d8c5f
| Author | SHA1 | Date | |
|---|---|---|---|
|
a23b3d8c5f
|
|||
|
4bf05f8b51
|
@@ -173,7 +173,7 @@
|
||||
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
hostPlatform = system;
|
||||
targetPlatform = system;
|
||||
buildPlatform = builtins.currentSystem;
|
||||
};
|
||||
lib = import ./modules/lib.nix { inherit inputs pkgs; };
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
graphing-calculator = inputs.ytbn-graphing-software.packages.${pkgs.stdenv.hostPlatform.system}.web;
|
||||
graphing-calculator =
|
||||
inputs.ytbn-graphing-software.packages.${pkgs.stdenv.targetPlatform.system}.web;
|
||||
in
|
||||
{
|
||||
services.caddy.virtualHosts."graphing.${service_configs.https.domain}".extraConfig = ''
|
||||
|
||||
@@ -151,4 +151,24 @@
|
||||
"z ${service_configs.minecraft.parent_dir}/${service_configs.minecraft.server_name}/squaremap 710 ${config.services.minecraft-servers.user} ${config.services.minecraft-servers.group}"
|
||||
"Z ${service_configs.minecraft.parent_dir}/${service_configs.minecraft.server_name}/squaremap/web 750 ${config.services.minecraft-servers.user} ${config.services.minecraft-servers.group}"
|
||||
];
|
||||
|
||||
# Protect Minecraft server from connection spam / brute force attempts
|
||||
# Based on https://github.com/fail2ban/fail2ban/pull/2852#issuecomment-3105039910
|
||||
# Only bans IPs that fail whitelist/ban checks - NOT legitimate player disconnects
|
||||
services.fail2ban.jails.minecraft = {
|
||||
enabled = true;
|
||||
settings = {
|
||||
backend = "auto";
|
||||
port = builtins.toString config.services.minecraft-servers.servers.${service_configs.minecraft.server_name}.serverProperties.server-port;
|
||||
logpath = "${config.services.minecraft-servers.dataDir}/${service_configs.minecraft.server_name}/logs/latest.log";
|
||||
# defaults: maxretry=5, findtime=10m, bantime=10m
|
||||
};
|
||||
filter.Definition = {
|
||||
# Only match whitelist rejections and bans - safe patterns that won't affect legitimate players
|
||||
# Format: [HH:MM:SS] [Server thread/INFO]: Disconnecting <name> (/<IP>:<PORT>): <reason>
|
||||
datepattern = "^\\[%%H:%%M:%%S\\]";
|
||||
failregex = "^\\s*\\[Server thread/INFO\\]: Disconnecting .+ \\(/<HOST>:\\d+\\): (?:You are not white-listed on this server|You are banned from this server)";
|
||||
ignoreregex = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
170
tests/fail2ban-minecraft.nix
Normal file
170
tests/fail2ban-minecraft.nix
Normal file
@@ -0,0 +1,170 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
testServerName = "testserver";
|
||||
|
||||
# Create pkgs with nix-minecraft overlay and unfree packages allowed
|
||||
testPkgs = import inputs.nixpkgs {
|
||||
system = pkgs.stdenv.targetPlatform.system;
|
||||
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "minecraft-server" ];
|
||||
overlays = [
|
||||
inputs.nix-minecraft.overlay
|
||||
(import ../modules/overlays.nix)
|
||||
];
|
||||
};
|
||||
|
||||
testServiceConfigs = {
|
||||
zpool_ssds = "";
|
||||
https = {
|
||||
domain = "test.local";
|
||||
};
|
||||
minecraft = {
|
||||
parent_dir = "/var/lib/minecraft";
|
||||
server_name = testServerName;
|
||||
};
|
||||
};
|
||||
|
||||
testLib = lib.extend (
|
||||
final: prev: {
|
||||
serviceMountWithZpool =
|
||||
serviceName: zpool: dirs:
|
||||
{ ... }:
|
||||
{ };
|
||||
}
|
||||
);
|
||||
|
||||
minecraftModule =
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
(import ../services/minecraft.nix {
|
||||
inherit config inputs;
|
||||
pkgs = testPkgs;
|
||||
lib = testLib;
|
||||
service_configs = testServiceConfigs;
|
||||
})
|
||||
];
|
||||
# Override nixpkgs config to prevent conflicts in test environment
|
||||
nixpkgs.config = lib.mkForce {
|
||||
allowUnfreePredicate = pkg: builtins.elem (testPkgs.lib.getName pkg) [ "minecraft-server" ];
|
||||
};
|
||||
# Disable whitelist import to avoid missing secrets file and reduce memory
|
||||
services.minecraft-servers.servers.${testServerName} = {
|
||||
whitelist = lib.mkForce { };
|
||||
jvmOpts = lib.mkForce "-Xmx1G -Xms1G";
|
||||
};
|
||||
};
|
||||
in
|
||||
testPkgs.testers.runNixOSTest {
|
||||
name = "fail2ban-minecraft";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
../modules/security.nix
|
||||
minecraftModule
|
||||
];
|
||||
|
||||
# Disable ZFS mount dependency
|
||||
systemd.services."minecraft-server-${testServerName}-mounts".enable = lib.mkForce false;
|
||||
systemd.services."minecraft-server-${testServerName}" = {
|
||||
wants = lib.mkForce [ ];
|
||||
after = lib.mkForce [ ];
|
||||
requires = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
# Override for faster testing
|
||||
services.fail2ban.jails.minecraft.settings = {
|
||||
maxretry = lib.mkForce 3;
|
||||
findtime = lib.mkForce "5m";
|
||||
bantime = lib.mkForce "10m";
|
||||
};
|
||||
|
||||
# Create log directory and placeholder for fail2ban
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/minecraft/${testServerName}/logs 0755 minecraft minecraft"
|
||||
"f /var/lib/minecraft/${testServerName}/logs/latest.log 0644 minecraft minecraft"
|
||||
];
|
||||
|
||||
# Make fail2ban start after minecraft
|
||||
systemd.services.fail2ban = {
|
||||
wants = [ "minecraft-server-${testServerName}.service" ];
|
||||
after = [ "minecraft-server-${testServerName}.service" ];
|
||||
};
|
||||
|
||||
# Give minecraft server more resources
|
||||
virtualisation.diskSize = 4 * 1024;
|
||||
virtualisation.memorySize = 4 * 1024;
|
||||
};
|
||||
|
||||
client =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = [
|
||||
(pkgs.python3.withPackages (ps: [ ps.mcstatus ]))
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import time
|
||||
|
||||
start_all()
|
||||
|
||||
# Wait for minecraft server to fully start
|
||||
server.wait_for_unit("minecraft-server-${testServerName}.service", timeout=180)
|
||||
server.wait_for_unit("fail2ban.service")
|
||||
server.wait_for_open_port(25565, timeout=120)
|
||||
|
||||
# Wait for server to be ready (shows "Done" in logs)
|
||||
server.wait_until_succeeds(
|
||||
"grep -q 'Done' /var/lib/minecraft/${testServerName}/logs/latest.log",
|
||||
timeout=120
|
||||
)
|
||||
time.sleep(2)
|
||||
|
||||
# Reload fail2ban now that the real log file exists
|
||||
server.succeed("fail2ban-client reload minecraft")
|
||||
time.sleep(2)
|
||||
|
||||
with subtest("Verify minecraft jail is active"):
|
||||
status = server.succeed("fail2ban-client status")
|
||||
print(f"fail2ban status:\n{status}")
|
||||
assert "minecraft" in status, f"minecraft jail not found in: {status}"
|
||||
|
||||
with subtest("Verify jail configuration"):
|
||||
# Check jail status shows it's monitoring the log file
|
||||
status = server.succeed("fail2ban-client status minecraft")
|
||||
print(f"Jail status:\n{status}")
|
||||
assert "minecraft" in status, "minecraft jail not properly configured"
|
||||
|
||||
with subtest("Check server logs"):
|
||||
logs = server.succeed("tail -20 /var/lib/minecraft/${testServerName}/logs/latest.log")
|
||||
print(f"Server logs:\n{logs}")
|
||||
|
||||
with subtest("Test regex with fail2ban-regex"):
|
||||
# Test the filter regex against the log file
|
||||
result = server.execute("fail2ban-regex /var/lib/minecraft/${testServerName}/logs/latest.log /etc/fail2ban/filter.d/minecraft.local 2>&1")
|
||||
print(f"Regex test result:\n{result}")
|
||||
|
||||
with subtest("Verify jail is functional"):
|
||||
# The jail should be running and monitoring - mcstatus won't trigger bans
|
||||
# since it only does status pings, not login attempts that would fail whitelist
|
||||
status = server.succeed("fail2ban-client status minecraft")
|
||||
print(f"Final jail status:\n{status}")
|
||||
# Verify the jail is running (has filter file loaded)
|
||||
assert "Filter" in status or "File list" in status or "Currently" in status, "Jail not properly running"
|
||||
'';
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
let
|
||||
# Create pkgs with nix-minecraft overlay and unfree packages allowed
|
||||
testPkgs = import inputs.nixpkgs {
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
system = pkgs.stdenv.targetPlatform.system;
|
||||
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "minecraft-server" ];
|
||||
overlays = [
|
||||
inputs.nix-minecraft.overlay
|
||||
|
||||
@@ -20,4 +20,5 @@ in
|
||||
fail2banVaultwardenTest = handleTest ./fail2ban-vaultwarden.nix;
|
||||
fail2banImmichTest = handleTest ./fail2ban-immich.nix;
|
||||
fail2banJellyfinTest = handleTest ./fail2ban-jellyfin.nix;
|
||||
fail2banMinecraftTest = handleTest ./fail2ban-minecraft.nix;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user