Configuring IPv6 Firewall Rules with firewalld on RHEL
IPv6 is no longer optional for Australian networks. ISPs like Telstra and Optus, along with AARNet, now hand out IPv6 prefixes alongside IPv4, and administrators running RHEL 9 or CentOS Stream must keep their firewall in step with both protocol families.
firewalld, the default firewall daemon on RHEL-based distributions, governs IPv4 and IPv6 traffic from a single interface. The challenge lies in understanding how firewalld decides which family a rule targets and ensuring IPv6 is fully enabled across the kernel and the daemon.
A misconfigured IPv6 stack leaves services silently exposed on dual-stack hosts. The ACSC has repeatedly flagged this as a recurring weakness for Australian businesses, whether they host web applications in Sydney, run mail gateways in Brisbane, or operate edge devices in Perth.
Verifying IPv6 Support in firewalld and the Kernel
Begin by confirming the kernel has IPv6 enabled. On RHEL-based systems, sysctl net.ipv6.conf.all.disable_ipv6 should read 0, and /proc/net/if_inet6 should list at least the loopback entry.
Run firewall-cmd --state and inspect the active zones with firewall-cmd --get-active-zones. Every zone accepts both IPv4 and IPv6 addresses by default, but the default zone's behaviour can be tightened once you understand the rule families.
The following comparison highlights the most useful commands when configuring IPv6 firewall rules with firewalld versus their IPv4 counterparts.
| Task | IPv4 command | IPv6 command |
|---|---|---|
| List all rules | firewall-cmd --list-all |
firewall-cmd --list-all (shows both) |
| Allow service permanently | firewall-cmd --add-service=http --permanent |
identical syntax, applies to both families |
| Restrict to IPv6 only | ipset:ipv4-net |
family="ipv6" in rich rules |
| Reload runtime config | firewall-cmd --reload |
identical |
| Check active bindings | ss -tlnp |
ss -tln6 |
Most commands are family-agnostic; the distinction surfaces only when filtering IPv6 specifically or verifying listening sockets separately.
Designing IPv6-Aware Zones and Services
In multi-tenant cloud environments, particularly Sydney and Melbourne data centres, hosts often need IPv6 rules that differ from their IPv4 ones. A public-facing web service might accept HTTP from any IPv4 address but only from specific IPv6 prefixes belonging to a content delivery partner.
The cleanest approach assigns the affected interface to a dedicated zone, then adds services and rich rules accordingly. Use firewall-cmd --new-zone=publicv6 --permanent and configure it with family=ipv6 for strict isolation. Always reload after structural changes so runtime state matches the permanent configuration.
Application-level configuration complements the firewall work. Tutorials like configuring Apache Virtual Hosts on RHEL 9 walk through binding NameVirtualHost entries to specific IPv6 addresses, which pairs neatly with this rule structure.
Crafting Permanent Rules with firewall-cmd
Permanent storage keeps rules intact across reboots, important for unattended servers in branch offices from Adelaide to Darwin. To open port 8443 only on IPv6, combine a rich rule with --add-rich-rule:
firewall-cmd --add-rich-rule='rule family=ipv6 port port=8443 protocol=tcp accept' --permanent
After any --permanent change, run firewall-cmd --reload to push the configuration into the running daemon. During maintenance windows, edit runtime rules instead, then settle them with --runtime-to-permanent to avoid service interruptions.
Block-by-block testing beats blanket changes. Use firewall-cmd --zone=public --list-rich-rules before and after each adjustment to audit what is actually live.
Rich Rules, Address Matching, and Forwarding
Rich rules accept source and destination addresses, plus logging prefixes. A typical dual-stack audit rule looks like:
firewall-cmd --add-rich-rule='rule family=ipv6 source address=2001:db8::/32 service name=ssh log prefix="ssh6" level=info limit value=5/m accept' --permanent
For routers and gateways, enable masquerading with firewall-cmd --add-masquerade and pair it with IPv6 source-specific routes. Confirm the kernel has net.ipv6.conf.all.forwarding=1 when masquerading, or traffic silently drops.
The address arithmetic often catches junior engineers off guard. Working through subnet math, prefix lengths, and hexadecimal conversions in a calmer environment, perhaps through a guide like building a calculator app, reinforces the same Boolean logic that these rules depend on.
Auditing, Logging, and Long-Term Maintenance
Audit logs should run early in any deployment so anomalies surface before they become incidents. Pair broad denylog capture during initial rollouts with selective logging once services stabilise, and run periodic nmap and nmap -6 sweeps from a jump host in a different Australian state to confirm only the expected ports respond.
Treat the IPv6 stack like any other production dependency: version-control zone XML, mirror rules across sites, and rehearse failure scenarios. Mirror configuration files between Brisbane, Sydney, and Perth nodes with the same tooling so a single source of truth drives every region's firewall.
Routine Practices for IPv6 Rule Hygiene
- Enable
--log-denied=allinfirewalld.confduring initial rollouts, then disable verbose logging once normal behaviour is documented. - Schedule quarterly reviews of every zone using
firewall-cmd --list-all-zonesand diff the output against a stored baseline. - Pin rich rules to specific
/64prefixes for partner networks rather than blanket-allowing regional allocations. - Mirror permanent rules across hosts with configuration management so nodes in different states stay aligned.
- Forward audit logs to a central syslog server, ideally an Australian-hosted SIEM aligned with ACSC logging expectations.
- Re-test failover paths after every IPv6 routing change; the absence of NAT makes asymmetric failures behave differently than with IPv4.
- Document each rule's business owner so the next administrator inherits context, not surprises.
A reliable IPv6 firewall setup on RHEL comes from discipline rather than exotic syntax: confirm the kernel, decide which family each rule targets, test in runtime before going permanent, and keep rich rules as narrow as possible. Australian administrators who provision IPv6 alongside IPv4 from day one avoid the late-night incident calls that follow silent dual-stack exposure, and the same baseline hygiene lines up neatly with the ACSC Essential Eight maturity model.