---
- name: OS is supported
  ansible.builtin.meta: end_host
  when:
    - not __sshd_os_supported | bool

- name: Check variables are safe for use for shell expansions and word splitting
  ansible.builtin.include_tasks: check_vars.yml

- name: Install ssh packages
  ansible.builtin.package:
    name: "{{ sshd_packages }}"
    state: present
    use: "{{ (__sshd_is_ostree | d(false)) | ternary('ansible.posix.rhel_rpm_ostree', omit) }}"

- name: Sysconfig configuration
  ansible.builtin.template:
    src: sysconfig.j2
    dest: "/etc/sysconfig/sshd"
    owner: "root"
    group: "root"
    mode: "600"
    backup: "{{ sshd_backup }}"
  when:
    - sshd_sysconfig | bool
    - __sshd_sysconfig_supports_use_strong_rng or __sshd_sysconfig_supports_crypto_policy
  notify: sshd_restart

- name: Check FIPS mode
  ansible.builtin.include_tasks: check_fips.yml
  when:
    - __sshd_hostkeys_nofips | d([]) | length > 0

- name: Make sure hostkeys are available and have expected permissions
  vars: &share_vars
              # 'MAo=' evaluates to '0\n' in base 64 encoding, which is default
    __sshd_fips_mode: >-
      {{ __sshd_hostkeys_nofips | d([]) | length > 0 and
         (__sshd_kernel_fips_mode.content | d('MAo=') | b64decode | trim == '1' or
          __sshd_userspace_fips_mode.content | d('MAo=') | b64decode | trim != '0') }}
  # This mimics the macro body_option() in sshd_config.j2
  # The explicit to_json filter is needed for Python 2 compatibility
    __sshd_hostkeys_from_config: >-
      {% if sshd_HostKey is defined %}
        {{ sshd_HostKey | to_json }}
      {% elif __sshd_config['HostKey'] is defined %}
        {{ __sshd_config['HostKey'] | to_json }}
      {% elif __sshd_defaults['HostKey'] is defined and not sshd_skip_defaults %}
        {% if __sshd_fips_mode %}
          {{ __sshd_defaults['HostKey'] | difference(__sshd_hostkeys_nofips) | to_json }}
        {% else %}
          {{ __sshd_defaults['HostKey'] | to_json }}
        {% endif %}
      {% else %}
        {{ [] | to_json }}
      {% endif %}
    __sshd_verify_hostkeys: >-
      {% if not sshd_verify_hostkeys %}
        {{ [] | to_json }}
      {% elif sshd_verify_hostkeys == 'auto' %}
        {% if not __sshd_hostkeys_from_config | from_json %}
          {% if __sshd_fips_mode %}
            {{ __sshd_verify_hostkeys_default | difference(__sshd_hostkeys_nofips) | to_json }}
          {% else %}
            {{ __sshd_verify_hostkeys_default | to_json }}
          {% endif %}
        {% elif __sshd_hostkeys_from_config | from_json is string %}
          {{ [__sshd_hostkeys_from_config | from_json] | to_json }}
        {% else %}
          {{ __sshd_hostkeys_from_config }}
        {% endif %}
      {% else %}
        {{ sshd_verify_hostkeys | to_json }}
      {% endif %}
  block:
    - name: Make sure hostkeys are available
      ansible.builtin.shell: |
        set -eu
        if set -o | grep pipefail 2>&1 /dev/null ; then
          set -o pipefail
        fi
        {% if sshd_sysconfig %}
          source /etc/sysconfig/sshd
        {% endif %}
        ssh-keygen -q -t {{ item | regex_search('(rsa|dsa|ecdsa|ed25519)') }} -f {{ item | quote }} -C '' -N ''
      args:
        creates: "{{ item }}"
      loop: "{{ __sshd_verify_hostkeys | from_json | list }}"
      changed_when: false

    - name: Make sure private hostkeys have expected permissions
      ansible.builtin.file:
        path: "{{ item }}"
        owner: "{{ sshd_hostkey_owner }}"
        group: "{{ sshd_hostkey_group }}"
        mode: "{{ sshd_hostkey_mode }}"
      loop: "{{ __sshd_verify_hostkeys | from_json | list }}"

- name: Apply configuration
  vars:
    <<: *share_vars
  block:
    - name: Create a temporary hostkey for syntax verification if needed
      ansible.builtin.tempfile:
        state: directory
      register: sshd_test_hostkey
      changed_when: false
      when:
        - __sshd_hostkeys_from_config | from_json == []
        - __sshd_supports_validate

    - name: Generate temporary hostkey
      ansible.builtin.command: >
        ssh-keygen -q -t rsa -f {{ sshd_test_hostkey.path | quote }}/rsa_key -C '' -N ''
      changed_when: false
      when: sshd_test_hostkey.path is defined

    - name: Make sure sshd runtime directory is present
      ansible.builtin.file:
        path: "/run/{{ __sshd_runtime_directory }}"
        state: directory
        owner: root
        group: root
        mode: "{{ __sshd_runtime_directory_mode }}"
      when:
        - __sshd_runtime_directory is not none

    - name: Find SSHD ports
      ansible.builtin.include_tasks: find_ports.yml

    - name: Configure firewall
      ansible.builtin.include_tasks: firewall.yml
      when:
        - sshd_manage_firewall | bool
        - ansible_facts['os_family'] == 'RedHat'
        - ansible_facts['distribution_version'] is version('7', '>=')
        - ansible_facts['virtualization_type'] | default(None) not in __sshd_skip_virt_env

    - name: Configure selinux
      ansible.builtin.include_tasks: selinux.yml
      when:
        - sshd_manage_selinux | bool
        - ansible_facts['os_family'] == 'RedHat'
        - ansible_facts['virtualization_type'] | default(None) not in __sshd_skip_virt_env

    - name: Create the complete configuration file
      ansible.builtin.include_tasks: install_config.yml
      when: sshd_config_namespace is none

    - name: Update configuration file snippet
      ansible.builtin.include_tasks: install_namespace.yml
      when: sshd_config_namespace is not none

    - name: Configure sshd to use SSH certificates
      ansible.builtin.include_tasks: certificates.yml
      when: sshd_trusted_user_ca_keys_list != []

  rescue:
    - name: Re-raise the error
      ansible.builtin.fail:
        msg: "{{ ansible_failed_result }}"
  always:
    - name: Remove temporary host keys
      ansible.builtin.file:
        path: "{{ sshd_test_hostkey.path }}"
        state: absent
      changed_when: false
      when: sshd_test_hostkey.path is defined

- name: Install and start systemd service
  ansible.builtin.include_tasks: install_service.yml

- name: Register that this role has run
  ansible.builtin.set_fact:
    sshd_has_run: true
  when: sshd_has_run is not defined
