From e46c687b89b71cf9f2acda9719b5fe748713ffad Mon Sep 17 00:00:00 2001 From: transcaffeine Date: Sat, 16 Oct 2021 15:01:14 +0200 Subject: [PATCH] feat(ldap-user-backend): add role for managing ldap user backend in nextcloud --- README.md | 2 + galaxy.yml | 2 + roles/ldap-user-backend/README.md | 37 ++++++++++ roles/ldap-user-backend/defaults/main.yml | 67 +++++++++++++++++++ roles/ldap-user-backend/meta/main.yml | 4 ++ .../tasks/load_config_http.yml | 49 ++++++++++++++ .../tasks/load_config_occ.yml | 49 ++++++++++++++ roles/ldap-user-backend/tasks/main.yml | 10 +++ roles/ldap-user-backend/vars/main.yml | 60 +++++++++++++++++ 9 files changed, 280 insertions(+) create mode 100644 roles/ldap-user-backend/README.md create mode 100644 roles/ldap-user-backend/defaults/main.yml create mode 100644 roles/ldap-user-backend/meta/main.yml create mode 100644 roles/ldap-user-backend/tasks/load_config_http.yml create mode 100644 roles/ldap-user-backend/tasks/load_config_occ.yml create mode 100644 roles/ldap-user-backend/tasks/main.yml create mode 100644 roles/ldap-user-backend/vars/main.yml diff --git a/README.md b/README.md index 783ec85..b05cf0b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ and managing nextcloud installations - [`roles/apps`](roles/apps/README.md): For managing nextcloud apps in an already installed nextcloud server instance. Can install, remove, enable/disable and update apps. +- [`roles/ldap-user-backend`](roles/ldap-user-backend/README.md): + Manages LDAP authentication sources in installed nextcloud instances. ## License diff --git a/galaxy.yml b/galaxy.yml index c45c8bc..706b0f1 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,6 +5,8 @@ readme: README.md authors: - Johanna Dorothea Reichmann description: Installing and configuring nextcloud (and related apps/services) using docker +dependencies: + "community.docker": "^1.10.0" license: - CNPLv7+ build_ignore: diff --git a/roles/ldap-user-backend/README.md b/roles/ldap-user-backend/README.md new file mode 100644 index 0000000..d6954f1 --- /dev/null +++ b/roles/ldap-user-backend/README.md @@ -0,0 +1,37 @@ +# `finallycoffee.nextcloud.ldap-user-backend` ansible role + +Ansible role for managing LDAP authentication of nextcloud instances using ansible. + +## Prerequisites + +This role assumes a nextcloud instance is up and running, and has the `user_ldap` +nextcloud app installed. For starting a nextcloud instance, see the +`finallycoffee.nextcloud.server` role, for managing nextcloud apps see the +`finallycoffee.nextcloud.apps` ansible role. + +## Configuration + +- Set `nc_ldap_api_method` to either `occ` or `http` to control wether the + configuration is set using `php occ` command line calls or the `http` API + of the `user_ldap` nextcloud app. + +- For `nc_ldap_api_method: occ`, ensure `nc_ldap_container` is set to the name + of the docker container where nextcloud is running, and `nc_ldap_occ_user` is + the user the container / nextcloud itself runs as. `nc_ldap_occ_command` + _can_ also be tweaked if `php` is not in the path, but the default should + be fine in most cases. + +- For `nc_ldap_api_method: http`, ensure `nc_ldapi_api_instance_url` contains + the URL to the nextcloud server, including protocol (and port, if + non-standard), and `nc_ldap_api_basic_auth_[user|password]` contain the + credentials of an admin user with the rights to edit the LDAP settings. + +- Set `nc_ldap_test_configuration` to `true`/`false` to have the role issue a + nextcloud-provided test of the configured LDAP configuration, this corresponds + to a `occ ldap:test-config `. + +For most of the options, see the +[nextcloud manual on configuration keys](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/user_auth_ldap_api.html#configuration-keys), +the config keys are mapped 1:1 with a prefix of `nc_ldap_config_` and +the so-called "snake-case" (`ldap_backup_host`), so `ldapUserFilterMode` becomes +`nc_ldap_config_ldap_user_filter_mode`. diff --git a/roles/ldap-user-backend/defaults/main.yml b/roles/ldap-user-backend/defaults/main.yml new file mode 100644 index 0000000..36a4373 --- /dev/null +++ b/roles/ldap-user-backend/defaults/main.yml @@ -0,0 +1,67 @@ +--- + +nc_ldap_api_method: occ +nc_ldap_test_configuration: true + +nc_ldap_api_instance_url: http://localhost +nc_ldap_api_basic_auth_user: +nc_ldap_api_basic_auth_password: + +nc_ldap_occ_command: "php occ" +nc_ldap_occ_user: "nextcloud" +nc_ldap_container: nextcloud + +nc_ldap_config_id: s01 +nc_ldap_config_host: 127.0.0.1 +nc_ldap_config_port: 389 +nc_ldap_config_backup_host: ~ +nc_ldap_config_backup_port: ~ + +nc_ldap_config_base_dn: +nc_ldap_config_base_dn_users: +nc_ldap_config_base_dn_groups: +nc_ldap_config_agent_name: +nc_ldap_config_agent_password: + +nc_ldap_config_override_main_server: ~ +nc_ldap_config_tls: ~ +nc_ldap_config_turn_off_cert_check: ~ +nc_ldap_config_user_displayname: ~ +nc_ldap_config_user_displayname2: ~ +nc_ldap_config_user_avatar_rule: ~ +nc_ldap_config_gid_number: ~ +nc_ldap_config_user_filter_objectclass: ~ +nc_ldap_config_user_filter_groups: ~ +nc_ldap_config_user_filter: ~ +nc_ldap_config_user_filter_mode: ~ +nc_ldap_config_attributes_for_user_search: ~ +nc_ldap_config_group_filter: ~ +nc_ldap_config_group_filter_mode: ~ +nc_ldap_config_group_filter_objectclass: ~ +nc_ldap_config_group_filter_groups: ~ +nc_ldap_config_group_member_assoc_attr: ~ +nc_ldap_config_group_displayname: ~ +nc_ldap_config_attributes_for_group_search: ~ +nc_ldap_config_login_filter: ~ +nc_ldap_config_login_filter_mode: ~ +nc_ldap_config_login_filter_email: ~ +nc_ldap_config_login_filter_username: ~ +nc_ldap_config_login_filter_attributes: ~ +nc_ldap_config_quota_attribute: ~ +nc_ldap_config_quota_default: ~ +nc_ldap_config_email_attribute: ~ +nc_ldap_config_cache_ttl: ~ +nc_ldap_config_configuration_active: ~ +nc_ldap_config_experienced_admin: ~ +nc_ldap_config_home_folder_naming_rule: ~ +nc_ldap_config_use_memberOf_to_detect_membership: ~ +nc_ldap_config_expert_username_attr: ~ +nc_ldap_config_expert_uuid_user_attr: ~ +nc_ldap_config_expert_uuid_group_attr: ~ +nc_ldap_config_nested_groups: ~ +nc_ldap_config_paging_size: ~ +nc_ldap_config_turn_on_password_change: ~ +nc_ldap_config_dynamic_group_member_url: ~ +nc_ldap_config_default_ppolicy_dn: ~ + +nc_ldap_meta_http_agent: "ansible-httpget/finallycoffee.nextcloud.ldap-user-backend" diff --git a/roles/ldap-user-backend/meta/main.yml b/roles/ldap-user-backend/meta/main.yml new file mode 100644 index 0000000..ec33810 --- /dev/null +++ b/roles/ldap-user-backend/meta/main.yml @@ -0,0 +1,4 @@ +--- + +collections: + - community.docker diff --git a/roles/ldap-user-backend/tasks/load_config_http.yml b/roles/ldap-user-backend/tasks/load_config_http.yml new file mode 100644 index 0000000..422a5e3 --- /dev/null +++ b/roles/ldap-user-backend/tasks/load_config_http.yml @@ -0,0 +1,49 @@ +--- + +- name: Set default api parameters for HTTP + meta: noop + vars: &api_defaults + http_agent: "{{ nc_ldap_meta_http_agent }}" + headers: "{{ nc_ldap_api_headers }}" + url_username: "{{ nc_ldap_api_basic_auth_user }}" + url_password: "{{ nc_ldap_api_basic_auth_password }}" + force_basic_auth: yes + force: yes + +- name: Check if configuration with given config ID already exists + uri: + <<: *api_defaults + url: "{{ nc_ldap_api_path }}/{{ nc_ldap_config_id }}{{ query_params }}" + method: GET + vars: + query_params: "?showPassword={{ '1' if nc_ldap_config_agent_password else '0' }}&format={{nc_ldap_api_parameter_format }}" + register: nc_ldap_existing_config_api + +# TODO: Can we force an ID on POST? +- name: Create ldap configuration with id={{ nc_ldap_config_id }} + uri: + <<: *api_defaults + url: "{{ nc_ldap_api_path }}" + method: POST + when: nc_ldap_existing_config_api.status != 200 + +- name: Parse output of query command to dict + set_fact: + nc_ldap_existing_config: "{{ nc_ldap_existing_config_api.stdout | from_json }}" + changed_when: false + +- name: Create changeset + set_fact: + nc_ldap_config_changeset: "{{ nc_ldap_config_changeset | combine(changed_entry) }}" + vars: + changed_entry: "{{ { item : nc_ldap_config_keys[item] } }}" + loop: "{{ nc_ldap_config_keys.keys() }}" + when: nc_ldap_config_keys[item] is defined and nc_ldap_config_keys[item] and nc_ldap_config_keys[item] != nc_ldap_existing_config[nc_ldap_config_id][item] + +- name: Ensure ldap configuration is in sync (http) + uri: + <<: *api_defaults + url: "{{ nc_lap_api_path }}/{{ nc_ldap_config_id }}" + method: PUT + body: "{{ { 'configData': nc_ldap_config_changeset } }}" + body_format: "form-urlencoded" diff --git a/roles/ldap-user-backend/tasks/load_config_occ.yml b/roles/ldap-user-backend/tasks/load_config_occ.yml new file mode 100644 index 0000000..45ca02a --- /dev/null +++ b/roles/ldap-user-backend/tasks/load_config_occ.yml @@ -0,0 +1,49 @@ +--- + +- name: Check if configuration with given config ID already exists + docker_container_exec: + container: "{{ nc_ldap_container }}" + command: "{{ nc_ldap_occ_command }} ldap:show-config --output json {{ '--show-password' if nc_ldap_config_agent_password else '' }} {{ nc_ldap_config_id }}" + user: "{{ nc_ldap_occ_user }}" + tty: yes + changed_when: false + check_mode: false + register: nc_ldap_existing_config_occ + +- name: Create ldap configuration with id={{ nc_ldap_config_id }} + docker_container_exec: + container: "{{ nc_ldap_container }}" + command: "{{ nc_ldap_occ_command }} ldap:create-empty-config --output json {{ nc_ldap_config_id }}" + user: "{{ nc_ldap_occ_user }}" + tty: yes + when: nc_ldap_existing_config_occ.rc != 0 and nc_ldap_config_id not in (nc_ldap_existing_config_occ.stdout | from_json).keys() + +- name: Parse output of query command to dict + set_fact: + nc_ldap_existing_config: "{{ nc_ldap_existing_config_occ.stdout | from_json }}" + changed_when: false + +- name: Create changeset + set_fact: + nc_ldap_config_changeset: "{{ nc_ldap_config_changeset | combine(changed_entry) }}" + vars: + changed_entry: "{{ { item : nc_ldap_config_keys[item] } }}" + loop: "{{ nc_ldap_config_keys.keys() }}" + when: nc_ldap_config_keys[item] is defined and nc_ldap_config_keys[item] and nc_ldap_config_keys[item] != nc_ldap_existing_config[nc_ldap_config_id][item] + +- name: Ensure ldap configuration is in sync + docker_container_exec: + container: "{{ nc_ldap_container }}" + command: "{{ nc_ldap_occ_command }} ldap:set-config \"{{ nc_ldap_config_id }}\" \"{{ item.key }}\" \"{{ item.value }}\"" + user: "{{ nc_ldap_occ_user }}" + tty: yes + loop: "{{ nc_ldap_config_changeset | dict2items }}" + +- name: Ensure ldap configuration is working + docker_container_exec: + container: "{{ nc_ldap_container }}" + command: "{{ nc_ldap_occ_command }} ldap:test-config {{ nc_ldap_config_id }}" + user: "{{ nc_ldap_occ_user }}" + tty: yes + changed_when: false + when: nc_ldap_test_configuration diff --git a/roles/ldap-user-backend/tasks/main.yml b/roles/ldap-user-backend/tasks/main.yml new file mode 100644 index 0000000..ee9501e --- /dev/null +++ b/roles/ldap-user-backend/tasks/main.yml @@ -0,0 +1,10 @@ +--- + +- name: Load config {{ nc_ldap_config_id }} (and create if not exists) when running mode is http + include_tasks: load_config_http.yml + when: nc_ldap_api_method == 'http' + +- name: Load config {{ nc_ldap_config_id }} (and create if not exists) when running mode is occ + include_tasks: load_config_occ.yml + when: nc_ldap_api_method == 'occ' + diff --git a/roles/ldap-user-backend/vars/main.yml b/roles/ldap-user-backend/vars/main.yml new file mode 100644 index 0000000..8154f0f --- /dev/null +++ b/roles/ldap-user-backend/vars/main.yml @@ -0,0 +1,60 @@ +--- + +nc_ldap_api_path: "/ocs/v2.php/apps/user_ldap/api/v1/config" +nc_ldap_api_url: "{{ nc_ldap_api_instance_url }}{{ nc_ldap_api_path }}" +nc_ldap_api_headers: + OCS-APIREQUEST: "true" +nc_ldap_api_parameter_format: json + +nc_ldap_config_keys: + ldapHost: "{{ nc_ldap_config_host }}" + ldapPort: "{{ nc_ldap_config_port }}" + ldapBackupHost: "{{ nc_ldap_config_backup_host }}" + ldapBackupPort: "{{ nc_ldap_config_backup_port }}" + ldapOverrideMainServer: "{{ nc_ldap_config_override_main_server }}" + ldapBase: "{{ nc_ldap_config_base_dn }}" + ldapBaseUsers: "{{ nc_ldap_config_base_dn_users }}" + ldapBaseGroups: "{{ nc_ldap_config_base_dn_groups }}" + ldapAgentName: "{{ nc_ldap_config_agent_name }}" + ldapAgentPassword: "{{ nc_ldap_config_agent_password }}" + ldapTLS: "{{ nc_ldap_config_tls }}" + turnOffCertCheck: "{{ nc_ldap_config_turn_off_cert_check }}" + ldapUserDisplayName: "{{ nc_ldap_config_user_displayname }}" + ldapUserDisplayName2: "{{ nc_ldap_config_user_displayname2 }}" + ldapUserAvatarRule: "{{ nc_ldap_config_user_avatar_rule }}" + ldapGidNumber: "{{ nc_ldap_config_gid_number }}" + ldapUserFilterObjectclass: "{{ nc_ldap_config_user_filter_objectclass }}" + ldapUserFilterGroups: "{{ nc_ldap_config_user_filter_groups }}" + ldapUserFilter: "{{ nc_ldap_config_user_filter }}" + ldapUserFilterMode: "{{ nc_ldap_config_user_filter_mode }}" + ldapAttributesForUserSearch: "{{ nc_ldap_config_attributes_for_user_search }}" + ldapGroupFilter: "{{ nc_ldap_config_group_filter }}" + ldapGroupFilterMode: "{{ nc_ldap_config_group_filter_mode }}" + ldapGroupFilterObjectclass: "{{ nc_ldap_config_group_filter_objectclass }}" + ldapGroupFilterGroups: "{{ nc_ldap_config_group_filter_groups }}" + ldapGroupMemberAssocAttr: "{{ nc_ldap_config_group_member_assoc_attr }}" + ldapGroupDisplayName: "{{ nc_ldap_config_group_displayname }}" + ldapAttributesForGroupSearch: "{{ nc_ldap_config_attributes_for_group_search }}" + ldapLoginFilter: "{{ nc_ldap_config_login_filter }}" + ldapLoginFilterMode: "{{ nc_ldap_config_login_filter_mode }}" + ldapLoginFilterEmail: "{{ nc_ldap_config_login_filter_email }}" + ldapLoginFilterUsername: "{{ nc_ldap_config_login_filter_username }}" + ldapLoginFilterAttributes: "{{ nc_ldap_config_login_filter_attributes }}" + ldapQuotaAttribute: "{{ nc_ldap_config_quota_attribute }}" + ldapQuotaDefault: "{{ nc_ldap_config_quota_default }}" + ldapEmailAttribute: "{{ nc_ldap_config_email_attribute }}" + ldapCacheTTL: "{{ nc_ldap_config_cache_ttl }}" + ldapConfigurationActive: "{{ nc_ldap_config_configuration_active }}" + ldapExperiencedAdmin: "{{ nc_ldap_config_experienced_admin }}" + homeFolderNamingRule: "{{ nc_ldap_config_home_folder_naming_rule }}" + useMemberOfToDetectMembership: "{{ nc_ldap_config_use_memberOf_to_detect_membership }}" + ldapExpertUsernameAttr: "{{ nc_ldap_config_expert_username_attr }}" + ldapExpertUUIDUserAttr: "{{ nc_ldap_config_expert_uuid_user_attr }}" + ldapExpertUUIDGroupAttr: "{{ nc_ldap_config_expert_uuid_group_attr }}" + ldapNestedGroups: "{{ nc_ldap_config_nested_groups }}" + ldapPagingSize: "{{ nc_ldap_config_paging_size }}" + turnOnPasswordChange: "{{ nc_ldap_config_turn_on_password_change }}" + ldapDynamicGroupMemberURL: "{{ nc_ldap_config_dynamic_group_member_url }}" + ldapDefaultPPolicyDN: "{{ nc_ldap_config_default_ppolicy_dn }}" + +nc_ldap_config_changeset: {}