diff --git a/filter_plugins/canonical_domains_map.py b/filter_plugins/canonical_domains_map.py index 712e6514..7a7f5591 100644 --- a/filter_plugins/canonical_domains_map.py +++ b/filter_plugins/canonical_domains_map.py @@ -18,7 +18,7 @@ class FilterModule(object): seen_domains = {} for app_id, cfg in apps.items(): - if app_id.startswith(("web-","svc-")): + if app_id.startswith(("web-")): if not isinstance(cfg, dict): raise AnsibleFilterError( f"Invalid configuration for application '{app_id}': " diff --git a/filter_plugins/csp_filters.py b/filter_plugins/csp_filters.py index 41a3cea4..505fb929 100644 --- a/filter_plugins/csp_filters.py +++ b/filter_plugins/csp_filters.py @@ -129,7 +129,7 @@ class FilterModule(object): sld_tld = ".".join(domain.split(".")[-2:]) # yields "example.com" tokens.append(f"{sld_tld}") # yields "*.example.com" - if self.is_feature_enabled(applications, 'universal_logout', application_id): + if self.is_feature_enabled(applications, 'logout', application_id): # Allow logout via cymais logout proxy domain = domains.get('web-svc-logout')[0] diff --git a/group_vars/all/00_general.yml b/group_vars/all/00_general.yml index 305f5016..192b1632 100644 --- a/group_vars/all/00_general.yml +++ b/group_vars/all/00_general.yml @@ -48,8 +48,15 @@ certbot_credentials_file: "{{ certbot_credentials_dir }}/{{ cert certbot_dns_api_token: "" # Define in inventory file certbot_dns_propagation_wait_seconds: 40 # How long should the script wait for DNS propagation before continuing certbot_flavor: san # Possible options: san (recommended, with a dns flavor like cloudflare, or hetzner), wildcard(doesn't function with www redirect), deicated -certbot_webroot_path: "/var/lib/letsencrypt/" # Path used by Certbot to serve HTTP-01 ACME challenges -certbot_cert_path: "/etc/letsencrypt/live" # Path containing active certificate symlinks for domains + +# Path where Certbot stores challenge webroot files +letsencrypt_webroot_path: "/var/lib/letsencrypt/" + +# Base directory containing Certbot configuration, account data, and archives +letsencrypt_base_path: "/etc/letsencrypt/" + +# Symlink directory for the current active certificate and private key +letsencrypt_live_path: "{{ letsencrypt_base_path }}live/" ## Docker Role Specific Parameters docker_restart_policy: "unless-stopped" diff --git a/group_vars/all/06_nginx.yml b/group_vars/all/06_nginx.yml index a9eaadee..3d7f18f1 100644 --- a/group_vars/all/06_nginx.yml +++ b/group_vars/all/06_nginx.yml @@ -1,20 +1,25 @@ # Webserver Configuration +# Helper +_nginx_www_dir: /var/www/ ## Nginx-Specific Path Configurations nginx: + files: + configuration: "/etc/nginx/nginx.conf" directories: - configuration: "/etc/nginx/conf.d/" # Configuration directory + configuration: "/etc/nginx/conf.d/" # Configuration directory http: - global: "/etc/nginx/conf.d/http/global/" # Contains global configurations which will be loaded into the http block - servers: "/etc/nginx/conf.d/http/servers/" # Contains one configuration per domain - maps: "/etc/nginx/conf.d/http/maps/" # Contains mappings - streams: "/etc/nginx/conf.d/streams/" # Contains streams configuration e.g. for ldaps + global: "/etc/nginx/conf.d/http/global/" # Contains global configurations which will be loaded into the http block + servers: "/etc/nginx/conf.d/http/servers/" # Contains one configuration per domain + maps: "/etc/nginx/conf.d/http/maps/" # Contains mappings + streams: "/etc/nginx/conf.d/streams/" # Contains streams configuration e.g. for ldaps data: - well_known: "/usr/share/nginx/well-known/" # Path where well-known files are stored - html: "/var/www/public_html/" # Path where the static homepage files are stored - files: "/var/www/public_files/" # Path where the web accessable files are stored - global: "/var/www/global/" # Directory containing files which will be globaly accessable + www: "{{ _nginx_www_dir }}" + well_known: "/usr/share/nginx/well-known/" # Path where well-known files are stored + html: "{{ _nginx_www_dir }}public_html/" # Path where the static homepage files are stored + files: "{{ _nginx_www_dir }}public_files/" # Path where the web accessable files are stored + global: "{{ _nginx_www_dir }}global/" # Directory containing files which will be globaly accessable cache: - general: "/tmp/cache_nginx_general/" # Directory which nginx uses to cache general data - image: "/tmp/cache_nginx_image/" # Directory which nginx uses to cache images - user: "http" # Default nginx user in ArchLinux \ No newline at end of file + general: "/tmp/cache_nginx_general/" # Directory which nginx uses to cache general data + image: "/tmp/cache_nginx_image/" # Directory which nginx uses to cache images + user: "http" # Default nginx user in ArchLinux \ No newline at end of file diff --git a/group_vars/all/13_ldap.yml b/group_vars/all/13_ldap.yml index 04023ab2..49f880bc 100644 --- a/group_vars/all/13_ldap.yml +++ b/group_vars/all/13_ldap.yml @@ -10,7 +10,7 @@ _ldap_docker_network_enabled: "{{ applications | get_app_conf('svc-db-openldap _ldap_protocol: "{{ 'ldap' if _ldap_docker_network_enabled else 'ldaps' }}" _ldap_server_port: "{{ ports.localhost[_ldap_protocol]['svc-db-openldap'] }}" _ldap_name: "{{ applications | get_app_conf('svc-db-openldap', 'docker.services.openldap.name') }}" -_ldap_domain: "{{ domains | get_domain('svc-db-openldap') }}" +_ldap_domain: "{{ primary_domain }}" # LDAP is jsut listening to a port not to a dedicated domain, so primary domain should be sufficient _ldap_user_id: "uid" _ldap_filters_users_all: "(|(objectclass=inetOrgPerson))" diff --git a/roles/categories.yml b/roles/categories.yml index aa3be5ad..da6e8921 100644 --- a/roles/categories.yml +++ b/roles/categories.yml @@ -118,6 +118,11 @@ roles: description: "Optimation Services to improve your system" icon: "fas fa-database" invokable: true + prx: + title: "Proxy Servers" + description: "Reverse‑proxy roles for routing and load‑balancing traffic to backend services" + icon: "fas fa-project-diagram" + invokable: true user: title: "Users & Access" diff --git a/roles/cmp-db-docker/tasks/main.yml b/roles/cmp-db-docker/tasks/main.yml index 8902e0b1..324e15be 100644 --- a/roles/cmp-db-docker/tasks/main.yml +++ b/roles/cmp-db-docker/tasks/main.yml @@ -10,10 +10,6 @@ - "{{ cmp_db_docker_vars_file_docker }}" # Important to load docker variables first so that database can use them - "{{ cmp_db_docker_vars_file_db }}" # Important to load them before docker role so that backup can use them -- name: "For '{{ application_id }}': Load docker-compose" - include_role: - name: docker-compose - - name: "For '{{ application_id }}': Load cmp-docker-oauth2" include_role: name: cmp-docker-oauth2 diff --git a/roles/desk-virtual-box/README.md b/roles/desk-virtual-box/README.md deleted file mode 100644 index 7cda3554..00000000 --- a/roles/desk-virtual-box/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# pc-virtual-box - -```bash - sudo pacman -S virtualbox "$(pacman -Qsq "^linux" | grep "^linux[0-9]*[-rt]*$" | awk '{print $1"-virtualbox-host-modules"}' ORS=' ')" && - sudo vboxreload && - pamac build virtualbox-ext-oracle && - sudo gpasswd -a "$USER" vboxusers || exit 1 - echo "Keep in mind to install the guest additions in the virtualized system. See https://wiki.manjaro.org/index.php?title=VirtualBox" -``` \ No newline at end of file diff --git a/roles/desk-virtual-box/meta/main.yml b/roles/desk-virtual-box/meta/main.yml deleted file mode 100644 index 286d168f..00000000 --- a/roles/desk-virtual-box/meta/main.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -galaxy_info: - author: "Kevin Veen-Birchenbach" - description: "Installs and configures VirtualBox and its kernel modules on Pacman-based systems, including extension packs and user group setup." - license: "CyMaIS NonCommercial License (CNCL)" - license_url: "https://s.veen.world/cncl" - company: | - Kevin Veen-Birkenbach - Consulting & Coaching Solutions - https://www.veen.world - galaxy_tags: - - virtualbox - - virtualization - - kernel-modules - repository: "https://github.com/kevinveenbirkenbach/cymais" - issue_tracker_url: "https://github.com/kevinveenbirkenbach/cymais/issues" - documentation: "https://github.com/kevinveenbirkenbach/cymais/tree/main/roles/desk-virtual-box" - min_ansible_version: "2.9" - platforms: - - name: Archlinux - versions: - - all -dependencies: [] diff --git a/roles/desk-virtual-box/vars/main.yml b/roles/desk-virtual-box/vars/main.yml deleted file mode 100644 index 2f8ac3bd..00000000 --- a/roles/desk-virtual-box/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -application_id: desk-virtual-box diff --git a/roles/docker-compose/defaults/main.yml b/roles/docker-compose/defaults/main.yml index bb2680ea..00eefec1 100644 --- a/roles/docker-compose/defaults/main.yml +++ b/roles/docker-compose/defaults/main.yml @@ -1,3 +1,3 @@ docker_compose_skipp_file_creation: false # If set to true the file creation will be skipped -docker_pull_git_repository: false # Activates docker repository download and routine +docker_pull_git_repository: false # Activates docker repository download and routine docker_compose_flush_handlers: false # Set to true in the vars/main.yml of the including role to autoflush after docker compose routine \ No newline at end of file diff --git a/roles/srv-proxy-6-6-domain/tasks/main.yml b/roles/srv-proxy-6-6-domain/tasks/main.yml index f7623b91..dd61e0ba 100644 --- a/roles/srv-proxy-6-6-domain/tasks/main.yml +++ b/roles/srv-proxy-6-6-domain/tasks/main.yml @@ -9,7 +9,7 @@ src: "{{ vhost_template_src }}" dest: "{{ configuration_destination }}" register: nginx_conf - notify: restart nginx + notify: restart openresty - name: "Check if {{ domains | get_domain(application_id) }} is reachable (only if config unchanged)" uri: @@ -22,7 +22,7 @@ - name: Restart nginx if site is down command: cmd: "true" - notify: restart nginx + notify: restart openresty when: - not nginx_conf.changed - site_check.status is defined diff --git a/roles/srv-proxy-6-6-tls-deploy/tasks/main.yml b/roles/srv-proxy-6-6-tls-deploy/tasks/main.yml index 25763ba9..b81a577c 100644 --- a/roles/srv-proxy-6-6-tls-deploy/tasks/main.yml +++ b/roles/srv-proxy-6-6-tls-deploy/tasks/main.yml @@ -1,6 +1,6 @@ - name: add srv-proxy-6-6-tls-deploy.sh - copy: - src: "srv-proxy-6-6-tls-deploy.sh" + template: + src: "srv-proxy-6-6-tls-deploy.sh.j2" dest: "{{nginx_docker_cert_deploy_script}}" when: run_once_nginx_docker_cert_deploy is not defined notify: restart srv-proxy-6-6-tls-deploy.cymais.service diff --git a/roles/srv-proxy-6-6-tls-deploy/files/srv-proxy-6-6-tls-deploy.sh b/roles/srv-proxy-6-6-tls-deploy/templates/srv-proxy-6-6-tls-deploy.sh.j2 similarity index 82% rename from roles/srv-proxy-6-6-tls-deploy/files/srv-proxy-6-6-tls-deploy.sh rename to roles/srv-proxy-6-6-tls-deploy/templates/srv-proxy-6-6-tls-deploy.sh.j2 index dbdb3e43..22e5c318 100644 --- a/roles/srv-proxy-6-6-tls-deploy/files/srv-proxy-6-6-tls-deploy.sh +++ b/roles/srv-proxy-6-6-tls-deploy/templates/srv-proxy-6-6-tls-deploy.sh.j2 @@ -12,11 +12,11 @@ docker_compose_instance_directory="$2" docker_compose_cert_directory="$docker_compose_instance_directory/volumes/certs" # Copy certificates -cp -RvL "/etc/letsencrypt/live/$ssl_cert_folder/"* "$docker_compose_cert_directory" || exit 1 +cp -RvL "{{ letsencrypt_live_path }}/$ssl_cert_folder/"* "$docker_compose_cert_directory" || exit 1 # This code is optimized for mailu -cp -v "/etc/letsencrypt/live/$ssl_cert_folder/privkey.pem" "$docker_compose_cert_directory/key.pem" || exit 1 -cp -v "/etc/letsencrypt/live/$ssl_cert_folder/fullchain.pem" "$docker_compose_cert_directory/cert.pem" || exit 1 +cp -v "{{ letsencrypt_live_path }}/$ssl_cert_folder/privkey.pem" "$docker_compose_cert_directory/key.pem" || exit 1 +cp -v "{{ letsencrypt_live_path }}/$ssl_cert_folder/fullchain.pem" "$docker_compose_cert_directory/cert.pem" || exit 1 # Set correct reading rights chmod a+r -v "$docker_compose_cert_directory/"* diff --git a/roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 b/roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 index b2970eb1..5dc84f51 100644 --- a/roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 +++ b/roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 @@ -6,7 +6,7 @@ server {% include 'roles/web-app-oauth2-proxy/templates/endpoint.conf.j2'%} {% endif %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% if proxy_extra_configuration is defined %} {# Additional Domain Specific Configuration #} @@ -15,7 +15,7 @@ server {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% if applications | get_app_conf(application_id, 'features.universal_logout', False) or domain == primary_domain %} + {% if applications | get_app_conf(application_id, 'features.logout', False) or domain == primary_domain %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} {% if applications | get_app_conf(application_id, 'features.oauth2', False) %} diff --git a/roles/srv-proxy-7-4-core/templates/vhost/ws_generic.conf.j2 b/roles/srv-proxy-7-4-core/templates/vhost/ws_generic.conf.j2 index eba88d92..2a6c0bda 100644 --- a/roles/srv-proxy-7-4-core/templates/vhost/ws_generic.conf.j2 +++ b/roles/srv-proxy-7-4-core/templates/vhost/ws_generic.conf.j2 @@ -7,7 +7,7 @@ server { server_name {{ domain }}; {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2' %} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2' %} client_max_body_size {{ client_max_body_size | default('100m') }}; keepalive_timeout 70; @@ -26,7 +26,7 @@ server { {% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %} - {% if applications | get_app_conf(application_id, 'features.universal_logout', False) or domain == primary_domain %} + {% if applications | get_app_conf(application_id, 'features.logout', False) or domain == primary_domain %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} diff --git a/roles/srv-web-6-6-tls-core/tasks/flavors/dedicated.yml b/roles/srv-web-6-6-tls-core/tasks/flavors/dedicated.yml index 08962944..ce324118 100644 --- a/roles/srv-web-6-6-tls-core/tasks/flavors/dedicated.yml +++ b/roles/srv-web-6-6-tls-core/tasks/flavors/dedicated.yml @@ -1,7 +1,7 @@ - name: "Check if certificate already exists for {{ domain }}" cert_check_exists: domain: "{{ domain }}" - cert_base_path: "{{ certbot_cert_path }}" + cert_base_path: "{{ letsencrypt_live_path }}" register: cert_check - name: "receive certificate for {{ domain }}" @@ -16,7 +16,7 @@ --dns-{{ certbot_acme_challenge_method }}-propagation-seconds {{ certbot_dns_propagation_wait_seconds }} {% else %} --webroot - -w {{ certbot_webroot_path }} + -w {{ letsencrypt_webroot_path }} {% endif %} {% if wildcard_domain is defined and ( wildcard_domain | bool ) %} -d {{ primary_domain }} diff --git a/roles/srv-web-6-6-tls-core/tasks/flavors/san.yml b/roles/srv-web-6-6-tls-core/tasks/flavors/san.yml index 6a0af9fc..573ccf93 100644 --- a/roles/srv-web-6-6-tls-core/tasks/flavors/san.yml +++ b/roles/srv-web-6-6-tls-core/tasks/flavors/san.yml @@ -16,7 +16,7 @@ --certbot-credentials-file "{{ certbot_credentials_file }}" --certbot-dns-propagation-seconds "{{ certbot_dns_propagation_wait_seconds }}" {% else %} - --certbot-webroot-path "{{ certbot_webroot_path }}" + --certbot-webroot-path "{{ letsencrypt_webroot_path }}" {% endif %} {{ '--mode-test' if mode_test | bool else '' }} register: certbundle_result diff --git a/roles/srv-web-6-6-tls-core/tasks/main.yml b/roles/srv-web-6-6-tls-core/tasks/main.yml index 10300faa..83bfadd4 100644 --- a/roles/srv-web-6-6-tls-core/tasks/main.yml +++ b/roles/srv-web-6-6-tls-core/tasks/main.yml @@ -22,7 +22,7 @@ - name: "Find SSL cert folder for '{{ domain }}'" cert_folder_find: domain: "{{ domain }}" - cert_base_path: "{{ certbot_cert_path }}" + cert_base_path: "{{ letsencrypt_live_path }}" debug: "{{ enable_debug | default(false) }}" register: cert_folder_result delegate_to: "{{ inventory_hostname }}" diff --git a/roles/srv-web-7-4-core/handlers/main.yml b/roles/srv-web-7-4-core/handlers/main.yml deleted file mode 100644 index 12e4e5cd..00000000 --- a/roles/srv-web-7-4-core/handlers/main.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -- name: Validate Nginx configuration - command: nginx -t - register: nginx_test - changed_when: false - failed_when: nginx_test.rc != 0 - listen: restart nginx - -- name: restart nginx - service: - name: nginx - state: restarted - enabled: yes - listen: restart nginx diff --git a/roles/srv-web-7-4-core/tasks/main.yml b/roles/srv-web-7-4-core/tasks/main.yml index 37ab9086..a57d39dd 100644 --- a/roles/srv-web-7-4-core/tasks/main.yml +++ b/roles/srv-web-7-4-core/tasks/main.yml @@ -1,11 +1,18 @@ --- -- name: install nginx - pacman: - name: - - nginx - - nginx-mod-stream - state: present - notify: restart nginx + +- name: "Store 'application_id' : {{ application_id }}" + set_fact: + original_application_id: "{{ application_id }}" + when: run_once_srv_web_core is not defined + +- name: Include openresty + include_role: + name: svc-prx-openresty + when: run_once_srv_web_core is not defined + +- name: "Restore 'application_id':\n Current: {{ application_id }}\n Restored: {{ original_application_id }}" + set_fact: + application_id: "{{ original_application_id }}" when: run_once_srv_web_core is not defined - name: "reset (if enabled)" @@ -46,8 +53,8 @@ - name: create nginx config file template: src: nginx.conf.j2 - dest: /etc/nginx/nginx.conf - notify: restart nginx + dest: "{{ nginx.files.configuration }}" + notify: restart openresty when: run_once_srv_web_core is not defined - name: flush nginx service diff --git a/roles/srv-web-7-4-core/templates/nginx.conf.j2 b/roles/srv-web-7-4-core/templates/nginx.conf.j2 index e1c678a8..dfb9bc0b 100644 --- a/roles/srv-web-7-4-core/templates/nginx.conf.j2 +++ b/roles/srv-web-7-4-core/templates/nginx.conf.j2 @@ -1,4 +1,3 @@ -load_module /usr/lib/nginx/modules/ngx_stream_module.so; worker_processes auto; events @@ -33,11 +32,11 @@ http '"ConnRequests: $connection_requests" ' '"X-Forwarded-For: $http_x_forwarded_for" ' '"Scheme: $scheme" "Protocol: $server_protocol" "ServerName: $server_name"'; - access_log syslog:server=unix:/dev/log debug; + access_log /dev/stdout debug; {% else %} - access_log syslog:server=unix:/dev/log; + access_log /dev/stdout debug; {% endif %} - error_log syslog:server=unix:/dev/log; + error_log /dev/stderr info; sendfile on; keepalive_timeout 65; diff --git a/roles/srv-web-7-6-https/README.md b/roles/srv-web-7-6-https/README.md index f28bf531..5db76c6a 100644 --- a/roles/srv-web-7-6-https/README.md +++ b/roles/srv-web-7-6-https/README.md @@ -45,8 +45,8 @@ All tasks are idempotent—once your certificates are in place and your configur - A working `srv-web-7-4-core` setup. - DNS managed via Cloudflare (for CAA record tasks) or equivalent ACME DNS flow. - Variables: - - `certbot_webroot_path` - - `certbot_cert_path` + - `letsencrypt_webroot_path` + - `letsencrypt_live_path` - `on_calendar_renew_lets_encrypt_certificates` --- diff --git a/roles/srv-web-7-7-inj-compose/tasks/main.yml b/roles/srv-web-7-7-inj-compose/tasks/main.yml index 384bd0bb..55033305 100644 --- a/roles/srv-web-7-7-inj-compose/tasks/main.yml +++ b/roles/srv-web-7-7-inj-compose/tasks/main.yml @@ -10,12 +10,17 @@ name: srv-web-7-7-inj-matomo when: applications | get_app_conf(application_id, 'features.matomo', False) -- name: "Activate Portfolio iFrame Notifier for {{ domain }}" +- name: "Activate Portfolio iFrame notifier for {{ domain }}" include_role: - name: srv-web-7-7-inj-iframe + name: srv-web-7-7-inj-port-ui-desktop when: applications | get_app_conf(application_id, 'features.port-ui-desktop', False) - name: "Activate Javascript for {{ domain }}" include_role: name: srv-web-7-7-inj-javascript when: applications | get_app_conf(application_id, 'features.javascript', False) + +- name: "Activate logout proxy for {{ domain }}" + include_role: + name: srv-web-7-7-inj-logout + when: applications | get_app_conf(application_id, 'features.logout', False) diff --git a/roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2 b/roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2 deleted file mode 100644 index 710f2d19..00000000 --- a/roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2 +++ /dev/null @@ -1,33 +0,0 @@ -{# Allow multiple sub_filters #} -sub_filter_once off; -sub_filter_types text/html; - -{% set modifier_css_enabled = applications | get_app_conf(application_id, 'features.css', False) %} -{% set modifier_matomo_enabled = applications | get_app_conf(application_id, 'features.matomo', False) %} -{% set modifier_iframe_enabled = applications | get_app_conf(application_id, 'features.port-ui-desktop', False) %} -{% set modifier_javascript_enabled = applications | get_app_conf(application_id, 'features.javascript', False) %} - -{% if modifier_iframe_enabled or modifier_css_enabled or modifier_matomo_enabled or modifier_javascript_enabled %} -sub_filter '' ' - {%- if modifier_css_enabled -%} - {%- include "roles/srv-web-7-7-inj-css/templates/head_sub.j2" -%} - {%- endif -%} - {%- if modifier_matomo_enabled -%} - {%- include "roles/srv-web-7-7-inj-matomo/templates/head_sub.j2" -%} - {%- endif -%} - {%- if modifier_iframe_enabled -%} - {%- include "roles/srv-web-7-7-inj-iframe/templates/head_sub.j2" -%} - {%- endif -%} - {%- if modifier_javascript_enabled -%} - {%- include "roles/srv-web-7-7-inj-javascript/templates/head_sub.j2" -%} - {%- endif -%} - '; -{% endif %} - -{% if modifier_css_enabled | bool %} -{% include 'roles/srv-web-7-7-inj-css/templates/location.conf.j2' %} -{% endif %} - -{% if modifier_matomo_enabled %} -{% include 'roles/srv-web-7-7-inj-matomo/templates/matomo-tracking.conf.j2' %} -{% endif %} \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2 b/roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2 new file mode 100644 index 00000000..55ae147b --- /dev/null +++ b/roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2 @@ -0,0 +1,52 @@ +{% set modifier_css_enabled = applications | get_app_conf(application_id, 'features.css', false) | bool %} +{% if modifier_css_enabled %} +{%- include 'roles/srv-web-7-7-inj-css/templates/location.conf.j2' -%} +{% endif %} + +lua_need_request_body on; + +body_filter_by_lua_block { + -- initialize buffer + ngx.ctx.buf = ngx.ctx.buf or {} + local chunk, eof = ngx.arg[1], ngx.arg[2] + + if chunk ~= "" then + table.insert(ngx.ctx.buf, chunk) + end + + if not eof then + ngx.arg[1] = nil + return + end + + -- on eof: concatenate and reset buffer + local whole = table.concat(ngx.ctx.buf) + ngx.ctx.buf = nil + + -- build head-injection snippets + local head_snippets = {} + + {% for head_feature in ['css', 'matomo', 'port-ui-desktop', 'javascript', 'logout'] %} + {% if applications | get_app_conf(application_id, 'features.' ~ head_feature, false) | bool %} + head_snippets[#head_snippets + 1] = [=[ + {%- include "roles/srv-web-7-7-inj-" ~ head_feature ~ "/templates/head_sub.j2" -%} + ]=] + {% endif %} + {% endfor %} + + -- inject into + local head_payload = table.concat(head_snippets, "\n") .. "" + whole = string.gsub(whole, "", head_payload) + + {% if applications | get_app_conf(application_id, 'features.matomo', false) | bool %} + -- build Matomo noscript tracking for body + local body_matomo = [=[ + {%- include 'roles/srv-web-7-7-inj-matomo/templates/body_sub.j2' -%} + ]=] + + -- inject before + whole = string.gsub(whole, "", body_matomo) + {% endif %} + + ngx.arg[1] = whole +} diff --git a/roles/srv-web-7-7-inj-iframe/templates/head_sub.j2 b/roles/srv-web-7-7-inj-iframe/templates/head_sub.j2 deleted file mode 100644 index 1ba92d16..00000000 --- a/roles/srv-web-7-7-inj-iframe/templates/head_sub.j2 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-javascript/templates/head_sub.j2 b/roles/srv-web-7-7-inj-javascript/templates/head_sub.j2 index 1731b48b..a0f0a102 100644 --- a/roles/srv-web-7-7-inj-javascript/templates/head_sub.j2 +++ b/roles/srv-web-7-7-inj-javascript/templates/head_sub.j2 @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-logout/README.md b/roles/srv-web-7-7-inj-logout/README.md new file mode 100644 index 00000000..2c9dbf64 --- /dev/null +++ b/roles/srv-web-7-7-inj-logout/README.md @@ -0,0 +1,29 @@ +# srv-web-7-7-inj-logout + +This role injects a catcher that intercepts all logout elements in HTML pages served by Nginx and redirects them to a centralized logout endpoint via JavaScript. + +## Description + +The `srv-web-7-7-inj-logout` Ansible role automatically embeds a lightweight JavaScript snippet into your web application's HTML responses. This script identifies logout links, buttons, forms, and other elements, overrides their target URLs, and ensures users are redirected to a central OIDC logout endpoint, providing a consistent single sign‑out experience. + +## Overview + +- **Detection**: Scans the DOM for anchors (``), buttons, inputs, forms, `use` elements and any attributes indicating logout functionality. +- **Override**: Rewrites logout URLs to point at your OIDC provider’s logout endpoint, including a redirect back to the application. +- **Dynamic content support**: Uses a `MutationObserver` to handle AJAX‑loaded or dynamically injected logout elements. +- **CSP integration**: Automatically appends the required script hash into your CSP policy via the role’s CSP helper. + +## Features + +- Seamless injection via Nginx `sub_filter` on ``. +- Automatic detection of various logout mechanisms (links, buttons, forms). +- Centralized logout redirection for a unified user experience. +- No changes required in application code. +- Compatible with SPAs and dynamically generated content. +- CSP‑friendly: manages script hash for you. + +## Further Resources + +- [OpenID Connect RP-Initiated Logout](https://openid.net/specs/openid-connect-session-1_0.html#RPLogout) +- [Nginx `sub_filter` Module](http://nginx.org/en/docs/http/ngx_http_sub_module.html) +- [Ansible Role Directory Structure](https://docs.ansible.com/ansible/latest/user_guide/playbooks_roles.html#role-directory-structure) diff --git a/roles/srv-web-7-7-inj-logout/meta/main.yml b/roles/srv-web-7-7-inj-logout/meta/main.yml index 3ea40421..4a83e273 100644 --- a/roles/srv-web-7-7-inj-logout/meta/main.yml +++ b/roles/srv-web-7-7-inj-logout/meta/main.yml @@ -1,28 +1,29 @@ --- galaxy_info: - author: "Kevin Veen-Birkenbach" - description: "Injects a catcher, which catches the actions of all logout elements and redirects them to the central logout." - company: | - Kevin Veen-Birkenbach - Consulting & Coaching Solutions - https://www.veen.world + author: "Kevin Veen‑Birkenbach" + role_name: "srv-web-7-7-inj-logout" + description: > + Injects a JavaScript snippet via Nginx sub_filter that intercepts all logout actions + (links, buttons, forms) and redirects users to a centralized OIDC logout endpoint. license: "CyMaIS NonCommercial License (CNCL)" license_url: "https://s.veen.world/cncl" min_ansible_version: "2.9" platforms: - - name: Archlinux - versions: - - rolling + - name: Any + versions: ["all"] galaxy_tags: - nginx + - logout + - oidc - javascript - csp - sub_filter - - injection - - global - repository: "https://s.veen.world/cymais" - documentation: "https://s.veen.world/cymais" - issue_tracker_url: "https://s.veen.world/cymaisissues" - + company: > + Kevin Veen‑Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + repository: "https://github.com/kevinveenbirkenbach/cymais" + issue_tracker_url: "https://github.com/kevinveenbirkenbach/cymais/issues" + documentation: "https://github.com/kevinveenbirkenbach/cymais/tree/main/roles/srv-web-7-7-inj-logout" dependencies: - srv-web-7-4-core diff --git a/roles/srv-web-7-7-inj-logout/tasks/main.yml b/roles/srv-web-7-7-inj-logout/tasks/main.yml index 39206a1f..cb767a5c 100644 --- a/roles/srv-web-7-7-inj-logout/tasks/main.yml +++ b/roles/srv-web-7-7-inj-logout/tasks/main.yml @@ -1,13 +1,13 @@ -# run_once_srv_web_7_7_inj_javascript: deactivated -- name: "Load JavaScript code for '{{ application_id }}'" +# run_once_srv_web_7_7_inj_logout: deactivated +- name: "Load logout code for '{{ application_id }}'" set_fact: - javascript_code: "{{ lookup('template', modifier_javascript_template_file) }}" + logout_code: "{{ lookup('template', 'logout.js.j2') }}" -- name: "Collapse Javascript code into one-liner for '{{application_id}}'" +- name: "Collapse logout code into one-liner for '{{application_id}}'" set_fact: - javascript_code_one_liner: "{{ javascript_code | to_one_liner }}" + logout_code_one_liner: "{{ logout_code | to_one_liner }}" -- name: "Append Javascript CSP hash for '{{application_id}}'" +- name: "Append logout CSP hash for '{{application_id}}'" set_fact: - applications: "{{ applications | append_csp_hash(application_id, javascript_code_one_liner) }}" + applications: "{{ applications | append_csp_hash(application_id, logout_code_one_liner) }}" changed_when: false diff --git a/roles/srv-web-7-7-inj-logout/templates/head_sub.j2 b/roles/srv-web-7-7-inj-logout/templates/head_sub.j2 index 1731b48b..81e67a4d 100644 --- a/roles/srv-web-7-7-inj-logout/templates/head_sub.j2 +++ b/roles/srv-web-7-7-inj-logout/templates/head_sub.j2 @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-logout/templates/logout.js.j2 b/roles/srv-web-7-7-inj-logout/templates/logout.js.j2 index d8b2fdea..7e8e613c 100644 --- a/roles/srv-web-7-7-inj-logout/templates/logout.js.j2 +++ b/roles/srv-web-7-7-inj-logout/templates/logout.js.j2 @@ -1,38 +1,100 @@ -(function() { - const logoutUrlBase = 'https://auth.cymais.cloud/realms/cymais.cloud/protocol/openid-connect/logout'; - const redirectUri = encodeURIComponent('https://cymais.cloud'); - const logoutUrl = `${logoutUrlBase}?redirect_uri=${redirectUri}`; +(function () { + const logoutUrlBase = '{{ oidc.client.logout_url }}'; + const redirectUri = encodeURIComponent('{{ web_protocol }}://{{ primary_domain }}'); + const logoutUrl = logoutUrlBase + '?redirect_uri=' + redirectUri; - // Check if a string matches logout keywords function matchesLogout(str) { - return str && /logout|log\s*out|abmelden/i.test(str); + return str && /(?:^|\W)log\s*out(?:\W|$)|logout/i.test(str); } - // Check if any attribute name contains "logout" (case-insensitive) function hasLogoutAttribute(el) { - for (let attr of el.attributes) { - if (/logout/i.test(attr.name)) { + for (const attr of el.attributes) { + if (/logout/i.test(attr.name) || /\/logout/i.test(attr.value)) { return true; } } return false; } - // Find all elements - const allElements = document.querySelectorAll('*'); - allElements.forEach(el => { - if ( - matchesLogout(el.getAttribute('name')) || - matchesLogout(el.id) || - matchesLogout(el.className) || - matchesLogout(el.innerText) || - hasLogoutAttribute(el) - ) { - el.style.cursor = 'pointer'; - el.addEventListener('click', function(event) { - event.preventDefault(); - window.location.href = logoutUrl; - }); + function matchesTechnicalIndicators(el) { + const title = el.getAttribute('title'); + const ariaLabel = el.getAttribute('aria-label'); + const onclick = el.getAttribute('onclick'); + + if (matchesLogout(title) || matchesLogout(ariaLabel) || matchesLogout(onclick)) return true; + + for (const attr of el.attributes) { + if (attr.name.startsWith('data-') && matchesLogout(attr.name + attr.value)) return true; } + + if (typeof el.onclick === 'function' && matchesLogout(el.onclick.toString())) return true; + + if (el.tagName.toLowerCase() === 'use') { + const href = el.getAttribute('xlink:href') || el.getAttribute('href'); + if (matchesLogout(href)) return true; + } + + return false; + } + + function overrideLogout(el) { + if (el.dataset._logoutHandled) return; // Prevent duplicate handling + el.dataset._logoutHandled = "true"; + + el.style.cursor = 'pointer'; + el.addEventListener('click', function (event) { + event.preventDefault(); + window.location.href = logoutUrl; + }); + + const tagName = el.tagName.toLowerCase(); + + if (tagName === 'a' && el.hasAttribute('href') && /\/logout/i.test(el.getAttribute('href'))) { + el.setAttribute('href', logoutUrl); + } + + if ((tagName === 'button' || tagName === 'input') && + el.hasAttribute('formaction') && /\/logout/i.test(el.getAttribute('formaction'))) { + el.setAttribute('formaction', logoutUrl); + } + + if (tagName === 'form' && el.hasAttribute('action') && /\/logout/i.test(el.getAttribute('action'))) { + el.setAttribute('action', logoutUrl); + } + } + + function scanAndPatch(elements) { + elements.forEach(el => { + const tagName = el.tagName.toLowerCase(); + const isPotentialLogoutElement = ['a', 'button', 'input', 'form', 'use'].includes(tagName); + + if ( + isPotentialLogoutElement && ( + matchesLogout(el.getAttribute('name')) || + matchesLogout(el.id) || + matchesLogout(el.className) || + matchesLogout(el.innerText) || + hasLogoutAttribute(el) || + matchesTechnicalIndicators(el) + ) + ) { + overrideLogout(el); + } + }); + } + + // Initial scan + scanAndPatch(document.querySelectorAll('*')); + + // MutationObserver for dynamic content + const observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + mutation.addedNodes.forEach(node => { + if (!(node instanceof Element)) return; + scanAndPatch([node, ...node.querySelectorAll('*')]); + }); + }); }); + + observer.observe(document.body, { childList: true, subtree: true }); })(); diff --git a/roles/srv-web-7-7-inj-logout/vars/main.yml b/roles/srv-web-7-7-inj-logout/vars/main.yml deleted file mode 100644 index 1e72f9bc..00000000 --- a/roles/srv-web-7-7-inj-logout/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -modifier_javascript_template_file: "{{ application_id | abs_role_path_by_application_id }}/templates/javascript.js.j2" \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-matomo/templates/body_sub.j2 b/roles/srv-web-7-7-inj-matomo/templates/body_sub.j2 new file mode 100644 index 00000000..9247c42d --- /dev/null +++ b/roles/srv-web-7-7-inj-matomo/templates/body_sub.j2 @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-matomo/templates/matomo-tracking.conf.j2 b/roles/srv-web-7-7-inj-matomo/templates/matomo-tracking.conf.j2 deleted file mode 100644 index 76eb6553..00000000 --- a/roles/srv-web-7-7-inj-matomo/templates/matomo-tracking.conf.j2 +++ /dev/null @@ -1,2 +0,0 @@ -# sub filters to integrate matomo tracking code in nginx websites -sub_filter '' ''; \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-iframe/README.md b/roles/srv-web-7-7-inj-port-ui-desktop/README.md similarity index 100% rename from roles/srv-web-7-7-inj-iframe/README.md rename to roles/srv-web-7-7-inj-port-ui-desktop/README.md diff --git a/roles/srv-web-7-7-inj-iframe/meta/main.yml b/roles/srv-web-7-7-inj-port-ui-desktop/meta/main.yml similarity index 100% rename from roles/srv-web-7-7-inj-iframe/meta/main.yml rename to roles/srv-web-7-7-inj-port-ui-desktop/meta/main.yml diff --git a/roles/srv-web-7-7-inj-iframe/tasks/main.yml b/roles/srv-web-7-7-inj-port-ui-desktop/tasks/main.yml similarity index 89% rename from roles/srv-web-7-7-inj-iframe/tasks/main.yml rename to roles/srv-web-7-7-inj-port-ui-desktop/tasks/main.yml index 8caaa151..339c86b2 100644 --- a/roles/srv-web-7-7-inj-iframe/tasks/main.yml +++ b/roles/srv-web-7-7-inj-port-ui-desktop/tasks/main.yml @@ -1,4 +1,4 @@ -# run_once_srv_web_7_7_inj_iframe: deactivated +# run_once_srv_web_7_7_inj_port_ui_desktop: deactivated - name: "Load iFrame handler JS template for '{{ application_id }}'" set_fact: iframe_code: "{{ lookup('template','iframe-handler.js.j2') }}" diff --git a/roles/srv-web-7-7-inj-port-ui-desktop/templates/head_sub.j2 b/roles/srv-web-7-7-inj-port-ui-desktop/templates/head_sub.j2 new file mode 100644 index 00000000..42f9bafd --- /dev/null +++ b/roles/srv-web-7-7-inj-port-ui-desktop/templates/head_sub.j2 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/roles/srv-web-7-7-inj-iframe/templates/iframe-handler.js.j2 b/roles/srv-web-7-7-inj-port-ui-desktop/templates/iframe-handler.js.j2 similarity index 100% rename from roles/srv-web-7-7-inj-iframe/templates/iframe-handler.js.j2 rename to roles/srv-web-7-7-inj-port-ui-desktop/templates/iframe-handler.js.j2 diff --git a/roles/srv-web-7-7-letsencrypt/tasks/main.yml b/roles/srv-web-7-7-letsencrypt/tasks/main.yml index b8db39d8..b1c0db4d 100644 --- a/roles/srv-web-7-7-letsencrypt/tasks/main.yml +++ b/roles/srv-web-7-7-letsencrypt/tasks/main.yml @@ -2,7 +2,7 @@ template: src: "letsencrypt.conf.j2" dest: "{{nginx.directories.http.global}}letsencrypt.conf" - notify: restart nginx + notify: restart openresty when: run_once_letsencrypt is not defined - name: "Set CAA records for all base domains" diff --git a/roles/srv-web-7-7-letsencrypt/templates/letsencrypt.conf.j2 b/roles/srv-web-7-7-letsencrypt/templates/letsencrypt.conf.j2 index 72f96aae..8c9106b9 100644 --- a/roles/srv-web-7-7-letsencrypt/templates/letsencrypt.conf.j2 +++ b/roles/srv-web-7-7-letsencrypt/templates/letsencrypt.conf.j2 @@ -9,7 +9,7 @@ server #letsencrypt location ^~ /.well-known/acme-challenge/ { allow all; - root {{ certbot_webroot_path }}; + root {{ letsencrypt_webroot_path }}; default_type "text/plain"; try_files $uri =404; } diff --git a/roles/srv-web-7-7-letsencrypt/templates/ssl_credentials.j2 b/roles/srv-web-7-7-letsencrypt/templates/ssl_credentials.j2 index ad128f0e..872c1560 100644 --- a/roles/srv-web-7-7-letsencrypt/templates/ssl_credentials.j2 +++ b/roles/srv-web-7-7-letsencrypt/templates/ssl_credentials.j2 @@ -1,3 +1,3 @@ -ssl_certificate {{ certbot_cert_path }}/{{ ssl_cert_folder }}/fullchain.pem; -ssl_certificate_key {{ certbot_cert_path }}/{{ ssl_cert_folder }}/privkey.pem; -ssl_trusted_certificate {{ certbot_cert_path }}/{{ ssl_cert_folder }}/chain.pem; \ No newline at end of file +ssl_certificate {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.pem; +ssl_certificate_key {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/privkey.pem; +ssl_trusted_certificate {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/chain.pem; \ No newline at end of file diff --git a/roles/svc-db-openldap/tasks/main.yml b/roles/svc-db-openldap/tasks/main.yml index ecfdb88b..37e60adc 100644 --- a/roles/svc-db-openldap/tasks/main.yml +++ b/roles/svc-db-openldap/tasks/main.yml @@ -8,7 +8,7 @@ template: src: "nginx.stream.conf.j2" dest: "{{nginx.directories.streams}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty when: applications | get_app_conf(application_id, 'network.public', True) | bool - name: Remove {{domains | get_domain(application_id)}}.conf if LDAP is not exposed to internet diff --git a/roles/svc-db-postgres/meta/main.yml b/roles/svc-db-postgres/meta/main.yml index 7a0498b5..30b89162 100644 --- a/roles/svc-db-postgres/meta/main.yml +++ b/roles/svc-db-postgres/meta/main.yml @@ -24,3 +24,5 @@ galaxy_info: repository: "https://s.veen.world/cymais" issue_tracker_url: "https://s.veen.world/cymaisissues" documentation: "https://s.veen.world/cymais" +dependencies: + - docker-core # Loading it here to avoid extra flush for network init diff --git a/roles/svc-prx-openresty/README.md b/roles/svc-prx-openresty/README.md new file mode 100644 index 00000000..0856d1f8 --- /dev/null +++ b/roles/svc-prx-openresty/README.md @@ -0,0 +1,23 @@ +# OpenResty + +This role deploys an OpenResty container via Docker Compose, validates its configuration, and restarts it on changes. + +## Description + +- Runs an OpenResty container in host network mode +- Mounts Nginx configuration and Let’s Encrypt directories +- Validates the OpenResty (Nginx) configuration before any restart +- Restarts the container only if the configuration is valid + +## Overview + +1. Loads the base Docker Compose setup +2. Adds the OpenResty service +3. Defines handlers to validate and restart the container +4. Triggers a restart on configuration changes + +## Further Reading + +- [OpenResty Docker Hub](https://hub.docker.com/r/openresty/openresty) +- [OpenResty Official Documentation](https://openresty.org/) +- [Ansible Docker Compose Role on Galaxy](https://galaxy.ansible.com/) diff --git a/roles/svc-prx-openresty/handlers/main.yml b/roles/svc-prx-openresty/handlers/main.yml new file mode 100644 index 00000000..d8481d16 --- /dev/null +++ b/roles/svc-prx-openresty/handlers/main.yml @@ -0,0 +1,12 @@ +--- +- name: Validate OpenResty configuration + command: > + docker exec {{ openresty_container }} openresty -t -q + register: openresty_test + changed_when: false + failed_when: openresty_test.rc != 0 + listen: restart openresty + +- name: Restart OpenResty container + command: docker restart {{ openresty_container }} + listen: restart openresty diff --git a/roles/svc-prx-openresty/meta/main.yml b/roles/svc-prx-openresty/meta/main.yml new file mode 100644 index 00000000..2b929f84 --- /dev/null +++ b/roles/svc-prx-openresty/meta/main.yml @@ -0,0 +1,20 @@ +--- +galaxy_info: + author: "Kevin Veen‑Birkenbach" + description: > + Role to provision an OpenResty container via Docker Compose. + license: "CyMaIS NonCommercial License (CNCL)" + license_url: "https://s.veen.world/cncl" + company: | + Kevin Veen‑Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + galaxy_tags: + - openresty + - nginx + - docker + - reverse_proxy + repository: "https://github.com/kevinveenbirkenbach/cymais" + issue_tracker_url: "https://github.com/kevinveenbirkenbach/cymais/issues" + documentation: "https://github.com/kevinveenbirkenbach/cymais/tree/main/roles/svc-prx-openresty" + min_ansible_version: "2.9" diff --git a/roles/svc-prx-openresty/tasks/main.yml b/roles/svc-prx-openresty/tasks/main.yml new file mode 100644 index 00000000..57a6bf4b --- /dev/null +++ b/roles/svc-prx-openresty/tasks/main.yml @@ -0,0 +1,9 @@ +- name: "For '{{ application_id }}': Load docker-compose" + include_role: + name: docker-compose + when: run_once_svc_prx_openresty is not defined + +- name: Run the docker_postgres tasks once + set_fact: + run_once_svc_prx_openresty: true + when: run_once_svc_prx_openresty is not defined \ No newline at end of file diff --git a/roles/svc-prx-openresty/templates/docker-compose.yml.j2 b/roles/svc-prx-openresty/templates/docker-compose.yml.j2 new file mode 100644 index 00000000..95a171b3 --- /dev/null +++ b/roles/svc-prx-openresty/templates/docker-compose.yml.j2 @@ -0,0 +1,15 @@ +{% include 'roles/docker-compose/templates/base.yml.j2' %} + openresty: + container_name: {{ openresty_container }} + image: {{ openresty_image }}:{{ openresty_version }} + network_mode: "host" + volumes: + - {{ nginx.files.configuration }}:/usr/local/openresty/nginx/conf/nginx.conf:ro + - {{ nginx.directories.configuration }}:/usr/local/openresty/nginx/conf/conf.d:ro + - {{ nginx.files.configuration }}:{{ nginx.files.configuration }}:ro + - {{ nginx.directories.configuration }}:{{ nginx.directories.configuration }}:ro + - {{ nginx.directories.data.www }}:{{ nginx.directories.data.www }}:ro + - {{ nginx.directories.data.well_known }}:{{ nginx.directories.data.well_known }}:ro + - {{ letsencrypt_webroot_path }}:{{ letsencrypt_webroot_path }}:ro + - {{ letsencrypt_base_path }}:{{ letsencrypt_base_path }}:ro + command: ["openresty", "-g", "daemon off;"] \ No newline at end of file diff --git a/roles/svc-prx-openresty/vars/main.yml b/roles/svc-prx-openresty/vars/main.yml new file mode 100644 index 00000000..cb93c131 --- /dev/null +++ b/roles/svc-prx-openresty/vars/main.yml @@ -0,0 +1,9 @@ +application_id: "svc-prx-openresty" + +# Openresty +openresty_image: "openresty/openresty" +openresty_version: "alpine" +openresty_container: "openresty" + +# Docker +docker_compose_flush_handlers: true \ No newline at end of file diff --git a/roles/sys-cln-domains/tasks/remove_deprecated_nginx_configs.yml b/roles/sys-cln-domains/tasks/remove_deprecated_nginx_configs.yml index 0b611346..377c1d37 100644 --- a/roles/sys-cln-domains/tasks/remove_deprecated_nginx_configs.yml +++ b/roles/sys-cln-domains/tasks/remove_deprecated_nginx_configs.yml @@ -11,10 +11,10 @@ state: absent loop: "{{ find_result.files | default([]) }}" when: item is defined - notify: restart nginx + notify: restart openresty - name: Remove exact nginx config for {{ domain }} ansible.builtin.file: path: "{{ nginx.directories.http.servers }}{{ domain }}.conf" state: absent - notify: restart nginx \ No newline at end of file + notify: restart openresty \ No newline at end of file diff --git a/roles/web-app-akaunting/config/main.yml b/roles/web-app-akaunting/config/main.yml index 13152c30..af103ccf 100644 --- a/roles/web-app-akaunting/config/main.yml +++ b/roles/web-app-akaunting/config/main.yml @@ -7,7 +7,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true domains: canonical: - "accounting.{{ primary_domain }}" diff --git a/roles/web-app-attendize/config/main.yml b/roles/web-app-attendize/config/main.yml index 8a5f54be..3871af93 100644 --- a/roles/web-app-attendize/config/main.yml +++ b/roles/web-app-attendize/config/main.yml @@ -6,7 +6,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true docker: services: redis: diff --git a/roles/web-app-attendize/tasks/main.yml b/roles/web-app-attendize/tasks/main.yml index 585980ec..9501db68 100644 --- a/roles/web-app-attendize/tasks/main.yml +++ b/roles/web-app-attendize/tasks/main.yml @@ -17,7 +17,7 @@ template: src: roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 dest: "{{nginx.directories.http.servers}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty - name: "For '{{ application_id }}': include tasks update-repository-with-files.yml" include_tasks: utils/update-repository-with-files.yml diff --git a/roles/web-app-baserow/config/main.yml b/roles/web-app-baserow/config/main.yml index e033f79a..749ab100 100644 --- a/roles/web-app-baserow/config/main.yml +++ b/roles/web-app-baserow/config/main.yml @@ -3,7 +3,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true docker: services: redis: diff --git a/roles/web-app-bigbluebutton/config/main.yml b/roles/web-app-bigbluebutton/config/main.yml index 3735c648..91c4b4f5 100644 --- a/roles/web-app-bigbluebutton/config/main.yml +++ b/roles/web-app-bigbluebutton/config/main.yml @@ -12,7 +12,7 @@ features: ldap: false oidc: true central_database: false - universal_logout: true + logout: true domains: canonical: - "meet.{{ primary_domain }}" diff --git a/roles/web-app-bigbluebutton/tasks/main.yml b/roles/web-app-bigbluebutton/tasks/main.yml index dca8f040..d9b9acbe 100644 --- a/roles/web-app-bigbluebutton/tasks/main.yml +++ b/roles/web-app-bigbluebutton/tasks/main.yml @@ -12,7 +12,7 @@ copy: src: "websocket_upgrade.conf" dest: "{{nginx.directories.http.maps}}websocket_upgrade.conf" - notify: restart nginx + notify: restart openresty - name: "Set BBB Facts" set_fact: diff --git a/roles/web-app-bigbluebutton/templates/env.j2 b/roles/web-app-bigbluebutton/templates/env.j2 index c413bba1..77be8e13 100644 --- a/roles/web-app-bigbluebutton/templates/env.j2 +++ b/roles/web-app-bigbluebutton/templates/env.j2 @@ -1,6 +1,6 @@ ENABLE_COTURN=true -COTURN_TLS_CERT_PATH={{ certbot_cert_path }}/{{ ssl_cert_folder }}/fullchain.pem -COTURN_TLS_KEY_PATH={{ certbot_cert_path }}/{{ ssl_cert_folder }}/privkey.pem +COTURN_TLS_CERT_PATH={{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.pem +COTURN_TLS_KEY_PATH={{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/privkey.pem ENABLE_GREENLIGHT={{ applications | get_app_conf(application_id, 'enable_greenlight', True) }} # Enable Webhooks diff --git a/roles/web-app-bluesky/config/main.yml b/roles/web-app-bluesky/config/main.yml index 504267aa..3dc93aae 100644 --- a/roles/web-app-bluesky/config/main.yml +++ b/roles/web-app-bluesky/config/main.yml @@ -7,7 +7,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true domains: canonical: web: "bskyweb.{{ primary_domain }}" diff --git a/roles/web-app-collabora/config/main.yml b/roles/web-app-collabora/config/main.yml index 7e345045..8dbee19b 100644 --- a/roles/web-app-collabora/config/main.yml +++ b/roles/web-app-collabora/config/main.yml @@ -8,4 +8,4 @@ docker: database: enabled: false # May this is wrong. Just set during refactoring features: - universal_logout: false # I think collabora is more a service then a app. So no login neccessary Propably it makes sense to rename it ;) + logout: false # I think collabora is more a service then a app. So no login neccessary Propably it makes sense to rename it ;) diff --git a/roles/web-app-collabora/tasks/main.yml b/roles/web-app-collabora/tasks/main.yml index 54fc0368..49d9f985 100644 --- a/roles/web-app-collabora/tasks/main.yml +++ b/roles/web-app-collabora/tasks/main.yml @@ -1,8 +1,8 @@ -- name: create nextcloud nginx proxy configuration file +- name: create collabora proxy configuration file template: src: "nginx.conf.j2" dest: "{{nginx.directories.http.servers}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty - name: "Include docker-compose role" include_role: diff --git a/roles/web-app-collabora/templates/nginx.conf.j2 b/roles/web-app-collabora/templates/nginx.conf.j2 index 2532213d..5c38e3ba 100644 --- a/roles/web-app-collabora/templates/nginx.conf.j2 +++ b/roles/web-app-collabora/templates/nginx.conf.j2 @@ -3,7 +3,7 @@ server { {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %} diff --git a/roles/web-app-coturn/templates/docker-compose.yml.j2 b/roles/web-app-coturn/templates/docker-compose.yml.j2 index ac8637e6..26277274 100644 --- a/roles/web-app-coturn/templates/docker-compose.yml.j2 +++ b/roles/web-app-coturn/templates/docker-compose.yml.j2 @@ -2,7 +2,6 @@ coturn: {% include 'roles/docker-container/templates/base.yml.j2' %} -{% include 'roles/docker-container/templates/networks.yml.j2' %} image: coturn/coturn restart: always network_mode: "host" # Nutzt die Host-IP für externe Erreichbarkeit (optional) @@ -42,8 +41,5 @@ --denied-peer-ip=203.0.113.0-203.0.113.255 --denied-peer-ip=240.0.0.0-255.255.255.255 -{% include 'roles/docker-compose/templates/networks.yml.j2' %} - {% include 'roles/docker-compose/templates/volumes.yml.j2' %} coturn-config: - diff --git a/roles/web-app-discourse/Todo.md b/roles/web-app-discourse/Todo.md index 33711e91..df9ac152 100644 --- a/roles/web-app-discourse/Todo.md +++ b/roles/web-app-discourse/Todo.md @@ -1,3 +1,4 @@ # Todo - Finish LDAP implementation -- Check if this current network setting makes sense. Seems a bit unneccessary complicated. Could be that a more straight foreword approach makes more sense. \ No newline at end of file +- Check if this current network setting makes sense. Seems a bit unneccessary complicated. Could be that a more straight foreword approach makes more sense. +- Implement, that username can just be identical to ldap\keycloak username. First dirty hack; Block the changing of the field via JS \ No newline at end of file diff --git a/roles/web-app-discourse/config/main.yml b/roles/web-app-discourse/config/main.yml index 519a8378..39606047 100644 --- a/roles/web-app-discourse/config/main.yml +++ b/roles/web-app-discourse/config/main.yml @@ -6,7 +6,7 @@ features: oidc: true central_database: true ldap: false # @todo implement and activate - universal_logout: true + logout: true csp: flags: style-src: diff --git a/roles/web-app-elk/config/main.yml b/roles/web-app-elk/config/main.yml index 08b1b4cf..c3916685 100644 --- a/roles/web-app-elk/config/main.yml +++ b/roles/web-app-elk/config/main.yml @@ -1,5 +1,5 @@ features: - universal_logout: false # Just deactivated to oppress warnings, elk is anyhow not running + logout: false # Just deactivated to oppress warnings, elk is anyhow not running domains: canonical: diff --git a/roles/web-app-espocrm/config/main.yml b/roles/web-app-espocrm/config/main.yml index f437b8cc..a638289d 100644 --- a/roles/web-app-espocrm/config/main.yml +++ b/roles/web-app-espocrm/config/main.yml @@ -5,7 +5,7 @@ features: ldap: false oidc: true central_database: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-friendica/config/main.yml b/roles/web-app-friendica/config/main.yml index d3629420..26474db2 100644 --- a/roles/web-app-friendica/config/main.yml +++ b/roles/web-app-friendica/config/main.yml @@ -8,7 +8,7 @@ features: central_database: true ldap: true oauth2: false # No special login side which could be protected, use 2FA of Friendica instead - universal_logout: true + logout: true domains: canonical: - "social.{{ primary_domain }}" diff --git a/roles/web-app-funkwhale/config/main.yml b/roles/web-app-funkwhale/config/main.yml index c545abd3..fa0e8ee3 100644 --- a/roles/web-app-funkwhale/config/main.yml +++ b/roles/web-app-funkwhale/config/main.yml @@ -19,7 +19,7 @@ features: ldap: true central_database: true oauth2: false # Doesn't make sense to activate it atm, because login is possible on homepage - universal_logout: true + logout: true domains: canonical: - "audio.{{ primary_domain }}" diff --git a/roles/web-app-gitea/config/main.yml b/roles/web-app-gitea/config/main.yml index d80178e6..f82badbc 100644 --- a/roles/web-app-gitea/config/main.yml +++ b/roles/web-app-gitea/config/main.yml @@ -12,7 +12,7 @@ features: ldap: true oauth2: true oidc: false # Deactivated because users aren't auto-created. - universal_logout: true + logout: true oauth2_proxy: application: "application" port: "<< defaults_applications[web-app-gitea].docker.services.gitea.port >>" diff --git a/roles/web-app-gitlab/config/main.yml b/roles/web-app-gitlab/config/main.yml index 0a4e4233..9b5e21cb 100644 --- a/roles/web-app-gitlab/config/main.yml +++ b/roles/web-app-gitlab/config/main.yml @@ -3,7 +3,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true docker: services: redis: diff --git a/roles/web-app-jenkins/config/main.yml b/roles/web-app-jenkins/config/main.yml index b440f1e4..d929a9b7 100644 --- a/roles/web-app-jenkins/config/main.yml +++ b/roles/web-app-jenkins/config/main.yml @@ -1,5 +1,5 @@ features: - universal_logout: true # Same like with elk, anyhow not active atm + logout: true # Same like with elk, anyhow not active atm domains: canonical: diff --git a/roles/web-app-joomla/config/main.yml b/roles/web-app-joomla/config/main.yml index 25d93676..a4deddd6 100644 --- a/roles/web-app-joomla/config/main.yml +++ b/roles/web-app-joomla/config/main.yml @@ -5,7 +5,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true domains: canonical: - "cms.{{ primary_domain }}" diff --git a/roles/web-app-keycloak/config/main.yml b/roles/web-app-keycloak/config/main.yml index 0f74b1d0..a7e8b5c3 100644 --- a/roles/web-app-keycloak/config/main.yml +++ b/roles/web-app-keycloak/config/main.yml @@ -6,7 +6,7 @@ features: ldap: true central_database: true recaptcha: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-lam/config/main.yml b/roles/web-app-lam/config/main.yml index e56c83fb..354aafc3 100644 --- a/roles/web-app-lam/config/main.yml +++ b/roles/web-app-lam/config/main.yml @@ -12,7 +12,7 @@ features: ldap: true central_database: false oauth2: true - universal_logout: true + logout: true csp: flags: style-src: diff --git a/roles/web-app-libretranslate/config/main.yml b/roles/web-app-libretranslate/config/main.yml index 1745367c..cb7dc2e6 100644 --- a/roles/web-app-libretranslate/config/main.yml +++ b/roles/web-app-libretranslate/config/main.yml @@ -17,7 +17,7 @@ features: recaptcha: false # Enable ReCaptcha oauth2: false # Enable the OAuth2-Proy javascript: false # Enables the custom JS in the javascript.js.j2 file - universal_logout: false # With this app I assume that it's a service, so should be renamed and logging is unneccessary + logout: false # With this app I assume that it's a service, so should be renamed and logging is unneccessary csp: whitelist: {} # URL's which should be whitelisted flags: {} # Flags which should be set diff --git a/roles/web-app-listmonk/config/main.yml b/roles/web-app-listmonk/config/main.yml index e5a7d655..ba0946ba 100644 --- a/roles/web-app-listmonk/config/main.yml +++ b/roles/web-app-listmonk/config/main.yml @@ -5,7 +5,7 @@ features: port-ui-desktop: true central_database: true oidc: true - universal_logout: true + logout: true domains: canonical: - "newsletter.{{ primary_domain }}" diff --git a/roles/web-app-mailu/config/main.yml b/roles/web-app-mailu/config/main.yml index 564a1db5..252dd498 100644 --- a/roles/web-app-mailu/config/main.yml +++ b/roles/web-app-mailu/config/main.yml @@ -8,7 +8,7 @@ features: port-ui-desktop: true # Deactivated mailu iframe loading until keycloak supports it oidc: true central_database: false # Deactivate central database for mailu, I don't know why the database deactivation is necessary - universal_logout: true + logout: true domains: canonical: - "mail.{{ primary_domain }}" diff --git a/roles/web-app-mastodon/config/main.yml b/roles/web-app-mastodon/config/main.yml index 1100dfa9..ebf91e97 100644 --- a/roles/web-app-mastodon/config/main.yml +++ b/roles/web-app-mastodon/config/main.yml @@ -6,7 +6,7 @@ features: port-ui-desktop: true oidc: true central_database: true - universal_logout: true + logout: true domains: canonical: - "microblog.{{ primary_domain }}" diff --git a/roles/web-app-matomo/config/main.yml b/roles/web-app-matomo/config/main.yml index 3068d355..e300160e 100644 --- a/roles/web-app-matomo/config/main.yml +++ b/roles/web-app-matomo/config/main.yml @@ -8,7 +8,7 @@ features: port-ui-desktop: false # Didn't work in frame didn't have high priority @todo figure out pcause and solve it central_database: true oauth2: false - universal_logout: true + logout: true csp: whitelist: script-src-elem: diff --git a/roles/web-app-matrix/config/main.yml b/roles/web-app-matrix/config/main.yml index 638c23ba..63855b2d 100644 --- a/roles/web-app-matrix/config/main.yml +++ b/roles/web-app-matrix/config/main.yml @@ -23,7 +23,7 @@ features: port-ui-desktop: true oidc: true # Deactivated OIDC due to this issue https://github.com/matrix-org/synapse/issues/10492 central_database: true - universal_logout: true + logout: true csp: flags: script-src: diff --git a/roles/web-app-matrix/tasks/main.yml b/roles/web-app-matrix/tasks/main.yml index 6e39011e..10568b66 100644 --- a/roles/web-app-matrix/tasks/main.yml +++ b/roles/web-app-matrix/tasks/main.yml @@ -37,7 +37,7 @@ vars: domain: "{{domains[application_id].synapse}}" # Didn't work in the past. May it works now. This does not seem to work @todo Check how to solve without declaring set_fact, seems a bug at templates http_port: "{{ports.localhost.http['web-app-matrix_synapse']}}" - notify: restart nginx + notify: restart openresty - name: "include role srv-proxy-6-6-domain for {{application_id}}" include_role: diff --git a/roles/web-app-matrix/templates/nginx.conf.j2 b/roles/web-app-matrix/templates/nginx.conf.j2 index c35b1c4a..76537ebb 100644 --- a/roles/web-app-matrix/templates/nginx.conf.j2 +++ b/roles/web-app-matrix/templates/nginx.conf.j2 @@ -11,10 +11,10 @@ server { listen 8448 ssl default_server; listen [::]:8448 ssl default_server; - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %} - {% if applications | get_app_conf(application_id, 'features.universal_logout', False) %} + {% if applications | get_app_conf(application_id, 'features.logout', False) %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} diff --git a/roles/web-app-mediawiki/config/main.yml b/roles/web-app-mediawiki/config/main.yml index d74c9b8e..8309b338 100644 --- a/roles/web-app-mediawiki/config/main.yml +++ b/roles/web-app-mediawiki/config/main.yml @@ -12,4 +12,4 @@ docker: volumes: data: mediawiki_data features: - universal_logout: true + logout: true diff --git a/roles/web-app-mig/config/main.yml b/roles/web-app-mig/config/main.yml index 6b69fcf7..671eb34d 100644 --- a/roles/web-app-mig/config/main.yml +++ b/roles/web-app-mig/config/main.yml @@ -8,7 +8,7 @@ features: matomo: true # activate tracking css: true # use custom cymais stile port-ui-desktop: true # Enable in port-ui - universal_logout: false + logout: false csp: whitelist: script-src-elem: diff --git a/roles/web-app-mobilizon/config/main.yml b/roles/web-app-mobilizon/config/main.yml index 57ae74ce..9daf9bbe 100644 --- a/roles/web-app-mobilizon/config/main.yml +++ b/roles/web-app-mobilizon/config/main.yml @@ -4,7 +4,7 @@ features: oidc: true matomo: true port-ui-desktop: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-moodle/config/main.yml b/roles/web-app-moodle/config/main.yml index 586252a9..6dd797c0 100644 --- a/roles/web-app-moodle/config/main.yml +++ b/roles/web-app-moodle/config/main.yml @@ -5,7 +5,7 @@ features: port-ui-desktop: true central_database: true oidc: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-mybb/config/main.yml b/roles/web-app-mybb/config/main.yml index c1d1af3a..49b1aca1 100644 --- a/roles/web-app-mybb/config/main.yml +++ b/roles/web-app-mybb/config/main.yml @@ -4,7 +4,7 @@ features: css: true port-ui-desktop: true central_database: true - universal_logout: true + logout: true docker: services: database: diff --git a/roles/web-app-mybb/tasks/setup-domain.yml b/roles/web-app-mybb/tasks/setup-domain.yml index 18980b11..73d3431f 100644 --- a/roles/web-app-mybb/tasks/setup-domain.yml +++ b/roles/web-app-mybb/tasks/setup-domain.yml @@ -8,6 +8,6 @@ template: src: "roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2" dest: "{{nginx.directories.http.servers}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty vars: proxy_extra_configuration: "sub_filter '{{source_domain}}' '{{domains | get_domain(application_id)}}';" diff --git a/roles/web-app-navigator/config/main.yml b/roles/web-app-navigator/config/main.yml index 3b6ec98b..09b866da 100644 --- a/roles/web-app-navigator/config/main.yml +++ b/roles/web-app-navigator/config/main.yml @@ -2,7 +2,7 @@ features: matomo: true css: true port-ui-desktop: true - universal_logout: false + logout: false csp: whitelist: script-src-elem: diff --git a/roles/web-app-nextcloud/config/main.yml b/roles/web-app-nextcloud/config/main.yml index 0478ec28..ca6aa10a 100644 --- a/roles/web-app-nextcloud/config/main.yml +++ b/roles/web-app-nextcloud/config/main.yml @@ -59,7 +59,7 @@ features: ldap: true oidc: true central_database: true - universal_logout: true + logout: true default_quota: '1000000000' # Quota to assign if no quota is specified in the OIDC response (bytes) legacy_login_mask: enabled: False # If true, then legacy login mask is shown. Otherwise just SSO diff --git a/roles/web-app-nextcloud/tasks/main.yml b/roles/web-app-nextcloud/tasks/main.yml index 8b1124de..3661ddc8 100644 --- a/roles/web-app-nextcloud/tasks/main.yml +++ b/roles/web-app-nextcloud/tasks/main.yml @@ -23,11 +23,11 @@ include_role: name: srv-web-7-6-composer -- name: create nextcloud nginx proxy configuration file +- name: create nextcloud proxy configuration file template: src: "nginx/host.conf.j2" dest: "{{nginx.directories.http.servers}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty - name: create internal nextcloud nginx configuration template: diff --git a/roles/web-app-nextcloud/templates/nginx/host.conf.j2 b/roles/web-app-nextcloud/templates/nginx/host.conf.j2 index 81d6d2e7..4168791f 100644 --- a/roles/web-app-nextcloud/templates/nginx/host.conf.j2 +++ b/roles/web-app-nextcloud/templates/nginx/host.conf.j2 @@ -6,7 +6,7 @@ server {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} # Remove X-Powered-By, which is an information leak fastcgi_hide_header X-Powered-By; @@ -20,7 +20,7 @@ server {% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %} - {% if applications | get_app_conf(application_id, 'features.universal_logout', False) %} + {% if applications | get_app_conf(application_id, 'features.logout', False) %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} diff --git a/roles/web-app-oauth2-proxy/config/main.yml b/roles/web-app-oauth2-proxy/config/main.yml index d69388d1..222fc19c 100644 --- a/roles/web-app-oauth2-proxy/config/main.yml +++ b/roles/web-app-oauth2-proxy/config/main.yml @@ -5,7 +5,7 @@ features: matomo: true css: true port-ui-desktop: false - universal_logout: true + logout: true domains: canonical: diff --git a/roles/web-app-oauth2-proxy/tasks/main.yml b/roles/web-app-oauth2-proxy/tasks/main.yml index b61b6c13..b13588e3 100644 --- a/roles/web-app-oauth2-proxy/tasks/main.yml +++ b/roles/web-app-oauth2-proxy/tasks/main.yml @@ -1,3 +1,10 @@ +- name: "Fail if 'web-app-oauth2-proxy' matches current role name" + fail: + msg: >- + Don't call the 'web-app-oauth2-proxy' role directly via include_role or import_role. + It is a helper role only. Direct use would lead to confusion due to application_id mismatch. + when: role_name == "web-app-oauth2-proxy" + - name: "Transfering oauth2-proxy-keycloak.cfg.j2 to {{( application_id | get_docker_paths(path_docker_compose_instances) ).directories.volumes }}" template: src: "{{ playbook_dir }}/roles/web-app-oauth2-proxy/templates/oauth2-proxy-keycloak.cfg.j2" diff --git a/roles/web-app-openproject/config/main.yml b/roles/web-app-openproject/config/main.yml index cd512499..ae0df1e2 100644 --- a/roles/web-app-openproject/config/main.yml +++ b/roles/web-app-openproject/config/main.yml @@ -17,7 +17,7 @@ features: ldap: true central_database: true oauth2: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-peertube/config/main.yml b/roles/web-app-peertube/config/main.yml index 835f67a9..90338882 100644 --- a/roles/web-app-peertube/config/main.yml +++ b/roles/web-app-peertube/config/main.yml @@ -4,7 +4,7 @@ features: port-ui-desktop: true central_database: true oidc: true - universal_logout: true + logout: true csp: flags: script-src-elem: diff --git a/roles/web-app-peertube/tasks/create-domains.yml b/roles/web-app-peertube/tasks/create-domains.yml index d693c9b3..005d410e 100644 --- a/roles/web-app-peertube/tasks/create-domains.yml +++ b/roles/web-app-peertube/tasks/create-domains.yml @@ -6,4 +6,4 @@ template: src: "templates/peertube.conf.j2" dest: "{{nginx.directories.http.servers}}{{domain}}.conf" - notify: restart nginx \ No newline at end of file + notify: restart openresty \ No newline at end of file diff --git a/roles/web-app-peertube/templates/peertube.conf.j2 b/roles/web-app-peertube/templates/peertube.conf.j2 index a3f7a860..3abe1971 100644 --- a/roles/web-app-peertube/templates/peertube.conf.j2 +++ b/roles/web-app-peertube/templates/peertube.conf.j2 @@ -3,7 +3,7 @@ server { {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %} @@ -27,7 +27,7 @@ server { proxy_pass http://127.0.0.1:{{ports.localhost.http[application_id]}}; } - {% if applications | get_app_conf(application_id, 'features.universal_logout', False) %} + {% if applications | get_app_conf(application_id, 'features.logout', False) %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} diff --git a/roles/web-app-pgadmin/config/main.yml b/roles/web-app-pgadmin/config/main.yml index 202675eb..4c7ec42a 100644 --- a/roles/web-app-pgadmin/config/main.yml +++ b/roles/web-app-pgadmin/config/main.yml @@ -12,7 +12,7 @@ features: port-ui-desktop: true central_database: true oauth2: true - universal_logout: true + logout: true csp: flags: style-src: diff --git a/roles/web-app-phpldapadmin/config/main.yml b/roles/web-app-phpldapadmin/config/main.yml index 50fc12cb..bc61834e 100644 --- a/roles/web-app-phpldapadmin/config/main.yml +++ b/roles/web-app-phpldapadmin/config/main.yml @@ -10,7 +10,7 @@ features: port-ui-desktop: true ldap: true oauth2: true - universal_logout: true + logout: true domains: canonical: diff --git a/roles/web-app-phpmyadmin/config/main.yml b/roles/web-app-phpmyadmin/config/main.yml index 022f7bf4..9b47680a 100644 --- a/roles/web-app-phpmyadmin/config/main.yml +++ b/roles/web-app-phpmyadmin/config/main.yml @@ -11,7 +11,7 @@ features: # it's anyhow not so enduser relevant, so it can be kept like this central_database: true oauth2: true - universal_logout: true + logout: true csp: flags: style-src: diff --git a/roles/web-app-pixelfed/config/main.yml b/roles/web-app-pixelfed/config/main.yml index c184cf84..666ff25d 100644 --- a/roles/web-app-pixelfed/config/main.yml +++ b/roles/web-app-pixelfed/config/main.yml @@ -5,7 +5,7 @@ features: port-ui-desktop: true central_database: true oidc: true - universal_logout: true + logout: true csp: flags: script-src: diff --git a/roles/web-app-port-ui/config/main.yml b/roles/web-app-port-ui/config/main.yml index c737c016..2f45684c 100644 --- a/roles/web-app-port-ui/config/main.yml +++ b/roles/web-app-port-ui/config/main.yml @@ -4,7 +4,7 @@ features: port-ui-desktop: false simpleicons: true # Activate Brand Icons for your groups javascript: true # Necessary for URL sync - universal_logout: false # Doesn't have own user data. Just a frame. + logout: false # Doesn't have own user data. Just a frame. csp: whitelist: script-src-elem: diff --git a/roles/web-app-pretix/config/main.yml b/roles/web-app-pretix/config/main.yml index 7a957e2a..4efbc043 100644 --- a/roles/web-app-pretix/config/main.yml +++ b/roles/web-app-pretix/config/main.yml @@ -17,7 +17,7 @@ features: recaptcha: false # Enable ReCaptcha oauth2: false # Enable the OAuth2-Proy javascript: false # Enables the custom JS in the javascript.js.j2 file - universal_logout: true + logout: true csp: whitelist: {} # URL's which should be whitelisted flags: {} # Flags which should be set diff --git a/roles/web-app-roulette-wheel/config/main.yml b/roles/web-app-roulette-wheel/config/main.yml index 55ac2467..820fda83 100644 --- a/roles/web-app-roulette-wheel/config/main.yml +++ b/roles/web-app-roulette-wheel/config/main.yml @@ -1,5 +1,5 @@ features: - universal_logout: false + logout: false domains: canonical: - "wheel.{{ primary_domain }}" diff --git a/roles/web-app-snipe-it/config/main.yml b/roles/web-app-snipe-it/config/main.yml index a6df9502..8354b782 100644 --- a/roles/web-app-snipe-it/config/main.yml +++ b/roles/web-app-snipe-it/config/main.yml @@ -5,7 +5,7 @@ features: central_database: true ldap: true oauth2: true - universal_logout: true + logout: true domains: canonical: - "inventory.{{ primary_domain }}" diff --git a/roles/web-app-sphinx/config/main.yml b/roles/web-app-sphinx/config/main.yml index 40a00533..fa1e95a2 100644 --- a/roles/web-app-sphinx/config/main.yml +++ b/roles/web-app-sphinx/config/main.yml @@ -2,7 +2,7 @@ features: matomo: true css: true port-ui-desktop: true - universal_logout: false + logout: false csp: flags: script-src: diff --git a/roles/web-app-syncope/config/main.yml b/roles/web-app-syncope/config/main.yml index 3e04b9b5..307be51f 100644 --- a/roles/web-app-syncope/config/main.yml +++ b/roles/web-app-syncope/config/main.yml @@ -1,6 +1,6 @@ features: - universal_logout: false # Role is not enabled until then keep it false + logout: false # Role is not enabled until then keep it false # syncope: # version: "latest" # credentials: diff --git a/roles/web-app-syncope/tasks/main.yml b/roles/web-app-syncope/tasks/main.yml index 679ef2fb..7032f201 100644 --- a/roles/web-app-syncope/tasks/main.yml +++ b/roles/web-app-syncope/tasks/main.yml @@ -11,13 +11,13 @@ template: src: "templates/proxy.conf.j2" dest: "{{nginx.directories.http.servers}}{{domain}}.conf" - notify: restart nginx + notify: restart openresty - name: configure {{domain}}.conf template: src: "templates/proxy.conf.j2" dest: "{{nginx.directories.http.servers}}{{domain}}.conf" - notify: restart nginx + notify: restart openresty - name: "create {{docker_compose.files.env}}" template: diff --git a/roles/web-app-syncope/templates/proxy.conf b/roles/web-app-syncope/templates/proxy.conf index 4dcc01a0..522ab428 100644 --- a/roles/web-app-syncope/templates/proxy.conf +++ b/roles/web-app-syncope/templates/proxy.conf @@ -6,7 +6,7 @@ server {% include 'roles/web-app-oauth2-proxy/templates/endpoint.conf.j2'%} {% endif %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% if proxy_extra_configuration is defined %} {# Additional Domain Specific Configuration #} diff --git a/roles/web-app-taiga/config/main.yml b/roles/web-app-taiga/config/main.yml index 6e7225c3..a170e170 100644 --- a/roles/web-app-taiga/config/main.yml +++ b/roles/web-app-taiga/config/main.yml @@ -11,7 +11,7 @@ features: port-ui-desktop: true oidc: false central_database: true - universal_logout: true + logout: true docker: services: database: diff --git a/roles/web-app-wordpress/config/main.yml b/roles/web-app-wordpress/config/main.yml index d8701fa1..09ba320a 100644 --- a/roles/web-app-wordpress/config/main.yml +++ b/roles/web-app-wordpress/config/main.yml @@ -12,7 +12,7 @@ features: port-ui-desktop: true oidc: true central_database: true - universal_logout: true + logout: true csp: flags: style-src: diff --git a/roles/web-app-xmpp/config/main.yml b/roles/web-app-xmpp/config/main.yml index a435f55b..cb4f0d8a 100644 --- a/roles/web-app-xmpp/config/main.yml +++ b/roles/web-app-xmpp/config/main.yml @@ -1,6 +1,6 @@ # xmpp is more a service then a app with ui interface. @todo Rename it features: - universal_logout: false # Reactivated as soon as xmpp is fully implemented + logout: false # Reactivated as soon as xmpp is fully implemented domains: canonical: diff --git a/roles/web-app-yourls/config/main.yml b/roles/web-app-yourls/config/main.yml index e5b0fe4f..533336ef 100644 --- a/roles/web-app-yourls/config/main.yml +++ b/roles/web-app-yourls/config/main.yml @@ -12,7 +12,7 @@ features: port-ui-desktop: true central_database: true oauth2: true - universal_logout: true + logout: true domains: canonical: - "s.{{ primary_domain }}" diff --git a/roles/web-opt-rdr-domains/tasks/redirect-domain.yml b/roles/web-opt-rdr-domains/tasks/redirect-domain.yml index a85cc519..6927477c 100644 --- a/roles/web-opt-rdr-domains/tasks/redirect-domain.yml +++ b/roles/web-opt-rdr-domains/tasks/redirect-domain.yml @@ -6,4 +6,4 @@ template: src: redirect.domain.nginx.conf.j2 dest: "{{ nginx.directories.http.servers }}{{ domain }}.conf" - notify: restart nginx \ No newline at end of file + notify: restart openresty \ No newline at end of file diff --git a/roles/web-svc-file/tasks/main.yml b/roles/web-svc-file/tasks/main.yml index af9ae5b9..bad23ba0 100644 --- a/roles/web-svc-file/tasks/main.yml +++ b/roles/web-svc-file/tasks/main.yml @@ -10,4 +10,4 @@ template: src: "nginx.conf.j2" dest: "{{ nginx.directories.http.servers }}{{ domains | get_domain(application_id) }}.conf" - notify: restart nginx \ No newline at end of file + notify: restart openresty \ No newline at end of file diff --git a/roles/web-svc-file/templates/nginx.conf.j2 b/roles/web-svc-file/templates/nginx.conf.j2 index 58a0e9e7..1dd6c11d 100644 --- a/roles/web-svc-file/templates/nginx.conf.j2 +++ b/roles/web-svc-file/templates/nginx.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %} charset utf-8; diff --git a/roles/web-svc-html/tasks/main.yml b/roles/web-svc-html/tasks/main.yml index a94824da..d34a66be 100644 --- a/roles/web-svc-html/tasks/main.yml +++ b/roles/web-svc-html/tasks/main.yml @@ -10,4 +10,4 @@ template: src: "nginx.conf.j2" dest: "{{nginx.directories.http.servers}}{{domains | get_domain(application_id)}}.conf" - notify: restart nginx + notify: restart openresty diff --git a/roles/web-svc-html/templates/nginx.conf.j2 b/roles/web-svc-html/templates/nginx.conf.j2 index bebc2e9b..311e8485 100644 --- a/roles/web-svc-html/templates/nginx.conf.j2 +++ b/roles/web-svc-html/templates/nginx.conf.j2 @@ -4,7 +4,7 @@ server {% include 'roles/srv-web-7-7-letsencrypt/templates/ssl_header.j2' %} - {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%} + {% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.lua.j2'%} {% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %} charset utf-8; diff --git a/roles/web-svc-logout/README.md b/roles/web-svc-logout/README.md index 260b02dc..223db706 100644 --- a/roles/web-svc-logout/README.md +++ b/roles/web-svc-logout/README.md @@ -18,7 +18,7 @@ It solves the common challenge of logging a user out from all connected apps wit ## Features -- Automatic discovery of logout domains from applications with the `features.universal_logout` flag enabled. +- Automatic discovery of logout domains from applications with the `features.logout` flag enabled. - Centralized logout proxy that clears cookies and sessions across all configured subdomains. - Status page with live feedback on logout progress for each domain. - Built-in support for Docker Compose deployment and integration with the CyMaIS ecosystem. diff --git a/roles/web-svc-logout/filter_plugins/domain_filters.py b/roles/web-svc-logout/filter_plugins/domain_filters.py index 5daecb46..f2e81763 100644 --- a/roles/web-svc-logout/filter_plugins/domain_filters.py +++ b/roles/web-svc-logout/filter_plugins/domain_filters.py @@ -6,7 +6,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', from module_utils.config_utils import get_app_conf class FilterModule(object): - """Ansible filter plugin for generating logout domains based on universal_logout feature.""" + """Ansible filter plugin for generating logout domains based on logout feature.""" def filters(self): return { @@ -15,7 +15,7 @@ class FilterModule(object): def logout_domains(self, applications, group_names): """ - Return a list of domains for applications where features.universal_logout is true. + Return a list of domains for applications where features.logout is true. :param applications: dict of application configs :param group_names: list of application IDs to consider @@ -27,7 +27,7 @@ class FilterModule(object): if app_id not in group_names: continue - if not get_app_conf(applications, app_id, 'features.universal_logout', False): + if not get_app_conf(applications, app_id, 'features.logout', False): continue # use canonical domains list if present diff --git a/tasks/stages/01_constructor.yml b/tasks/stages/01_constructor.yml index b35e5ec1..79d94fea 100644 --- a/tasks/stages/01_constructor.yml +++ b/tasks/stages/01_constructor.yml @@ -105,9 +105,10 @@ - name: "Load base roles" include_tasks: "./tasks/groups/{{ item }}-roles.yml" loop: - - drv # 1. Load driver roles - - gen # 2. Load generic roles - - net # 3. Load network roles - - svc-db # 4. Load database roles + - drv # 1. Load driver roles + - gen # 2. Load generic roles + - net # 3. Load network roles + - svc-db # 4. Load database roles + - svc-prx # 5. Load proxy roles loop_control: label: "{{ item }}-roles.yml" \ No newline at end of file diff --git a/templates/roles/web-app/config/main.yml.j2 b/templates/roles/web-app/config/main.yml.j2 index 3c036266..5846f7c1 100644 --- a/templates/roles/web-app/config/main.yml.j2 +++ b/templates/roles/web-app/config/main.yml.j2 @@ -24,7 +24,7 @@ features: recaptcha: false # Enable ReCaptcha oauth2: false # Enable the OAuth2-Proy javascript: false # Enable the custom JS in the javascript.js.j2 file - universal_logout: true # Enable the logout via the central logout mechanism (deleting all cookies) + logout: true # Enable the logout via the central logout mechanism (deleting all cookies) csp: whitelist: # URL's which should be whitelisted script-src-elem: [] diff --git a/tests/integration/test_docker_compose_templates.py b/tests/integration/test_docker_compose_templates.py index 6e08aa16..a789ad80 100644 --- a/tests/integration/test_docker_compose_templates.py +++ b/tests/integration/test_docker_compose_templates.py @@ -17,14 +17,16 @@ class TestDockerComposeTemplates(unittest.TestCase): ] BASE_INCLUDE = "{% include 'roles/docker-compose/templates/base.yml.j2' %}" - NET_INCLUDE = "{% include 'roles/docker-compose/templates/networks.yml.j2' %}" + NET_INCLUDE = "{% include 'roles/docker-compose/templates/networks.yml.j2' %}" + HOST_MODE = 'network_mode: "host"' def test_docker_compose_includes(self): """ Verifies for each found docker-compose.yml.j2: - 1. BASE_INCLUDE and NET_INCLUDE are present exactly once - 2. BASE_INCLUDE appears before NET_INCLUDE - 3. Only allowed lines appear before BASE_INCLUDE (invalid lines issue warnings) + 1. BASE_INCLUDE is present exactly once + 2. If no host‑mode is set, NET_INCLUDE must appear exactly once + 3. BASE_INCLUDE appears before NET_INCLUDE when both are required + 4. Only allowed lines appear before BASE_INCLUDE (invalid lines issue warnings) """ template_paths = sorted( self.PROJECT_ROOT.glob(self.TEMPLATE_PATTERN) @@ -36,33 +38,42 @@ class TestDockerComposeTemplates(unittest.TestCase): content = template_path.read_text(encoding='utf-8') lines = content.splitlines() - # Check each include occurs exactly once + # BASE_INCLUDE must always occur exactly once count_base = lines.count(self.BASE_INCLUDE) self.assertEqual( - count_base, - 1, + count_base, 1, f"{template_path}: '{self.BASE_INCLUDE}' occurs {count_base} times, expected once" ) + + # Determine if host‑mode is in use + host_mode = self.HOST_MODE in content + + # If not host‑mode, NET_INCLUDE must occur exactly once count_net = lines.count(self.NET_INCLUDE) - self.assertEqual( - count_net, - 1, - f"{template_path}: '{self.NET_INCLUDE}' occurs {count_net} times, expected once" - ) + if host_mode: + # No network include needed for host mode + self.assertEqual( + count_net, 0, + f"{template_path}: '{self.NET_INCLUDE}' should be omitted when using host networking" + ) + else: + # Must include networks.yml exactly once + self.assertEqual( + count_net, 1, + f"{template_path}: '{self.NET_INCLUDE}' occurs {count_net} times, expected once" + ) - # Find BASE_INCLUDE index - idx_base = lines.index(self.BASE_INCLUDE) - # Find NET_INCLUDE index - idx_net = lines.index(self.NET_INCLUDE) - - # Check order - self.assertLess( - idx_base, - idx_net, - f"{template_path}: '{self.BASE_INCLUDE}' must come before '{self.NET_INCLUDE}'" - ) + # If both includes are present, check order + if count_base and count_net: + idx_base = lines.index(self.BASE_INCLUDE) + idx_net = lines.index(self.NET_INCLUDE) + self.assertLess( + idx_base, idx_net, + f"{template_path}: '{self.BASE_INCLUDE}' must come before '{self.NET_INCLUDE}'" + ) # Warn on invalid lines before BASE_INCLUDE + idx_base = lines.index(self.BASE_INCLUDE) for i, line in enumerate(lines[:idx_base]): if not any(pat.match(line) for pat in self.ALLOWED_BEFORE_BASE): warnings.warn( diff --git a/tests/integration/test_universal_logout.py b/tests/integration/test_universal_logout.py index 90e3ff06..dbf47a53 100644 --- a/tests/integration/test_universal_logout.py +++ b/tests/integration/test_universal_logout.py @@ -5,7 +5,7 @@ import yaml class TestUniversalLogoutSetting(unittest.TestCase): ROLES_PATH = "roles/web-app-*/config/main.yml" - def test_universal_logout_defined(self): + def test_logout_defined(self): files = glob.glob(self.ROLES_PATH) self.assertGreater(len(files), 0, f"No role config files found under {self.ROLES_PATH}") @@ -23,16 +23,16 @@ class TestUniversalLogoutSetting(unittest.TestCase): if data is not None: features = data.get("features", {}) - if "universal_logout" not in features: + if "logout" not in features: errors.append( - f"Missing 'universal_logout' setting in features of '{file_path}'. " - "You must explicitly set 'universal_logout' to true or false for this app." + f"Missing 'logout' setting in features of '{file_path}'. " + "You must explicitly set 'logout' to true or false for this app." ) else: - val = features["universal_logout"] + val = features["logout"] if not isinstance(val, bool): errors.append( - f"The 'universal_logout' setting in '{file_path}' must be boolean true or false, " + f"The 'logout' setting in '{file_path}' must be boolean true or false, " f"but found: {val} (type {type(val).__name__})" ) diff --git a/tests/unit/roles/web-svc-logout/filter_plugins/test_domain_filters.py b/tests/unit/roles/web-svc-logout/filter_plugins/test_domain_filters.py index 1b41b8d9..595d387a 100644 --- a/tests/unit/roles/web-svc-logout/filter_plugins/test_domain_filters.py +++ b/tests/unit/roles/web-svc-logout/filter_plugins/test_domain_filters.py @@ -34,23 +34,23 @@ class TestLogoutDomainsFilter(unittest.TestCase): applications = { "app1": { "domains": {"canonical": "single.domain.com"}, - "features": {"universal_logout": True}, + "features": {"logout": True}, }, "app2": { "domains": {"canonical": ["list1.com", "list2.com"]}, - "features": {"universal_logout": True}, + "features": {"logout": True}, }, "app3": { "domains": {"canonical": {"k1": "dictA.com", "k2": "dictB.com"}}, - "features": {"universal_logout": True}, + "features": {"logout": True}, }, "app4": { "domains": {"canonical": "no-logout.com"}, - "features": {"universal_logout": False}, + "features": {"logout": False}, }, "other": { "domains": {"canonical": "ignored.com"}, - "features": {"universal_logout": True}, + "features": {"logout": True}, }, } group_names = ["app1", "app2", "app3", "app4"] @@ -68,7 +68,7 @@ class TestLogoutDomainsFilter(unittest.TestCase): applications = { "app1": { "domains": {}, # no 'canonical' key - "features": {"universal_logout": True}, + "features": {"logout": True}, } } group_names = ["app1"] @@ -78,7 +78,7 @@ class TestLogoutDomainsFilter(unittest.TestCase): applications = { "app1": { "domains": {"canonical": "domain.com"}, - "features": {"universal_logout": True}, + "features": {"logout": True}, } } group_names = [] @@ -88,7 +88,7 @@ class TestLogoutDomainsFilter(unittest.TestCase): applications = { "app1": { "domains": {"canonical": 123}, - "features": {"universal_logout": True}, + "features": {"logout": True}, } } group_names = ["app1"]