---
- name: Harden Amazon Linux 2023 EC2 hosts
  hosts: env_production
  become: true
  gather_facts: true

  handlers:
    - name: Restart journald
      ansible.builtin.systemd_service:
        name: systemd-journald
        state: restarted

  tasks:
    - name: Preflight | Verify the OS family is supported
      ansible.builtin.assert:
        that:
          - ansible_distribution in ['Amazon', 'RedHat']
        fail_msg: "This playbook assumes Amazon Linux or a RHEL-compatible host. Split distro-specific tasks before widening scope."

    - name: Baseline | Install baseline packages
      ansible.builtin.dnf:
        name:
          - audit
          - chrony
          - dnf-automatic
          - rsyslog
          - sudo
          - amazon-ssm-agent
        state: present
      tags: [baseline, patching]

    - name: Baseline | Apply latest package updates
      ansible.builtin.dnf:
        name: "*"
        state: latest
        update_only: true
      tags: [baseline, patching]

    - name: Baseline | Enable automatic update timer
      ansible.builtin.systemd_service:
        name: dnf-automatic.timer
        enabled: true
        state: started
      when: automatic_updates_enabled | bool
      tags: [baseline, patching]

    - name: Identity | Create ops-admin group
      ansible.builtin.group:
        name: ops-admin
        state: present
      tags: [identity]

    - name: Identity | Create opsadmin user
      ansible.builtin.user:
        name: opsadmin
        groups: ops-admin,wheel
        append: true
        create_home: true
        shell: /bin/bash
        state: present
      tags: [identity]

    - name: Identity | Install authorized key for opsadmin
      ansible.posix.authorized_key:
        user: opsadmin
        state: present
        key: "{{ opsadmin_public_key }}"
      tags: [identity]

    - name: Identity | Create sudoers drop-in for ops-admin
      ansible.builtin.copy:
        dest: /etc/sudoers.d/90-ops-admin
        owner: root
        group: root
        mode: '0440'
        content: |
          %ops-admin ALL=(ALL) ALL
        validate: '/usr/sbin/visudo -cf %s'
      tags: [identity]

    - name: SSH | Disable password authentication
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PasswordAuthentication'
        line: 'PasswordAuthentication no'
        backup: true
      tags: [ssh_hardening]

    - name: SSH | Disable direct root login over SSH
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin no'
        backup: true
      tags: [ssh_hardening]

    - name: SSH | Limit SSH auth attempts
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?MaxAuthTries'
        line: 'MaxAuthTries 3'
        backup: true
      tags: [ssh_hardening]

    - name: SSH | Reload sshd safely
      ansible.builtin.systemd_service:
        name: sshd
        state: reloaded
      tags: [ssh_hardening]

    - name: Logging | Ensure chronyd is enabled and started
      ansible.builtin.systemd_service:
        name: "{{ chrony_service_name }}"
        enabled: true
        state: started
      tags: [logging]

    - name: Logging | Ensure rsyslog is enabled and started
      ansible.builtin.systemd_service:
        name: rsyslog
        enabled: true
        state: started
      tags: [logging]

    - name: Logging | Ensure auditd is enabled and started
      ansible.builtin.systemd_service:
        name: auditd
        enabled: true
        state: started
      tags: [logging]

    - name: Logging | Set journald retention cap
      ansible.builtin.lineinfile:
        path: /etc/systemd/journald.conf
        regexp: '^#?SystemMaxUse='
        line: 'SystemMaxUse={{ journald_max_use }}'
        backup: true
      notify: Restart journald
      tags: [logging]

    - name: Kernel | Apply sysctl hardening values
      ansible.posix.sysctl:
        name: "{{ item.name }}"
        value: "{{ item.value }}"
        state: present
        reload: true
      loop:
        - { name: 'net.ipv4.conf.all.accept_redirects', value: '0' }
        - { name: 'net.ipv4.conf.default.accept_redirects', value: '0' }
        - { name: 'net.ipv4.conf.all.send_redirects', value: '0' }
        - { name: 'net.ipv4.conf.default.send_redirects', value: '0' }
        - { name: 'net.ipv4.conf.all.rp_filter', value: '1' }
        - { name: 'kernel.randomize_va_space', value: '2' }
      tags: [kernel]

    - name: Platform | Ensure amazon-ssm-agent is enabled and started
      ansible.builtin.systemd_service:
        name: amazon-ssm-agent
        enabled: true
        state: started
      tags: [platform]

    - name: Platform | Require IMDSv2 for the current EC2 instance
      ansible.builtin.command:
        cmd: >-
          aws ec2 modify-instance-metadata-options
          --instance-id {{ ec2_instance_id }}
          --http-tokens required
          --http-endpoint enabled
      delegate_to: localhost
      when: apply_imdsv2 | bool
      changed_when: true
      tags: [platform]
