--- - name: Install UFW ansible.builtin.apt: name: ufw state: present tags: [firewall, ufw] - name: Set UFW default policies community.general.ufw: direction: "{{ item.direction }}" policy: "{{ item.policy }}" loop: - { direction: incoming, policy: "{{ ufw_default_incoming }}" } - { direction: outgoing, policy: "{{ ufw_default_outgoing }}" } tags: [firewall, ufw] - name: Allow OpenSSH (rate-limited) from LAN community.general.ufw: rule: limit port: "22" proto: tcp src: "{{ lan_subnet }}" comment: "SSH from LAN" tags: [firewall, ufw] - name: Allow OpenSSH from anywhere (optional) community.general.ufw: rule: limit port: "22" proto: tcp comment: "SSH world" when: ufw_allow_ssh_from_anywhere | bool tags: [firewall, ufw] - name: Allow stack service ports from LAN community.general.ufw: rule: allow port: "{{ item.port | string }}" proto: tcp src: "{{ lan_subnet }}" comment: "{{ item.comment }}" loop: "{{ ufw_lan_tcp_ports }}" # SSH already handled above with limit; skip duplicate 22 if listed when: item.port | int != 22 tags: [firewall, ufw] - name: Allow Docker published ports on loopback freely (implicit) ansible.builtin.debug: msg: "UFW will leave local/docker internal traffic alone; LAN rules cover published WebUIs." tags: [firewall, ufw] # Docker + UFW: without this, published ports can bypass UFW on some setups. # Route forwarded packets through ufw-before-forward is complex; we rely on # binding services to LAN + ufw allow from lan_subnet. Documented in README. - name: Enable UFW community.general.ufw: state: enabled when: ufw_enabled | bool tags: [firewall, ufw] - name: Show UFW status ansible.builtin.command: ufw status verbose register: ufw_status changed_when: false tags: [firewall, ufw] - name: Print UFW status ansible.builtin.debug: msg: "{{ ufw_status.stdout_lines }}" tags: [firewall, ufw]