Zones alone cannot express "internal may talk out to external, but only on HTTPS and DNS", and that gap is what firewalld policies fill. This guide builds a small permanent policy file, checks it, reloads firewalld and confirms the policy is visible. The example uses firewalld 2.1.1, installed here as package version 2.1.1-1, and takes about 20 minutes.
Read the local manual page and identify the command paths before editing anything. These are ordinary, read-only commands:
$ man firewalld.policy
$ firewall-cmd --version
FirewallD is not running
$ dpkg-query -W -f='${Package} ${Version}\n' firewalld
firewalld 2.1.1-1
The version command can print a warning when the daemon is stopped. That is not a version mismatch. The package query is the useful version check on this Debian-style system; on another distribution, use its package tool instead.
A few facts about policy files:
policy_name.xml. The installed manual limits policy_name to 17 characters, so this guide uses lab-egress./etc/firewalld/policies. Files in /usr/lib/firewalld/policies are supplied defaults./etc. A package update will not overwrite your changes there.Confirm the directory and the zones you intend to connect:
$ sudo install -d -m 0755 /etc/firewalld/policies
$ firewall-cmd --get-zones
block dmz drop external home internal public trusted work
Checkpoint: the directory exists and the zone list contains the two zones you plan to use. The zone output is host-specific, so replace internal and external below only if your host uses different existing zone names. A policy is directional: ingress internal to egress external is not the reverse direction.
Before a privileged change, check whether this policy already exists and keep any file with the same name. The commands below read the configuration and create a recoverable backup:
$ sudo test -e /etc/firewalld/policies/lab-egress.xml && \
sudo cp --preserve=all /etc/firewalld/policies/lab-egress.xml \
/etc/firewalld/policies/lab-egress.xml.bak || true
$ sudo ls -l /etc/firewalld/policies
If the file already exists, inspect the backup before you carry on. Do not overwrite an unfamiliar production policy just to make this example fit. If you decide not to continue, remove only the backup you just made after confirming its path, or leave it for later recovery.
Now create the policy as root. This is the first state-changing step. The XML has one policy element, two zone bindings and two port entries. CONTINUE is the default target, so packets that do not match these entries are left for the rest of firewalld's policy evaluation.
$ sudo tee /etc/firewalld/policies/lab-egress.xml >/dev/null <<'XML'
<?xml version="1.0" encoding="utf-8"?>
<policy target="CONTINUE">
<short>Lab egress</short>
<description>Allow web and DNS traffic from the internal lab to the external zone.</description>
<ingress-zone name="internal"/>
<egress-zone name="external"/>
<port port="443" protocol="tcp"/>
<port port="53" protocol="udp"/>
</policy>
XML
$ sudo sed -n '1,80p' /etc/firewalld/policies/lab-egress.xml
tee needs elevation because the destination is under /etc.Checkpoint: the sed output is the same XML you supplied. If the file is empty or truncated, stop and restore the backup before doing anything that reloads firewalld.
Ask firewalld to check its permanent configuration. This is read-only with respect to the running rules, but it may need root access to read all the configuration files:
$ sudo firewall-cmd --check-config
success
If it reports an XML or policy error, do not reload. Check these:
--get-zones shows them.A valid XML document can still describe a policy that matches no traffic if its zone bindings are wrong.
Checkpoint: continue only after you see success. A non-zero exit status means the configuration is not ready for activation.
Warning: reloading is the second state-changing step and can briefly disrupt connections. Run it during a suitable maintenance window.
$ sudo firewall-cmd --reload
success
$ firewall-cmd --get-policies
lab-egress
$ firewall-cmd --policy=lab-egress --list-ingress-zones
internal
$ firewall-cmd --policy=lab-egress --list-egress-zones
external
$ firewall-cmd --policy=lab-egress --list-ports
443:tcp 53:udp
The exact ordering and whitespace can differ. What matters is that lab-egress is listed, both zone bindings are present, and both ports appear. If a command reports that the daemon is not running, start or enable firewalld according to your distribution's service policy before treating that as a policy-file error.
These checks confirm that firewalld loaded the policy. They do not prove that an application can reach a particular host. Test from a machine in the ingress zone, using a destination and route that really belong to the egress zone. A connection test can also be affected by the destination service, routing, DNS or another policy.
Warning: warn users or operators before rolling back, because the reload can again interrupt connections.
If this example replaced an existing file, restore its backup. If it was a new file, remove only this policy file:
$ if sudo test -e /etc/firewalld/policies/lab-egress.xml.bak; then
sudo cp --preserve=all /etc/firewalld/policies/lab-egress.xml.bak \
/etc/firewalld/policies/lab-egress.xml
else
sudo rm -- /etc/firewalld/policies/lab-egress.xml
fi
$ sudo firewall-cmd --check-config
$ sudo firewall-cmd --reload
Do not use a broad recursive removal under /etc/firewalld. The backup copy is the undo path for an existing policy. Once the restored configuration has been checked and reloaded, verify that firewall-cmd --get-policies no longer shows the temporary policy if it was newly created.
/usr/lib/firewalld/policies is a packaged default, not the normal place for a local override.CONTINUE is not a blanket rule. target="CONTINUE" is neither allow nor deny. Use the policy's rules and the rest of the firewall configuration to understand unmatched traffic.firewall-cmd may not be the file you are inspecting.ACCEPT, REJECT or DROP in place of CONTINUE decides what happens to unmatched packets in the policy and can block traffic beyond the two examples above.firewalld.policy(5) syntax./etc/firewalld/policies.firewall-cmd --check-config returned success before the reload.firewall-cmd, or you applied the documented rollback.