diff --git a/roles/cmp-db-docker/tasks/main.yml b/roles/cmp-db-docker/tasks/main.yml index 120cd9b1..8902e0b1 100644 --- a/roles/cmp-db-docker/tasks/main.yml +++ b/roles/cmp-db-docker/tasks/main.yml @@ -1,4 +1,4 @@ -# run_once_cmp_db_docker +# run_once_cmp_db_docker: disabled - name: "For '{{ application_id }}': Set database_application_id (Needed due to lazzy loading issue)" set_fact: @@ -14,6 +14,10 @@ include_role: name: docker-compose +- name: "For '{{ application_id }}': Load cmp-docker-oauth2" + include_role: + name: cmp-docker-oauth2 + - name: "For '{{ application_id }}': Load central RDBMS" include_role: name: cmp-rdbms \ No newline at end of file diff --git a/roles/cmp-docker-oauth2/README.md b/roles/cmp-docker-oauth2/README.md new file mode 100644 index 00000000..2486ece7 --- /dev/null +++ b/roles/cmp-docker-oauth2/README.md @@ -0,0 +1,21 @@ +# cmp-docker-oauth2 + +This Ansible role enhances a Docker Compose application by conditionally enabling OAuth2-based authentication. It ensures that the `docker-compose` role is always loaded, and if the application has OAuth2 support enabled via `features.oauth2`, it also configures the OAuth2 proxy. + +## Features + +- Loads the `docker-compose` role +- Conditionally configures OAuth2 reverse proxy via `web-app-oauth2-proxy` +- Supports OIDC providers like Keycloak +- Application-driven behavior via `features.oauth2` in the configuration + +## License + +CyMaIS NonCommercial License (CNCL) +See: [https://s.veen.world/cncl](https://s.veen.world/cncl) + +## Author + +Kevin Veen-Birkenbach +Consulting & Coaching Solutions +[https://www.veen.world](https://www.veen.world) \ No newline at end of file diff --git a/roles/cmp-docker-oauth2/meta/main.yml b/roles/cmp-docker-oauth2/meta/main.yml new file mode 100644 index 00000000..b210f8ba --- /dev/null +++ b/roles/cmp-docker-oauth2/meta/main.yml @@ -0,0 +1,25 @@ +--- +galaxy_info: + author: "Kevin Veen-Birkenbach" + description: > + Loads the docker-compose role and adds OAuth2 proxy support if enabled + in the application's configuration. This ensures authentication via a + centralized OIDC provider (e.g., Keycloak) with minimal configuration overhead. + license: "CyMaIS NonCommercial License (CNCL)" + license_url: "https://s.veen.world/cncl" + company: "Kevin Veen-Birkenbach Consulting & Coaching Solutions" + min_ansible_version: "2.9" + platforms: + - name: Docker + versions: + - "latest" + galaxy_tags: + - docker + - compose + - oauth2 + - oidc + - authentication + - proxy + repository: "https://s.veen.world/cymais" + issue_tracker_url: "https://s.veen.world/cymaisissues" + documentation: "https://s.veen.world/cymais" diff --git a/roles/cmp-docker-oauth2/tasks/main.yml b/roles/cmp-docker-oauth2/tasks/main.yml new file mode 100644 index 00000000..5ed999f9 --- /dev/null +++ b/roles/cmp-docker-oauth2/tasks/main.yml @@ -0,0 +1,14 @@ +# run_once_cmp_docker_oauth2: disabled + +- name: "For '{{ application_id }}': Load docker-compose" + include_role: + name: docker-compose + +- name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)" + set_fact: + oauth2_proxy_application_id: "{{ application_id }}" + when: applications | get_app_conf(application_id, 'features.oauth2', False) + +- name: "include the web-app-oauth2-proxy role {{domain}}" + include_tasks: "{{ playbook_dir }}/roles/web-app-oauth2-proxy/tasks/main.yml" + when: applications | get_app_conf(application_id, 'features.oauth2', False) \ No newline at end of file diff --git a/roles/cmp-docker-proxy/tasks/main.yml b/roles/cmp-docker-proxy/tasks/main.yml index 4e6c0940..ab4e38f7 100644 --- a/roles/cmp-docker-proxy/tasks/main.yml +++ b/roles/cmp-docker-proxy/tasks/main.yml @@ -8,7 +8,7 @@ domain: "{{ domains | get_domain(application_id) }}" http_port: "{{ ports.localhost.http[application_id] }}" -- name: "For '{{ application_id }}': include docker-compose role" - include_role: - name: docker-compose +- name: "For '{{ application_id }}': Load cmp-docker-oauth2" + include_role: + name: cmp-docker-oauth2 diff --git a/roles/docker-compose/templates/networks.yml.j2 b/roles/docker-compose/templates/networks.yml.j2 index f0bb8e36..8861a9e5 100644 --- a/roles/docker-compose/templates/networks.yml.j2 +++ b/roles/docker-compose/templates/networks.yml.j2 @@ -1,7 +1,9 @@ {# This template needs to be included in docker-compose.yml #} networks: -{% if applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined %} - +{% if + (applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined) or + application_id in ['svc-db-mariadb','svc-db-postgres'] +%} {{ applications | get_app_conf('svc-db-' ~ database_type, 'docker.network') }}: external: true {% endif %} @@ -12,7 +14,7 @@ networks: {{ applications | get_app_conf('svc-db-openldap', 'docker.network') }}: external: true {% endif %} -{% if application_id != 'svc-db-openldap' %} +{% if not application_id.startswith('svc-db-') %} default: {% if application_id in networks.local and diff --git a/roles/docker-compose/templates/volumes.yml.j2 b/roles/docker-compose/templates/volumes.yml.j2 index 50c07625..eac101a5 100644 --- a/roles/docker-compose/templates/volumes.yml.j2 +++ b/roles/docker-compose/templates/volumes.yml.j2 @@ -6,6 +6,6 @@ volumes: {% endif %} {% if applications | is_docker_service_enabled(application_id, 'redis') or applications | get_app_conf(application_id, 'features.oauth2', False) %} redis: - name: {{ application_id | get_entity_name }} + name: {{ application_id | get_entity_name }}_redis {% endif %} {{ "\n" }} \ No newline at end of file diff --git a/roles/docker-container/templates/networks.yml.j2 b/roles/docker-container/templates/networks.yml.j2 index f4fa2a42..4734bc79 100644 --- a/roles/docker-container/templates/networks.yml.j2 +++ b/roles/docker-container/templates/networks.yml.j2 @@ -1,6 +1,9 @@ {# This template needs to be included in docker-compose.yml containers #} networks: -{% if applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined %} +{% if + (applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined) or + application_id in ['svc-db-mariadb','svc-db-postgres'] +%} {{ applications | get_app_conf('svc-db-' ~ database_type, 'docker.network') }}: {% endif %} {% if applications | get_app_conf(application_id, 'features.ldap', False) and applications | get_app_conf('svc-db-openldap', 'network.docker') %} diff --git a/roles/srv-proxy-6-6-domain/tasks/main.yml b/roles/srv-proxy-6-6-domain/tasks/main.yml index 93d3e469..f7623b91 100644 --- a/roles/srv-proxy-6-6-domain/tasks/main.yml +++ b/roles/srv-proxy-6-6-domain/tasks/main.yml @@ -26,13 +26,4 @@ when: - not nginx_conf.changed - site_check.status is defined - - not site_check.status in [200,301,302] - -- name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)" - set_fact: - oauth2_proxy_application_id: "{{ application_id }}" - when: applications | get_app_conf(application_id, 'features.oauth2', False) - -- name: "include the web-app-oauth2-proxy role {{domain}}" - include_tasks: "{{ playbook_dir }}/roles/web-app-oauth2-proxy/tasks/main.yml" - when: applications | get_app_conf(application_id, 'features.oauth2', False) \ No newline at end of file + - not site_check.status in [200,301,302] \ No newline at end of file diff --git a/roles/svc-db-postgres/defaults/main.yml b/roles/svc-db-postgres/defaults/main.yml new file mode 100644 index 00000000..67ab90d2 --- /dev/null +++ b/roles/svc-db-postgres/defaults/main.yml @@ -0,0 +1 @@ +postgres_gis_enabled: false # Needed by mobilizon \ No newline at end of file diff --git a/roles/svc-db-postgres/tasks/init.yml b/roles/svc-db-postgres/tasks/init.yml index df734cfe..d60f2097 100644 --- a/roles/svc-db-postgres/tasks/init.yml +++ b/roles/svc-db-postgres/tasks/init.yml @@ -1,8 +1,8 @@ --- -- name: "Wait until Postgres is listening on port {{ database_port }}" +- name: "Wait until Postgres is listening on port {{ postgres_port }}" wait_for: - host: 127.0.0.1 - port: "{{ database_port }}" + host: "{{ postgres_local_host }}" + port: "{{ postgres_port }}" delay: 5 timeout: 300 state: started @@ -14,8 +14,8 @@ state: present login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" # 2) Create the database user (with password) - name: "Create database user: {{ database_username }}" @@ -26,8 +26,8 @@ state: present login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" # 3) Enable LOGIN for the role (removes NOLOGIN) - name: "Enable login for role {{ database_username }}" @@ -35,8 +35,8 @@ db: postgres login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" query: | ALTER ROLE "{{ database_username }}" WITH LOGIN; @@ -53,8 +53,8 @@ state: present login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" # 5) Grant ALL privileges at the database level - name: "Grant all privileges on database {{ database_name }} to {{ database_username }}" @@ -66,8 +66,8 @@ state: present login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" # 6) Grant USAGE/CREATE on schema and set default privileges - name: "Set comprehensive schema privileges for {{ database_username }}" @@ -75,8 +75,8 @@ db: "{{ database_name }}" login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" query: | GRANT USAGE ON SCHEMA public TO "{{ database_username }}"; GRANT CREATE ON SCHEMA public TO "{{ database_username }}"; @@ -91,10 +91,21 @@ state: present login_user: postgres login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" - login_host: 127.0.0.1 - login_port: "{{ database_port }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" loop: - postgis - pg_trgm - unaccent - when: database_gis_enabled is defined and database_gis_enabled + when: postgres_gis_enabled | bool + +# 8) Ensure pgvector (vector) extension is installed (for Discourse‑AI, pgvector, …) +- name: "Ensure pgvector (vector) extension is installed" + community.postgresql.postgresql_ext: + db: "{{ database_name }}" + ext: vector + state: present + login_user: postgres + login_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" + login_host: "{{ postgres_local_host }}" + login_port: "{{ postgres_port }}" diff --git a/roles/svc-db-postgres/tasks/main.yml b/roles/svc-db-postgres/tasks/main.yml index 95e8484f..2084450c 100644 --- a/roles/svc-db-postgres/tasks/main.yml +++ b/roles/svc-db-postgres/tasks/main.yml @@ -6,28 +6,9 @@ - subnet: "{{ postgres_subnet }}" when: run_once_svc_db_postgres is not defined -- name: Install PostgreSQL - docker_container: - name: "{{ postgres_name }}" - image: "{{ postgres_image }}:{{ postgres_version }}" - detach: yes - env: - POSTGRES_PASSWORD: "{{ postgres_password }}" - POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C" # Necessary for web-app-matrix - networks: - - name: "{{ postgres_network_name }}" - published_ports: - - "127.0.0.1:{{ postgres_port }}:5432" - volumes: - - "{{ postgres_volume }}:/var/lib/postgresql/data" - restart_policy: "{{ docker_restart_policy }}" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s - register: setup_postgres_container_result +- name: "include docker-compose role" + include_role: + name: docker-compose when: run_once_svc_db_postgres is not defined - name: Wait for Postgres inside the container @@ -37,8 +18,6 @@ retries: 30 delay: 5 when: - - setup_postgres_container_result is defined - - setup_postgres_container_result.changed - run_once_svc_db_postgres is not defined - name: install python-psycopg2 diff --git a/roles/svc-db-postgres/templates/Dockerfile.j2 b/roles/svc-db-postgres/templates/Dockerfile.j2 new file mode 100644 index 00000000..e7447403 --- /dev/null +++ b/roles/svc-db-postgres/templates/Dockerfile.j2 @@ -0,0 +1,22 @@ +FROM {{ postgres_image }}:{{ postgres_version }} + +{% if postgres_pg_vector_enabled %} +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + git \ + postgresql-server-dev-all \ + && git clone https://github.com/pgvector/pgvector.git /tmp/pgvector \ + && cd /tmp/pgvector \ + && make \ + && make install \ + && rm -rf /tmp/pgvector \ + && apt-get purge -y --auto-remove git build-essential \ + && rm -rf /var/lib/apt/lists/* +{% endif %} + +HEALTHCHECK --interval=10s \ + --timeout=5s \ + --start-period=15m \ + --retries=5 \ + CMD pg_isready -U postgres || exit 1 \ No newline at end of file diff --git a/roles/svc-db-postgres/templates/docker-compose.yml.j2 b/roles/svc-db-postgres/templates/docker-compose.yml.j2 new file mode 100644 index 00000000..331d8cbc --- /dev/null +++ b/roles/svc-db-postgres/templates/docker-compose.yml.j2 @@ -0,0 +1,22 @@ +{% include 'roles/docker-compose/templates/base.yml.j2' %} + + postgres: + container_name: "{{ postgres_name }}" + image: "{{ postgres_custom_image_name }}" + build: + context: . + dockerfile: Dockerfile +{% include 'roles/docker-container/templates/base.yml.j2' %} +{% if postgres_expose_local %} + ports: + - "{{ postgres_local_host }}:{{ postgres_port }}:5432" +{% endif %} + volumes: + - "data:/var/lib/postgresql/data" +{% include 'roles/docker-container/templates/networks.yml.j2' %} + +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} + data: + name: "{{ postgres_volume }}" + +{% include 'roles/docker-compose/templates/networks.yml.j2' %} \ No newline at end of file diff --git a/roles/svc-db-postgres/templates/env.j2 b/roles/svc-db-postgres/templates/env.j2 new file mode 100644 index 00000000..413533d7 --- /dev/null +++ b/roles/svc-db-postgres/templates/env.j2 @@ -0,0 +1,3 @@ +POSTGRES_PASSWORD="{{ postgres_password }}" +# Necessary for web-app-matrix +POSTGRES_INITDB_ARGS="--encoding=UTF8 --locale=C" \ No newline at end of file diff --git a/roles/svc-db-postgres/vars/main.yml b/roles/svc-db-postgres/vars/main.yml index 6cf1f6ab..a10f6e31 100644 --- a/roles/svc-db-postgres/vars/main.yml +++ b/roles/svc-db-postgres/vars/main.yml @@ -1,10 +1,20 @@ -application_id: svc-db-postgres -postgres_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) }}" -postgres_name: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.name', True) }}" -postgres_image: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.image', True) }}" -postgres_subnet: "{{ networks.local['svc-db-postgres'].subnet }}" -postgres_network_name: "{{ applications | get_app_conf(application_id, 'docker.network', True) }}" -postgres_version: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.version', True) }}" -postgres_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" -postgres_port: "{{ database_port | default(ports.localhost.database[ application_id ]) }}" -postgres_init: "{{ database_username is defined and database_password is defined and database_name is defined }}" \ No newline at end of file +# General +application_id: svc-db-postgres + +# Docker +docker_compose_flush_handlers: true + +## Postgres +postgres_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) }}" +postgres_name: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.name', True) }}" +postgres_image: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.image', True) }}" +postgres_subnet: "{{ networks.local['svc-db-postgres'].subnet }}" +postgres_network_name: "{{ applications | get_app_conf(application_id, 'docker.network', True) }}" +postgres_version: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.version', True) }}" +postgres_password: "{{ applications | get_app_conf(application_id, 'credentials.postgres_password', True) }}" +postgres_port: "{{ database_port | default(ports.localhost.database[ application_id ]) }}" +postgres_init: "{{ database_username is defined and database_password is defined and database_name is defined }}" +postgres_expose_local: True # Exposes the db to localhost, almost everytime neccessary +postgres_custom_image_name: "postgres_custom" +postgres_local_host: "127.0.0.1" +postgres_pg_vector_enabled: True # Required by discourse, propably in a later step it makes sense to define this as a configuration option in config/main.yml \ 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 fb91ce5f..519a8378 100644 --- a/roles/web-app-discourse/config/main.yml +++ b/roles/web-app-discourse/config/main.yml @@ -42,7 +42,7 @@ plugins: enabled: true discourse-cakeday: enabled: true - discourse-solved: - enabled: true - discourse-voting: - enabled: true +# discourse-solved: Seems like this plugin is now also part of the default setup +# enabled: true +# discourse-voting: +# enabled: true diff --git a/roles/web-app-lam/templates/docker-compose.yml.j2 b/roles/web-app-lam/templates/docker-compose.yml.j2 index 5d2b2a98..c3847d44 100644 --- a/roles/web-app-lam/templates/docker-compose.yml.j2 +++ b/roles/web-app-lam/templates/docker-compose.yml.j2 @@ -8,4 +8,5 @@ {% include 'roles/docker-container/templates/base.yml.j2' %} {% include 'roles/docker-container/templates/networks.yml.j2' %} +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} {% include 'roles/docker-compose/templates/networks.yml.j2' %} \ No newline at end of file diff --git a/roles/web-app-mailu/vars/main.yml b/roles/web-app-mailu/vars/main.yml index 91a033ef..f7d39da6 100644 --- a/roles/web-app-mailu/vars/main.yml +++ b/roles/web-app-mailu/vars/main.yml @@ -22,4 +22,3 @@ mailu_webmail_data: "mailu_webmail_data" mailu_filter: "mailu_filter" mailu_dkim: "mailu_dkim" mailu_dovecot_mail: "mailu_dovecot_mail" -mailu_redis: "mailu_redis" diff --git a/roles/web-app-mobilizon/vars/main.yml b/roles/web-app-mobilizon/vars/main.yml index e9b7e36f..3bf49724 100644 --- a/roles/web-app-mobilizon/vars/main.yml +++ b/roles/web-app-mobilizon/vars/main.yml @@ -1,7 +1,7 @@ application_id: web-app-mobilizon database_type: "postgres" -database_gis_enabled: true +postgres_gis_enabled: true container_port: 4000 mobilizon_host_conf_exs_file: "{{docker_compose.directories.config}}config.exs" diff --git a/roles/web-app-phpmyadmin/templates/docker-compose.yml.j2 b/roles/web-app-phpmyadmin/templates/docker-compose.yml.j2 index e51def5e..29f4f02e 100644 --- a/roles/web-app-phpmyadmin/templates/docker-compose.yml.j2 +++ b/roles/web-app-phpmyadmin/templates/docker-compose.yml.j2 @@ -11,4 +11,5 @@ {% include 'roles/docker-container/templates/networks.yml.j2' %} {% include 'roles/docker-container/templates/healthcheck/tcp.yml.j2' %} +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} {% include 'roles/docker-compose/templates/networks.yml.j2' %} \ No newline at end of file diff --git a/roles/web-app-yourls/templates/docker-compose.yml.j2 b/roles/web-app-yourls/templates/docker-compose.yml.j2 index ad116163..00041ba6 100644 --- a/roles/web-app-yourls/templates/docker-compose.yml.j2 +++ b/roles/web-app-yourls/templates/docker-compose.yml.j2 @@ -13,6 +13,6 @@ {% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %} {% include 'roles/docker-container/templates/networks.yml.j2' %} -{% include 'roles/docker-compose/templates/volumes-just-database.yml.j2' %} +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} {% include 'roles/docker-compose/templates/networks.yml.j2' %}