diff --git a/ansible/roles/podman/defaults/main.yml b/ansible/roles/podman/defaults/main.yml index d2f7d67..5ecd67e 100644 --- a/ansible/roles/podman/defaults/main.yml +++ b/ansible/roles/podman/defaults/main.yml @@ -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" +zomboid_path: "{{ podman_volumes }}/zomboid" pihole_path: "{{ podman_volumes }}/pihole" sshpass_cron_path: "{{ podman_volumes }}/sshpass_cron" caddy_path: "{{ podman_volumes }}/caddy" @@ -35,6 +36,14 @@ uptime_kuma_server_name: uptime.debyltech.com parts_server_name: parts.bdebyl.net photos_server_name: photos.bdebyl.net +# debyl.io domains (migration from bdebyl.net) +base_server_name_io: debyl.io +assistant_server_name_io: assistant.debyl.io +cloud_server_name_io: cloud.debyl.io +home_server_name_io: home.debyl.io +parts_server_name_io: parts.debyl.io +photos_server_name_io: photos.debyl.io + # Legacy nginx/ModSecurity configuration removed - Caddy provides built-in security # Web server configuration (Caddy is the default) diff --git a/ansible/roles/podman/handlers/main.yml b/ansible/roles/podman/handlers/main.yml index 88cd92c..fa095dd 100644 --- a/ansible/roles/podman/handlers/main.yml +++ b/ansible/roles/podman/handlers/main.yml @@ -33,3 +33,12 @@ tags: - caddy - caddy-config + +- name: reload zomboid systemd + become: true + become_user: "{{ podman_user }}" + ansible.builtin.systemd: + daemon_reload: true + scope: user + tags: + - zomboid diff --git a/ansible/roles/podman/tasks/containers/base/awsddns.yml b/ansible/roles/podman/tasks/containers/base/awsddns.yml index 792e6ef..74dc68a 100644 --- a/ansible/roles/podman/tasks/containers/base/awsddns.yml +++ b/ansible/roles/podman/tasks/containers/base/awsddns.yml @@ -78,4 +78,31 @@ - name: create systemd startup job for awsddns-fulfillr include_tasks: podman/systemd-generate.yml vars: - container_name: awsddns-fulfillr \ No newline at end of file + container_name: awsddns-fulfillr + +- import_tasks: podman/podman-check.yml + vars: + container_name: awsddns-debyl + container_image: "{{ image }}" + +- name: create home.debyl.io awsddns server container + become: true + become_user: "{{ podman_user }}" + diff: false + containers.podman.podman_container: + name: awsddns-debyl + image: "{{ image }}" + restart_policy: on-failure:3 + log_driver: journald + env: + AWS_ZONE_TTL: 60 + AWS_ZONE_ID: "Z07501202A6AYMHCVP50A" + AWS_ZONE_HOSTNAME: "home.debyl.io" + AWS_ACCESS_KEY_ID: "{{ aws_access_key_id }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_access_key }}" + AWS_DEFAULT_REGION: "{{ aws_default_region }}" + +- name: create systemd startup job for awsddns-debyl + include_tasks: podman/systemd-generate.yml + vars: + container_name: awsddns-debyl \ No newline at end of file diff --git a/ansible/roles/podman/tasks/containers/home/zomboid.yml b/ansible/roles/podman/tasks/containers/home/zomboid.yml new file mode 100644 index 0000000..ed60c32 --- /dev/null +++ b/ansible/roles/podman/tasks/containers/home/zomboid.yml @@ -0,0 +1,106 @@ +--- +- 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" + +- 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: 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: journald + env: + SERVER_NAME: zomboid + MIN_RAM: 8g + MAX_RAM: 24g + AUTO_UPDATE: "true" + ADMIN_PASSWORD: "{{ zomboid_admin_password }}" + SERVER_PASSWORD: "{{ zomboid_password }}" + volumes: + - "{{ zomboid_path }}/server:/home/steam/pzserver" + - "{{ zomboid_path }}/data:/home/steam/Zomboid" + - "{{ zomboid_path }}/scripts/entrypoint.sh:/entrypoint.sh:ro" + ports: + - "16261:16261/udp" + - "16262:16262/udp" + command: /bin/bash /entrypoint.sh + +- name: create systemd startup job for zomboid + include_tasks: podman/systemd-generate.yml + vars: + container_name: zomboid + +# Ensure zomboid restarts on any exit (including admin-triggered restarts) +- name: configure zomboid systemd to always restart + become: true + become_user: "{{ podman_user }}" + ansible.builtin.lineinfile: + path: "{{ podman_home }}/.config/systemd/user/zomboid.service" + regexp: "^Restart=" + line: "Restart=always" + notify: reload zomboid systemd + +# Configuration management (requires server to have run once to generate ini) +- name: configure zomboid server settings + become: true + ansible.builtin.lineinfile: + path: "{{ zomboid_path }}/data/Server/zomboid.ini" + regexp: "^{{ item.key }}=" + line: "{{ item.key }}={{ item.value }}" + loop: + - { key: "PublicName", value: "Modded Joboid" } + - { key: "MaxPlayers", value: "8" } + - { key: "Password", value: "{{ zomboid_password }}" } + - { key: "Mods", value: "PzkVanillaPlusCarPack;PZKExtendedVehicleZones;PZKCarzoneWorkshop;Pogo;Pogo;Pogo;LethalHeadHit;VanillaFoodsExpanded;VanillaFoodsExpanded;RebalancedPropMoving;GaelGunStore_B42;STA_PryOpen;tsarslib;Ahu;Ahu;Ahu;ModernStatus;StandardizedVehicleUpgrades3V;StandardizedVehicleUpgrades3Core;survivingthroughseasons;survivingthroughseasons;RVInteriorExpansionPart2;RVInteriorExpansion;TchernoLib;HereGoesTheSun;hf_point_blank;WayMoreCars;WaterGoesBad;WaterGoesBad;PROJECTRVInterior42;ClimbWall;amclub;RepairableWindows;RepairableWindows;StarlitLibrary;StarlitLibrary;StarlitLibrary;ImmersiveBlackouts;ModLoadOrderSorter_b42;NeatUI_Framework;SomewhatWater;SomewhatWaterBright;VanillaVehiclesAnimated;VanillaVehiclesAnimated_SVU;VVA_nascarlights;VVA_cullseats;VVA_slowdoors;kitsunelib;ChuckleberryFinnAlertSystem;ImmersiveVehiclePaint;darkerMap;SLDarkerSnowB42;BecomeBraveB42;Louisville spawn v42;ItemretexturePSC" } + - { key: "WorkshopItems", value: "3217685049;3058134369;3543588687;3577903007;2699828474;3616176188;3579640010;3402491515;3422418897;3451167732;3304582091;3403490889;2753086629;3622163276;3618427553;3389605231;3618557184;2990322197;3520758551;2849467715;3543229299;3389681224;3404737883;3378304610;3378285185;3607686447;3423660713;3508537032;3582960654;3281755175;3390453390;3077900375;3464606086;2939187818;3390411200;3388028737;3387071727;3618491765" } + tags: zomboid-conf + +# Sandbox settings (requires world reset to take effect) +- name: configure zomboid sandbox settings + become: true + ansible.builtin.lineinfile: + path: "{{ zomboid_path }}/data/Server/zomboid_SandboxVars.lua" + regexp: "^\\s*{{ item.key }} = " + line: " {{ item.key }} = {{ item.value }}," + backrefs: false + loop: + - { key: "StartMonth", value: "12" } + - { key: "StartDay", value: "15" } + tags: zomboid-conf + +# World reset tasks REMOVED - too dangerous to have in automation +# To reset the world manually: +# 1. Stop the server: systemctl --user stop zomboid.service +# 2. Delete saves: rm -rf /home/podman/.local/share/volumes/zomboid/data/Saves +# 3. Delete db: rm -rf /home/podman/.local/share/volumes/zomboid/data/db +# 4. Start the server: systemctl --user start zomboid.service diff --git a/ansible/roles/podman/tasks/main.yml b/ansible/roles/podman/tasks/main.yml index f494b96..c50cefc 100644 --- a/ansible/roles/podman/tasks/main.yml +++ b/ansible/roles/podman/tasks/main.yml @@ -88,5 +88,10 @@ - import_tasks: containers/home/gregtime.yml vars: - image: localhost/greg-time-bot:1.3.1 - tags: gregtime \ No newline at end of file + image: localhost/greg-time-bot:1.3.2 + tags: gregtime + +- import_tasks: containers/home/zomboid.yml + vars: + image: docker.io/cm2network/steamcmd:root + tags: zomboid \ No newline at end of file diff --git a/ansible/roles/podman/templates/caddy/Caddyfile.j2 b/ansible/roles/podman/templates/caddy/Caddyfile.j2 index 7bd4855..b216960 100644 --- a/ansible/roles/podman/templates/caddy/Caddyfile.j2 +++ b/ansible/roles/podman/templates/caddy/Caddyfile.j2 @@ -53,20 +53,25 @@ # SITE CONFIGURATIONS # ============================================================================ -# Simple redirect: {{ base_server_name }} -> debyl.io +# Simple redirect: {{ base_server_name }} -> {{ base_server_name_io }} {{ base_server_name }} { - redir https://debyl.io permanent + redir https://{{ base_server_name_io }}{uri} 302 } # ============================================================================ # SIMPLE REVERSE PROXIES # ============================================================================ -# Photos service - {{ photos_server_name }} +# Photos service - redirect old to new {{ photos_server_name }} { + redir https://{{ photos_server_name_io }}{uri} 302 +} + +# Photos service - {{ photos_server_name_io }} +{{ photos_server_name_io }} { import common_headers reverse_proxy localhost:8088 - + log { output file /var/log/caddy/photos.log format json @@ -89,23 +94,28 @@ # IP-RESTRICTED SITES # ============================================================================ -# Home Assistant - {{ assistant_server_name }} +# Home Assistant - redirect old to new {{ assistant_server_name }} { + redir https://{{ assistant_server_name_io }}{uri} 302 +} + +# Home Assistant - {{ assistant_server_name_io }} +{{ assistant_server_name_io }} { {{ ip_restricted_site() }} - + handle @local { reverse_proxy localhost:8123 { # WebSocket support is automatic flush_interval -1 } - + header { Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" Referrer-Policy "same-origin" } } - + log { output file /var/log/caddy/assistant.log format json @@ -115,8 +125,13 @@ # CI/Drone - REMOVED # ci.bdebyl.net configuration removed - Drone CI infrastructure decommissioned -# Home server - {{ home_server_name }} +# Home server - redirect old to new {{ home_server_name }} { + redir https://{{ home_server_name_io }}{uri} 302 +} + +# Home server - {{ home_server_name_io }} +{{ home_server_name_io }} { {{ ip_restricted_site() }} handle @local { @@ -124,8 +139,13 @@ } } -# Parts/Partsy - {{ parts_server_name }} +# Parts/Partsy - redirect old to new {{ parts_server_name }} { + redir https://{{ parts_server_name_io }}{uri} 302 +} + +# Parts/Partsy - {{ parts_server_name_io }} +{{ parts_server_name_io }} { {{ ip_restricted_site() }} handle @local { @@ -161,29 +181,34 @@ # COMPLEX CONFIGURATIONS # ============================================================================ -# Nextcloud - {{ cloud_server_name }} +# Nextcloud - redirect old to new {{ cloud_server_name }} { + redir https://{{ cloud_server_name_io }}{uri} 302 +} + +# Nextcloud - {{ cloud_server_name_io }} +{{ cloud_server_name_io }} { request_body { max_size {{ caddy_max_request_body_mb }}MB } - + reverse_proxy localhost:8089 { header_up Host {host} header_up X-Real-IP {remote} # X-Forwarded-For and X-Forwarded-Proto are automatic } - + header { Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" Referrer-Policy "same-origin" -X-Powered-By } - + # Nextcloud specific redirects redir /.well-known/carddav /remote.php/dav 301 redir /.well-known/caldav /remote.php/dav 301 - + log { output file /var/log/caddy/cloud.log format json diff --git a/ansible/roles/podman/templates/zomboid/entrypoint.sh.j2 b/ansible/roles/podman/templates/zomboid/entrypoint.sh.j2 new file mode 100644 index 0000000..6330790 --- /dev/null +++ b/ansible/roles/podman/templates/zomboid/entrypoint.sh.j2 @@ -0,0 +1,103 @@ +#!/bin/bash +set -e + +STEAMCMD="/home/steam/steamcmd/steamcmd.sh" +INSTALL_DIR="/home/steam/pzserver" +DATA_DIR="/home/steam/Zomboid" +SERVER_NAME="${SERVER_NAME:-zomboid}" +MIN_RAM="${MIN_RAM:-8g}" +MAX_RAM="${MAX_RAM:-24g}" + +echo "=== Project Zomboid Build 42 Server ===" +echo "Server Name: ${SERVER_NAME}" +echo "RAM: ${MIN_RAM} - ${MAX_RAM}" + +# Fix ownership of mounted volumes (container runs as steam user, UID 1000) +echo "=== Fixing volume permissions ===" +chown -R steam:steam "${INSTALL_DIR}" || true +chown -R steam:steam "${DATA_DIR}" || true +chmod -R 755 "${INSTALL_DIR}" || true +chmod -R 755 "${DATA_DIR}" || true + +# Create required subdirectories with correct ownership +mkdir -p "${DATA_DIR}/Server" +mkdir -p "${DATA_DIR}/Saves/Multiplayer" +mkdir -p "${DATA_DIR}/db" +chown -R steam:steam "${DATA_DIR}" + +# Ensure steam user has proper home directory setup +export HOME=/home/steam + +# Initialize SteamCMD if needed (creates config directories) +if [ ! -d "/home/steam/Steam" ]; then + echo "=== Initializing SteamCMD ===" + su -c "${STEAMCMD} +quit" steam || true +fi + +# Update/Install PZ dedicated server with Build 42 unstable branch +if [ "${AUTO_UPDATE:-true}" = "true" ]; then + echo "=== Updating Project Zomboid Server (Build 42 unstable) ===" + # Run steamcmd as steam user with proper quoting for beta flag + su -c "${STEAMCMD} +force_install_dir ${INSTALL_DIR} +login anonymous +app_update 380870 -beta unstable validate +quit" steam + echo "=== Update complete ===" +fi + +# Ensure data directories exist (created earlier with correct permissions) + +# Configure server settings on first run +SERVER_INI="${DATA_DIR}/Server/${SERVER_NAME}.ini" +if [ ! -f "${SERVER_INI}" ]; then + echo "=== First run detected, server will generate default config ===" +fi + +# Handle admin password for first run +# PZ requires interactive password input on first run, so we create a db file +ADMIN_DB="${DATA_DIR}/db/${SERVER_NAME}.db" +if [ ! -f "${ADMIN_DB}" ] && [ -n "${ADMIN_PASSWORD}" ]; then + echo "=== Setting up admin account ===" + mkdir -p "${DATA_DIR}/db" + # The server will prompt for password on first run + # We'll use expect-like behavior or let it use defaults +fi + +# Modify memory settings in ProjectZomboid64.json (Build 42 uses JSON config) +PZ_JSON="${INSTALL_DIR}/ProjectZomboid64.json" +if [ -f "${PZ_JSON}" ]; then + echo "=== Setting JVM memory: Xms=${MIN_RAM}, Xmx=${MAX_RAM} ===" + # Add -Xms if not present, otherwise update it + if grep -q "\-Xms" "${PZ_JSON}"; then + sed -i "s/-Xms[0-9]*[gGmM]*/-Xms${MIN_RAM}/g" "${PZ_JSON}" + else + # Insert -Xms before -Xmx + sed -i "s/\"-Xmx/\"-Xms${MIN_RAM}\",\n\t\t\"-Xmx/g" "${PZ_JSON}" + fi + sed -i "s/-Xmx[0-9]*[gGmM]*/-Xmx${MAX_RAM}/g" "${PZ_JSON}" +fi + +# If server password is set, we'll need to configure it in the ini after first run +# For now, store it for later configuration +if [ -n "${SERVER_PASSWORD}" ]; then + echo "${SERVER_PASSWORD}" > "${DATA_DIR}/.server_password" +fi + +if [ -n "${ADMIN_PASSWORD}" ]; then + echo "${ADMIN_PASSWORD}" > "${DATA_DIR}/.admin_password" +fi + +# Change to install directory and start server +cd "${INSTALL_DIR}" + +echo "=== Starting Project Zomboid Server ===" +echo "Connect to: home.bdebyl.net:16261" + +# Start server - on first run this will prompt for admin password +# We handle this by providing input via stdin if password file exists +if [ -f "${DATA_DIR}/.admin_password" ] && [ ! -f "${ADMIN_DB}" ]; then + # First run with admin password + ADMIN_PASS=$(cat "${DATA_DIR}/.admin_password") + echo "=== First run: setting admin password ===" + printf "%s\n%s\n" "${ADMIN_PASS}" "${ADMIN_PASS}" | su -c "bash start-server.sh -servername ${SERVER_NAME}" steam +else + # Normal run + exec su -c "bash start-server.sh -servername ${SERVER_NAME}" steam +fi diff --git a/ansible/vars/vault.yml b/ansible/vars/vault.yml index b018e53..be73d20 100644 Binary files a/ansible/vars/vault.yml and b/ansible/vars/vault.yml differ