From bf6ed83fd67946a7a45459c39136fd91488ed4a5 Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Fri, 8 Aug 2025 12:00:26 +0100 Subject: [PATCH] Feat: add cleaninsights role --- roles/podman_cleaninsights/defaults/main.yml | 7 ++ roles/podman_cleaninsights/handlers/main.yml | 26 +++++ roles/podman_cleaninsights/tasks/main.yml | 90 +++++++++++++++++ .../templates/cleaninsights.network | 2 + .../templates/cleaninsights.slice | 8 ++ .../templates/frontend.network | 2 + .../templates/matomo.container | 23 +++++ .../templates/mysql.container | 15 +++ .../podman_cleaninsights/templates/nginx.conf | 99 +++++++++++++++++++ .../templates/redis.container | 9 ++ 10 files changed, 281 insertions(+) create mode 100644 roles/podman_cleaninsights/defaults/main.yml create mode 100644 roles/podman_cleaninsights/handlers/main.yml create mode 100644 roles/podman_cleaninsights/tasks/main.yml create mode 100644 roles/podman_cleaninsights/templates/cleaninsights.network create mode 100644 roles/podman_cleaninsights/templates/cleaninsights.slice create mode 100644 roles/podman_cleaninsights/templates/frontend.network create mode 100644 roles/podman_cleaninsights/templates/matomo.container create mode 100644 roles/podman_cleaninsights/templates/mysql.container create mode 100644 roles/podman_cleaninsights/templates/nginx.conf create mode 100644 roles/podman_cleaninsights/templates/redis.container diff --git a/roles/podman_cleaninsights/defaults/main.yml b/roles/podman_cleaninsights/defaults/main.yml new file mode 100644 index 0000000..322e06a --- /dev/null +++ b/roles/podman_cleaninsights/defaults/main.yml @@ -0,0 +1,7 @@ +podman_cleaninsights_podman_rootless_user: cleaninsights +podman_cleaninsights_web_hostname: "{{ inventory_hostname }}" +podman_cleaninsights_mysql_database: matomo +# podman_cleaninsights_mysql_password: +# podman_cleaninsights_mysql_root_password: +podman_cleaninsights_php_memory_limit: 2048M +podman_cleaninsights_mysql_user: matomo diff --git a/roles/podman_cleaninsights/handlers/main.yml b/roles/podman_cleaninsights/handlers/main.yml new file mode 100644 index 0000000..667381f --- /dev/null +++ b/roles/podman_cleaninsights/handlers/main.yml @@ -0,0 +1,26 @@ +- name: restart mysql + ansible.builtin.systemd_service: + name: mysql + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + +- name: restart matomo + ansible.builtin.systemd_service: + name: matomo + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + +- name: restart redis + ansible.builtin.systemd_service: + name: "{{ podman_cleaninsights_systemd_service_slice }}" + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_cleaninsights_podman_rootless_user }}" diff --git a/roles/podman_cleaninsights/tasks/main.yml b/roles/podman_cleaninsights/tasks/main.yml new file mode 100644 index 0000000..965624a --- /dev/null +++ b/roles/podman_cleaninsights/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- name: create service configuration directories + ansible.builtin.file: + path: "/home/{{ podman_cleaninsights_podman_rootless_user }}/{{ item }}" + state: directory + owner: "{{ podman_cleaninsights_podman_rootless_user }}" + group: "{{ podman_cleaninsights_podman_rootless_user }}" + mode: "0755" + become: true + with_items: + - mysql + - matomo + +- name: install podman quadlet for rootless podman user + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" + owner: "{{ podman_cleaninsights_podman_rootless_user }}" + mode: "0400" + with_items: + - matomo.container + - mysql.container + - redis.container + notify: + - "restart {{ item | split('.') | first }}" + become: true + + +- name: install network quadlets for rootless podman user + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" + owner: "{{ podman_cleaninsights_podman_rootless_user }}" + mode: "0400" + with_items: + - frontend.network + - cleaninsights.network + become: true + +- name: verify quadlets are correctly defined + ansible.builtin.command: /usr/libexec/podman/quadlet -dryrun -user + register: podman_cleaninsights_quadlet_result + ignore_errors: true + changed_when: false + become: true + become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + +- name: assert that the quadlet verification succeeded + ansible.builtin.assert: + that: + - podman_cleaninsights_quadlet_result.rc == 0 + fail_msg: "'/usr/libexec/podman/quadlet -dryrun -user' failed! Output withheld to prevent leaking secrets." + +- name: set up nginx + ansible.builtin.include_role: + name: irl.wip.podman_nginx + vars: + podman_nginx_frontend_network: frontend + podman_nginx_podman_rootless_user: "{{ podman_cleaninsights_podman_rootless_user }}" + podman_nginx_primary_hostname: "{{ podman_cleaninsights_web_hostname }}" + podman_nginx_systemd_service_slice: cleaninsights.slice + podman_nginx_systemd_service_requires: ["matomo"] + +- name: create nginx configuration file + ansible.builtin.template: + src: nginx.conf + dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/nginx/nginx.conf" + owner: "{{ podman_cleaninsights_podman_rootless_user }}" + group: "{{ podman_cleaninsights_podman_rootless_user }}" + mode: "0644" + become: true + +- name: install services slice for rootless podman user + ansible.builtin.template: + src: "cleaninsights.slice" + dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/systemd/user/cleaninsights.slice" + owner: "{{ podman_cleaninsights_podman_rootless_user }}" + group: "{{ podman_cleaninsights_podman_rootless_user }}" + mode: "0655" + become: true + +- name: make sure services are started on boot + ansible.builtin.systemd_service: + name: "cleaninsights.slice" + enabled: true + state: started + daemon_reload: true + scope: user + become: true + become_user: "{{ podman_cleaninsights_podman_rootless_user }}" diff --git a/roles/podman_cleaninsights/templates/cleaninsights.network b/roles/podman_cleaninsights/templates/cleaninsights.network new file mode 100644 index 0000000..f95a29b --- /dev/null +++ b/roles/podman_cleaninsights/templates/cleaninsights.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=cleaninsights diff --git a/roles/podman_cleaninsights/templates/cleaninsights.slice b/roles/podman_cleaninsights/templates/cleaninsights.slice new file mode 100644 index 0000000..71c7a82 --- /dev/null +++ b/roles/podman_cleaninsights/templates/cleaninsights.slice @@ -0,0 +1,8 @@ +[Unit] +Description=Cleaninsights Slice +Before=slices.target +Requires=matomo.service +Requires=nginx.service + +[Install] +WantedBy=default.target diff --git a/roles/podman_cleaninsights/templates/frontend.network b/roles/podman_cleaninsights/templates/frontend.network new file mode 100644 index 0000000..379c059 --- /dev/null +++ b/roles/podman_cleaninsights/templates/frontend.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=frontend diff --git a/roles/podman_cleaninsights/templates/matomo.container b/roles/podman_cleaninsights/templates/matomo.container new file mode 100644 index 0000000..c5e9c70 --- /dev/null +++ b/roles/podman_cleaninsights/templates/matomo.container @@ -0,0 +1,23 @@ +[Unit] +Requires=mysql.service +After=mysql.service +Requires=redis.service +After=redis.service + +[Container] +ContainerName=matomo +Environment=MATOMO_DATABASE_HOST=mysql +Environment=PHP_MEMORY_LIMIT={{ podman_cleaninsights_php_memory_limit }} +Environment=MATOMO_DATABASE_DBNAME={{ podman_cleaninsights_mysql_database }} +Environment=MATOMO_DATABASE_PASSWORD={{ podman_cleaninsights_mysql_password }} +Environment=MATOMO_DATABASE_USERNAME={{ podman_cleaninsights_mysql_user }} +Image=docker.io/matomo:5-fpm +Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo:/var/www/html +#Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/cleaninsights.php:/var/www/html/cleaninsights.php:ro +#Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/cleaninsights.ini:/var/www/html/cleaninsights.ini:ro +Network=cleaninsights.network +Network=frontend.network + +[Service] +Restart=always +Slice=cleaninsights.slice diff --git a/roles/podman_cleaninsights/templates/mysql.container b/roles/podman_cleaninsights/templates/mysql.container new file mode 100644 index 0000000..71e6dd6 --- /dev/null +++ b/roles/podman_cleaninsights/templates/mysql.container @@ -0,0 +1,15 @@ +[Container] +ContainerName=mysql +Environment=MYSQL_ROOT_PASSWORD={{ podman_cleaninsights_mysql_root_password }} +Environment=MYSQL_DATABASE={{ podman_cleaninsights_mysql_database }} +Environment=MYSQL_USER={{ podman_cleaninsights_mysql_user }} +Environment=MYSQL_PASSWORD={{ podman_cleaninsights_mysql_password }} +Image=docker.io/mysql:9 +PublishPort=127.0.0.1:3306:3306 +Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/mysql:/var/lib/mysql +Network=cleaninsights.network + +[Service] +Restart=always +Slice=cleaninsights.slice + diff --git a/roles/podman_cleaninsights/templates/nginx.conf b/roles/podman_cleaninsights/templates/nginx.conf new file mode 100644 index 0000000..4a3d231 --- /dev/null +++ b/roles/podman_cleaninsights/templates/nginx.conf @@ -0,0 +1,99 @@ +# {{ ansible_managed }} + +upstream php-handler { + server matomo:9000; +} + +server { + listen 80; + listen [::]:80; + + server_name {{ podman_cleaninsights_web_hostname }}; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://{{ podman_cleaninsights_web_hostname }}$request_uri; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + + server_name matomo {{ podman_cleaninsights_web_hostname }}; + server_tokens off; + + ssl_certificate /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/privkey.pem; + + add_header Strict-Transport-Security max-age=15768000 always; + add_header Referrer-Policy origin always; # make sure outgoing links don't show the URL to the Matomo instance + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + root /var/www/html/; # this is the default matomo image path + index index.php; + + location ~ ^/(index|matomo|cleaninsights|piwik|js/index|plugins/HeatmapSessionRecording/configs).php { + # regex to split $uri to $fastcgi_script_name and $fastcgi_path + fastcgi_split_path_info ^(.+\.php)(/.+)$; + + # Check that the PHP script exists before passing it + #try_files $fastcgi_script_name =404; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/ + include fastcgi_params; + fastcgi_pass php-handler; + fastcgi_read_timeout 60s; + } + + # deny access to all other .php files + location ~* ^.+\.php$ { + return 403; + } + + # deny access to all cleaninsights.ini file + location ~ cleaninsights.ini { + return 403; + } + + # disable all access to the following directories + location ~ ^/(config|tmp|core|lang) { + return 403; # replace with 404 to not show these directories exist + } + + location ~ /\.ht { + return 403; + } + + location ~ js/container_.*_preview\.js$ { + expires off; + add_header Cache-Control 'private, no-cache, no-store'; + } + + location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2)$ { + allow all; + # Cache images,CSS,JS and webfonts for an hour + # Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade + expires 1h; + add_header Pragma public; + add_header Cache-Control "public"; + } + + location ~ ^/(libs|vendor|plugins|misc|node_modules) { + deny all; + return 403; + } + + # properly display textfiles in root directory + location ~/(.*\.md|LEGALNOTICE|LICENSE) { + default_type text/plain; + } +} \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/redis.container b/roles/podman_cleaninsights/templates/redis.container new file mode 100644 index 0000000..aa85fb3 --- /dev/null +++ b/roles/podman_cleaninsights/templates/redis.container @@ -0,0 +1,9 @@ +[Container] +ContainerName=redis +ExposeHostPort=6379 +Image=docker.io/redis:7.2-rc1-alpine +Network=cleaninsights.network + +[Service] +Restart=always +Slice=cleaninsights.slice