Compare commits

..

3 commits

Author SHA1 Message Date
422b196831
Chore: add admin user and seed staging
added creation of the admin user, the basic homeboard and all basic setup tasks.
Todo: Cleanup
2026-04-07 16:58:28 +02:00
d3bdb1fdec
chore: base config and deployment for role homarr 2026-01-23 15:47:15 +01:00
029b1a86d4
chore: add new boilerplate role for homarr 2026-01-23 15:47:15 +01:00
74 changed files with 110 additions and 2447 deletions

View file

@ -1,38 +0,0 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -1,32 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for 389ds
# Base directory configuration (inherited from base role or defined here)
docker_compose_base_dir: /etc/docker/compose
docker_volume_base_dir: /srv/data
# 389ds-specific configuration
ds389_service_name: 389ds
ds389_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ ds389_service_name }}"
ds389_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ ds389_service_name }}"
# 389ds service configuration
ds389_image: "docker.io/389ds/dirsrv:3.1"
ds389_suffix: "dc=example,dc=com"
ds389_root_dn: "cn=Directory Manager"
ds389_root_password: "changeme"
# Instance configuration
ds389_instance_name: "localhost"
ds389_hostname: "{{ ds389_service_name }}"
# Network configuration
ds389_backend_network: "backend"
ds389_ldap_port: 3389
ds389_ldaps_port: 3636
# Base OUs to create after container starts
ds389_base_ous:
- users
- groups

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# handlers file for 389ds

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.2
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -1,76 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for 389ds
- name: Create docker compose directory
file:
path: "{{ ds389_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create 389ds data directory
file:
path: "{{ ds389_docker_volume_dir }}/data"
state: directory
mode: '0755'
- name: Create 389ds config directory
file:
path: "{{ ds389_docker_volume_dir }}/config"
state: directory
mode: '0755'
- name: Create docker-compose file for 389ds
template:
src: docker-compose.yml.j2
dest: "{{ ds389_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
- name: Start 389ds container
community.docker.docker_compose_v2:
project_src: "{{ ds389_docker_compose_dir }}"
state: present
- name: Wait for LDAP to be ready
shell: >
docker compose -f {{ ds389_docker_compose_dir }}/docker-compose.yml
exec -T {{ ds389_service_name }} ldapsearch -H ldap://localhost:3389 -x
-D "{{ ds389_root_dn }}" -w "{{ ds389_root_password }}"
-b "" -s base "(objectClass=*)"
register: ds389_ldap_ready
retries: 30
delay: 2
until: ds389_ldap_ready.rc == 0
changed_when: false
no_log: true
- name: Ensure backend and suffix exist
shell: >
docker compose -f {{ ds389_docker_compose_dir }}/docker-compose.yml
exec -T {{ ds389_service_name }} dsconf localhost backend create
--suffix "{{ ds389_suffix }}" --be-name userroot --create-suffix
register: ds389_backend_result
failed_when:
- ds389_backend_result.rc != 0
- "'already exists' not in ds389_backend_result.stderr"
- "'suffix exists' not in ds389_backend_result.stderr"
changed_when: ds389_backend_result.rc == 0
- name: Template base OUs LDIF
template:
src: base-ous.ldif.j2
dest: "{{ ds389_docker_volume_dir }}/data/base-ous.ldif"
mode: '0644'
- name: Apply base OUs LDIF
shell: >
docker compose -f {{ ds389_docker_compose_dir }}/docker-compose.yml
exec -T {{ ds389_service_name }} ldapadd -H ldap://localhost:3389 -x
-D "{{ ds389_root_dn }}" -w "{{ ds389_root_password }}"
-f /data/base-ous.ldif
register: ds389_ldapadd_result
failed_when:
- ds389_ldapadd_result.rc != 0
- "'Already exists' not in ds389_ldapadd_result.stderr"
changed_when: "'Already exists' not in ds389_ldapadd_result.stderr"
no_log: true

View file

@ -1,7 +0,0 @@
{% for ou in ds389_base_ous %}
dn: ou={{ ou }},{{ ds389_suffix }}
changetype: add
objectClass: organizationalUnit
ou: {{ ou }}
{% endfor %}

View file

@ -1,19 +0,0 @@
services:
{{ ds389_service_name }}:
image: {{ ds389_image }}
hostname: {{ ds389_hostname }}
restart: unless-stopped
environment:
DS_SUFFIX_NAME: {{ ds389_suffix }}
DS_DM_PASSWORD: {{ ds389_root_password }}
ports:
- "{{ ds389_ldap_port }}:3389"
- "{{ ds389_ldaps_port }}:3636"
volumes:
- {{ ds389_docker_volume_dir }}/data:/data
- {{ ds389_docker_volume_dir }}/config:/etc/dirsrv/slapd-{{ ds389_instance_name }}
networks:
- {{ ds389_backend_network }}
networks:
{{ ds389_backend_network }}:

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -1,6 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- 389ds

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for 389ds

View file

@ -13,7 +13,7 @@ authentik_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ authentik_service_
# Authentik service configuration # Authentik service configuration
authentik_domain: "authentik.local.test" authentik_domain: "authentik.local.test"
authentik_image: "ghcr.io/goauthentik/server:2026.2.2" authentik_image: "ghcr.io/goauthentik/server:2025.12.0"
authentik_port: 9000 authentik_port: 9000
authentik_secret_key: "changeme-generate-a-random-string" authentik_secret_key: "changeme-generate-a-random-string"
@ -57,29 +57,11 @@ authentik_proxy_outposts: []
# authentik_host_browser: "https://authentik.local.test/" # authentik_host_browser: "https://authentik.local.test/"
# log_level: "info" # log_level: "info"
authentik_ldap_apps: []
# - slug: ldap
# name: LDAP
# base_dn: "dc=local,dc=test"
# search_mode: cached # cached | direct
# bind_mode: cached # cached | direct
# search_group: null # optional: group name whose members can search
# certificate: null # optional: certificate name for LDAPS
# uid_start_number: 2000
# gid_start_number: 4000
authentik_ldap_outpost: {}
# name: "ldap-outpost"
# token: "changeme" # known token for outpost authentication
# config:
# authentik_host: "https://authentik.local.test/"
# log_level: "info"
authentik_oidc_apps: [] authentik_oidc_apps: []
# - slug: grafana # - slug: grafana
# name: Grafana # name: Grafana
# client_id: "grafana" # client_id_env: GRAFANA_OIDC_CLIENT_ID
# client_secret: "changeme" # client_secret_env: GRAFANA_OIDC_CLIENT_SECRET
# redirect_uris: # redirect_uris:
# - url: "https://grafana.example.com/login/generic_oauth" # - url: "https://grafana.example.com/login/generic_oauth"
# matching_mode: strict # matching_mode: strict
@ -89,14 +71,21 @@ authentik_oidc_apps: []
# invalidation_slug: default-provider-invalidation-flow # invalidation_slug: default-provider-invalidation-flow
# scopes: [openid, email, profile, offline_access] # scopes: [openid, email, profile, offline_access]
authentik_blueprint_env: []
# GRAFANA_OIDC_CLIENT_ID: "grafana"
# GRAFANA_OIDC_CLIENT_SECRET: "{{ vault_grafana_oidc_secret }}"
# ENTRA_TENANT_ID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# ENTRA_CLIENT_ID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# ENTRA_CLIENT_SECRET: "{{ vault_entra_client_secret }}"
# Oauth sources # Oauth sources
authentik_entra_sources: [] authentik_entra_sources: []
# - slug: entra-id # - slug: entra-id
# name: "Login with Entra" # name: "Login with Entra"
# tenant_mode: single # single | common # tenant_mode: single # single | common
# tenant_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # tenant_id_env: ENTRA_TENANT_ID
# client_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # client_id_env: ENTRA_CLIENT_ID
# client_secret: "changeme" # client_secret_env: ENTRA_CLIENT_SECRET
# scopes: # scopes:
# - openid # - openid
# - profile # - profile
@ -116,19 +105,12 @@ authentik_login_user_fields:
- username - username
- email - email
# Groups to provision
authentik_groups: []
# - name: admins
# - name: editors
# is_superuser: false
# parent: null
# Local users to provision # Local users to provision
authentik_local_users: [] authentik_local_users: []
# - username: admin # - username: admin
# name: "Admin User" # name: "Admin User"
# email: "admin@example.com" # email: "admin@example.com"
# password: "changeme" # password_env: AUTHENTIK_ADMIN_PASSWORD # reference env var in authentik_blueprint_env
# is_active: true # is_active: true
# groups: # groups:
# - authentik Admins # - authentik Admins

View file

@ -9,17 +9,17 @@
register: existing_blueprints register: existing_blueprints
- name: Build list of expected blueprint files - name: Build list of expected blueprint files
vars:
_oidc: "{{ authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^', '50-oidc-') | map('regex_replace', '$', '.yaml') | list }}"
_ldap: "{{ authentik_ldap_apps | map(attribute='slug') | map('regex_replace', '^', '55-ldap-') | map('regex_replace', '$', '.yaml') | list }}"
_proxy: "{{ authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^', '60-proxy-') | map('regex_replace', '$', '.yaml') | list }}"
_outpost: "{{ authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^', '70-outpost-') | map('regex_replace', '$', '.yaml') | list }}"
_entra: "{{ authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^', '40-source-entra-') | map('regex_replace', '$', '.yaml') | list }}"
_ldap_out: "{{ ['75-outpost-ldap.yaml'] if authentik_ldap_outpost.name is defined else [] }}"
_users: "{{ ['10-local-users.yaml'] if (authentik_local_users | length > 0 or authentik_groups | length > 0) else [] }}"
_cleanup: "{{ ['00-cleanup.yaml'] if (authentik_removed_oidc_apps + authentik_removed_proxy_apps + authentik_removed_local_users) | length > 0 else [] }}"
set_fact: set_fact:
expected_blueprints: "{{ _oidc + _ldap + _proxy + _outpost + _entra + ['45-login-sources.yaml'] + _ldap_out + _users + _cleanup }}" expected_blueprints: >-
{{
(authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '50-oidc-\1.yaml') | list) +
(authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '60-proxy-\1.yaml') | list) +
(authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '70-outpost-\1.yaml') | list) +
(authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '40-source-entra-\1.yaml') | list) +
['45-login-sources.yaml'] +
((authentik_local_users | length > 0) | ternary(['10-local-users.yaml'], [])) +
(((authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0)) | ternary(['00-cleanup.yaml'], []))
}}
- name: Remove stale blueprint files - name: Remove stale blueprint files
file: file:
@ -36,14 +36,6 @@
loop: "{{ authentik_oidc_apps }}" loop: "{{ authentik_oidc_apps }}"
register: oidc_templates register: oidc_templates
- name: Render LDAP blueprints
ansible.builtin.template:
src: blueprints/blueprint-ldap-app.yaml.j2
dest: "{{ authentik_docker_volume_dir }}/blueprints/55-ldap-{{ item.slug }}.yaml"
mode: "0644"
loop: "{{ authentik_ldap_apps }}"
register: ldap_templates
- name: Render Proxy blueprints - name: Render Proxy blueprints
ansible.builtin.template: ansible.builtin.template:
src: blueprints/blueprint-proxy-app.yaml.j2 src: blueprints/blueprint-proxy-app.yaml.j2
@ -60,14 +52,6 @@
loop: "{{ authentik_proxy_outposts }}" loop: "{{ authentik_proxy_outposts }}"
register: outpost_bp register: outpost_bp
- name: Render LDAP outpost blueprint
ansible.builtin.template:
src: blueprints/outpost-ldap.yaml.j2
dest: "{{ authentik_docker_volume_dir }}/blueprints/75-outpost-ldap.yaml"
mode: "0644"
when: authentik_ldap_outpost.name is defined
register: ldap_outpost_bp
- name: Render Entra source blueprints - name: Render Entra source blueprints
ansible.builtin.template: ansible.builtin.template:
src: blueprints/blueprint-source-entra.yaml.j2 src: blueprints/blueprint-source-entra.yaml.j2
@ -88,7 +72,7 @@
src: blueprints/blueprint-local-users.yaml.j2 src: blueprints/blueprint-local-users.yaml.j2
dest: "{{ authentik_docker_volume_dir }}/blueprints/10-local-users.yaml" dest: "{{ authentik_docker_volume_dir }}/blueprints/10-local-users.yaml"
mode: "0644" mode: "0644"
when: authentik_local_users | length > 0 or authentik_groups | length > 0 when: authentik_local_users | length > 0
register: local_users_bp register: local_users_bp
- name: Render cleanup blueprint - name: Render cleanup blueprint
@ -104,10 +88,8 @@
blueprints_changed: >- blueprints_changed: >-
{{ {{
(oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0))
or (ldap_templates is defined and (ldap_templates.results | selectattr('changed') | list | length > 0))
or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0))
or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0))
or (ldap_outpost_bp.changed | default(false))
or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0))
or (login_bp is defined and login_bp.changed) or (login_bp is defined and login_bp.changed)
or (local_users_bp.changed | default(false)) or (local_users_bp.changed | default(false))

View file

@ -2,18 +2,44 @@
--- ---
# tasks file for authentik # tasks file for authentik
- name: Create authentik directories - name: Create docker compose directory
file: file:
path: "{{ item }}" path: "{{ authentik_docker_compose_dir }}"
state: directory state: directory
mode: '0755' mode: '0755'
loop:
- "{{ authentik_docker_compose_dir }}" - name: Create authentik data directory
- "{{ authentik_docker_volume_dir }}/data" file:
- "{{ authentik_docker_volume_dir }}/certs" path: "{{ authentik_docker_volume_dir }}/data"
- "{{ authentik_docker_volume_dir }}/templates" state: directory
- "{{ authentik_docker_volume_dir }}/postgresql" mode: '0755'
- "{{ authentik_docker_volume_dir }}/blueprints"
- name: Create authentik certs directory
file:
path: "{{ authentik_docker_volume_dir }}/certs"
state: directory
mode: '0755'
- name: Create authentik templates directory
file:
path: "{{ authentik_docker_volume_dir }}/templates"
state: directory
mode: '0755'
- name: Create postgres data directory
file:
path: "{{ authentik_docker_volume_dir }}/postgresql"
state: directory
mode: '0755'
- name: Create blueprints directory
file:
path: "{{ authentik_docker_volume_dir }}/blueprints"
state: directory
mode: '0755'
- name: Render blueprints
import_tasks: blueprints.yml
- name: Create docker-compose file for authentik - name: Create docker-compose file for authentik
template: template:
@ -25,46 +51,6 @@
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ authentik_docker_compose_dir }}" project_src: "{{ authentik_docker_compose_dir }}"
state: present state: present
recreate: "{{ blueprints_changed | ternary('always', 'auto') }}"
wait: true wait: true
wait_timeout: 300 wait_timeout: 300
- name: Render blueprints
import_tasks: blueprints.yml
- name: Render blueprint wait script
template:
src: wait-for-blueprints.py.j2
dest: "{{ authentik_docker_volume_dir }}/data/wait-for-blueprints.py"
mode: '0644'
- name: Wait for custom blueprints to be applied
community.docker.docker_compose_v2_exec:
project_src: "{{ authentik_docker_compose_dir }}"
service: server
command: ak shell -c "exec(open('/data/wait-for-blueprints.py').read())"
register: blueprint_wait_result
changed_when: "'changed' in blueprint_wait_result.stdout"
retries: 30
delay: 10
until: blueprint_wait_result.rc == 0
when: blueprints_changed
- name: Render LDAP outpost token script
template:
src: set-outpost-token.py.j2
dest: "{{ authentik_docker_volume_dir }}/data/set-outpost-token.py"
mode: '0644'
when: authentik_ldap_outpost.name is defined
register: ldap_token_script
- name: Set known token for LDAP outpost
community.docker.docker_compose_v2_exec:
project_src: "{{ authentik_docker_compose_dir }}"
service: server
command: ak shell -c "exec(open('/data/set-outpost-token.py').read())"
register: ldap_token_result
changed_when: "'changed' in ldap_token_result.stdout"
retries: 30
delay: 10
until: ldap_token_result.rc == 0
when: authentik_ldap_outpost.name is defined and (blueprints_changed or ldap_token_script.changed)

View file

@ -1,124 +0,0 @@
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: "ldap-{{ item.slug }}"
labels:
blueprints.goauthentik.io/instantiate: "true"
blueprints.goauthentik.io/description: "LDAP provider + application for {{ item.slug }}"
entries:
# Simple password-only flow for LDAP bind (no browser policies)
- model: authentik_stages_password.passwordstage
id: ldap-password-stage
identifiers:
name: ldap-bind-password
attrs:
name: ldap-bind-password
backends:
- authentik.core.auth.InbuiltBackend
- authentik.core.auth.TokenBackend
- authentik.sources.ldap.auth.LDAPBackend
- model: authentik_stages_identification.identificationstage
id: ldap-identification-stage
identifiers:
name: ldap-bind-identification
attrs:
name: ldap-bind-identification
user_fields:
- username
- email
password_stage: !KeyOf ldap-password-stage
- model: authentik_stages_user_login.userloginstage
id: ldap-login-stage
identifiers:
name: ldap-bind-login
attrs:
name: ldap-bind-login
- model: authentik_flows.flow
id: ldap-bind-flow
identifiers:
slug: ldap-bind
attrs:
name: LDAP Bind
slug: ldap-bind
title: LDAP Bind
designation: authentication
authentication: none
- model: authentik_flows.flowstagebinding
identifiers:
target: !KeyOf ldap-bind-flow
stage: !KeyOf ldap-identification-stage
order: 0
attrs:
target: !KeyOf ldap-bind-flow
stage: !KeyOf ldap-identification-stage
order: 0
- model: authentik_flows.flowstagebinding
identifiers:
target: !KeyOf ldap-bind-flow
stage: !KeyOf ldap-login-stage
order: 10
attrs:
target: !KeyOf ldap-bind-flow
stage: !KeyOf ldap-login-stage
order: 10
{% if item.search_group is defined and item.search_group %}
- model: authentik_rbac.role
id: ldap-search-role-{{ item.slug }}
identifiers:
name: ldap-search-{{ item.slug }}
attrs:
name: ldap-search-{{ item.slug }}
{% endif %}
- model: authentik_providers_ldap.ldapprovider
id: ldap-provider-{{ item.slug }}
identifiers:
name: {{ item.name }}
attrs:
name: {{ item.name }}
base_dn: "{{ item.base_dn }}"
authorization_flow: !KeyOf ldap-bind-flow
invalidation_flow: !Find [authentik_flows.flow, [slug, {{ item.invalidation_flow_slug | default('default-provider-invalidation-flow') }}]]
authentication_flow: !KeyOf ldap-bind-flow
search_mode: {{ item.search_mode | default('cached') }}
bind_mode: {{ item.bind_mode | default('direct') }}
{% if item.certificate is defined and item.certificate %}
certificate: !Find [authentik_crypto.certificatekeypair, [name, {{ item.certificate }}]]
{% endif %}
{% if item.uid_start_number is defined %}
uid_start_number: {{ item.uid_start_number }}
{% endif %}
{% if item.gid_start_number is defined %}
gid_start_number: {{ item.gid_start_number }}
{% endif %}
{% if item.search_group is defined and item.search_group %}
permissions:
- permission: authentik_providers_ldap.search_full_directory
role: !KeyOf ldap-search-role-{{ item.slug }}
{% endif %}
{% if item.search_group is defined and item.search_group %}
# Assign the LDAP search role to the search group
- model: authentik_core.group
identifiers:
name: {{ item.search_group }}
attrs:
roles:
- !KeyOf ldap-search-role-{{ item.slug }}
{% endif %}
- model: authentik_core.application
id: app-{{ item.slug }}
identifiers:
slug: {{ item.slug }}
attrs:
name: "{{ item.name | default(item.slug) }}"
slug: {{ item.slug }}
provider: !KeyOf ldap-provider-{{ item.slug }}

View file

@ -4,24 +4,9 @@ metadata:
name: "local-users" name: "local-users"
labels: labels:
blueprints.goauthentik.io/instantiate: "true" blueprints.goauthentik.io/instantiate: "true"
blueprints.goauthentik.io/description: "Local groups and user accounts" blueprints.goauthentik.io/description: "Local user accounts"
entries: entries:
{% for group in authentik_groups %}
- model: authentik_core.group
id: group-{{ group.name | regex_replace('[^a-zA-Z0-9]', '-') }}
identifiers:
name: {{ group.name }}
attrs:
name: {{ group.name }}
{% if group.is_superuser is defined %}
is_superuser: {{ group.is_superuser | lower }}
{% endif %}
{% if group.parent is defined and group.parent %}
parent: !Find [authentik_core.group, [name, {{ group.parent }}]]
{% endif %}
{% endfor %}
{% for user in authentik_local_users %} {% for user in authentik_local_users %}
- model: authentik_core.user - model: authentik_core.user
id: user-{{ user.username }} id: user-{{ user.username }}
@ -32,8 +17,8 @@ entries:
name: "{{ user.name | default(user.username) }}" name: "{{ user.name | default(user.username) }}"
email: "{{ user.email | default('') }}" email: "{{ user.email | default('') }}"
is_active: {{ user.is_active | default(true) | lower }} is_active: {{ user.is_active | default(true) | lower }}
{% if user.password is defined %} {% if user.password_env is defined %}
password: "{{ user.password }}" password: !Env {{ user.password_env }}
{% endif %} {% endif %}
{% if user.groups is defined and user.groups | length > 0 %} {% if user.groups is defined and user.groups | length > 0 %}
groups: groups:

View file

@ -13,11 +13,9 @@ entries:
name: {{ item.slug }} name: {{ item.slug }}
attrs: attrs:
name: {{ item.slug }} name: {{ item.slug }}
client_type: {{ item.client_type | default('confidential') }} client_type: confidential
client_id: "{{ item.client_id }}" client_id: !Env {{ item.client_id_env }}
{% if item.client_type | default('confidential') == 'confidential' %} client_secret: !Env {{ item.client_secret_env }}
client_secret: "{{ item.client_secret }}"
{% endif %}
redirect_uris: redirect_uris:
{% for ru in item.redirect_uris %} {% for ru in item.redirect_uris %}

View file

@ -15,12 +15,12 @@ entries:
name: "{{ item.name | default('Microsoft Entra ID') }}" name: "{{ item.name | default('Microsoft Entra ID') }}"
slug: {{ item.slug }} slug: {{ item.slug }}
# Authentik's OAuth sources support vendor-specific types. # Authentiks OAuth sources support vendor-specific types.
# Entra guide calls it "Entra ID OAuth Source". # Entra guide calls it “Entra ID OAuth Source”.
provider_type: entraid provider_type: entraid
consumer_key: "{{ item.client_id }}" consumer_key: !Env {{ item.client_id_env }}
consumer_secret: "{{ item.client_secret }}" consumer_secret: !Env {{ item.client_secret_env }}
scopes: scopes:
{% for s in (item.scopes | default(['openid','profile','email'])) %} {% for s in (item.scopes | default(['openid','profile','email'])) %}
@ -28,10 +28,10 @@ entries:
{% endfor %} {% endfor %}
{% if (item.tenant_mode | default('single')) == 'single' %} {% if (item.tenant_mode | default('single')) == 'single' %}
authorization_url: "https://login.microsoftonline.com/{{ item.tenant_id }}/oauth2/v2.0/authorize" authorization_url: !Format ["https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", !Env {{ item.tenant_id_env }}]
access_token_url: "https://login.microsoftonline.com/{{ item.tenant_id }}/oauth2/v2.0/token" access_token_url: !Format ["https://login.microsoftonline.com/%s/oauth2/v2.0/token", !Env {{ item.tenant_id_env }}]
profile_url: "https://graph.microsoft.com/v1.0/me" profile_url: "https://graph.microsoft.com/v1.0/me"
oidc_jwks_url: "https://login.microsoftonline.com/{{ item.tenant_id }}/discovery/v2.0/keys" oidc_jwks_url: !Format ["https://login.microsoftonline.com/%s/discovery/v2.0/keys", !Env {{ item.tenant_id_env }}]
{% else %} {% else %}
authorization_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" authorization_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
access_token_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token" access_token_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token"

View file

@ -1,27 +0,0 @@
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: "outpost-{{ authentik_ldap_outpost.name }}"
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
- model: authentik_outposts.outpost
identifiers:
name: "{{ authentik_ldap_outpost.name }}"
attrs:
name: "{{ authentik_ldap_outpost.name }}"
type: ldap
service_connection: null
providers:
{% for app in authentik_ldap_apps %}
- !Find [authentik_providers_ldap.ldapprovider, [name, {{ app.name }}]]
{% endfor %}
{% if authentik_ldap_outpost.config is defined %}
config:
{% for k, v in authentik_ldap_outpost.config.items() %}
{{ k }}: {{ v | tojson }}
{% endfor %}
{% endif %}

View file

@ -35,6 +35,11 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }}
AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }}
AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}"
{% if authentik_blueprint_env|length > 0 %}
{% for k, v in authentik_blueprint_env.items() %}
{{ k }}: "{{ v }}"
{% endfor %}
{% endif %}
volumes: volumes:
- {{ authentik_docker_volume_dir }}/blueprints:/blueprints/custom - {{ authentik_docker_volume_dir }}/blueprints:/blueprints/custom
- {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/data:/data
@ -70,6 +75,11 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }}
AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }}
AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}"
{% if authentik_blueprint_env|length > 0 %}
{% for k, v in authentik_blueprint_env.items() %}
{{ k }}: "{{ v }}"
{% endfor %}
{% endif %}
volumes: volumes:
- {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/data:/data
- {{ authentik_docker_volume_dir }}/certs:/certs - {{ authentik_docker_volume_dir }}/certs:/certs

View file

@ -1,10 +0,0 @@
from authentik.outposts.models import Outpost
from authentik.core.models import Token
o = Outpost.objects.get(name='{{ authentik_ldap_outpost.name }}')
t = Token.objects.get(identifier=o.token_identifier)
if t.key != '{{ authentik_ldap_outpost.token }}':
t.key = '{{ authentik_ldap_outpost.token }}'
t.save(update_fields=['key'])
print('changed')
else:
print('ok')

View file

@ -1,20 +0,0 @@
from authentik.blueprints.models import BlueprintInstance
from authentik.blueprints.v1.importer import Importer
failed = list(BlueprintInstance.objects.filter(enabled=True, path__startswith="custom/").exclude(status="successful").order_by("path"))
if not failed:
print("ok")
else:
for bp in failed:
content = bp.retrieve()
importer = Importer.from_string(content)
valid, _ = importer.validate()
if valid:
importer.apply()
bp.status = "successful"
bp.save()
still_failed = BlueprintInstance.objects.filter(enabled=True, path__startswith="custom/").exclude(status="successful")
if still_failed.exists():
names = ", ".join(bp.name for bp in still_failed)
raise Exception(f"Blueprints still failing: {names}")
print("changed")

View file

@ -1,38 +0,0 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -1,26 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for authentik_outpost_ldap
# Base directory configuration (inherited from base role or defined here)
docker_compose_base_dir: /etc/docker/compose
docker_volume_base_dir: /srv/data
# Service configuration
authentik_outpost_ldap_service_name: authentik-outpost-ldap
authentik_outpost_ldap_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ authentik_outpost_ldap_service_name }}"
# Container image (must match authentik server version)
authentik_outpost_ldap_image: "ghcr.io/goauthentik/ldap:2026.2.2"
# Connection to authentik server
authentik_outpost_ldap_host: "https://authentik.local.test"
authentik_outpost_ldap_token: "changeme"
authentik_outpost_ldap_insecure: "true"
# Dedicated network for LDAP clients (nextcloud, opencloud, etc.)
authentik_outpost_ldap_network: "ldap"
# Extra hosts for DNS resolution within the container
authentik_outpost_ldap_extra_hosts: []
# - "authentik.local.test:192.168.56.11"

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# handlers file for authentik_outpost_ldap

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.2
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -1,31 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for authentik_outpost_ldap
- name: Create LDAP network
community.docker.docker_network:
name: "{{ authentik_outpost_ldap_network }}"
state: present
- name: Create docker compose directory
file:
path: "{{ authentik_outpost_ldap_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create docker-compose file for authentik LDAP outpost
template:
src: docker-compose.yml.j2
dest: "{{ authentik_outpost_ldap_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
- name: Start authentik LDAP outpost container
community.docker.docker_compose_v2:
project_src: "{{ authentik_outpost_ldap_docker_compose_dir }}"
state: present
wait: true
wait_timeout: 120
retries: 3
delay: 15
register: result
until: result is not failed

View file

@ -1,27 +0,0 @@
services:
ldap:
image: {{ authentik_outpost_ldap_image }}
restart: unless-stopped
environment:
AUTHENTIK_HOST: {{ authentik_outpost_ldap_host }}
AUTHENTIK_TOKEN: {{ authentik_outpost_ldap_token }}
AUTHENTIK_INSECURE: "{{ authentik_outpost_ldap_insecure }}"
{% if authentik_outpost_ldap_extra_hosts | length > 0 %}
extra_hosts:
{% for host in authentik_outpost_ldap_extra_hosts %}
- "{{ host }}"
{% endfor %}
{% endif %}
networks:
- {{ authentik_outpost_ldap_network }}
{% if authentik_outpost_ldap_authentik_network is defined %}
- {{ authentik_outpost_ldap_authentik_network }}
{% endif %}
networks:
{{ authentik_outpost_ldap_network }}:
external: true
{% if authentik_outpost_ldap_authentik_network is defined %}
{{ authentik_outpost_ldap_authentik_network }}:
external: true
{% endif %}

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -1,6 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- authentik_outpost_ldap

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for authentik_outpost_ldap

View file

@ -1,38 +0,0 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for collabora
# Base directory configuration (inherited from base role or defined here)
docker_compose_base_dir: /etc/docker/compose
docker_volume_base_dir: /srv/data
# Collabora-specific configuration
collabora_service_name: collabora
collabora_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ collabora_service_name }}"
collabora_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ collabora_service_name }}"
# Service configuration
collabora_domain: "office.local.test"
collabora_image: "collabora/code:latest"
collabora_port: 9980
collabora_extra_hosts: []
# Traefik configuration
collabora_traefik_network: "proxy"
collabora_use_ssl: true
# SSL verification for WOPI callbacks (set to false for self-signed certs)
collabora_ssl_verification: true
# Allowed WOPI host domains (Nextcloud, OpenCloud WOPI server, etc.)
# These domains are allowed to send WOPI requests to Collabora.
# Each entry is used as a regex pattern (dots are auto-escaped).
collabora_allowed_domains:
- "nextcloud.local.test"
# Domains allowed to embed Collabora in an iframe (Nextcloud, OpenCloud, etc.)
collabora_frame_ancestors:
- "nextcloud.local.test"

View file

@ -1,8 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# handlers file for collabora
- name: restart collabora
community.docker.docker_compose_v2:
project_src: "{{ collabora_docker_compose_dir }}"
state: restarted

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.2
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -1,34 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for collabora
- name: Create docker compose directory
file:
path: "{{ collabora_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create collabora volume directory
file:
path: "{{ collabora_docker_volume_dir }}"
state: directory
mode: '0755'
- name: Create coolwsd configuration
template:
src: coolwsd.xml.j2
dest: "{{ collabora_docker_volume_dir }}/coolwsd.xml"
mode: '0644'
notify: restart collabora
- name: Create docker-compose file for collabora
template:
src: docker-compose.yml.j2
dest: "{{ collabora_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
notify: restart collabora
- name: Start collabora container
community.docker.docker_compose_v2:
project_src: "{{ collabora_docker_compose_dir }}"
state: present

View file

@ -1,340 +0,0 @@
<!-- Managed by Ansible - do not edit manually -->
<!-- Based on Collabora CODE default coolwsd.xml with alias_groups and frame_ancestors customized -->
<config>
<accessibility desc="Accessibility settings">
<enable type="bool" default="false">false</enable>
</accessibility>
<allowed_languages desc="List of supported languages on this instance." default="de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru">de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru</allowed_languages>
<languagetool desc="Remote API settings for spell and grammar checking">
<enabled type="bool" default="false">false</enabled>
<base_url type="string" default=""></base_url>
<user_name type="string" default=""></user_name>
<api_key type="string" default=""></api_key>
<ssl_verification type="string" default="true">true</ssl_verification>
<rest_protocol type="string" default=""></rest_protocol>
</languagetool>
<deepl desc="DeepL API settings">
<enabled type="bool" default="false">false</enabled>
<api_url type="string" default=""></api_url>
<auth_key type="string" default=""></auth_key>
</deepl>
<sys_template_path type="path" relative="true" default="systemplate"></sys_template_path>
<child_root_path type="path" relative="true" default="jails"></child_root_path>
<mount_jail_tree type="bool" default="true">true</mount_jail_tree>
<server_name type="string" default=""></server_name>
<file_server_root_path type="path" relative="true" default="browser/../"></file_server_root_path>
<hexify_embedded_urls type="bool" default="false">false</hexify_embedded_urls>
<experimental_features type="bool" default="true">true</experimental_features>
<memproportion type="double" default="80.0"></memproportion>
<num_prespawn_children type="uint" default="4">4</num_prespawn_children>
<fetch_update_check type="uint" default="10">10</fetch_update_check>
<allow_update_popup type="bool" default="true">true</allow_update_popup>
<per_document>
<max_concurrency type="uint" default="4">4</max_concurrency>
<batch_priority type="uint" default="5">5</batch_priority>
<bgsave_priority type="uint" default="5">5</bgsave_priority>
<bgsave_timeout_secs type="uint" default="120">120</bgsave_timeout_secs>
<redlining_as_comments type="bool" default="false">false</redlining_as_comments>
<pdf_resolution_dpi type="uint" default="96">96</pdf_resolution_dpi>
<idle_timeout_secs type="uint" default="3600">3600</idle_timeout_secs>
<idlesave_duration_secs type="uint" default="30">30</idlesave_duration_secs>
<autosave_duration_secs type="uint" default="300">300</autosave_duration_secs>
<background_autosave type="bool" default="true">true</background_autosave>
<background_manualsave type="bool" default="true">true</background_manualsave>
<always_save_on_exit type="bool" default="false">false</always_save_on_exit>
<limit_virt_mem_mb type="uint">0</limit_virt_mem_mb>
<limit_stack_mem_kb type="uint">8000</limit_stack_mem_kb>
<limit_file_size_mb type="uint">0</limit_file_size_mb>
<limit_num_open_files type="uint">0</limit_num_open_files>
<limit_load_secs type="uint" default="100">100</limit_load_secs>
<limit_store_failures type="uint" default="5">5</limit_store_failures>
<limit_convert_secs type="uint" default="100">100</limit_convert_secs>
<min_time_between_saves_ms type="uint" default="500">500</min_time_between_saves_ms>
<min_time_between_uploads_ms type="uint" default="5000">5000</min_time_between_uploads_ms>
<cleanup enable="true">
<cleanup_interval_ms type="uint" default="10000">10000</cleanup_interval_ms>
<bad_behavior_period_secs type="uint" default="60">60</bad_behavior_period_secs>
<idle_time_secs type="uint" default="300">300</idle_time_secs>
<limit_dirty_mem_mb type="uint" default="3072">3072</limit_dirty_mem_mb>
<limit_cpu_per type="uint" default="85">85</limit_cpu_per>
<lost_kit_grace_period_secs default="120">120</lost_kit_grace_period_secs>
</cleanup>
</per_document>
<per_view>
<out_of_focus_timeout_secs type="uint" default="300">300</out_of_focus_timeout_secs>
<idle_timeout_secs type="uint" default="900">900</idle_timeout_secs>
<custom_os_info type="string" default=""></custom_os_info>
<min_saved_message_timeout_secs type="uint" default="6">6</min_saved_message_timeout_secs>
</per_view>
<ver_suffix type="string" default=""></ver_suffix>
<logging>
<color type="bool">true</color>
<level type="string" default="warning">warning</level>
<level_startup type="string" default="trace">trace</level_startup>
<disabled_areas type="string" default="Socket,WebSocket,Admin,Pixel">Socket,WebSocket,Admin,Pixel</disabled_areas>
<most_verbose_level_settable_from_client type="string" default="notice">notice</most_verbose_level_settable_from_client>
<least_verbose_level_settable_from_client type="string" default="fatal">fatal</least_verbose_level_settable_from_client>
<protocol type="bool">false</protocol>
<lokit_sal_log type="string" default="-INFO-WARN">-INFO-WARN</lokit_sal_log>
<file enable="false">
<property name="path">/var/log/coolwsd.log</property>
<property name="rotation">never</property>
<property name="archive">timestamp</property>
<property name="compress">true</property>
<property name="purgeAge">10 days</property>
<property name="purgeCount">10</property>
<property name="rotateOnOpen">true</property>
<property name="flush">false</property>
</file>
<anonymize>
<anonymize_user_data type="bool" default="false">false</anonymize_user_data>
<anonymization_salt type="uint" default="82589933">82589933</anonymization_salt>
</anonymize>
<docstats type="bool" default="false">false</docstats>
<userstats type="bool" default="false">false</userstats>
<disable_server_audit type="bool" default="false">false</disable_server_audit>
</logging>
<canvas_slideshow_enabled type="bool" default="true">true</canvas_slideshow_enabled>
<logging_ui_cmd>
<merge type="bool" default="true">true</merge>
<merge_display_end_time type="bool" default="false">true</merge_display_end_time>
<file enable="false">
<property name="path">/var/log/coolwsd-ui-cmd.log</property>
<property name="purgeCount">10</property>
<property name="rotateOnOpen">true</property>
<property name="flush">false</property>
</file>
</logging_ui_cmd>
<trace_event enable="false">
<path type="string" default="/var/log/coolwsd.trace.json">/var/log/coolwsd.trace.json</path>
</trace_event>
<browser_logging default="false">false</browser_logging>
<trace enable="false">
<path compress="true" snapshot="false"></path>
<filter>
<message></message>
</filter>
<outgoing>
<record default="false">false</record>
</outgoing>
</trace>
<net>
<proto type="string" default="all">all</proto>
<listen type="string" default="any">any</listen>
<service_root type="path" default=""></service_root>
<post_allow allow="true">
<host>192\.168\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:192\.168\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>127\.0\.0\.1</host>
<host>::ffff:127\.0\.0\.1</host>
<host>::1</host>
<host>172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}</host>
</post_allow>
<lok_allow>
<host>192\.168\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:192\.168\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>127\.0\.0\.1</host>
<host>::ffff:127\.0\.0\.1</host>
<host>::1</host>
<host>172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>::ffff:10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}</host>
<host>localhost</host>
</lok_allow>
<content_security_policy></content_security_policy>
<frame_ancestors>{{ collabora_frame_ancestors | map('regex_replace', '^(.*)$', 'https://\\1') | join(' ') }}</frame_ancestors>
<connection_timeout_secs type="int" default="30">30</connection_timeout_secs>
<proxy_prefix type="bool" default="false">false</proxy_prefix>
</net>
<ssl>
<enable type="bool" default="true">true</enable>
<termination type="bool" default="false">false</termination>
<cert_file_path type="path" relative="false">/etc/coolwsd/cert.pem</cert_file_path>
<key_file_path type="path" relative="false">/etc/coolwsd/key.pem</key_file_path>
<ca_file_path type="path" relative="false">/etc/coolwsd/ca-chain.cert.pem</ca_file_path>
<ssl_verification type="string" default="false">false</ssl_verification>
<cipher_list default="ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"></cipher_list>
<hpkp enable="false" report_only="false">
<max_age enable="true" type="uint" default="1000">1000</max_age>
<report_uri enable="false" type="string"></report_uri>
<pins><pin></pin></pins>
</hpkp>
<sts>
<enabled type="bool" default="false">false</enabled>
<max_age type="int" default="31536000">31536000</max_age>
</sts>
</ssl>
<security>
<seccomp type="bool" default="true">true</seccomp>
<capabilities type="bool" default="true">true</capabilities>
<jwt_expiry_secs type="int" default="1800">1800</jwt_expiry_secs>
<enable_macros_execution type="bool" default="false">false</enable_macros_execution>
<macro_security_level type="int" default="1">1</macro_security_level>
<enable_websocket_urp type="bool" default="false">false</enable_websocket_urp>
<enable_metrics_unauthenticated type="bool" default="false">false</enable_metrics_unauthenticated>
<server_signature type="bool" default="false">false</server_signature>
</security>
<certificates>
<database_path type="string" default=""></database_path>
</certificates>
<watermark>
<opacity type="double" default="0.2">0.2</opacity>
<text type="string"></text>
</watermark>
<user_interface>
<mode type="string" default="default">default</mode>
<use_integration_theme type="bool" default="true">true</use_integration_theme>
<statusbar_save_indicator type="bool" default="true">true</statusbar_save_indicator>
</user_interface>
<storage>
<filesystem allow="false" />
<wopi allow="true">
<max_file_size type="uint">0</max_file_size>
<locking>
<refresh type="int" default="900">900</refresh>
</locking>
<alias_groups mode="groups">
{% for domain in collabora_allowed_domains %}
<group>
<host allow="true">https://{{ domain }}:443</host>
</group>
{% endfor %}
</alias_groups>
<is_legacy_server type="bool" default="false">false</is_legacy_server>
</wopi>
<ssl>
<as_scheme type="bool" default="true">true</as_scheme>
<enable type="bool"></enable>
<cert_file_path type="path" relative="false"></cert_file_path>
<key_file_path type="path" relative="false"></key_file_path>
<ca_file_path type="path" relative="false"></ca_file_path>
<cipher_list></cipher_list>
</ssl>
</storage>
<admin_console>
<enable type="bool" default="true">true</enable>
<enable_pam type="bool" default="false">false</enable_pam>
<username></username>
<password></password>
<logging>
<admin_login type="bool" default="true">true</admin_login>
<metrics_fetch type="bool" default="true">true</metrics_fetch>
<monitor_connect type="bool" default="true">true</monitor_connect>
<admin_action type="bool" default="true">true</admin_action>
</logging>
</admin_console>
<monitors></monitors>
<quarantine_files default="false" enable="false">
<limit_dir_size_mb default="250" type="uint">250</limit_dir_size_mb>
<max_versions_to_maintain default="5" type="uint">5</max_versions_to_maintain>
<path type="path" relative="false"></path>
<expiry_min type="int" default="3000">3000</expiry_min>
</quarantine_files>
<cache_files>
<path type="path" relative="false"></path>
<expiry_min type="int" default="3000">1000</expiry_min>
</cache_files>
<extra_export_formats>
<impress_swf type="bool" default="false">false</impress_swf>
<impress_bmp type="bool" default="false">false</impress_bmp>
<impress_gif type="bool" default="false">false</impress_gif>
<impress_png type="bool" default="false">false</impress_png>
<impress_svg type="bool" default="false">false</impress_svg>
<impress_tiff type="bool" default="false">false</impress_tiff>
</extra_export_formats>
<serverside_config>
<idle_timeout_secs type="uint" default="3600">3600</idle_timeout_secs>
</serverside_config>
<remote_config>
<remote_url type="string" default=""></remote_url>
</remote_config>
<stop_on_config_change type="bool" default="false">false</stop_on_config_change>
<remote_font_config>
<url type="string" default=""></url>
</remote_font_config>
<fonts_missing>
<handling type="string" default="log">log</handling>
</fonts_missing>
<indirection_endpoint>
<url type="string" default=""></url>
<migration_timeout_secs type="uint" default="180">180</migration_timeout_secs>
<geolocation_setup>
<enable type="bool" default="false">false</enable>
<timezone type="string"></timezone>
<allowed_websocket_origins></allowed_websocket_origins>
</geolocation_setup>
<server_name type="string" default=""></server_name>
</indirection_endpoint>
<home_mode>
<enable type="bool" default="false">false</enable>
</home_mode>
<zotero>
<enable type="bool" default="true">true</enable>
</zotero>
<help_url type="string" default="https://help.collaboraoffice.com/help.html?">https://help.collaboraoffice.com/help.html?</help_url>
<overwrite_mode>
<enable type="bool" default="false">false</enable>
</overwrite_mode>
<wasm>
<enable type="bool" default="false">false</enable>
<force type="bool" default="false">false</force>
</wasm>
<document_signing>
<enable type="bool" default="true">true</enable>
</document_signing>
</config>

View file

@ -1,34 +0,0 @@
services:
collabora:
image: {{ collabora_image }}
container_name: {{ collabora_service_name }}
restart: unless-stopped
environment:
extra_params: "--o:ssl.enable=false --o:ssl.termination=true --o:ssl.ssl_verification={{ collabora_ssl_verification | string | lower }}"
volumes:
- {{ collabora_docker_volume_dir }}/coolwsd.xml:/etc/coolwsd/coolwsd.xml:ro
cap_add:
- MKNOD
networks:
- {{ collabora_traefik_network }}
{% if collabora_extra_hosts is defined and collabora_extra_hosts | length > 0 %}
extra_hosts:
{% for host in collabora_extra_hosts %}
- "{{ host }}"
{% endfor %}
{% endif %}
labels:
- traefik.enable=true
- traefik.docker.network={{ collabora_traefik_network }}
- traefik.http.routers.{{ collabora_service_name }}.rule=Host(`{{ collabora_domain }}`)
- traefik.http.services.{{ collabora_service_name }}.loadbalancer.server.port={{ collabora_port }}
{% if collabora_use_ssl %}
- traefik.http.routers.{{ collabora_service_name }}.entrypoints=websecure
- traefik.http.routers.{{ collabora_service_name }}.tls=true
{% else %}
- traefik.http.routers.{{ collabora_service_name }}.entrypoints=web
{% endif %}
networks:
{{ collabora_traefik_network }}:
external: true

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -1,6 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- collabora

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for collabora

View file

@ -1,38 +0,0 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -1,20 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for drawio
# Base directory configuration (inherited from base role or defined here)
docker_compose_base_dir: /etc/docker/compose
# Drawio-specific configuration
drawio_service_name: drawio
drawio_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ drawio_service_name }}"
# Service configuration
drawio_domain: "drawio.local.test"
drawio_image: "jgraph/drawio:latest"
drawio_port: 8080
drawio_extra_hosts: []
# Traefik configuration
drawio_traefik_network: "proxy"
drawio_use_ssl: true

View file

@ -1,8 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# handlers file for drawio
- name: restart drawio
community.docker.docker_compose_v2:
project_src: "{{ drawio_docker_compose_dir }}"
state: restarted

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.2
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -1,21 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for drawio
- name: Create docker compose directory
file:
path: "{{ drawio_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create docker-compose file for drawio
template:
src: docker-compose.yml.j2
dest: "{{ drawio_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
notify: restart drawio
- name: Start drawio container
community.docker.docker_compose_v2:
project_src: "{{ drawio_docker_compose_dir }}"
state: present

View file

@ -1,28 +0,0 @@
services:
drawio:
image: {{ drawio_image }}
container_name: {{ drawio_service_name }}
restart: unless-stopped
networks:
- {{ drawio_traefik_network }}
{% if drawio_extra_hosts is defined and drawio_extra_hosts | length > 0 %}
extra_hosts:
{% for host in drawio_extra_hosts %}
- "{{ host }}"
{% endfor %}
{% endif %}
labels:
- traefik.enable=true
- traefik.docker.network={{ drawio_traefik_network }}
- traefik.http.routers.{{ drawio_service_name }}.rule=Host(`{{ drawio_domain }}`)
- traefik.http.services.{{ drawio_service_name }}.loadbalancer.server.port={{ drawio_port }}
{% if drawio_use_ssl %}
- traefik.http.routers.{{ drawio_service_name }}.entrypoints=websecure
- traefik.http.routers.{{ drawio_service_name }}.tls=true
{% else %}
- traefik.http.routers.{{ drawio_service_name }}.entrypoints=web
{% endif %}
networks:
{{ drawio_traefik_network }}:
external: true

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -1,6 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- drawio

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for drawio

View file

@ -33,97 +33,3 @@ keycloak_use_ssl: true
keycloak_log_level: "INFO" keycloak_log_level: "INFO"
keycloak_proxy_mode: "edge" keycloak_proxy_mode: "edge"
keycloak_gzip_enabled: false # Disable GZIP encoding to avoid MIME type issues keycloak_gzip_enabled: false # Disable GZIP encoding to avoid MIME type issues
# Extra CA certificates to trust (host paths to PEM files)
keycloak_truststore_certificates: []
# - /srv/data/389ds/data/ssca/ca.crt
# Extra /etc/hosts entries for the Keycloak container
keycloak_extra_hosts: []
# - "ldap:192.168.56.11"
# Provisioning configuration
keycloak_provisioning_enabled: false
# Realm configuration
keycloak_realm: "default"
keycloak_realm_display_name: "Default Realm"
# Auth URL for API access (used by provisioning tasks)
keycloak_auth_url: "{{ 'https' if keycloak_use_ssl else 'http' }}://{{ keycloak_domain }}"
# Groups to provision
keycloak_groups: []
# - name: admins
# - name: users
# Local users to provision
keycloak_local_users: []
# - username: admin
# first_name: "Admin"
# last_name: "User"
# email: "admin@example.com"
# password: "changeme"
# groups:
# - name: admins
# OIDC clients to provision
keycloak_oidc_clients: []
# - client_id: nextcloud
# name: "Nextcloud"
# client_secret: "changeme"
# redirect_uris:
# - "https://nextcloud.example.com/apps/user_oidc/code"
# default_client_scopes:
# - openid
# - email
# - profile
# Identity providers (e.g., Entra ID, Google)
keycloak_identity_providers: []
# - alias: entra-id
# display_name: "Login with Microsoft"
# provider_id: oidc
# config:
# clientId: "{{ entra_client_id }}"
# clientSecret: "{{ entra_client_secret }}"
# authorizationUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
# tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token"
# defaultScope: "openid profile email"
# Resources to remove from Keycloak (cleanup)
# Add names/aliases here when removing from the lists above
keycloak_removed_users: []
# - olduser
keycloak_removed_groups: []
# - oldgroup
keycloak_removed_clients: []
# - old-client
keycloak_removed_identity_providers: []
# - old-idp
# LDAP user federations
keycloak_user_federations: []
# - name: ldap-389ds
# provider_id: ldap
# config:
# editMode: WRITABLE
# syncRegistrations: "true"
# importEnabled: "true"
# vendor: rhds
# connectionUrl: "ldaps://ldap.example.com:636"
# usersDn: "ou=users,dc=example,dc=com"
# bindDn: "cn=Directory Manager"
# bindCredential: "changeme"
# usernameLDAPAttribute: uid
# rdnLDAPAttribute: uid
# uuidLDAPAttribute: nsuniqueid
# userObjectClasses: "inetOrgPerson, organizationalPerson"
# authType: simple
# useTruststoreSpi: never
keycloak_removed_user_federations: []
# - old-federation

View file

@ -13,8 +13,6 @@
path: "{{ keycloak_docker_volume_dir }}/data" path: "{{ keycloak_docker_volume_dir }}/data"
state: directory state: directory
mode: '0755' mode: '0755'
owner: "1000"
group: "1000"
- name: Create postgres data directory - name: Create postgres data directory
file: file:
@ -32,25 +30,3 @@
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ keycloak_docker_compose_dir }}" project_src: "{{ keycloak_docker_compose_dir }}"
state: present state: present
- name: Wait for Keycloak health endpoint
uri:
url: "{{ keycloak_auth_url }}/health/ready"
method: GET
status_code: 200
validate_certs: false
register: keycloak_health
until: keycloak_health.status == 200
retries: 30
delay: 10
delegate_to: localhost
become: false
when: keycloak_provisioning_enabled | bool
- name: Run Keycloak provisioning
ansible.builtin.include_tasks: provisioning.yml
args:
apply:
become: false
delegate_to: localhost
when: keycloak_provisioning_enabled | bool

View file

@ -1,190 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# Keycloak provisioning tasks
# Create realm (if not master)
- name: Create Keycloak realm
community.general.keycloak_realm:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
display_name: "{{ keycloak_realm_display_name }}"
enabled: true
state: present
validate_certs: false
no_log: true
when: keycloak_realm != "master"
# Cleanup: Remove deleted identity providers
- name: Remove deleted identity providers
community.general.keycloak_identity_provider:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
alias: "{{ item }}"
state: absent
validate_certs: false
loop: "{{ keycloak_removed_identity_providers }}"
no_log: true
# Cleanup: Remove deleted user federations
- name: Remove deleted user federations
community.general.keycloak_user_federation:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: "{{ item }}"
state: absent
validate_certs: false
loop: "{{ keycloak_removed_user_federations }}"
no_log: true
# Cleanup: Remove deleted clients
- name: Remove deleted clients
community.general.keycloak_client:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
client_id: "{{ item }}"
state: absent
validate_certs: false
loop: "{{ keycloak_removed_clients }}"
no_log: true
# Cleanup: Remove deleted users
- name: Remove deleted users
community.general.keycloak_user:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
username: "{{ item }}"
state: absent
validate_certs: false
loop: "{{ keycloak_removed_users }}"
no_log: true
# Cleanup: Remove deleted groups
- name: Remove deleted groups
community.general.keycloak_group:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: "{{ item }}"
state: absent
validate_certs: false
loop: "{{ keycloak_removed_groups }}"
no_log: true
# Create groups
- name: Create groups
community.general.keycloak_group:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: "{{ item.name }}"
state: present
validate_certs: false
loop: "{{ keycloak_groups }}"
no_log: true
# Create user federations (LDAP)
- name: Create user federations
community.general.keycloak_user_federation:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: "{{ item.name }}"
provider_id: "{{ item.provider_id }}"
provider_type: org.keycloak.storage.UserStorageProvider
config: "{{ item.config }}"
mappers: "{{ item.mappers | default(omit) }}"
bind_credential_update_mode: only_indirect
state: present
validate_certs: false
loop: "{{ keycloak_user_federations }}"
no_log: true
# Create local users
- name: Create local users
community.general.keycloak_user:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
username: "{{ item.username }}"
first_name: "{{ item.first_name | default(omit) }}"
last_name: "{{ item.last_name | default(omit) }}"
email: "{{ item.email | default(omit) }}"
enabled: "{{ item.enabled | default(true) }}"
email_verified: "{{ item.email_verified | default(true) }}"
credentials:
- type: password
value: "{{ item.password }}"
temporary: false
groups: "{{ item.groups | default([]) }}"
state: present
validate_certs: false
loop: "{{ keycloak_local_users }}"
no_log: true
# Create OIDC clients
- name: Create OIDC clients
community.general.keycloak_client:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
client_id: "{{ item.client_id }}"
name: "{{ item.name | default(item.client_id) }}"
enabled: true
client_authenticator_type: client-secret
secret: "{{ item.client_secret }}"
redirect_uris: "{{ item.redirect_uris | default([]) }}"
web_origins: "{{ item.web_origins | default(['+']) }}"
standard_flow_enabled: true
implicit_flow_enabled: false
direct_access_grants_enabled: "{{ item.direct_access_grants_enabled | default(false) }}"
protocol: openid-connect
default_client_scopes: "{{ item.default_client_scopes | default(['openid', 'email', 'profile']) }}"
protocol_mappers: "{{ item.protocol_mappers | default(omit) }}"
state: present
validate_certs: false
loop: "{{ keycloak_oidc_clients }}"
no_log: true
# Create identity providers
- name: Create identity providers
community.general.keycloak_identity_provider:
auth_keycloak_url: "{{ keycloak_auth_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
alias: "{{ item.alias }}"
display_name: "{{ item.display_name | default(item.alias) }}"
provider_id: "{{ item.provider_id }}"
enabled: "{{ item.enabled | default(true) }}"
trust_email: "{{ item.trust_email | default(true) }}"
first_broker_login_flow_alias: "{{ item.first_broker_login_flow_alias | default('first broker login') }}"
config: "{{ item.config }}"
state: present
validate_certs: false
loop: "{{ keycloak_identity_providers }}"
no_log: true

View file

@ -32,26 +32,13 @@ services:
KC_SPI_RESOURCE_ENCODING_GZIP_CACHE_DIR: /opt/keycloak/data/gzip-cache KC_SPI_RESOURCE_ENCODING_GZIP_CACHE_DIR: /opt/keycloak/data/gzip-cache
KC_PROXY: {{ keycloak_proxy_mode }} KC_PROXY: {{ keycloak_proxy_mode }}
KC_HOSTNAME: {{ keycloak_domain }} KC_HOSTNAME: {{ keycloak_domain }}
KC_HEALTH_ENABLED: "true"
{% if keycloak_truststore_certificates | length > 0 %}
KC_TRUSTSTORE_PATHS: "{{ keycloak_truststore_certificates | map('regex_replace', '^.*/(.*)$', '/opt/keycloak/certs/\\1') | join(',') }}"
{% endif %}
depends_on: depends_on:
- postgres - postgres
volumes: volumes:
- {{ keycloak_docker_volume_dir }}/data:/opt/keycloak/data - {{ keycloak_docker_volume_dir }}/data:/opt/keycloak/data
{% for cert in keycloak_truststore_certificates %}
- {{ cert }}:/opt/keycloak/certs/{{ cert | basename }}:ro
{% endfor %}
networks: networks:
- {{ keycloak_backend_network }} - {{ keycloak_backend_network }}
- {{ keycloak_traefik_network }} - {{ keycloak_traefik_network }}
{% if keycloak_extra_hosts | length > 0 %}
extra_hosts:
{% for host in keycloak_extra_hosts %}
- "{{ host }}"
{% endfor %}
{% endif %}
tmpfs: tmpfs:
- /opt/keycloak/data/tmp:size=1024m - /opt/keycloak/data/tmp:size=1024m
labels: labels:

View file

@ -14,8 +14,6 @@ nextcloud_image: "nextcloud:fpm"
nextcloud_redis_image: "redis:latest" nextcloud_redis_image: "redis:latest"
nextcloud_port: 80 nextcloud_port: 80
nextcloud_extra_hosts: [] nextcloud_extra_hosts: []
nextcloud_extra_networks: []
nextcloud_allow_local_remote_servers: false # Set to true to allow requests to local network (dev only)
nextcloud_postgres_image: "postgres:15" nextcloud_postgres_image: "postgres:15"
nextcloud_postgres_db: nextcloud nextcloud_postgres_db: nextcloud
@ -28,14 +26,10 @@ nextcloud_use_ssl: true
nextcloud_enable_collabora: true nextcloud_enable_collabora: true
nextcloud_collabora_domain: "office.local.test" nextcloud_collabora_domain: "office.local.test"
nextcloud_collabora_service_name: collabora
nextcloud_collabora_image: collabora/code:latest
nextcloud_collabora_disable_cert_verification: false nextcloud_collabora_disable_cert_verification: false
# Draw.io integration (set nextcloud_drawio_url to enable)
nextcloud_enable_drawio: false
nextcloud_drawio_url: ""
nextcloud_drawio_theme: "kennedy"
nextcloud_drawio_offline: "yes"
nextcloud_use_s3_storage: false nextcloud_use_s3_storage: false
nextcloud_s3_key: changeme nextcloud_s3_key: changeme
nextcloud_s3_secret: changeme nextcloud_s3_secret: changeme
@ -54,13 +48,6 @@ nextcloud_upload_limit_mb: 2048
nextcloud_scale_factor: 2 nextcloud_scale_factor: 2
# Trusted proxies (Docker internal networks)
nextcloud_trusted_proxies: "172.16.0.0/12"
# File locking and real-time push notifications
nextcloud_enable_notify_push: false
nextcloud_notify_push_image: "icewind1991/notify_push:1.3.1"
# Non-default apps to install and enable # Non-default apps to install and enable
nextcloud_apps_to_install: nextcloud_apps_to_install:
- groupfolders - groupfolders
@ -68,55 +55,4 @@ nextcloud_apps_to_install:
- spreed - spreed
- user_ldap - user_ldap
- user_oidc - user_oidc
- whiteboard - whiteboard
- files_lock
- notify_push
# OIDC provider configuration
nextcloud_oidc_allow_selfsigned: false # Set to true to disable SSL verification for OIDC providers (dev only)
nextcloud_oidc_providers: []
# - identifier: keycloak
# display_name: "Login with Keycloak"
# client_id: "nextcloud"
# client_secret: "changeme"
# discovery_url: "https://keycloak.example.com/realms/default/.well-known/openid-configuration"
# scope: "openid email profile"
# unique_uid: true
# check_bearer: false
# send_id_token_hint: true
# mapping:
# uid: preferred_username
# display_name: name
# email: email
# groups: groups
# OIDC providers to remove
nextcloud_oidc_providers_removed: []
# - old-provider
# LDAP configuration
nextcloud_ldap_enabled: false
nextcloud_ldap_config: {}
# Example for 389ds with Keycloak user federation:
# ldapHost: "ldaps://389ds"
# ldapPort: "3636"
# ldapAgentName: "cn=Directory Manager"
# ldapAgentPassword: "changeme"
# ldapBase: "dc=example,dc=com"
# ldapBaseUsers: "ou=users,dc=example,dc=com"
# ldapBaseGroups: "dc=example,dc=com"
# ldapTLS: "0"
# turnOffCertCheck: "0"
# ldapUserFilter: "(&(objectclass=inetOrgPerson)(uid=*))"
# ldapLoginFilter: "(&(objectclass=inetOrgPerson)(uid=%uid))"
# ldapUserDisplayName: "displayname"
# ldapEmailAttribute: "mail"
# ldapExpertUsernameAttr: "uid"
# ldapExpertUUIDUserAttr: "nsuniqueid"
# ldapBaseGroups: "ou=groups,dc=example,dc=com"
# ldapGroupFilter: "(&(objectClass=groupOfNames))"
# ldapGroupFilterObjectclass: "groupOfNames"
# ldapGroupDisplayName: "cn"
# ldapGroupMemberAssocAttr: "member"
# ldapAdminGroup: "admins"
# ldapConfigurationActive: "1"

View file

@ -14,9 +14,4 @@
- name: Set Collabora WOPI allowlist - name: Set Collabora WOPI allowlist
community.docker.docker_container_exec: community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ config:app:set richdocuments wopi_allowlist --value='' command: php /var/www/html/occ config:app:set richdocuments wopi_allowlist --value=''
- name: Activate richdocuments configuration (fetch discovery from Collabora)
community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ richdocuments:activate-config

View file

@ -1,19 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for configuring draw.io in Nextcloud
- name: Configure draw.io URL
community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ config:app:set drawio DrawioUrl --value={{ nextcloud_drawio_url }}
when: nextcloud_drawio_url | length > 0
- name: Configure draw.io theme
community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ config:app:set drawio DrawioTheme --value={{ nextcloud_drawio_theme }}
- name: Configure draw.io offline mode
community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ config:app:set drawio DrawioOffline --value={{ nextcloud_drawio_offline }}

View file

@ -1,41 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# LDAP configuration for Nextcloud user_ldap app
- name: Check if LDAP configuration exists
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
command: php /var/www/html/occ ldap:show-config
register: ldap_show_config
changed_when: false
- name: Create LDAP configuration
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
command: php /var/www/html/occ ldap:create-empty-config
when: "'s01' not in ldap_show_config.stdout"
- name: Configure LDAP settings
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
argv:
- php
- /var/www/html/occ
- ldap:set-config
- s01
- "{{ item.key }}"
- "{{ item.value | string }}"
loop: "{{ nextcloud_ldap_config | dict2items }}"
loop_control:
label: "{{ item.key }}"
no_log: true
- name: Test LDAP configuration
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
command: php /var/www/html/occ ldap:test-config s01
register: ldap_test_result
changed_when: false
failed_when:
- ldap_test_result.rc != 0
- "'succeeded' not in (ldap_test_result.stdout | default('') | lower)"

View file

@ -19,12 +19,6 @@
state: directory state: directory
mode: '0755' mode: '0755'
- name: Ensure extra networks exist
community.docker.docker_network:
name: "{{ item }}"
state: present
loop: "{{ nextcloud_extra_networks }}"
- name: Create docker-compose file for nextcloud - name: Create docker-compose file for nextcloud
template: template:
src: docker-compose.yml.j2 src: docker-compose.yml.j2
@ -61,33 +55,9 @@
- (nextcloud_ready.stdout | from_json).installed == true - (nextcloud_ready.stdout | from_json).installed == true
changed_when: false changed_when: false
- name: Deploy local network config file
ansible.builtin.template:
src: local-network.config.php.j2
dest: "{{ nextcloud_docker_volume_dir }}/nextcloud/config/local-network.config.php"
owner: www-data
group: www-data
mode: '0640'
- name: Install nextcloud plugins - name: Install nextcloud plugins
ansible.builtin.include_tasks: plugins.yml ansible.builtin.include_tasks: plugins.yml
- name: Configure nextcloud collabora - name: Configure nextcloud collabora
ansible.builtin.include_tasks: collabora.yml ansible.builtin.include_tasks: collabora.yml
when: nextcloud_enable_collabora when: nextcloud_enable_collabora
- name: Configure nextcloud draw.io
ansible.builtin.include_tasks: drawio.yml
when: nextcloud_enable_drawio
- name: Configure notify_push
ansible.builtin.include_tasks: notify_push.yml
when: nextcloud_enable_notify_push
- name: Configure LDAP backend
ansible.builtin.include_tasks: ldap.yml
when: nextcloud_ldap_enabled
- name: Configure OIDC providers
ansible.builtin.include_tasks: oidc.yml
when: nextcloud_oidc_providers | length > 0 or nextcloud_oidc_providers_removed | length > 0

View file

@ -1,8 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for configuring notify_push in Nextcloud
- name: Configure notify_push base endpoint
community.docker.docker_container_exec:
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
command: php /var/www/html/occ notify_push:setup https://{{ nextcloud_domain }}/push

View file

@ -1,53 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# OIDC provider configuration for Nextcloud user_oidc app
- name: Deploy OIDC config file
ansible.builtin.template:
src: oidc.config.php.j2
dest: "{{ nextcloud_docker_volume_dir }}/nextcloud/config/oidc.config.php"
owner: www-data
group: www-data
mode: '0640'
- name: Remove deleted OIDC providers
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
command: php /var/www/html/occ user_oidc:provider:delete "{{ item }}" --force
loop: "{{ nextcloud_oidc_providers_removed }}"
register: oidc_delete_result
changed_when: "'deleted' in (oidc_delete_result.stdout | default('') | lower)"
failed_when:
- oidc_delete_result.rc != 0
- "'not found' not in (oidc_delete_result.stderr | default('') | lower)"
- "'does not exist' not in (oidc_delete_result.stderr | default('') | lower)"
- name: Create or update OIDC providers
vars:
_mapping: "{{ item.mapping | default({}) }}"
_base_args:
- php
- /var/www/html/occ
- user_oidc:provider
- "{{ item.identifier }}"
- "--clientid={{ item.client_id }}"
- "--clientsecret={{ item.client_secret }}"
- "--discoveryuri={{ item.discovery_url }}"
- "--unique-uid={{ '1' if item.unique_uid | default(true) else '0' }}"
- "--check-bearer={{ '1' if item.check_bearer | default(false) else '0' }}"
- "--send-id-token-hint={{ '1' if item.send_id_token_hint | default(true) else '0' }}"
_optional_args: "{{
((['--scope=' ~ item.scope]) if item.scope is defined else []) +
((['--group-provisioning=1']) if item.group_provisioning | default(false) else []) +
((['--mapping-uid=' ~ _mapping.uid]) if _mapping.uid is defined else []) +
((['--mapping-display-name=' ~ _mapping.display_name]) if _mapping.display_name is defined else []) +
((['--mapping-email=' ~ _mapping.email]) if _mapping.email is defined else []) +
((['--mapping-groups=' ~ _mapping.groups]) if _mapping.groups is defined else [])
}}"
community.docker.docker_container_exec:
container: "{{ nextcloud_service_name }}-nextcloud-1"
argv: "{{ _base_args + _optional_args }}"
loop: "{{ nextcloud_oidc_providers }}"
register: oidc_create_result
changed_when: "'created' in (oidc_create_result.stdout | default('') | lower) or 'updated' in (oidc_create_result.stdout | default('') | lower)"
no_log: true

View file

@ -61,14 +61,11 @@ services:
PHP_UPLOAD_LIMIT: {{ nextcloud_upload_limit_mb }}M PHP_UPLOAD_LIMIT: {{ nextcloud_upload_limit_mb }}M
OVERWRITEPROTOCOL: https OVERWRITEPROTOCOL: https
OVERWRITEHOST: {{ nextcloud_domain }} OVERWRITEHOST: {{ nextcloud_domain }}
TRUSTED_PROXIES: "{{ nextcloud_trusted_proxies }}" TRUSTED_PROXIES: "172.18.0.0/16 172.16.9.88/16 172.16.17.0/24 172.16.9.88"
volumes: volumes:
- {{ nextcloud_docker_volume_dir }}/nextcloud/:/var/www/html - {{ nextcloud_docker_volume_dir }}/nextcloud/:/var/www/html
networks: networks:
- {{ nextcloud_backend_network }} - {{ nextcloud_backend_network }}
{% for net in nextcloud_extra_networks %}
- {{ net }}
{% endfor %}
nextcloud: nextcloud:
image: {{ nextcloud_image }} image: {{ nextcloud_image }}
@ -82,14 +79,14 @@ services:
POSTGRES_DB: {{ nextcloud_postgres_db }} POSTGRES_DB: {{ nextcloud_postgres_db }}
POSTGRES_USER: {{ nextcloud_postgres_user }} POSTGRES_USER: {{ nextcloud_postgres_user }}
POSTGRES_PASSWORD: {{ nextcloud_postgres_password }} POSTGRES_PASSWORD: {{ nextcloud_postgres_password }}
NEXTCLOUD_ADMIN_USER: {{ nextcloud_admin_user }} NEXTCLOUD_ADMIN_USER: {{ nextcloud_admin_user }}
NEXTCLOUD_ADMIN_PASSWORD: {{ nextcloud_admin_password }} NEXTCLOUD_ADMIN_PASSWORD: {{ nextcloud_admin_password }}
REDIS_HOST: redis REDIS_HOST: redis
PHP_MEMORY_LIMIT: {{ nextcloud_memory_limit_mb }}M PHP_MEMORY_LIMIT: {{ nextcloud_memory_limit_mb }}M
PHP_UPLOAD_LIMIT: {{ nextcloud_upload_limit_mb }}M PHP_UPLOAD_LIMIT: {{ nextcloud_upload_limit_mb }}M
OVERWRITEPROTOCOL: https OVERWRITEPROTOCOL: https
OVERWRITEHOST: {{ nextcloud_domain }} OVERWRITEHOST: {{ nextcloud_domain }}
TRUSTED_PROXIES: "{{ nextcloud_trusted_proxies }}" TRUSTED_PROXIES: "172.18.0.0/16 172.16.9.88/16 172.16.17.0/24 172.16.9.88"
{% if nextcloud_use_s3_storage %} {% if nextcloud_use_s3_storage %}
OBJECTSTORE_S3_KEY: {{ nextcloud_s3_key }} OBJECTSTORE_S3_KEY: {{ nextcloud_s3_key }}
OBJECTSTORE_S3_SECRET: {{ nextcloud_s3_secret }} OBJECTSTORE_S3_SECRET: {{ nextcloud_s3_secret }}
@ -105,9 +102,6 @@ services:
- {{ nextcloud_docker_volume_dir }}/nextcloud/:/var/www/html - {{ nextcloud_docker_volume_dir }}/nextcloud/:/var/www/html
networks: networks:
- {{ nextcloud_backend_network }} - {{ nextcloud_backend_network }}
{% for net in nextcloud_extra_networks %}
- {{ net }}
{% endfor %}
{% if nextcloud_extra_hosts is defined and nextcloud_extra_hosts | length > 0 %} {% if nextcloud_extra_hosts is defined and nextcloud_extra_hosts | length > 0 %}
extra_hosts: extra_hosts:
{% for host in nextcloud_extra_hosts %} {% for host in nextcloud_extra_hosts %}
@ -115,44 +109,35 @@ services:
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if nextcloud_enable_notify_push %} {% if nextcloud_enable_collabora %}
notify-push: collabora:
image: {{ nextcloud_notify_push_image }} image: {{ nextcloud_collabora_image }}
restart: always restart: always
depends_on:
- redis
- db
volumes:
- {{ nextcloud_docker_volume_dir }}/nextcloud/:/var/www/html
environment: environment:
PORT: "7867" domain: ^{{ nextcloud_domain | replace('.', '\\.') }}$
REDIS_URL: "redis://redis:6379" extra_params: >-
DATABASE_URL: "postgres://{{ nextcloud_postgres_user }}:{{ nextcloud_postgres_password }}@db:5432/{{ nextcloud_postgres_db }}" --o:ssl.enable=false
DATABASE_PREFIX: "oc_" --o:ssl.termination=true
NEXTCLOUD_URL: "http://nginx" --o:net.frame_ancestors=https://{{ nextcloud_domain }}
cap_add:
- MKNOD
networks: networks:
- {{ nextcloud_backend_network }}
- {{ nextcloud_traefik_network }} - {{ nextcloud_traefik_network }}
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.docker.network={{ nextcloud_traefik_network }} - traefik.docker.network={{ nextcloud_traefik_network }}
- traefik.http.routers.{{ nextcloud_service_name }}-push.rule=Host(`{{ nextcloud_domain }}`) && PathPrefix(`/push`) - traefik.http.routers.{{ nextcloud_collabora_service_name }}.rule=Host(`{{ nextcloud_collabora_domain }}`)
- traefik.http.services.{{ nextcloud_service_name }}-push.loadbalancer.server.port=7867 - traefik.http.services.{{ nextcloud_collabora_service_name }}.loadbalancer.server.port=9980
{% if nextcloud_use_ssl %} {% if nextcloud_use_ssl %}
- traefik.http.routers.{{ nextcloud_service_name }}-push.entrypoints=websecure - traefik.http.routers.{{ nextcloud_collabora_service_name }}.entrypoints=websecure
- traefik.http.routers.{{ nextcloud_service_name }}-push.tls=true - traefik.http.routers.{{ nextcloud_collabora_service_name }}.tls=true
{% else %} {% else %}
- traefik.http.routers.{{ nextcloud_service_name }}-push.entrypoints=web - traefik.http.routers.{{ nextcloud_collabora_service_name }}.entrypoints=web
{% endif %} {% endif %}
- traefik.http.middlewares.{{ nextcloud_service_name }}-push-https.headers.customrequestheaders.X-Forwarded-Proto=https
- traefik.http.routers.{{ nextcloud_service_name }}-push.middlewares={{ nextcloud_service_name }}-push-https
{% endif %} {% endif %}
networks: networks:
{{ nextcloud_backend_network }}: {{ nextcloud_backend_network }}:
{{ nextcloud_traefik_network }}: {{ nextcloud_traefik_network }}:
external: true external: true
{% for net in nextcloud_extra_networks %}
{{ net }}:
external: true
{% endfor %}

View file

@ -1,4 +0,0 @@
<?php
$CONFIG = array (
'allow_local_remote_servers' => {{ nextcloud_allow_local_remote_servers | lower }},
);

View file

@ -1,6 +0,0 @@
<?php
$CONFIG = array (
'user_oidc' => array (
'httpclient.allowselfsigned' => {{ nextcloud_oidc_allow_selfsigned | lower }},
),
);

View file

@ -1,38 +0,0 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -1,86 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# defaults file for opencloud
# Base directory configuration (inherited from base role or defined here)
docker_compose_base_dir: /etc/docker/compose
docker_volume_base_dir: /srv/data
# OpenCloud-specific configuration
opencloud_service_name: opencloud
opencloud_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ opencloud_service_name }}"
opencloud_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ opencloud_service_name }}"
# Service configuration
opencloud_domain: "opencloud.local.test"
opencloud_image: "opencloudeu/opencloud:latest"
opencloud_port: 9200
opencloud_admin_password: "admin"
opencloud_log_level: "warn"
opencloud_extra_hosts: []
opencloud_extra_networks: []
# Traefik configuration
opencloud_traefik_network: "proxy"
opencloud_use_ssl: true
# OIDC configuration (leave empty to use built-in IDP)
opencloud_oidc_issuer: ""
opencloud_oidc_client_id: "opencloud"
opencloud_oidc_client_secret: ""
opencloud_oidc_rewrite_wellknown: true
opencloud_oidc_user_claim: "preferred_username"
opencloud_oidc_user_cs3_claim: "username"
opencloud_oidc_account_edit_url: ""
opencloud_oidc_autoprovision_accounts: true
# S3 storage configuration (leave empty to use local storage)
opencloud_use_s3_storage: false
opencloud_s3_endpoint: ""
opencloud_s3_region: "us-east-1"
opencloud_s3_access_key: ""
opencloud_s3_secret_key: ""
opencloud_s3_bucket: "opencloud"
# Collabora integration (set opencloud_collabora_domain to enable)
opencloud_collabora_domain: ""
opencloud_wopi_domain: ""
opencloud_collabora_insecure: true
# LDAP configuration (set opencloud_ldap_uri to enable external LDAP)
opencloud_ldap_uri: ""
opencloud_ldap_insecure: true
opencloud_ldap_bind_dn: ""
opencloud_ldap_bind_password: ""
opencloud_ldap_user_base_dn: ""
opencloud_ldap_group_base_dn: ""
opencloud_ldap_user_schema_id: "nsuniqueid"
opencloud_ldap_user_schema_id_is_octet_string: true
opencloud_ldap_user_schema_username: "uid"
opencloud_ldap_user_schema_mail: "mail"
opencloud_ldap_user_schema_display_name: "displayName"
opencloud_ldap_group_schema_id: "nsuniqueid"
opencloud_ldap_group_schema_id_is_octet_string: true
opencloud_ldap_group_schema_groupname: "cn"
opencloud_ldap_group_schema_member: "member"
opencloud_ldap_write_enabled: false
# Role assignment via OIDC (set opencloud_role_assignment_driver to "oidc" to enable)
opencloud_role_assignment_driver: "default"
opencloud_role_assignment_oidc_claim: "groups"
opencloud_role_mapping: []
# Example mapping LDAP groups to OpenCloud roles:
# opencloud_role_mapping:
# - role_name: admin
# claim_value: admins
# - role_name: user
# claim_value: users
# Draw.io integration (set opencloud_drawio_url to enable)
opencloud_drawio_url: ""
opencloud_drawio_theme: "minimal"
opencloud_drawio_extension_image: "opencloudeu/web-extensions:draw-io-latest"
# CSP configuration (extra URLs to allow in connect-src and frame-src)
opencloud_csp_extra_connect_src: []
opencloud_csp_extra_frame_src: []

View file

@ -1,8 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# handlers file for opencloud
- name: restart opencloud
community.docker.docker_compose_v2:
project_src: "{{ opencloud_docker_compose_dir }}"
state: restarted

View file

@ -1,35 +0,0 @@
#SPDX-License-Identifier: MIT-0
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.2
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -1,88 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# tasks file for opencloud
- name: Create docker compose directory
file:
path: "{{ opencloud_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create opencloud data directory
file:
path: "{{ opencloud_docker_volume_dir }}/data"
state: directory
owner: "1000"
group: "1000"
mode: '0750'
- name: Create opencloud config directory
file:
path: "{{ opencloud_docker_volume_dir }}/config"
state: directory
owner: "1000"
group: "1000"
mode: '0750'
- name: Create CSP override file
template:
src: csp-override.yaml.j2
dest: "{{ opencloud_docker_volume_dir }}/config/csp-override.yaml"
owner: "1000"
group: "1000"
mode: '0644'
when: opencloud_csp_extra_connect_src | length > 0 or opencloud_csp_extra_frame_src | length > 0
notify: restart opencloud
- name: Create proxy role assignment config
template:
src: proxy.yaml.j2
dest: "{{ opencloud_docker_volume_dir }}/config/proxy.yaml"
owner: "1000"
group: "1000"
mode: '0644'
when: opencloud_role_assignment_driver == "oidc" and opencloud_role_mapping | length > 0
notify: restart opencloud
- name: Create draw.io extension apps directory
file:
path: "{{ opencloud_docker_volume_dir }}/data/web/assets/apps/draw-io"
state: directory
owner: "1000"
group: "1000"
mode: '0755'
when: opencloud_drawio_url | length > 0
- name: Create draw.io extension config
copy:
content: |
{
"config": {
"url": "{{ opencloud_drawio_url }}",
"theme": "{{ opencloud_drawio_theme }}"
}
}
dest: "{{ opencloud_docker_volume_dir }}/data/web/assets/apps/draw-io/config.json"
owner: "1000"
group: "1000"
mode: '0644'
when: opencloud_drawio_url | length > 0
notify: restart opencloud
- name: Ensure extra networks exist
community.docker.docker_network:
name: "{{ item }}"
state: present
loop: "{{ opencloud_extra_networks }}"
- name: Create docker-compose file for opencloud
template:
src: docker-compose.yml.j2
dest: "{{ opencloud_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
notify: restart opencloud
- name: Start opencloud container
community.docker.docker_compose_v2:
project_src: "{{ opencloud_docker_compose_dir }}"
state: present

View file

@ -1,20 +0,0 @@
directives:
connect-src:
- "'self'"
- "blob:"
- "https://raw.githubusercontent.com/opencloud-eu/awesome-apps/"
- "https://update.opencloud.eu/"
{% for url in opencloud_csp_extra_connect_src %}
- "{{ url }}"
{% endfor %}
{% if opencloud_csp_extra_frame_src | length > 0 %}
frame-src:
- "'self'"
{% for url in opencloud_csp_extra_frame_src %}
- "{{ url }}"
{% endfor %}
{% endif %}
script-src:
- "'self'"
- "'unsafe-inline'"
- "'unsafe-eval'"

View file

@ -1,145 +0,0 @@
services:
{% if opencloud_drawio_url %}
drawio-ext:
image: {{ opencloud_drawio_extension_image }}
entrypoint: /bin/sh
command: ["-c", "cp -R /usr/share/nginx/html/apps/draw-io/ /apps/"]
volumes:
- {{ opencloud_docker_volume_dir }}/data/web/assets/apps:/apps
{% endif %}
opencloud:
image: {{ opencloud_image }}
container_name: {{ opencloud_service_name }}
restart: unless-stopped
{% if opencloud_drawio_url %}
depends_on:
drawio-ext:
condition: service_completed_successfully
{% endif %}
entrypoint:
- /bin/sh
command: ["-c", "opencloud init || true; opencloud server"]
volumes:
- {{ opencloud_docker_volume_dir }}/config:/etc/opencloud
- {{ opencloud_docker_volume_dir }}/data:/var/lib/opencloud
environment:
{% if opencloud_use_ssl %}
OC_URL: "https://{{ opencloud_domain }}"
{% else %}
OC_URL: "http://{{ opencloud_domain }}"
{% endif %}
OC_INSECURE: "true"
OC_LOG_LEVEL: "{{ opencloud_log_level }}"
PROXY_TLS: "false"
{% if opencloud_csp_extra_connect_src | length > 0 or opencloud_csp_extra_frame_src | length > 0 %}
PROXY_CSP_CONFIG_FILE_OVERRIDE_LOCATION: "/etc/opencloud/csp-override.yaml"
{% endif %}
IDM_ADMIN_PASSWORD: "{{ opencloud_admin_password }}"
{% if opencloud_role_assignment_driver == "oidc" %}
PROXY_ROLE_ASSIGNMENT_DRIVER: "oidc"
PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM: "{{ opencloud_role_assignment_oidc_claim }}"
GRAPH_ASSIGN_DEFAULT_USER_ROLE: "false"
SETTINGS_SETUP_DEFAULT_ASSIGNMENTS: "false"
{% endif %}
{% if opencloud_oidc_issuer %}
OC_OIDC_ISSUER: "{{ opencloud_oidc_issuer }}"
OC_OIDC_CLIENT_ID: "{{ opencloud_oidc_client_id }}"
{% if opencloud_oidc_client_secret %}
OC_OIDC_CLIENT_SECRET: "{{ opencloud_oidc_client_secret }}"
{% endif %}
PROXY_OIDC_REWRITE_WELLKNOWN: "{{ opencloud_oidc_rewrite_wellknown | string | lower }}"
PROXY_USER_OIDC_CLAIM: "{{ opencloud_oidc_user_claim }}"
PROXY_USER_CS3_CLAIM: "{{ opencloud_oidc_user_cs3_claim }}"
PROXY_AUTOPROVISION_ACCOUNTS: "{{ opencloud_oidc_autoprovision_accounts | string | lower }}"
{% if opencloud_oidc_account_edit_url %}
WEB_OPTION_ACCOUNT_EDIT_LINK_HREF: "{{ opencloud_oidc_account_edit_url }}"
{% endif %}
{% endif %}
{% if opencloud_use_s3_storage %}
STORAGE_USERS_DRIVER: "decomposeds3"
STORAGE_USERS_DECOMPOSEDS3_ENDPOINT: "{{ opencloud_s3_endpoint }}"
STORAGE_USERS_DECOMPOSEDS3_REGION: "{{ opencloud_s3_region }}"
STORAGE_USERS_DECOMPOSEDS3_ACCESS_KEY: "{{ opencloud_s3_access_key }}"
STORAGE_USERS_DECOMPOSEDS3_SECRET_KEY: "{{ opencloud_s3_secret_key }}"
STORAGE_USERS_DECOMPOSEDS3_BUCKET: "{{ opencloud_s3_bucket }}"
{% endif %}
{% if opencloud_ldap_uri %}
# Disable built-in IDM when using external LDAP
OC_EXCLUDE_RUN_SERVICES: "idm"
IDM_CREATE_DEMO_USERS: "false"
# LDAP connection
OC_LDAP_URI: "{{ opencloud_ldap_uri }}"
OC_LDAP_INSECURE: "{{ opencloud_ldap_insecure | string | lower }}"
OC_LDAP_BIND_DN: "{{ opencloud_ldap_bind_dn }}"
OC_LDAP_BIND_PASSWORD: "{{ opencloud_ldap_bind_password }}"
# LDAP user/group base
OC_LDAP_USER_BASE_DN: "{{ opencloud_ldap_user_base_dn }}"
OC_LDAP_GROUP_BASE_DN: "{{ opencloud_ldap_group_base_dn }}"
# LDAP user schema
OC_LDAP_USER_SCHEMA_ID: "{{ opencloud_ldap_user_schema_id }}"
OC_LDAP_USER_SCHEMA_ID_IS_OCTET_STRING: "{{ opencloud_ldap_user_schema_id_is_octet_string | string | lower }}"
OC_LDAP_USER_SCHEMA_USERNAME: "{{ opencloud_ldap_user_schema_username }}"
OC_LDAP_USER_SCHEMA_MAIL: "{{ opencloud_ldap_user_schema_mail }}"
OC_LDAP_USER_SCHEMA_DISPLAY_NAME: "{{ opencloud_ldap_user_schema_display_name }}"
# LDAP group schema
OC_LDAP_GROUP_SCHEMA_ID: "{{ opencloud_ldap_group_schema_id }}"
OC_LDAP_GROUP_SCHEMA_ID_IS_OCTET_STRING: "{{ opencloud_ldap_group_schema_id_is_octet_string | string | lower }}"
OC_LDAP_GROUP_SCHEMA_GROUPNAME: "{{ opencloud_ldap_group_schema_groupname }}"
OC_LDAP_GROUP_SCHEMA_MEMBER: "{{ opencloud_ldap_group_schema_member }}"
GRAPH_LDAP_SERVER_WRITE_ENABLED: "{{ opencloud_ldap_write_enabled | string | lower }}"
{% endif %}
{% if opencloud_collabora_domain %}
OC_ADD_RUN_SERVICES: "collaboration"
COLLABORA_DOMAIN: "{{ opencloud_collabora_domain }}"
COLLABORATION_APP_NAME: "CollaboraOnline"
COLLABORATION_APP_PRODUCT: "Collabora"
COLLABORATION_APP_ADDR: "https://{{ opencloud_collabora_domain }}"
COLLABORATION_APP_INSECURE: "{{ opencloud_collabora_insecure | string | lower }}"
COLLABORATION_APP_PROOF_DISABLE: "{{ opencloud_collabora_insecure | string | lower }}"
COLLABORATION_CS3API_DATAGATEWAY_INSECURE: "{{ opencloud_collabora_insecure | string | lower }}"
COLLABORATION_HTTP_ADDR: "0.0.0.0:9300"
COLLABORATION_WOPI_SRC: "https://{{ opencloud_wopi_domain }}"
FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR: "eu.opencloud.api.collaboration"
{% endif %}
networks:
- {{ opencloud_traefik_network }}
{% for net in opencloud_extra_networks %}
- {{ net }}
{% endfor %}
{% if opencloud_extra_hosts is defined and opencloud_extra_hosts | length > 0 %}
extra_hosts:
{% for host in opencloud_extra_hosts %}
- "{{ host }}"
{% endfor %}
{% endif %}
labels:
- traefik.enable=true
- traefik.docker.network={{ opencloud_traefik_network }}
- traefik.http.routers.{{ opencloud_service_name }}.rule=Host(`{{ opencloud_domain }}`)
{% if opencloud_use_ssl %}
- traefik.http.routers.{{ opencloud_service_name }}.entrypoints=websecure
- traefik.http.routers.{{ opencloud_service_name }}.tls=true
{% else %}
- traefik.http.routers.{{ opencloud_service_name }}.entrypoints=web
{% endif %}
- traefik.http.services.{{ opencloud_service_name }}.loadbalancer.server.port={{ opencloud_port }}
{% if opencloud_collabora_domain %}
- traefik.http.routers.{{ opencloud_service_name }}.service={{ opencloud_service_name }}
- traefik.http.routers.{{ opencloud_service_name }}-wopi.rule=Host(`{{ opencloud_wopi_domain }}`)
- traefik.http.routers.{{ opencloud_service_name }}-wopi.service={{ opencloud_service_name }}-wopi
- traefik.http.services.{{ opencloud_service_name }}-wopi.loadbalancer.server.port=9300
{% if opencloud_use_ssl %}
- traefik.http.routers.{{ opencloud_service_name }}-wopi.entrypoints=websecure
- traefik.http.routers.{{ opencloud_service_name }}-wopi.tls=true
{% else %}
- traefik.http.routers.{{ opencloud_service_name }}-wopi.entrypoints=web
{% endif %}
{% endif %}
networks:
{{ opencloud_traefik_network }}:
external: true
{% for net in opencloud_extra_networks %}
{{ net }}:
external: true
{% endfor %}

View file

@ -1,9 +0,0 @@
role_assignment:
driver: oidc
oidc_role_mapper:
role_claim: {{ opencloud_role_assignment_oidc_claim }}
role_mapping:
{% for mapping in opencloud_role_mapping %}
- role_name: {{ mapping.role_name }}
claim_value: "{{ mapping.claim_value }}"
{% endfor %}

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -1,6 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
- hosts: localhost
remote_user: root
roles:
- opencloud

View file

@ -1,3 +0,0 @@
#SPDX-License-Identifier: MIT-0
---
# vars file for opencloud