LFCS 108 ๐ง Firewalld โ Rich Rules and Services
The previous chapter introduced services and rich rules as the mechanisms for controlling traffic within a zone. This chapter goes deeper into both. A service is not just a name that opens a portโit is an XML definition that can specify multiple ports, protocols, kernel modules, and destination addresses. A rich rule is not just a syntax variant of --add-serviceโit is a language with priorities, logging, rate limiting, and source restrictions that the simple service and port commands cannot express. The LFCS exam tests both layers: the ability to read and create service definitions, and the ability to write rich rules that solve problems the basic commands cannot.
Key point: A service definition is an XML file under /usr/lib/firewalld/services/ (default) or /etc/firewalld/services/ (custom). A rich rule is a single string passed to --add-rich-rule that can combine source, destination, service or port, logging, rate limiting, and an action. The syntax is order-sensitive and quote-sensitive.
Why service definitions and rich rules exist
The multi-port service problem. A single application often requires more than one port. An FTP server needs TCP port 21 for control and a range of ports for data transfer. A SIP server needs UDP 5060 and TCP 5061. Opening each port individually with --add-port is error-prone and undocumented. A service definition groups all the required ports and protocols under a single name. Adding the service to a zone opens every port it defines, in one command .
The kernel module problem. Some services require kernel modules to be loaded before they function correctly. A NetBIOS service needs nf_conntrack_netbios_ns. A service definition can specify the module name, and firewalld loads it when the service is added to a zone . The --add-port command cannot do this. The service definition is the only mechanism that ties a kernel module to a firewall rule.
The source restriction problem. --add-service=ssh opens SSH to the entire world. If the requirement is “allow SSH only from the 10.0.0.0/8 subnet,” the simple service command cannot express it. A rich rule with a source address condition is the only way to restrict a service to a specific source .
The rate limiting problem. A public-facing SSH server will be scanned and brute-forced. --add-service=ssh accepts every connection attempt. A rich rule with limit value="4/m" accepts only four connection attempts per minute from any given source, which dramatically slows brute-force attacks .
The logging problem. When a packet is dropped, the administrator may need to know why. The drop action in a simple rule is silent. A rich rule can add a log clause before the action, writing a message to the kernel log with a custom prefix .
The trade-off. Service definitions are static XML files. They must be created before they can be used, and they are not modified by firewall-cmd commands. Rich rules are dynamic but fragile: a single syntax error causes the command to fail, and the error message is often unhelpful. The exam tests both, and the correct choice depends on whether the requirement can be expressed with a service or requires the flexibility of a rich rule.
a. Service definitions: XML structure and custom services
A service definition is an XML file that describes the ports, protocols, modules, and destination addresses required by a service. The file lives in /usr/lib/firewalld/services/ for default services and /etc/firewalld/services/ for custom services. A custom service with the same name as a default service overrides the default .
The structure of a service file is straightforward. The <service> root element contains one or more <port> elements, optional <protocol> elements, optional <module> elements, and an optional <destination> element.
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>My Custom App</short>
<description>Custom application on ports 8080 and 8443</description>
<port port="8080" protocol="tcp"/>
<port port="8443" protocol="tcp"/>
<module name="nf_conntrack_myapp"/>
</service>
The <port> element is the most common. The port attribute accepts a single port number or a range (8080-8090). The protocol attribute accepts tcp, udp, sctp, or dccp . The <module> element specifies a kernel module that must be loaded for the service to function. The <destination> element restricts the service to a specific destination address, which is used for multicast services .
To create a custom service, write the XML file to /etc/firewalld/services/ and run firewall-cmd --reload. The service name is the filename without the .xml extension. It can then be added to a zone with --add-service=myapp .
The --get-services command lists every service definition available on the system, including custom services. The --info-service=<name> command shows the details of a specific service, including its ports, protocols, and modules.
b. Rich rules: syntax, priorities, and actions
A rich rule is a structured string that combines conditions and an action. The general syntax is defined in the firewalld.richlanguage man page . The order of elements matters: rule, optional family and priority, optional source and destination, the match condition (service or port), optional log, and the action (accept, reject, or drop).
# Allow SSH from a specific subnet
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" service name="ssh" accept' --permanent
The family attribute must be specified when a source or destination address is used. The source address condition restricts the rule to packets originating from that address or subnet. The service name condition matches the service definition. The accept action allows the packet .
The priority attribute controls the order of evaluation. Lower numbers are evaluated first. A rule with a negative priority is evaluated before the zone’s default services and ports. A rule with a positive priority is evaluated after them. This allows a drop rule for a specific source to be evaluated before a general accept rule for the same service .
# Drop all traffic from a specific IP before any accept rules
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-10" source address="192.168.1.100" drop' --permanent
The log element writes a message to the kernel log when the rule matches. The prefix attribute sets the log message prefix, and the level attribute sets the syslog level. The limit attribute restricts how often the log is written, which prevents log flooding .
# Log and accept HTTP from a specific subnet
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" service name="http" log prefix="HTTP-ALLOWED: " level="info" accept' --permanent
The limit value="N/m" syntax sets a rate limit. The N is the number of matches, and m is the time unit (s, m, h, or d). A rule with limit value="4/m" matches at most four times per minute. This is used for rate limiting SSH connections or for preventing log flooding .
c. Combining services and rich rules for common scenarios
The exam may present a scenario that requires both a service definition and a rich rule. For example: “Allow MySQL (port 3306) only from the application server at 10.0.1.50.” The port is defined in the mysql service, but the source restriction requires a rich rule.
# Allow MySQL only from the app server
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.1.50" port port="3306" protocol="tcp" accept' --permanent
The rich rule uses port port="3306" protocol="tcp" instead of service name="mysql". Both forms are valid, but the port form is more explicit when the service definition does not exist or when a different port is required.
Another common scenario: “Allow SSH from the internal subnet, but log and reject SSH from everywhere else.” This requires two rich rules with different priorities.
# Allow SSH from internal subnet
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-100" source address="10.0.1.0/24" service name="ssh" accept' --permanent
# Log and reject SSH from everywhere else
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-99" service name="ssh" log prefix="SSH-BLOCKED: " level="warning" limit value="5/m" reject' --permanent
The priority="-100" rule is evaluated first. If the source is in the internal subnet, the packet is accepted. If not, the priority="-99" rule is evaluated, which logs and rejects the packet. Without the priority attribute, the order would be determined by the action, and the reject rule might be evaluated before the accept rule, blocking legitimate traffic .
Complete Example Session
# ============================================
# PART 1: LIST AVAILABLE SERVICES
# ============================================
firewall-cmd --get-services
# Output includes ssh, http, https, mysql, ...
# ============================================
# PART 2: INSPECT A SERVICE DEFINITION
# ============================================
firewall-cmd --info-service=ssh
# Output: ssh
# ports: 22/tcp
# protocols:
# source-ports:
# modules:
# ============================================
# PART 3: CREATE A CUSTOM SERVICE
# ============================================
sudo tee /etc/firewalld/services/myapp.xml <<'EOF'
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>My Custom App</short>
<port port="8080" protocol="tcp"/>
<port port="8443" protocol="tcp"/>
</service>
EOF
sudo firewall-cmd --reload
firewall-cmd --get-services | grep myapp
# Output: myapp
# ============================================
# PART 4: ADD THE CUSTOM SERVICE
# ============================================
sudo firewall-cmd --zone=public --add-service=myapp --permanent
sudo firewall-cmd --reload
firewall-cmd --zone=public --list-services
# Output includes myapp
# ============================================
# PART 5: ADD A SOURCE-RESTRICTED RICH RULE
# ============================================
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" service name="ssh" accept' --permanent
sudo firewall-cmd --reload
# ============================================
# PART 6: ADD A RATE-LIMITING RICH RULE
# ============================================
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" service name="ssh" limit value="4/m" accept' --permanent
sudo firewall-cmd --reload
# ============================================
# PART 7: ADD A LOGGING RICH RULE
# ============================================
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.200" log prefix="DROPPED: " level="warning" limit value="5/m" drop' --permanent
sudo firewall-cmd --reload
# ============================================
# PART 8: ADD A PRIORITY-BASED RICH RULE
# ============================================
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-10" source address="203.0.113.0/24" drop' --permanent
sudo firewall-cmd --reload
# ============================================
# PART 9: LIST RICH RULES
# ============================================
firewall-cmd --zone=public --list-rich-rules
# Output shows all rich rules with their conditions and actions.
The nine parts trace the workflow: listing services, inspecting a definition, creating a custom service, adding it to a zone, adding source-restricted, rate-limiting, logging, and priority-based rich rules, and listing them.
Quick Reference
Service Definition Elements
| Element | Attributes | Purpose |
|---|---|---|
<port> | port, protocol | Open a port or range |
<protocol> | value | Allow a protocol (e.g., igmp) |
<module> | name | Load kernel module |
<destination> | ipv4, ipv6 | Restrict to destination |
<short> | (text) | Human-readable name |
<description> | (text) | Detailed description |
Rich Rule Structure
| Element | Example | Purpose |
|---|---|---|
rule | rule | Start of rule |
family | family="ipv4" | IPv4 or IPv6 |
priority | priority="-10" | Evaluation order (lower first) |
source address | source address="10.0.0.0/8" | Source restriction |
service name | service name="ssh" | Match a service |
port port protocol | port port="3306" protocol="tcp" | Match a port |
log | log prefix="X: " level="info" | Log matched packets |
limit value | limit value="4/m" | Rate limit |
accept / reject / drop | accept | Action |
Commands
| Command | Purpose |
|---|---|
firewall-cmd --get-services | List all service definitions |
firewall-cmd --info-service=<name> | Show service details |
firewall-cmd --add-rich-rule='...' | Add a rich rule |
firewall-cmd --remove-rich-rule='...' | Remove a rich rule |
firewall-cmd --list-rich-rules | List rich rules in zone |
Best Practices
โ Do This:
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept' --permanent
<!-- Use source restriction for services --> # โ
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" service name="ssh" limit value="4/m" accept' --permanent
<!-- Rate limit to slow brute force --> # โ
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-10" source address="203.0.113.0/24" drop' --permanent
<!-- Use priority to ensure drop before accept --> # โ
โ Don’t Do This:
# Use --add-service=ssh for a public-facing server
<!-- Opens SSH to the entire internet --> # โ
# Forget --permanent on rich rules
<!-- Rule is lost on reload --> # โ
# Assume rich rule syntax is forgiving
<!-- A single quote error breaks the rule --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Rich rule rejected | Syntax error | Verify quoting and keyword order |
| Rule not evaluated first | No priority attribute | Use priority with negative value |
| Rate limit not working | Existing accept rule | Remove broader accept or adjust priority |
| Custom service not recognized | Wrong directory or no reload | Place in /etc/firewalld/services/, reload |
| Source address ignored | Missing family | Add family="ipv4" or "ipv6" |
Real-World Examples
1. List Services
firewall-cmd --get-services
2. Inspect Service
firewall-cmd --info-service=http
3. Create Custom Service
sudo tee /etc/firewalld/services/myapp.xml <<'EOF'
<service>
<port port="8080" protocol="tcp"/>
</service>
EOF
4. Source-Restricted SSH
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept' --permanent
5. Rate-Limited SSH
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" service name="ssh" limit value="4/m" accept' --permanent
6. Log and Drop
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.200" log prefix="DROPPED: " limit value="5/m" drop' --permanent
7. Priority Drop
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" priority="-10" source address="203.0.113.0/24" drop' --permanent
8. Allow MySQL from App Server
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.1.50" port port="3306" protocol="tcp" accept' --permanent
9. List Rich Rules
firewall-cmd --zone=public --list-rich-rules
10. Remove Rich Rule
sudo firewall-cmd --zone=public --remove-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept' --permanent
Visual
Service Definition Structure
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ /etc/firewalld/services/myapp.xml โ
โ โ
โ <service> โ
โ <short>My App</short> โ
โ <port port="8080" protocol="tcp"/> โ
โ <port port="8443" protocol="tcp"/> โ
โ <module name="nf_conntrack_myapp"/> โ
โ </service> โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ firewall-cmd --reload
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Service "myapp" available โ
โ --add-service=myapp opens both ports โ
โ and loads the kernel module. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Rich Rule Evaluation Order
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Incoming packet โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Priority < 0 rules (pre-chain) โ
โ e.g., priority="-10" drop 203.0.113.0/24 โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Zone default rules (services, ports) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Priority > 0 rules (post-chain) โ
โ e.g., priority="32767" log unexpected โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Lower priority numbers are evaluated first. The pre-chain is the correct place for blocking rules.
Summary
| Item | Value |
|---|---|
| Service definition location | /etc/firewalld/services/ |
| Service file extension | .xml |
| Port element | <port port="8080" protocol="tcp"/> |
| Rich rule syntax | rule [family="..."] [priority="..."] [source ...] [service/port ...] [log ...] [action] |
| Source restriction | source address="10.0.0.0/8" |
| Rate limiting | limit value="4/m" |
| Logging | log prefix="X: " level="info" |
| Priority | Negative = before defaults, positive = after |
| Action | accept, reject, drop |
Key takeaways:
- A service definition groups ports, protocols, and modules. The XML file is placed in
/etc/firewalld/services/and the service is available after a reload. This is the only way to tie a kernel module to a firewall rule . - Rich rules express what services cannot. Source restrictions, rate limiting, and logging are only possible with rich rules. The syntax is order-sensitive and quote-sensitive .
- Priority controls evaluation order. A rule with a negative priority is evaluated before the zone’s default services and ports. This is how a
droprule is placed before anacceptrule . - Rate limiting slows brute force. The
limit value="4/m"syntax accepts at most four connection attempts per minute. Without a broader accept rule, the excess attempts fall through to the zone’s default policy . - Logging requires a limit. An unlimited log rule on a scanned port fills the disk. The
limitattribute must be included in any logging rule .
Remember: The exam tests the syntax, not just the concept. A rich rule with a missing quote or a misspelled keyword fails. A service definition with a malformed XML fails on reload. The safe workflow is to test in runtime, verify with --list-all, then save with --runtime-to-permanent. The syntax is strict, but the diagnostic tools are available.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!