Optimized LDAP integration, keycloak realm import and health checks for docker images

This commit is contained in:
Kevin Veen-Birkenbach 2025-02-18 21:00:14 +01:00
parent e87c3e2090
commit 0f44e65bf1
26 changed files with 111 additions and 63 deletions

View File

@ -54,7 +54,8 @@ defaults_applications:
## Funkwhale ## Funkwhale
funkwhale: funkwhale:
version: "1.4.0" version: "1.4.0"
ldap_enabled: True # Enables LDAP by default
## Gitea ## Gitea
gitea: gitea:
@ -70,11 +71,11 @@ defaults_applications:
## Keycloak ## Keycloak
keycloak: keycloak:
version: "latest" version: "latest"
administrator_username: "{{administrator_username}}" # Administrator Username for Keycloak administrator_username: "{{administrator_username}}" # Administrator Username for Keycloak
# database_password: # Needs to be defined in inventory file ldap_enabled: True # Enables LDAP by default
# administrator_password: # Needs to be defined in inventory file # database_password: # Needs to be defined in inventory file
# administrator_password: # Needs to be defined in inventory file
## LDAP ## LDAP
ldap: ldap:
@ -92,15 +93,17 @@ defaults_applications:
version: "2.0.0-dev" # @todo Attention: Change this as fast as released to latest version: "2.0.0-dev" # @todo Attention: Change this as fast as released to latest
webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin
administrator_username: "{{administrator_username}}" administrator_username: "{{administrator_username}}"
# administrator_password: # CHANGE for security reasons in inventory file ldap_enabled: True # Should have the same value as applications.ldap.openldap.network.local.
# administrator_database_password: # CHANGE for security reasons in inventory file # Both need to be set to True to load the ldap_network in the docker compose file
# administrator_password: # CHANGE for security reasons in inventory file
# administrator_database_password: # CHANGE for security reasons in inventory file
## Listmonk ## Listmonk
listmonk: listmonk:
administrator_username: "{{administrator_username}}" administrator_username: "{{administrator_username}}"
public_api_activated: False # Security hole. Can be used for spaming public_api_activated: False # Security hole. Can be used for spaming
version: "latest" version: "latest" # Docker Image version
setup: false # Set true in inventory file to execute the setup and initializing procedures setup: false # Set true in inventory file to execute the setup and initializing procedures
## MariaDB ## MariaDB
mariadb: mariadb:
@ -148,19 +151,21 @@ defaults_applications:
## Nextcloud ## Nextcloud
nextcloud: nextcloud:
version: "production" # @see https://nextcloud.com/blog/nextcloud-release-channels-and-how-to-track-them/ version: "production" # @see https://nextcloud.com/blog/nextcloud-release-channels-and-how-to-track-them/
ldap_enabled: True # Enables LDAP by default
## OAuth2 Proxy ## OAuth2 Proxy
oauth2_proxy: oauth2_proxy:
configuration_file: "oauth2-proxy-keycloak.cfg" # Needs to be set true in the roles which use it configuration_file: "oauth2-proxy-keycloak.cfg" # Needs to be set true in the roles which use it
version: "latest" version: "latest" # Docker Image version
redirect_url: "https://{{domains.keycloak}}/auth/realms/{{primary_domain}}/protocol/openid-connect/auth" # The redirect URL for the OAuth2 flow. It should match the redirect URL configured in Keycloak. redirect_url: "https://{{domains.keycloak}}/auth/realms/{{primary_domain}}/protocol/openid-connect/auth" # The redirect URL for the OAuth2 flow. It should match the redirect URL configured in Keycloak.
allowed_roles: admin # Restrict it default to admin role. Use the vars/main.yml to open the specific role for other groups allowed_roles: admin # Restrict it default to admin role. Use the vars/main.yml to open the specific role for other groups
cookie_secret: "{{ applications.oauth2_proxy.cookie_secret if applications.oauth2_proxy is defined else '' }}" # Default use wildcard for primary domain, subdomain client specific configuration in vars files in the roles is possible openssl rand -hex 16 cookie_secret: "{{ applications.oauth2_proxy.cookie_secret if applications.oauth2_proxy is defined else '' }}" # Default use wildcard for primary domain, subdomain client specific configuration in vars files in the roles is possible openssl rand -hex 16
## Open Project ## Open Project
openproject: openproject:
version: "13" # Update when available. Sadly no rolling release implemented version: "13" # Update when available. Sadly no rolling release implemented
oauth2_proxy_active: true oauth2_proxy_active: true
ldap_enabled: True # Enables LDAP by default
## Peertube ## Peertube
peertube: peertube:
@ -174,8 +179,8 @@ defaults_applications:
## Pixelfed ## Pixelfed
pixelfed: pixelfed:
titel: "Pictures on {{primary_domain}}" titel: "Pictures on {{primary_domain}}"
version: "latest" version: "latest"
## Postgres ## Postgres
# Please set an version in your inventory file - Rolling release for postgres isn't recommended # Please set an version in your inventory file - Rolling release for postgres isn't recommended
@ -188,7 +193,7 @@ defaults_applications:
## Taiga ## Taiga
taiga: taiga:
version: "latest" version: "latest"
## YOURLS ## YOURLS
yourls: yourls:

View File

@ -39,10 +39,6 @@ oauth2_proxy_active: false
# Helper variables # Helper variables
_ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}" _ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}"
# This leads to that the role gets configured to use ldap
ldap_enabled: false
ldap: ldap:
# Enables LDAP for all roles in play if true # Enables LDAP for all roles in play if true
enabled: true enabled: true

View File

@ -100,7 +100,7 @@ DJANGO_SETTINGS_MODULE=config.settings.production
# Generate one using `openssl rand -base64 45`, for example # Generate one using `openssl rand -base64 45`, for example
DJANGO_SECRET_KEY={{funkwhale_django_secret}} DJANGO_SECRET_KEY={{funkwhale_django_secret}}
{% if ldap_enabled | bool %} {% if applications[application_id].ldap_enabled | bool %}
# LDAP settings # LDAP settings
# Use the following options to allow authentication on your Funkwhale instance # Use the following options to allow authentication on your Funkwhale instance
# using a LDAP directory. # using a LDAP directory.
@ -110,7 +110,7 @@ DJANGO_SECRET_KEY={{funkwhale_django_secret}}
LDAP_ENABLED = True LDAP_ENABLED = True
LDAP_SERVER_URI = "{{ldap.server.uri}}" LDAP_SERVER_URI = "{{ldap.server.uri}}"
LDAP_BIND_DN = "{{ldap.dn.bind}}" LDAP_BIND_DN = "{{ldap.dn.bind}}"
LDAP_BIND_PASSWORD = "{{ldap.dn.bind_credential}}" LDAP_BIND_PASSWORD = "{{ldap.bind_credential}}"
LDAP_SEARCH_FILTER = "(|(cn={0})(mail={0}))" LDAP_SEARCH_FILTER = "(|(cn={0})(mail={0}))"
LDAP_START_TLS = False LDAP_START_TLS = False
LDAP_ROOT_DN = "{{ldap.dn.root}}" LDAP_ROOT_DN = "{{ldap.dn.root}}"

View File

@ -2,7 +2,6 @@ application_id: "funkwhale"
nginx_docker_reverse_proxy_extra_configuration: "client_max_body_size 512M;" nginx_docker_reverse_proxy_extra_configuration: "client_max_body_size 512M;"
database_password: "{{funkwhale_database_password}}" database_password: "{{funkwhale_database_password}}"
database_type: "postgres" database_type: "postgres"
ldap_enabled: True
media_root: "/srv/funkwhale/data/" media_root: "/srv/funkwhale/data/"
static_root: "{{media_root}}static" static_root: "{{media_root}}static"
celeryd_concurrency: 1 celeryd_concurrency: 1

View File

@ -8,3 +8,17 @@
- name: "copy docker-compose.yml and env file" - name: "copy docker-compose.yml and env file"
include_tasks: copy-docker-compose-and-env.yml include_tasks: copy-docker-compose-and-env.yml
- name: "create directory {{import_directory_host}}"
file:
path: "{{import_directory_host}}"
state: directory
mode: 0755
- name: "Copy import files to {{ import_directory_host }}"
template:
src: "{{ item }}"
dest: "{{ import_directory_host }}/{{ item | basename | regex_replace('\\.j2$', '') }}"
mode: '770'
loop: "{{ lookup('fileglob', '{{ role_path }}/templates/import/*.j2', wantlist=True) }}"
notify: docker compose project setup

View File

@ -4,11 +4,19 @@ services:
application: application:
image: quay.io/keycloak/keycloak:{{applications.keycloak.version}} image: quay.io/keycloak/keycloak:{{applications.keycloak.version}}
command: start container_name: {{container_name}}
command: start --import-realm # imports realms on startup
{% include 'roles/docker-compose/templates/services/base.yml.j2' %} {% include 'roles/docker-compose/templates/services/base.yml.j2' %}
ports: ports:
- "127.0.0.1:{{http_port}}:8080" - "127.0.0.1:{{http_port}}:8080"
volumes:
- "{{import_directory_host}}:{{import_directory_docker}}"
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %} {% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
test: ["CMD", "sh", "-c", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/live HTTP/1.1\\r\\nHost: {{domains.keycloak}}\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/networks.yml.j2' %} {% include 'templates/docker/compose/networks.yml.j2' %}

View File

@ -1,6 +1,15 @@
# Environment File for Keycloak
# Documentation can be found here:
# @see https://www.keycloak.org/server/containers
KC_HOSTNAME= https://{{domain}} KC_HOSTNAME= https://{{domain}}
KC_HTTP_ENABLED= true KC_HTTP_ENABLED= true
# Health Checks
# @see https://quarkus.io/guides/smallrye-health
KC_HEALTH_ENABLED= true KC_HEALTH_ENABLED= true
KC_METRICS_ENABLED= true
KEYCLOAK_ADMIN= "{{applications.keycloak.administrator_username}}" KEYCLOAK_ADMIN= "{{applications.keycloak.administrator_username}}"
KEYCLOAK_ADMIN_PASSWORD= "{{applications.keycloak.administrator_password}}" KEYCLOAK_ADMIN_PASSWORD= "{{applications.keycloak.administrator_password}}"
KC_DB= postgres KC_DB= postgres

View File

@ -1,5 +1,7 @@
application_id: "keycloak" application_id: "keycloak"
database_type: "postgres" database_type: "postgres"
database_password: "{{applications.keycloak.database_password}}" database_password: "{{applications.keycloak.database_password}}"
ldap_enabled: True container_name: "{{application_id}}_application"
realm: "{{primary_domain}}" # This is the name of the default realm which is used by the applications realm: "{{primary_domain}}" # This is the name of the default realm which is used by the applications
import_directory_host: "{{docker_compose.directories.volumes}}import/" # Directory in which keycloack import files are placed on the host
import_directory_docker: "/opt/keycloak/data/import/" # Directory in which keycloack import files are placed in the running docker container

View File

@ -19,7 +19,7 @@
- name: "Import Access Roles to OpenLDAP" - name: "Import Access Roles to OpenLDAP"
shell: > shell: >
docker exec -i openldap ldapadd -x -D "{{ldap.dn.bind}}" -w "{{ldap.dn.bind_credential}}" -c -f "{{ldif_docker_path}}04_access_profiles.ldif" docker exec -i openldap ldapadd -x -D "{{ldap.dn.bind}}" -w "{{ldap.bind_credential}}" -c -f "{{ldif_docker_path}}04_access_profiles.ldif"
register: ldapadd_result register: ldapadd_result
changed_when: "'adding new entry' in ldapadd_result.stdout" changed_when: "'adding new entry' in ldapadd_result.stdout"
# Allow return code 0 (all entries added) or 68 (entry already exists) # Allow return code 0 (all entries added) or 68 (entry already exists)

View File

@ -34,7 +34,7 @@ services:
- '{{ldif_host_path}}:{{ldif_docker_path}}:ro' # Mounting all ldif files for import - '{{ldif_host_path}}:{{ldif_docker_path}}:ro' # Mounting all ldif files for import
healthcheck: healthcheck:
test: > test: >
ldapsearch -x -H ldap://localhost:{{ldap_docker_port}} -b "{{ldap.dn.root}}" -D "{{ldap.dn.bind}}" -w "{{ldap.dn.bind_credential}}" ldapsearch -x -H ldap://localhost:{{ldap_docker_port}} -b "{{ldap.dn.root}}" -D "{{ldap.dn.bind}}" -w "{{ldap.bind_credential}}"
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

View File

@ -10,4 +10,4 @@ LAM_CONFIGURATION_DATABASE= files
LDAP_SERVER= {{ldap.server.domain}} # domain of LDAP database root entry LDAP_SERVER= {{ldap.server.domain}} # domain of LDAP database root entry
LDAP_BASE_DN= {{ldap.dn.root}} # LDAP base DN to overwrite value generated by LDAP_DOMAIN LDAP_BASE_DN= {{ldap.dn.root}} # LDAP base DN to overwrite value generated by LDAP_DOMAIN
LDAP_USER= {{ldap.dn.bind}} # LDAP admin user (set as login user for LAM) LDAP_USER= {{ldap.dn.bind}} # LDAP admin user (set as login user for LAM)
LDAP_ADMIN_PASSWORD= {{ldap.dn.bind_credential}} # LDAP admin password LDAP_ADMIN_PASSWORD= {{ldap.bind_credential}} # LDAP admin password

View File

@ -1,7 +1,6 @@
application_id: "ldap" application_id: "ldap"
ldaps_docker_port: 636 ldaps_docker_port: 636
ldap_docker_port: 389 ldap_docker_port: 389
ldap_enabled: True
# OAuth2 Proxy Configuration # OAuth2 Proxy Configuration
oauth2_proxy_upstream_application_and_port: "{{ applications.ldap.webinterface }}:{% if applications.ldap.webinterface == 'phpldapadmin' %}8080{% else %}80{% endif %}" oauth2_proxy_upstream_application_and_port: "{{ applications.ldap.webinterface }}:{% if applications.ldap.webinterface == 'phpldapadmin' %}8080{% else %}80{% endif %}"

View File

@ -11,6 +11,8 @@ services:
- {{docker_compose.directories.config}}config.toml:/listmonk/config.toml - {{docker_compose.directories.config}}config.toml:/listmonk/config.toml
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %} {% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
healthcheck:
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:9000/health || exit 1']
{% include 'templates/docker/compose/volumes-just-database.yml.j2' %} {% include 'templates/docker/compose/volumes-just-database.yml.j2' %}

View File

@ -8,14 +8,6 @@ This Ansible role deploys a [Matomo](https://matomo.org/) analytics platform ins
- Nginx installed for reverse proxy configuration. - Nginx installed for reverse proxy configuration.
- Certbot installed for SSL certificate generation. - Certbot installed for SSL certificate generation.
## Role Variables
- `domain`: The domain where Matomo will be accessible.
- `administrator_email`: The email used for SSL certificate registration.
- `path_docker_compose_instances`: Path to store Docker Compose files.
- `http_port`: The host port that Matomo will be accessible on.
- `matomo_database_password`: Password for the Matomo database.
## AI Generated ## AI Generated
This script was created with the help of ChatGPT. The full conversation is [here](https://chat.openai.com/share/49e0c7e4-a2af-4a04-adad-7a735bdd85c4) available. This script was created with the help of ChatGPT. The full conversation is [here](https://chat.openai.com/share/49e0c7e4-a2af-4a04-adad-7a735bdd85c4) available.

View File

@ -11,6 +11,11 @@ services:
- data:/var/www/html - data:/var/www/html
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %} {% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/80 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/volumes.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %}
data: data:

View File

@ -1,3 +1,6 @@
# Environment File for Matomo
# @see https://hub.docker.com/_/matomo/
MATOMO_DATABASE_HOST= "{{database_host}}:{{database_port}}" MATOMO_DATABASE_HOST= "{{database_host}}:{{database_port}}"
MATOMO_DATABASE_ADAPTER= "mysql" MATOMO_DATABASE_ADAPTER= "mysql"
MATOMO_DATABASE_USERNAME= "{{database_username}}" MATOMO_DATABASE_USERNAME= "{{database_username}}"

View File

@ -10,17 +10,14 @@ services:
volumes: volumes:
- 'moodle:/bitnami/moodle' - 'moodle:/bitnami/moodle'
- 'data:/bitnami/moodledata' - 'data:/bitnami/moodledata'
# Healthcheck is not possible due to missing curl and wget in container healthcheck:
# @todo implement healthcheck test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
# healthcheck: interval: 30s
# test: ["CMD", "curl", "-f", "http://127.0.0.1:8080"] timeout: 10s
# interval: 1m retries: 3
# timeout: 10s
# retries: 3
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %} {% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
{% include 'templates/docker/compose/volumes.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %}
moodle: moodle:
data: data:

View File

@ -4,4 +4,3 @@ database_password: "{{nextcloud_database_password}}"
database_type: "mariadb" database_type: "mariadb"
nextcloud_application_container_name: "nextcloud-application" nextcloud_application_container_name: "nextcloud-application"
nextcloud_nginx_container_name: "nextcloud-web" nextcloud_nginx_container_name: "nextcloud-web"
ldap_enabled: True

View File

@ -14,5 +14,3 @@ dummy_volume: "{{docker_compose.directories.volu
# OAuth2 Proxy Configuration # OAuth2 Proxy Configuration
oauth2_proxy_upstream_application_and_port: "proxy:80" oauth2_proxy_upstream_application_and_port: "proxy:80"
oauth2_proxy_active: "{{ applications.openproject.oauth2_proxy_active | bool }}" oauth2_proxy_active: "{{ applications.openproject.oauth2_proxy_active | bool }}"
ldap_enabled: True

View File

@ -16,6 +16,12 @@ services:
- config:/config - config:/config
{% include 'templates/docker/container/depends-on-database-redis.yml.j2' %} {% include 'templates/docker/container/depends-on-database-redis.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
# This just tests if the service is running on port 9000. It doesn't check if there is an 200 or e.g. an 404 response
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/volumes.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %}
assets: assets:

View File

@ -10,5 +10,10 @@ services:
- "127.0.0.1:{{http_port}}:80" - "127.0.0.1:{{http_port}}:80"
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %} {% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/80 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/networks.yml.j2' %} {% include 'templates/docker/compose/networks.yml.j2' %}

View File

@ -11,5 +11,9 @@ services:
- ./app:/app - ./app:/app
restart: unless-stopped restart: unless-stopped
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/5000 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/networks.yml.j2' %} {% include 'templates/docker/compose/networks.yml.j2' %}

View File

@ -13,6 +13,11 @@ services:
- "127.0.0.1:{{ports.localhost.http.snipe_it}}:80" - "127.0.0.1:{{ports.localhost.http.snipe_it}}:80"
{% include 'templates/docker/container/depends-on-database-redis.yml.j2' %} {% include 'templates/docker/container/depends-on-database-redis.yml.j2' %}
{% include 'templates/docker/container/networks.yml.j2' %} {% include 'templates/docker/container/networks.yml.j2' %}
healthcheck:
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/80 && echo -e 'GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1'"]
interval: 30s
timeout: 10s
retries: 3
{% include 'templates/docker/compose/volumes.yml.j2' %} {% include 'templates/docker/compose/volumes.yml.j2' %}
redis: redis:

View File

@ -4,12 +4,12 @@ networks:
central_{{ database_type }}: central_{{ database_type }}:
external: true external: true
{% endif %} {% endif %}
{% if ldap_enabled | bool and applications.ldap.openldap.network.local | bool %} {% if applications[application_id].ldap_enabled is defined and applications[application_id].ldap_enabled | bool and applications.ldap.openldap.network.local | bool %}
central_ldap: central_ldap:
external: true external: true
{% endif %} {% endif %}
default: default:
{% if application_id in networks.local and networks.local[application_id].subnet is defined %} {% if applications[application_id].ldap_enabled is defined and applications[application_id].ldap_enabled | bool and applications.ldap.openldap.network.local | bool %}
driver: bridge driver: bridge
ipam: ipam:
driver: default driver: default

View File

@ -3,7 +3,7 @@
{% if enable_central_database | bool and database_type is defined %} {% if enable_central_database | bool and database_type is defined %}
central_{{ database_type }}: central_{{ database_type }}:
{% endif %} {% endif %}
{% if ldap_enabled | bool and applications.ldap.openldap.network.local | bool %} {% if applications[application_id].ldap_enabled is defined and applications[application_id].ldap_enabled | bool and applications.ldap.openldap.network.local | bool %}
central_ldap: central_ldap:
{% endif %} {% endif %}
default: default: