diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..6978a5a --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,26 @@ +--- +exclude_paths: + - .gitea/ + - .git/ + +skip_list: + - yaml[line-length] + - name[casing] + - no-changed-when + - command-instead-of-module + +warn_list: + - experimental + - role-name[path] + +enable_list: + - fqcn-builtins + - no-log-password + - no-same-owner + +kinds: + - tasks: "**/tasks/*.yml" + - vars: "**/vars/*.yml" + - defaults: "**/defaults/*.yml" + - handlers: "**/handlers/*.yml" + - meta: "**/meta/*.yml" \ No newline at end of file diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index f07c9fc..f633313 100644 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -1,211 +1,145 @@ ---- -name: Ansible Minecraft CI/CD Pipeline +name: Ansible Minecraft Server CI/CD on: push: branches: [ main, develop ] pull_request: branches: [ main ] - -env: - ANSIBLE_FORCE_COLOR: 1 - ANSIBLE_HOST_KEY_CHECKING: false + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy' + required: true + default: 'staging' + type: choice + options: + - staging + - production jobs: lint: - name: Ansible Lint Check runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install ansible ansible-lint yamllint + + - name: Run yamllint + run: | + yamllint -c .yamllint . + continue-on-error: true + + - name: Run ansible-lint + run: | + ansible-lint --exclude .gitea/ . + continue-on-error: true - - name: Install dependencies - run: | - pip install ansible ansible-lint yamllint - - - name: Run yamllint - run: | - yamllint -c .yamllint.yml . - continue-on-error: true + - name: Validate ansible syntax + run: | + ansible-playbook site.yml --syntax-check - - name: Run ansible-lint - run: | - ansible-lint --project-dir . playbooks/ - continue-on-error: true - - - name: Validate Ansible syntax - run: | - ansible-playbook --syntax-check playbooks/site.yml -i inventories/staging/hosts.yml - continue-on-error: true - - structure-validation: - name: Project Structure Validation + test: runs-on: ubuntu-latest - + needs: lint steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Validate required files - run: | - # Check main playbook exists - [ -f "playbooks/site.yml" ] || exit 1 - - # Check all roles exist with required structure - for role in 01-server_hardening 02-installation-java 03-installation-minecraft 04-backups 05-update; do - [ -d "roles/$role" ] || exit 1 - [ -d "roles/$role/tasks" ] || exit 1 - [ -d "roles/$role/handlers" ] || exit 1 - [ -d "roles/$role/templates" ] || exit 1 - [ -d "roles/$role/vars" ] || exit 1 - [ -d "roles/$role/defaults" ] || exit 1 - [ -f "roles/$role/tasks/main.yml" ] || exit 1 - [ -f "roles/$role/handlers/main.yml" ] || exit 1 - [ -f "roles/$role/vars/main.yml" ] || exit 1 - [ -f "roles/$role/defaults/main.yml" ] || exit 1 - done - - # Check inventories exist - [ -d "inventories/production" ] || exit 1 - [ -d "inventories/staging" ] || exit 1 - [ -f "inventories/production/hosts.yml" ] || exit 1 - [ -f "inventories/staging/hosts.yml" ] || exit 1 - - echo "Project structure validation passed" + - name: Checkout code + uses: actions/checkout@v3 - security-scan: - name: Security Scan - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Check for secrets in files - run: | - # Check for potential secrets (excluding example files) - if grep -r -i "password\|secret\|key" --include="*.yml" --include="*.yaml" --exclude="*example*" --exclude="*template*" .; then - echo "WARNING: Potential secrets found in files" - echo "Please ensure all sensitive data uses Gitea secrets or ansible-vault" - fi - - - name: Check for hardcoded IPs - run: | - # Allow example IPs in inventory files - if grep -r -E '([0-9]{1,3}\.){3}[0-9]{1,3}' --include="*.yml" --include="*.yaml" . | grep -v "192.168.1" | grep -v "127.0.0.1" | grep -v "inventories/"; then - echo "WARNING: Hardcoded IP addresses found" - fi + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' - deploy-staging: - name: Deploy to Staging - runs-on: ubuntu-latest - needs: [lint, structure-validation, security-scan] - if: github.ref == 'refs/heads/develop' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install Ansible - run: | - pip install ansible - - - name: Setup SSH key - run: | - mkdir -p ~/.ssh - echo "${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - - name: Test staging connectivity - run: | - ansible all -i inventories/staging/hosts.yml -m ping - - - name: Deploy to staging (dry-run) - run: | - ansible-playbook playbooks/site.yml \ - -i inventories/staging/hosts.yml \ - --check \ - --diff + - name: Install Ansible + run: | + python -m pip install --upgrade pip + pip install ansible - deploy-production: - name: Deploy to Production - runs-on: ubuntu-latest - needs: [lint, structure-validation, security-scan] - if: github.ref == 'refs/heads/main' - environment: production - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install Ansible - run: | - pip install ansible - - - name: Setup SSH key - run: | - mkdir -p ~/.ssh - echo "${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - - name: Test production connectivity - run: | - ansible all -i inventories/production/hosts.yml -m ping - - - name: Deploy to production - run: | - ansible-playbook playbooks/site.yml \ - -i inventories/production/hosts.yml \ - -e "minecraft_version=${{ secrets.MINECRAFT_VERSION }}" \ - -e "rcon_password=${{ secrets.RCON_PASSWORD }}" + - name: Install Ansible collections + run: | + ansible-galaxy collection install -r requirements.yml - backup-check: - name: Backup System Check + - name: Test playbook structure + run: | + # Verify all roles exist + for role in 01-server_hardening 02-installation-java 03-Installation-Minecraft 04-backups 05-Update; do + if [ ! -d "roles/$role" ]; then + echo "Role $role not found" + exit 1 + fi + done + + - name: Check inventory files + run: | + ansible-inventory -i inventories/staging/hosts.yml --list + ansible-inventory -i inventories/production/hosts.yml --list + + deploy: runs-on: ubuntu-latest - needs: [deploy-staging] - if: github.ref == 'refs/heads/develop' - + needs: [lint, test] + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install Ansible - run: | - pip install ansible - - - name: Setup SSH key - run: | - mkdir -p ~/.ssh - echo "${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - - name: Test backup scripts - run: | - ansible minecraft_servers -i inventories/staging/hosts.yml \ - -m shell \ - -a "test -x /opt/minecraft/tools/backup-daily.sh" - - ansible minecraft_servers -i inventories/staging/hosts.yml \ - -m shell \ - -a "test -x /opt/minecraft/tools/restore-backup.sh" \ No newline at end of file + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install Ansible + run: | + python -m pip install --upgrade pip + pip install ansible + + - name: Install collections + run: | + ansible-galaxy collection install -r requirements.yml + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.ANSIBLE_SSH_KEY }}" > ~/.ssh/ansible_key + chmod 600 ~/.ssh/ansible_key + + - name: Setup vault password + run: | + echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass + chmod 600 .vault_pass + + - name: Determine environment + id: env + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" == "refs/heads/main" ]; then + echo "environment=production" >> $GITHUB_OUTPUT + else + echo "environment=staging" >> $GITHUB_OUTPUT + fi + + - name: Deploy to environment + env: + ANSIBLE_HOST_KEY_CHECKING: false + ANSIBLE_VAULT_PASSWORD_FILE: .vault_pass + run: | + ansible-playbook \ + -i inventories/${{ steps.env.outputs.environment }}/hosts.yml \ + site.yml \ + --limit minecraft_servers \ + --diff + + - name: Clean up + if: always() + run: | + rm -f ~/.ssh/ansible_key + rm -f .vault_pass \ No newline at end of file diff --git a/.yamllint.yml b/.yamllint.yml index 8fe9226..3d4df66 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -1 +1,18 @@ -.yamllint.yml \ No newline at end of file +--- +extends: default + +rules: + line-length: + max: 150 + level: warning + truthy: + allowed-values: ['true', 'false', 'yes', 'no'] + comments: + min-spaces-from-content: 1 + indentation: + spaces: 2 + indent-sequences: true + +ignore: | + .gitea/ + *.md \ No newline at end of file diff --git a/ansible.cfg b/ansible.cfg index 50bc86f..e14300d 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,15 +1,21 @@ [defaults] host_key_checking = False -inventory = inventories/production/hosts.yml +inventory = ./inventories/production/hosts.yml remote_user = ansible private_key_file = ~/.ssh/ansible_key -roles_path = roles -stdout_callback = yaml +roles_path = ./roles +collections_path = ./collections +retry_files_enabled = False gathering = smart -fact_caching = memory -forks = 5 -timeout = 30 +fact_caching = jsonfile +fact_caching_connection = /tmp/ansible_facts +fact_caching_timeout = 86400 +stdout_callback = yaml +callback_whitelist = profile_tasks +deprecation_warnings = False +command_warnings = False +ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host} [ssh_connection] -ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -pipelining = True \ No newline at end of file +pipelining = True +control_path = /tmp/ansible-ssh-%%h-%%p-%%r \ No newline at end of file diff --git a/inventories/production/group_vars/all.yml b/inventories/production/group_vars/all.yml index 13777e4..65193ac 100644 --- a/inventories/production/group_vars/all.yml +++ b/inventories/production/group_vars/all.yml @@ -1,18 +1,31 @@ --- -# Environment -environment: production +# Variables globales pour la production +ansible_user: ansible +ansible_ssh_private_key_file: ~/.ssh/ansible_key +ansible_become: true +ansible_become_method: sudo -# Minecraft Configuration +# Configuration Minecraft minecraft_version: "1.21.6" -minecraft_memory_min: "2G" -minecraft_memory_max: "4G" +minecraft_type: "spigot" +minecraft_user: minecraft +minecraft_group: minecraft +minecraft_base_dir: /opt/minecraft +minecraft_server_dir: /opt/minecraft/server +minecraft_sources_dir: /opt/minecraft/sources +minecraft_tools_dir: /opt/minecraft/tools +minecraft_backup_dir: /opt/minecraft/backups -# Security -ssh_port: 22 -fail2ban_enabled: true -firewall_enabled: true +# Configuration Java +java_version: 21 +java_vendor: openjdk -# Backup settings -backup_retention_days: 30 -backup_retention_weeks: 12 -backup_retention_months: 6 \ No newline at end of file +# Configuration Réseau +server_port: 25565 +rcon_port: 25575 +enable_rcon: true + +# Configuration Backup +backup_retention_daily: 7 +backup_retention_weekly: 4 +backup_retention_monthly: 3 \ No newline at end of file diff --git a/inventories/production/hosts.yml b/inventories/production/hosts.yml index 8a90e13..764cca8 100644 --- a/inventories/production/hosts.yml +++ b/inventories/production/hosts.yml @@ -4,10 +4,12 @@ all: minecraft_servers: hosts: minecraft-prod-01: - ansible_host: 192.168.1.100 + ansible_host: 192.168.1.10 ansible_user: ansible - ansible_ssh_private_key_file: ~/.ssh/ansible_key minecraft-prod-02: - ansible_host: 192.168.1.101 + ansible_host: 192.168.1.11 ansible_user: ansible - ansible_ssh_private_key_file: ~/.ssh/ansible_key \ No newline at end of file + vars: + environment: production + minecraft_memory: 4096 + minecraft_port: 25565 \ No newline at end of file diff --git a/inventories/staging/group_vars/all.yml b/inventories/staging/group_vars/all.yml index e60495f..7152ffc 100644 --- a/inventories/staging/group_vars/all.yml +++ b/inventories/staging/group_vars/all.yml @@ -1,18 +1,31 @@ --- -# Environment -environment: staging +# Variables globales pour le staging +ansible_user: ansible +ansible_ssh_private_key_file: ~/.ssh/ansible_key +ansible_become: true +ansible_become_method: sudo -# Minecraft Configuration +# Configuration Minecraft (staging) minecraft_version: "1.21.6" -minecraft_memory_min: "1G" -minecraft_memory_max: "2G" +minecraft_type: "spigot" +minecraft_user: minecraft +minecraft_group: minecraft +minecraft_base_dir: /opt/minecraft +minecraft_server_dir: /opt/minecraft/server +minecraft_sources_dir: /opt/minecraft/sources +minecraft_tools_dir: /opt/minecraft/tools +minecraft_backup_dir: /opt/minecraft/backups -# Security -ssh_port: 22 -fail2ban_enabled: true -firewall_enabled: true +# Configuration Java +java_version: 21 +java_vendor: openjdk -# Backup settings -backup_retention_days: 7 -backup_retention_weeks: 4 -backup_retention_months: 2 \ No newline at end of file +# Configuration Réseau +server_port: 25565 +rcon_port: 25575 +enable_rcon: true + +# Configuration Backup +backup_retention_daily: 3 +backup_retention_weekly: 2 +backup_retention_monthly: 1 \ No newline at end of file diff --git a/inventories/staging/hosts.yml b/inventories/staging/hosts.yml index 5473f8d..5fe5c91 100644 --- a/inventories/staging/hosts.yml +++ b/inventories/staging/hosts.yml @@ -4,6 +4,9 @@ all: minecraft_servers: hosts: minecraft-staging-01: - ansible_host: 192.168.1.200 + ansible_host: 192.168.2.10 ansible_user: ansible - ansible_ssh_private_key_file: ~/.ssh/ansible_key \ No newline at end of file + vars: + environment: staging + minecraft_memory: 2048 + minecraft_port: 25565 \ No newline at end of file diff --git a/playbooks/hardening.yml b/playbooks/hardening.yml deleted file mode 100644 index 95227ac..0000000 --- a/playbooks/hardening.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Server Hardening Only - hosts: minecraft_servers - become: true - gather_facts: true - - roles: - - role: 01-server_hardening - tags: ['hardening', 'security'] \ No newline at end of file diff --git a/playbooks/install-java.yml b/playbooks/install-java.yml deleted file mode 100644 index 9a700a5..0000000 --- a/playbooks/install-java.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Java Installation Only - hosts: minecraft_servers - become: true - gather_facts: true - - roles: - - role: 02-installation-java - tags: ['java', 'dependencies'] \ No newline at end of file diff --git a/playbooks/install-minecraft.yml b/playbooks/install-minecraft.yml deleted file mode 100644 index ba3a49a..0000000 --- a/playbooks/install-minecraft.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Minecraft Installation Only - hosts: minecraft_servers - become: true - gather_facts: true - - pre_tasks: - - name: Ensure Java is installed - ansible.builtin.shell: java -version - register: java_check - failed_when: java_check.rc != 0 - - roles: - - role: 03-installation-minecraft - tags: ['minecraft', 'installation'] \ No newline at end of file diff --git a/playbooks/setup-backups.yml b/playbooks/setup-backups.yml deleted file mode 100644 index 9cb0981..0000000 --- a/playbooks/setup-backups.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Backup Setup Only - hosts: minecraft_servers - become: true - gather_facts: true - - roles: - - role: 04-backups - tags: ['backups', 'maintenance'] \ No newline at end of file diff --git a/playbooks/site.yml b/playbooks/site.yml deleted file mode 100644 index e5537b7..0000000 --- a/playbooks/site.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- -- name: Complete Minecraft Spigot Server Setup - hosts: minecraft_servers - become: true - gather_facts: true - - pre_tasks: - - name: Display target information - ansible.builtin.debug: - msg: - - "Target host: {{ inventory_hostname }}" - - "Environment: {{ environment }}" - - "Minecraft version: {{ minecraft_version }}" - - roles: - - role: 01-server_hardening - tags: ['hardening', 'security'] - - - role: 02-installation-java - tags: ['java', 'dependencies'] - - - role: 03-installation-minecraft - tags: ['minecraft', 'installation'] - - - role: 04-backups - tags: ['backups', 'maintenance'] - - - role: 05-update - tags: ['update', 'maintenance'] - - post_tasks: - - name: Final server status check - ansible.builtin.systemd: - name: minecraft - state: started - register: final_status - - - name: Display installation summary - ansible.builtin.debug: - msg: - - "Minecraft Spigot Server installation completed successfully" - - "Server status: {{ final_status.status.ActiveState }}" - - "Server directory: {{ minecraft_server_dir }}" - - "Version: {{ minecraft_version }}" \ No newline at end of file diff --git a/playbooks/update-system.yml b/playbooks/update-system.yml deleted file mode 100644 index fb908c8..0000000 --- a/playbooks/update-system.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: System Updates Only - hosts: minecraft_servers - become: true - gather_facts: true - - roles: - - role: 05-update - tags: ['update', 'maintenance'] \ No newline at end of file diff --git a/requirements.yml b/requirements.yml index 0411312..45ea735 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,8 +1,8 @@ --- collections: - - name: community.general - version: ">=7.0.0" - name: ansible.posix - version: ">=1.5.0" - - name: community.crypto - version: ">=2.15.0" \ No newline at end of file + version: ">=1.5.4" + - name: community.general + version: ">=8.0.0" + +roles: [] \ No newline at end of file diff --git a/roles/01-server_hardening/defaults/main.yml b/roles/01-server_hardening/defaults/main.yml index 8e10c45..30ff066 100644 --- a/roles/01-server_hardening/defaults/main.yml +++ b/roles/01-server_hardening/defaults/main.yml @@ -1,22 +1,36 @@ --- -# SSH Configuration +# Configuration SSH par défaut ssh_port: 22 -ssh_permit_root_login: false -ssh_password_authentication: false +ssh_permit_root_login: "no" +ssh_password_authentication: "no" +ssh_pubkey_authentication: "yes" ssh_max_auth_tries: 3 +ssh_max_sessions: 10 ssh_client_alive_interval: 300 ssh_client_alive_count_max: 2 -# Firewall Configuration -firewall_allowed_ports: - - "{{ ssh_port }}/tcp" - - "25565/tcp" # Minecraft default port +# Configuration Firewall +firewall_allowed_tcp_ports: + - 22 + - 25565 + - 25575 +firewall_allowed_udp_ports: [] -# Fail2ban Configuration -fail2ban_jail_ssh_enabled: true -fail2ban_jail_ssh_port: "{{ ssh_port }}" -fail2ban_jail_ssh_maxretry: 3 -fail2ban_jail_ssh_bantime: 600 +# Configuration Fail2ban +fail2ban_enabled: true +fail2ban_bantime: 3600 +fail2ban_findtime: 600 +fail2ban_maxretry: 5 -# System users +# Paquets de sécurité à installer +security_packages: + - ufw + - fail2ban + - unattended-upgrades + - apt-listchanges + - logwatch + - rkhunter + - chkrootkit + +# Administrateurs SSH admin_users: [] \ No newline at end of file diff --git a/roles/01-server_hardening/handlers/main.yml b/roles/01-server_hardening/handlers/main.yml index bd9e18a..1e7bcab 100644 --- a/roles/01-server_hardening/handlers/main.yml +++ b/roles/01-server_hardening/handlers/main.yml @@ -1,17 +1,23 @@ --- - name: restart ssh ansible.builtin.systemd: - name: ssh + name: sshd state: restarted - listen: "restart ssh service" + daemon_reload: true + listen: restart ssh service - name: restart fail2ban ansible.builtin.systemd: name: fail2ban state: restarted - listen: "restart fail2ban service" + daemon_reload: true + listen: restart fail2ban service - name: reload ufw community.general.ufw: state: reloaded - listen: "reload firewall" \ No newline at end of file + listen: reload firewall + +- name: reload sysctl + ansible.builtin.command: sysctl -p + listen: reload sysctl settings \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/01-update-system.yml b/roles/01-server_hardening/tasks/01-update-system.yml index 18e82c3..e7b26a8 100644 --- a/roles/01-server_hardening/tasks/01-update-system.yml +++ b/roles/01-server_hardening/tasks/01-update-system.yml @@ -1,9 +1,6 @@ --- -- name: Update apt cache and upgrade system packages (Debian/Ubuntu) +- name: Update apt cache for Debian/Ubuntu ansible.builtin.apt: - update_cache: true - upgrade: dist - autoremove: true - autoclean: true - when: ansible_os_family == "Debian" - register: system_update_result \ No newline at end of file + update_cache: yes + cache_valid_time: 3600 + when: ansible_os_family == "Debian" \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/02-install-packages.yml b/roles/01-server_hardening/tasks/02-install-packages.yml new file mode 100644 index 0000000..40f3916 --- /dev/null +++ b/roles/01-server_hardening/tasks/02-install-packages.yml @@ -0,0 +1,14 @@ +--- +- name: Upgrade all packages + ansible.builtin.apt: + upgrade: dist + autoremove: yes + autoclean: yes + when: ansible_os_family == "Debian" + register: system_upgraded + +- name: Install security packages + ansible.builtin.apt: + name: "{{ security_packages }}" + state: present + when: ansible_os_family == "Debian" \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/02-ssh-hardening.yml b/roles/01-server_hardening/tasks/02-ssh-hardening.yml deleted file mode 100644 index 53a1e78..0000000 --- a/roles/01-server_hardening/tasks/02-ssh-hardening.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Configure SSH daemon - ansible.builtin.template: - src: sshd_config.j2 - dest: /etc/ssh/sshd_config - owner: root - group: root - mode: '0644' - backup: true - notify: "restart ssh service" \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/03-configure-ssh.yml b/roles/01-server_hardening/tasks/03-configure-ssh.yml new file mode 100644 index 0000000..2519115 --- /dev/null +++ b/roles/01-server_hardening/tasks/03-configure-ssh.yml @@ -0,0 +1,11 @@ +--- +- name: Configure SSH daemon + ansible.builtin.template: + src: sshd_config.j2 + dest: "{{ ssh_config_file }}" + owner: root + group: root + mode: '0600' + backup: yes + validate: '/usr/sbin/sshd -t -f %s' + notify: restart ssh service \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/03-firewall-setup.yml b/roles/01-server_hardening/tasks/03-firewall-setup.yml deleted file mode 100644 index 7c47b0f..0000000 --- a/roles/01-server_hardening/tasks/03-firewall-setup.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- -- name: Install UFW - ansible.builtin.apt: - name: ufw - state: present - when: firewall_enabled | bool - -- name: Reset UFW rules - community.general.ufw: - state: reset - when: firewall_enabled | bool - -- name: Allow SSH port - community.general.ufw: - rule: allow - port: "{{ ssh_port }}" - proto: tcp - when: firewall_enabled | bool - -- name: Allow Minecraft port - community.general.ufw: - rule: allow - port: "25565" - proto: tcp - when: firewall_enabled | bool - -- name: Enable UFW - community.general.ufw: - state: enabled - policy: deny - when: firewall_enabled | bool - notify: "reload firewall" \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/04-configure-firewall.yml b/roles/01-server_hardening/tasks/04-configure-firewall.yml new file mode 100644 index 0000000..ed1299b --- /dev/null +++ b/roles/01-server_hardening/tasks/04-configure-firewall.yml @@ -0,0 +1,27 @@ +--- +- name: Install UFW firewall + ansible.builtin.apt: + name: ufw + state: present + when: ansible_os_family == "Debian" + +- name: Configure UFW defaults + community.general.ufw: + direction: "{{ item.direction }}" + policy: "{{ item.policy }}" + loop: + - { direction: 'incoming', policy: 'deny' } + - { direction: 'outgoing', policy: 'allow' } + notify: reload firewall + +- name: Allow TCP ports + community.general.ufw: + rule: allow + port: "{{ item }}" + proto: tcp + loop: "{{ firewall_allowed_tcp_ports }}" + notify: reload firewall + +- name: Enable UFW + community.general.ufw: + state: enabled \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/05-additional-security.yml b/roles/01-server_hardening/tasks/05-additional-security.yml deleted file mode 100644 index 6c6f5ca..0000000 --- a/roles/01-server_hardening/tasks/05-additional-security.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -- name: Install security packages - ansible.builtin.apt: - name: - - unattended-upgrades - - logwatch - - rkhunter - - chkrootkit - state: present - -- name: Configure automatic security updates - ansible.builtin.copy: - content: | - APT::Periodic::Update-Package-Lists "1"; - APT::Periodic::Unattended-Upgrade "1"; - APT::Periodic::AutocleanInterval "7"; - dest: /etc/apt/apt.conf.d/20auto-upgrades - owner: root - group: root - mode: '0644' \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/04-fail2ban-setup.yml b/roles/01-server_hardening/tasks/05-configure-fail2ban.yml similarity index 50% rename from roles/01-server_hardening/tasks/04-fail2ban-setup.yml rename to roles/01-server_hardening/tasks/05-configure-fail2ban.yml index 7700c8d..3c5808c 100644 --- a/roles/01-server_hardening/tasks/04-fail2ban-setup.yml +++ b/roles/01-server_hardening/tasks/05-configure-fail2ban.yml @@ -3,21 +3,21 @@ ansible.builtin.apt: name: fail2ban state: present - when: fail2ban_enabled | bool + when: ansible_os_family == "Debian" - name: Configure fail2ban jail ansible.builtin.template: - src: fail2ban-jail.local.j2 - dest: /etc/fail2ban/jail.local + src: fail2ban.jail.local.j2 + dest: "{{ fail2ban_config_dir }}/jail.local" owner: root group: root mode: '0644' - when: fail2ban_enabled | bool - notify: "restart fail2ban service" + backup: yes + notify: restart fail2ban service -- name: Start and enable fail2ban +- name: Ensure fail2ban is started and enabled ansible.builtin.systemd: name: fail2ban state: started - enabled: true - when: fail2ban_enabled | bool \ No newline at end of file + enabled: yes + daemon_reload: yes \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/06-manage-ssh-keys.yml b/roles/01-server_hardening/tasks/06-manage-ssh-keys.yml new file mode 100644 index 0000000..696c1d4 --- /dev/null +++ b/roles/01-server_hardening/tasks/06-manage-ssh-keys.yml @@ -0,0 +1,17 @@ +--- +- name: Create .ssh directory for ansible user + ansible.builtin.file: + path: /home/ansible/.ssh + state: directory + owner: ansible + group: ansible + mode: '0700' + +- name: Add SSH keys for administrators + ansible.posix.authorized_key: + user: ansible + state: present + key: "{{ item.key }}" + comment: "{{ item.name }}" + loop: "{{ admin_ssh_keys | default([]) }}" + when: admin_ssh_keys is defined \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/06-ssh-keys-management.yml b/roles/01-server_hardening/tasks/06-ssh-keys-management.yml deleted file mode 100644 index 7fb9e24..0000000 --- a/roles/01-server_hardening/tasks/06-ssh-keys-management.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: Create ansible user if not exists - ansible.builtin.user: - name: ansible - groups: sudo - shell: /bin/bash - create_home: true - state: present - -- name: Add authorized keys for admin users - ansible.posix.authorized_key: - user: ansible - key: "{{ item.key }}" - comment: "{{ item.comment | default('') }}" - state: present - loop: "{{ admin_users }}" - when: admin_users is defined and admin_users | length > 0 \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/07-additional-security.yml b/roles/01-server_hardening/tasks/07-additional-security.yml new file mode 100644 index 0000000..901b192 --- /dev/null +++ b/roles/01-server_hardening/tasks/07-additional-security.yml @@ -0,0 +1,9 @@ +--- +- name: Configure kernel parameters for security + ansible.posix.sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + state: present + reload: yes + loop: "{{ hardening_sysctl_settings }}" + notify: reload sysctl settings \ No newline at end of file diff --git a/roles/01-server_hardening/tasks/main.yml b/roles/01-server_hardening/tasks/main.yml index b87f4b8..1e7bcab 100644 --- a/roles/01-server_hardening/tasks/main.yml +++ b/roles/01-server_hardening/tasks/main.yml @@ -1,18 +1,23 @@ --- -- name: Include system update tasks - ansible.builtin.include_tasks: 01-system-update.yml +- name: restart ssh + ansible.builtin.systemd: + name: sshd + state: restarted + daemon_reload: true + listen: restart ssh service -- name: Include SSH hardening tasks - ansible.builtin.include_tasks: 02-ssh-hardening.yml +- name: restart fail2ban + ansible.builtin.systemd: + name: fail2ban + state: restarted + daemon_reload: true + listen: restart fail2ban service -- name: Include firewall setup tasks - ansible.builtin.include_tasks: 03-firewall-setup.yml +- name: reload ufw + community.general.ufw: + state: reloaded + listen: reload firewall -- name: Include fail2ban setup tasks - ansible.builtin.include_tasks: 04-fail2ban-setup.yml - -- name: Include additional security tasks - ansible.builtin.include_tasks: 05-additional-security.yml - -- name: Include SSH keys management tasks - ansible.builtin.include_tasks: 06-ssh-keys-management.yml \ No newline at end of file +- name: reload sysctl + ansible.builtin.command: sysctl -p + listen: reload sysctl settings \ No newline at end of file diff --git a/roles/01-server_hardening/templates/fail2ban-jail.local.j2 b/roles/01-server_hardening/templates/fail2ban-jail.local.j2 index 6668f53..94b3330 100644 --- a/roles/01-server_hardening/templates/fail2ban-jail.local.j2 +++ b/roles/01-server_hardening/templates/fail2ban-jail.local.j2 @@ -1,11 +1,30 @@ +# {{ ansible_managed }} [DEFAULT] -bantime = {{ fail2ban_jail_ssh_bantime }} -findtime = 600 -maxretry = {{ fail2ban_jail_ssh_maxretry }} +ignoreip = 127.0.0.1/8 ::1 +bantime = {{ fail2ban_bantime }} +findtime = {{ fail2ban_findtime }} +maxretry = {{ fail2ban_maxretry }} +backend = systemd [sshd] -enabled = {{ fail2ban_jail_ssh_enabled | ternary('true', 'false') }} -port = {{ fail2ban_jail_ssh_port }} +enabled = true +port = {{ ssh_port }} filter = sshd logpath = /var/log/auth.log -maxretry = {{ fail2ban_jail_ssh_maxretry }} \ No newline at end of file +maxretry = {{ fail2ban_maxretry }} + +[sshd-ddos] +enabled = true +port = {{ ssh_port }} +filter = sshd-ddos +logpath = /var/log/auth.log +maxretry = 10 + +[minecraft] +enabled = true +port = {{ minecraft_port | default(25565) }} +filter = minecraft +logpath = {{ minecraft_server_dir | default('/opt/minecraft/server') }}/logs/latest.log +maxretry = 10 +findtime = 600 +bantime = 3600 \ No newline at end of file diff --git a/roles/01-server_hardening/templates/sshd_config.j2 b/roles/01-server_hardening/templates/sshd_config.j2 index d14bbee..3022846 100644 --- a/roles/01-server_hardening/templates/sshd_config.j2 +++ b/roles/01-server_hardening/templates/sshd_config.j2 @@ -1,25 +1,47 @@ -# SSH Configuration - Managed by Ansible +# {{ ansible_managed }} +# SSH Server Configuration + Port {{ ssh_port }} Protocol 2 +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + +# Logging +SyslogFacility AUTH +LogLevel INFO # Authentication -PermitRootLogin {{ ssh_permit_root_login | ternary('yes', 'no') }} -PasswordAuthentication {{ ssh_password_authentication | ternary('yes', 'no') }} -PubkeyAuthentication yes -AuthorizedKeysFile .ssh/authorized_keys +LoginGraceTime 120 +PermitRootLogin {{ ssh_permit_root_login }} +StrictModes yes MaxAuthTries {{ ssh_max_auth_tries }} +MaxSessions {{ ssh_max_sessions }} -# Connection settings -ClientAliveInterval {{ ssh_client_alive_interval }} -ClientAliveCountMax {{ ssh_client_alive_count_max }} -MaxSessions 10 -MaxStartups 10:30:60 +PubkeyAuthentication {{ ssh_pubkey_authentication }} +AuthorizedKeysFile .ssh/authorized_keys -# Security settings +PasswordAuthentication {{ ssh_password_authentication }} PermitEmptyPasswords no ChallengeResponseAuthentication no -UsePAM yes + +# Security +IgnoreRhosts yes +HostbasedAuthentication no X11Forwarding no PrintMotd no +PrintLastLog yes +TCPKeepAlive yes +Compression delayed + +# Client alive +ClientAliveInterval {{ ssh_client_alive_interval }} +ClientAliveCountMax {{ ssh_client_alive_count_max }} + +# Allow only ansible user +AllowUsers ansible + +# Disable unused features +UsePAM yes +Banner none AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server \ No newline at end of file diff --git a/roles/01-server_hardening/templates/ufw-rules.j2 b/roles/01-server_hardening/templates/ufw-rules.j2 new file mode 100644 index 0000000..5b4a048 --- /dev/null +++ b/roles/01-server_hardening/templates/ufw-rules.j2 @@ -0,0 +1,16 @@ +# {{ ansible_managed }} +# UFW Rules Configuration + +# Allow SSH +-A ufw-user-input -p tcp --dport {{ ssh_port }} -j ACCEPT + +# Allow Minecraft +-A ufw-user-input -p tcp --dport {{ minecraft_port | default(25565) }} -j ACCEPT + +# Allow RCON if enabled +{% if enable_rcon | default(false) %} +-A ufw-user-input -p tcp --dport {{ rcon_port | default(25575) }} -j ACCEPT +{% endif %} + +# Drop everything else +-A ufw-user-input -j DROP \ No newline at end of file diff --git a/roles/01-server_hardening/vars/main.yml b/roles/01-server_hardening/vars/main.yml index 209a1db..bdc2485 100644 --- a/roles/01-server_hardening/vars/main.yml +++ b/roles/01-server_hardening/vars/main.yml @@ -1,13 +1,24 @@ --- -# Internal variables for server hardening -security_packages: - - ufw - - fail2ban - - unattended-upgrades - - logwatch - - rkhunter - - chkrootkit +# Variables spécifiques au rôle server_hardening +hardening_sysctl_settings: + - name: net.ipv4.tcp_syncookies + value: 1 + - name: net.ipv4.conf.all.rp_filter + value: 1 + - name: net.ipv4.conf.default.rp_filter + value: 1 + - name: net.ipv4.conf.all.accept_source_route + value: 0 + - name: net.ipv4.conf.default.accept_source_route + value: 0 + - name: net.ipv4.icmp_echo_ignore_broadcasts + value: 1 + - name: net.ipv4.icmp_ignore_bogus_error_responses + value: 1 + - name: net.ipv4.conf.all.log_martians + value: 1 + - name: net.ipv4.conf.default.log_martians + value: 1 -required_directories: - - /var/log/security - - /etc/security/limits.d \ No newline at end of file +ssh_config_file: /etc/ssh/sshd_config +fail2ban_config_dir: /etc/fail2ban \ No newline at end of file diff --git a/roles/02-installation-java/defaults/main.yml b/roles/02-installation-java/defaults/main.yml index d8c7fa0..302f0e3 100644 --- a/roles/02-installation-java/defaults/main.yml +++ b/roles/02-installation-java/defaults/main.yml @@ -1,8 +1,5 @@ --- -# Java configuration -java_version: "17" -java_package: "openjdk-{{ java_version }}-jdk" -java_home: "/usr/lib/jvm/java-{{ java_version }}-openjdk-amd64" - -# Minimum required Java version for Spigot 1.21.6 -required_java_version: "17" \ No newline at end of file +java_version: 21 +java_vendor: openjdk +java_home: /usr/lib/jvm/java-{{ java_version }}-openjdk-amd64 +java_bin_path: "{{ java_home }}/bin" \ No newline at end of file diff --git a/roles/02-installation-java/handlers/main.yml b/roles/02-installation-java/handlers/main.yml index 213d46a..6914d6e 100644 --- a/roles/02-installation-java/handlers/main.yml +++ b/roles/02-installation-java/handlers/main.yml @@ -1,6 +1,8 @@ --- - name: update java alternatives - ansible.builtin.shell: | - update-alternatives --install /usr/bin/java java {{ java_home }}/bin/java 1 - update-alternatives --install /usr/bin/javac javac {{ java_home }}/bin/javac 1 - listen: "configure java alternatives" \ No newline at end of file + ansible.builtin.command: update-alternatives --config java + listen: update java configuration + +- name: reload environment + ansible.builtin.command: source /etc/environment + listen: reload system environment \ No newline at end of file diff --git a/roles/02-installation-java/tasks/01-check-java-presence.yml b/roles/02-installation-java/tasks/01-check-java-presence.yml deleted file mode 100644 index a254b8f..0000000 --- a/roles/02-installation-java/tasks/01-check-java-presence.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Check if Java is already installed - ansible.builtin.shell: java -version 2>&1 | head -n 1 - register: java_check_result - failed_when: false - changed_when: false - -- name: Set Java installation requirement - ansible.builtin.set_fact: - java_needs_installation: "{{ java_check_result.rc != 0 or 'openjdk' not in java_check_result.stdout }}" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/02-install-java.yml b/roles/02-installation-java/tasks/02-install-java.yml deleted file mode 100644 index f8217df..0000000 --- a/roles/02-installation-java/tasks/02-install-java.yml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- name: Update apt cache - ansible.builtin.apt: - update_cache: true - when: ansible_os_family == "Debian" - -- name: Install OpenJDK - ansible.builtin.apt: - name: "{{ java_package }}" - state: present - notify: "configure java alternatives" - -- name: Set JAVA_HOME environment variable - ansible.builtin.lineinfile: - path: /etc/environment - regexp: '^JAVA_HOME=' - line: 'JAVA_HOME={{ java_home }}' - create: true - owner: root - group: root - mode: '0644' \ No newline at end of file diff --git a/roles/02-installation-java/tasks/02-remove-old-java.yml b/roles/02-installation-java/tasks/02-remove-old-java.yml new file mode 100644 index 0000000..e3ec0ec --- /dev/null +++ b/roles/02-installation-java/tasks/02-remove-old-java.yml @@ -0,0 +1,21 @@ +--- +- name: Remove old Java versions + ansible.builtin.apt: + name: "{{ item }}" + state: absent + purge: yes + loop: + - openjdk-8-jdk + - openjdk-8-jre + - openjdk-11-jdk + - openjdk-11-jre + - openjdk-17-jdk + - openjdk-17-jre + when: ansible_os_family == "Debian" + ignore_errors: yes + +- name: Clean apt cache after removal + ansible.builtin.apt: + autoclean: yes + autoremove: yes + when: ansible_os_family == "Debian" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/03-install-java.yml b/roles/02-installation-java/tasks/03-install-java.yml new file mode 100644 index 0000000..285af52 --- /dev/null +++ b/roles/02-installation-java/tasks/03-install-java.yml @@ -0,0 +1,24 @@ +--- +- name: Add Java repository for Ubuntu + ansible.builtin.apt_repository: + repo: "{{ java_apt_repository.ubuntu }}" + state: present + when: ansible_distribution == "Ubuntu" + +- name: Add backports repository for Debian + ansible.builtin.apt_repository: + repo: "{{ java_apt_repository.debian }}" + state: present + when: ansible_distribution == "Debian" + +- name: Update apt cache + ansible.builtin.apt: + update_cache: yes + cache_valid_time: 3600 + when: ansible_os_family == "Debian" + +- name: Install Java packages + ansible.builtin.apt: + name: "{{ java_packages[ansible_distribution | lower] }}" + state: present + when: ansible_os_family == "Debian" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/03-verify-java-installation.yml b/roles/02-installation-java/tasks/03-verify-java-installation.yml deleted file mode 100644 index e42cf86..0000000 --- a/roles/02-installation-java/tasks/03-verify-java-installation.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Verify Java installation - ansible.builtin.shell: java -version 2>&1 | head -n 1 - register: java_verification_result - failed_when: java_verification_result.rc != 0 - changed_when: false - -- name: Display Java version - ansible.builtin.debug: - msg: "Java installation verified: {{ java_verification_result.stdout }}" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/04-configure-java.yml b/roles/02-installation-java/tasks/04-configure-java.yml new file mode 100644 index 0000000..bf3376b --- /dev/null +++ b/roles/02-installation-java/tasks/04-configure-java.yml @@ -0,0 +1,27 @@ +--- +- name: Set JAVA_HOME environment variable + ansible.builtin.lineinfile: + path: /etc/environment + line: "JAVA_HOME={{ java_home }}" + create: yes + state: present + +- name: Add Java to PATH + ansible.builtin.lineinfile: + path: /etc/environment + line: 'PATH="{{ java_bin_path }}:$PATH"' + state: present + +- name: Create Java profile script + ansible.builtin.template: + src: java.sh.j2 + dest: /etc/profile.d/java.sh + owner: root + group: root + mode: '0644' + +- name: Set default Java version + ansible.builtin.alternatives: + name: java + path: "{{ java_home }}/bin/java" + notify: update java configuration \ No newline at end of file diff --git a/roles/02-installation-java/tasks/04-validate-java-version.yml b/roles/02-installation-java/tasks/04-validate-java-version.yml deleted file mode 100644 index b95f3e3..0000000 --- a/roles/02-installation-java/tasks/04-validate-java-version.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Extract Java version number - ansible.builtin.shell: | - java -version 2>&1 | grep -oP 'version "\K[^"]*' | cut -d'.' -f1 - register: installed_java_version - changed_when: false - -- name: Validate Java version compatibility - ansible.builtin.fail: - msg: "Java version {{ installed_java_version.stdout }} is not compatible with Minecraft Spigot. Minimum required: {{ required_java_version }}" - when: installed_java_version.stdout | int < required_java_version | int - -- name: Confirm Java compatibility - ansible.builtin.debug: - msg: "Java version {{ installed_java_version.stdout }} is compatible with Minecraft Spigot {{ minecraft_version }}" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/05-validate-installation.yml b/roles/02-installation-java/tasks/05-validate-installation.yml new file mode 100644 index 0000000..59dfa6f --- /dev/null +++ b/roles/02-installation-java/tasks/05-validate-installation.yml @@ -0,0 +1,21 @@ +--- +- name: Verify Java installation + ansible.builtin.command: "{{ java_home }}/bin/java -version" + register: java_version_output + changed_when: false + +- name: Display Java version + ansible.builtin.debug: + msg: "Java version installed: {{ java_version_output.stderr_lines[0] }}" + +- name: Verify Java compiler + ansible.builtin.command: "{{ java_home }}/bin/javac -version" + register: javac_version_output + changed_when: false + +- name: Validate Java version matches requirement + ansible.builtin.assert: + that: + - "'{{ java_version }}' in java_version_output.stderr" + fail_msg: "Java version {{ java_version }} not properly installed" + success_msg: "Java version {{ java_version }} successfully installed" \ No newline at end of file diff --git a/roles/02-installation-java/tasks/1-check-java.yml b/roles/02-installation-java/tasks/1-check-java.yml new file mode 100644 index 0000000..4e4f060 --- /dev/null +++ b/roles/02-installation-java/tasks/1-check-java.yml @@ -0,0 +1,6 @@ +--- +- name: Check if Java is installed + ansible.builtin.command: java -version + register: java_installed + failed_when: false + changed_when: false \ No newline at end of file diff --git a/roles/02-installation-java/tasks/main.yml b/roles/02-installation-java/tasks/main.yml index aa5b4bb..b8e6700 100644 --- a/roles/02-installation-java/tasks/main.yml +++ b/roles/02-installation-java/tasks/main.yml @@ -1,13 +1,17 @@ --- -- name: Include Java presence check tasks - ansible.builtin.include_tasks: 01-check-java-presence.yml +- name: Include check Java tasks + ansible.builtin.include_tasks: 01-check-java.yml -- name: Include Java installation tasks - ansible.builtin.include_tasks: 02-install-java.yml - when: java_needs_installation | default(true) +- name: Include remove old Java tasks + ansible.builtin.include_tasks: 02-remove-old-java.yml + when: java_needs_update | default(false) -- name: Include Java installation verification tasks - ansible.builtin.include_tasks: 03-verify-java-installation.yml +- name: Include install Java tasks + ansible.builtin.include_tasks: 03-install-java.yml + when: java_not_installed | default(false) or java_needs_update | default(false) -- name: Include Java version validation tasks - ansible.builtin.include_tasks: 04-validate-java-version.yml \ No newline at end of file +- name: Include configure Java tasks + ansible.builtin.include_tasks: 04-configure-java.yml + +- name: Include validate installation tasks + ansible.builtin.include_tasks: 05-validate-installation.yml \ No newline at end of file diff --git a/roles/02-installation-java/templates/java.sh.j2 b/roles/02-installation-java/templates/java.sh.j2 new file mode 100644 index 0000000..7dfbadd --- /dev/null +++ b/roles/02-installation-java/templates/java.sh.j2 @@ -0,0 +1,5 @@ +#!/bin/bash +# {{ ansible_managed }} + +export JAVA_HOME={{ java_home }} +export PATH=$JAVA_HOME \ No newline at end of file diff --git a/roles/02-installation-java/vars/main.yml b/roles/02-installation-java/vars/main.yml index 36ca99b..aaace3d 100644 --- a/roles/02-installation-java/vars/main.yml +++ b/roles/02-installation-java/vars/main.yml @@ -1,14 +1,12 @@ --- -# Java installation variables -supported_java_versions: - - "17" - - "18" - - "19" - - "20" - - "21" +java_packages: + debian: + - "openjdk-{{ java_version }}-jdk" + - "openjdk-{{ java_version }}-jre" + ubuntu: + - "openjdk-{{ java_version }}-jdk" + - "openjdk-{{ java_version }}-jre" -java_compatibility_matrix: - "1.21.6": "17" - "1.21.5": "17" - "1.21.4": "17" - "1.21.3": "17" \ No newline at end of file +java_apt_repository: + debian: "deb http://deb.debian.org/debian {{ ansible_distribution_release }}-backports main" + ubuntu: "ppa:openjdk-r/ppa" \ No newline at end of file diff --git a/roles/03-installation-minecraft/defaults/main.yml b/roles/03-installation-minecraft/defaults/main.yml index 615b743..4ad99ad 100644 --- a/roles/03-installation-minecraft/defaults/main.yml +++ b/roles/03-installation-minecraft/defaults/main.yml @@ -1,35 +1,57 @@ --- -# Minecraft user and group +# Utilisateur et groupe minecraft_user: minecraft minecraft_group: minecraft -# Minecraft directories +# Répertoires minecraft_base_dir: /opt/minecraft -minecraft_sources_dir: "{{ minecraft_base_dir }}/sources" -minecraft_server_dir: "{{ minecraft_base_dir }}/server" -minecraft_tools_dir: "{{ minecraft_base_dir }}/tools" -minecraft_plugins_dir: "{{ minecraft_server_dir }}/plugins" +minecraft_server_dir: /opt/minecraft/server +minecraft_sources_dir: /opt/minecraft/sources +minecraft_tools_dir: /opt/minecraft/tools -# Minecraft configuration +# Version et mémoire minecraft_version: "1.21.6" -minecraft_memory_min: "1G" -minecraft_memory_max: "2G" -minecraft_port: 25565 -minecraft_eula: true +minecraft_memory_min: 1024 +minecraft_memory_max: 4096 -# Server properties +# URLs de téléchargement +minecraft_build_tools_url: "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" +mcrcon_url: "https://github.com/Tiiffi/mcrcon/releases/latest/download/mcrcon-linux-x86_64.tar.gz" + +# Configuration serveur server_name: "Minecraft Server" -server_motd: "A Minecraft Server" +server_port: 25565 max_players: 20 -difficulty: "normal" -gamemode: "survival" -pvp_enabled: true - -# MCRCON configuration -mcrcon_version: "0.7.2" -rcon_enabled: true +view_distance: 10 +gamemode: survival +difficulty: normal +enable_command_block: false +enable_rcon: true rcon_port: 25575 -rcon_password: "changeme" +rcon_password: "{{ vault_rcon_password | default('ChangeMe123!') }}" +online_mode: true +pvp: true +white_list: false +spawn_protection: 16 +max_world_size: 29999984 +level_seed: "" +level_type: "DEFAULT" +generate_structures: true +spawn_animals: true +spawn_monsters: true +spawn_npcs: true -# Default operators (will be populated from ops_list variable) +# Service +minecraft_service_name: minecraft.service +minecraft_service_enabled: true +minecraft_service_state: started + +# Plugins par défaut +minecraft_plugins_list: + - name: "EssentialsX" + url: "https://github.com/EssentialsX/Essentials/releases/latest/download/EssentialsX.jar" + - name: "Vault" + url: "https://github.com/MilkBowl/Vault/releases/latest/download/Vault.jar" + +# Opérateurs minecraft_ops: [] \ No newline at end of file diff --git a/roles/03-installation-minecraft/handlers/main.yml b/roles/03-installation-minecraft/handlers/main.yml index 64f5d68..10813be 100644 --- a/roles/03-installation-minecraft/handlers/main.yml +++ b/roles/03-installation-minecraft/handlers/main.yml @@ -1,18 +1,24 @@ --- - name: restart minecraft ansible.builtin.systemd: - name: minecraft + name: "{{ minecraft_service_name }}" state: restarted daemon_reload: true - listen: "restart minecraft service" + listen: restart minecraft server + +- name: reload minecraft + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + state: reloaded + listen: reload minecraft server - name: reload systemd ansible.builtin.systemd: daemon_reload: true - listen: "systemd daemon reload" + listen: reload systemd daemon -- name: restart logrotate +- name: restart rsyslog ansible.builtin.systemd: - name: logrotate + name: rsyslog state: restarted - listen: "restart log rotation" \ No newline at end of file + listen: restart rsyslog service \ No newline at end of file diff --git a/roles/03-installation-minecraft/meta/main.yml b/roles/03-installation-minecraft/meta/main.yml new file mode 100644 index 0000000..f83a076 --- /dev/null +++ b/roles/03-installation-minecraft/meta/main.yml @@ -0,0 +1,31 @@ +--- +galaxy_info: + author: Tips-Of-Mine + description: Installation et configuration complète d'un serveur Minecraft Spigot + company: Tips-Of-Mine + license: MIT + min_ansible_version: "2.14" + platforms: + - name: Debian + versions: + - bullseye # 11 + - bookworm # 12 + - trixie # 13 + - buster # 10 + - name: Ubuntu + versions: + - focal # 20.04 + - jammy # 22.04 + - noble # 24.04 + galaxy_tags: + - minecraft + - spigot + - gaming + - server + - java + - backup + - monitoring + +dependencies: + - role: 02-installation-java + when: java_required | default(true) \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/01-create-minecraft-user.yml b/roles/03-installation-minecraft/tasks/01-create-user-group.yml similarity index 51% rename from roles/03-installation-minecraft/tasks/01-create-minecraft-user.yml rename to roles/03-installation-minecraft/tasks/01-create-user-group.yml index 5fb95dc..af65107 100644 --- a/roles/03-installation-minecraft/tasks/01-create-minecraft-user.yml +++ b/roles/03-installation-minecraft/tasks/01-create-user-group.yml @@ -3,13 +3,21 @@ ansible.builtin.group: name: "{{ minecraft_group }}" state: present + system: yes - name: Create minecraft user ansible.builtin.user: name: "{{ minecraft_user }}" group: "{{ minecraft_group }}" + groups: [] + append: no home: "{{ minecraft_base_dir }}" shell: /bin/bash - system: true - create_home: false - state: present \ No newline at end of file + system: yes + create_home: yes + state: present + comment: "Minecraft Server User" + +- name: Set minecraft user password to never expire + ansible.builtin.command: chage -M -1 {{ minecraft_user }} + changed_when: false \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/02-create-directories.yml b/roles/03-installation-minecraft/tasks/02-create-directories.yml index 2629a18..a7901be 100644 --- a/roles/03-installation-minecraft/tasks/02-create-directories.yml +++ b/roles/03-installation-minecraft/tasks/02-create-directories.yml @@ -1,16 +1,25 @@ --- -- name: Create minecraft base directories +- name: Create minecraft directories ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ minecraft_user }}" group: "{{ minecraft_group }}" mode: '0755' - loop: - - "{{ minecraft_base_dir }}" - - "{{ minecraft_sources_dir }}" - - "{{ minecraft_server_dir }}" - - "{{ minecraft_tools_dir }}" - - "{{ minecraft_plugins_dir }}" - - "{{ minecraft_base_dir }}/logs" - - "{{ minecraft_base_dir }}/backups" \ No newline at end of file + loop: "{{ minecraft_dirs }}" + +- name: Create .ssh directory for minecraft user + ansible.builtin.file: + path: "{{ minecraft_base_dir }}/.ssh" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0700' + +- name: Set correct permissions on base directory + ansible.builtin.file: + path: "{{ minecraft_base_dir }}" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + recurse: yes \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/03-download-spigot.yml b/roles/03-installation-minecraft/tasks/03-download-spigot.yml index 71f23a5..92dd0a8 100644 --- a/roles/03-installation-minecraft/tasks/03-download-spigot.yml +++ b/roles/03-installation-minecraft/tasks/03-download-spigot.yml @@ -1,9 +1,30 @@ --- +- name: Install required packages for building + ansible.builtin.apt: + name: "{{ build_packages }}" + state: present + update_cache: yes + when: ansible_os_family == "Debian" + +- name: Check if BuildTools already exists + ansible.builtin.stat: + path: "{{ minecraft_sources_dir }}/{{ buildtools_jar }}" + register: buildtools_exists + - name: Download BuildTools ansible.builtin.get_url: - url: "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" - dest: "{{ minecraft_sources_dir }}/BuildTools.jar" + url: "{{ minecraft_build_tools_url }}" + dest: "{{ minecraft_sources_dir }}/{{ buildtools_jar }}" owner: "{{ minecraft_user }}" group: "{{ minecraft_group }}" mode: '0644' - timeout: 300 \ No newline at end of file + force: yes + when: not buildtools_exists.stat.exists or force_download | default(false) + +- name: Create work directory for compilation + ansible.builtin.file: + path: "{{ minecraft_sources_dir }}/work" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/04-compile-spigot.yml b/roles/03-installation-minecraft/tasks/04-compile-spigot.yml new file mode 100644 index 0000000..385ac09 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/04-compile-spigot.yml @@ -0,0 +1,52 @@ +--- +- name: Check if Spigot is already compiled + ansible.builtin.stat: + path: "{{ minecraft_sources_dir }}/{{ spigot_jar_name }}" + register: spigot_compiled + +- name: Remove old work directory if exists + ansible.builtin.file: + path: "{{ minecraft_sources_dir }}/work" + state: absent + when: not spigot_compiled.stat.exists + +- name: Compile Spigot with BuildTools + ansible.builtin.shell: | + cd {{ minecraft_sources_dir }} + java -Xmx1024M -jar {{ buildtools_jar }} --rev {{ minecraft_version }} --compile-if-changed + args: + creates: "{{ minecraft_sources_dir }}/{{ spigot_jar_name }}" + become_user: "{{ minecraft_user }}" + when: not spigot_compiled.stat.exists + register: compile_result + async: 1800 + poll: 30 + +- name: Wait for compilation to complete + ansible.builtin.async_status: + jid: "{{ compile_result.ansible_job_id }}" + register: job_result + until: job_result.finished + retries: 60 + delay: 30 + when: compile_result.ansible_job_id is defined + +- name: Verify Spigot jar was created + ansible.builtin.stat: + path: "{{ minecraft_sources_dir }}/{{ spigot_jar_name }}" + register: spigot_jar_check + failed_when: not spigot_jar_check.stat.exists + +- name: Copy compiled Spigot to server directory + ansible.builtin.copy: + src: "{{ minecraft_sources_dir }}/{{ spigot_jar_name }}" + dest: "{{ minecraft_server_dir }}/{{ spigot_jar_name }}" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + remote_src: yes + +- name: Clean up work directory + ansible.builtin.file: + path: "{{ minecraft_sources_dir }}/work" + state: absent \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/04-install-mcrcon.yml b/roles/03-installation-minecraft/tasks/04-install-mcrcon.yml deleted file mode 100644 index fb1f2f5..0000000 --- a/roles/03-installation-minecraft/tasks/04-install-mcrcon.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- -- name: Install packages needed for MCRCON compilation - ansible.builtin.apt: - name: - - git - - build-essential - state: present - -- name: Clone MCRCON repository - ansible.builtin.git: - repo: "https://github.com/Tiiffi/mcrcon.git" - dest: "{{ minecraft_tools_dir }}/mcrcon" - version: "v{{ mcrcon_version }}" - force: true - become_user: "{{ minecraft_user }}" - -- name: Compile MCRCON - ansible.builtin.shell: | - cd {{ minecraft_tools_dir }}/mcrcon - make - become_user: "{{ minecraft_user }}" - args: - creates: "{{ minecraft_tools_dir }}/mcrcon/mcrcon" - -- name: Install MCRCON binary - ansible.builtin.copy: - src: "{{ minecraft_tools_dir }}/mcrcon/mcrcon" - dest: "/usr/local/bin/mcrcon" - owner: root - group: root - mode: '0755' - remote_src: true \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/05-compile-spigot.yml b/roles/03-installation-minecraft/tasks/05-compile-spigot.yml deleted file mode 100644 index 9b3b9ac..0000000 --- a/roles/03-installation-minecraft/tasks/05-compile-spigot.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -- name: Compile Spigot with BuildTools - ansible.builtin.shell: | - cd {{ minecraft_sources_dir }} - java -jar BuildTools.jar --rev {{ minecraft_version }} - become_user: "{{ minecraft_user }}" - args: - creates: "{{ minecraft_sources_dir }}/spigot-{{ minecraft_version }}.jar" - register: spigot_compilation - timeout: 1800 - -- name: Copy compiled Spigot to server directory - ansible.builtin.copy: - src: "{{ minecraft_sources_dir }}/spigot-{{ minecraft_version }}.jar" - dest: "{{ minecraft_server_dir }}/spigot.jar" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0644' - remote_src: true \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/05-configure-server.yml b/roles/03-installation-minecraft/tasks/05-configure-server.yml new file mode 100644 index 0000000..7ab6776 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/05-configure-server.yml @@ -0,0 +1,57 @@ +--- +- name: Create server.properties + ansible.builtin.template: + src: server.properties.j2 + dest: "{{ minecraft_server_dir }}/server.properties" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + backup: yes + +- name: Accept EULA + ansible.builtin.template: + src: eula.txt.j2 + dest: "{{ minecraft_server_dir }}/eula.txt" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + +- name: Create spigot.yml configuration + ansible.builtin.template: + src: spigot.yml.j2 + dest: "{{ minecraft_server_dir }}/spigot.yml" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + +- name: Create bukkit.yml configuration + ansible.builtin.template: + src: bukkit.yml.j2 + dest: "{{ minecraft_server_dir }}/bukkit.yml" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + +- name: Create start script + ansible.builtin.template: + src: start.sh.j2 + dest: "{{ minecraft_base_dir }}/scripts/start.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Create stop script + ansible.builtin.template: + src: stop.sh.j2 + dest: "{{ minecraft_base_dir }}/scripts/stop.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Store current version + ansible.builtin.copy: + content: "{{ minecraft_version }}" + dest: "{{ minecraft_server_dir }}/.version" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/06-configure-minecraft.yml b/roles/03-installation-minecraft/tasks/06-configure-minecraft.yml deleted file mode 100644 index b418784..0000000 --- a/roles/03-installation-minecraft/tasks/06-configure-minecraft.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: Create server.properties - ansible.builtin.template: - src: server.properties.j2 - dest: "{{ minecraft_server_dir }}/server.properties" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0644' - notify: "restart minecraft service" - -- name: Accept EULA - ansible.builtin.copy: - content: "eula={{ minecraft_eula | ternary('true', 'false') }}" - dest: "{{ minecraft_server_dir }}/eula.txt" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0644' \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/06-install-mcrcon.yml b/roles/03-installation-minecraft/tasks/06-install-mcrcon.yml new file mode 100644 index 0000000..2cd0934 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/06-install-mcrcon.yml @@ -0,0 +1,46 @@ +--- +- name: Check if mcrcon is already installed + ansible.builtin.stat: + path: "{{ minecraft_tools_dir }}/mcrcon" + register: mcrcon_installed + +- name: Download mcrcon + ansible.builtin.get_url: + url: "{{ mcrcon_url }}" + dest: "{{ minecraft_tools_dir }}/mcrcon.tar.gz" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + when: not mcrcon_installed.stat.exists + +- name: Extract mcrcon + ansible.builtin.unarchive: + src: "{{ minecraft_tools_dir }}/mcrcon.tar.gz" + dest: "{{ minecraft_tools_dir }}" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + remote_src: yes + creates: "{{ minecraft_tools_dir }}/mcrcon" + when: not mcrcon_installed.stat.exists + +- name: Make mcrcon executable + ansible.builtin.file: + path: "{{ minecraft_tools_dir }}/mcrcon" + mode: '0755' + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + +- name: Create mcrcon wrapper script + ansible.builtin.copy: + content: | + #!/bin/bash + {{ minecraft_tools_dir }}/mcrcon -H localhost -P {{ rcon_port }} -p "{{ rcon_password }}" "$@" + dest: "{{ minecraft_base_dir }}/scripts/rcon.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Clean up mcrcon archive + ansible.builtin.file: + path: "{{ minecraft_tools_dir }}/mcrcon.tar.gz" + state: absent \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/07-create-service.yml b/roles/03-installation-minecraft/tasks/07-create-service.yml new file mode 100644 index 0000000..8dd1927 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/07-create-service.yml @@ -0,0 +1,36 @@ +--- +- name: Create minecraft systemd service + ansible.builtin.template: + src: minecraft.service.j2 + dest: "/etc/systemd/system/{{ minecraft_service_name }}" + owner: root + group: root + mode: '0644' + notify: + - reload systemd daemon + - restart minecraft server + +- name: Reload systemd daemon + ansible.builtin.systemd: + daemon_reload: yes + +- name: Enable minecraft service + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + enabled: "{{ minecraft_service_enabled }}" + masked: no + +- name: Start minecraft service + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + state: "{{ minecraft_service_state }}" + when: minecraft_service_state == "started" + +- name: Wait for server to be ready + ansible.builtin.wait_for: + port: "{{ server_port }}" + delay: 10 + timeout: 120 + state: started + host: 127.0.0.1 + when: minecraft_service_state == "started" \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/07-setup-systemd-service.yml b/roles/03-installation-minecraft/tasks/07-setup-systemd-service.yml deleted file mode 100644 index 1e181d7..0000000 --- a/roles/03-installation-minecraft/tasks/07-setup-systemd-service.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: Create Minecraft systemd service - ansible.builtin.template: - src: minecraft.service.j2 - dest: /etc/systemd/system/minecraft.service - owner: root - group: root - mode: '0644' - notify: - - "systemd daemon reload" - - "restart minecraft service" - -- name: Enable Minecraft service - ansible.builtin.systemd: - name: minecraft - enabled: true - daemon_reload: true \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/08-configure-logrotate.yml b/roles/03-installation-minecraft/tasks/08-configure-logrotate.yml new file mode 100644 index 0000000..fafd293 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/08-configure-logrotate.yml @@ -0,0 +1,36 @@ +--- +- name: Configure logrotate for minecraft logs + ansible.builtin.template: + src: minecraft-logrotate.j2 + dest: /etc/logrotate.d/minecraft + owner: root + group: root + mode: '0644' + +- name: Create rsyslog configuration for minecraft + ansible.builtin.copy: + content: | + # Minecraft Server Logging + :programname, isequal, "minecraft" /var/log/minecraft.log + & stop + dest: /etc/rsyslog.d/30-minecraft.conf + owner: root + group: root + mode: '0644' + notify: restart rsyslog service + +- name: Create minecraft log file + ansible.builtin.file: + path: /var/log/minecraft.log + state: touch + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + modification_time: preserve + access_time: preserve + +- name: Test logrotate configuration + ansible.builtin.command: logrotate -d /etc/logrotate.d/minecraft + register: logrotate_test + changed_when: false + failed_when: logrotate_test.rc != 0 \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/08-setup-log-rotation.yml b/roles/03-installation-minecraft/tasks/08-setup-log-rotation.yml deleted file mode 100644 index e148c32..0000000 --- a/roles/03-installation-minecraft/tasks/08-setup-log-rotation.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Configure log rotation for Minecraft - ansible.builtin.template: - src: logrotate-minecraft.j2 - dest: /etc/logrotate.d/minecraft - owner: root - group: root - mode: '0644' - notify: "restart log rotation" \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/09-manage-ops.yml b/roles/03-installation-minecraft/tasks/09-manage-ops.yml new file mode 100644 index 0000000..8941f42 --- /dev/null +++ b/roles/03-installation-minecraft/tasks/09-manage-ops.yml @@ -0,0 +1,32 @@ +--- +- name: Check if ops.json exists + ansible.builtin.stat: + path: "{{ minecraft_server_dir }}/ops.json" + register: ops_file + +- name: Create ops.json file + ansible.builtin.template: + src: ops.json.j2 + dest: "{{ minecraft_server_dir }}/ops.json" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + backup: yes + when: minecraft_ops is defined and minecraft_ops | length > 0 + +- name: Create empty ops.json if no ops defined + ansible.builtin.copy: + content: "[]" + dest: "{{ minecraft_server_dir }}/ops.json" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + force: no + when: not ops_file.stat.exists and (minecraft_ops is not defined or minecraft_ops | length == 0) + +- name: Set permissions on ops.json + ansible.builtin.file: + path: "{{ minecraft_server_dir }}/ops.json" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/09-setup-ops-management.yml b/roles/03-installation-minecraft/tasks/09-setup-ops-management.yml deleted file mode 100644 index 632c470..0000000 --- a/roles/03-installation-minecraft/tasks/09-setup-ops-management.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Create ops.json file - ansible.builtin.template: - src: ops.json.j2 - dest: "{{ minecraft_server_dir }}/ops.json" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0644' - when: minecraft_ops is defined and minecraft_ops | length > 0 - notify: "restart minecraft service" \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/10-install-plugins.yml b/roles/03-installation-minecraft/tasks/10-install-plugins.yml new file mode 100644 index 0000000..b34c7ef --- /dev/null +++ b/roles/03-installation-minecraft/tasks/10-install-plugins.yml @@ -0,0 +1,39 @@ +--- +- name: Create plugins directory + ansible.builtin.file: + path: "{{ minecraft_server_dir }}/plugins" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Download plugins + ansible.builtin.get_url: + url: "{{ item.url }}" + dest: "{{ minecraft_server_dir }}/plugins/{{ item.name }}.jar" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + timeout: 30 + loop: "{{ minecraft_plugins_list }}" + when: minecraft_plugins_list is defined and minecraft_plugins_list | length > 0 + ignore_errors: yes + +- name: Create plugins config directory + ansible.builtin.file: + path: "{{ minecraft_server_dir }}/plugins/{{ item.name }}" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + loop: "{{ minecraft_plugins_list }}" + when: minecraft_plugins_list is defined and item.create_config_dir | default(false) + +- name: Restart server to load new plugins + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + state: restarted + when: + - minecraft_plugins_list is defined + - minecraft_plugins_list | length > 0 + - restart_after_plugins | default(false) \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/10-setup-plugins-directory.yml b/roles/03-installation-minecraft/tasks/10-setup-plugins-directory.yml deleted file mode 100644 index 5100185..0000000 --- a/roles/03-installation-minecraft/tasks/10-setup-plugins-directory.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -- name: Ensure plugins directory exists with correct permissions - ansible.builtin.file: - path: "{{ minecraft_plugins_dir }}" - state: directory - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - -- name: Create plugins management script - ansible.builtin.copy: - content: | - #!/bin/bash - # Plugin management script - PLUGINS_DIR="{{ minecraft_plugins_dir }}" - - echo "Minecraft plugins directory: $PLUGINS_DIR" - echo "Current plugins:" - ls -la "$PLUGINS_DIR" - dest: "{{ minecraft_tools_dir }}/manage-plugins.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' \ No newline at end of file diff --git a/roles/03-installation-minecraft/tasks/main.yml b/roles/03-installation-minecraft/tasks/main.yml index ff20f88..e22e285 100644 --- a/roles/03-installation-minecraft/tasks/main.yml +++ b/roles/03-installation-minecraft/tasks/main.yml @@ -1,30 +1,40 @@ --- -- name: Include minecraft user creation tasks - ansible.builtin.include_tasks: 01-create-minecraft-user.yml +- name: Include create user and group tasks + ansible.builtin.include_tasks: 01-create-user-group.yml + tags: ['minecraft-user'] -- name: Include directory creation tasks +- name: Include create directories tasks ansible.builtin.include_tasks: 02-create-directories.yml + tags: ['minecraft-dirs'] -- name: Include Spigot download tasks +- name: Include download Spigot tasks ansible.builtin.include_tasks: 03-download-spigot.yml + tags: ['minecraft-download'] -- name: Include MCRCON installation tasks - ansible.builtin.include_tasks: 04-install-mcrcon.yml +- name: Include compile Spigot tasks + ansible.builtin.include_tasks: 04-compile-spigot.yml + tags: ['minecraft-compile'] -- name: Include Spigot compilation tasks - ansible.builtin.include_tasks: 05-compile-spigot.yml +- name: Include configure server tasks + ansible.builtin.include_tasks: 05-configure-server.yml + tags: ['minecraft-config'] -- name: Include Minecraft configuration tasks - ansible.builtin.include_tasks: 06-configure-minecraft.yml +- name: Include install mcrcon tasks + ansible.builtin.include_tasks: 06-install-mcrcon.yml + tags: ['minecraft-mcrcon'] -- name: Include systemd service setup tasks - ansible.builtin.include_tasks: 07-setup-systemd-service.yml +- name: Include create service tasks + ansible.builtin.include_tasks: 07-create-service.yml + tags: ['minecraft-service'] -- name: Include log rotation setup tasks - ansible.builtin.include_tasks: 08-setup-log-rotation.yml +- name: Include configure logrotate tasks + ansible.builtin.include_tasks: 08-configure-logrotate.yml + tags: ['minecraft-logs'] -- name: Include ops management setup tasks - ansible.builtin.include_tasks: 09-setup-ops-management.yml +- name: Include manage ops tasks + ansible.builtin.include_tasks: 09-manage-ops.yml + tags: ['minecraft-ops'] -- name: Include plugins directory setup tasks - ansible.builtin.include_tasks: 10-setup-plugins-directory.yml \ No newline at end of file +- name: Include install plugins tasks + ansible.builtin.include_tasks: 10-install-plugins.yml + tags: ['minecraft-plugins'] \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/banned-ips.json.j2 b/roles/03-installation-minecraft/templates/banned-ips.json.j2 new file mode 100644 index 0000000..2be0213 --- /dev/null +++ b/roles/03-installation-minecraft/templates/banned-ips.json.j2 @@ -0,0 +1,12 @@ +[ +{% for ip in minecraft_banned_ips | default([]) %} + { + "ip": "{{ ip.address }}", + "created": "{{ ip.created | default(ansible_date_time.iso8601) }}", + "source": "{{ ip.source | default('Server') }}", + "expires": "{{ ip.expires | default('forever') }}", + "reason": "{{ ip.reason | default('Banned by administrator') }}" + }{% if not loop.last %},{% endif %} + +{% endfor %} +] \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/banned-players.json.j2 b/roles/03-installation-minecraft/templates/banned-players.json.j2 new file mode 100644 index 0000000..603fa06 --- /dev/null +++ b/roles/03-installation-minecraft/templates/banned-players.json.j2 @@ -0,0 +1,13 @@ +[ +{% for player in minecraft_banned_players | default([]) %} + { + "uuid": "{{ player.uuid }}", + "name": "{{ player.name }}", + "created": "{{ player.created | default(ansible_date_time.iso8601) }}", + "source": "{{ player.source | default('Server') }}", + "expires": "{{ player.expires | default('forever') }}", + "reason": "{{ player.reason | default('Banned by administrator') }}" + }{% if not loop.last %},{% endif %} + +{% endfor %} +] \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/bukkit.yml.j2 b/roles/03-installation-minecraft/templates/bukkit.yml.j2 index e69de29..81779c2 100644 --- a/roles/03-installation-minecraft/templates/bukkit.yml.j2 +++ b/roles/03-installation-minecraft/templates/bukkit.yml.j2 @@ -0,0 +1,39 @@ +# {{ ansible_managed }} +# This is the main configuration file for Bukkit. +# Bukkit version: {{ minecraft_version }} + +settings: + allow-end: true + warn-on-overload: true + permissions-file: permissions.yml + update-folder: update + plugin-profiling: false + connection-throttle: 4000 + query-plugins: true + deprecated-verbose: default + shutdown-message: Server closed + minimum-api: none + +spawn-limits: + monsters: 70 + animals: 10 + water-animals: 5 + water-ambient: 20 + water-underground-creature: 5 + axolotls: 5 + ambient: 15 + +chunk-gc: + period-in-ticks: 600 + +ticks-per: + animal-spawns: 400 + monster-spawns: 1 + water-spawns: 1 + water-ambient-spawns: 1 + water-underground-creature-spawns: 1 + axolotl-spawns: 1 + ambient-spawns: 1 + autosave: 6000 + +aliases: now-in-commands.yml \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/eula.txt.j2 b/roles/03-installation-minecraft/templates/eula.txt.j2 new file mode 100644 index 0000000..e34d986 --- /dev/null +++ b/roles/03-installation-minecraft/templates/eula.txt.j2 @@ -0,0 +1,4 @@ +# {{ ansible_managed }} +# By changing the setting below to TRUE you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA). +# Generated on {{ ansible_date_time.iso8601 }} +eula=true \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/logrotate-minecraft.j2 b/roles/03-installation-minecraft/templates/logrotate-minecraft.j2 index fc328b7..2e562a7 100644 --- a/roles/03-installation-minecraft/templates/logrotate-minecraft.j2 +++ b/roles/03-installation-minecraft/templates/logrotate-minecraft.j2 @@ -1,21 +1,30 @@ -{{ minecraft_base_dir }}/logs/*.log { - daily - missingok - rotate 30 - compress - delaycompress - notifempty - copytruncate - su {{ minecraft_user }} {{ minecraft_group }} -} - +# {{ ansible_managed }} {{ minecraft_server_dir }}/logs/*.log { daily - missingok - rotate 30 + rotate 14 compress delaycompress + missingok notifempty - copytruncate - su {{ minecraft_user }} {{ minecraft_group }} + create 0644 {{ minecraft_user }} {{ minecraft_group }} + sharedscripts + postrotate + # Demander au serveur de recharger les logs + if systemctl is-active --quiet {{ minecraft_service_name }}; then + {{ minecraft_tools_dir }}/mcrcon -H localhost -P {{ rcon_port }} -p "{{ rcon_password }}" "say Rotation des logs effectuée" > /dev/null 2>&1 || true + fi + endscript +} + +/var/log/minecraft.log { + weekly + rotate 4 + compress + delaycompress + missingok + notifempty + create 0644 {{ minecraft_user }} {{ minecraft_group }} + postrotate + systemctl reload rsyslog > /dev/null 2>&1 || true + endscript } \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/minecraft-backup.sh.j2 b/roles/03-installation-minecraft/templates/minecraft-backup.sh.j2 new file mode 100644 index 0000000..5a0314e --- /dev/null +++ b/roles/03-installation-minecraft/templates/minecraft-backup.sh.j2 @@ -0,0 +1,64 @@ +#!/bin/bash +# {{ ansible_managed }} +# Script de backup intégré pour Minecraft + +BACKUP_DIR="{{ minecraft_backup_dir | default('/opt/minecraft/backups') }}" +SERVER_DIR="{{ minecraft_server_dir }}" +MCRCON="{{ minecraft_tools_dir }}/mcrcon" +RCON_PORT="{{ rcon_port }}" +RCON_PASS="{{ rcon_password }}" +DATE=$(date +%Y%m%d_%H%M%S) + +# Créer le répertoire de backup si nécessaire +mkdir -p "$BACKUP_DIR" + +# Fonction pour envoyer des commandes RCON +send_command() { + $MCRCON -H localhost -P $RCON_PORT -p "$RCON_PASS" "$1" 2>/dev/null +} + +# Fonction de backup +perform_backup() { + echo "[$(date)] Démarrage du backup..." + + # Avertir les joueurs + send_command "say Backup automatique dans 10 secondes..." + sleep 5 + send_command "say Backup en cours, lag possible..." + + # Forcer la sauvegarde + send_command "save-all flush" + sleep 2 + + # Désactiver l'auto-save temporairement + send_command "save-off" + + # Créer le backup + tar -czf "$BACKUP_DIR/minecraft_backup_$DATE.tar.gz" \ + -C "$(dirname $SERVER_DIR)" \ + "$(basename $SERVER_DIR)" \ + --exclude='*.log' \ + --exclude='logs/*.gz' \ + --exclude='crash-reports/*' + + # Réactiver l'auto-save + send_command "save-on" + + # Informer les joueurs + send_command "say Backup terminé!" + + echo "[$(date)] Backup créé: minecraft_backup_$DATE.tar.gz" + + # Nettoyer les vieux backups (garder les 7 derniers) + ls -t "$BACKUP_DIR"/minecraft_backup_*.tar.gz 2>/dev/null | tail -n +8 | xargs rm -f +} + +# Vérifier si le serveur est actif +if systemctl is-active --quiet {{ minecraft_service_name }}; then + perform_backup +else + echo "[$(date)] Serveur inactif, backup sans RCON..." + tar -czf "$BACKUP_DIR/minecraft_backup_offline_$DATE.tar.gz" \ + -C "$(dirname $SERVER_DIR)" \ + "$(basename $SERVER_DIR)" +fi \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/minecraft-commands.sh.j2 b/roles/03-installation-minecraft/templates/minecraft-commands.sh.j2 new file mode 100644 index 0000000..918810b --- /dev/null +++ b/roles/03-installation-minecraft/templates/minecraft-commands.sh.j2 @@ -0,0 +1,194 @@ +#!/bin/bash +# {{ ansible_managed }} +# Script de commandes utiles pour Minecraft + +MCRCON="{{ minecraft_tools_dir }}/mcrcon" +RCON_HOST="localhost" +RCON_PORT="{{ rcon_port }}" +RCON_PASS="{{ rcon_password }}" + +# Fonction d'exécution RCON +rcon() { + $MCRCON -H $RCON_HOST -P $RCON_PORT -p "$RCON_PASS" "$@" +} + +# Commandes disponibles +case "$1" in + say) + shift + rcon "say $@" + ;; + + list) + rcon "list" + ;; + + save) + echo "Sauvegarde du monde..." + rcon "save-all flush" + echo "Sauvegarde terminée" + ;; + + whitelist-add) + if [ -z "$2" ]; then + echo "Usage: $0 whitelist-add " + exit 1 + fi + rcon "whitelist add $2" + ;; + + whitelist-remove) + if [ -z "$2" ]; then + echo "Usage: $0 whitelist-remove " + exit 1 + fi + rcon "whitelist remove $2" + ;; + + whitelist-list) + rcon "whitelist list" + ;; + + ban) + if [ -z "$2" ]; then + echo "Usage: $0 ban [raison]" + exit 1 + fi + shift + rcon "ban $@" + ;; + + unban) + if [ -z "$2" ]; then + echo "Usage: $0 unban " + exit 1 + fi + rcon "pardon $2" + ;; + + kick) + if [ -z "$2" ]; then + echo "Usage: $0 kick [raison]" + exit 1 + fi + shift + rcon "kick $@" + ;; + + op) + if [ -z "$2" ]; then + echo "Usage: $0 op " + exit 1 + fi + rcon "op $2" + ;; + + deop) + if [ -z "$2" ]; then + echo "Usage: $0 deop " + exit 1 + fi + rcon "deop $2" + ;; + + tp) + if [ -z "$3" ]; then + echo "Usage: $0 tp " + exit 1 + fi + rcon "tp $2 $3" + ;; + + gamemode) + if [ -z "$3" ]; then + echo "Usage: $0 gamemode " + echo "Modes: survival, creative, adventure, spectator" + exit 1 + fi + rcon "gamemode $2 $3" + ;; + + time) + if [ -z "$2" ]; then + echo "Usage: $0 time " + exit 1 + fi + shift + rcon "time $@" + ;; + + weather) + if [ -z "$2" ]; then + echo "Usage: $0 weather [durée]" + exit 1 + fi + shift + rcon "weather $@" + ;; + + difficulty) + if [ -z "$2" ]; then + echo "Usage: $0 difficulty " + exit 1 + fi + rcon "difficulty $2" + ;; + + give) + if [ -z "$3" ]; then + echo "Usage: $0 give [quantité]" + exit 1 + fi + shift + rcon "give $@" + ;; + + reload) + echo "Rechargement de la configuration..." + rcon "reload" + echo "Configuration rechargée" + ;; + + stop) + echo "Arrêt du serveur..." + rcon "stop" + ;; + + console) + # Mode console interactif + echo "Mode console RCON (tapez 'exit' pour quitter)" + while true; do + read -p "minecraft> " cmd + if [ "$cmd" = "exit" ]; then + break + fi + rcon "$cmd" + done + ;; + + *) + echo "Commandes Minecraft disponibles:" + echo "" + echo " $0 say - Envoyer un message à tous" + echo " $0 list - Liste des joueurs connectés" + echo " $0 save - Sauvegarder le monde" + echo " $0 whitelist-add - Ajouter à la whitelist" + echo " $0 whitelist-remove - Retirer de la whitelist" + echo " $0 whitelist-list - Afficher la whitelist" + echo " $0 ban [raison] - Bannir un joueur" + echo " $0 unban - Débannir un joueur" + echo " $0 kick [raison] - Expulser un joueur" + echo " $0 op - Donner les droits OP" + echo " $0 deop - Retirer les droits OP" + echo " $0 tp - Téléporter un joueur" + echo " $0 gamemode - Changer le mode de jeu" + echo " $0 time - Gérer le temps" + echo " $0 weather [durée] - Changer la météo" + echo " $0 difficulty - Changer la difficulté" + echo " $0 give [qty] - Donner des objets" + echo " $0 reload - Recharger la configuration" + echo " $0 stop - Arrêter le serveur" + echo " $0 console - Mode console interactif" + exit 1 + ;; +esac \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/minecraft-monitor.sh.j2 b/roles/03-installation-minecraft/templates/minecraft-monitor.sh.j2 new file mode 100644 index 0000000..67faaaf --- /dev/null +++ b/roles/03-installation-minecraft/templates/minecraft-monitor.sh.j2 @@ -0,0 +1,133 @@ +#!/bin/bash +# {{ ansible_managed }} +# Script de monitoring pour le serveur Minecraft + +SERVER_DIR="{{ minecraft_server_dir }}" +MCRCON="{{ minecraft_tools_dir }}/mcrcon" +RCON_PORT="{{ rcon_port }}" +RCON_PASS="{{ rcon_password }}" +SERVICE_NAME="{{ minecraft_service_name }}" +LOG_FILE="/var/log/minecraft-monitor.log" + +# Fonction de log +log_message() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" +} + +# Fonction pour vérifier le statut +check_server_status() { + if ! systemctl is-active --quiet "$SERVICE_NAME"; then + echo "OFFLINE" + return 1 + fi + + # Tester RCON + if ! $MCRCON -H localhost -P $RCON_PORT -p "$RCON_PASS" "list" &>/dev/null; then + echo "RCON_FAILED" + return 2 + fi + + echo "ONLINE" + return 0 +} + +# Fonction pour obtenir les métriques +get_metrics() { + local status=$(check_server_status) + + if [ "$status" = "ONLINE" ]; then + # Nombre de joueurs + local players=$($MCRCON -H localhost -P $RCON_PORT -p "$RCON_PASS" "list" 2>/dev/null | grep -oP '\d+(?= of a max)' || echo "0") + + # TPS (si disponible avec un plugin) + local tps=$($MCRCON -H localhost -P $RCON_PORT -p "$RCON_PASS" "tps" 2>/dev/null || echo "N/A") + + # Utilisation mémoire du processus + local pid=$(systemctl show -p MainPID "$SERVICE_NAME" | cut -d= -f2) + local mem="0" + if [ "$pid" != "0" ]; then + mem=$(ps -o rss= -p "$pid" 2>/dev/null | awk '{printf "%.2f", $1/1024}' || echo "0") + fi + + # Utilisation CPU + local cpu="0" + if [ "$pid" != "0" ]; then + cpu=$(ps -o %cpu= -p "$pid" 2>/dev/null | awk '{print $1}' || echo "0") + fi + + # Espace disque du serveur + local disk=$(du -sh "$SERVER_DIR" 2>/dev/null | cut -f1 || echo "N/A") + + # Affichage JSON pour intégration + cat </dev/null; then + log_message "INFO: Serveur redémarré avec succès" + else + log_message "ERREUR: Échec du redémarrage du serveur" + fi + ;; + "RCON_FAILED") + log_message "AVERTISSEMENT: RCON ne répond pas, vérification..." + # Attendre 60 secondes avant de considérer comme critique + sleep 60 + if [ "$(check_server_status)" = "RCON_FAILED" ]; then + log_message "ERREUR: RCON toujours en échec, redémarrage du serveur..." + systemctl restart "$SERVICE_NAME" + fi + ;; + "ONLINE") + # Tout va bien + ;; + esac +} + +# Main +case "${1:-status}" in + status) + check_server_status + ;; + metrics) + get_metrics + ;; + monitor) + auto_restart_check + ;; + *) + echo "Usage: $0 {status|metrics|monitor}" + exit 1 + ;; +esac \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/minecraft.service.j2 b/roles/03-installation-minecraft/templates/minecraft.service.j2 index b7c8cd8..f080b26 100644 --- a/roles/03-installation-minecraft/templates/minecraft.service.j2 +++ b/roles/03-installation-minecraft/templates/minecraft.service.j2 @@ -1,18 +1,54 @@ +# {{ ansible_managed }} [Unit] -Description=Minecraft Spigot Server -After=network.target +Description=Minecraft Server (Spigot {{ minecraft_version }}) +After=network-online.target +Wants=network-online.target [Service] -Type=forking +Type=simple User={{ minecraft_user }} Group={{ minecraft_group }} WorkingDirectory={{ minecraft_server_dir }} -ExecStart=/usr/bin/java -Xms{{ minecraft_memory_min }} -Xmx{{ minecraft_memory_max }} -jar spigot.jar nogui -ExecStop=/usr/local/bin/mcrcon -H 127.0.0.1 -P {{ rcon_port }} -p {{ rcon_password }} stop -TimeoutStartSec=600 -TimeoutStopSec=600 + +# Start command with optimized JVM flags +ExecStart=/usr/bin/java \ + -Xms{{ minecraft_memory_min }}M \ + -Xmx{{ minecraft_memory_max }}M \ + {{ jvm_flags }} \ + -jar {{ spigot_jar_name }} \ + --nogui + +# Stop command using mcrcon +ExecStop={{ minecraft_tools_dir }}/mcrcon -H localhost -P {{ rcon_port }} -p "{{ rcon_password }}" stop + +# Restart settings Restart=on-failure -RestartSec=20 +RestartSec=10 +StartLimitInterval=600 +StartLimitBurst=3 + +# Security settings +PrivateTmp=yes +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths={{ minecraft_base_dir }} +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictNamespaces=true +RestrictRealtime=true +RestrictSUIDSGID=true +LockPersonality=true + +# Resource limits +LimitNOFILE=100000 +LimitNPROC=512 + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=minecraft [Install] WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/ops.json.j2 b/roles/03-installation-minecraft/templates/ops.json.j2 index fc04429..58e60aa 100644 --- a/roles/03-installation-minecraft/templates/ops.json.j2 +++ b/roles/03-installation-minecraft/templates/ops.json.j2 @@ -1,10 +1,11 @@ [ {% for op in minecraft_ops %} -{ -"uuid": "{{ op.uuid }}", -"name": "{{ op.name }}", -"level": {{ op.level | default(4) }}, -"bypassesPlayerLimit": {{ op.bypass_player_limit | default(false) | ternary('true', 'false') }} -}{% if not loop.last %},{% endif %} + { + "uuid": "{{ op.uuid }}", + "name": "{{ op.name }}", + "level": {{ op.level | default(4) }}, + "bypassesPlayerLimit": {{ op.bypassesPlayerLimit | default(false) | lower }} + }{% if not loop.last %},{% endif %} + {% endfor %} ] \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/paper.yml.j2 b/roles/03-installation-minecraft/templates/paper.yml.j2 new file mode 100644 index 0000000..3ccf063 --- /dev/null +++ b/roles/03-installation-minecraft/templates/paper.yml.j2 @@ -0,0 +1,188 @@ +# {{ ansible_managed }} +# Paper configuration file - only used if Paper is detected +# Version: {{ minecraft_version }} + +verbose: false +config-version: 28 + +settings: + console-has-all-permissions: false + region-file-cache-size: 256 + incoming-packet-spam-threshold: 300 + max-joins-per-tick: 3 + track-plugin-scoreboards: false + suggest-player-names-when-null-tab-completions: true + use-display-name-in-quit-message: false + load-permissions-yml-before-plugins: true + bungee-online-mode: false + unsupported-settings: + allow-permanent-block-break-exploits: false + allow-piston-duplication: false + allow-headless-pistons: false + watchdog: + early-warning-every: 5000 + early-warning-delay: 10000 + spam-limiter: + recipe-spam-increment: 1 + recipe-spam-limit: 20 + tab-spam-increment: 1 + tab-spam-limit: 500 + book-size: + page-max: 2560 + total-multiplier: 0.98 + async-chunks: + enable: true + threads: -1 + velocity-support: + enabled: false + online-mode: false + secret: '' + +world-settings: + default: + fix-climbing-bypassing-cramming-rule: false + max-leash-distance: 10.0 + show-sign-click-command-failure-msgs-to-player: false + piglins-guard-chests: true + should-remove-dragon: false + max-auto-save-chunks-per-tick: 24 + baby-zombie-movement-modifier: 0.5 + optimize-explosions: false + disable-teleportation-suffocation-check: false + fixed-chunk-inhabited-time: -1 + use-vanilla-world-scoreboard-name-coloring: false + prevent-moving-into-unloaded-chunks: false + spawn-limits: + monsters: -1 + animals: -1 + water-animals: -1 + water-ambient: -1 + water-underground-creature: -1 + axolotls: -1 + ambient: -1 + despawn-ranges: + monster: + soft: 32 + hard: 128 + creature: + soft: 32 + hard: 128 + ambient: + soft: 32 + hard: 128 + axolotls: + soft: 32 + hard: 128 + water-creature: + soft: 32 + hard: 128 + water-ambient: + soft: 32 + hard: 64 + water-underground-creature: + soft: 32 + hard: 128 + misc: + soft: 32 + hard: 128 + frosted-ice: + enabled: true + delay: + min: 20 + max: 40 + lootables: + auto-replenish: false + restrict-player-reloot: true + reset-seed-on-fill: true + max-refills: -1 + refresh-min: 12h + refresh-max: 2d + filter-nbt-data-from-spawn-eggs-and-related: true + max-entity-collisions: 8 + disable-creeper-lingering-effect: false + duplicate-uuid-resolver: saferegen + duplicate-uuid-saferegen-delete-range: 32 + per-player-mob-spawns: true + experience-merge-max-value: -1 + prevent-tnt-from-moving-in-water: false + falling-block-height-nerf: 0 + tnt-entity-height-nerf: 0 + slime-spawn-height: + swamp-biome: + maximum: 70 + minimum: 50 + slime-chunk: + maximum: 40 + portal-search-radius: 128 + portal-create-radius: 16 + portal-search-vanilla-dimension-scaling: true + enable-treasure-maps: true + treasure-maps-find-already-discovered: + loot-tables: default + villager-trade: false + disable-thunder: false + disable-ice-and-snow: false + disable-explosion-knockback: false + keep-spawn-loaded: true + keep-spawn-loaded-range: 10 + auto-save-interval: -1 + disable-mob-spawner-spawn-egg-transformation: false + entities-target-with-follow-range: false + zombies-target-turtle-eggs: true + zombie-villager-infection-chance: -1.0 + all-chunks-are-slime-chunks: false + mob-spawner-tick-rate: 1 + light-queue-size: 20 + grass-spread-tick-rate: 1 + redstone-implementation: vanilla + armor-stands-tick: true + water-over-lava-flow-speed: 5 + use-faster-eigencraft-redstone: false + disable-move-event: false + container-update-tick-rate: 1 + non-player-arrow-despawn-rate: -1 + creative-arrow-despawn-rate: -1 + skeleton-horse-thunder-spawn-chance: 0.01 + disable-ice-and-snow: false + iron-golems-can-spawn-in-air: false + count-all-mobs-for-spawning: false + delay-chunk-unloads-by: 10s + seed-based-feature-search: true + seed-based-feature-search-loads-chunks: true + water-flow-speed: 5 + lava-flow-speed: + normal: 30 + nether: 10 + fix-items-merging-through-walls: false + prevent-lightning-strike-blocks-from-decaying: false + squid-spawn-height: + minimum: 45 + maximum: 0 + disable-pillager-patrols: false + game-mechanics: + scan-for-legacy-ender-dragon: true + disable-chest-cat-detection: false + nerf-pigmen-from-nether-portals: false + disable-player-crits: false + disable-sprint-interruption-on-attack: false + shield-blocking-delay: 5 + disable-end-credits: false + disable-relative-projectile-velocity: false + disable-mob-spawner-spawn-egg-transformation: false + pillager-patrols: + spawn-chance: 0.2 + spawn-delay: + per-player: false + ticks: 12000 + start: + per-player: false + day: 5 + max-growth-height: + cactus: 3 + reeds: 3 + bamboo: + max: 16 + min: 11 + fishing-time-range: + minimum: 100 + maximum: 600 \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/permissions.yml.j2 b/roles/03-installation-minecraft/templates/permissions.yml.j2 new file mode 100644 index 0000000..19988ab --- /dev/null +++ b/roles/03-installation-minecraft/templates/permissions.yml.j2 @@ -0,0 +1,54 @@ +# {{ ansible_managed }} +# Permissions configuration for Bukkit + +# Default permissions +default: + default: true + permissions: + bukkit.command.help: true + bukkit.command.list: true + bukkit.command.me: true + bukkit.command.msg: true + bukkit.command.tell: true + bukkit.command.trigger: true + +# Moderator permissions +moderator: + default: false + inheritance: + - default + permissions: + bukkit.command.kick: true + bukkit.command.ban: true + bukkit.command.ban.ip: true + bukkit.command.ban.list: true + bukkit.command.unban: true + bukkit.command.unban.ip: true + bukkit.command.pardon: true + bukkit.command.pardon.ip: true + bukkit.command.whitelist: true + bukkit.command.whitelist.add: true + bukkit.command.whitelist.remove: true + bukkit.command.whitelist.list: true + bukkit.command.whitelist.on: true + bukkit.command.whitelist.off: true + bukkit.command.whitelist.reload: true + +# Admin permissions +admin: + default: false + inheritance: + - moderator + permissions: + bukkit.command.op: true + bukkit.command.op.give: true + bukkit.command.op.take: true + bukkit.command.save: true + bukkit.command.save.disable: true + bukkit.command.save.enable: true + bukkit.command.save.perform: true + bukkit.command.stop: true + bukkit.command.restart: true + bukkit.command.reload: true + bukkit.command.version: true + bukkit.command.plugins: true \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/server.properties.j2 b/roles/03-installation-minecraft/templates/server.properties.j2 index fe31a67..bce7db6 100644 --- a/roles/03-installation-minecraft/templates/server.properties.j2 +++ b/roles/03-installation-minecraft/templates/server.properties.j2 @@ -1,46 +1,66 @@ -# Minecraft server properties -# Generated by Ansible +# {{ ansible_managed }} +# Minecraft server properties - Generated on {{ ansible_date_time.iso8601 }} -# Server settings -server-name={{ server_name }} -motd={{ server_motd }} -server-port={{ minecraft_port }} -max-players={{ max_players }} - -# Game settings -gamemode={{ gamemode }} -difficulty={{ difficulty }} -pvp={{ pvp_enabled | ternary('true', 'false') }} -hardcore=false -enable-command-block=false -spawn-protection=16 -max-world-size=29999984 - -# RCON settings -enable-rcon={{ rcon_enabled | ternary('true', 'false') }} +# Server Settings +server-port={{ server_port }} +server-ip= +max-tick-time=60000 +enable-rcon={{ enable_rcon | lower }} rcon.port={{ rcon_port }} rcon.password={{ rcon_password }} - -# Network settings -server-ip= -online-mode=true -white-list=false -enable-status=true enable-query=false -query.port=25565 +query.port={{ server_port }} -# World generation -level-name=world -level-seed= -level-type=minecraft\:normal -generate-structures=true -spawn-animals=true -spawn-monsters=true -spawn-npcs=true - -# Performance settings -view-distance=10 +# Gameplay +gamemode={{ gamemode }} +difficulty={{ difficulty }} +hardcore=false +pvp={{ pvp | lower }} +enable-command-block={{ enable_command_block | lower }} +max-players={{ max_players }} +online-mode={{ online_mode | lower }} +white-list={{ white_list | lower }} +enforce-whitelist={{ white_list | lower }} +op-permission-level=4 +player-idle-timeout=0 +prevent-proxy-connections=false +hide-online-players=false simulation-distance=10 -max-tick-time=60000 + +# World +level-name=world +level-seed={{ level_seed }} +level-type={{ level_type }} +generate-structures={{ generate_structures | lower }} +spawn-protection={{ spawn_protection }} +max-world-size={{ max_world_size }} +max-build-height=319 +view-distance={{ view_distance }} +spawn-animals={{ spawn_animals | lower }} +spawn-monsters={{ spawn_monsters | lower }} +spawn-npcs={{ spawn_npcs | lower }} +allow-nether=true + +# Performance +network-compression-threshold=256 +rate-limit=0 +sync-chunk-writes=true use-native-transport=true -enable-jmx-monitoring=false \ No newline at end of file +allow-flight=false + +# Messages +motd={{ server_name }} +resource-pack= +resource-pack-prompt= +resource-pack-sha1= +require-resource-pack=false + +# Advanced +enable-jmx-monitoring=false +enable-status=true +entity-broadcast-range-percentage=100 +force-gamemode=false +function-permission-level=2 +initial-disabled-packs= +initial-enabled-packs=vanilla +text-filtering-config= \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/spigot.yml.j2 b/roles/03-installation-minecraft/templates/spigot.yml.j2 index e69de29..ad94202 100644 --- a/roles/03-installation-minecraft/templates/spigot.yml.j2 +++ b/roles/03-installation-minecraft/templates/spigot.yml.j2 @@ -0,0 +1,130 @@ +# {{ ansible_managed }} +# This is the main configuration file for Spigot. +# Spigot version: {{ minecraft_version }} + +settings: + debug: false + save-user-cache-on-stop-only: false + bungeecord: false + late-bind: false + sample-count: 12 + player-shuffle: 0 + user-cache-size: 1000 + moved-wrongly-threshold: 0.0625 + moved-too-quickly-multiplier: 10.0 + timeout-time: 60 + restart-on-crash: true + restart-script: {{ minecraft_base_dir }}/scripts/start.sh + netty-threads: 4 + log-villager-deaths: true + log-named-deaths: true + attribute: + maxHealth: + max: 2048.0 + movementSpeed: + max: 2048.0 + attackDamage: + max: 2048.0 + +messages: + whitelist: You are not whitelisted on this server! + unknown-command: Unknown command. Type "/help" for help. + server-full: The server is full! + outdated-client: Outdated client! Please use {0} + outdated-server: Outdated server! I'm still on {0} + restart: Server is restarting + +commands: + replace-commands: + - setblock + - summon + - testforblock + - tellraw + spam-exclusions: + - /skill + silent-commandblock-console: false + log: true + tab-complete: -1 + send-namespaced: true + +advancements: + disable-saving: false + disabled: + - minecraft:story/disabled + +players: + disable-saving: false + +world-settings: + default: + below-zero-generation-in-existing-chunks: true + merge-radius: + exp: 3.0 + item: 2.5 + growth: + cactus-modifier: 100 + cane-modifier: 100 + melon-modifier: 100 + mushroom-modifier: 100 + pumpkin-modifier: 100 + sapling-modifier: 100 + beetroot-modifier: 100 + carrot-modifier: 100 + potato-modifier: 100 + wheat-modifier: 100 + netherwart-modifier: 100 + vine-modifier: 100 + cocoa-modifier: 100 + bamboo-modifier: 100 + sweetberry-modifier: 100 + kelp-modifier: 100 + entity-activation-range: + animals: 32 + monsters: 32 + raiders: 48 + misc: 16 + tick-inactive-villagers: true + entity-tracking-range: + players: 48 + animals: 48 + monsters: 48 + misc: 32 + other: 64 + ticks-per: + hopper-transfer: 8 + hopper-check: 1 + hopper-amount: 1 + save-structure-info: true + dragon-death-sound-radius: 0 + seed-village: 10387312 + seed-desert: 14357617 + seed-igloo: 14357618 + seed-jungle: 14357619 + seed-swamp: 14357620 + seed-monument: 10387313 + seed-shipwreck: 165745295 + seed-ocean: 14357621 + seed-outpost: 165745296 + seed-endcity: 10387313 + seed-slime: 987234911 + seed-bastion: 30084232 + seed-fortress: 30084232 + seed-mansion: 10387319 + seed-fossil: 14357921 + seed-portal: 34222645 + max-tnt-per-tick: 100 + enable-zombie-pigmen-portal-spawns: true + item-despawn-rate: 6000 + view-distance: default + simulation-distance: default + thunder-chance: 100000 + wither-spawn-sound-radius: 0 + arrow-despawn-rate: 1200 + trident-despawn-rate: 1200 + hanging-tick-frequency: 100 + zombie-aggressive-towards-villager: true + nerf-spawner-mobs: false + mob-spawn-range: 8 + end-portal-sound-radius: 0 + +config-version: 12 \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/start.sh.j2 b/roles/03-installation-minecraft/templates/start.sh.j2 new file mode 100644 index 0000000..9979e29 --- /dev/null +++ b/roles/03-installation-minecraft/templates/start.sh.j2 @@ -0,0 +1,17 @@ +#!/bin/bash +# {{ ansible_managed }} + +# Configuration +SERVER_DIR="{{ minecraft_server_dir }}" +JAR_FILE="{{ spigot_jar_name }}" +MIN_RAM="{{ minecraft_memory_min }}M" +MAX_RAM="{{ minecraft_memory_max }}M" + +# JVM Flags optimisés pour Minecraft +JVM_FLAGS="{{ jvm_flags }}" + +# Aller dans le répertoire du serveur +cd "$SERVER_DIR" || exit 1 + +# Démarrer le serveur +exec java -Xms${MIN_RAM} -Xmx${MAX_RAM} ${JVM_FLAGS} -jar ${JAR_FILE} --nogui \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/stop.sh.j2 b/roles/03-installation-minecraft/templates/stop.sh.j2 new file mode 100644 index 0000000..b08b881 --- /dev/null +++ b/roles/03-installation-minecraft/templates/stop.sh.j2 @@ -0,0 +1,39 @@ +#!/bin/bash +# {{ ansible_managed }} + +MCRCON="{{ minecraft_tools_dir }}/mcrcon" +RCON_HOST="localhost" +RCON_PORT="{{ rcon_port }}" +RCON_PASS="{{ rcon_password }}" + +# Fonction pour envoyer des commandes au serveur +send_command() { + $MCRCON -H $RCON_HOST -P $RCON_PORT -p "$RCON_PASS" "$1" +} + +# Avertir les joueurs +echo "Avertissement des joueurs..." +send_command "say Le serveur va redémarrer dans 60 secondes!" +sleep 30 + +send_command "say Le serveur va redémarrer dans 30 secondes!" +sleep 20 + +send_command "say Le serveur va redémarrer dans 10 secondes!" +sleep 5 + +for i in 5 4 3 2 1; do + send_command "say Arrêt dans $i..." + sleep 1 +done + +# Sauvegarder le monde +echo "Sauvegarde du monde..." +send_command "save-all" +sleep 5 + +# Arrêter le serveur +echo "Arrêt du serveur..." +send_command "stop" + +echo "Serveur arrêté proprement." \ No newline at end of file diff --git a/roles/03-installation-minecraft/templates/whitelist.json.j2 b/roles/03-installation-minecraft/templates/whitelist.json.j2 new file mode 100644 index 0000000..07623bb --- /dev/null +++ b/roles/03-installation-minecraft/templates/whitelist.json.j2 @@ -0,0 +1,9 @@ +[ +{% for player in minecraft_whitelist | default([]) %} + { + "uuid": "{{ player.uuid }}", + "name": "{{ player.name }}" + }{% if not loop.last %},{% endif %} + +{% endfor %} +] \ No newline at end of file diff --git a/roles/03-installation-minecraft/vars/main.yml b/roles/03-installation-minecraft/vars/main.yml index 8003bb7..b336899 100644 --- a/roles/03-installation-minecraft/vars/main.yml +++ b/roles/03-installation-minecraft/vars/main.yml @@ -1,21 +1,32 @@ --- -# Internal variables for Minecraft installation -required_packages: - - openjdk-17-jdk +# Variables internes du rôle +spigot_jar_name: "spigot-{{ minecraft_version }}.jar" +buildtools_jar: "BuildTools.jar" +server_jar_path: "{{ minecraft_server_dir }}/{{ spigot_jar_name }}" + +# Liste des répertoires à créer +minecraft_dirs: + - "{{ minecraft_base_dir }}" + - "{{ minecraft_server_dir }}" + - "{{ minecraft_sources_dir }}" + - "{{ minecraft_tools_dir }}" + - "{{ minecraft_server_dir }}/plugins" + - "{{ minecraft_server_dir }}/world" + - "{{ minecraft_server_dir }}/world_nether" + - "{{ minecraft_server_dir }}/world_the_end" + - "{{ minecraft_server_dir }}/logs" + - "{{ minecraft_server_dir }}/crash-reports" + - "{{ minecraft_base_dir }}/scripts" + +# Packages requis pour la compilation +build_packages: - git - build-essential - screen + - htop + - iotop + - wget + - curl -spigot_build_tools_url: "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" - -mcrcon_repository: "https://github.com/Tiiffi/mcrcon.git" - -# Default server configuration -default_server_properties: - server-port: 25565 - max-players: 20 - gamemode: survival - difficulty: normal - pvp: true - online-mode: true - white-list: false \ No newline at end of file +# Configuration JVM +jvm_flags: "-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1" \ No newline at end of file diff --git a/roles/04-backups/defaults/main.yml b/roles/04-backups/defaults/main.yml index 94def38..f525f21 100644 --- a/roles/04-backups/defaults/main.yml +++ b/roles/04-backups/defaults/main.yml @@ -1,26 +1,11 @@ --- -# Backup configuration -backup_base_dir: "{{ minecraft_base_dir }}/backups" +backup_base_dir: /opt/minecraft/backups backup_daily_dir: "{{ backup_base_dir }}/daily" backup_weekly_dir: "{{ backup_base_dir }}/weekly" backup_monthly_dir: "{{ backup_base_dir }}/monthly" - -# Retention settings -backup_retention_days: 7 -backup_retention_weeks: 4 -backup_retention_months: 6 - -# Source directories to backup -backup_sources: - - "{{ minecraft_server_dir }}" - - "{{ minecraft_base_dir }}/logs" - -# Backup schedule -backup_daily_time: "02:00" -backup_weekly_time: "03:00" -backup_weekly_day: "0" # Sunday -backup_monthly_time: "04:00" -backup_monthly_day: "1" # First day of month - -# Rsync options -rsync_options: "-avz --delete" \ No newline at end of file +backup_retention_daily: 7 +backup_retention_weekly: 4 +backup_retention_monthly: 3 +backup_time_daily: "03:00" +backup_time_weekly: "04:00" +backup_time_monthly: "05:00" \ No newline at end of file diff --git a/roles/04-backups/handlers/main.yml b/roles/04-backups/handlers/main.yml index 94def38..8afb668 100644 --- a/roles/04-backups/handlers/main.yml +++ b/roles/04-backups/handlers/main.yml @@ -1,26 +1,6 @@ --- -# Backup configuration -backup_base_dir: "{{ minecraft_base_dir }}/backups" -backup_daily_dir: "{{ backup_base_dir }}/daily" -backup_weekly_dir: "{{ backup_base_dir }}/weekly" -backup_monthly_dir: "{{ backup_base_dir }}/monthly" - -# Retention settings -backup_retention_days: 7 -backup_retention_weeks: 4 -backup_retention_months: 6 - -# Source directories to backup -backup_sources: - - "{{ minecraft_server_dir }}" - - "{{ minecraft_base_dir }}/logs" - -# Backup schedule -backup_daily_time: "02:00" -backup_weekly_time: "03:00" -backup_weekly_day: "0" # Sunday -backup_monthly_time: "04:00" -backup_monthly_day: "1" # First day of month - -# Rsync options -rsync_options: "-avz --delete" \ No newline at end of file +- name: reload cron + ansible.builtin.service: + name: cron + state: reloaded + listen: reload cron service \ No newline at end of file diff --git a/roles/04-backups/tasks/01-create-backup-directories.yml b/roles/04-backups/tasks/01-create-backup-directories.yml deleted file mode 100644 index ab75bbe..0000000 --- a/roles/04-backups/tasks/01-create-backup-directories.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -- name: Create backup directories - ansible.builtin.file: - path: "{{ item }}" - state: directory - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - loop: - - "{{ backup_base_dir }}" - - "{{ backup_daily_dir }}" - - "{{ backup_weekly_dir }}" - - "{{ backup_monthly_dir }}" - -- name: Install rsync - ansible.builtin.apt: - name: rsync - state: present \ No newline at end of file diff --git a/roles/04-backups/tasks/01-create-backup-dirs.yml b/roles/04-backups/tasks/01-create-backup-dirs.yml new file mode 100644 index 0000000..683a432 --- /dev/null +++ b/roles/04-backups/tasks/01-create-backup-dirs.yml @@ -0,0 +1,9 @@ +--- +- name: Create backup directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + loop: "{{ backup_dirs }}" \ No newline at end of file diff --git a/roles/04-backups/tasks/02-install-rsync.yml b/roles/04-backups/tasks/02-install-rsync.yml new file mode 100644 index 0000000..ef7112e --- /dev/null +++ b/roles/04-backups/tasks/02-install-rsync.yml @@ -0,0 +1,6 @@ +--- +- name: Install rsync + ansible.builtin.apt: + name: rsync + state: present + when: ansible_os_family == "Debian" \ No newline at end of file diff --git a/roles/04-backups/tasks/02-setup-daily-backup.yml b/roles/04-backups/tasks/02-setup-daily-backup.yml deleted file mode 100644 index 2117c1b..0000000 --- a/roles/04-backups/tasks/02-setup-daily-backup.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: Create daily backup script - ansible.builtin.template: - src: backup-daily.sh.j2 - dest: "{{ minecraft_tools_dir }}/backup-daily.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - -- name: Setup daily backup cron job - ansible.builtin.cron: - name: "Minecraft daily backup" - minute: "0" - hour: "{{ backup_daily_time.split(':')[0] }}" - job: "{{ minecraft_tools_dir }}/backup-daily.sh" - user: "{{ minecraft_user }}" - state: present \ No newline at end of file diff --git a/roles/04-backups/tasks/03-configure-backup-script.yml b/roles/04-backups/tasks/03-configure-backup-script.yml new file mode 100644 index 0000000..1400481 --- /dev/null +++ b/roles/04-backups/tasks/03-configure-backup-script.yml @@ -0,0 +1,16 @@ +--- +- name: Create backup script + ansible.builtin.template: + src: backup.sh.j2 + dest: "{{ minecraft_base_dir }}/backup.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Create restore script + ansible.builtin.template: + src: restore.sh.j2 + dest: "{{ minecraft_base_dir }}/restore.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' \ No newline at end of file diff --git a/roles/04-backups/tasks/03-setup-weekly-backup.yml b/roles/04-backups/tasks/03-setup-weekly-backup.yml deleted file mode 100644 index 82a843d..0000000 --- a/roles/04-backups/tasks/03-setup-weekly-backup.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -- name: Create weekly backup script - ansible.builtin.template: - src: backup-weekly.sh.j2 - dest: "{{ minecraft_tools_dir }}/backup-weekly.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - -- name: Setup weekly backup cron job - ansible.builtin.cron: - name: "Minecraft weekly backup" - minute: "0" - hour: "{{ backup_weekly_time.split(':')[0] }}" - weekday: "{{ backup_weekly_day }}" - job: "{{ minecraft_tools_dir }}/backup-weekly.sh" - user: "{{ minecraft_user }}" - state: present \ No newline at end of file diff --git a/roles/04-backups/tasks/04-setup-cron.yml b/roles/04-backups/tasks/04-setup-cron.yml new file mode 100644 index 0000000..de5fab8 --- /dev/null +++ b/roles/04-backups/tasks/04-setup-cron.yml @@ -0,0 +1,29 @@ +--- +- name: Setup daily backup cron job + ansible.builtin.cron: + name: "Minecraft daily backup" + user: "{{ minecraft_user }}" + hour: "{{ backup_time_daily.split(':')[0] }}" + minute: "{{ backup_time_daily.split(':')[1] }}" + job: "{{ minecraft_base_dir }}/backup.sh daily" + notify: reload cron service + +- name: Setup weekly backup cron job + ansible.builtin.cron: + name: "Minecraft weekly backup" + user: "{{ minecraft_user }}" + hour: "{{ backup_time_weekly.split(':')[0] }}" + minute: "{{ backup_time_weekly.split(':')[1] }}" + weekday: "0" + job: "{{ minecraft_base_dir }}/backup.sh weekly" + notify: reload cron service + +- name: Setup monthly backup cron job + ansible.builtin.cron: + name: "Minecraft monthly backup" + user: "{{ minecraft_user }}" + hour: "{{ backup_time_monthly.split(':')[0] }}" + minute: "{{ backup_time_monthly.split(':')[1] }}" + day: "1" + job: "{{ minecraft_base_dir }}/backup.sh monthly" + notify: reload cron service \ No newline at end of file diff --git a/roles/04-backups/tasks/04-setup-monthly-backup.yml b/roles/04-backups/tasks/04-setup-monthly-backup.yml deleted file mode 100644 index c8a8071..0000000 --- a/roles/04-backups/tasks/04-setup-monthly-backup.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -- name: Create monthly backup script - ansible.builtin.template: - src: backup-monthly.sh.j2 - dest: "{{ minecraft_tools_dir }}/backup-monthly.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - -- name: Setup monthly backup cron job - ansible.builtin.cron: - name: "Minecraft monthly backup" - minute: "0" - hour: "{{ backup_monthly_time.split(':')[0] }}" - day: "{{ backup_monthly_day }}" - job: "{{ minecraft_tools_dir }}/backup-monthly.sh" - user: "{{ minecraft_user }}" - state: present \ No newline at end of file diff --git a/roles/04-backups/tasks/05-restore-backup.ym b/roles/04-backups/tasks/05-restore-backup.ym new file mode 100644 index 0000000..177bdfe --- /dev/null +++ b/roles/04-backups/tasks/05-restore-backup.ym @@ -0,0 +1,26 @@ +--- +- name: Create restore script + ansible.builtin.template: + src: restore.sh.j2 + dest: "{{ minecraft_base_dir }}/restore.sh" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Stop minecraft server before restore + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + state: stopped + when: restore_backup_type is defined + +- name: Execute restore + ansible.builtin.command: + cmd: "{{ minecraft_base_dir }}/restore.sh {{ restore_backup_type }} {{ restore_backup_date | default('latest') }}" + become_user: "{{ minecraft_user }}" + when: restore_backup_type is defined + +- name: Start minecraft server after restore + ansible.builtin.systemd: + name: "{{ minecraft_service_name }}" + state: started + when: restore_backup_type is defined \ No newline at end of file diff --git a/roles/04-backups/tasks/05-setup-backup-scripts.yml b/roles/04-backups/tasks/05-setup-backup-scripts.yml deleted file mode 100644 index d27591d..0000000 --- a/roles/04-backups/tasks/05-setup-backup-scripts.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -- name: Create backup utility script - ansible.builtin.copy: - content: | - #!/bin/bash - # Backup utility functions - - # Function to stop Minecraft server safely - stop_minecraft() { - if systemctl is-active --quiet minecraft; then - echo "Stopping Minecraft server..." - /usr/local/bin/mcrcon -H 127.0.0.1 -P {{ rcon_port }} -p "{{ rcon_password }}" "say Server backup starting in 30 seconds..." - sleep 30 - /usr/local/bin/mcrcon -H 127.0.0.1 -P {{ rcon_port }} -p "{{ rcon_password }}" stop - sleep 10 - fi - } - - # Function to start Minecraft server - start_minecraft() { - if ! systemctl is-active --quiet minecraft; then - echo "Starting Minecraft server..." - systemctl start minecraft - fi - } - - # Function to clean old backups - clean_old_backups() { - local backup_dir=$1 - local retention_days=$2 - - find "$backup_dir" -type f -mtime +$retention_days -delete - find "$backup_dir" -type d -empty -delete - } - dest: "{{ minecraft_tools_dir }}/backup-functions.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' \ No newline at end of file diff --git a/roles/04-backups/tasks/06-setup-restore-scripts.yml b/roles/04-backups/tasks/06-setup-restore-scripts.yml deleted file mode 100644 index 6f8a6ac..0000000 --- a/roles/04-backups/tasks/06-setup-restore-scripts.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -- name: Create restore backup script - ansible.builtin.template: - src: restore-backup.sh.j2 - dest: "{{ minecraft_tools_dir }}/restore-backup.sh" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' \ No newline at end of file diff --git a/roles/04-backups/tasks/main.yml b/roles/04-backups/tasks/main.yml index fb96441..4e7dba6 100644 --- a/roles/04-backups/tasks/main.yml +++ b/roles/04-backups/tasks/main.yml @@ -1,18 +1,16 @@ --- -- name: Include backup directories creation tasks - ansible.builtin.include_tasks: 01-create-backup-directories.yml +- name: Include create backup directories tasks + ansible.builtin.include_tasks: 01-create-backup-dirs.yml -- name: Include daily backup setup tasks - ansible.builtin.include_tasks: 02-setup-daily-backup.yml +- name: Include install rsync tasks + ansible.builtin.include_tasks: 02-install-rsync.yml -- name: Include weekly backup setup tasks - ansible.builtin.include_tasks: 03-setup-weekly-backup.yml +- name: Include configure backup script tasks + ansible.builtin.include_tasks: 03-configure-backup-script.yml -- name: Include monthly backup setup tasks - ansible.builtin.include_tasks: 04-setup-monthly-backup.yml +- name: Include setup cron tasks + ansible.builtin.include_tasks: 04-setup-cron.yml -- name: Include backup scripts setup tasks - ansible.builtin.include_tasks: 05-setup-backup-scripts.yml - -- name: Include restore scripts setup tasks - ansible.builtin.include_tasks: 06-setup-restore-scripts.yml \ No newline at end of file +- name: Include restore backup tasks + ansible.builtin.include_tasks: 05-restore-backup.yml + when: restore_backup | default(false) \ No newline at end of file diff --git a/roles/04-backups/templates/backup-daily.sh.j2 b/roles/04-backups/templates/backup-daily.sh.j2 deleted file mode 100644 index 4cbab2a..0000000 --- a/roles/04-backups/templates/backup-daily.sh.j2 +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Daily Minecraft Backup Script -# Generated by Ansible - -set -e - -# Source backup functions -source {{ minecraft_tools_dir }}/backup-functions.sh - -# Configuration -BACKUP_DIR="{{ backup_daily_dir }}" -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_NAME="daily_backup_$DATE" -LOG_FILE="{{ minecraft_base_dir }}/logs/backup-daily.log" - -# Create log entry -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting daily backup..." >> "$LOG_FILE" - -# Create backup directory -mkdir -p "$BACKUP_DIR/$BACKUP_NAME" - -# Stop Minecraft server for consistent backup -stop_minecraft - -# Perform backup -{% for source in backup_sources %} -echo "Backing up {{ source }}..." >> "$LOG_FILE" -rsync {{ rsync_options }} "{{ source }}/" "$BACKUP_DIR/$BACKUP_NAME/$(basename {{ source }})/" -{% endfor %} - -# Start Minecraft server -start_minecraft - -# Clean old daily backups -clean_old_backups "$BACKUP_DIR" {{ backup_retention_days }} - -# Compress backup -cd "$BACKUP_DIR" -tar -czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME" -rm -rf "$BACKUP_NAME" - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Daily backup completed: ${BACKUP_NAME}.tar.gz" >> "$LOG_FILE" \ No newline at end of file diff --git a/roles/04-backups/templates/backup-monthly.sh.j2 b/roles/04-backups/templates/backup-monthly.sh.j2 deleted file mode 100644 index 60eb9ef..0000000 --- a/roles/04-backups/templates/backup-monthly.sh.j2 +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Monthly Minecraft Backup Script -# Generated by Ansible - -set -e - -# Source backup functions -source {{ minecraft_tools_dir }}/backup-functions.sh - -# Configuration -BACKUP_DIR="{{ backup_monthly_dir }}" -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_NAME="monthly_backup_$DATE" -LOG_FILE="{{ minecraft_base_dir }}/logs/backup-monthly.log" - -# Create log entry -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting monthly backup..." >> "$LOG_FILE" - -# Create backup directory -mkdir -p "$BACKUP_DIR/$BACKUP_NAME" - -# Stop Minecraft server for consistent backup -stop_minecraft - -# Perform backup -{% for source in backup_sources %} -echo "Backing up {{ source }}..." >> "$LOG_FILE" -rsync {{ rsync_options }} "{{ source }}/" "$BACKUP_DIR/$BACKUP_NAME/$(basename {{ source }})/" -{% endfor %} - -# Start Minecraft server -start_minecraft - -# Clean old monthly backups (convert months to days approximately) -clean_old_backups "$BACKUP_DIR" $(({{ backup_retention_months }} * 30)) - -# Compress backup -cd "$BACKUP_DIR" -tar -czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME" -rm -rf "$BACKUP_NAME" - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Monthly backup completed: ${BACKUP_NAME}.tar.gz" >> "$LOG_FILE" \ No newline at end of file diff --git a/roles/04-backups/templates/backup-weekly.sh.j2 b/roles/04-backups/templates/backup-weekly.sh.j2 deleted file mode 100644 index a1a55e9..0000000 --- a/roles/04-backups/templates/backup-weekly.sh.j2 +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Weekly Minecraft Backup Script -# Generated by Ansible - -set -e - -# Source backup functions -source {{ minecraft_tools_dir }}/backup-functions.sh - -# Configuration -BACKUP_DIR="{{ backup_weekly_dir }}" -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_NAME="weekly_backup_$DATE" -LOG_FILE="{{ minecraft_base_dir }}/logs/backup-weekly.log" - -# Create log entry -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting weekly backup..." >> "$LOG_FILE" - -# Create backup directory -mkdir -p "$BACKUP_DIR/$BACKUP_NAME" - -# Stop Minecraft server for consistent backup -stop_minecraft - -# Perform backup -{% for source in backup_sources %} -echo "Backing up {{ source }}..." >> "$LOG_FILE" -rsync {{ rsync_options }} "{{ source }}/" "$BACKUP_DIR/$BACKUP_NAME/$(basename {{ source }})/" -{% endfor %} - -# Start Minecraft server -start_minecraft - -# Clean old weekly backups (convert weeks to days) -clean_old_backups "$BACKUP_DIR" $(({{ backup_retention_weeks }} * 7)) - -# Compress backup -cd "$BACKUP_DIR" -tar -czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME" -rm -rf "$BACKUP_NAME" - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Weekly backup completed: ${BACKUP_NAME}.tar.gz" >> "$LOG_FILE" \ No newline at end of file diff --git a/roles/04-backups/templates/backup.sh.j2 b/roles/04-backups/templates/backup.sh.j2 new file mode 100644 index 0000000..b0d127e --- /dev/null +++ b/roles/04-backups/templates/backup.sh.j2 @@ -0,0 +1,69 @@ +#!/bin/bash +# {{ ansible_managed }} + +BACKUP_TYPE=$1 +BACKUP_BASE="{{ backup_base_dir }}" +SERVER_DIR="{{ minecraft_server_dir }}" +TOOLS_DIR="{{ minecraft_tools_dir }}" +RCON_CMD="{{ minecraft_tools_dir }}/mcrcon -H localhost -P {{ rcon_port }} -p {{ rcon_password }}" +DATE=$(date +%Y%m%d_%H%M%S) + +# Function to clean old backups +clean_old_backups() { + local dir=$1 + local keep=$2 + ls -1t $dir/*.tar.gz 2>/dev/null | tail -n +$((keep+1)) | xargs rm -f +} + +# Function to perform backup +perform_backup() { + local backup_dir=$1 + local retention=$2 + + echo "Starting $BACKUP_TYPE backup at $(date)" + + # Notify players + $RCON_CMD "say Server backup starting in 10 seconds..." + sleep 10 + + # Save world + $RCON_CMD "save-all" + sleep 5 + + # Disable auto-save + $RCON_CMD "save-off" + + # Create backup + tar -czf "$backup_dir/minecraft_${BACKUP_TYPE}_${DATE}.tar.gz" \ + -C "{{ minecraft_base_dir }}" \ + server/ sources/ tools/ \ + --exclude='*.log' \ + --exclude='logs/*' + + # Re-enable auto-save + $RCON_CMD "save-on" + + # Clean old backups + clean_old_backups "$backup_dir" "$retention" + + # Notify players + $RCON_CMD "say Server backup completed!" + + echo "Backup completed at $(date)" +} + +case "$BACKUP_TYPE" in + daily) + perform_backup "{{ backup_daily_dir }}" "{{ backup_retention_daily }}" + ;; + weekly) + perform_backup "{{ backup_weekly_dir }}" "{{ backup_retention_weekly }}" + ;; + monthly) + perform_backup "{{ backup_monthly_dir }}" "{{ backup_retention_monthly }}" + ;; + *) + echo "Usage: $0 {daily|weekly|monthly}" + exit 1 + ;; +esac \ No newline at end of file diff --git a/roles/04-backups/templates/restore-backup.sh.j2 b/roles/04-backups/templates/restore-backup.sh.j2 deleted file mode 100644 index e8efe87..0000000 --- a/roles/04-backups/templates/restore-backup.sh.j2 +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -# Minecraft Backup Restore Script -# Generated by Ansible - -set -e - -# Usage function -usage() { - echo "Usage: $0 " - echo "backup_type: daily, weekly, or monthly" - echo "backup_file: name of the backup file to restore" - echo "" - echo "Example: $0 daily daily_backup_20241225_120000.tar.gz" - exit 1 -} - -# Check arguments -if [ $# -ne 2 ]; then - usage -fi - -BACKUP_TYPE="$1" -BACKUP_FILE="$2" -LOG_FILE="{{ minecraft_base_dir }}/logs/restore.log" - -# Source backup functions -source {{ minecraft_tools_dir }}/backup-functions.sh - -# Validate backup type -case "$BACKUP_TYPE" in - daily) - BACKUP_DIR="{{ backup_daily_dir }}" - ;; - weekly) - BACKUP_DIR="{{ backup_weekly_dir }}" - ;; - monthly) - BACKUP_DIR="{{ backup_monthly_dir }}" - ;; - *) - echo "Invalid backup type: $BACKUP_TYPE" - usage - ;; -esac - -# Check if backup file exists -BACKUP_PATH="$BACKUP_DIR/$BACKUP_FILE" -if [ ! -f "$BACKUP_PATH" ]; then - echo "Backup file not found: $BACKUP_PATH" - exit 1 -fi - -# Confirmation -echo "WARNING: This will replace the current Minecraft server data!" -echo "Backup file: $BACKUP_PATH" -echo "Type 'yes' to continue:" -read -r confirmation -if [ "$confirmation" != "yes" ]; then - echo "Restore cancelled." - exit 0 -fi - -# Start restoration process -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting restore from $BACKUP_FILE..." >> "$LOG_FILE" - -# Stop Minecraft server -stop_minecraft - -# Create temporary restore directory -TEMP_DIR="/tmp/minecraft_restore_$" -mkdir -p "$TEMP_DIR" - -# Extract backup -echo "Extracting backup..." >> "$LOG_FILE" -cd "$TEMP_DIR" -tar -xzf "$BACKUP_PATH" - -# Find the extracted directory -EXTRACTED_DIR=$(find . -maxdepth 1 -type d -name "*backup*" | head -1) -if [ -z "$EXTRACTED_DIR" ]; then - echo "Could not find extracted backup directory" - rm -rf "$TEMP_DIR" - exit 1 -fi - -# Backup current data -CURRENT_BACKUP="{{ minecraft_base_dir }}/current_backup_$(date +%Y%m%d_%H%M%S)" -mkdir -p "$CURRENT_BACKUP" -{% for source in backup_sources %} -if [ -d "{{ source }}" ]; then - cp -r "{{ source }}" "$CURRENT_BACKUP/" -fi -{% endfor %} - -echo "Current data backed up to: $CURRENT_BACKUP" >> "$LOG_FILE" - -# Restore data -echo "Restoring data..." >> "$LOG_FILE" -{% for source in backup_sources %} -SOURCE_NAME=$(basename {{ source }}) -if [ -d "$EXTRACTED_DIR/$SOURCE_NAME" ]; then - rm -rf "{{ source }}" - cp -r "$EXTRACTED_DIR/$SOURCE_NAME" "{{ source }}" - chown -R {{ minecraft_user }}:{{ minecraft_group }} "{{ source }}" -fi -{% endfor %} - -# Cleanup temporary directory -rm -rf "$TEMP_DIR" - -# Start Minecraft server -start_minecraft - -echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restore completed successfully" >> "$LOG_FILE" -echo "Restore completed successfully!" -echo "Previous data backed up to: $CURRENT_BACKUP" \ No newline at end of file diff --git a/roles/04-backups/templates/restore.sh.j2 b/roles/04-backups/templates/restore.sh.j2 new file mode 100644 index 0000000..887e175 --- /dev/null +++ b/roles/04-backups/templates/restore.sh.j2 @@ -0,0 +1,61 @@ +#!/bin/bash +# {{ ansible_managed }} + +BACKUP_TYPE=$1 +BACKUP_DATE=$2 +BACKUP_BASE="{{ backup_base_dir }}" +SERVER_DIR="{{ minecraft_server_dir }}" + +# Function to find backup file +find_backup() { + local type=$1 + local date=$2 + local dir="" + + case "$type" in + daily) dir="{{ backup_daily_dir }}" ;; + weekly) dir="{{ backup_weekly_dir }}" ;; + monthly) dir="{{ backup_monthly_dir }}" ;; + *) echo "Invalid backup type"; exit 1 ;; + esac + + if [ "$date" = "latest" ]; then + ls -1t $dir/*.tar.gz 2>/dev/null | head -n 1 + else + ls $dir/*${date}*.tar.gz 2>/dev/null | head -n 1 + fi +} + +# Main restore process +BACKUP_FILE=$(find_backup "$BACKUP_TYPE" "$BACKUP_DATE") + +if [ -z "$BACKUP_FILE" ]; then + echo "No backup found for type: $BACKUP_TYPE, date: $BACKUP_DATE" + exit 1 +fi + +echo "Restoring from: $BACKUP_FILE" + +# Create restore directory +RESTORE_DIR="{{ minecraft_base_dir }}/restore_$(date +%Y%m%d_%H%M%S)" +mkdir -p "$RESTORE_DIR" + +# Extract backup +tar -xzf "$BACKUP_FILE" -C "$RESTORE_DIR" + +# Backup current server +echo "Backing up current server state..." +tar -czf "{{ backup_base_dir }}/pre_restore_$(date +%Y%m%d_%H%M%S).tar.gz" \ + -C "{{ minecraft_base_dir }}" server/ + +# Restore files +echo "Restoring server files..." +rsync -av --delete "$RESTORE_DIR/server/" "$SERVER_DIR/" + +# Set permissions +chown -R {{ minecraft_user }}:{{ minecraft_group }} "$SERVER_DIR" + +# Clean up +rm -rf "$RESTORE_DIR" + +echo "Restore completed successfully!" \ No newline at end of file diff --git a/roles/04-backups/vars/main.yml b/roles/04-backups/vars/main.yml index e855cb9..35249c0 100644 --- a/roles/04-backups/vars/main.yml +++ b/roles/04-backups/vars/main.yml @@ -1,16 +1,11 @@ --- -# Internal backup variables -backup_script_names: - - backup-daily.sh - - backup-weekly.sh - - backup-monthly.sh - - restore-backup.sh - - backup-functions.sh +backup_dirs: + - "{{ backup_base_dir }}" + - "{{ backup_daily_dir }}" + - "{{ backup_weekly_dir }}" + - "{{ backup_monthly_dir }}" -cron_jobs: - - name: "Minecraft daily backup" - script: "backup-daily.sh" - - name: "Minecraft weekly backup" - script: "backup-weekly.sh" - - name: "Minecraft monthly backup" - script: "backup-monthly.sh" \ No newline at end of file +backup_source_dirs: + - "{{ minecraft_server_dir }}" + - "{{ minecraft_sources_dir }}" + - "{{ minecraft_tools_dir }}" \ No newline at end of file diff --git a/roles/05-update/defaults/main.yml b/roles/05-update/defaults/main.yml index 5089885..ffaf256 100644 --- a/roles/05-update/defaults/main.yml +++ b/roles/05-update/defaults/main.yml @@ -1,14 +1,6 @@ --- -# Update configuration update_check_ssh_keys: true update_check_system: true update_check_spigot: true - -# Spigot update settings -spigot_current_version_file: "{{ minecraft_server_dir }}/.version" -spigot_new_version_dir: "{{ minecraft_base_dir }}/new_version" -spigot_backup_dir: "{{ minecraft_base_dir }}/version_backups" - -# System update settings -system_update_auto: false -system_reboot_required_check: true \ No newline at end of file +spigot_update_dir: "{{ minecraft_sources_dir }}/update" +spigot_previous_dir: "{{ minecraft_sources_dir }}/previous" \ No newline at end of file diff --git a/roles/05-update/handlers/main.yml b/roles/05-update/handlers/main.yml index be9196b..a13444c 100644 --- a/roles/05-update/handlers/main.yml +++ b/roles/05-update/handlers/main.yml @@ -1,13 +1,13 @@ --- -- name: restart minecraft after update +- name: restart services ansible.builtin.systemd: - name: minecraft + name: "{{ item }}" state: restarted - daemon_reload: true - listen: "restart minecraft after version change" + loop: + - minecraft + - ssh + listen: restart updated services -- name: reload ssh after key update - ansible.builtin.systemd: - name: ssh - state: reloaded - listen: "reload ssh service" \ No newline at end of file +- name: switch minecraft version + ansible.builtin.command: "{{ minecraft_base_dir }}/switch_version.sh" + listen: switch to new version \ No newline at end of file diff --git a/roles/05-update/meta/main.yml b/roles/05-update/meta/main.yml new file mode 100644 index 0000000..7cda289 --- /dev/null +++ b/roles/05-update/meta/main.yml @@ -0,0 +1,30 @@ +--- +galaxy_info: + author: Tips-Of-Mine + description: Système de mise à jour automatique pour Minecraft et le système + company: Tips-Of-Mine + license: MIT + min_ansible_version: "2.14" + platforms: + - name: Debian + versions: + - buster + - bullseye + - bookworm + - trixie + - name: Ubuntu + versions: + - focal + - jammy + - noble + galaxy_tags: + - update + - maintenance + - minecraft + - automation + +dependencies: + - role: 02-installation-java + when: java_update_required | default(false) + - role: 03-Installation-Minecraft + when: minecraft_update_required | default(false) \ No newline at end of file diff --git a/roles/05-update/tasks/01-check-ssh-keys.yml b/roles/05-update/tasks/01-check-ssh-keys.yml new file mode 100644 index 0000000..61429dc --- /dev/null +++ b/roles/05-update/tasks/01-check-ssh-keys.yml @@ -0,0 +1,21 @@ +--- +- name: Check for new SSH keys in repository + ansible.builtin.set_fact: + ssh_keys_to_add: "{{ admin_ssh_keys | default([]) }}" + +- name: Get current authorized keys + ansible.builtin.slurp: + src: /home/ansible/.ssh/authorized_keys + register: current_keys + ignore_errors: yes + +- name: Add new SSH keys if found + ansible.posix.authorized_key: + user: ansible + state: present + key: "{{ item.key }}" + comment: "{{ item.name }}" + loop: "{{ ssh_keys_to_add }}" + when: + - ssh_keys_to_add | length > 0 + - item.key not in (current_keys.content | b64decode | default('')) \ No newline at end of file diff --git a/roles/05-update/tasks/01-update-ssh-keys.yml b/roles/05-update/tasks/01-update-ssh-keys.yml deleted file mode 100644 index 2169919..0000000 --- a/roles/05-update/tasks/01-update-ssh-keys.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -- name: Check for new SSH keys to add - ansible.posix.authorized_key: - user: ansible - key: "{{ item.key }}" - comment: "{{ item.comment | default('') }}" - state: present - loop: "{{ admin_users }}" - when: admin_users is defined and admin_users | length > 0 - notify: "reload ssh service" - register: ssh_keys_result - -- name: Log SSH keys update - ansible.builtin.debug: - msg: "SSH keys updated for {{ ssh_keys_result.results | selectattr('changed', 'equalto', true) | list | length }} users" - when: ssh_keys_result is defined \ No newline at end of file diff --git a/roles/05-update/tasks/02-check-system-updates.yml b/roles/05-update/tasks/02-check-system-updates.yml index fb955f7..2383751 100644 --- a/roles/05-update/tasks/02-check-system-updates.yml +++ b/roles/05-update/tasks/02-check-system-updates.yml @@ -1,41 +1,26 @@ --- - name: Update apt cache ansible.builtin.apt: - update_cache: true + update_cache: yes + cache_valid_time: 3600 when: ansible_os_family == "Debian" -- name: Check for available system updates - ansible.builtin.shell: | - apt list --upgradable 2>/dev/null | grep -v "Listing..." | wc -l - register: available_updates_count +- name: Check for available updates + ansible.builtin.command: apt list --upgradable + register: apt_updates changed_when: false when: ansible_os_family == "Debian" -- name: Display available updates count +- name: Display available updates ansible.builtin.debug: - msg: "{{ available_updates_count.stdout }} system updates available" - when: available_updates_count is defined + msg: "Available updates: {{ apt_updates.stdout_lines | length - 1 }} packages" + when: apt_updates is defined -- name: Apply system updates if auto-update enabled +- name: Perform system update if requested ansible.builtin.apt: - upgrade: dist - autoremove: true - autoclean: true + upgrade: safe + autoremove: yes + autoclean: yes when: - - system_update_auto | bool - ansible_os_family == "Debian" - - available_updates_count.stdout | int > 0 - register: system_update_result - -- name: Check if reboot is required - ansible.builtin.stat: - path: /var/run/reboot-required - register: reboot_required_file - when: system_reboot_required_check | bool - -- name: Warn about required reboot - ansible.builtin.debug: - msg: "ATTENTION: System reboot is required to complete updates" - when: - - reboot_required_file is defined - - reboot_required_file.stat.exists \ No newline at end of file + - perform_system_update | default(false) \ No newline at end of file diff --git a/roles/05-update/tasks/03-check-spigot-version.yml b/roles/05-update/tasks/03-check-spigot-version.yml index a58b202..ab44b9a 100644 --- a/roles/05-update/tasks/03-check-spigot-version.yml +++ b/roles/05-update/tasks/03-check-spigot-version.yml @@ -1,27 +1,30 @@ --- -- name: Check current Spigot version +- name: Get current Spigot version ansible.builtin.slurp: - src: "{{ spigot_current_version_file }}" - register: current_version_file - failed_when: false + src: "{{ current_version_file }}" + register: current_version + ignore_errors: yes -- name: Set current version variable +- name: Set current version fact ansible.builtin.set_fact: - current_spigot_version: "{{ (current_version_file.content | b64decode).strip() }}" - when: current_version_file.failed == false + current_spigot_version: "{{ (current_version.content | b64decode).strip() if current_version is succeeded else 'unknown' }}" -- name: Set default current version if file doesn't exist +- name: Check latest Spigot version + ansible.builtin.uri: + url: "https://hub.spigotmc.org/versions/{{ minecraft_version }}.json" + method: GET + return_content: yes + register: latest_version_info + ignore_errors: yes + +- name: Compare versions ansible.builtin.set_fact: - current_spigot_version: "unknown" - when: current_version_file.failed == true + spigot_needs_update: "{{ current_spigot_version != minecraft_version }}" + when: latest_version_info is succeeded -- name: Check if new version is available - ansible.builtin.set_fact: - spigot_update_available: "{{ minecraft_version != current_spigot_version }}" - -- name: Display version information +- name: Display version status ansible.builtin.debug: msg: - - "Current Spigot version: {{ current_spigot_version }}" - - "Target Spigot version: {{ minecraft_version }}" - - "Update available: {{ spigot_update_available }}" \ No newline at end of file + - "Current version: {{ current_spigot_version }}" + - "Target version: {{ minecraft_version }}" + - "Update needed: {{ spigot_needs_update | default(false) }}" \ No newline at end of file diff --git a/roles/05-update/tasks/04-download-new-spigot.yml b/roles/05-update/tasks/04-download-new-spigot.yml deleted file mode 100644 index d4620d1..0000000 --- a/roles/05-update/tasks/04-download-new-spigot.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -- name: Create new version directory - ansible.builtin.file: - path: "{{ spigot_new_version_dir }}" - state: directory - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0755' - -- name: Download BuildTools for new version - ansible.builtin.get_url: - url: "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" - dest: "{{ spigot_new_version_dir }}/BuildTools.jar" - owner: "{{ minecraft_user }}" - group: "{{ minecraft_group }}" - mode: '0644' - timeout: 300 - force: true \ No newline at end of file diff --git a/roles/05-update/tasks/04-download-new-version.yml b/roles/05-update/tasks/04-download-new-version.yml new file mode 100644 index 0000000..e3e798d --- /dev/null +++ b/roles/05-update/tasks/04-download-new-version.yml @@ -0,0 +1,22 @@ +--- +- name: Create update directory + ansible.builtin.file: + path: "{{ spigot_update_dir }}" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + +- name: Download latest BuildTools + ansible.builtin.get_url: + url: "{{ minecraft_build_tools_url }}" + dest: "{{ spigot_update_dir }}/BuildTools.jar" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + force: yes + +- name: Clean update directory + ansible.builtin.file: + path: "{{ spigot_update_dir }}/work" + state: absent \ No newline at end of file diff --git a/roles/05-update/tasks/05-compile-new-spigot.yml b/roles/05-update/tasks/05-compile-new-spigot.yml deleted file mode 100644 index 6e1600d..0000000 --- a/roles/05-update/tasks/05-compile-new-spigot.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: Compile new Spigot version - ansible.builtin.shell: | - cd {{ spigot_new_version_dir }} - java -jar BuildTools.jar --rev {{ minecraft_version }} - become_user: "{{ minecraft_user }}" - register: new_spigot_compilation - failed_when: new_spigot_compilation.rc != 0 - timeout: 1800 - -- name: Set compilation success flag - ansible.builtin.set_fact: - spigot_compilation_successful: "{{ new_spigot_compilation.rc == 0 }}" - -- name: Display compilation result - ansible.builtin.debug: - msg: "New Spigot compilation {{ 'successful' if spigot_compilation_successful else 'failed' }}" \ No newline at end of file diff --git a/roles/05-update/tasks/05-compile-new-version.yml b/roles/05-update/tasks/05-compile-new-version.yml new file mode 100644 index 0000000..ef466dc --- /dev/null +++ b/roles/05-update/tasks/05-compile-new-version.yml @@ -0,0 +1,25 @@ +--- +- name: Compile new Spigot version + ansible.builtin.command: + cmd: "java -jar BuildTools.jar --rev {{ minecraft_version }}" + chdir: "{{ spigot_update_dir }}" + become_user: "{{ minecraft_user }}" + register: compile_result + +- name: Verify compilation success + ansible.builtin.stat: + path: "{{ spigot_update_dir }}/spigot-{{ minecraft_version }}.jar" + register: new_jar + +- name: Set update ready flag + ansible.builtin.set_fact: + spigot_update_ready: "{{ new_jar.stat.exists }}" + +- name: Create staging directory + ansible.builtin.file: + path: "{{ update_staging_dir }}" + state: directory + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0755' + when: spigot_update_ready \ No newline at end of file diff --git a/roles/05-update/tasks/06-configure-new-version.yml b/roles/05-update/tasks/06-configure-new-version.yml index ebe2506..d9d785b 100644 --- a/roles/05-update/tasks/06-configure-new-version.yml +++ b/roles/05-update/tasks/06-configure-new-version.yml @@ -1 +1,27 @@ -06-configure-new-version.yml \ No newline at end of file +--- +- name: Copy new jar to staging + ansible.builtin.copy: + src: "{{ spigot_update_dir }}/spigot-{{ minecraft_version }}.jar" + dest: "{{ update_staging_dir }}/spigot-{{ minecraft_version }}.jar" + owner: "{{ minecraft_user }}" + group: "{{ minecraft_group }}" + mode: '0644' + remote_src: yes + +- name: Copy current configuration to staging + ansible.builtin.synchronize: + src: "{{ minecraft_server_dir }}/" + dest: "{{ update_staging_dir }}/" + rsync_opts: + - "--exclude=*.jar" + - "--exclude=world*" + - "--exclude=logs" + - "--exclude=crash-reports" + delegate_to: "{{ inventory_hostname }}" + +- name: Update jar reference in staging + ansible.builtin.replace: + path: "{{ update_staging_dir }}/start.sh" + regexp: 'spigot-.*\.jar' + replace: 'spigot-{{ minecraft_version }}.jar' + when: spigot_update_ready \ No newline at end of file diff --git a/roles/05-update/tasks/07-switch-versions.yml b/roles/05-update/tasks/07-switch-versions.yml index 6afd0ff..f4d349e 100644 --- a/roles/05-update/tasks/07-switch-versions.yml +++ b/roles/05-update/tasks/07-switch-versions.yml @@ -1,8 +1,8 @@ --- -- name: Create version backup directory - ansible.builtin.file: - path: "{{ spigot_backup_dir }}" - state: directory +- name: Create version switch script + ansible.builtin.template: + src: version-switch.sh.j2 + dest: "{{ minecraft_base_dir }}/switch_version.sh" owner: "{{ minecraft_user }}" group: "{{ minecraft_group }}" mode: '0755' @@ -13,39 +13,22 @@ state: stopped - name: Backup current version - ansible.builtin.shell: | - BACKUP_NAME="version_backup_$(date +%Y%m%d_%H%M%S)" - mkdir -p "{{ spigot_backup_dir }}/$BACKUP_NAME" - cp -r "{{ minecraft_server_dir }}"/* "{{ spigot_backup_dir }}/$BACKUP_NAME/" + ansible.builtin.command: + cmd: | + tar -czf {{ spigot_previous_dir }}/backup_$(date +%Y%m%d_%H%M%S).tar.gz \ + -C {{ minecraft_server_dir }} . become_user: "{{ minecraft_user }}" -- name: Copy world data to new version - ansible.builtin.shell: | - if [ -d "{{ minecraft_server_dir }}/world" ]; then - cp -r "{{ minecraft_server_dir }}/world" "{{ spigot_new_version_dir }}/" - fi - if [ -d "{{ minecraft_server_dir }}/world_nether" ]; then - cp -r "{{ minecraft_server_dir }}/world_nether" "{{ spigot_new_version_dir }}/" - fi - if [ -d "{{ minecraft_server_dir }}/world_the_end" ]; then - cp -r "{{ minecraft_server_dir }}/world_the_end" "{{ spigot_new_version_dir }}/" - fi - if [ -d "{{ minecraft_server_dir }}/plugins" ]; then - cp -r "{{ minecraft_server_dir }}/plugins" "{{ spigot_new_version_dir }}/" - fi - become_user: "{{ minecraft_user }}" - -- name: Replace server directory with new version - ansible.builtin.shell: | - rm -rf "{{ minecraft_server_dir }}.old" - mv "{{ minecraft_server_dir }}" "{{ minecraft_server_dir }}.old" - mv "{{ spigot_new_version_dir }}" "{{ minecraft_server_dir }}" +- name: Switch to new version + ansible.builtin.command: + cmd: "{{ minecraft_base_dir }}/switch_version.sh" become_user: "{{ minecraft_user }}" + register: switch_result - name: Update version file ansible.builtin.copy: content: "{{ minecraft_version }}" - dest: "{{ spigot_current_version_file }}" + dest: "{{ current_version_file }}" owner: "{{ minecraft_user }}" group: "{{ minecraft_group }}" mode: '0644' @@ -54,30 +37,13 @@ ansible.builtin.systemd: name: minecraft state: started - notify: "restart minecraft after version change" -- name: Wait for server to start +- name: Verify server is running ansible.builtin.wait_for: - port: "{{ minecraft_port }}" - delay: 30 - timeout: 300 + port: "{{ server_port }}" + timeout: 60 + state: started -- name: Verify server is running new version - ansible.builtin.shell: | - sleep 10 - /usr/local/bin/mcrcon -H 127.0.0.1 -P {{ rcon_port }} -p "{{ rcon_password }}" "version" - register: version_check - failed_when: false - -- name: Display version switch result +- name: Display update result ansible.builtin.debug: - msg: - - "Version switch completed successfully" - - "Server response: {{ version_check.stdout | default('No response') }}" - - "Old version backed up to: {{ minecraft_server_dir }}.old" - -- name: Clean up old version after successful switch - ansible.builtin.file: - path: "{{ minecraft_server_dir }}.old" - state: absent - when: version_check.rc == 0 \ No newline at end of file + msg: "Successfully updated to Spigot {{ minecraft_version }}" \ No newline at end of file diff --git a/roles/05-update/tasks/main.yml b/roles/05-update/tasks/main.yml index 29df3aa..7a7c61c 100644 --- a/roles/05-update/tasks/main.yml +++ b/roles/05-update/tasks/main.yml @@ -1,46 +1,28 @@ --- -- name: Include SSH keys update tasks - ansible.builtin.include_tasks: 01-update-ssh-keys.yml - when: update_check_ssh_keys | bool +- name: Include check SSH keys tasks + ansible.builtin.include_tasks: 01-check-ssh-keys.yml + when: update_check_ssh_keys -- name: Include system updates check tasks +- name: Include check system updates tasks ansible.builtin.include_tasks: 02-check-system-updates.yml - when: update_check_system | bool + when: update_check_system -- name: Include Spigot version check tasks +- name: Include check Spigot version tasks ansible.builtin.include_tasks: 03-check-spigot-version.yml - when: update_check_spigot | bool + when: update_check_spigot -- name: Include new Spigot download tasks - ansible.builtin.include_tasks: 04-download-new-spigot.yml - when: - - update_check_spigot | bool - - spigot_update_available is defined - - spigot_update_available | bool +- name: Include download new version tasks + ansible.builtin.include_tasks: 04-download-new-version.yml + when: spigot_needs_update | default(false) -- name: Include new Spigot compilation tasks - ansible.builtin.include_tasks: 05-compile-new-spigot.yml - when: - - update_check_spigot | bool - - spigot_update_available is defined - - spigot_update_available | bool +- name: Include compile new version tasks + ansible.builtin.include_tasks: 05-compile-new-version.yml + when: spigot_needs_update | default(false) -- name: Include new version configuration tasks +- name: Include configure new version tasks ansible.builtin.include_tasks: 06-configure-new-version.yml - when: - - update_check_spigot | bool - - spigot_update_available is defined - - spigot_update_available | bool - - spigot_compilation_successful is defined - - spigot_compilation_successful | bool + when: spigot_needs_update | default(false) -- name: Include version switching tasks +- name: Include switch versions tasks ansible.builtin.include_tasks: 07-switch-versions.yml - when: - - update_check_spigot | bool - - spigot_update_available is defined - - spigot_update_available | bool - - spigot_compilation_successful is defined - - spigot_compilation_successful | bool - - spigot_configuration_successful is defined - - spigot_configuration_successful | bool \ No newline at end of file + when: spigot_update_ready | default(false) \ No newline at end of file diff --git a/roles/05-update/templates/version-switch.sh.j2 b/roles/05-update/templates/version-switch.sh.j2 index 70a9a3c..554c48f 100644 --- a/roles/05-update/templates/version-switch.sh.j2 +++ b/roles/05-update/templates/version-switch.sh.j2 @@ -1,18 +1,36 @@ #!/bin/bash +# {{ ansible_managed }} -OLD_VERSION="$1" -NEW_VERSION="$2" +STAGING_DIR="{{ update_staging_dir }}" SERVER_DIR="{{ minecraft_server_dir }}" -BACKUP_DIR="{{ minecraft_backups_dir }}/version_backup" +PREVIOUS_DIR="{{ spigot_previous_dir }}" +DATE=$(date +%Y%m%d_%H%M%S) -# Create backup directory -mkdir -p "${BACKUP_DIR}" +# Create previous directory if not exists +mkdir -p "$PREVIOUS_DIR" -# Backup current version -mv "${SERVER_DIR}" "${BACKUP_DIR}/server_${OLD_VERSION}_$(date +%Y%m%d_%H%M%S)" +# Backup current server +echo "Backing up current server..." +tar -czf "$PREVIOUS_DIR/server_before_update_$DATE.tar.gz" \ + -C "$SERVER_DIR" \ + --exclude='world*' \ + --exclude='logs' \ + --exclude='crash-reports' \ + . -# Switch to new version -mv "${SERVER_DIR}_${NEW_VERSION}" "${SERVER_DIR}" +# Sync new version to server directory +echo "Switching to new version..." +rsync -av --delete \ + --exclude='world*' \ + --exclude='logs' \ + --exclude='crash-reports' \ + --exclude='plugins/*.yml' \ + --exclude='ops.json' \ + --exclude='whitelist.json' \ + --exclude='banned-*.json' \ + "$STAGING_DIR/" "$SERVER_DIR/" -echo "Version switched from ${OLD_VERSION} to ${NEW_VERSION}" -exit 0 \ No newline at end of file +# Set permissions +chown -R {{ minecraft_user }}:{{ minecraft_group }} "$SERVER_DIR" + +echo "Version switch completed!" \ No newline at end of file diff --git a/roles/05-update/vars/main.yml b/roles/05-update/vars/main.yml index 011c21a..8336936 100644 --- a/roles/05-update/vars/main.yml +++ b/roles/05-update/vars/main.yml @@ -1,18 +1,4 @@ --- -# Internal update variables -update_scripts: - - check-updates.sh - - apply-updates.sh - -spigot_version_pattern: "spigot-(.*?)\\.jar" - -system_packages_to_update: - - openjdk-17-jdk - - git - - build-essential - - rsync - - fail2ban - - ufw - -# Backup retention for version backups -version_backup_retention_days: 30 \ No newline at end of file +spigot_version_url: "https://hub.spigotmc.org/versions/latest.json" +current_version_file: "{{ minecraft_server_dir }}/.version" +update_staging_dir: "{{ minecraft_sources_dir }}/staging" \ No newline at end of file diff --git a/secrets.example b/secrets.example index e69de29..a74c32b 100644 --- a/secrets.example +++ b/secrets.example @@ -0,0 +1,27 @@ +# Variables secrètes à créer dans Gitea Secrets +# NE PAS COMMITER CE FICHIER + +# SSH Keys pour les administrateurs +admin_ssh_keys: + - name: admin1 + key: "SSH_KEY_ADMIN1" + - name: admin2 + key: "SSH_KEY_ADMIN2" + +# Mot de passe pour la base de données (si nécessaire) +db_password: "DB_PASSWORD_SECRET" + +# Token API pour les mises à jour +spigot_api_token: "SPIGOT_API_TOKEN" + +# Clés pour les backups distants +backup_ssh_key: "BACKUP_SSH_KEY" +backup_remote_host: "backup.example.com" +backup_remote_user: "backup" +backup_remote_path: "/backups/minecraft" + +# RCON Password +rcon_password: "RCON_PASSWORD_SECRET" + +# Minecraft server properties secrets +server_rcon_password: "SERVER_RCON_PASSWORD" \ No newline at end of file diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..7d3f2b4 --- /dev/null +++ b/site.yml @@ -0,0 +1,30 @@ +--- +- name: Installation complète du serveur Minecraft + hosts: minecraft_servers + become: true + gather_facts: true + + pre_tasks: + - name: Vérification de la connectivité + ansible.builtin.ping: + + - name: Collecte des facts si nécessaire + ansible.builtin.setup: + when: ansible_facts == {} + + roles: + - role: 01-server_hardening + tags: ['hardening', 'security'] + + - role: 02-installation-java + tags: ['java', 'prerequisites'] + + - role: 03-Installation-Minecraft + tags: ['minecraft', 'installation'] + + - role: 04-backups + tags: ['backup', 'maintenance'] + + - role: 05-Update + tags: ['update', 'maintenance'] + when: update_check | default(false) \ No newline at end of file