A firewalld direct rule bypasses the normal service, port, rich rule, and policy abstractions and writes a lower-level rule into the backend firewall. Use it only when a zone rule cannot express the packet match, because direct rules are harder to audit and current upstream documentation marks the direct interface as deprecated.
Direct rules require an address family, table, chain, priority, and backend-style rule arguments. The example adds an IPv4 rule to accept TCP port 8443 from one trusted host in the INPUT path, then checks the saved direct-rule inventory before relying on it.
Runtime and permanent direct rules are separate. Add a temporary runtime rule only for testing, add the permanent rule when it must survive a reload, and keep a replacement plan with a rich rule or policy whenever the same match can be represented through supported firewalld objects.
Related: Allow traffic from one source with a rich rule
Related: Open a permanent port in firewalld
Related: Check firewalld status
$ sudo firewall-cmd --state running
$ sudo firewall-cmd --zone=public --list-rich-rules rule family="ipv4" source address="10.77.0.10/32" port port="8443" protocol="tcp" accept
Prefer services, ports, rich rules, or policies when they can express the match. The direct interface is deprecated upstream and should be kept for cases that need backend-specific behavior.
$ sudo firewall-cmd --permanent --direct --get-all-rules
An empty result means no permanent direct rules are saved. Existing rules with the same family, table, chain, and low priority can run before the new rule.
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 8443 -s 10.77.0.10 -j ACCEPT success
The priority value sorts rules inside the selected chain. Lower numbers run earlier, so use a narrow source and destination match when adding a rule near the front of INPUT.
$ sudo firewall-cmd --check-config success
$ sudo firewall-cmd --reload success
A reload normally replaces runtime rules with permanent configuration. Direct runtime changes may be affected by FlushAllOnReload settings, so verify the final runtime inventory after reload.
$ sudo firewall-cmd --direct --get-all-rules ipv4 filter INPUT 0 -p tcp --dport 8443 -s 10.77.0.10 -j ACCEPT
$ sudo firewall-cmd --direct --query-rule ipv4 filter INPUT 0 -p tcp --dport 8443 -s 10.77.0.10 -j ACCEPT yes
$ nc -vz app01.example.net 8443 Connection to app01.example.net 8443 port [tcp/*] succeeded!
$ nc -vz -w 2 app01.example.net 8443 nc: connect to app01.example.net port 8443 (tcp) failed: Connection timed out
If the unmatched source still connects, look for a broader service, port, rich rule, source-zone assignment, policy, or earlier direct rule that allows the same traffic.