From 9586bc600bd7820c68b724f1fbaa541cba5f8f39 Mon Sep 17 00:00:00 2001 From: Tyler Hale Date: Tue, 4 Apr 2023 15:33:23 -0600 Subject: [PATCH] Initial commit --- hosts.yml | 18 + roles/zammad/defaults/main.yml | 27 ++ roles/zammad/handlers/main.yml | 30 ++ roles/zammad/tasks/customTemplates.yml | 27 ++ roles/zammad/tasks/main.yml | 374 ++++++++++++++++++ .../templates/certbot-renewal.service.j2 | 5 + .../zammad/templates/certbot-renewal.timer.j2 | 9 + .../zammad/templates/customCSS/CustomCSS.css | 11 + .../templates/elasticsearch-fail.service.j2 | 5 + .../templates/elasticsearch.override.conf.j2 | 9 + roles/zammad/templates/httpsRedirect.conf.j2 | 6 + .../templates/ingest-attachment-fix.sh.j2 | 15 + roles/zammad/templates/zammad.conf.j2 | 68 ++++ .../zammad/templates/zammad.override.conf.j2 | 3 + site.yml | 5 + zammad.yml | 7 + 16 files changed, 619 insertions(+) create mode 100644 hosts.yml create mode 100644 roles/zammad/defaults/main.yml create mode 100644 roles/zammad/handlers/main.yml create mode 100644 roles/zammad/tasks/customTemplates.yml create mode 100644 roles/zammad/tasks/main.yml create mode 100644 roles/zammad/templates/certbot-renewal.service.j2 create mode 100644 roles/zammad/templates/certbot-renewal.timer.j2 create mode 100644 roles/zammad/templates/customCSS/CustomCSS.css create mode 100644 roles/zammad/templates/elasticsearch-fail.service.j2 create mode 100644 roles/zammad/templates/elasticsearch.override.conf.j2 create mode 100644 roles/zammad/templates/httpsRedirect.conf.j2 create mode 100644 roles/zammad/templates/ingest-attachment-fix.sh.j2 create mode 100644 roles/zammad/templates/zammad.conf.j2 create mode 100644 roles/zammad/templates/zammad.override.conf.j2 create mode 100644 site.yml create mode 100644 zammad.yml diff --git a/hosts.yml b/hosts.yml new file mode 100644 index 0000000..5f5068c --- /dev/null +++ b/hosts.yml @@ -0,0 +1,18 @@ +--- +# file: hosts.yml + +all: + hosts: + zammad-svr1: + ansible_host: 10.1.1.19 + + vars: + ansible_user: Ansible + zammad_elasticsearch_network_host: "0.0.0.0" + zammad_elasticsearch_discovery_seed_hosts: "[]" + zammad_certificate: /etc/letsencrypt/live/example.avenging.systems/fullchain.pem + zammad_certificate_key: /etc/letsencrypt/live/example.avenging.systems/privkey.pem + zammad_url: "example.avenging.systems" + zammad_certbot: true + zammad_precompile: true + zammad_es_ingest_fix: true diff --git a/roles/zammad/defaults/main.yml b/roles/zammad/defaults/main.yml new file mode 100644 index 0000000..07ab4ee --- /dev/null +++ b/roles/zammad/defaults/main.yml @@ -0,0 +1,27 @@ +--- +# file: roles/zammad/defaults/main.yml + +zammad_url: "{{ ansible_nodename }}" +zammad_certificate: "/etc/ssl/{{ ansible_facts['nodename'] }}/live/fullchain.pem" +zammad_certificate_key: "/etc/ssl/{{ ansible_facts['nodename'] }}/live/privkey.pem" +zammad_certbot: false +zammad_precompile: false +zammad_es_ingest_fix: false + +zammad_custom_css: "templates/customCSS" +zammad_custom_templates: "templates/customTemplates" + +zammad_httpsRedirect: true + +zammad_es_url: "http://localhost:9200" +zammad_es_attachment_ignore: "'.png', '.jpg', '.jpeg', '.mpeg', '.mpg', '.mov', '.bin', '.exe'" +zammad_es_attachment_max_size_in_mb: "50" +zammad_es_username: "" +zammad_es_password: "" + +zammad_es_version: "8" +zammad_es_xpack_security_enabled: "false" +zammad_es_network_host: "" +zammad_es_discovery_seed_hosts: "" +zammad_es_max_content_length: "400mb" +zammad_es_max_clause_count: "2000" diff --git a/roles/zammad/handlers/main.yml b/roles/zammad/handlers/main.yml new file mode 100644 index 0000000..27fce4c --- /dev/null +++ b/roles/zammad/handlers/main.yml @@ -0,0 +1,30 @@ +--- +# file: roles/zammad/handlers/main.yml + +- name: Daemon Reload + systemd: + daemon_reload: yes + +- name: Reload nginx + service: + name: nginx + state: reloaded + +- name: Zammad precompile + shell: zammad run rake assets:precompile + +- name: Restart elasticsearch + service: + name: elasticsearch + state: restarted + +- name: Restart postgresql + service: + name: postgresql + state: restarted + +- name: Reload firewalld + service: + name: firewalld + state: reloaded + diff --git a/roles/zammad/tasks/customTemplates.yml b/roles/zammad/tasks/customTemplates.yml new file mode 100644 index 0000000..156f339 --- /dev/null +++ b/roles/zammad/tasks/customTemplates.yml @@ -0,0 +1,27 @@ +--- +# file: roles/zammad/tasks/customTemplates.yml + +- name: Get current files from folders + find: + paths: "{{ role_path }}/{{ zammad_custom_templates }}/{{ templateType.path | basename }}/" + file_type: file + register: templateFiles + loop: "{{ templateDirs.files }}" + delegate_to: 127.0.0.1 + +- name: Ensure directory exists + file: + path: "/opt/zammad/app/views/mailer/{{ templateType.path | basename }}" + state: directory + owner: zammad + group: zammad + +- name: Deploy mail templates + template: + src: "{{ item }}" + dest: "/opt/zammad/app/views/mailer/{{ templateType.path | basename }}/{{ item | basename }}" + owner: zammad + group: zammad + loop: "{{ lookup('fileglob', '{{ zammad_custom_templates }}/{{ templateType.path | basename }}/*', wantlist=True) }}" + notify: Zammad precompile + diff --git a/roles/zammad/tasks/main.yml b/roles/zammad/tasks/main.yml new file mode 100644 index 0000000..3f6d621 --- /dev/null +++ b/roles/zammad/tasks/main.yml @@ -0,0 +1,374 @@ +--- +# file: roles/zammad/tasks/main.yml + +- name: Install epel + package: + name: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm" + state: present + disable_gpg_check: True + +- name: Install prereq packages + package: + name: + - imlib2 + - glibc-langpack-en + state: present + disable_gpg_check: True + +- name: Install certbot + pip: + name: certbot + executable: pip3 + state: latest + when: zammad_certbot + +- name: Deploy certbot renewal service + template: + src: certbot-renewal.service.j2 + dest: /etc/systemd/system/certbot-renewal.service + notify: Daemon Reload + +- name: Deploy certbot renewal timer + template: + src: certbot-renewal.timer.j2 + dest: /etc/systemd/system/certbot-renewal.timer + notify: Daemon Reload + +- name: Enable systemd timer for certbot renewal + service: + name: certbot-renewal.timer + enabled: yes + +- name: Get localectl status + shell: + cmd: localectl status | grep -E "en_US.UTF-8" + register: locale_status + failed_when: false + changed_when: false + +- name: Set localectl setting to en_US + command: localectl set-locale LANG=en_US.UTF-8 + when: locale_status.rc == 1 + +- name: Import zammad rpm key + rpm_key: + state: present + key: https://dl.packager.io/srv/zammad/zammad/key + +- name: Add zammad repo + yum_repository: + name: zammad + description: "Repository for zammad/zammad (stable) packages." + baseurl: "https://dl.packager.io/srv/rpm/zammad/zammad/stable/el/8/$basearch" + gpgcheck: false + gpgkey: "https://dl.packager.io/srv/zammad/zammad/key" + state: present + +- name: Add elasticsearch repo + yum_repository: + name: elasticsearch + description: "Elasticsearch repository for {{ zammad_es_version }}.x packages" + baseurl: "https://artifacts.elastic.co/packages/{{ zammad_es_version }}.x/yum" + gpgcheck: false + gpgkey: "https://artifacts.elastic.co/GPG-KEY-elasticsearch" + state: present + +- name: Install core packages + package: + name: + - elasticsearch + - nginx + - postgresql-server + - compat-openssl11 + state: present + +- name: Check if postgresql is initialized + stat: + path: "/var/lib/pgsql/data/pg_hba.conf" + register: postgres_data + +- name: Initialize postgresql + shell: "postgresql-setup initdb" + when: not postgres_data.stat.exists + +- name: Ensure that password auth is enabled for postgre on ipv4 addresses + lineinfile: + path: /var/lib/pgsql/data/pg_hba.conf + regexp: '^host all all 127.0.0.1/32' + insertafter: '^# IPv4 local connections:' + line: "host all all 127.0.0.1/32 md5" + notify: Restart postgresql + +- name: Ensure that password auth is enabled for postgre on ipv6 addresses + lineinfile: + path: /var/lib/pgsql/data/pg_hba.conf + regexp: '^host all all ::1/128' + insertafter: '^# IPv6 local connections:' + line: "host all all ::1/128 md5" + notify: Restart postgresql + +- name: Start and enable postgresql services + service: + name: postgresql + state: started + enabled: yes + +- name: Install ingest-attachment plugin in elasticsearch + elasticsearch_plugin: + name: ingest-attachment + state: present + +- name: Ensure the network host is set + lineinfile: + path: /etc/elasticsearch/elasticsearch.yml + regexp: '^network.host:' + insertafter: '^#network.host:' + line: "network.host: {{ zammad_es_network_host }}" + when: zammad_es_network_host != "" + notify: Restart elasticsearch + +- name: Ensure the discovery seed hosts is set + lineinfile: + path: /etc/elasticsearch/elasticsearch.yml + regexp: '^discovery.seed_hosts:' + insertafter: '^#discovery.seed_hosts:' + line: "discovery.seed_hosts: {{ zammad_es_discovery_seed_hosts }}" + when: zammad_es_discovery_seed_hosts != "" + notify: Restart elasticsearch + +- name: Ensure the max_content_length is set + lineinfile: + path: /etc/elasticsearch/elasticsearch.yml + regexp: '^http.max_content_length:' + insertafter: '^#http.max_content_length:' + line: "http.max_content_length: {{ zammad_es_max_content_length }}" + when: zammad_es_max_content_length != "" + notify: Restart elasticsearch + +- name: Ensure the max_content_length is set + lineinfile: + path: /etc/elasticsearch/elasticsearch.yml + regexp: '^indices.query.bool.max_clause_count:' + insertafter: '^#indices.query.bool.max_clause_count:' + line: "indices.query.bool.max_clause_count: {{ zammad_es_max_clause_count }}" + when: zammad_es_max_clause_count != "" + notify: Restart elasticsearch + +- name: Ensure the xpack_security is set + lineinfile: + path: /etc/elasticsearch/elasticsearch.yml + regexp: '^xpack.security.enabled:' + insertafter: '^#xpack.security.enabled:' + line: "xpack.security.enabled: {{ zammad_es_xpack_security_enabled }}" + when: zammad_es_xpack_security_enabled != "" + notify: Restart elasticsearch + +# TODO Add elasticsearch user/pass + +- name: Enable elastic search ingest fix + block: + - name: Ensure directory exists for zammad scripts + file: + path: /bin/zammadUtilites + recurse: yes + state: directory + + - name: Deploy ingest-attachment-fix script + template: + src: ingest-attachment-fix.sh.j2 + dest: /bin/zammadUtilites/ingest-attachment-fix.sh + + - name: Ensure directory exists for elasticsearch override + file: + path: /etc/systemd/system/elasticsearch.service.d + recurse: yes + state: directory + + - name: Configure elasticsearch-fail service + template: + src: elasticsearch-fail.service.j2 + dest: /etc/systemd/system/elasticsearch-fail.service + notify: Daemon Reload + + - name: Configure elasticsearch override + template: + src: elasticsearch.override.conf.j2 + dest: /etc/systemd/system/elasticsearch.service.d/override.conf + notify: Daemon Reload + when: zammad_es_ingest_fix + +- name: Start and enable elasticsearch services + service: + name: elasticsearch + state: started + enabled: yes + +- name: Install zammad + package: + name: zammad + state: present + +- name: Fix permissions on zammad public folder + file: + path: /opt/zammad/public + owner: zammad + group: zammad + mode: '755' + recurse: true + +- name: Manage es_user + block: + - name: Get es_user + shell: zammad run rails r "p Setting.get('es_user')" + changed_when: False + register: es_user_result + + - name: Set es_user + shell: zammad run rails r "Setting.set('es_user', '{{ zammad_es_username }}')" + when: not ( ('"' + zammad_es_username + '"') == es_user_result.stdout) + when: zammad_es_username != "" + +- name: Manage es_password + block: + - name: Get es_password + shell: zammad run rails r "p Setting.get('es_password')" + changed_when: False + register: es_password_result + + - name: Set es_password + shell: zammad run rails r "Setting.set('es_password', '{{ zammad_es_password }}')" + when: not ( ('"' + zammad_es_password + '"') == es_password_result.stdout) + when: zammad_es_password != "" + +- name: Get es_url + shell: zammad run rails r "p Setting.get('es_url')" + changed_when: False + register: es_url_result + +- name: Manage es_url + block: + - name: Set es_url + shell: zammad run rails r "Setting.set('es_url', '{{ zammad_es_url }}')" + + - name: Rebuild search index + shell: zammad run rake zammad:searchindex:rebuild + when: not ( ('"' + zammad_es_url + '"') == es_url_result.stdout) + +- name: Manage es_attachment_ignore + block: + - name: Get es_attachment_ignore + shell: zammad run rails r "p Setting.get('es_attachment_ignore')" + changed_when: False + register: es_attachment_ignore_result + + - name: Set es_attachment_ignore + shell: zammad run rails r "Setting.set('es_attachment_ignore', [ {{ zammad_es_attachment_ignore }} ] )" + when: not ( ("[" + zammad_es_attachment_ignore | regex_replace("'", '"') + "]") == es_attachment_ignore_result.stdout ) + when: zammad_es_attachment_ignore != "" + +- name: Manage es_attachment_max_size_in_mb + block: + - name: Get es_attachment_max_size_in_mb + shell: zammad run rails r "p Setting.get('es_attachment_max_size_in_mb')" + changed_when: False + register: es_attachment_max_size_in_mb_result + + - name: Set es_attachment_max_size_in_mb + shell: zammad run rails r "Setting.set('es_attachment_max_size_in_mb', '{{ zammad_es_attachment_max_size_in_mb }}')" + when: not ( ('"' + zammad_es_attachment_max_size_in_mb + '"') == es_attachment_max_size_in_mb_result.stdout) + when: zammad_es_attachment_max_size_in_mb != "" + +- name: Deploy custom CSS + template: + src: "{{ item }}" + dest: "/opt/zammad/app/assets/stylesheets/custom/{{ item | basename }}" + owner: zammad + group: zammad + with_fileglob: "{{ zammad_custom_css }}/*" + notify: Zammad precompile + +- name: Get current template folders + find: + paths: "{{ role_path }}/{{ zammad_custom_templates }}/" + file_type: directory + register: templateDirs + delegate_to: 127.0.0.1 + +- name: Process loop for a given template + include_tasks: + file: customTemplates.yml + loop: "{{ templateDirs.files }}" + loop_control: + loop_var: templateType + extended: yes + +- name: Enable zammad override + block: + - name: Ensure directory exists for zammad override + file: + path: /etc/systemd/system/zammad.service.d + recurse: yes + state: directory + + - name: Configure zammad override + template: + src: zammad.override.conf.j2 + dest: /etc/systemd/system/zammad.service.d/override.conf + notify: Daemon Reload + when: zammad_precompile + +- name: Start and enable zammad service + service: + name: zammad + state: started + enabled: yes + +- name: Deploy zammad configuration file + template: + src: zammad.conf.j2 + dest: /etc/nginx/conf.d/zammad.conf + notify: Reload nginx + +- name: Deploy httpsRedirect configuration file + template: + src: httpsRedirect.conf.j2 + dest: /etc/nginx/conf.d/httpsRedirect.conf + notify: Reload nginx + when: zammad_httpsRedirect + +- name: Start and enable nginx services + service: + name: nginx + state: started + enabled: yes + +- name: Set httpd_can_network_connect flag + seboolean: + name: httpd_can_network_connect + state: true + persistent: true + +- name: Allow apache to modify files in /opt/zammad/public/ + sefcontext: + target: '/opt/zammad/public(/.*)?' + setype: httpd_sys_content_t + state: present + register: se_zammad_public + +- name: Apply SELinux file context to files + command: restorecon -irv /opt/zammad/public + when: se_zammad_public.changed + +- name: Allow http ports access through the firewall + firewalld: + service: http + permanent: yes + state: enabled + notify: Reload firewalld + +- name: Allow https ports access through the firewall + firewalld: + service: https + permanent: yes + state: enabled + notify: Reload firewalld diff --git a/roles/zammad/templates/certbot-renewal.service.j2 b/roles/zammad/templates/certbot-renewal.service.j2 new file mode 100644 index 0000000..db6aaf3 --- /dev/null +++ b/roles/zammad/templates/certbot-renewal.service.j2 @@ -0,0 +1,5 @@ +[Unit] +Description=Certbot Renewal Service +[Service] +Type=oneshot +ExecStart=/usr/local/bin/certbot renew diff --git a/roles/zammad/templates/certbot-renewal.timer.j2 b/roles/zammad/templates/certbot-renewal.timer.j2 new file mode 100644 index 0000000..e4cdfd6 --- /dev/null +++ b/roles/zammad/templates/certbot-renewal.timer.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=Certbot Renewal Timer +[Timer] +WakeSystem=false +OnCalendar=*-*-* 01:00 +RandomizedDelaySec=600 + +[Install] +WantedBy=timers.target diff --git a/roles/zammad/templates/customCSS/CustomCSS.css b/roles/zammad/templates/customCSS/CustomCSS.css new file mode 100644 index 0000000..5eef140 --- /dev/null +++ b/roles/zammad/templates/customCSS/CustomCSS.css @@ -0,0 +1,11 @@ +/* Set text black for better readability while dealing with ticket creation */ + +.page-content .formGroup-label label { + color: black; +} + +/* Remove Zammad default branding */ + +.poweredBy { + visibility: hidden; +} diff --git a/roles/zammad/templates/elasticsearch-fail.service.j2 b/roles/zammad/templates/elasticsearch-fail.service.j2 new file mode 100644 index 0000000..54ace75 --- /dev/null +++ b/roles/zammad/templates/elasticsearch-fail.service.j2 @@ -0,0 +1,5 @@ +[Unit] +Description=Repair Elastic Seach Failure due to ingest plugin + +[Service] +ExecStart=/bin/zammadUtilites/ingest-attachment-fix.sh diff --git a/roles/zammad/templates/elasticsearch.override.conf.j2 b/roles/zammad/templates/elasticsearch.override.conf.j2 new file mode 100644 index 0000000..387477a --- /dev/null +++ b/roles/zammad/templates/elasticsearch.override.conf.j2 @@ -0,0 +1,9 @@ +[Unit] +OnFailure=elasticsearch-fail.service +StartLimitIntervalSec=600 +StartLimitBurst=5 + +[Service] +Restart=on-failure +RestartSec=5s +TimeoutStartSec=240 diff --git a/roles/zammad/templates/httpsRedirect.conf.j2 b/roles/zammad/templates/httpsRedirect.conf.j2 new file mode 100644 index 0000000..38c9641 --- /dev/null +++ b/roles/zammad/templates/httpsRedirect.conf.j2 @@ -0,0 +1,6 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name _; + return 301 https://$host$request_uri; +} diff --git a/roles/zammad/templates/ingest-attachment-fix.sh.j2 b/roles/zammad/templates/ingest-attachment-fix.sh.j2 new file mode 100644 index 0000000..4e318d7 --- /dev/null +++ b/roles/zammad/templates/ingest-attachment-fix.sh.j2 @@ -0,0 +1,15 @@ +#!/bin/bash + +date | tee -a /var/log/zammad/ingest.log + +if systemctl is-failed --quiet elasticsearch; then + echo Elastic Search is failed | tee -a /var/log/zammad/ingest.log + + if tail --line=20 /var/log/elasticsearch/elasticsearch.log | grep "Plugin \[ingest-attachment\] was built for Elasticsearch version"; then + echo Ingest-Attachment needs to be reinstalled | tee -a /var/log/zammad/ingest.log + /usr/share/elasticsearch/bin/elasticsearch-plugin remove ingest-attachment + sleep 10 + /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment --batch + systemctl start elasticsearch + fi +fi diff --git a/roles/zammad/templates/zammad.conf.j2 b/roles/zammad/templates/zammad.conf.j2 new file mode 100644 index 0000000..0c0be26 --- /dev/null +++ b/roles/zammad/templates/zammad.conf.j2 @@ -0,0 +1,68 @@ +# +# this is the nginx config for zammad +# + +upstream zammad-railsserver { + server 127.0.0.1:3000; +} + +upstream zammad-websocket { + server 127.0.0.1:6042; +} + +server { + + listen 443 ssl http2; + server_name {{ zammad_url }}; + + ssl_certificate {{ zammad_certificate }}; + ssl_certificate_key {{ zammad_certificate_key }}; + + #uncomment this if dhparams where generated above. + #ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + ssl_protocols TLSv1.2; + ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; + + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 180m; + + root /opt/zammad/public; + + # security - prevent information disclosure about server version + server_tokens off; + + access_log /var/log/nginx/zammad.access.log; + error_log /var/log/nginx/zammad.error.log; + + client_max_body_size 50M; + + location ~ ^/(assets/|robots.txt|humans.txt|favicon.ico) { + expires max; + } + + location /ws { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header CLIENT_IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 86400; + proxy_pass http://zammad-websocket; + } + + location / { + proxy_set_header Host $http_host; + proxy_set_header CLIENT_IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 300; + proxy_pass http://zammad-railsserver; + + gzip on; + gzip_types text/plain text/xml text/css image/svg+xml application/javascript application/x-javascript application/json application/xml; + gzip_proxied any; + } +} \ No newline at end of file diff --git a/roles/zammad/templates/zammad.override.conf.j2 b/roles/zammad/templates/zammad.override.conf.j2 new file mode 100644 index 0000000..254fba7 --- /dev/null +++ b/roles/zammad/templates/zammad.override.conf.j2 @@ -0,0 +1,3 @@ +[Service] +ExecStartPre=zammad run rake assets:precompile +TimeoutStartSec=240 diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..489a22e --- /dev/null +++ b/site.yml @@ -0,0 +1,5 @@ +--- +# file: site.yml +## This playbook deploys the whole application stack in this site. + +- import_playbook: zammad.yml diff --git a/zammad.yml b/zammad.yml new file mode 100644 index 0000000..58293f6 --- /dev/null +++ b/zammad.yml @@ -0,0 +1,7 @@ +--- +# file: zammad.yml + +- hosts: all + become: true + roles: + - zammad