Compare commits

...

2 Commits

Author SHA1 Message Date
Bastian de Byl
9d562c7188 feat: smart zomboid traffic filtering with packet-size detection
Replace per-IP hashlimit with smarter filtering that distinguishes
legitimate players from scanner bots based on packet behavior:
- Players send varied packet sizes (53, 37, 1472 bytes)
- Scanners only send 53-byte query packets

New firewall rule chain:
- Priority 2: Mark + ACCEPT non-query packets (verifies player)
- Priority 3: ACCEPT queries from verified IPs (1 hour TTL)
- Priority 4: LOG rate-limited queries from unverified IPs
- Priority 5: DROP rate-limited queries (2 burst, then 1/hour)

Also includes:
- Fail2ban zomboid jail with tighter thresholds (5 retries/4h, 1w ban)
- Graylog streams for zomboid-connections, zomboid-ratelimit, fail2ban
- GeoIP pipeline enrichment for zomboid traffic
- Fluent-bit inputs for ratelimit logs and fail2ban events
- Remove Legendary Katana mod (Workshop 3418366499) - removed from Steam
- Bump Immich to v2.5.0
- Fix fulfillr config (nil → null)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 15:09:26 -05:00
Bastian de Byl
33eceff1fe feat: add personal uptime kuma instance at uptime.debyl.io
- Add uptime-kuma-personal container on port 3002
- Add Caddy config for uptime.debyl.io with IP restriction
- Update both uptime-kuma instances to 2.0.2
- Rename debyltech tag from uptime-kuma to uptime-debyltech

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 08:04:33 -05:00
13 changed files with 393 additions and 10 deletions

View File

@@ -12,7 +12,8 @@ deps:
python-docker,
]
fail2ban_jails: [sshd.local]
fail2ban_jails: [sshd.local, zomboid.local]
fail2ban_filters: [zomboid.conf]
services:
- crond

View File

@@ -0,0 +1,5 @@
[Definition]
# Match ZOMBOID_RATELIMIT firewall log entries
# Example: ZOMBOID_RATELIMIT: IN=eth0 OUT= MAC=... SRC=1.2.3.4 DST=...
failregex = ZOMBOID_RATELIMIT:.*SRC=<HOST>
ignoreregex =

View File

@@ -0,0 +1,9 @@
[zomboid]
enabled = true
filter = zomboid
banaction = iptables-allports
backend = systemd
maxretry = 5
findtime = 4h
bantime = 1w
ignoreip = 127.0.0.1/32 192.168.1.0/24

View File

@@ -21,6 +21,16 @@
notify: restart_sshd
tags: security
- name: setup fail2ban filters
become: true
ansible.builtin.copy:
src: files/fail2ban/filters/{{ item }}
dest: /etc/fail2ban/filter.d/{{ item }}
mode: 0644
loop: "{{ fail2ban_filters }}"
notify: restart_fail2ban
tags: security
- name: setup fail2ban jails
become: true
ansible.builtin.copy:

View File

@@ -56,6 +56,30 @@ graylog_streams:
type: 1
inverted: false
- title: "zomboid-connections"
description: "Zomboid game server connection logs"
rules:
- field: "log_type"
value: "zomboid_connection"
type: 1
inverted: false
- title: "zomboid-ratelimit"
description: "Zomboid rate-limited connection attempts"
rules:
- field: "log_type"
value: "zomboid_ratelimit"
type: 1
inverted: false
- title: "fail2ban-actions"
description: "Fail2ban ban and unban events"
rules:
- field: "source"
value: "fail2ban"
type: 1
inverted: false
# Pipeline definitions
graylog_pipelines:
- title: "GeoIP Enrichment"
@@ -65,6 +89,7 @@ graylog_pipelines:
match: "EITHER"
rules:
- "geoip_caddy_access"
- "geoip_zomboid"
- title: "Debyltech Event Classification"
description: "Categorize debyltech-api events"
@@ -98,6 +123,20 @@ graylog_pipeline_rules:
set_field("geo_coordinates", geo["coordinates"]);
end
- title: "geoip_zomboid"
description: "GeoIP lookup for Zomboid connection logs"
source: |
rule "GeoIP for Zomboid"
when
has_field("src_ip")
then
let ip = to_string($message.src_ip);
let geo = lookup("geoip-lookup", ip);
set_field("geo_country", geo["country"].iso_code);
set_field("geo_city", geo["city"].names.en);
set_field("geo_coordinates", geo["coordinates"]);
end
- title: "classify_order_events"
description: "Classify order events"
source: |
@@ -164,6 +203,8 @@ graylog_pipeline_connections:
streams:
- "caddy-access"
- "caddy-fulfillr"
- "zomboid-connections"
- "zomboid-ratelimit"
- pipeline: "Debyltech Event Classification"
streams:

View File

@@ -15,6 +15,7 @@ partkeepr_path: "{{ podman_volumes }}/partkeepr"
partsy_path: "{{ podman_volumes }}/partsy"
photos_path: "{{ podman_volumes }}/photos"
uptime_kuma_path: "{{ podman_volumes }}/uptime-kuma"
uptime_kuma_personal_path: "{{ podman_volumes }}/uptime-kuma-personal"
zomboid_path: "{{ podman_volumes }}/zomboid"
# Zomboid server mode: 'vanilla', 'modded', or 'b42revamp'
@@ -50,10 +51,10 @@ zomboid_mods:
# Added: LogCabin (3653045510) - player connect/disconnect logging
zomboid_mods_b42revamp:
workshop_items: >-
3402491515;2529746725;3378285185;3171167894;3635394848;3411888105;3616536783;3635591071;3636241120;3629835761;3618427553;3543229299;3622163276;3634921455;3626823538;2335368829;3580577925;3401134276;3418366499;3586216562;3452711271;2757712197;3330403100;2686624983;3409143790;3439305933;3378304610;2503622437;2896041179;2625625421;3590632059;3097650043;2366717227;3077900375;3596827035;3508537032;3630693325;3631572046;2463184726;2447729538;2142622992;3566868353;3539691958;3504401781;3570973322;3614034284;2900580391;3478633453;3001592312;3041122351;3315443103;2799152995;3248388837;2897390033;3379334330;2409333430;3110913021;3287727378;2846036306;3435796523;3447272250;2932547723;3110911330;2971246021;3005903549;3320947974;2942793445;3008795514;2870394916;2952802178;2805630347;3490370700;2772575623;3428008364;2566953935;3413704851;3152529790;2811383142;3592777775;3418252689;3404737883;3631989559;2969343830;3366300557;2962175696;3596903773;3226885926;2937786633;3601417745;2886832936;2932549988;3088951320;3611100835;3409472393;3623784989;3625020432;3138387399;3119788162;2286124931;2866258937;3623919908;2714198296;3628835042;3429790870;3431734923;3577903007;3398874593;2950902979;3420581050;3538760023;3423984426;3396446795;3618557184;2840805724;3437629766;3632610172;3633882960;3307376332;3453676250;3431256608;3475347500;3404956403;3451167732;3281755175;3252451158;3162566044;2684285534;3424309174;3199474685;3483407987;3387222454;3614959302;3351207258;2699828474;3461263912;3322066592;2972289937;2948824747;3624268336;2857762294;2920899878;3572556874;3470426196;3470422050;3432928943;3430172149;3637373250;3044705007;2705406713;3554424111;3412105017;3554048011;3635228703;2940354599;3414634809;3627047348;3442862183;2990322197;3394044313;3617854007;3388867450;3532685233;3411695932;3643808082;3413150945;3531611692;3653045510
3402491515;2529746725;3378285185;3171167894;3635394848;3411888105;3616536783;3635591071;3636241120;3629835761;3618427553;3543229299;3622163276;3634921455;3626823538;2335368829;3580577925;3401134276;3586216562;3452711271;2757712197;3330403100;2686624983;3409143790;3439305933;3378304610;2503622437;2896041179;2625625421;3590632059;3097650043;2366717227;3077900375;3596827035;3508537032;3630693325;3631572046;2463184726;2447729538;2142622992;3566868353;3539691958;3504401781;3570973322;3614034284;2900580391;3478633453;3001592312;3041122351;3315443103;2799152995;3248388837;2897390033;3379334330;2409333430;3110913021;3287727378;2846036306;3435796523;3447272250;2932547723;3110911330;2971246021;3005903549;3320947974;2942793445;3008795514;2870394916;2952802178;2805630347;3490370700;2772575623;3428008364;2566953935;3413704851;3152529790;2811383142;3592777775;3418252689;3404737883;3631989559;2969343830;3366300557;2962175696;3596903773;3226885926;2937786633;3601417745;2886832936;2932549988;3088951320;3611100835;3409472393;3623784989;3625020432;3138387399;3119788162;2286124931;2866258937;3623919908;2714198296;3628835042;3429790870;3431734923;3577903007;3398874593;2950902979;3420581050;3538760023;3423984426;3396446795;3618557184;2840805724;3437629766;3632610172;3633882960;3307376332;3453676250;3431256608;3475347500;3404956403;3451167732;3281755175;3252451158;3162566044;2684285534;3424309174;3199474685;3483407987;3387222454;3614959302;3351207258;2699828474;3461263912;3322066592;2972289937;2948824747;3624268336;2857762294;2920899878;3572556874;3470426196;3470422050;3432928943;3430172149;3637373250;3044705007;2705406713;3554424111;3412105017;3554048011;3635228703;2940354599;3414634809;3627047348;3442862183;2990322197;3394044313;3617854007;3388867450;3532685233;3411695932;3643808082;3413150945;3531611692;3653045510
# Load order from Steam collection (UpgradeableStorage removed for MP compatibility, LogCabin added)
mod_ids: >-
\MoodleFramework;\NeatUI_Framework;\Optimal;\SPNCC;\SPNCCDetails;\SPNCCDetailsHD;\TombBodyTexNUDE;\Authentic Z - Current;\PROJECTRVInterior42;\RVInteriorExpansion;\RVInteriorExpansionPart2;\RVmilitaryaddon;\damnlib;\04vwTouran;\49powerWagon;\59meteor;\63beetle;\63Type2Van;\65banshee;\66pontiacLeMans;\69charger;\69mini;\69mini_ItalianJob;\69mini_MrBean;\69mini_PitbullSpecial;\73fordFalcon;\73fordFalconPS;\78amgeneralM35A2;\78amgeneralM35A2extra;\78amgeneralM49A2C;\78amgeneralM50A3;\78amgeneralM62;\80manKat1;\82firebird;\82firebirdKITT;\82porsche911;\83amgeneralM923;\83amgeneralM923extra;\84buickElectra;\84cadillacDeVille;\84merc;\84oldsmobile98;\85buickLeSabre;\85chevyCaprice;\85chevyStepVan;\85chevyStepVanexpanded;\86chevyCUCV;\86fordE150;\86fordE150dnd;\86fordE150expanded;\86fordE150mm;\86fordE150pd;\86oshkoshP19A;\87buickRegal;\87fordB700;\88chevyS10;\88toyotaHilux;\89defender;\89trooper;\90bmwE30;\90fordF350ambulance;\90pierceArrow;\91fordLTD;\91fordRanger;\91geoMetro;\91nissan240sx;\91range;\92fordCVPI;\92jeepYJ;\92jeepYJJP18;\92nissanGTR;\93chevySuburban;\93chevySuburbanExpanded;\93fordElgin;\93fordTaurus;\93mustangSSP;\93townCar;\97bushmaster;\98stagea;\99fordCVPI;\KI5trailers;\CargoTrailer_BubbysVariants;\ECTO1;\isoContainers;\tsarslib;\LIAZ 300;\Military_Tool_Kit;\RotatorsLib;\rSemiTruck;\U.S. M998 Humvee by Papa_Chad;\2920899878/ReloadAllMagazines;\BB_WhereAmI;\Buttstroke;\CleanHotBar;\CleanUI;\ClientModsToServer;\CombatText;\ContextMenuIconsCore;\DG_MIVehicles;\EffortlessTowing;\EQUIPMENT_UI;\EURY_BUGS;\FixBlowTorchPropaneTank;\flipvehicleplustrailer;\ForceSync42;\FWOBenchPress&Treadmill;\FWOFitnessWorkoutOverhaul;\GenRange;\HereGoesTheSun;\hf_point_blank;\HideDebugMenu;\HNDLBR_Preppers;\LongStandingMetalConstructions;\MBFTiming;\MiniHealthPanel;\ModernStatus;\ModLoadOrderSorter_b42;\ChuckleberryFinnAlertSystem;\ModManager;\MoreDescriptionForTraits4213;\NoLighterNeeded;\OCsPacking;\phunlib;\phunzones;\ProgressiveMultihit;\ProgressiveMultihit42.13patch;\RealisticDash;\REORDER_CONTAINERS;\StarlitLibrary;\RepairableWindows;\SleepWithFriends;\SmokingSoundsOverhaul;\errorMagnifier;\SwapIt;\TMRRemoveMumble42;\trunk_organizer;\TVRadio_ReInvented;\UdderlyUpToDate_B42.13;\UnseasonalWeather;\VehicleRepairOverhaul;\VehicleSalvageOverhaulB42;\ArcheryNexus;\EFTBP;\FH;\GanydeBielovzki's Frockin Shirts n Ties;\GanydeBielovzki's Frockin Splendor! Vol.2;\GanydeBielovzki's Frockin Splendor! Vol.3;\GanydeBielovzki's Frockin Splendor! Vol.4;\GanydeBielovzki's Frockin Splendor! Vol.5;\GanydeBielovzki's Frockin Wiseguys;\H_E_C_U;\KATTAJ1_ClothesCore;\SapphCooking_B42;\SpnCloth;\TombBody;\TombWardrobeALT;\VanillaGearExpanded;\zReApoModernArmorB42;\B42RainsFirearmsAndGunPartsExpanded4213;\AatheomEMVFSM;\amclub;\FunctionalGutters;\GanydeBielovzki's Frockin Splendor!;\grasslands;\HGOEXP;\KATTAJ1_Military;\Ladders42131;\LKB42;\LNB42;\LongHammer;\MoreDamagedObjects;\N&CsNarcotics;\phunsprinters;\phunsprintersui;\Project_Seasons_B41;\RebalancedPropMoving;\RepairAnyClothes;\RET_LethalStealth;\RiskyInspectWeapon;\ShelterHold_Beehive;\SimpleOverhaulTraitsAndOccupations;\SkillRecoveryJournal;\SPNCCFaces;\SpnHair;\TrueMoozic;\TMMMB42.13+;\TombBodyCompat;\TombBodyCustom;\TrueMusicJukebox;\TrueMusicRadio42;\TrueSmoking;\VanillaFoodsExpanded;\VanillaVehiclesAnimated;\WorkshopUpdateCheck;\zReBetterLockpickingb42mp;\CACustomWoodWeight;\LogCabin
\MoodleFramework;\NeatUI_Framework;\Optimal;\SPNCC;\SPNCCDetails;\SPNCCDetailsHD;\TombBodyTexNUDE;\Authentic Z - Current;\PROJECTRVInterior42;\RVInteriorExpansion;\RVInteriorExpansionPart2;\RVmilitaryaddon;\damnlib;\04vwTouran;\49powerWagon;\59meteor;\63beetle;\63Type2Van;\65banshee;\66pontiacLeMans;\69charger;\69mini;\69mini_ItalianJob;\69mini_MrBean;\69mini_PitbullSpecial;\73fordFalcon;\73fordFalconPS;\78amgeneralM35A2;\78amgeneralM35A2extra;\78amgeneralM49A2C;\78amgeneralM50A3;\78amgeneralM62;\80manKat1;\82firebird;\82firebirdKITT;\82porsche911;\83amgeneralM923;\83amgeneralM923extra;\84buickElectra;\84cadillacDeVille;\84merc;\84oldsmobile98;\85buickLeSabre;\85chevyCaprice;\85chevyStepVan;\85chevyStepVanexpanded;\86chevyCUCV;\86fordE150;\86fordE150dnd;\86fordE150expanded;\86fordE150mm;\86fordE150pd;\86oshkoshP19A;\87buickRegal;\87fordB700;\88chevyS10;\88toyotaHilux;\89defender;\89trooper;\90bmwE30;\90fordF350ambulance;\90pierceArrow;\91fordLTD;\91fordRanger;\91geoMetro;\91nissan240sx;\91range;\92fordCVPI;\92jeepYJ;\92jeepYJJP18;\92nissanGTR;\93chevySuburban;\93chevySuburbanExpanded;\93fordElgin;\93fordTaurus;\93mustangSSP;\93townCar;\97bushmaster;\98stagea;\99fordCVPI;\KI5trailers;\CargoTrailer_BubbysVariants;\ECTO1;\isoContainers;\tsarslib;\LIAZ 300;\Military_Tool_Kit;\RotatorsLib;\rSemiTruck;\U.S. M998 Humvee by Papa_Chad;\2920899878/ReloadAllMagazines;\BB_WhereAmI;\Buttstroke;\CleanHotBar;\CleanUI;\ClientModsToServer;\CombatText;\ContextMenuIconsCore;\DG_MIVehicles;\EffortlessTowing;\EQUIPMENT_UI;\EURY_BUGS;\FixBlowTorchPropaneTank;\flipvehicleplustrailer;\ForceSync42;\FWOBenchPress&Treadmill;\FWOFitnessWorkoutOverhaul;\GenRange;\HereGoesTheSun;\hf_point_blank;\HideDebugMenu;\HNDLBR_Preppers;\LongStandingMetalConstructions;\MBFTiming;\MiniHealthPanel;\ModernStatus;\ModLoadOrderSorter_b42;\ChuckleberryFinnAlertSystem;\ModManager;\MoreDescriptionForTraits4213;\NoLighterNeeded;\OCsPacking;\phunlib;\phunzones;\ProgressiveMultihit;\ProgressiveMultihit42.13patch;\RealisticDash;\REORDER_CONTAINERS;\StarlitLibrary;\RepairableWindows;\SleepWithFriends;\SmokingSoundsOverhaul;\errorMagnifier;\SwapIt;\TMRRemoveMumble42;\trunk_organizer;\TVRadio_ReInvented;\UdderlyUpToDate_B42.13;\UnseasonalWeather;\VehicleRepairOverhaul;\VehicleSalvageOverhaulB42;\ArcheryNexus;\EFTBP;\FH;\GanydeBielovzki's Frockin Shirts n Ties;\GanydeBielovzki's Frockin Splendor! Vol.2;\GanydeBielovzki's Frockin Splendor! Vol.3;\GanydeBielovzki's Frockin Splendor! Vol.4;\GanydeBielovzki's Frockin Splendor! Vol.5;\GanydeBielovzki's Frockin Wiseguys;\H_E_C_U;\KATTAJ1_ClothesCore;\SapphCooking_B42;\SpnCloth;\TombBody;\TombWardrobeALT;\VanillaGearExpanded;\zReApoModernArmorB42;\B42RainsFirearmsAndGunPartsExpanded4213;\AatheomEMVFSM;\amclub;\FunctionalGutters;\GanydeBielovzki's Frockin Splendor!;\grasslands;\HGOEXP;\KATTAJ1_Military;\Ladders42131;\LNB42;\LongHammer;\MoreDamagedObjects;\N&CsNarcotics;\phunsprinters;\phunsprintersui;\Project_Seasons_B41;\RebalancedPropMoving;\RepairAnyClothes;\RET_LethalStealth;\RiskyInspectWeapon;\ShelterHold_Beehive;\SimpleOverhaulTraitsAndOccupations;\SkillRecoveryJournal;\SPNCCFaces;\SpnHair;\TrueMoozic;\TMMMB42.13+;\TombBodyCompat;\TombBodyCustom;\TrueMusicJukebox;\TrueMusicRadio42;\TrueSmoking;\VanillaFoodsExpanded;\VanillaVehiclesAnimated;\WorkshopUpdateCheck;\zReBetterLockpickingb42mp;\CACustomWoodWeight;\LogCabin
# Map configuration per server mode
zomboid_maps:
@@ -78,6 +79,7 @@ cloud_skudak_server_name: cloud.skudakrennsport.com
fulfillr_server_name: fulfillr.debyltech.com
home_server_name: home.bdebyl.net
uptime_kuma_server_name: uptime.debyltech.com
uptime_kuma_personal_server_name: uptime.debyl.io
parts_server_name: parts.bdebyl.net
photos_server_name: photos.bdebyl.net
@@ -146,6 +148,7 @@ caddy_log_names:
- assistant
- parts
- uptime-kuma
- uptime-kuma-personal
- graylog
- cloud
- cloud-skudak

View File

@@ -0,0 +1,38 @@
---
- name: create uptime-kuma-personal host directory volumes
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: 0755
notify: restorecon podman
loop:
- "{{ uptime_kuma_personal_path }}/data"
- name: flush handlers
ansible.builtin.meta: flush_handlers
- import_tasks: podman/podman-check.yml
vars:
container_name: uptime-kuma-personal
container_image: "{{ image }}"
- name: create uptime-kuma-personal container
become: true
become_user: "{{ podman_user }}"
containers.podman.podman_container:
name: uptime-kuma-personal
image: "{{ image }}"
restart_policy: on-failure:3
log_driver: journald
volumes:
- "{{ uptime_kuma_personal_path }}/data:/app/data"
ports:
- "3002:3001/tcp"
- name: create systemd startup job for uptime-kuma-personal
include_tasks: podman/systemd-generate.yml
vars:
container_name: uptime-kuma-personal

View File

@@ -291,6 +291,7 @@
changed_when: "'already' not in firewall_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: add firewall rule to log zomboid connections (runtime)
become: true
@@ -300,6 +301,209 @@
-j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4
changed_when: false
failed_when: false
tags: firewall
# =============================================================================
# Add logging for port 16262 (mirrors existing 16261 logging)
# =============================================================================
- name: add firewall rule to log zomboid connections on 16262
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0
-p udp --dport 16262 -m conntrack --ctstate NEW
-j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4
register: firewall_result_16262
changed_when: "'already' not in firewall_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: add firewall rule to log zomboid connections on 16262 (runtime)
become: true
ansible.builtin.command: >
firewall-cmd --direct --add-rule ipv4 filter INPUT 0
-p udp --dport 16262 -m conntrack --ctstate NEW
-j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4
changed_when: false
failed_when: false
tags: firewall
# =============================================================================
# Zomboid Rate Limiting and Query Flood Protection
# =============================================================================
# These rules mitigate Steam server query floods while allowing legitimate play.
# Query packets are typically 53 bytes; game traffic is larger and sustained.
#
# Rule priority: 0=logging (existing), 1=allow established, 2=rate limit queries
# Allow established/related connections without rate limiting
# This ensures active players aren't affected by query rate limits
- name: allow established zomboid connections on 16261
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1
-p udp --dport 16261 -m conntrack --ctstate ESTABLISHED,RELATED
-j ACCEPT
register: established_result
changed_when: "'already' not in established_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: allow established zomboid connections on 16262
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1
-p udp --dport 16262 -m conntrack --ctstate ESTABLISHED,RELATED
-j ACCEPT
register: established_result_16262
changed_when: "'already' not in established_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
# =============================================================================
# Smart Zomboid Traffic Filtering (Packet-Size Based)
# =============================================================================
# Distinguishes legitimate players from scanner bots:
# - Players send varied packet sizes (53, 37, 1472 bytes)
# - Scanners only send 53-byte query packets
#
# Rule priority:
# 0 = LOG all (existing above)
# 1 = ACCEPT established (existing above)
# 2 = Mark + ACCEPT non-query packets (verifies player)
# 3 = ACCEPT queries from verified IPs
# 4 = LOG rate-limited queries from unverified IPs
# 5 = DROP rate-limited queries from unverified IPs
# Priority 2: Mark IPs sending non-query packets as verified (1 hour TTL)
# Any packet NOT 53 bytes proves actual connection attempt
- name: mark verified players on 16261 (non-query packets)
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2
-p udp --dport 16261 -m conntrack --ctstate NEW
-m length ! --length 53
-m recent --name zomboid_verified --set
-j ACCEPT
register: verify_result
changed_when: "'already' not in verify_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: mark verified players on 16262 (non-query packets)
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2
-p udp --dport 16262 -m conntrack --ctstate NEW
-m length ! --length 53
-m recent --name zomboid_verified --set
-j ACCEPT
register: verify_result_16262
changed_when: "'already' not in verify_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
# Priority 3: Allow queries from verified players (within 1 hour)
- name: allow queries from verified players on 16261
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 3
-p udp --dport 16261 -m conntrack --ctstate NEW
-m length --length 53
-m recent --name zomboid_verified --rcheck --seconds 3600
-j ACCEPT
register: verified_query_result
changed_when: "'already' not in verified_query_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: allow queries from verified players on 16262
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 3
-p udp --dport 16262 -m conntrack --ctstate NEW
-m length --length 53
-m recent --name zomboid_verified --rcheck --seconds 3600
-j ACCEPT
register: verified_query_result_16262
changed_when: "'already' not in verified_query_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
# Priority 4: LOG rate-limited queries from unverified IPs
# Very aggressive: 2 burst, then 1 per hour
# Note: Uses same hashlimit name as DROP rule to share bucket
- name: log rate-limited queries from unverified IPs on 16261
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 4
-p udp --dport 16261 -m conntrack --ctstate NEW
-m length --length 53
-m hashlimit --hashlimit-above 1/hour --hashlimit-burst 2
--hashlimit-mode srcip --hashlimit-name zomboid_query_16261
--hashlimit-htable-expire 3600000
-j LOG --log-prefix "ZOMBOID_RATELIMIT: " --log-level 4
register: unverified_log_result
changed_when: "'already' not in unverified_log_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: log rate-limited queries from unverified IPs on 16262
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 4
-p udp --dport 16262 -m conntrack --ctstate NEW
-m length --length 53
-m hashlimit --hashlimit-above 1/hour --hashlimit-burst 2
--hashlimit-mode srcip --hashlimit-name zomboid_query_16262
--hashlimit-htable-expire 3600000
-j LOG --log-prefix "ZOMBOID_RATELIMIT: " --log-level 4
register: unverified_log_result_16262
changed_when: "'already' not in unverified_log_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
# Priority 5: DROP rate-limited queries from unverified IPs
# Note: Uses same hashlimit name as LOG rule to share bucket
- name: drop rate-limited queries from unverified IPs on 16261
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 5
-p udp --dport 16261 -m conntrack --ctstate NEW
-m length --length 53
-m hashlimit --hashlimit-above 1/hour --hashlimit-burst 2
--hashlimit-mode srcip --hashlimit-name zomboid_query_16261
--hashlimit-htable-expire 3600000
-j DROP
register: unverified_drop_result
changed_when: "'already' not in unverified_drop_result.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
- name: drop rate-limited queries from unverified IPs on 16262
become: true
ansible.builtin.command: >
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 5
-p udp --dport 16262 -m conntrack --ctstate NEW
-m length --length 53
-m hashlimit --hashlimit-above 1/hour --hashlimit-burst 2
--hashlimit-mode srcip --hashlimit-name zomboid_query_16262
--hashlimit-htable-expire 3600000
-j DROP
register: unverified_drop_result_16262
changed_when: "'already' not in unverified_drop_result_16262.stderr"
failed_when: false
notify: restart firewalld
tags: firewall
# World reset is now triggered via Discord bot -> systemd path unit
# See zomboid-world-reset.path and zomboid-world-reset.service

View File

@@ -54,9 +54,9 @@
- import_tasks: containers/home/photos.yml
vars:
db_image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
ml_image: ghcr.io/immich-app/immich-machine-learning:v2.4.1
ml_image: ghcr.io/immich-app/immich-machine-learning:v2.5.0
redis_image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8
image: ghcr.io/immich-app/immich-server:v2.4.1
image: ghcr.io/immich-app/immich-server:v2.5.0
tags: photos
- import_tasks: containers/home/cloud.yml
@@ -78,8 +78,13 @@
- import_tasks: containers/debyltech/uptime-kuma.yml
vars:
image: docker.io/louislam/uptime-kuma:1
tags: debyltech, uptime-kuma
image: docker.io/louislam/uptime-kuma:2.0.2
tags: debyltech, uptime-debyltech
- import_tasks: containers/home/uptime-kuma.yml
vars:
image: docker.io/louislam/uptime-kuma:2.0.2
tags: home, uptime
- import_tasks: containers/debyltech/geoip.yml
tags: debyltech, graylog, geoip
@@ -92,7 +97,7 @@
- import_tasks: containers/home/gregtime.yml
vars:
image: localhost/greg-time-bot:3.0.1
image: localhost/greg-time-bot:3.0.2
tags: gregtime
- import_tasks: containers/home/zomboid.yml

View File

@@ -164,7 +164,7 @@
}
}
# Uptime Kuma - {{ uptime_kuma_server_name }}
# Uptime Kuma (Debyltech) - {{ uptime_kuma_server_name }}
{{ uptime_kuma_server_name }} {
{{ ip_restricted_site() }}
@@ -182,6 +182,24 @@
}
}
# Uptime Kuma (Personal) - {{ uptime_kuma_personal_server_name }}
{{ uptime_kuma_personal_server_name }} {
{{ ip_restricted_site() }}
handle @local {
import common_headers
reverse_proxy localhost:3002 {
# WebSocket support for live updates
flush_interval -1
}
}
log {
output file /var/log/caddy/uptime-kuma-personal.log
format json
}
}
# Graylog Logs - {{ logs_server_name }}
{{ logs_server_name }} {
# GELF HTTP endpoint - open for Lambda (auth via header)

View File

@@ -36,6 +36,27 @@
Read_From_Tail On
Strip_Underscores On
# =============================================================================
# INPUT: Kernel firewall logs for Zomboid rate limiting
# =============================================================================
# Captures ZOMBOID_RATELIMIT firewall events for fail2ban monitoring
[INPUT]
Name systemd
Tag firewall.zomboid.ratelimit
Systemd_Filter _TRANSPORT=kernel
Read_From_Tail On
Strip_Underscores On
# =============================================================================
# INPUT: Fail2ban actions (ban/unban events)
# =============================================================================
[INPUT]
Name systemd
Tag fail2ban.*
Systemd_Filter _SYSTEMD_UNIT=fail2ban.service
Read_From_Tail On
Strip_Underscores On
# =============================================================================
# INPUT: Caddy access logs (JSON format)
# =============================================================================
@@ -93,6 +114,27 @@
Record source firewall
Record log_type zomboid_connection
# Filter kernel logs to only keep ZOMBOID_RATELIMIT messages
[FILTER]
Name grep
Match firewall.zomboid.ratelimit
Regex MESSAGE ZOMBOID_RATELIMIT
[FILTER]
Name record_modifier
Match firewall.zomboid.ratelimit
Record host {{ ansible_hostname }}
Record source firewall
Record log_type zomboid_ratelimit
# Fail2ban ban/unban events
[FILTER]
Name record_modifier
Match fail2ban.*
Record host {{ ansible_hostname }}
Record source fail2ban
Record log_type security
# =============================================================================
# OUTPUT: All logs to Graylog GELF UDP
# =============================================================================

View File

@@ -15,3 +15,10 @@
Name zomboid_firewall
Format regex
Regex ZOMBOID_CONN:.*SRC=(?<src_ip>[0-9.]+).*DST=(?<dst_ip>[0-9.]+).*DPT=(?<dst_port>[0-9]+)
# Parse ZOMBOID_RATELIMIT firewall logs to extract source IP
# Example: ZOMBOID_RATELIMIT: IN=enp0s31f6 OUT= MAC=... SRC=45.5.113.90 DST=192.168.1.10 ...
[PARSER]
Name zomboid_ratelimit
Format regex
Regex ZOMBOID_RATELIMIT:.*SRC=(?<src_ip>[0-9.]+).*DST=(?<dst_ip>[0-9.]+).*DPT=(?<dst_port>[0-9]+)

View File

@@ -10,7 +10,7 @@
},
"tax": {
"ein": "{{ fulfillr_tax_ein }}",
"ioss": nil
"ioss": null
},
"sender_address": {
"city": "Newbury",