Automation can help increase efficiency, save time and improve consistency, which is why Red Hat Enterprise Linux (RHEL) includes features that help automate many tasks. RHEL System Roles are a collection of Ansible content that helps provide more consistent workflows and streamline the execution of many manual tasks.
Firewalls are an important aspect to system security as they control network traffic flow on systems. This can range from basic firewall operations to allow traffic on a certain port to more advanced functionality using multiple zones. The RHEL firewall can be used for other actions as well, such as port forwarding. For more information on the RHEL firewall, see the using and configuring firewalld documentation.
RHEL 9 and RHEL 8.6 introduce a new firewall system role for automating the management of the RHEL firewall. This role can automate tasks such as opening ports, creating and configuring zones and forwarding ports.
In this article, I’ll show you how to use the firewall role to automate several common firewall related tasks.
A note about the ansible.posix.firewalld module
Many administrators using RHEL System Roles have relied on the ansible.posix.firewalld module to open ports in the firewall for system roles that involve network services (such as opening port 80 in the firewall for Tang when using the nbde_server role, or opening the pmcd service in the firewall when using the metrics role).
In RHEL 9 and 8.6 Ansible Core is being introduced which does not include the ansible.posix.firewalld module. For more information on these changes, see updates to using Ansible in RHEL 8.6 and 9.0.
Existing playbooks utilizing the ansible.posix.firewalld module may be impacted. For example, if you try to run a playbook that calls this module with Ansible Core in RHEL 8.6 or 9, it may fail with an error message similar to the following:
ERROR! couldn't resolve module/action 'firewalld'. This often indicates a misspelling, missing collection, or incorrect module path. The error appears to be in '/home/ansible/metrics.yml': line 4, column 7, but may be elsewhere in the file depending on the exact syntax problem. The offending line appears to be: tasks: - firewalld: ^ here
If you are using RHEL System Roles with Red Hat Ansible Automation Platform, you can either update your playbooks to use the firewall system role, or you can continue to use ansible.posix.firewalld which is available as part of the posix collection on Automation Hub.
If you are using RHEL System Roles without Ansible Automation Platform, you’ll need to update your playbooks to use the firewall system role.
In my example environment, I have a control node system named rhel9-controlnode.example.com and four managed nodes: rhel9-server1.example.com, rhel9-server2.example.com, rhel8-server1.example.com, and rhel8-server2.example.com. The three servers with rhel9 in the name are running RHEL 9 beta, and the two servers with rhel8 in the name are running RHEL 8.6 beta.
I’ll demonstrate automating several tasks using the firewall system role:
Adding a service in the firewall on the managed nodes
Removing a service from the firewall on the managed nodes
Completing several tasks at once:
Creating a new zone
Opening services in the firewall by name as well as by port number
Configuring the zone source to allow connections from two IP addresses
Configuring port forwarding on the managed nodes
Writing the playbook
Like other RHEL system roles, the desired firewall configuration is specified using role variables. In each of the examples, I’ll define an inventory file that contains my list of managed nodes and role variables. For more information on the available role variables for the firewall role, refer to the role README.md file at /usr/share/doc/rhel-system-roles/firewall/README.md
I’ll start by creating the playbook file named firewall.yml with the following content:
- name: Configure firewall hosts: all roles: - redhat.rhel_system_roles.firewall
This playbook simply calls the firewall system role. Because my role variables will be defined in inventory files, this same playbook will be used in all of the examples.
If you are using Ansible automation controller as your control node, you can import this Ansible playbook into Red Hat Ansible Automation Platform by creating a Project, following the documentation provided here. It is very common to use Git repos to store Ansible playbooks. Ansible Automation Platform stores automation in units called Jobs which contain the playbook, credentials and inventory. Create a Job Template following the documentation here.
Adding a service in the firewall
I’ll start by covering a basic, but common task: adding a service in the firewall. I would like to open the cockpit service in the firewall on all four of my managed nodes.
Let’s define an inventory file named inventory_add_cockpit_service.yml with the following content:
all: hosts: rhel9-server1.example.com: rhel9-server2.example.com: rhel8-server1.example.com: rhel8-server2.example.com: vars: firewall: - service: cockpit state: enabled
If using Ansible automation controller as your control node, this Inventory can be imported into Red Hat Ansible Automation Platform via an SCM project (example GitHub or GitLab) or using the awx-manage Utility as specified in the documentation.
This inventory file lists my four managed nodes, and defines a firewall role variable that specifies the cockpit service should be enabled. By default, the firewall role applies the change to both the running configuration as well as the permanent configuration (this behavior can be changed by using the runtime and permanent role variables)
I can run the playbook using this inventory file with the following command:
$ ansible-playbook -i inventory_add_cockpit_service.yml -b firewall.yml
If you are using Ansible automation controller as your control node, you can launch the job from the automation controller web interface.
Removing a service in the firewall
In the next example, I’ll show how to remove the cockpit service from the firewall on the managed nodes. The only change I need to make to the previous inventory file is changing the state from enabled to disabled:
$ cp inventory_add_cockpit_service.yml inventory_remove_cockpit_service.yml $ sed -i 's/enabled/disabled/' inventory_remove_cockpit_service.yml
After running the playbook with the following command, the cockpit service will be removed from the firewall on the four managed nodes:
$ ansible-playbook -i inventory_remove_cockpit_service.yml -b firewall.yml
Creating a custom firewall zone
Next, I’ll show how to create a custom firewall zone, named my-custom-zone. In this zone, I’ll add the cockpit, pmcd and ssh services by name, and specify a port number to add (TCP port 80). In addition, I’ll specify that the zone source setting should be set to two IP addresses I specify. This will cause traffic originating from these two IP addresses to be directed to this zone. For more information on the zone source setting, refer to the using zones to manage incoming traffic depending on a source documentation. Also keep in mind that in RHEL 9 the firewalld service does not allow packet transmission between two different zones.
I’ll define a new inventory file named inventory_custom_zone.yml with the following content:
all: hosts: rhel9-server1.example.com: rhel9-server2.example.com: rhel8-server1.example.com: rhel8-server2.example.com: vars: firewall: - zone: my-custom-zone state: present - source: - 192.168.0.74 - 10.0.0.233 zone: my-custom-zone service: [cockpit, pmcd, ssh] port: ['80/tcp'] state: enabled
In this example, I have two items defined under the firewall list. The first item creates the new zone (named my-custom-zone). The second item specifies my desired configuration for this zone.
I can run the playbook with the following command:
$ ansible-playbook -i inventory_custom_zone.yml -b firewall.yml
I can validate the zone configuration by running the following command from my control node:
$ ssh rhel8-server1.example.com sudo firewall-cmd --zone my-custom-zone --list-all my-custom-zone (active) target: default icmp-block-inversion: no interfaces: sources: 192.168.0.74 10.0.0.233 services: cockpit pmcd ssh ports: 80/tcp protocols: forward: no masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
Configuring port forwarding
In the final example, I’ll configure TCP port 443 to be forwarded to port 9090 within the previously created my-custom-zone. This will make the Web Console service available on the standard HTTPS port 443 in addition to port 9090.
I’ll define a new inventory file named inventory_port_forwarding.yml with the following content:
all: hosts: rhel9-server1.example.com: rhel9-server2.example.com: rhel8-server1.example.com: rhel8-server2.example.com: vars: firewall: - zone: my-custom-zone state: present - source: - 192.168.0.74 - 10.0.0.233 zone: my-custom-zone forward_port: '443/tcp;9090;' service: [cockpit, pmcd, ssh] port: ['80/tcp'] state: enabled
This inventory file matches the previous example, except for the line added to enable the port forwarding.
I can run the playbook with the following command:
$ ansible-playbook -i inventory_port_forwarding.yml -b firewall.yml
From one of the IP addresses listed in the zone source, I can now connect to the Web Console on port 443 or port 9090.
The firewall RHEL System Role can help you more quickly and consistently implement the firewall configuration across your RHEL environment.
Red Hat offers many RHEL System Roles that can help automate other important aspects of your RHEL environment. To explore additional roles, review the list of available RHEL System Roles and start managing your RHEL servers in a more efficient, consistent and automated manner today.
Want to learn more about the Red Hat Ansible Automation Platform? Check out our e-book, The automation architect's handbook.
About the author
Brian Smith is a Product Manager at Red Hat focused on RHEL automation and management. He has been at Red Hat since 2018, previously working with Public Sector customers as a Technical Account Manager (TAM).