Introduction
Ansible manages Linux firewalls through dedicated modules: ansible.posix.firewalld for RHEL/CentOS/Fedora and community.general.ufw for Ubuntu/Debian. Open ports, allow services, configure zones, and ensure firewall rules persist across reboots.
firewalld (RHEL/CentOS/Fedora)
Open a Port
# Open HTTP port
- name: Allow HTTP
ansible.posix.firewalld:
port: 80/tcp
permanent: true
state: enabled
immediate: true
# Open HTTPS
- name: Allow HTTPS
ansible.posix.firewalld:
port: 443/tcp
permanent: true
state: enabled
immediate: true
# Open custom port
- name: Allow app port
ansible.posix.firewalld:
port: 8080/tcp
permanent: true
state: enabled
immediate: true
Allow a Service
# Allow by service name
- name: Allow SSH
ansible.posix.firewalld:
service: ssh
permanent: true
state: enabled
immediate: true
# Allow multiple services
- name: Allow web services
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
immediate: true
loop:
- http
- https
- ssh
Zone Management
# Set default zone
- name: Set default zone to public
ansible.builtin.command: firewall-cmd --set-default-zone=public
# Add interface to zone
- name: Add eth0 to public zone
ansible.posix.firewalld:
zone: public
interface: eth0
permanent: true
state: enabled
immediate: true
# Open port in specific zone
- name: Allow port in internal zone
ansible.posix.firewalld:
zone: internal
port: 5432/tcp
permanent: true
state: enabled
immediate: true
# Allow source IP range in zone
- name: Trust internal network
ansible.posix.firewalld:
zone: trusted
source: 10.0.0.0/24
permanent: true
state: enabled
immediate: true
Rich Rules
# Allow specific IP to a port
- name: Allow monitoring server
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="10.0.0.50" port port="9090" protocol="tcp" accept'
permanent: true
state: enabled
immediate: true
# Rate limit SSH
- name: Rate limit SSH connections
ansible.posix.firewalld:
rich_rule: 'rule service name="ssh" accept limit value="10/m"'
permanent: true
state: enabled
immediate: true
Close Ports / Remove Rules
- name: Close port
ansible.posix.firewalld:
port: 8080/tcp
permanent: true
state: disabled
immediate: true
- name: Remove service
ansible.posix.firewalld:
service: http
permanent: true
state: disabled
immediate: true
UFW (Ubuntu/Debian)
Open a Port
# Enable UFW
- name: Enable UFW
community.general.ufw:
state: enabled
policy: deny # Default deny incoming
# Allow SSH (always do this before enabling!)
- name: Allow SSH
community.general.ufw:
rule: allow
port: "22"
proto: tcp
# Allow HTTP/HTTPS
- name: Allow web traffic
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "80"
- "443"
# Allow custom port
- name: Allow app port
community.general.ufw:
rule: allow
port: "8080"
proto: tcp
Allow from Specific IPs
# Allow from specific IP
- name: Allow monitoring server
community.general.ufw:
rule: allow
src: 10.0.0.50
port: "9090"
proto: tcp
# Allow from subnet
- name: Allow internal network
community.general.ufw:
rule: allow
src: 10.0.0.0/24
# Allow specific IP to specific port
- name: Allow DB access from app server
community.general.ufw:
rule: allow
src: 10.0.0.10
port: "5432"
proto: tcp
Rate Limiting
# Rate limit SSH (UFW built-in)
- name: Rate limit SSH
community.general.ufw:
rule: limit
port: "22"
proto: tcp
Deny Traffic
# Deny specific port
- name: Block port 23 (telnet)
community.general.ufw:
rule: deny
port: "23"
proto: tcp
# Deny from IP
- name: Block attacker IP
community.general.ufw:
rule: deny
src: 203.0.113.100
Delete Rules
- name: Remove rule
community.general.ufw:
rule: allow
port: "8080"
proto: tcp
delete: true
Practical Patterns
Web Server Firewall (firewalld)
---
- name: Configure web server 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
state: enabled
immediate: true
loop:
- http
- https
- ssh
- name: Allow app port from internal only
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="10.0.0.0/24" port port="{{ app_port }}" protocol="tcp" accept'
permanent: true
state: enabled
immediate: true
Web Server Firewall (UFW)
---
- name: Configure web server firewall
hosts: webservers
become: true
tasks:
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Allow SSH (before enabling!)
community.general.ufw:
rule: allow
port: "22"
proto: tcp
- name: Allow web ports
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: ["80", "443"]
- name: Allow app port from internal
community.general.ufw:
rule: allow
src: 10.0.0.0/24
port: "{{ app_port }}"
proto: tcp
- name: Enable UFW with default deny
community.general.ufw:
state: enabled
policy: deny
Database Server Lockdown
- name: Only allow app servers to reach DB
ansible.posix.firewalld:
rich_rule: 'rule family="ipv4" source address="{{ item }}" port port="5432" protocol="tcp" accept'
permanent: true
state: enabled
immediate: true
loop: "{{ groups['appservers'] | map('extract', hostvars, 'ansible_host') }}"
firewalld Parameters
| Parameter | Description |
|---|---|
port | Port/protocol (e.g., 80/tcp) |
service | Service name (e.g., http) |
zone | Firewall zone |
source | Source IP/CIDR |
interface | Network interface |
rich_rule | Rich rule string |
permanent | Persist across reboots |
immediate | Apply immediately |
state | enabled or disabled |
Troubleshooting
"Locked out of SSH"
Always allow SSH before enabling the firewall:
# Order matters!
- ufw: { rule: allow, port: "22", proto: tcp }
- ufw: { state: enabled, policy: deny }
Rules Not Persisting
For firewalld, set permanent: true AND immediate: true:
- firewalld:
port: 80/tcp
permanent: true # Survives reboot
immediate: true # Applies now
state: enabled
Related Articles
Conclusion
Use ansible.posix.firewalld on RHEL/CentOS and community.general.ufw on Ubuntu/Debian. Always allow SSH before enabling a deny-all policy. Use permanent: true + immediate: true with firewalld. Use rich rules or source-based rules to restrict access to specific IPs. For database servers, only allow connections from application servers. Firewall configuration is a critical security baseline — automate it to ensure consistency.