{"id":145,"sha1":"70e509753cc4b185efd7ccb2d0b5cbaddc07900f","playbook":{"id":2,"items":{"plays":18,"tasks":316,"results":313,"hosts":2,"files":136,"records":0},"arguments":{"version":null,"verbosity":0,"private_key_file":null,"remote_user":null,"connection":"openstack.osa.ssh","timeout":null,"ssh_common_args":null,"sftp_extra_args":null,"scp_extra_args":null,"ssh_extra_args":null,"ask_pass":false,"connection_password_file":null,"force_handlers":true,"flush_cache":false,"become":false,"become_method":"sudo","become_user":null,"become_ask_pass":false,"become_password_file":null,"tags":["all"],"skip_tags":[],"check":false,"diff":false,"inventory":["/home/zuul/src/opendev.org/openstack/openstack-ansible/inventory/dynamic_inventory.py","/home/zuul/src/opendev.org/openstack/openstack-ansible/inventory/inventory.ini","/etc/openstack_deploy/inventory.ini"],"listhosts":false,"subset":null,"extra_vars":"Not saved by ARA as configured by 'ignored_arguments'","vault_ids":[],"ask_vault_pass":false,"vault_password_files":[],"forks":8,"module_path":null,"syntax":false,"listtasks":false,"listtags":false,"step":false,"start_at_task":null,"args":["setup-hosts.yml"]},"labels":[{"id":1,"name":"check:False"},{"id":2,"name":"tags:all"}],"started":"2025-12-08T13:27:39.675908Z","ended":"2025-12-08T13:33:13.621332Z","duration":"00:05:33.945424","name":null,"ansible_version":"2.18.6","client_version":"1.7.4","python_version":"3.12.3","server_version":"1.7.4","status":"completed","path":"/home/zuul/src/opendev.org/openstack/openstack-ansible/playbooks/setup-hosts.yml","controller":"aio1.openstack.local","user":"root"},"content":"---\n# Copyright 2017, Rackspace US, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n- name: Check if /etc/security/pwquality.conf exists\n  ansible.builtin.stat:\n    path: /etc/security/pwquality.conf\n  check_mode: false\n  register: pwquality_config_check\n  tags:\n    - always\n\n- name: Set password quality requirements\n  ansible.builtin.blockinfile:\n    dest: /etc/security/pwquality.conf\n    backup: true\n    insertbefore: EOF\n    marker: \"# {mark} Added by ansible-hardening role\"\n    state: present\n    block: \"{{ lookup('template', 'pwquality.conf.j2') }}\"\n  when:\n    - pwquality_config_check.stat.exists\n  tags:\n    - accounts\n    - medium\n    - V-71903\n    - V-71905\n    - V-71907\n    - V-71909\n    - V-71911\n    - V-71913\n    - V-71915\n    - V-71917\n    - V-71935\n\n- name: Check for SHA512 password storage in PAM\n  ansible.builtin.command: \"grep pam_unix.so {{ pam_password_file }}\"\n  register: password_sha512_check\n  changed_when: false\n  check_mode: false\n  tags:\n    - always\n\n- name: Print warning if PAM is not using SHA512 for password storage\n  ansible.builtin.debug:\n    msg: >\n      PAM is not using SHA512 for password storage. This is a security issue.\n  when:\n    - password_sha512_check is defined\n    - \"'sha512' not in password_sha512_check.stdout\"\n  tags:\n    - accounts\n    - medium\n    - V-71919\n\n- name: Ensure libuser is storing passwords using SHA512\n  community.general.ini_file:\n    dest: /etc/libuser.conf\n    section: defaults\n    option: crypt_style\n    value: sha512\n    backup: true\n    mode: \"0644\"\n  when:\n    - security_libuser_crypt_style_sha512 | bool\n    - ansible_facts['os_family'] | lower == 'redhat'\n  tags:\n    - accounts\n    - medium\n    - V-71923\n\n# NOTE(mhayden): The \"is mapping\" check is required below because some users\n# may be attached to a Kerberos realm and they may not have shadow data on the\n# system. See bug 1659232 for more details.\n- name: Set minimum password lifetime limit to 24 hours for interactive accounts\n  ansible.builtin.command: \"chage -m 1 {{ item.name }}\"\n  changed_when: false\n  when:\n    - item.shadow is mapping\n    - item.shadow.min_days != 1\n    - security_set_minimum_password_lifetime | bool\n  with_items:\n    - \"{{ interactive_user_list.users }}\"\n  tags:\n    - accounts\n    - medium\n    - V-71927\n\n# NOTE(mhayden): The \"is mapping\" check is required below because some users\n# may be attached to a Kerberos realm and they may not have shadow data on the\n# system. See bug 1659232 for more details.\n- name: Set maximum password lifetime limit to 60 days for interactive accounts\n  ansible.builtin.command: \"chage -M 60 {{ item.name }}\"\n  changed_when: false\n  when:\n    - item.shadow is mapping\n    - item.shadow.max_days > 60\n    - security_set_maximum_password_lifetime | bool\n  with_items:\n    - \"{{ interactive_user_list.users }}\"\n  tags:\n    - accounts\n    - medium\n    - V-71931\n\n- name: Ensure that users cannot reuse one of their last 5 passwords\n  ansible.builtin.lineinfile:\n    dest: \"{{ pam_password_file }}\"\n    regexp: \"^(password\\\\s+[a-z0-9\\\\=\\\\[\\\\] ]+\\\\s+pam_unix\\\\.so.+?)\\\\s+(?:remember=\\\\d+)?$\"\n    line: \"\\\\1 remember={{ security_password_remember_password }}\"\n    backrefs: true\n    state: present\n  when:\n    - security_password_remember_password is defined\n  tags:\n    - accounts\n    - medium\n    - V-71933\n\n- name: Ensure accounts are disabled if the password expires\n  ansible.builtin.lineinfile:\n    dest: /etc/default/useradd\n    regexp: \"^[#\\\\s]*INACTIVE\"\n    line: \"INACTIVE=0\"\n  when:\n    - security_disable_account_if_password_expires | bool\n  tags:\n    - accounts\n    - medium\n    - V-71941\n\n- name: Apply shadow-utils configurations\n  ansible.builtin.lineinfile:\n    dest: /etc/login.defs\n    regexp: \"^{{ item.parameter }}\"\n    line: \"{{ item.parameter }} {{ item.value }}\"\n    state: present\n  when:\n    - item.value is truthy(convert_bool=True)\n    - item.os_family == 'all' or item.os_family == ansible_facts['os_family']\n  with_items: \"{{ shadow_utils_rhel7 }}\"\n  tags:\n    - accounts\n    - medium\n    - V-71921\n    - V-71925\n    - V-71929\n    - V-71951\n    - V-71995\n    - V-72013\n\n- name: Print warning for groups in /etc/passwd that are not in /etc/group\n  ansible.builtin.debug:\n    msg: >\n      The following users have GIDs in /etc/passwd that do not exist in /etc/group:\n      {{ hardening_user_list.users | selectattr('group', 'equalto', False) | map(attribute='name') | join(', ') }}\n  when:\n    - hardening_user_list is defined\n    - hardening_user_list.users | selectattr('group', 'equalto', False) | list | length > 0\n  tags:\n    - accounts\n    - low\n    - V-72003\n\n- name: Get all accounts with UID 0\n  shell: \"awk -F: '$3 == 0 {print $1}' /etc/passwd\"\n  changed_when: false\n  check_mode: false\n  register: root_user_check\n  tags:\n    - accounts\n    - high\n    - V-72005\n    - skip_ansible_lint\n\n- name: Print warnings for non-root users with UID 0\n  ansible.builtin.fail:\n    msg: |\n      Only the 'root' user should have UID 0. Other users were found:\n      {{ root_user_check.stdout_lines | join(', ') }}\"\n  when:\n    - root_user_check.stdout != 'root'\n  tags:\n    - accounts\n    - high\n    - V-72005\n\n- name: Print warning for local interactive users without a home directory assigned\n  ansible.builtin.debug:\n    msg: |\n      The following users do not have a home directory assigned:\n      {{ hardening_user_list.users | selectattr('dir', 'equalto', '') | map(attribute='name') | join(', ') }}\n  when:\n    - hardening_user_list is defined\n    - hardening_user_list.users | selectattr('dir', 'equalto', '') | map(attribute='name') | list | length > 0\n  tags:\n    - accounts\n    - medium\n    - V-72011\n\n- name: Check each user to see if its home directory exists on the filesystem\n  ansible.builtin.stat:\n    path: \"{{ item['dir'] }}\"\n  when:\n    - item['dir'] | length > 0\n  with_items: \"{{ hardening_user_list.users }}\"\n  register: home_directory_checks\n  tags:\n    - accounts\n    - medium\n    - V-72015\n\n- name: Print warning for users with an assigned home directory that does not exist\n  ansible.builtin.debug:\n    msg: |\n      These users have a home directory assigned, but the directory does not exist:\n      {% for check in home_directory_checks.results %}\n      {% if not check.stat.exists %}\n      {{ check.item.name }} ({{ check.item.dir }} does not exist)\n      {% endif %}\n      {% endfor %}\n  when:\n    - home_directory_checks.results | selectattr('stat.exists', 'sameas', false) | list | length > 0\n  tags:\n    - accounts\n    - medium\n    - V-72015\n\n- name: Use pwquality when passwords are changed or created\n  ansible.builtin.lineinfile:\n    dest: /etc/pam.d/passwd\n    line: \"password required pam_pwquality.so retry=3\"\n    state: present\n  when:\n    - security_enable_pwquality_password_set | bool\n  tags:\n    - accounts\n    - medium\n    - V-73159\n","created":"2025-12-08T13:31:59.823182Z","updated":"2025-12-08T13:31:59.823220Z","path":"/home/zuul/src/opendev.org/openstack/ansible-hardening/tasks/rhel7stig/accounts.yml"}