The reset script died on its very first line. log() piped through tee into zomboid/logs/world-reset.log, that directory was owned by the container's subuid, and the script runs as the podman user -- so tee returned EACCES and set -e killed the run before it stopped the server or touched a save. Every `@bot` reset since had been a no-op that reported nothing. Nothing mounts zomboid/logs into a container; it only holds output from host-side helpers running as the podman user, so it is now owned by that user rather than by the subuid the container volumes need. log() no longer treats the file as load-bearing either. stdout is already captured by the journal, so an unwritable log is worth continuing past rather than aborting a wipe over. Verified end to end by writing the trigger exactly as the bot does: server stopped, saves and player database deleted, service restarted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
688 lines
24 KiB
YAML
688 lines
24 KiB
YAML
---
|
|
- name: load vendored sophie modlist
|
|
ansible.builtin.include_vars:
|
|
file: zomboid_sophie_mods.yml
|
|
tags: zomboid-conf
|
|
|
|
- name: create zomboid host directory volumes
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: "{{ item }}"
|
|
state: directory
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0755
|
|
notify: restorecon podman
|
|
loop:
|
|
- "{{ zomboid_path }}/server"
|
|
- "{{ zomboid_path }}/data"
|
|
- "{{ zomboid_path }}/scripts"
|
|
|
|
# Not a container volume -- nothing mounts it. It holds output from host-side
|
|
# helpers that run as {{ podman_user }}, so it must be owned by that user rather
|
|
# than the container's subuid. Getting this wrong silently broke world resets:
|
|
# the script's first log line failed with EACCES and set -e killed it before it
|
|
# stopped the server, so `@bot` resets did nothing at all.
|
|
- name: create zomboid host-side log directory
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: "{{ zomboid_path }}/logs"
|
|
state: directory
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0755
|
|
|
|
- name: create podman bin directory
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: "{{ podman_home }}/bin"
|
|
state: directory
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0755'
|
|
|
|
- name: deploy zomboid world reset script
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/world-reset.sh.j2
|
|
dest: "{{ podman_home }}/bin/zomboid-world-reset.sh"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0755'
|
|
|
|
- name: deploy zomboid world reset path unit
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-world-reset.path.j2
|
|
dest: "{{ podman_home }}/.config/systemd/user/zomboid-world-reset.path"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
notify: reload zomboid systemd
|
|
|
|
- name: deploy zomboid world reset service unit
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-world-reset.service.j2
|
|
dest: "{{ podman_home }}/.config/systemd/user/zomboid-world-reset.service"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
notify: reload zomboid systemd
|
|
|
|
- name: deploy zomboid stats script
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-stats.sh.j2
|
|
dest: "{{ podman_home }}/bin/zomboid-stats.sh"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0755'
|
|
|
|
- name: create zomboid stats file with correct permissions
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: "{{ podman_volumes }}/zomboid-stats.json"
|
|
state: touch
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
modification_time: preserve
|
|
access_time: preserve
|
|
|
|
- name: deploy zomboid stats service unit
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-stats.service.j2
|
|
dest: "{{ podman_home }}/.config/systemd/user/zomboid-stats.service"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
notify: reload zomboid systemd
|
|
|
|
- name: deploy zomboid stats timer unit
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-stats.timer.j2
|
|
dest: "{{ podman_home }}/.config/systemd/user/zomboid-stats.timer"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
notify: reload zomboid systemd
|
|
|
|
- name: enable zomboid stats timer
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
ansible.builtin.systemd:
|
|
name: zomboid-stats.timer
|
|
scope: user
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
- name: copy zomboid entrypoint script
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/entrypoint.sh.j2
|
|
dest: "{{ zomboid_path }}/scripts/entrypoint.sh"
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0755
|
|
notify: restorecon podman
|
|
|
|
- name: copy zomboid steamcmd install script
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/install.scmd.j2
|
|
dest: "{{ zomboid_path }}/scripts/install.scmd"
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0644
|
|
notify: restorecon podman
|
|
|
|
# Server config is seeded from the vendored Sophie 42 preset *before* first
|
|
# boot, so the server never generates a vanilla INI we then have to patch.
|
|
#
|
|
# force is off by design: these files are a starting point, not a managed
|
|
# state. Stop the server, hand-edit them, regenerate the world -- Ansible will
|
|
# not clobber the edits on the next deploy. Re-push deliberately with
|
|
# -e zomboid_config_force=true.
|
|
- name: create zomboid server config directory
|
|
become: true
|
|
ansible.builtin.file:
|
|
path: "{{ zomboid_path }}/data/Server"
|
|
state: directory
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0755
|
|
notify: restorecon podman
|
|
tags: zomboid-conf
|
|
|
|
- name: seed zomboid server ini from sophie preset
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/server.ini.j2
|
|
dest: "{{ zomboid_path }}/data/Server/{{ zomboid_server_name }}.ini"
|
|
force: "{{ zomboid_config_force | bool }}"
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0644
|
|
notify: restorecon podman
|
|
tags: zomboid-conf
|
|
|
|
# Copied, not templated: these are Lua and must not go through Jinja.
|
|
- name: seed zomboid sandbox and spawn config from sophie preset
|
|
become: true
|
|
ansible.builtin.copy:
|
|
src: "zomboid/sophie/{{ item }}.lua"
|
|
dest: "{{ zomboid_path }}/data/Server/{{ zomboid_server_name }}_{{ item }}.lua"
|
|
force: "{{ zomboid_config_force | bool }}"
|
|
owner: "{{ podman_subuid.stdout }}"
|
|
group: "{{ podman_user }}"
|
|
mode: 0644
|
|
loop:
|
|
- SandboxVars
|
|
- spawnregions
|
|
- spawnpoints
|
|
notify: restorecon podman
|
|
tags: zomboid-conf
|
|
|
|
- name: deploy zomboid log prune script
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/zomboid-log-prune.sh.j2
|
|
dest: "{{ podman_home }}/bin/zomboid-log-prune.sh"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0755'
|
|
|
|
- name: deploy zomboid log prune units
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: "zomboid/zomboid-log-prune.{{ item }}.j2"
|
|
dest: "{{ podman_home }}/.config/systemd/user/zomboid-log-prune.{{ item }}"
|
|
owner: "{{ podman_user }}"
|
|
group: "{{ podman_user }}"
|
|
mode: '0644'
|
|
loop:
|
|
- service
|
|
- timer
|
|
notify: reload zomboid systemd
|
|
|
|
- name: enable zomboid log prune timer
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
ansible.builtin.systemd:
|
|
name: zomboid-log-prune.timer
|
|
scope: user
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
# The server's own logs, not the container's. PZ holds these fds open for the
|
|
# life of the process, so copytruncate is mandatory -- a rename would leave it
|
|
# writing into an unlinked inode and the file would appear to stop growing.
|
|
- name: deploy zomboid logrotate config
|
|
become: true
|
|
ansible.builtin.template:
|
|
src: zomboid/logrotate.j2
|
|
dest: /etc/logrotate.d/zomboid
|
|
owner: root
|
|
group: root
|
|
mode: 0644
|
|
|
|
# Set volume permissions for steam user (UID 1000) inside container
|
|
# This uses podman unshare to set ownership correctly for rootless podman
|
|
- name: set zomboid volume permissions for steam user
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
ansible.builtin.shell: |
|
|
podman unshare chown -R 1000:1000 {{ zomboid_path }}/server
|
|
podman unshare chown -R 1000:1000 {{ zomboid_path }}/data
|
|
changed_when: false
|
|
|
|
- name: flush handlers
|
|
ansible.builtin.meta: flush_handlers
|
|
|
|
- import_tasks: podman/podman-check.yml
|
|
vars:
|
|
container_name: zomboid
|
|
container_image: "{{ image }}"
|
|
|
|
- name: create zomboid container
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
containers.podman.podman_container:
|
|
name: zomboid
|
|
image: "{{ image }}"
|
|
restart_policy: on-failure:3
|
|
log_driver: k8s-file
|
|
log_opt:
|
|
max_size: 50m
|
|
env:
|
|
SERVER_NAME: "{{ zomboid_server_name }}"
|
|
MIN_RAM: "{{ zomboid_min_ram }}"
|
|
MAX_RAM: "{{ zomboid_max_ram }}"
|
|
AUTO_UPDATE: "true"
|
|
ADMIN_PASSWORD: "{{ zomboid_admin_password }}"
|
|
SERVER_PASSWORD: "{{ zomboid_password }}"
|
|
PUID: "1000"
|
|
PGID: "1000"
|
|
volumes:
|
|
- "{{ zomboid_path }}/server:/project-zomboid"
|
|
- "{{ zomboid_path }}/data:/project-zomboid-config"
|
|
- "{{ zomboid_path }}/scripts/entrypoint.sh:/entrypoint.sh:ro"
|
|
- "{{ zomboid_path }}/scripts/install.scmd:/home/steam/install.scmd:ro"
|
|
ports:
|
|
- "16261:16261/udp"
|
|
- "16262:16262/udp"
|
|
- "{{ zomboid_rcon_port }}:{{ zomboid_rcon_port }}/tcp"
|
|
command: /bin/bash /entrypoint.sh
|
|
|
|
- name: create systemd startup job for zomboid
|
|
include_tasks: podman/systemd-generate.yml
|
|
vars:
|
|
container_name: zomboid
|
|
|
|
# Make systemd actually supervise the container, so it restarts on any exit --
|
|
# including the clean one that `@bot restart` produces via RCON quit.
|
|
#
|
|
# `podman generate systemd` emits Type=forking with ExecStart=podman start and
|
|
# a PIDFile pointing at conmon. podman start returns immediately, so the process
|
|
# systemd is told to watch was never its child; it says so itself in the journal
|
|
# ("Supervising process N which is not our child. We'll most likely not notice
|
|
# when it exits") and it does not notice. The unit sat at active/running with
|
|
# NRestarts=0 while the container was exited(0) and the server was down. The
|
|
# PIDFile is worse than useless here: it embeds the container ID, so it goes
|
|
# stale every time a deploy recreates the container.
|
|
#
|
|
# Type=simple with `podman start -a` keeps podman in the foreground as systemd's
|
|
# own child, so the exit is seen and Restart=always fires.
|
|
#
|
|
# stdout/stderr are discarded on purpose. Attaching re-emits the container's
|
|
# output on podman's stdout, which systemd would capture straight back into the
|
|
# journal -- exactly the flood the k8s-file log driver exists to avoid. Nothing
|
|
# is lost: `podman logs` still serves it from the container's own log.
|
|
- name: configure zomboid systemd supervision
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ podman_home }}/.config/systemd/user/zomboid.service"
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
state: "{{ item.state | default('present') }}"
|
|
insertafter: '^\[Service\]'
|
|
loop:
|
|
- { regexp: '^Restart=', line: 'Restart=always' }
|
|
- { regexp: '^Type=', line: 'Type=simple' }
|
|
- { regexp: '^ExecStart=', line: 'ExecStart=/usr/bin/podman start -a zomboid' }
|
|
- { regexp: '^StandardOutput=', line: 'StandardOutput=null' }
|
|
- { regexp: '^StandardError=', line: 'StandardError=null' }
|
|
- { regexp: '^PIDFile=', line: '', state: absent }
|
|
notify: reload zomboid systemd
|
|
|
|
# Firewall logging for player IP correlation
|
|
# Logs new UDP connections to Zomboid port for IP address tracking
|
|
- name: add firewall rule to log zomboid connections
|
|
become: true
|
|
ansible.builtin.command: >
|
|
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0
|
|
-p udp --dport 16261 -m conntrack --ctstate NEW
|
|
-j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4
|
|
register: firewall_result
|
|
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
|
|
ansible.builtin.command: >
|
|
firewall-cmd --direct --add-rule ipv4 filter INPUT 0
|
|
-p udp --dport 16261 -m conntrack --ctstate NEW
|
|
-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/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 --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
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: firewall
|
|
|
|
- name: remove broken log query rate-limit rule on 16261 (runtime)
|
|
become: true
|
|
ansible.builtin.command: >
|
|
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_16261
|
|
--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 16261 (permanent)
|
|
become: true
|
|
ansible.builtin.command: >
|
|
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
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: firewall
|
|
|
|
- name: remove broken drop query rate-limit rule on 16261 (runtime)
|
|
become: true
|
|
ansible.builtin.command: >
|
|
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
|
|
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
|
|
|
|
# World reset is now triggered via Discord bot -> systemd path unit
|
|
# See zomboid-world-reset.path and zomboid-world-reset.service
|
|
- name: enable zomboid world reset path unit
|
|
become: true
|
|
become_user: "{{ podman_user }}"
|
|
ansible.builtin.systemd:
|
|
name: zomboid-world-reset.path
|
|
scope: user
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|