fix: stop the query filter banning every player who tries to connect
The anti-scanner rules dropped 53-byte Steam query packets above 1/hour with a burst of 2, per source IP, on the theory that only scanners send queries and that real players would already have been marked verified by the priority-2 rule when they sent something larger. That premise is inverted. A client's first contact with the server IS a 53-byte query, so nobody can be verified before querying, and nobody can query more than twice an hour without being dropped. The counters said so plainly: five packets had ever matched the verified-accept rule, against 30,563 drops. fail2ban then banned each dropped player for a week -- 32 live bans, 550 total, firing every ten minutes, every one of them a residential address. Keeps the shape of the protection and moves the threshold somewhere no real client reaches: opening the server browser or retrying a connection is a handful of queries, a flood is thousands. Removes the old rules first so hosts carrying them converge instead of stacking a second copy. The bans themselves were also hooked into INPUT for tcp only, so they never blocked the UDP game traffic they were meant to -- the drop rule was doing all the damage on its own. Cleared the outstanding 32. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a4bb78585b
commit
32748d1786
@@ -83,6 +83,14 @@ zomboid_mods_excluded:
|
||||
mod_id: IconsInventory
|
||||
reason: Sophie recommends removing Icon Inventory
|
||||
|
||||
# Per-source-IP ceiling on 53-byte Steam/PZ query packets before they are
|
||||
# logged and dropped. Set well above anything a real client produces -- opening
|
||||
# the server browser or retrying a connection is a handful of queries, while a
|
||||
# scanner flood is thousands. The previous 1/hour burst 2 was below normal play
|
||||
# and made the server unreachable. See containers/home/zomboid.yml.
|
||||
zomboid_query_rate_limit: 60/minute
|
||||
zomboid_query_burst: 120
|
||||
|
||||
# How long to keep PZ's own log archive under data/Logs.
|
||||
#
|
||||
# PZ rolls logs into dated directories and never prunes them; logrotate cannot
|
||||
|
||||
@@ -438,71 +438,198 @@
|
||||
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
|
||||
# Priority 4/5: query flood limiting.
|
||||
#
|
||||
# The original thresholds were 1/hour burst 2 per source IP, on the theory that
|
||||
# only scanners send 53-byte query packets and that real players would first be
|
||||
# marked verified by the priority-2 rule when they sent something else. That
|
||||
# premise is inverted: a client's *first* contact is a 53-byte query, so nobody
|
||||
# can be verified before they query, and nobody can query more than twice an
|
||||
# hour without being dropped. The counters were unambiguous -- 5 packets ever
|
||||
# matched the verified-accept rule against 30,563 drops -- and fail2ban then
|
||||
# banned the dropped players for a week each, several an hour, all residential
|
||||
# IPs. The server was unreachable for everyone.
|
||||
#
|
||||
# Remove the old rules so hosts carrying them converge, then re-add the same
|
||||
# shape at a threshold no real client reaches. Opening the server browser sends
|
||||
# a handful of queries; a flood sends thousands.
|
||||
|
||||
- name: remove broken log query rate-limit rule on 16261 (permanent)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 4
|
||||
firewall-cmd --permanent --direct --remove-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"
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
- name: log rate-limited queries from unverified IPs on 16262
|
||||
- name: remove broken log query rate-limit rule on 16261 (runtime)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 4
|
||||
-p udp --dport 16262 -m conntrack --ctstate NEW
|
||||
firewall-cmd --direct --remove-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_16262
|
||||
--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_16262
|
||||
changed_when: "'already' not in unverified_log_result_16262.stderr"
|
||||
changed_when: false
|
||||
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
|
||||
- name: remove broken drop query rate-limit rule on 16261 (permanent)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 5
|
||||
firewall-cmd --permanent --direct --remove-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"
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
- name: drop rate-limited queries from unverified IPs on 16262
|
||||
- name: remove broken drop query rate-limit rule on 16261 (runtime)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 5
|
||||
firewall-cmd --direct --remove-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
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: firewall
|
||||
|
||||
- name: remove broken log query rate-limit rule on 16262 (permanent)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --remove-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
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: firewall
|
||||
|
||||
- name: remove broken log query rate-limit rule on 16262 (runtime)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --direct --remove-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
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: firewall
|
||||
|
||||
- name: remove broken drop query rate-limit rule on 16262 (permanent)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --permanent --direct --remove-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"
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: firewall
|
||||
|
||||
- name: remove broken drop query rate-limit rule on 16262 (runtime)
|
||||
become: true
|
||||
ansible.builtin.command: >
|
||||
firewall-cmd --direct --remove-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
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: firewall
|
||||
|
||||
- name: log query floods 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 {{ zomboid_query_rate_limit }}
|
||||
--hashlimit-burst {{ zomboid_query_burst }}
|
||||
--hashlimit-mode srcip --hashlimit-name zomboid_query_16261
|
||||
--hashlimit-htable-expire 3600000
|
||||
-j LOG --log-prefix "ZOMBOID_RATELIMIT: " --log-level 4
|
||||
register: qflood_log_16261
|
||||
changed_when: "'already' not in qflood_log_16261.stderr"
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
- name: drop query floods 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 {{ zomboid_query_rate_limit }}
|
||||
--hashlimit-burst {{ zomboid_query_burst }}
|
||||
--hashlimit-mode srcip --hashlimit-name zomboid_query_16261
|
||||
--hashlimit-htable-expire 3600000
|
||||
-j DROP
|
||||
register: qflood_drop_16261
|
||||
changed_when: "'already' not in qflood_drop_16261.stderr"
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
- name: log query floods 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 {{ zomboid_query_rate_limit }}
|
||||
--hashlimit-burst {{ zomboid_query_burst }}
|
||||
--hashlimit-mode srcip --hashlimit-name zomboid_query_16262
|
||||
--hashlimit-htable-expire 3600000
|
||||
-j LOG --log-prefix "ZOMBOID_RATELIMIT: " --log-level 4
|
||||
register: qflood_log_16262
|
||||
changed_when: "'already' not in qflood_log_16262.stderr"
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
- name: drop query floods 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 {{ zomboid_query_rate_limit }}
|
||||
--hashlimit-burst {{ zomboid_query_burst }}
|
||||
--hashlimit-mode srcip --hashlimit-name zomboid_query_16262
|
||||
--hashlimit-htable-expire 3600000
|
||||
-j DROP
|
||||
register: qflood_drop_16262
|
||||
changed_when: "'already' not in qflood_drop_16262.stderr"
|
||||
failed_when: false
|
||||
notify: restart firewalld
|
||||
tags: firewall
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
- import_tasks: containers/home/gregtime.yml
|
||||
vars:
|
||||
image: localhost/greg-time-bot:3.15.0
|
||||
image: localhost/greg-time-bot:3.16.1
|
||||
tags: gregtime
|
||||
|
||||
# Gated on zomboid_enabled (roles/podman/defaults/main.yml) so it can be taken
|
||||
|
||||
Reference in New Issue
Block a user