Home / Alt manpages / firewalld.policies(5)

  • firewalld.policies(5)
  • File format
  • linux

Allow Outbound DNS with a firewalld Policy

Lock a server down tight and the first thing to break is usually name resolution, because the host cannot send its own DNS queries. This guide builds a firewalld policy that permits DNS service traffic from the local host to any destination zone, checks it is active, and removes it cleanly. It uses firewalld 2.1.1, the version installed on the reference machine, and takes about fifteen minutes.

  • You need firewalld running, the firewall-cmd client, and root or sudo access for the changes.
  • The first checks are read-only. The policy changes are permanent configuration changes.
  • Keep a way back in. Have a console or out-of-band recovery path open before you reload a production firewall.

1. Check the service and the DNS definition

Confirm that the daemon is reachable and that you are using the installed command:

$ command -v firewall-cmd
/usr/bin/firewall-cmd
$ firewall-cmd --state
running
$ firewall-cmd --version
2.1.1

The version output shows the installed firewalld release. If --state does not print running, stop here and fix the service through your normal system-management process. Do not build a policy against a daemon you cannot inspect.

Check that the service name used by the example exists:

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

dns is a firewalld service definition, not a hostname. It stands for the ports defined by that service file. If it is missing, inspect the installed service definitions before substituting a service or adding raw ports.

2. Get the direction right

A policy filters traffic between an ingress zone set and an egress zone set. It is one-way: a policy from A to B does not automatically apply from B to A.

This example uses the two symbolic zones designed for this case:

  • HOST in the ingress set means traffic originating from the machine running firewalld.
  • ANY in the egress set means traffic destined for any regular zone. It does not include HOST.

So HOST to ANY is an outbound policy. It does not permit inbound DNS requests to a DNS server on this machine, and it does not cover traffic between two regular zones.

A policy is active only when both zone sets contain a valid entry and any regular zones named there are active. Symbolic zones avoid the regular-zone attachment requirement here.

Inspect the policies already known to the daemon and the ones currently active:

$ firewall-cmd --get-policies
$ firewall-cmd --get-active-policies

Warning

Do not reuse an existing policy name. The example name is client-dns. Policy names may contain letters, numbers, underscores and hyphens, and the policy file name is currently limited to 17 characters.

3. Prepare a small policy file

Save this as /tmp/client-dns.xml on the target machine. Creating the temporary file is an ordinary file operation. The later import is the privileged step.

<?xml version="1.0" encoding="utf-8"?>
<policy target="CONTINUE">
    <ingress-zone name="HOST"/>
    <egress-zone name="ANY"/>
    <short>Outbound DNS</short>
    <description>Permit DNS service traffic from this host to any egress zone.</description>
    <service name="dns"/>
</policy>

What the file says:

  • CONTINUE is the default non-terminal target. The policy does not decide the result for packets that do not match its service entry.
  • The service element adds the named firewalld service.
  • It is deliberately narrow. No masquerading, forwarding, arbitrary protocols or default accept rule.

Check the file before importing it. This only parses the XML; it does not alter firewalld:

$ xmllint --noout /tmp/client-dns.xml
$ sed -n '1,20p' /tmp/client-dns.xml

Tip

If xmllint is not installed, read the file carefully and use firewall-cmd --check-config after the import. Do not paste untrusted XML into a privileged shell command.

4. Import it into permanent configuration

Warning

Confirm that client-dns is not an existing policy you need to keep. This command writes permanent configuration and requires elevation.

$ sudo firewall-cmd --permanent --new-policy-from-file=/tmp/client-dns.xml --name=client-dns
success

The import does not make the policy active in the current runtime configuration. Verify the permanent copy and its direction:

$ sudo firewall-cmd --permanent --info-policy=client-dns
$ sudo firewall-cmd --permanent --policy=client-dns --list-ingress-zones
HOST
$ sudo firewall-cmd --permanent --policy=client-dns --list-egress-zones
ANY
$ sudo firewall-cmd --permanent --policy=client-dns --list-services
dns

Checkpoint

The policy name, both symbolic zones and the dns service are present. The info output may include other fields and formatting on your installation.

If any check is wrong, do not reload. Correct the source file and remove the imported policy before trying again, or use the individual policy options deliberately.

5. Validate and activate the policy

Ask firewalld to validate its permanent configuration:

$ sudo firewall-cmd --check-config
success

Now reload the firewall so the permanent configuration becomes the runtime configuration:

$ sudo firewall-cmd --reload
success

A normal reload keeps connection state, but it discards runtime-only changes that were never saved permanently. That is why the earlier --permanent checks matter.

Warning

Do not use --complete-reload for this task. The installed documentation warns that it loses state and can terminate active connections.

Confirm that the policy is now active:

$ firewall-cmd --get-active-policies
client-dns

If client-dns is not listed, query the policy again. Check that firewalld is running, that the ingress and egress entries were imported, and that the symbolic names are spelt exactly. An inactive policy has no effect.

6. Test the narrow result

Use a DNS client your host already has configured and test a normal lookup:

$ getent hosts example.com
93.184.216.34   example.com

The address and even the number of returned lines vary, so treat any valid resolver response as host-specific evidence rather than a fixed expected string. This test checks the machine's resolver path, not every possible DNS implementation.

To inspect the policy's configured service without changing it, run:

$ firewall-cmd --policy=client-dns --list-all

Warning

A successful lookup does not prove all outbound traffic is allowed. The policy contains only the dns service and has a non-terminal target. Other policies, zones, routing and stateful return traffic still determine the complete result.

7. Roll back without guessing

If this was a test or the policy is no longer required, remove exactly the policy you created.

Destructive action

This deletes the policy's permanent definition, so save any configuration you still need first.

$ sudo firewall-cmd --permanent --info-policy=client-dns
$ sudo firewall-cmd --permanent --delete-policy=client-dns
success
$ sudo firewall-cmd --check-config
success
$ sudo firewall-cmd --reload
success

Then verify that it is gone from the active list:

$ firewall-cmd --get-active-policies

The output should no longer contain client-dns. Remove /tmp/client-dns.xml separately when you have finished reviewing it.

Recovery

If you changed an existing policy instead of creating this example, do not use this deletion sequence. Restore the saved XML or reverse the specific service and zone changes instead.

Done means

  • The service is ready. firewalld 2.1.1 is running and the dns service exists.
  • The direction is right. The policy runs from HOST ingress to ANY egress.
  • The policy is narrow. The permanent policy contains only the intended dns service.
  • Validation passed. firewall-cmd --check-config and --reload completed successfully.
  • It is enforcing. --get-active-policies lists the policy when it should be enforcing.
  • You have a way back. You have a tested rollback path and have not confused outbound DNS with inbound service access or unrestricted egress.