Whitelist source IP addresses in CentOS 7
I'd accomplish this by adding sources to a zone. First checkout which sources there are for your zone:
firewall-cmd --permanent --zone=public --list-sources
If there are none, you can start to add them, this is your "whitelist"
firewall-cmd --permanent --zone=public --add-source=192.168.100.0/24
firewall-cmd --permanent --zone=public --add-source=192.168.222.123/32
(That adds a whole /24
and a single IP, just so you have a reference for both a subnet and a single IP)
Set the range of ports you'd like open:
firewall-cmd --permanent --zone=public --add-port=1-22/tcp
firewall-cmd --permanent --zone=public --add-port=1-22/udp
This just does ports 1 through 22. You can widen this, if you'd like.
Now, reload what you've done.
firewall-cmd --reload
And check your work:
firewall-cmd --zone=public --list-all
Side note / editorial: It doesn't matter but I like the "trusted" zone for a white-listed set of IPs in firewalld. You can make a further assessment by reading redhat's suggestions on choosing a zone.
See also:
- RHEL 7 using Firewalls article
- Fedora FirewallD docs (fairly good, fedora's been using firewalld for some while)
If you'd like to DROP
packets outside this source, here's an example for dropping those outside the /24
I used as an example earlier, you can use rich rules for this, I believe. This is conceptual, I have not tested it (further than seeing that centos 7 accepts the command), but, should be easy enough to do a pcap and see if it behaves how you'd expect
firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.100.0/24" invert="True" drop'
Even if an answer has been accepted and up-voted, I do not think it is a correct one. I fail to find clear explanation in the documentation, but from the implemented behaviour it looks like that:
- interface and source are used as selectors - which zone(s) to activate
- both are ignored for the default zone (always active)
So the answer would be:
- lock down the default zone, say "public" - no ports open or services available
- in another zone, say "work" - define source and open ports
For example, assuming default zone is public and has no open ports, add source and port range to "work" zone:
$ sudo firewall-cmd --zone=work --add-source=192.168.0.0/24
$ sudo firewall-cmd --zone=work --add-port=8080-8090/tcp
now check the active zones (default zone is always active):
$ sudo firewall-cmd --get-active-zones
you'll get:
work
sources: 192.168.0.0/24
so "work" zone rules will apply to the particular subnet. You will have a range of open ports for the "whitelist" = subnet as requested. And of course use --permanent
option in --add-xxx
statements to make the behaviour stick.
In turn any ports or services you have in "public" (default) zone will apply to all interfaces and source addresses.
$ sudo firewall-cmd --list-all-zones
public (default)
interfaces:
sources:
services:
ports:
masquerade: no
forward-ports:
icmp-blocks:
rich rules:
work (active)
interfaces:
sources: 192.168.0.0/24
services: dhcpv6-client ipp-client ssh
ports: 8080-8090/tcp
masquerade: no
forward-ports:
icmp-blocks:
rich rules:
The same system works for interfaces. Say by adding interface "ens3" to "work" zone:
$ sudo firewall-cmd --zone=work --add-interface=ens3
you will use the "work" zone rules to any requests from the particular interface - more rough selector than "source".
Disclaimer: I haven't actually tried what I'm suggesting, here, but it's fairly close to the last firewalld setup I did, so I'm going off of that. Firewalld provides you with a few pre-configured zones, just for this purpose. There's one called "drop", which drops anything coming in, and one called "trusted", which allows any connection (ie, so you shouldn't even need to open individual ports, I think). The trick is getting the right zone to trigger for what you want.
Firewalld will apply the rules for a zone based upon the following precedence:
- If the source IP matches a source IP bound to a zone, it uses that.
- If the source IP doesn't match any particular zone, it checks to see if there's a zone configured for the interface the packet came in on. If there is one, it uses that.
- Lastly, if nothing else matches, it uses the default zone.
So, first off, you want to bind your trusted IP's to the "trusted" zone:
firewall-cmd --permanent --zone=trusted --add-source=1.2.3.4
Then, either set your default zone to "drop" or bind your interface to it:
firewall-cmd --permanent --set-default-zone=drop
firewall-cmd --permanent --zone=drop --change-interface=eth0
and then make the changes take effect (warning: this will probably drop your connection if you're doing this over the network and you didn't add your source IP to the trusted zone):
firewall-cmd --reload
Of course, you can also just test these temporarily by omitting the "--permanent" (and then you don't have to --reload, either).