diff --git a/roles/web-app-confluence/README.md b/roles/web-app-confluence/README.md new file mode 100644 index 00000000..987a04bc --- /dev/null +++ b/roles/web-app-confluence/README.md @@ -0,0 +1,25 @@ +# Confluence + +## Description + +Confluence is Atlassian’s enterprise wiki and collaboration platform. This role deploys Confluence via Docker Compose, wires it to PostgreSQL, and integrates proxy awareness, optional OIDC SSO, health checks, and production-friendly defaults for Infinito.Nexus. + +## Overview + +The role builds a minimal custom image on top of the official Confluence image, prepares persistent volumes, and exposes the app behind your reverse proxy. Configuration is driven by variables (image, version, volumes, domains, OIDC). JVM heap sizing is auto-derived from host RAM with safe caps to avoid `Xms > Xmx`. + +## Features + +* **Fully Dockerized:** Compose stack with a dedicated data volume (`confluence_data`) and a slim overlay image for future add-ons. +* **Reverse-Proxy Ready:** Sets `ATL_PROXY_NAME/PORT/SCHEME/SECURE` so Confluence generates correct external URLs behind HTTPS. +* **OIDC SSO (Optional):** Pre-templated vars for issuer, client, scopes, JWKS; compatible with Atlassian DC SSO/OIDC marketplace apps. +* **Central Database:** PostgreSQL integration (local or central DB) with bootstrap credentials from role vars. +* **JVM Auto-Tuning:** `JVM_MINIMUM_MEMORY` / `JVM_MAXIMUM_MEMORY` computed from host memory with upper bounds. +* **Health Checks:** Curl-based container healthcheck for early failure detection. +* **CSP & Canonical Domains:** Hooks into platform CSP/SSL/domain management to keep policies strict and URLs stable. +* **Backup Friendly:** Data isolated under `/var/atlassian/application-data/confluence`. + +## Further Resources + +* Product page: [Atlassian Confluence](https://www.atlassian.com/software/confluence) +* Docker Hub (official image): [atlassian/confluence](https://hub.docker.com/r/atlassian/confluence) diff --git a/roles/web-app-confluence/config/main.yml b/roles/web-app-confluence/config/main.yml index 9bb4957c..439381b5 100644 --- a/roles/web-app-confluence/config/main.yml +++ b/roles/web-app-confluence/config/main.yml @@ -15,7 +15,8 @@ features: desktop: true central_database: true logout: true - oidc: true + oidc: false # Not enabled for demo version + ldap: false # Not enabled for demo version server: csp: whitelist: {} diff --git a/roles/web-app-confluence/templates/Dockerfile.j2 b/roles/web-app-confluence/templates/Dockerfile.j2 index e69de29b..d05160e7 100644 --- a/roles/web-app-confluence/templates/Dockerfile.j2 +++ b/roles/web-app-confluence/templates/Dockerfile.j2 @@ -0,0 +1,8 @@ +FROM "{{ CONFLUENCE_IMAGE }}:{{ CONFLUENCE_VERSION }}" + +# Optional: install OIDC SSO app (example path/name) +# COPY ./plugins/atlassian-sso-dc-latest.obr /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/ + +# Ensure proper permissions for app data +RUN mkdir -p /var/atlassian/application-data/confluence && \ + chown -R 2001:2001 /var/atlassian/application-data/confluence \ No newline at end of file diff --git a/roles/web-app-confluence/templates/docker-compose.yml.j2 b/roles/web-app-confluence/templates/docker-compose.yml.j2 index 735aa043..1efb6b5a 100644 --- a/roles/web-app-confluence/templates/docker-compose.yml.j2 +++ b/roles/web-app-confluence/templates/docker-compose.yml.j2 @@ -3,9 +3,7 @@ build: context: . dockerfile: Dockerfile - args: - CONFLUENCE_BASE_IMAGE: "{{ CONFLUENCE_IMAGE }}:{{ CONFLUENCE_VERSION }}" - image: "{{ CONFLUENCE_IMAGE }}:{{ CONFLUENCE_VERSION }}-oidc" + image: "{{ CONFLUENCE_CUSTOM_IMAGE }}" container_name: "{{ CONFLUENCE_CONTAINER }}" hostname: '{{ CONFLUENCE_HOSTNAME}}' ports: @@ -14,8 +12,7 @@ - 'data:/var/atlassian/application-data/confluence' {% include 'roles/docker-container/templates/healthcheck/curl.yml.j2' %} {% include 'roles/docker-container/templates/base.yml.j2' %} - depends_on: - - database +{% 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.yml.j2' %} diff --git a/roles/web-app-confluence/templates/env.j2 b/roles/web-app-confluence/templates/env.j2 index e15880ad..98a96737 100644 --- a/roles/web-app-confluence/templates/env.j2 +++ b/roles/web-app-confluence/templates/env.j2 @@ -1,6 +1,13 @@ ## Confluence core CONFLUENCE_URL="{{ CONFLUENCE_URL }}" +ATL_PROXY_NAME={{ CONFLUENCE_HOSTNAME }} +ATL_PROXY_PORT={{ WEB_PORT }} +ATL_TOMCAT_SCHEME={{ WEB_PROTOCOL }} +ATL_TOMCAT_SECURE={{ (WEB_PORT == 443) | lower }} +JVM_MINIMUM_MEMORY={{ CONFLUENCE_JVM_MIN }} +JVM_MAXIMUM_MEMORY={{ CONFLUENCE_JVM_MAX }} + ## Database CONFLUENCE_DATABASE_NAME="{{ database_name }}" CONFLUENCE_DATABASE_USER="{{ database_username }}" diff --git a/roles/web-app-confluence/vars/main.yml b/roles/web-app-confluence/vars/main.yml index fb85a5e1..da723495 100644 --- a/roles/web-app-confluence/vars/main.yml +++ b/roles/web-app-confluence/vars/main.yml @@ -1,27 +1,41 @@ -application_id: "web-app-confluence" -database_type: "postgres" -container_port: 8090 # Standardport Confluence +# General +application_id: "web-app-confluence" +database_type: "postgres" -# URLs -CONFLUENCE_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" -CONFLUENCE_HOSTNAME: "{{ domains | get_domain(application_id) }}" +# Container +container_port: 8090 +container_hostname: "{{ domains | get_domain(application_id) }}" -# OIDC -CONFLUENCE_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}" -CONFLUENCE_OIDC_LABEL: "{{ OIDC.BUTTON_TEXT }}" -CONFLUENCE_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}" -CONFLUENCE_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}" -CONFLUENCE_OIDC_ISSUER: "{{ OIDC.CLIENT.ISSUER_URL }}" -CONFLUENCE_OIDC_AUTH_URL: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" -CONFLUENCE_OIDC_TOKEN_URL: "{{ OIDC.CLIENT.TOKEN_URL }}" -CONFLUENCE_OIDC_USERINFO_URL: "{{ OIDC.CLIENT.USER_INFO_URL }}" -CONFLUENCE_OIDC_LOGOUT_URL: "{{ OIDC.CLIENT.LOGOUT_URL }}" -CONFLUENCE_OIDC_JWKS_URL: "{{ OIDC.CLIENT.CERTS }}" -CONFLUENCE_OIDC_SCOPES: "openid,email,profile" +# Confluence + +## URLs +CONFLUENCE_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" +CONFLUENCE_HOSTNAME: "{{ container_hostname }}" + +## OIDC +CONFLUENCE_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}" +CONFLUENCE_OIDC_LABEL: "{{ OIDC.BUTTON_TEXT }}" +CONFLUENCE_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}" +CONFLUENCE_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}" +CONFLUENCE_OIDC_ISSUER: "{{ OIDC.CLIENT.ISSUER_URL }}" +CONFLUENCE_OIDC_AUTH_URL: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" +CONFLUENCE_OIDC_TOKEN_URL: "{{ OIDC.CLIENT.TOKEN_URL }}" +CONFLUENCE_OIDC_USERINFO_URL: "{{ OIDC.CLIENT.USER_INFO_URL }}" +CONFLUENCE_OIDC_LOGOUT_URL: "{{ OIDC.CLIENT.LOGOUT_URL }}" +CONFLUENCE_OIDC_JWKS_URL: "{{ OIDC.CLIENT.CERTS }}" +CONFLUENCE_OIDC_SCOPES: "openid,email,profile" CONFLUENCE_OIDC_UNIQUE_ATTRIBUTE: "{{ OIDC.ATTRIBUTES.USERNAME }}" -# Docker -CONFLUENCE_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.application.version') }}" -CONFLUENCE_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.application.image') }}" -CONFLUENCE_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.application.name') }}" -CONFLUENCE_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}" \ No newline at end of file +## Docker +CONFLUENCE_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.application.version') }}" +CONFLUENCE_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.application.image') }}" +CONFLUENCE_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.application.name') }}" +CONFLUENCE_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}" +CONFLUENCE_CUSTOM_IMAGE: "{{ CONFLUENCE_IMAGE }}_custom" + +## Performance +CONFLUENCE_TOTAL_MB: "{{ ansible_memtotal_mb | int }}" +CONFLUENCE_JVM_MAX_MB: "{{ [ (CONFLUENCE_TOTAL_MB // 2), 12288 ] | min }}" +CONFLUENCE_JVM_MIN_MB: "{{ [ (CONFLUENCE_TOTAL_MB // 4), CONFLUENCE_JVM_MAX_MB ] | min }}" +CONFLUENCE_JVM_MIN: "{{ CONFLUENCE_JVM_MIN_MB }}m" +CONFLUENCE_JVM_MAX: "{{ CONFLUENCE_JVM_MAX_MB }}m" \ No newline at end of file diff --git a/roles/web-app-jira/README.md b/roles/web-app-jira/README.md new file mode 100644 index 00000000..79fc139c --- /dev/null +++ b/roles/web-app-jira/README.md @@ -0,0 +1,25 @@ +# Jira + +## Description + +Jira Software is Atlassian’s issue and project-tracking platform. This role deploys Jira via Docker Compose, connects it to PostgreSQL, and adds proxy awareness, optional OIDC SSO, health checks, and production-oriented defaults for Infinito.Nexus. + +## Overview + +The role builds a lean custom image on top of the official Jira Software image, provisions persistent volumes, and exposes the app behind your reverse proxy. Variables control image/version/volumes/domains/SSO. JVM heap sizing is auto-derived from host RAM with safe caps to prevent `Xms > Xmx`. + +## Features + +* **Fully Dockerized:** Compose stack with a dedicated data volume (`jira_data`) and a minimal overlay image to enable future plugins/config. +* **Reverse-Proxy/HTTPS Ready:** Preconfigured Atlassian Tomcat proxy envs so Jira respects external scheme/host/port. +* **OIDC SSO (Optional):** Pre-templated vars for issuer, client, endpoints, scopes; compatible with Atlassian DC SSO/OIDC marketplace apps. +* **Central Database:** PostgreSQL integration (local or central) with credentials sourced from role configuration. +* **JVM Auto-Tuning:** Safe calculation of `JVM_MINIMUM_MEMORY` / `JVM_MAXIMUM_MEMORY` with caps to avoid VM init errors. +* **Health Checks:** Container healthcheck for quicker failure detection and stable automation. +* **CSP & Canonical Domains:** Integrates with platform CSP and domain management. +* **Backup Ready:** Persistent data under `/var/atlassian/application-data/jira`. + +## Further Resources + +* Product page: [Atlassian Jira Software](https://www.atlassian.com/software/jira) +* Docker Hub (official image): [atlassian/jira-software](https://hub.docker.com/r/atlassian/jira-software) diff --git a/roles/web-app-jira/config/main.yml b/roles/web-app-jira/config/main.yml new file mode 100644 index 00000000..ed84d82e --- /dev/null +++ b/roles/web-app-jira/config/main.yml @@ -0,0 +1,29 @@ + +credentials: {} +docker: + services: + database: + enabled: true + application: + image: atlassian/jira-software + version: latest + name: jira + volumes: + data: "jira_data" +features: + matomo: true + css: true + desktop: true + central_database: true + logout: true + oidc: false # Not enabled for demo version + ldap: false # Not enabled for demo version +server: + csp: + whitelist: {} + flags: {} + domains: + canonical: + - "jira.{{ PRIMARY_DOMAIN }}" +rbac: + roles: {} diff --git a/roles/web-app-jira/meta/main.yml b/roles/web-app-jira/meta/main.yml new file mode 100644 index 00000000..a9fce9ec --- /dev/null +++ b/roles/web-app-jira/meta/main.yml @@ -0,0 +1,20 @@ +galaxy_info: + author: "Kevin Veen-Birkenbach" + description: "Jira Software is Atlassian’s issue & project tracking platform. This role deploys Jira in Docker, adds optional OIDC support, and integrates with the Infinito.Nexus ecosystem." + license: "Infinito.Nexus NonCommercial License" + license_url: "https://s.infinito.nexus/license" + company: | + Kevin Veen-Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + galaxy_tags: [] + repository: "https://s.infinito.nexus/code" + issue_tracker_url: "https://s.infinito.nexus/issues" + documentation: "https://s.infinito.nexus/code/" + logo: + class: "fas fa-diagram-project" + run_after: + - web-app-matomo + - web-app-keycloak + - web-app-mailu +dependencies: [] \ No newline at end of file diff --git a/roles/web-app-jira/schema/main.yml b/roles/web-app-jira/schema/main.yml new file mode 100644 index 00000000..e69de29b diff --git a/roles/web-app-jira/tasks/main.yml b/roles/web-app-jira/tasks/main.yml new file mode 100644 index 00000000..aadd805a --- /dev/null +++ b/roles/web-app-jira/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- block: + - name: "load docker, db and proxy for {{ application_id }}" + include_role: + name: sys-stk-full-stateful + - include_tasks: utils/run_once.yml + when: run_once_web_app_jira is not defined diff --git a/roles/web-app-jira/templates/Dockerfile.j2 b/roles/web-app-jira/templates/Dockerfile.j2 new file mode 100644 index 00000000..d6126699 --- /dev/null +++ b/roles/web-app-jira/templates/Dockerfile.j2 @@ -0,0 +1,8 @@ +FROM "{{ JIRA_IMAGE }}:{{ JIRA_VERSION }}" + +# Optional: install OIDC SSO app (example path/name) +# COPY ./plugins/atlassian-sso-dc-latest.obr /opt/atlassian/jira/atlassian-bundled-plugins/ + +# Ensure proper permissions for app data +RUN mkdir -p /var/atlassian/application-data/jira && \ + chown -R 2001:2001 /var/atlassian/application-data/jira diff --git a/roles/web-app-jira/templates/docker-compose.yml.j2 b/roles/web-app-jira/templates/docker-compose.yml.j2 new file mode 100644 index 00000000..a854cb56 --- /dev/null +++ b/roles/web-app-jira/templates/docker-compose.yml.j2 @@ -0,0 +1,23 @@ + +{% include 'roles/docker-compose/templates/base.yml.j2' %} + application: + build: + context: . + dockerfile: Dockerfile + image: "{{ JIRA_CUSTOM_IMAGE }}" + container_name: "{{ JIRA_CONTAINER }}" + hostname: '{{ JIRA_HOSTNAME }}' + ports: + - "127.0.0.1:{{ ports.localhost.http[application_id] }}:8080" + volumes: + - 'data:/var/atlassian/application-data/jira' +{% include 'roles/docker-container/templates/healthcheck/curl.yml.j2' %} +{% include 'roles/docker-container/templates/base.yml.j2' %} +{% 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.yml.j2' %} + data: + name: {{ JIRA_DATA_VOLUME }} + +{% include 'roles/docker-compose/templates/networks.yml.j2' %} \ No newline at end of file diff --git a/roles/web-app-jira/templates/env.j2 b/roles/web-app-jira/templates/env.j2 new file mode 100644 index 00000000..e6e4d006 --- /dev/null +++ b/roles/web-app-jira/templates/env.j2 @@ -0,0 +1,31 @@ +## Jira core +JIRA_URL="{{ JIRA_URL }}" + +## Database +JIRA_DATABASE_NAME="{{ database_name }}" +JIRA_DATABASE_USER="{{ database_username }}" +JIRA_DATABASE_PASSWORD="{{ database_password }}" +JIRA_DATABASE_HOST="{{ database_host }}" +JIRA_DATABASE_PORT="{{ database_port }}" + +ATL_PROXY_NAME={{ JIRA_HOSTNAME }} +ATL_PROXY_PORT={{ WEB_PORT }} +ATL_TOMCAT_SCHEME={{ WEB_PROTOCOL }} +ATL_TOMCAT_SECURE={{ (WEB_PORT == 443) | lower }} +JVM_MINIMUM_MEMORY={{ JIRA_JVM_MIN }} +JVM_MAXIMUM_MEMORY={{ JIRA_JVM_MAX }} + +## OIDC +{% if JIRA_OIDC_ENABLED %} +JIRA_OIDC_TITLE="{{ JIRA_OIDC_LABEL | replace('\"','\\\"') }}" +JIRA_OIDC_ISSUER="{{ JIRA_OIDC_ISSUER }}" +JIRA_OIDC_AUTHORIZATION_ENDPOINT="{{ JIRA_OIDC_AUTH_URL }}" +JIRA_OIDC_TOKEN_ENDPOINT="{{ JIRA_OIDC_TOKEN_URL }}" +JIRA_OIDC_USERINFO_ENDPOINT="{{ JIRA_OIDC_USERINFO_URL }}" +JIRA_OIDC_END_SESSION_ENDPOINT="{{ JIRA_OIDC_LOGOUT_URL }}" +JIRA_OIDC_JWKS_URI="{{ JIRA_OIDC_JWKS_URL }}" +JIRA_OIDC_CLIENT_ID="{{ JIRA_OIDC_CLIENT_ID }}" +JIRA_OIDC_CLIENT_SECRET="{{ JIRA_OIDC_CLIENT_SECRET }}" +JIRA_OIDC_SCOPES="{{ JIRA_OIDC_SCOPES }}" +JIRA_OIDC_UNIQUE_ATTRIBUTE="{{ JIRA_OIDC_UNIQUE_ATTRIBUTE }}" +{% endif %} diff --git a/roles/web-app-jira/vars/main.yml b/roles/web-app-jira/vars/main.yml new file mode 100644 index 00000000..5d63ab9d --- /dev/null +++ b/roles/web-app-jira/vars/main.yml @@ -0,0 +1,41 @@ +# General +application_id: "web-app-jira" +database_type: "postgres" + +# Container +container_port: 8080 # Standardport Jira +container_hostname: "{{ domains | get_domain(application_id) }}" + +# Jira + +## URLs +JIRA_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" +JIRA_HOSTNAME: "{{ container_hostname }}" + +## OIDC +JIRA_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}" +JIRA_OIDC_LABEL: "{{ OIDC.BUTTON_TEXT }}" +JIRA_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}" +JIRA_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}" +JIRA_OIDC_ISSUER: "{{ OIDC.CLIENT.ISSUER_URL }}" +JIRA_OIDC_AUTH_URL: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" +JIRA_OIDC_TOKEN_URL: "{{ OIDC.CLIENT.TOKEN_URL }}" +JIRA_OIDC_USERINFO_URL: "{{ OIDC.CLIENT.USER_INFO_URL }}" +JIRA_OIDC_LOGOUT_URL: "{{ OIDC.CLIENT.LOGOUT_URL }}" +JIRA_OIDC_JWKS_URL: "{{ OIDC.CLIENT.CERTS }}" +JIRA_OIDC_SCOPES: "openid,email,profile" +JIRA_OIDC_UNIQUE_ATTRIBUTE: "{{ OIDC.ATTRIBUTES.USERNAME }}" + +## Docker +JIRA_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.application.version') }}" +JIRA_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.application.image') }}" +JIRA_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.application.name') }}" +JIRA_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}" +JIRA_CUSTOM_IMAGE: "{{ JIRA_IMAGE }}_custom" + +## Performance (auto-derive from host memory) +JIRA_TOTAL_MB: "{{ ansible_memtotal_mb | int }}" +JIRA_JVM_MAX_MB: "{{ [ (JIRA_TOTAL_MB // 2), 12288 ] | min }}" +JIRA_JVM_MIN_MB: "{{ [ (JIRA_TOTAL_MB // 4), JIRA_JVM_MAX_MB ] | min }}" +JIRA_JVM_MIN: "{{ JIRA_JVM_MIN_MB }}m" +JIRA_JVM_MAX: "{{ JIRA_JVM_MAX_MB }}m" \ No newline at end of file