commit 67c5aae9131b0eb734799b3c4d3c749ef9b96fbf Author: Ansible Servercow Date: Tue Nov 19 16:04:17 2024 +0100 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..106d004 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This Repository contains all important playbooks/roles used for system operation automation such as updates and cleanups. \ No newline at end of file diff --git a/collections/.gitkeep b/collections/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/playbooks/docker/cleanup-all.yml b/playbooks/docker/cleanup-all.yml new file mode 100644 index 0000000..297d86f --- /dev/null +++ b/playbooks/docker/cleanup-all.yml @@ -0,0 +1,17 @@ +- name: Run Docker Cleanup (full) + hosts: all + tasks: + - include_role: + name: managed-mailcow + tasks_from: find-mailcow-composedir.yml + + - include_role: + name: docker + tasks_from: get-containerstatus.yml + vars: + docker_compose_path: "{{ mailcow_dir_result.files[0].path }}" + + - include_role: + name: docker + tasks_from: cleanup-all.yml + when: not 'false' in container_status.values() \ No newline at end of file diff --git a/playbooks/docker/cleanup-images.yml b/playbooks/docker/cleanup-images.yml new file mode 100644 index 0000000..212b80e --- /dev/null +++ b/playbooks/docker/cleanup-images.yml @@ -0,0 +1,12 @@ +- name: Clean Docker Images on Host + hosts: all + tasks: + - include_role: + name: docker + tasks_from: get-containerstatus.yml + vars: + docker_compose_path: /opt/mailcow-dockerized + + - include_role: + name: docker + tasks_from: cleanup-images.yml diff --git a/playbooks/managed-mailcow/playbooks/add-haveged.yaml b/playbooks/managed-mailcow/playbooks/add-haveged.yaml new file mode 100644 index 0000000..f9b4de5 --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/add-haveged.yaml @@ -0,0 +1,8 @@ +--- +- name: Deploy Haveged to VMs + hosts: all + tasks: + - name: Install Haveged + apt: + name: haveged + state: present \ No newline at end of file diff --git a/playbooks/managed-mailcow/playbooks/change-garbagecleaner.yaml b/playbooks/managed-mailcow/playbooks/change-garbagecleaner.yaml new file mode 100644 index 0000000..3d1a796 --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/change-garbagecleaner.yaml @@ -0,0 +1,26 @@ +--- + +- name: Garbage Cleaner ändern + hosts: all + tasks: + + - name: "Prüfe ob mailcow.conf exists" + ansible.builtin.stat: + path: /opt/mailcow-dockerized/mailcow.conf + register: mailcow_conf + + - name: "Setze Garbage Cleaner auf 7 Tage" + ansible.builtin.replace: + path: "/opt/mailcow-dockerized/mailcow.conf" + regexp: "^MAILDIR_GC_TIME=.*" + replace: "MAILDIR_GC_TIME=10080" + backup: yes + register: gc + when: mailcow_conf.stat.exists + + - name: "Starte mailcow Container neu" + ansible.builtin.shell: | + cd /opt/mailcow-dockerized + docker compose up -d + when: gc.changed + diff --git a/playbooks/managed-mailcow/playbooks/check-mailcow-health.yml b/playbooks/managed-mailcow/playbooks/check-mailcow-health.yml new file mode 100644 index 0000000..e60b174 --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/check-mailcow-health.yml @@ -0,0 +1,62 @@ +- name: Fetch webpages and check response + hosts: localhost + gather_facts: false + vars: + hosts: + - "mail.ps.develcow.de" + - "mail.np.develcow.de" + tasks: + - name: Fetch webpage + uri: + url: "https://{{ item }}" + return_content: yes + register: webpage_response + loop: "{{ hosts }}" + + - name: Fail if mailcow UI is not working - Skipping = OK + fail: + msg: "UI not OK" + loop: "{{ webpage_response.results }}" + when: "'showVersionModal' not in item.content" + no_log: true + ignore_errors: yes + + - name: Check SMTP Port 25 connection + wait_for: + host: "{{ item }}" + port: 25 + state: started + delay: 0 + timeout: 3 + loop: "{{ hosts }}" + ignore_errors: yes + + - name: Check SUBMISSION Port 587 connection + wait_for: + host: "{{ item }}" + port: 587 + state: started + delay: 0 + timeout: 3 + loop: "{{ hosts }}" + ignore_errors: yes + + - name: Check IMAP Port 143 connection + wait_for: + host: "{{ item }}" + port: 143 + state: started + delay: 0 + timeout: 3 + loop: "{{ hosts }}" + ignore_errors: yes + + - name: Check IMAPS Port 993 connection + wait_for: + host: "{{ item }}" + port: 993 + state: started + delay: 0 + timeout: 3 + loop: "{{ hosts }}" + ignore_errors: yes diff --git a/playbooks/managed-mailcow/playbooks/migrate-clamd.yaml b/playbooks/managed-mailcow/playbooks/migrate-clamd.yaml new file mode 100644 index 0000000..6652b36 --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/migrate-clamd.yaml @@ -0,0 +1,33 @@ +--- + +- name: ClamAV Server auf neuen shared ClamAV setzen + hosts: all + tasks: + - name: "Setze ClamAV Server in rspamd Config auf managed mailcows" + ansible.builtin.replace: + path: "/opt/mailcow-dockerized/data/conf/rspamd/local.d/antivirus.conf" + regexp: "^ servers = \".*\";$" + replace: " servers = \"[2a07:6fc0:c:2809::23]:3310\";" + backup: yes + register: rspamd + + - name: "Setze lokalen ClamAV auf n (falls noch nicht geschehen)" + ansible.builtin.replace: + path: "/opt/mailcow-dockerized/mailcow.conf" + regexp: "^SKIP_CLAMD=.*" + replace: "SKIP_CLAMD=y" + backup: yes + register: clamd + + - name: "Starte mailcow Container neu (wenn ClamAV noch nicht deaktiviert lokal)" + ansible.builtin.shell: | + cd /opt/mailcow-dockerized + docker compose up -d + when: clamd.changed + + - name: "Restart Rspamd Container" + shell: | + cd /opt/mailcow-dockerized + docker compose restart rspamd-mailcow + when: rspamd.changed + diff --git a/playbooks/managed-mailcow/playbooks/start-stop-mailcow.yaml b/playbooks/managed-mailcow/playbooks/start-stop-mailcow.yaml new file mode 100644 index 0000000..05b0480 --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/start-stop-mailcow.yaml @@ -0,0 +1,23 @@ +- name: Start/Stop mailcow + hosts: all + tasks: + - import_role: + name: managed-mailcow + tasks_from: find-mailcow-composedir.yml + + - import_role: + name: managed-mailcow + tasks_from: stop-mailcow.yml + vars: + docker_compose_path: "{{ mailcow_dir_result.files[0].path }}" + verbose: true # Or False if you dont' wanna see docker's outputs + + - import_role: + name: managed-mailcow + tasks_from: start-mailcow.yml + vars: + docker_compose_path: "{{ mailcow_dir_result.files[0].path }}" + verbose: true # Or False if you dont' wanna see docker's outputs + + + diff --git a/playbooks/managed-mailcow/playbooks/update-mailcow.yaml b/playbooks/managed-mailcow/playbooks/update-mailcow.yaml new file mode 100644 index 0000000..737b66e --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/update-mailcow.yaml @@ -0,0 +1,14 @@ +- name: Update mailcow (update.sh) + hosts: all + tasks: + - import_role: + name: roles/managed-mailcow + tasks_from: find-mailcow-composedir.yml + + - import_role: + name: roles/managed-mailcow + tasks_from: update-mailcow.yml + vars: + github_mailcow_ver: "2024-11b" # GitHub Version Tag | Value to compare the current running mailcow version to. + disk_space_percent_max: "97" # Number in percent | Defines the max allowed disk utilization until ansible is not updating mailcow automatically + debug: true # Or False if you dont' wanna see verbose outputs of role outputs \ No newline at end of file diff --git a/playbooks/managed-mailcow/playbooks/update-mailcow.yaml.old b/playbooks/managed-mailcow/playbooks/update-mailcow.yaml.old new file mode 100644 index 0000000..926296a --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/update-mailcow.yaml.old @@ -0,0 +1,41 @@ +--- +- name: Update mailcow stacks + hosts: all + vars: + github_mailcow_ver: "2024-08a" + mailcow_search_paths: + - /opt + - /data + - /root + tasks: + + - name: Find mailcow-dockerized directory + ansible.builtin.find: + file_type: directory + paths: "{{ mailcow_search_paths }}" + patterns: mailcow-dockerized + recurse: yes + register: mailcow_dir_result + ignore_errors: true + + - name: 'DEBUG: Show file paths' + debug: + msg: "{{ mailcow_dir_result.files[0].path }}" + when: mailcow_dir_result is defined + + - name: Check if mailcow.conf exists + ansible.builtin.stat: + path: "{{ mailcow_dir_result.files[0].path }}/mailcow.conf" + register: mailcow_conf + when: mailcow_dir_result is defined + + - name: Check mailcow Version + ansible.builtin.shell: | + cd {{ mailcow_dir_result.files[0].path }}/data/web/inc + grep -oP '\$MAILCOW_GIT_VERSION="\K[^"]+' app_info.inc.php + register: local_mailcow_version + when: mailcow_conf.stat.exists + + - name: Update mailcow + shell: "cd {{ mailcow_dir_result.files[0].path }} && git fetch && git checkout origin/master update.sh && ./update.sh --force" + when: local_mailcow_version.stdout != github_mailcow_ver and mailcow_conf.stat.exists diff --git a/playbooks/managed-mailcow/playbooks/use-syslog-server.yaml b/playbooks/managed-mailcow/playbooks/use-syslog-server.yaml new file mode 100644 index 0000000..25d923b --- /dev/null +++ b/playbooks/managed-mailcow/playbooks/use-syslog-server.yaml @@ -0,0 +1,63 @@ +--- +- name: Update Docker Daemon configuration to use Syslog Server + hosts: all + become: yes + tasks: + - name: Read current Docker daemon.json + ansible.builtin.slurp: + src: /etc/docker/daemon.json + register: current_daemon_json + + - name: Parse current Docker daemon.json + set_fact: + current_daemon_config: "{{ current_daemon_json['content'] | b64decode | from_json }}" + + - name: Check current log-driver setting + set_fact: + log_driver_current: "{{ current_daemon_config['log-driver'] | default('not_set') }}" + + - name: Update Docker daemon.json with syslog configuration if log-driver is local + ansible.builtin.copy: + dest: /etc/docker/daemon.json + content: "{{ updated_daemon_json | to_nice_json }}" + vars: + syslog_config: { + "log-driver": "syslog", + "log-opts": { + "syslog-address": "udp://[2a0e:b680:80::91]:5514", + "syslog-format": "rfc5424", + "tag": "{{ '{{.Name}}' }}" + } + } + updated_daemon_json: "{{ current_daemon_config | combine(syslog_config) }}" + when: log_driver_current == 'local' or log is undefined + register: daemon_update + + - name: Restart Docker to apply changes + ansible.builtin.systemd: + name: docker + state: restarted + when: daemon_update.changed + + - include_role: + name: managed-mailcow + tasks_from: find-mailcow-composedir.yml + when: daemon_update.changed + + - name: Stop mailcow stack + import_role: + name: managed-mailcow + tasks_from: stop-mailcow.yml + vars: + docker_compose_path: "{{ mailcow_dir_result.files[0].path }}" + verbose: False + when: daemon_update.changed and mailcow_dir_result.matched > 0 + + - name: Start mailcow stack + import_role: + name: managed-mailcow + tasks_from: start-mailcow.yml + vars: + docker_compose_path: "{{ mailcow_dir_result.files[0].path }}" + verbose: False + when: daemon_update.changed and mailcow_dir_result.matched > 0 \ No newline at end of file diff --git a/roles/docker/tasks/cleanup-all.yml b/roles/docker/tasks/cleanup-all.yml new file mode 100644 index 0000000..12e9113 --- /dev/null +++ b/roles/docker/tasks/cleanup-all.yml @@ -0,0 +1,9 @@ +- name: Prune everything + community.docker.docker_prune: + containers: true + images: true + networks: true + volumes: true + builder_cache: true + register: result + \ No newline at end of file diff --git a/roles/docker/tasks/cleanup-images.yml b/roles/docker/tasks/cleanup-images.yml new file mode 100644 index 0000000..86f9de9 --- /dev/null +++ b/roles/docker/tasks/cleanup-images.yml @@ -0,0 +1,4 @@ +- name: Prune images only + community.docker.docker_prune: + images: true + register: result \ No newline at end of file diff --git a/roles/docker/tasks/get-containerstatus.yml b/roles/docker/tasks/get-containerstatus.yml new file mode 100644 index 0000000..b4566c5 --- /dev/null +++ b/roles/docker/tasks/get-containerstatus.yml @@ -0,0 +1,12 @@ +--- +- name: Ensure Docker Compose project is up + community.docker.docker_compose_v2: + project_src: "{{ docker_compose_path }}" + state: present + register: compose_status + +- name: Set fact for container status + set_fact: + container_status: "{{ container_status | default({}) | combine({item.Name: (item.State == 'running')}) }}" + verbosity: 0 + with_items: "{{ compose_status.containers }}" \ No newline at end of file diff --git a/roles/docker/tasks/restart-daemon.yml b/roles/docker/tasks/restart-daemon.yml new file mode 100644 index 0000000..6ada3b1 --- /dev/null +++ b/roles/docker/tasks/restart-daemon.yml @@ -0,0 +1,5 @@ +--- +- name: Restart Docker Daemon + ansible.builtin.systemd: + name: docker + state: restarted \ No newline at end of file diff --git a/roles/managed-mailcow/tasks/find-mailcow-composedir.yml b/roles/managed-mailcow/tasks/find-mailcow-composedir.yml new file mode 100644 index 0000000..2eb4c42 --- /dev/null +++ b/roles/managed-mailcow/tasks/find-mailcow-composedir.yml @@ -0,0 +1,14 @@ +--- +- name: Find mailcow-dockerized directory + vars: + mailcow_search_paths: + - /opt + - /data + - /root + ansible.builtin.find: + file_type: directory + paths: "{{ mailcow_search_paths }}" + patterns: mailcow-dockerized + recurse: yes + register: mailcow_dir_result + ignore_errors: true \ No newline at end of file diff --git a/roles/managed-mailcow/tasks/start-mailcow.yml b/roles/managed-mailcow/tasks/start-mailcow.yml new file mode 100644 index 0000000..e10969d --- /dev/null +++ b/roles/managed-mailcow/tasks/start-mailcow.yml @@ -0,0 +1,12 @@ +--- +- name: Start mailcow stack + community.docker.docker_compose_v2: + project_src: "{{ docker_compose_path }}" + state: present + register: output + +- name: Print Replay + debug: + var: output + when: verbose | bool + diff --git a/roles/managed-mailcow/tasks/stop-mailcow.yml b/roles/managed-mailcow/tasks/stop-mailcow.yml new file mode 100644 index 0000000..fb781f0 --- /dev/null +++ b/roles/managed-mailcow/tasks/stop-mailcow.yml @@ -0,0 +1,11 @@ +--- +- name: Stop mailcow stack + community.docker.docker_compose_v2: + project_src: "{{ docker_compose_path }}" + state: absent + register: output + +- name: Print Replay + debug: + var: output + when: verbose | bool diff --git a/roles/managed-mailcow/tasks/update-mailcow.yml b/roles/managed-mailcow/tasks/update-mailcow.yml new file mode 100644 index 0000000..b1b6bd1 --- /dev/null +++ b/roles/managed-mailcow/tasks/update-mailcow.yml @@ -0,0 +1,22 @@ +--- +- name: Check if mailcow.conf exists + ansible.builtin.stat: + path: "{{ mailcow_dir_result.files[0].path }}/mailcow.conf" + register: mailcow_conf + when: mailcow_dir_result.files[0].path is defined + +- name: Check mailcow Version + ansible.builtin.shell: | + cd {{ mailcow_dir_result.files[0].path }}/data/web/inc + grep -oP '\$MAILCOW_GIT_VERSION="\K[^"]+' app_info.inc.php + register: local_mailcow_version + when: mailcow_conf.stat.exists + +- name: Check Disk Utilization + import_role: + name: roles/system + tasks_from: check-disk-utilization.yaml + +- name: Update mailcow + shell: "cd {{ mailcow_dir_result.files[0].path }} && git fetch && git checkout origin/master update.sh && ./update.sh --force" + when: local_mailcow_version.stdout != github_mailcow_ver and mailcow_conf.stat.exists and disk_space_output | bool \ No newline at end of file diff --git a/roles/requirements.yml b/roles/requirements.yml new file mode 100644 index 0000000..4c82cb3 --- /dev/null +++ b/roles/requirements.yml @@ -0,0 +1,4 @@ +--- +collections: + - name: community.docker + version: 3.11.0 \ No newline at end of file diff --git a/roles/system/tasks/check-disk-utilization.yaml b/roles/system/tasks/check-disk-utilization.yaml new file mode 100644 index 0000000..1242b2e --- /dev/null +++ b/roles/system/tasks/check-disk-utilization.yaml @@ -0,0 +1,9 @@ +- name: Run disk space command + ansible.builtin.shell: "df --output=used,avail / | awk 'NR==2 {used=$1; available=$2; total=used+available; percentage=used*100/total; if (percentage < {{ disk_space_percent_max }} ) printf \"true\"; else printf \"false\"}'" + # System uses the disk_space_percent_max variable to determine condition this check is getting. Over the amount defined in the var causes the check to fail! + register: disk_space_output + +- name: "**DEBUG**: Server disk Utilization condition" + ansible.builtin.debug: + var: disk_space_output.stdout + when: debug | bool \ No newline at end of file diff --git a/roles/updates/tasks/apk-based-update.yaml b/roles/updates/tasks/apk-based-update.yaml new file mode 100644 index 0000000..9625b90 --- /dev/null +++ b/roles/updates/tasks/apk-based-update.yaml @@ -0,0 +1,8 @@ + - name: Run apk update + ansible.builtin.apk: + update_cache: yes + register: apk_result + + - name: Run Package Upgrade (apk) + ansible.builtin.apk: + upgrade: yes \ No newline at end of file diff --git a/roles/updates/tasks/apt-based-update.yaml b/roles/updates/tasks/apt-based-update.yaml new file mode 100644 index 0000000..1617470 --- /dev/null +++ b/roles/updates/tasks/apt-based-update.yaml @@ -0,0 +1,10 @@ + - name: Run Cache refresh (apt) + ansible.builtin.apt: + update_cache: yes + register: apt_result + + - name: Run Package Upgrade (apt) + ansible.builtin.apt: + upgrade: dist + force: true + autoclean: true \ No newline at end of file diff --git a/roles/updates/tasks/apt-cleaning.yaml b/roles/updates/tasks/apt-cleaning.yaml new file mode 100644 index 0000000..358bd1f --- /dev/null +++ b/roles/updates/tasks/apt-cleaning.yaml @@ -0,0 +1,15 @@ +- name: APT run autoremove with purge + ansible.builtin.apt: + autoremove: yes + purge: yes + when: purge_autoremove | bool + +- name: APT run autoremove without purge + ansible.builtin.apt: + autoremove: yes + purge: no + when: not purge_autoremove + +- name: APT run autoclean + ansible.builtin.apt: + autoclean: yes diff --git a/roles/updates/tasks/debian-major-upgrade-apt.yaml b/roles/updates/tasks/debian-major-upgrade-apt.yaml new file mode 100644 index 0000000..999c334 --- /dev/null +++ b/roles/updates/tasks/debian-major-upgrade-apt.yaml @@ -0,0 +1,3 @@ +- name: Upgrade Debian Repos to newest Major Version + task: + a \ No newline at end of file