Open a Service Safely with firewalld

Firewalld sits between your new listener and the network, and opening a service in it safely means testing before anything is written to disk. You will finish with a tested rule that allows one predefined service in one zone, then save it so it survives a reload and reboot. The examples use firewalld 2.1.1, installed here as package version 2.1.1-1.

Allow about fifteen minutes. You need shell access, the firewall-cmd client and a service that is already listening on the host. Most commands need elevated privileges because they contact the daemon. This guide does not start firewalld, change SSH settings, or replace a host's network policy without a test.

1. Check the daemon before changing anything

Start with read-only checks. They do not need sudo on every installation, although a restricted system may require it:

$ firewall-cmd --state
$ firewall-cmd --get-default-zone
$ firewall-cmd --get-active-zones

--state should print running when the daemon is available. The other commands show the default zone and any interfaces or sources currently bound to zones. If the state is not running, stop here: starting a firewall service is an operational change that can affect remote access, and it should follow the host's service-management procedure.

Checkpoint: record the default zone and the interface carrying your administration session. Do not assume that public is the active zone just because it is the documented default in /etc/firewalld/firewalld.conf.

2. Inspect the target zone

Replace ZONE with the zone shown by the previous checks. This is still a read-only operation:

$ ZONE=public
$ firewall-cmd --zone="$ZONE" --list-all

Look at the interfaces, sources, services and ports lines. A zone is the trust level assigned to connections, interfaces or sources. Opening a service in the wrong zone does not reliably open it for the client you care about, and may expose it on a different interface.

Choose a predefined service name rather than copying a raw port when one exists. http, for example, represents the firewalld service definition for HTTP. Confirm the name is installed before making a change:

$ firewall-cmd --get-services | tr ' ' '\n' | grep '^http$'

If this prints http, the service definition is available. If it prints nothing, inspect firewall-cmd --info-service=http and check the installed service definitions before inventing a replacement.

3. Make a runtime-only test change

Opening a firewall is security-sensitive. Confirm the application is listening, confirm you have a second way back into the host, and confirm your maintenance window covers a failed test. Then add the service to the active configuration:

$ sudo firewall-cmd --zone="$ZONE" --add-service=http
success

This changes the runtime configuration only: it is effective now, but it is not automatically written to the permanent configuration. The output should be success; a non-zero exit status or an error means you should investigate before continuing.

Verify the rule and the listener separately:

$ firewall-cmd --zone="$ZONE" --query-service=http
yes
$ ss -ltnp | grep ':80\b'

The first command checks firewalld's runtime state. The second checks whether a local TCP listener is actually present. A firewall rule cannot make an application listen, and a listener does not imply that the firewall permits traffic. Test from the intended client as well.

4. Undo a failed runtime test

If the test is wrong, remove the runtime rule. This does not edit the saved configuration:

$ sudo firewall-cmd --zone="$ZONE" --remove-service=http
success
$ firewall-cmd --zone="$ZONE" --query-service=http
no

Warning: do not use --complete-reload as a general undo button. The client documentation warns that it can terminate active connections because it loses connection state. A normal --reload also replaces runtime settings with permanent settings, so it is a useful recovery only when the permanent configuration is the known-good version.

5. Save the verified runtime configuration

Once the service works from the right network and the exposure is intended, save the active configuration:

$ sudo firewall-cmd --runtime-to-permanent
success
$ sudo firewall-cmd --check-config

--runtime-to-permanent overwrites the permanent configuration with the current runtime configuration. That is convenient, but it can also save unrelated runtime changes made by another administrator. If you need a narrowly scoped change, use matching commands with and without --permanent instead, after checking the existing state.

For the explicit two-command form, the first command applies the rule now and the second records it for a later reload:

$ sudo firewall-cmd --zone="$ZONE" --add-service=http
$ sudo firewall-cmd --permanent --zone="$ZONE" --add-service=http
$ firewall-cmd --permanent --zone="$ZONE" --query-service=http
yes

Do not blindly run both forms. Choose either runtime-to-permanent after a deliberate test, or the paired commands when you have reviewed the exact change.

6. Understand the configuration file before editing it

The basic daemon configuration is /etc/firewalld/firewalld.conf. The shipped fallback files live under /usr/lib/firewalld; do not edit those, because package updates can replace them. Administrator-owned customisations belong under /etc/firewalld and override the fallback configuration.

The settings most likely to explain surprising behaviour:

Editing this file requires root and can change how the daemon handles all traffic. Make one change at a time, keep a root shell open, validate the file, and reload only when you have an out-of-band recovery path. To undo a change, restore the previous value and reload. Do not remove the whole configuration directory.

7. Recheck after a reload

A reload makes the permanent configuration the new runtime configuration. Run it only after checking that the saved configuration is valid:

$ sudo firewall-cmd --check-config
$ sudo firewall-cmd --reload
success
$ firewall-cmd --zone="$ZONE" --query-service=http
yes

If the final query says no, the rule was not saved in the zone you tested, or the variable names a different zone. Re-run --get-active-zones and compare both runtime and permanent output with --list-all.

Recovery: if the configuration is broken, return to the last known-good permanent files or use your system's configuration backup, then reload during a controlled maintenance window.

Done means