From 25370ed25e741ae7aad5903c5a265e192cf1b79a Mon Sep 17 00:00:00 2001 From: Alexandre Horst Date: Wed, 23 Feb 2022 11:12:43 -0300 Subject: [PATCH] Add mode to delete LDAP users Create a new parameter mode to remove LDAP user with keystone account and sudo access if exists. The playbook run a set of tasks to remove the user in all subclouds. The objective of the playbook is to remove the specified ldap account to disallow future ssh connects to the system using the account. The playbook works for stand-alone as well as Distributed Cloud systems. Test Plan: PASS: Create user with mode 'create' and then delete the same user using mode 'delete'. PASS: Verify the user deleted can not connect using SSH to all subclouds PASS: Verify the script can run in stand-alone system PASS: Verify the script can run in distributed cloud Story: 2009759 Task: 44537 Signed-off-by: Alexandre Horst Change-Id: I7271d77b2daa5beb5d55052ad7c9c2f0c4f36719 --- .../inventory-EXAMPLE | 8 +- .../playbooks/manage_local_ldap_account.yml | 107 ++++++++++-- .../common/add-hosts/tasks/main.yml | 18 ++ .../get-distributed-role/tasks/main.yml | 16 ++ .../get-online-subclouds/tasks/main.yml | 28 +++ .../create-account/tasks/main.yml | 59 ++----- .../create-keystone-account/handlers/main.yml | 12 -- .../create-keystone-account/tasks/main.yml | 159 ++++++++---------- .../delete-account/tasks/main.yml | 33 ++++ .../delete-keystone-account/tasks/main.yml | 23 +++ 10 files changed, 295 insertions(+), 168 deletions(-) create mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/add-hosts/tasks/main.yml create mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-distributed-role/tasks/main.yml create mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-online-subclouds/tasks/main.yml delete mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/handlers/main.yml create mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-account/tasks/main.yml create mode 100644 playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-keystone-account/tasks/main.yml diff --git a/examples/manage-local-ldap-account/inventory-EXAMPLE b/examples/manage-local-ldap-account/inventory-EXAMPLE index 35aa8f39c..e7c496142 100644 --- a/examples/manage-local-ldap-account/inventory-EXAMPLE +++ b/examples/manage-local-ldap-account/inventory-EXAMPLE @@ -32,8 +32,12 @@ # ansible-playbook --inventory inventory-secure --ask-vault-pass \ # --extra-vars='user_id=JohnDoo sudo_permission=yes' \ # /usr/share/ansible/stx-ansible/playbooks/manage_local_ldap_account.yml - - +# +# If you wish to delete an existing user account (e.g. na-admin): +# +# ansible-playbook --inventory inventory --extra-vars='user_id=na-admin \ +# mode=delete' /usr/share/ansible/stx-ansible/playbooks/manage_local_ldap_account.yml +# [all:vars] ansible_user=sysadmin diff --git a/playbookconfig/src/playbooks/manage_local_ldap_account.yml b/playbookconfig/src/playbooks/manage_local_ldap_account.yml index 684780386..4888e5b6a 100644 --- a/playbookconfig/src/playbooks/manage_local_ldap_account.yml +++ b/playbookconfig/src/playbooks/manage_local_ldap_account.yml @@ -32,10 +32,18 @@ # playbook enables this option while the user account creation is progressing # and disables it when complete. # -# Example command: +# Example to add user 'na-admin' (mode=create is default): # ansible-playbook --inventory inventory --extra-vars='user_id=na-admin' \ # /usr/share/ansible/stx-ansible/playbooks/manage_local_ldap_account.yml # +# Example to add user 'na-admin' with the use of variable mode=create: +# ansible-playbook --inventory inventory --extra-vars='user_id=na-admin mode=create' \ +# /usr/share/ansible/stx-ansible/playbooks/manage_local_ldap_account.yml +# +# Example to delete user 'na-admin': +# ansible-playbook --inventory inventory --extra-vars='user_id=na-admin \ +# mode=delete' /usr/share/ansible/stx-ansible/playbooks/manage_local_ldap_account.yml +# - hosts: systemcontroller gather_facts: no @@ -45,24 +53,44 @@ prompt: "What is the name of the user account?" private: no - - name: user_password - prompt: "What is the password for the user account?" - private: yes - unsafe: yes - pre_tasks: - set_fact: - password_change_period: 90 - password_warning_period: 2 + in_mode: "{{ mode }}" + when: mode is defined + + - name: Validate in_mode + fail: + msg: "The mode must be 'create' or 'delete'." + when: "in_mode not in ['create', 'delete']" + - name: Validate user_id fail: msg: "The user account cannot be empty. Please provide a valid user account." when: user_id == '' - - name: Validate user_password - fail: - msg: "The password cannot be empty. Please provide a valid password for the user account." - when: user_password == '' + - name: Default to create if mode is not specified + set_fact: + in_mode: "create" + when: "in_mode is not defined" + + - block: + - pause: + prompt: "What is the password for the user account?" + echo: no + register: prompt + no_log: no + - set_fact: + in_user_password: "{{ prompt.user_input }}" + no_log: no + - name: Validate user_password + fail: + msg: "The password cannot be empty. Please provide a valid password for the user account." + when: "in_mode == 'create' and ( in_user_password is not defined and in_user_password == '' )" + when: in_mode == 'create' + + - set_fact: + password_change_period: 90 + password_warning_period: 2 # The user id and password variables need to be explicitly set here. # Otherwise, there would be undefined variable errors in subsequent @@ -70,7 +98,6 @@ - name: Set the user id and password facts for subsequent plays set_fact: in_user_id: "{{ user_id }}" - in_user_password: "{{ user_password }}" - name: Set sudo_permission flag fact upfront set_fact: @@ -82,8 +109,14 @@ vars: ssh_internal_args: -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no - roles: - - manage-local-ldap-account/create-account + tasks: + - include_role: + name: manage-local-ldap-account/create-account + when: in_mode == 'create' + + - include_role: + name: manage-local-ldap-account/delete-account + when: in_mode == 'delete' - hosts: all gather_facts: no @@ -91,5 +124,45 @@ vars: ssh_internal_args: -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no - roles: - - manage-local-ldap-account/create-keystone-account + tasks: + - name: Enable AllowTcpForwarding setting in ssh config + lineinfile: + regexp: ^[ \t]*AllowTcpForwarding([ \t]+.*)$ + line: AllowTcpForwarding yes + dest: /etc/ssh/sshd_config + validate: sshd -t -f %s + notify: + - reload sshd + become: yes + when: ('systemcontroller' in group_names) + + - meta: flush_handlers + + - name: Manage keystone user {{ in_user_id }} + block: + - include_role: + name: manage-local-ldap-account/create-keystone-account + when: in_mode == 'create' + + - include_role: + name: manage-local-ldap-account/delete-keystone-account + when: in_mode == 'delete' + + always: + - name: Disable AllowTcpForwarding setting in ssh config + lineinfile: + regexp: ^[ \t]*AllowTcpForwarding([ \t]+.*)$ + line: AllowTcpForwarding no + dest: /etc/ssh/sshd_config + validate: sshd -t -f %s + notify: + - reload sshd + when: ('systemcontroller' in group_names) + become: yes + + handlers: + - name: reload sshd + service: + name: sshd + state: reloaded + become: true diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/add-hosts/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/add-hosts/tasks/main.yml new file mode 100644 index 000000000..3ad417489 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/add-hosts/tasks/main.yml @@ -0,0 +1,18 @@ +--- +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Tasks to populate the inventory with managed subclouds. + +- name: Populate inventory with subclouds + add_host: + name: "{{ in_item }}" + groups: "subclouds" + in_user_id: "{{ in_user_id }}" + ssh_internal_args: "{{ ssh_internal_args }}" + ansible_ssh_common_args: + '-o ProxyCommand="sshpass -p {{ ansible_password }} ssh -W [%h]:%p -q {{ ansible_user }}@{{ ansible_host }}"' + in_sudo_permission: "{{ in_sudo_permission }}" + in_mode: "{{ in_mode }}" + in_user_password: "{{ in_user_password if in_mode == 'create' else '' }}" diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-distributed-role/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-distributed-role/tasks/main.yml new file mode 100644 index 000000000..14d43ce53 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-distributed-role/tasks/main.yml @@ -0,0 +1,16 @@ +--- +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# The file has tasks to get/set some flags related to distributed cloud. + +- name: Get distributed_cloud role + shell: | + source /etc/platform/openrc + system show | grep distributed_cloud_role | awk '{ print $4 }' + register: distributed_cloud_role + +- name: Set if system is a DC + set_fact: + is_dc: "{{ true if distributed_cloud_role.stdout == 'systemcontroller' else false }}" diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-online-subclouds/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-online-subclouds/tasks/main.yml new file mode 100644 index 000000000..03c5b41cb --- /dev/null +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/common/get-online-subclouds/tasks/main.yml @@ -0,0 +1,28 @@ +--- +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# The file has tasks to get/set some flags related to subclouds from +# central system controller. This role also builds the list of subclouds +# that are currently online and managed to be used in the next play. + +- name: Set os_param_region_name if system is a DC + set_fact: + os_param_region_name: "{{ '--os-region-name SystemController' if is_dc | bool else '' }}" + +- name: Tasks for distributed cloud + block: + - name: Get subcloud list + shell: | + source /etc/platform/openrc + dcmanager subcloud list --format yaml + register: subcloud_list_result + + - name: Set a list for subclouds + set_fact: + subcloud_list: "{{ subcloud_list | default([]) + [ item.name ] }}" + when: (item.management == "managed" and item.availability == "online") + loop: "{{ subcloud_list_result.stdout | from_yaml if subcloud_list_result.stdout else [] }}" + + when: is_dc | bool diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-account/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-account/tasks/main.yml index 0dba36b0a..973a5b344 100644 --- a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-account/tasks/main.yml +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-account/tasks/main.yml @@ -3,10 +3,9 @@ # # SPDX-License-Identifier: Apache-2.0 # -# Tasks to check LDAP user existence, to create LDAP users and to set new -# initial password and to create home directory. If the system is distributed -# cloud, this role also builds the list of subclouds that are currently online -# and managed to be used in the next play. +# Tasks to check LDAP user existence, to create LDAP users and set a new +# initial password, and to create the home directory. If the system is +# distributed cloud, it dynamically adds subclouds to the target host list. - name: Check if LDAP user exists shell: ldapsearch -x -LLL uid={{ in_user_id }} @@ -38,46 +37,18 @@ no_log: true when: in_user_id_check.stdout == "" -- name: Get distributed_cloud role - shell: | - source /etc/platform/openrc - system show | grep distributed_cloud_role | awk '{ print $4 }' - register: distributed_cloud_role +- name: Get distributed cloud role + include_role: + name: manage-local-ldap-account/common/get-distributed-role -- name: Set if system is a DC - set_fact: - is_dc: "{{ true if distributed_cloud_role.stdout == 'systemcontroller' else false }}" - -- name: Tasks for distributed cloud - block: - - name: Get subcloud list - shell: | - source /etc/platform/openrc - dcmanager subcloud list --format yaml - register: subcloud_list_result - - - name: Set a list for subclouds - set_fact: - subcloud_list: "{{ subcloud_list | default([]) + [ item.name ] }}" - when: (item.management == "managed" and item.availability == "online") - loop: "{{ subcloud_list_result.stdout | from_yaml if subcloud_list_result.stdout else [] }}" - - - name: Populate inventory with subclouds - add_host: - name: "{{ item }}" - groups: "subclouds" - in_user_id: "{{ in_user_id }}" - in_user_password: "{{ in_user_password }}" - password_change_period: "{{ password_change_period }}" - password_warning_period: "{{ password_warning_period }}" - ssh_internal_args: "{{ ssh_internal_args }}" - ansible_ssh_common_args: - '-o ProxyCommand="sshpass -p {{ ansible_password }} ssh -W [%h]:%p -q {{ ansible_user }}@{{ ansible_host }}"' - in_sudo_permission: "{{ in_sudo_permission }}" - loop: "{{ subcloud_list }}" +- name: Get online subclouds + include_role: + name: manage-local-ldap-account/common/get-online-subclouds +- name: Populate inventory with subclouds + include_role: + name: manage-local-ldap-account/common/add-hosts + vars: + in_item: "{{ item }}" + loop: "{{ subcloud_list }}" when: is_dc | bool - -- name: Set os_param_region_name if system is a DC - set_fact: - os_param_region_name: "{{ '--os-region-name SystemController' if is_dc | bool else '' }}" diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/handlers/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/handlers/main.yml deleted file mode 100644 index d3bd58288..000000000 --- a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/handlers/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# Copyright (c) 2022 Wind River Systems, Inc. -# -# SPDX-License-Identifier: Apache-2.0 -# -# Handler to reload sshd service - -- name: reload sshd - service: - name: sshd - state: reloaded - become: true diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/tasks/main.yml index 81e0a23f4..52dc9504d 100644 --- a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/tasks/main.yml +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/create-keystone-account/tasks/main.yml @@ -7,110 +7,83 @@ # controller, and subclouds as well for distributed systems with specific # admin role. -- name: Create keystone user {{ in_user_id }} +- name: Create keystone user on System Controller block: - - name: Enable AllowTcpForwarding setting in ssh config - lineinfile: - regexp: ^[ \t]*AllowTcpForwarding([ \t]+.*)$ - line: AllowTcpForwarding yes - dest: /etc/ssh/sshd_config - validate: sshd -t -f %s - notify: - - reload sshd - become: yes - when: ('systemcontroller' in group_names) + - name: Create the keystone user {{ in_user_id }} + expect: + command: >- + /bin/sh -c 'source /etc/platform/openrc; openstack {{ os_param_region_name }} user create --project + admin --password-prompt {{ in_user_id }}' + responses: + Password: '{{ in_user_password }}' + "\\~\\$": exit - - meta: flush_handlers + - name: Add keystone user to the admin role + shell: | + source /etc/platform/openrc + openstack {{ os_param_region_name }} role add --user {{ in_user_id }} --project admin admin - - name: Create keystone user on System Controller - block: - - name: Create the keystone user {{ in_user_id }} - expect: - command: >- - /bin/sh -c 'source /etc/platform/openrc; openstack {{ os_param_region_name }} user create --project - admin --password-prompt {{ in_user_id }}' - responses: - Password: '{{ in_user_password }}' - "\\~\\$": exit + when: ('systemcontroller' in group_names) - - name: Add keystone user to the admin role - shell: | - source /etc/platform/openrc - openstack {{ os_param_region_name }} role add --user {{ in_user_id }} --project admin admin +- name: Complete keystone user creation on the subcloud + block: + - name: Wait for keystone user to propagate to all subclouds + shell: source /etc/platform/openrc; openstack user show {{ in_user_id }} + register: user_output + until: user_output.rc == 0 + retries: 12 + delay: 10 - when: ('systemcontroller' in group_names) + - name: Create LDAP user keystone-account home directory on the subcloud + expect: + command: ssh {{ ssh_internal_args }} {{ in_user_id }}@localhost + responses: + s password: "{{ in_user_password }}" + "\\~\\$": exit + # do not show passwords in the logs + no_log: true - - name: Complete keystone user creation on the subcloud - block: - - name: Wait for keystone user to propagate to all subclouds - shell: source /etc/platform/openrc; openstack user show {{ in_user_id }} - register: user_output - until: user_output.rc == 0 - retries: 12 - delay: 10 + when: ('systemcontroller' not in group_names) - - name: Create LDAP user keystone-account home directory on the subcloud - expect: - command: ssh {{ ssh_internal_args }} {{ in_user_id }}@localhost - responses: - s password: "{{ in_user_password }}" - "\\~\\$": exit - # do not show passwords in the logs - no_log: true +- name: Retrieve region name + shell: source /etc/platform/openrc; system show | grep region_name | awk '{ print $4 }' + register: region_name - when: ('systemcontroller' not in group_names) +- name: Retrieve management network floating IP + shell: >- + source /etc/platform/openrc; system addrpool-list --nowrap | + awk -F \| '$3 ~ / management / { gsub(/ /,"",$8); print $8 }' + register: management_floating_ip - - name: Retrieve region name - shell: source /etc/platform/openrc; system show | grep region_name | awk '{ print $4 }' - register: region_name +- name: Generate keystone user credentials file + template: + src: openrc-template.j2 + dest: /home/{{ in_user_id }}/{{ in_user_id }}-openrc + owner: "{{ in_user_id }}" + group: users + mode: 0600 + become: yes - - name: Retrieve management network floating IP - shell: >- - source /etc/platform/openrc; system addrpool-list --nowrap | - awk -F \| '$3 ~ / management / { gsub(/ /,"",$8); print $8 }' - register: management_floating_ip +- name: Add LDAP user to 'root' group + command: usermod -a -G root {{ in_user_id }} + become: yes + when: in_sudo_permission - - name: Generate keystone user credentials file - template: - src: openrc-template.j2 - dest: /home/{{ in_user_id }}/{{ in_user_id }}-openrc - owner: "{{ in_user_id }}" - group: users - mode: 0600 - become: yes +- name: Retrieve LDAP user groups + command: groups {{ in_user_id }} + register: user_groups - - name: Add LDAP user to 'root' group - command: usermod -a -G root {{ in_user_id }} - become: yes - when: in_sudo_permission +- name: Set array of user groups to check + set_fact: + user_group_array: ['users', 'sys_protected'] - - name: Retrieve LDAP user groups - command: groups {{ in_user_id }} - register: user_groups +- name: Update array of user groups to include root group if sudo permission is granted + set_fact: + user_group_array: "{{ user_group_array }} + ['root']" + when: in_sudo_permission - - name: Set array of user groups to check - set_fact: - user_group_array: ['users', 'sys_protected'] - - - name: Update array of user groups to include root group if sudo permission is granted - set_fact: - user_group_array: "{{ user_group_array }} + ['root']" - when: in_sudo_permission - - - name: Verify LDAP user groups - fail: - msg: "{{ in_user_id }} is not part of group {{ item }}" - when: item not in user_groups.stdout - loop: "{{ user_group_array }}" - - always: - - name: Disable AllowTcpForwarding setting in ssh config - lineinfile: - regexp: ^[ \t]*AllowTcpForwarding([ \t]+.*)$ - line: AllowTcpForwarding no - dest: /etc/ssh/sshd_config - validate: sshd -t -f %s - notify: - - reload sshd - when: ('systemcontroller' in group_names) - become: yes +- name: Verify LDAP user groups + fail: + msg: "{{ in_user_id }} is not part of group {{ item }}" + when: item not in user_groups.stdout + loop: "{{ user_group_array }}" diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-account/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-account/tasks/main.yml new file mode 100644 index 000000000..c9049019f --- /dev/null +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-account/tasks/main.yml @@ -0,0 +1,33 @@ +--- +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Tasks to check LDAP user existence, to delete LDAP account. If the system is +# distributed cloud, it dynamically adds subclouds to the target host list. + +- name: Check if LDAP user exists + command: ldapsearch -x -LLL uid={{ in_user_id }} + register: in_user_id_check + become: yes + +- name: Delete LDAP user {{ in_user_id }} only if it exists + command: ldapdeleteuser {{ in_user_id }} + become: yes + when: in_user_id_check.stdout | length != 0 + +- name: Get distributed role + include_role: + name: manage-local-ldap-account/common/get-distributed-role + +- name: Get online subclouds + include_role: + name: manage-local-ldap-account/common/get-online-subclouds + +- name: Populate inventory with subclouds + include_role: + name: manage-local-ldap-account/common/add-hosts + vars: + in_item: "{{ item }}" + loop: "{{ subcloud_list }}" + when: is_dc | bool diff --git a/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-keystone-account/tasks/main.yml b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-keystone-account/tasks/main.yml new file mode 100644 index 000000000..ecf582d3c --- /dev/null +++ b/playbookconfig/src/playbooks/roles/manage-local-ldap-account/delete-keystone-account/tasks/main.yml @@ -0,0 +1,23 @@ +--- +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Tasks to delete both keystone and LDAP user account inside the system +# controller, and subclouds as well for distributed systems. + +- name: Delete keystone user on System Controller + block: + - name: Delete the keystone user {{ in_user_id }} + shell: >- + source /etc/platform/openrc; openstack {{ os_param_region_name }} user delete {{ in_user_id }} + + when: ('systemcontroller' in group_names) + +- name: Wait for keystone user to be deleted in all subclouds + shell: source /etc/platform/openrc; ! openstack user show {{ in_user_id }} + register: user_output + until: user_output.rc == 0 + retries: 12 + delay: 10 + when: ('systemcontroller' not in group_names)