---
# Linux-specific firewall configuration (UFW) with security hardening

# Install and configure fail2ban for SSH brute-force protection
- name: Install fail2ban
  ansible.builtin.apt:
    name: fail2ban
    state: present
    update_cache: true

- name: Configure fail2ban for SSH protection
  ansible.builtin.copy:
    dest: /etc/fail2ban/jail.local
    owner: root
    group: root
    mode: '0644'
    content: |
      # OpenClaw security hardening - SSH protection
      [DEFAULT]
      bantime = 3600
      findtime = 600
      maxretry = 5
      backend = systemd

      [sshd]
      enabled = true
      port = ssh
      filter = sshd
      # logpath not needed - systemd backend reads from journal
  notify: Restart fail2ban

- name: Enable and start fail2ban
  ansible.builtin.systemd:
    name: fail2ban
    state: started
    enabled: true

# Install and configure unattended-upgrades for automatic security updates
- name: Install unattended-upgrades
  ansible.builtin.apt:
    name:
      - unattended-upgrades
      - apt-listchanges
    state: present

- name: Configure automatic security updates
  ansible.builtin.copy:
    dest: /etc/apt/apt.conf.d/20auto-upgrades
    owner: root
    group: root
    mode: '0644'
    content: |
      APT::Periodic::Update-Package-Lists "1";
      APT::Periodic::Unattended-Upgrade "1";
      APT::Periodic::AutocleanInterval "7";

- name: Configure unattended-upgrades to only install security updates
  ansible.builtin.copy:
    dest: /etc/apt/apt.conf.d/50unattended-upgrades
    owner: root
    group: root
    mode: '0644'
    content: |
      // OpenClaw security hardening - automatic security updates
      Unattended-Upgrade::Allowed-Origins {
          "${distro_id}:${distro_codename}-security";
          "${distro_id}ESMApps:${distro_codename}-apps-security";
          "${distro_id}ESM:${distro_codename}-infra-security";
      };
      Unattended-Upgrade::Package-Blacklist {
      };
      Unattended-Upgrade::AutoFixInterruptedDpkg "true";
      Unattended-Upgrade::MinimalSteps "true";
      Unattended-Upgrade::Remove-Unused-Dependencies "true";
      Unattended-Upgrade::Automatic-Reboot "false";

# UFW Firewall configuration
- name: Install UFW
  ansible.builtin.apt:
    name: ufw
    state: present
    update_cache: true

- name: Set UFW default policies
  community.general.ufw:
    direction: "{{ item.direction }}"
    policy: "{{ item.policy }}"
  loop:
    - { direction: 'incoming', policy: 'deny' }
    - { direction: 'outgoing', policy: 'allow' }
    - { direction: 'routed', policy: 'deny' }

- name: Allow SSH on port 22
  community.general.ufw:
    rule: allow
    port: '22'
    proto: tcp
    comment: 'SSH'

- name: Allow Tailscale UDP port 41641
  community.general.ufw:
    rule: allow
    port: '41641'
    proto: udp
    comment: 'Tailscale'
  when: tailscale_enabled | bool

- name: Get default network interface
  ansible.builtin.shell:
    cmd: |
      set -o pipefail
      ip route | grep default | awk '{print $5}' | head -n1
    executable: /bin/bash
  register: default_interface
  changed_when: false

- name: Validate default network interface was detected
  ansible.builtin.assert:
    that:
      - default_interface.stdout is defined
      - default_interface.stdout | length > 0
    fail_msg: "Failed to detect default network interface. Cannot configure firewall rules safely."
    success_msg: "Default network interface detected: {{ default_interface.stdout }}"

- name: Create UFW after.rules for Docker isolation
  ansible.builtin.blockinfile:
    path: /etc/ufw/after.rules
    marker: "# {mark} ANSIBLE MANAGED BLOCK - Docker isolation"
    insertbefore: "^COMMIT$"
    block: |
      # Docker port isolation - block all forwarded traffic by default
      :DOCKER-USER - [0:0]

      # Allow established connections
      -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

      # Allow localhost
      -A DOCKER-USER -i lo -j ACCEPT

      # Block all other forwarded traffic to Docker containers from external interface
      -A DOCKER-USER -i {{ default_interface.stdout }} -j DROP
    create: false

- name: Create Docker daemon config directory
  ansible.builtin.file:
    path: /etc/docker
    state: directory
    mode: '0755'

- name: Configure Docker daemon to work with UFW
  ansible.builtin.template:
    src: daemon.json.j2
    dest: /etc/docker/daemon.json
    owner: root
    group: root
    mode: '0644'
  notify: Restart docker

- name: Enable UFW
  community.general.ufw:
    state: enabled

- name: Reload UFW
  community.general.ufw:
    state: reloaded
