Firewalld
IPsets with firewalld
On RHEL-family hosts — and any distribution that uses firewalld as its firewall
front-end — Knocknoc does not talk to firewalld directly. As with the
main IPSet backend,
Knocknoc orchestrates the kernel's native IPSets, and firewalld simply
references those sets in a static rule. The set is the source of truth; the
firewall rule never changes.
Prerequisites
- The Knocknoc agent is installed and enrolled — see Linux Agent Installation.
- The
ipsetpackage is present (dnf install ipset; useyumon RHEL 7). - You have reviewed the main IPSet page,
which this page extends for the
firewalldcase.
The rule that decides the whole design: the Knocknoc agent owns the IPSet,
and firewalld only references it. Never create the set with
firewall-cmd --new-ipset.
A firewall-cmd --reload flushes and rebuilds every IPSet that firewalld manages,
repopulating it from permanent configuration. That would wipe every live, time-limited
grant the agent has added — silently revoking everyone the next time the firewall is
reloaded by an administrator or a configuration-management run. Keeping the set outside
firewalld's ownership makes the reload harmless.
How Knocknoc creates the IPSets
This is the same mechanism documented on the
main IPSet page;
nothing about it changes for firewalld. The agent creates the sets on boot from a
list of names, one per line:
# /opt/knocknoc-agent/etc/ipset.list
# One ACL / set name per line.
knoc_ssh
knoc_https
For each name, the agent's startup service creates two sets:
| Set | Type | Purpose |
|---|---|---|
knoc_ssh |
hash:ip |
IPv4 grants |
knoc_ssh_v6 |
hash:ip family inet6 |
IPv6 grants |
firewalld plays no part in creating them — these are real kernel IPSets that any
firewall rule can match against. When a user logs in to Knocknoc and is authorised for
the relevant Knoc, the agent adds their source IP to the set; on revoke or expiry, it
removes the address.
Adding a set for firewalld to reference
Adding a set is a host-side change, not a firewalld change:
- Add the name to
/opt/knocknoc-agent/etc/ipset.list(one per line). - Restart the creator service so the sets exist immediately:
Enable it on boot if it is not already:systemctl restart create-ipsetssystemctl enable create-ipsets. - Verify that the IPv4 and IPv6 sets now exist:
They will be empty until someone is granted access — this is expected.ipset -L knoc_ssh ipset -L knoc_ssh_v6 - Configure the matching Knoc in the Knocknoc server using the same set name.
That is the entire "create" side. Everything below is the static firewalld reference.
firewalld reference examples
Knocknoc-managed sets are referenced from direct rules, not rich rules (see
Why direct rules, not rich rules). Direct rules are
stored in firewalld's permanent configuration and survive reloads, and they can match
a set that firewalld does not own.
Priority 0 is evaluated before 1. The pattern is always allow the set, then deny
the rest on the same port.
If the active zone's target already drops unsolicited traffic and the port is not opened as a service or port in that zone, the explicit catch-all
DROPrules below are optional. They are shown for clarity and for hosts whose default policy is not already deny.
1. SSH (IPv4)
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-p tcp --dport 22 -m set --match-set knoc_ssh src -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
-p tcp --dport 22 -j DROP
firewall-cmd --reload
2. SSH, dual-stack (IPv4 + IPv6)
Add the IPv6 rules against the agent's auto-created _v6 set:
firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 0 \
-p tcp --dport 22 -m set --match-set knoc_ssh_v6 src -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 1 \
-p tcp --dport 22 -j DROP
firewall-cmd --reload
3. HTTPS web application
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-p tcp --dport 443 -m set --match-set knoc_https src -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
-p tcp --dport 443 -j DROP
firewall-cmd --reload
4. Custom internal application on multiple ports
Add a custom set — for example app_admin — to ipset.list, restart create-ipsets,
then gate several ports at once with multiport:
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-p tcp -m multiport --dports 8443,9443 -m set --match-set app_admin src -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
-p tcp -m multiport --dports 8443,9443 -j DROP
firewall-cmd --reload
5. Dropping established sessions on revoke
By default the firewall stops new connections when an IP leaves the set; existing TCP
sessions can linger until they time out. To terminate them on revoke, use Knocknoc's
blocking-set pattern (the ipset_block.sh example on the
main IPSet page,
which manages the knocknoc_blocked / knocknoc_blocked_v6 sets). Add
knocknoc_blocked to ipset.list so the set exists on boot, point the Knoc backend at
the block script, then place the blocked-set DROP ahead of the allow rule:
# 0: anything in the blocked set is dropped first — terminates established flows on revoke
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \
-m set --match-set knocknoc_blocked src -j DROP
# 1: granted users allowed
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 \
-p tcp --dport 22 -m set --match-set knoc_ssh src -j ACCEPT
# 2: everyone else denied
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 2 \
-p tcp --dport 22 -j DROP
firewall-cmd --reload
As written, the blocked-set rule drops all traffic from a revoked source. To limit
it to a single service, scope it with -p tcp --dport 22 like the other rules.
Why direct rules, not rich rules
A rich rule may look like the obvious choice:
# Not suitable for Knocknoc-managed sets
firewall-cmd --permanent --add-rich-rule='rule source ipset="knoc_ssh" ... accept'
The source ipset= syntax only works if firewalld owns the set — that is, you
created it with firewall-cmd --new-ipset. That reintroduces the reload-flush problem
and hands set management back to firewalld, which conflicts with the agent. Direct
rules reference a set purely by name and do not require ownership, which is what this
design needs.
Common pitfalls
- Do not also open the port in the zone. If SSH is permitted as a normal service or
port in the active zone (
--add-service=ssh,--add-port=22/tcp), it is open to everyone and the gate is bypassed. The directACCEPTrule must be the only path in. - Do not run
--new-ipsetfor Knocknoc sets. The agent owns them. - RHEL 8/9/10 use the nftables backend. Direct rules route through
iptables-nft, and-m set --match-setmatches the IPSet via thext_setmodule — this works. Make sure theipsetpackage is installed. - Rule order matters. Allow before the catch-all deny; the blocked-set
DROPbefore the allow.
Verify and test
# Sets exist (empty until a grant)
ipset -L knoc_ssh
# Direct rules are in place
firewall-cmd --direct --get-all-rules
See also
- IPSet (Linux Netfilter/IPTables) — the main backend page
- IPsets with UFW
- IPsets with Shorewall
- Linux Agent Installation
- Grant duration
- IPSet at the Netfilter project: https://ipset.netfilter.org/