From 72deb13d07dcc258e95075bfc2c46e2916bb253a Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 25 Apr 2025 11:34:14 +0200 Subject: [PATCH] Restructured LDAP role --- group_vars/all/11_iam.yml | 8 +-- roles/docker-ldap/docs/Administration.md | 15 ++++- roles/docker-ldap/handlers/main.yml | 44 ++++++++------ roles/docker-ldap/tasks/create_ldif_files.yml | 6 +- roles/docker-ldap/tasks/main.yml | 56 ++++++++++++++++-- .../templates/docker-compose.yml.j2 | 2 +- .../ldif/data/01_application_roles.ldif.j2 | 45 +++++++++----- .../templates/ldif/data/02_users.ldif.j2 | 58 ------------------- ...chema.ldif => 01_nextcloud.schema.ldif.j2} | 0 .../templates/global.css.j2 | 5 ++ templates/docker/compose/networks.yml.j2 | 2 +- templates/docker/container/networks.yml.j2 | 2 +- templates/vars/applications.yml.j2 | 4 +- 13 files changed, 137 insertions(+), 110 deletions(-) delete mode 100644 roles/docker-ldap/templates/ldif/data/02_users.ldif.j2 rename roles/docker-ldap/templates/ldif/schema/{01_nextcloud.schema.ldif => 01_nextcloud.schema.ldif.j2} (100%) diff --git a/group_vars/all/11_iam.yml b/group_vars/all/11_iam.yml index b67132c9..68a33871 100644 --- a/group_vars/all/11_iam.yml +++ b/group_vars/all/11_iam.yml @@ -39,7 +39,7 @@ defaults_oidc: # Helper Variables: # Keep in mind to mapp this variables if there is ever the possibility for the user to define them in the inventory _ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}" -_ldap_server_port: "{% if applications.ldap.network.local | bool %}{{ ports.localhost.ldap.ldap }}{% else %}{{ ports.localhost.ldaps.ldap }}{% endif %}" +_ldap_server_port: "{% if applications.ldap.network.docker | bool %}{{ ports.localhost.ldap.ldap }}{% else %}{{ ports.localhost.ldaps.ldap }}{% endif %}" ldap: # Distinguished Names (DN) @@ -60,11 +60,11 @@ ldap: # Password to access dn.bind bind_credential: "{{applications.ldap.administrator_database_password}}" server: - domain: "{{applications.ldap.hostname if applications.ldap.network.local | bool else domains.ldap}}" # Mapping for public or locale access + domain: "{{applications.ldap.hostname if applications.ldap.network.docker | bool else domains.ldap}}" # Mapping for public or locale access port: "{{_ldap_server_port}}" - uri: "{% if applications.ldap.network.local | bool %}ldap://{{ applications.ldap.hostname }}{% else %}ldaps://{{ domains.ldap }}{% endif %}:{{ _ldap_server_port }}" + uri: "{% if applications.ldap.network.docker | bool %}ldap://{{ applications.ldap.hostname }}{% else %}ldaps://{{ domains.ldap }}{% endif %}:{{ _ldap_server_port }}" network: - local: "{{applications.ldap.network.local}}" # Uses the application configuration to define if local network should be available or not + local: "{{applications.ldap.network.docker}}" # Uses the application configuration to define if local network should be available or not user_objects: - person # Basic person attributes (sn, cn …) – RFC 4519 - inetOrgPerson # Extended Internet / intranet person – RFC 2798 diff --git a/roles/docker-ldap/docs/Administration.md b/roles/docker-ldap/docs/Administration.md index 9fdd6484..92fafe62 100644 --- a/roles/docker-ldap/docs/Administration.md +++ b/roles/docker-ldap/docs/Administration.md @@ -1,6 +1,8 @@ # Administration -## Show Configuration +## Configuration + +### Show Configuration ```bash docker exec -it ldap bash -c "ldapsearch -LLL -Y EXTERNAL -H ldapi:/// -b 'cn=config'" ``` @@ -18,7 +20,16 @@ docker exec -it ldap bash -c "ldapsearch -LLL -Y EXTERNAL -H ldapi:/// -b 'cn=co docker exec -it ldap ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" "(olcDatabase=*)" ``` -## Show all Entries +## Data + +### Set Credentials +To execute the following commands set the credentials via: + +```bash +export $(grep -Ev '^(#|$)' .env/env | xargs) +``` + +### Show all Entries ```bash docker exec -it ldap bash -c "ldapsearch -LLL -o ldif-wrap=no -x -D \"\$LDAP_ADMIN_DN\" -w \"\$LDAP_ADMIN_PASSWORD\" -b \"\$LDAP_ROOT\""; ``` diff --git a/roles/docker-ldap/handlers/main.yml b/roles/docker-ldap/handlers/main.yml index b5082c48..9a1ba533 100644 --- a/roles/docker-ldap/handlers/main.yml +++ b/roles/docker-ldap/handlers/main.yml @@ -1,25 +1,42 @@ - name: Load memberof module from file in OpenLDAP container shell: > - docker exec -i {{ applications[application_id].hostname }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}01_member_of_configuration.ldif - listen: "Import LDIF files" + docker exec -i {{ applications[application_id].hostname }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}configuration/01_member_of_configuration.ldif + listen: + - "Import configuration LDIF files" + - "Import all LDIF files" # @todo Remove the following ignore errors when setting up a new server # Just here because debugging would take to much time ignore_errors: true - name: Refint Module Activation for OpenLDAP shell: > - docker exec -i {{ applications[application_id].hostname }} ldapadd -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}02_member_of_configuration.ldif - listen: "Import LDIF files" + docker exec -i {{ applications[application_id].hostname }} ldapadd -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}configuration/02_member_of_configuration.ldif + listen: + - "Import configuration LDIF files" + - "Import all LDIF files" register: ldapadd_result failed_when: ldapadd_result.rc not in [0, 68] # @todo Remove the following ignore errors when setting up a new server # Just here because debugging would take to much time ignore_errors: true +- name: "Import schemas" + shell: > + docker exec -i {{ applications[application_id].hostname }} ldapadd -Y EXTERNAL -H ldapi:/// -f "{{ldif_docker_path}}schema/{{ item | basename | regex_replace('\.j2$', '') }}" + register: ldapadd_result + changed_when: "'adding new entry' in ldapadd_result.stdout" + failed_when: ldapadd_result.rc not in [0, 80] + listen: + - "Import schema LDIF files" + - "Import all LDIF files" + loop: "{{ lookup('fileglob', role_path ~ '/templates/ldif/schema/*.j2', wantlist=True) }}" + - name: Refint Overlay Configuration for OpenLDAP shell: > - docker exec -i {{ applications[application_id].hostname }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}03_member_of_configuration.ldif - listen: "Import LDIF files" + docker exec -i {{ applications[application_id].hostname }} ldapmodify -Y EXTERNAL -H ldapi:/// -f {{ldif_docker_path}}configuration/03_member_of_configuration.ldif + listen: + - "Import configuration LDIF files" + - "Import all LDIF files" register: ldapadd_result failed_when: ldapadd_result.rc not in [0, 68] # @todo Remove the following ignore errors when setting up a new server @@ -32,14 +49,7 @@ register: ldapadd_result changed_when: "'adding new entry' in ldapadd_result.stdout" failed_when: ldapadd_result.rc not in [0, 20, 68] - listen: "Import LDIF files" - loop: "{{ lookup('fileglob', role_path ~ '/templates/ldif/data/*.j2', wantlist=True) }}" - -- name: "Import schemas" - shell: > - docker exec -i {{ applications[application_id].hostname }} ldapadd -Y EXTERNAL -H ldapi:/// -f "{{ldif_docker_path}}schema/{{ item | basename | regex_replace('\.j2$', '') }}" - register: ldapadd_result - changed_when: "'adding new entry' in ldapadd_result.stdout" - failed_when: ldapadd_result.rc not in [0, 80] - listen: "Import LDIF files" - loop: "{{ lookup('fileglob', role_path ~ '/templates/ldif/schema/*.j2', wantlist=True) }}" + listen: + - "Import data LDIF files" + - "Import all LDIF files" + loop: "{{ lookup('fileglob', role_path ~ '/templates/ldif/data/*.j2', wantlist=True) }}" \ No newline at end of file diff --git a/roles/docker-ldap/tasks/create_ldif_files.yml b/roles/docker-ldap/tasks/create_ldif_files.yml index 29c7bb65..a1d4b498 100644 --- a/roles/docker-ldap/tasks/create_ldif_files.yml +++ b/roles/docker-ldap/tasks/create_ldif_files.yml @@ -1,9 +1,9 @@ # In own task file for easier looping -- name: "Create LDIF files at {{ ldif_host_path }}/{{ folder }}" +- name: "Create LDIF files at {{ ldif_host_path }}{{ folder }}" template: src: "{{ item }}" - dest: "{{ ldif_host_path }}/{{ folder }}/{{ item | basename | regex_replace('\\.j2$', '') }}" + dest: "{{ ldif_host_path }}{{ folder }}/{{ item | basename | regex_replace('\\.j2$', '') }}" mode: '770' loop: "{{ lookup('fileglob', role_path ~ '/templates/ldif/' ~ folder ~ '/*.j2', wantlist=True) }}" - notify: Import LDIF files + notify: "Import {{ folder }} LDIF files" diff --git a/roles/docker-ldap/tasks/main.yml b/roles/docker-ldap/tasks/main.yml index f5547bb4..2f34a598 100644 --- a/roles/docker-ldap/tasks/main.yml +++ b/roles/docker-ldap/tasks/main.yml @@ -35,11 +35,57 @@ - name: "Process all LDIF types" include_tasks: create_ldif_files.yml - loop: "{{ ldif_types }}" + loop: + - configuration + - schema loop_control: loop_var: folder -- name: Force LDIF files import - command: /bin/true - notify: Import LDIF files - when: applications.ldap.force_import | bool \ No newline at end of file +- name: flush LDIF handlers + meta: flush_handlers + +- name: install python-ldap + community.general.pacman: + name: + - python-ldap + state: present + +- name: "Ensure LDAP users are present and up to date" + community.general.ldap_entry: + dn: "{{ ldap.attributes.user_id }}={{ item.key }},{{ ldap.dn.users }}" + server_uri: "ldap://127.0.0.1:{{ports.localhost.ldap.ldap}}" + bind_dn: "{{ ldap.dn.administrator }}" + bind_pw: "{{ ldap.bind_credential }}" + objectClass: "{{ ldap.user_objects }}" + attributes: + "{{ ldap.attributes.user_id }}": "{{ item.key }}" + sn: "{{ item.value.sn | default(item.key) }}" + cn: "{{ item.value.cn | default(item.key) }}" + userPassword: "{SSHA}{{ item.value.password }}" + loginShell: /bin/bash + homeDirectory: "/home/{{ item.key }}" + uidNumber: "{{ item.value.uid | int }}" + gidNumber: "{{ item.value.gid | int }}" + state: present + loop: "{{ users | dict2items }}" + loop_control: + label: "{{ item.key }}" + +- name: "Ensure container for application roles exists" + community.general.ldap_entry: + dn: "{{ ldap.dn.application_roles }}" + server_uri: "ldap://127.0.0.1:{{ ports.localhost.ldap.ldap }}" + bind_dn: "{{ ldap.dn.administrator }}" + bind_pw: "{{ ldap.bind_credential }}" + objectClass: organizationalUnit + attributes: + ou: roles + description: Container for application access profiles + state: present + +- name: "Process all LDIF types" + include_tasks: create_ldif_files.yml + loop: + - data + loop_control: + loop_var: folder \ No newline at end of file diff --git a/roles/docker-ldap/templates/docker-compose.yml.j2 b/roles/docker-ldap/templates/docker-compose.yml.j2 index b86fe754..63581088 100644 --- a/roles/docker-ldap/templates/docker-compose.yml.j2 +++ b/roles/docker-ldap/templates/docker-compose.yml.j2 @@ -6,7 +6,7 @@ services: image: bitnami/openldap:{{ applications[application_id].version }} container_name: {{ applications[application_id].hostname }} {% include 'roles/docker-compose/templates/services/base.yml.j2' %} -{% if applications[application_id].network.public | bool %} +{% if applications[application_id].network.public | bool or applications[application_id].network.local | bool %} ports: - 127.0.0.1:{{ports.localhost.ldap.ldap}}:{{ldap_docker_port}} # Expose just on localhost so that nginx stream proxy can use it {% endif %} diff --git a/roles/docker-ldap/templates/ldif/data/01_application_roles.ldif.j2 b/roles/docker-ldap/templates/ldif/data/01_application_roles.ldif.j2 index 0d63a2fb..953a842e 100644 --- a/roles/docker-ldap/templates/ldif/data/01_application_roles.ldif.j2 +++ b/roles/docker-ldap/templates/ldif/data/01_application_roles.ldif.j2 @@ -1,19 +1,4 @@ - -####################################################################### -# Generic container for Application roles -####################################################################### -dn: {{ldap.dn.application_roles}} -objectClass: organizationalUnit -ou: roles -description: Container for application access profiles - -{# - This template generates two LDIF entries for each application in defaults_applications: - one for the administrator role and one for the standard user role. - Please adjust the base DN (dc=example,dc=com) and other attributes as necessary. -#} - -{% for app, config in defaults_applications.items() %} +{% for app, config in applications.items() %} dn: cn={{ app }}-administrator,{{ldap.dn.application_roles}} objectClass: top objectClass: organizationalRole @@ -27,3 +12,31 @@ cn: {{ app }}-user description: Standard user role for {{ app }} (automatically generated) {% endfor %} + +{% for username, user in users.items() %} + +####################################################################### +# Assign {{ username }} to application user roles +####################################################################### +{% for app, config in applications.items() %} + +# Assign {{ username }} to {{ app }}-users + +dn: cn={{ app }}-user,{{ ldap.dn.application_roles }} +changetype: modify +add: roleOccupant +roleOccupant: {{ ldap.attributes.user_id }}={{ username }},{{ ldap.dn.users }} + +{% if users.is_admin | default(false) | bool %} + +# Assign {{ username }} to {{ app }}-administrator +dn: cn={{ app }}-administrator,{{ ldap.dn.application_roles }} +changetype: modify +add: roleOccupant +roleOccupant: {{ ldap.attributes.user_id }}={{ users.administrator.username }},{{ ldap.dn.users }} + +{% endif %} + +{% endfor %} + +{% endfor %} \ No newline at end of file diff --git a/roles/docker-ldap/templates/ldif/data/02_users.ldif.j2 b/roles/docker-ldap/templates/ldif/data/02_users.ldif.j2 deleted file mode 100644 index 5564e9f2..00000000 --- a/roles/docker-ldap/templates/ldif/data/02_users.ldif.j2 +++ /dev/null @@ -1,58 +0,0 @@ -{## -# Iterate over all users and create LDAP entries for each, then assign admin to application roles -# This template loops through a 'users' list variable where each user is a dict with keys: -# username, uid, gid, password (optional), sn (optional), cn (optional) -##} -####################################################################### -# Container for Application Roles (if not already created) -####################################################################### -dn: {{ ldap.dn.application_roles }} -objectClass: organizationalUnit -ou: roles -description: Container for application access profiles - -{% for username, user in users.items() %} -####################################################################### -# Create User {{ username }} -####################################################################### -dn: {{ ldap.attributes.user_id }}={{ username }},{{ ldap.dn.users }} -{% for cls in ldap.user_objects %} -objectClass: {{ cls }} -{% endfor %} -{{ ldap.attributes.user_id }}: {{ username }} -sn: {{ username }} -cn: {{ username }} -userPassword: {SSHA}{{ user.password }} -loginShell: /bin/bash -homeDirectory: /home/{{ username }} -uidNumber: {{ user.uid }} -gidNumber: {{ user.gid }} - -####################################################################### -# Assign {{ username }} to application user roles -####################################################################### -{% for app, config in defaults_applications.items() %} -dn: cn={{ app }}-user,{{ ldap.dn.application_roles }} -changetype: modify -add: roleOccupant -roleOccupant: {{ ldap.attributes.user_id }}={{ username }},{{ ldap.dn.users }} - -{% endfor %} -{% endfor %} - -####################################################################### -# Add Admin User to All Application Role Groups (unchanged) -####################################################################### -{% for app, config in defaults_applications.items() %} -dn: cn={{ app }}-administrator,{{ ldap.dn.application_roles }} -changetype: modify -add: roleOccupant -roleOccupant: {{ ldap.attributes.user_id }}={{ users.administrator.username }},{{ ldap.dn.users }} - -dn: cn={{ app }}-user,{{ ldap.dn.application_roles }} -changetype: modify -add: roleOccupant -roleOccupant: {{ ldap.attributes.user_id }}={{ users.administrator.username }},{{ ldap.dn.users }} - -{% endfor %} - diff --git a/roles/docker-ldap/templates/ldif/schema/01_nextcloud.schema.ldif b/roles/docker-ldap/templates/ldif/schema/01_nextcloud.schema.ldif.j2 similarity index 100% rename from roles/docker-ldap/templates/ldif/schema/01_nextcloud.schema.ldif rename to roles/docker-ldap/templates/ldif/schema/01_nextcloud.schema.ldif.j2 diff --git a/roles/nginx-modifier-css/templates/global.css.j2 b/roles/nginx-modifier-css/templates/global.css.j2 index e3b2286d..8578e20f 100644 --- a/roles/nginx-modifier-css/templates/global.css.j2 +++ b/roles/nginx-modifier-css/templates/global.css.j2 @@ -1193,4 +1193,9 @@ input.ng-empty::placeholder,.ng-empty::placeholder { .kanban-swimlane-title { border-bottom: none; +} + +.navbar-toggler { + background-color: rgba(var(--color-rgb-01-75), 0.9); + border-color: var(--color-01-67) } \ No newline at end of file diff --git a/templates/docker/compose/networks.yml.j2 b/templates/docker/compose/networks.yml.j2 index 4b3917d9..6b4bf113 100644 --- a/templates/docker/compose/networks.yml.j2 +++ b/templates/docker/compose/networks.yml.j2 @@ -4,7 +4,7 @@ networks: central_{{ database_type }}: external: true {% endif %} -{% if applications[application_id].get('features', {}).get('ldap', false) | bool and applications.ldap.network.local | bool %} +{% if applications[application_id].get('features', {}).get('ldap', false) | bool and applications.ldap.network.docker | bool %} central_ldap: external: true {% endif %} diff --git a/templates/docker/container/networks.yml.j2 b/templates/docker/container/networks.yml.j2 index 6215b1cf..421459e6 100644 --- a/templates/docker/container/networks.yml.j2 +++ b/templates/docker/container/networks.yml.j2 @@ -3,7 +3,7 @@ {% if applications | get_database_central_storage(application_id) | bool and database_type is defined %} central_{{ database_type }}: {% endif %} -{% if applications[application_id].get('features', {}).get('ldap', false) | bool and applications.ldap.network.local|bool %} +{% if applications[application_id].get('features', {}).get('ldap', false) | bool and applications.ldap.network.docker|bool %} central_ldap: {% endif %} default: diff --git a/templates/vars/applications.yml.j2 b/templates/vars/applications.yml.j2 index 58cb7e69..3a5ca20f 100644 --- a/templates/vars/applications.yml.j2 +++ b/templates/vars/applications.yml.j2 @@ -249,7 +249,8 @@ defaults_applications: ldap: version: "latest" network: - local: True # Activates local network to allow other docker containers to connect + local: True # Activates local network. Necessary for LDIF import routines + docker: True # Activates docker network to allow other docker containers to connect public: False # Set to true in inventory file if you want to expose the LDAP port to the internet hostname: "ldap" # Hostname of the LDAP Server in the central_ldap network webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin @@ -258,7 +259,6 @@ defaults_applications: username: "{{users.administrator.username}}" # Administrator username # administrator_password: # CHANGE for security reasons in inventory file # administrator_database_password: # CHANGE for security reasons in inventory file - force_import: False # Forces the import of the LDIF files {% endraw %}{{ features.render_features({ 'ldap': true, }) }}{% raw %}