/home/zuul/src/opendev.org/openstack/ansible-hardening/tasks/rhel7stig/accounts.yml
---
# Copyright 2017, Rackspace US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Check if /etc/security/pwquality.conf exists
  stat:
    path: /etc/security/pwquality.conf
  check_mode: no
  register: pwquality_config_check
  tags:
    - always

- name: Set password quality requirements
  blockinfile:
    dest: /etc/security/pwquality.conf
    backup: yes
    insertbefore: EOF
    marker: "# {mark} Added by ansible-hardening role"
    state: present
    block: "{{ lookup('template', 'pwquality.conf.j2') }}"
  when:
    - pwquality_config_check.stat.exists
  tags:
    - accounts
    - medium
    - V-71903
    - V-71905
    - V-71907
    - V-71909
    - V-71911
    - V-71913
    - V-71915
    - V-71917
    - V-71935

- name: Check for SHA512 password storage in PAM
  command: "grep pam_unix.so {{ pam_password_file }}"
  register: password_sha512_check
  changed_when: False
  check_mode: no
  tags:
    - always

- name: Print warning if PAM is not using SHA512 for password storage
  debug:
    msg: >
      PAM is not using SHA512 for password storage. This is a security issue.
  when:
    - password_sha512_check is defined
    - "'sha512' not in password_sha512_check.stdout"
  tags:
    - accounts
    - medium
    - V-71919

- name: Ensure libuser is storing passwords using SHA512
  ini_file:
    dest: /etc/libuser.conf
    section: defaults
    option: crypt_style
    value: sha512
    backup: yes
  when:
    - security_libuser_crypt_style_sha512 | bool
    - ansible_os_family | lower == 'redhat'
  tags:
    - accounts
    - medium
    - V-71923

# NOTE(mhayden): The "is mapping" check is required below because some users
# may be attached to a Kerberos realm and they may not have shadow data on the
# system. See bug 1659232 for more details.
- name: Set minimum password lifetime limit to 24 hours for interactive accounts
  shell: "chage -m 1 {{ item.name }}"
  when:
    - item.shadow is mapping
    - item.shadow.min_days != 1
    - security_set_minimum_password_lifetime | bool
  with_items:
    - "{{ interactive_user_list.users }}"
  tags:
    - accounts
    - medium
    - V-71927

# NOTE(mhayden): The "is mapping" check is required below because some users
# may be attached to a Kerberos realm and they may not have shadow data on the
# system. See bug 1659232 for more details.
- name: Set maximum password lifetime limit to 60 days for interactive accounts
  shell: "chage -M 60 {{ item.name }}"
  when:
    - item.shadow is mapping
    - item.shadow.max_days > 60
    - security_set_maximum_password_lifetime | bool
  with_items:
    - "{{ interactive_user_list.users }}"
  tags:
    - accounts
    - medium
    - V-71931

- name: Ensure that users cannot reuse one of their last 5 passwords
  lineinfile:
    dest: "{{ pam_password_file }}"
    regexp: '^(password\s+[a-z0-9\=\[\] ]+\s+pam_unix\.so.+?)\s+(?:remember=\d+)?$'
    line: '\1 remember={{ security_password_remember_password }}'
    backrefs: yes
    state: present
  when:
    - security_password_remember_password is defined
  tags:
    - accounts
    - medium
    - V-71933

- name: Ensure accounts are disabled if the password expires
  lineinfile:
    dest: /etc/default/useradd
    regexp: '^[#\s]*INACTIVE'
    line: 'INACTIVE=0'
  when:
    - security_disable_account_if_password_expires | bool
  tags:
    - accounts
    - medium
    - V-71941

- name: Apply shadow-utils configurations
  lineinfile:
    dest: /etc/login.defs
    regexp: "^{{ item.parameter }}"
    line: "{{ item.parameter }} {{ item.value }}"
    state: present
  when:
    - item.value != ''
    - item.ansible_os_family == 'all' or item.ansible_os_family == ansible_os_family
  with_items: "{{ shadow_utils_rhel7 }}"
  tags:
    - accounts
    - medium
    - V-71921
    - V-71925
    - V-71929
    - V-71951
    - V-71995
    - V-72013

- name: Print warning for groups in /etc/passwd that are not in /etc/group
  debug:
    msg: >
      The following users have GIDs in /etc/passwd that do not exist in /etc/group:
      {{ user_list.users | selectattr('group', 'equalto', False) | map(attribute='name') | join(', ') }}
  when:
    - user_list is defined
    - user_list.users | selectattr('group', 'equalto', False) | list | length > 0
  tags:
    - accounts
    - low
    - V-72003

- name: Get all accounts with UID 0
  shell: "awk -F: '$3 == 0 {print $1}' /etc/passwd"
  changed_when: False
  check_mode: no
  register: root_user_check
  tags:
    - accounts
    - high
    - V-72005
    - skip_ansible_lint

- name: Print warnings for non-root users with UID 0
  fail:
    msg: |
      Only the 'root' user should have UID 0. Other users were found:
      {{ root_user_check.stdout_lines | join(', ') }}"
  when:
    - root_user_check.stdout != 'root'
  tags:
    - accounts
    - high
    - V-72005

- name: Print warning for local interactive users without a home directory assigned
  debug:
    msg: |
      The following users do not have a home directory assigned:
      {{ user_list.users | selectattr('dir', 'equalto', '') | map(attribute='name') | join(', ') }}
  when:
    - user_list is defined
    - user_list.users | selectattr('dir', 'equalto', '') | map(attribute='name') | list | length > 0
  tags:
    - accounts
    - medium
    - V-72011

- name: Check each user to see if its home directory exists on the filesystem
  stat:
    path: "{{ item['dir'] }}"
  when:
    - item['dir'] != ''
  with_items: "{{ user_list.users }}"
  register: home_directory_checks
  tags:
    - accounts
    - medium
    - V-72015

- name: Print warning for users with an assigned home directory that does not exist
  debug:
    msg: |
      These users have a home directory assigned, but the directory does not exist:
      {% for check in home_directory_checks.results %}
      {% if not check.stat.exists %}
      {{ check.item.name }} ({{ check.item.dir }} does not exist)
      {% endif %}
      {% endfor %}
  when:
    - home_directory_checks.results | selectattr('stat.exists', 'sameas', false) | list | length > 0
  tags:
    - accounts
    - medium
    - V-72015

- name: Use pwquality when passwords are changed or created
  lineinfile:
    dest: /etc/pam.d/passwd
    line: "password required pam_pwquality.so retry=3"
    state: present
  when:
    - security_enable_pwquality_password_set | bool
  tags:
    - accounts
    - medium
    - V-73159