Ansible firewalld & ufw Modules: Manage Firewall Rules (Complete Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible firewalld and ufw modules. Open ports, allow services, manage zones, configure rich rules, set up NAT, and manage firewall state.
Ansible manages Linux firewalls through two modules: ansible.posix.firewalld for RHEL/CentOS/Fedora (firewalld) and community.general.ufw for Ubuntu/Debian (UFW). Both let you open ports, allow services, and manage firewall state idempotently.
firewalld Module (RHEL/CentOS/Fedora)
Open a Port
- name: Open HTTP port
ansible.posix.firewalld:
port: 80/tcp
permanent: true
immediate: true
state: enabled
- name: Open multiple ports
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
immediate: true
state: enabled
loop:
- 80/tcp
- 443/tcp
- 8080/tcp
Allow a Service
- name: Allow HTTPS service
ansible.posix.firewalld:
service: https
permanent: true
immediate: true
state: enabled
# List available services: firewall-cmd --get-services
- name: Allow common services
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
immediate: true
state: enabled
loop:
- http
- https
- ssh
- postgresql
Manage Zones
# Add interface to zone
- name: Assign eth1 to internal zone
ansible.posix.firewalld:
zone: internal
interface: eth1
permanent: true
immediate: true
state: enabled
# Allow service in specific zone
- name: Allow PostgreSQL in internal zone
ansible.posix.firewalld:
service: postgresql
zone: internal
permanent: true
immediate: true
state: enabled
Rich Rules
# Allow from specific IP
- name: Allow monitoring server
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="10.0.1.100" port port="9090" protocol="tcp" accept'
permanent: true
immediate: true
state: enabled
# Rate limit SSH
- name: Rate limit SSH connections
ansible.posix.firewalld:
rich_rule: 'rule service name="ssh" accept limit value="10/m"'
permanent: true
immediate: true
state: enabled
# Block an IP
- name: Block malicious IP
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="192.168.1.100" reject'
permanent: true
immediate: true
state: enabled
Port Forwarding
- name: Forward port 8080 to 80
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" forward-port port="8080" protocol="tcp" to-port="80"'
permanent: true
immediate: true
state: enabled
Source-Based Rules
- name: Allow subnet
ansible.posix.firewalld:
source: 10.0.0.0/24
zone: trusted
permanent: true
immediate: true
state: enabled
See also: Ansible firewalld Module: Open Firewall Ports on RHEL/CentOS (Examples)
UFW Module (Ubuntu/Debian)
Open a Port
- name: Allow SSH
community.general.ufw:
rule: allow
port: '22'
proto: tcp
- name: Allow HTTP and HTTPS
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- '80'
- '443'
Enable UFW
# Default deny incoming, allow outgoing
- name: Set default policies
community.general.ufw:
default: deny
direction: incoming
- name: Allow outgoing
community.general.ufw:
default: allow
direction: outgoing
- name: Enable UFW
community.general.ufw:
state: enabled
Allow from Specific IP
- name: Allow from monitoring server
community.general.ufw:
rule: allow
from_ip: 10.0.1.100
port: '9090'
proto: tcp
# Allow entire subnet
- name: Allow internal network
community.general.ufw:
rule: allow
from_ip: 10.0.0.0/24
Rate Limiting
# Limit SSH connections (deny if >6 attempts in 30 seconds)
- name: Rate limit SSH
community.general.ufw:
rule: limit
port: '22'
proto: tcp
Delete a Rule
- name: Remove rule
community.general.ufw:
rule: allow
port: '8080'
proto: tcp
delete: true
Application Profiles
# UFW supports application profiles
- name: Allow Nginx Full
community.general.ufw:
rule: allow
name: 'Nginx Full'
Complete Firewall Playbooks
RHEL Web Server
- name: Configure RHEL firewall
hosts: webservers
become: true
tasks:
- name: Ensure firewalld is running
ansible.builtin.service:
name: firewalld
state: started
enabled: true
- name: Allow web services
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
immediate: true
state: enabled
loop:
- http
- https
- name: Allow custom app port
ansible.posix.firewalld:
port: 8080/tcp
permanent: true
immediate: true
state: enabled
- name: Allow monitoring from internal
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="10.0.0.0/24" port port="9100" protocol="tcp" accept'
permanent: true
immediate: true
state: enabled
Ubuntu Web Server
- name: Configure Ubuntu firewall
hosts: webservers
become: true
tasks:
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Default deny incoming
community.general.ufw:
default: deny
direction: incoming
- name: Default allow outgoing
community.general.ufw:
default: allow
direction: outgoing
- name: Allow SSH
community.general.ufw:
rule: allow
port: '22'
proto: tcp
- name: Allow web traffic
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- '80'
- '443'
- name: Enable UFW
community.general.ufw:
state: enabled
See also: Ansible Network Automation: Configure Cisco, Arista, and Juniper at Scale
firewalld vs ufw Comparison
| Feature | firewalld | ufw |
|---------|-----------|-----|
| Distros | RHEL, CentOS, Fedora | Ubuntu, Debian |
| Zones | ✅ Yes | ❌ No |
| Rich rules | ✅ Yes | Limited |
| Application profiles | Limited | ✅ Yes |
| Collection | ansible.posix | community.general |
| Backend | nftables/iptables | iptables/nftables |
| permanent + immediate | Both needed | Auto-persistent |
FAQ
Why do I need both permanent and immediate in firewalld?
permanent: true saves the rule to survive reboots. immediate: true applies it now. Without immediate, the rule only takes effect after a firewall-cmd --reload or reboot. Always use both together.
How do I reset the firewall to defaults?
For UFW: community.general.ufw: state=reset. For firewalld: remove custom rules and reload. There's no single "reset" command, but you can set the default zone back to public and remove all custom rules.
Should I use firewalld or iptables directly?
Use firewalld (or ufw) — they provide a management layer over iptables/nftables with persistent rules, zones, and easier syntax. Direct iptables rules are harder to manage and don't persist by default.
How do I check what's currently allowed?
- name: List open ports (firewalld)
ansible.builtin.command: firewall-cmd --list-all
register: fw_status
changed_when: false
- name: List rules (ufw)
ansible.builtin.command: ufw status verbose
register: ufw_status
changed_when: false
See also: Ansible on Debian 11 Bullseye: UFW Firewall Automation Complete Guide
Conclusion
Use ansible.posix.firewalld on RHEL-family systems with zones and rich rules for fine-grained control. Use community.general.ufw on Ubuntu/Debian for simpler firewall management. Always set default deny policies, open only needed ports, and use source-based restrictions for internal services.
Related Articles
• Ansible service Module Guide • Ansible for Linux System Administration • Ansible Security Compliance GuideCategory: installation