23

I want to set up CentOS 7 firewall such that, all the incoming requests will be blocked except from the originating IP addresses that I whitelist. And for the Whitelist IP addresses all the ports should be accessible.

I'm able to find few solutions (not sure whether they will work) for iptables but CentOS 7 uses firewalld. I can't find something similar to achieve with firewall-cmd command.

The interfaces are in Public Zone. I have also moved all the services to Public zone already.

44

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:


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'
  • Thanks a lot for answering. Can't vote up due to shortage in reputation. But how do I drop all other IP's except the one that I added using sources? – Krishnandu Sarkar Oct 7 '14 at 18:31
  • Thanks @KrishnanduSarkar -- you're right, by default it should be an ICMP reject. But, I think you can add a rich rule to drop the packets. I added an example to my answer that I think will work. Thanks regarding the upvote, I understand, if an answer works, consider accepting an answer. – dougBTV Oct 7 '14 at 18:54
  • Great answer, I had been using iptables up until now. – Tensigh Dec 12 '17 at 8:45
  • (!) This answer won't work as expected for current default FirewallD configuration (interfaces are assigned to public zone by default). – dess Sep 25 at 21:07
25

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:

  1. interface and source are used as selectors - which zone(s) to activate
  2. both are ignored for the default zone (always active)

So the answer would be:

  1. lock down the default zone, say "public" - no ports open or services available
  2. 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".

  • 4
    This is the best answer. The most crucial part is the explanation that setting an interface broadens the access (in case sources are set). I had a problem that the ports were accessible even though I had sources whitelist. The reason was that the zone had an interface assigned. – pinkeen Dec 14 '17 at 13:27
  • 1
    To be exactly correct this answer needs removal all default services from public zone, if any (these services will be available to all addresses as interfaces are assigned to public zone by default). Or change default zone to another: block or drop (this is common practice). Or change public zone target to %%REJECT%% or DROP. – dess Sep 25 at 21:59
6

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).

  • block can also be used (instead of drop) if you want to tell other hosts that you are not talking to them.... – Gert van den Berg Dec 29 '16 at 14:27
5

I operate my firewalls in this manner. Here is my preferred method to accomplish what you want.

# firewall-cmd --list-all

You'll see your default zone is public and the services enabled are dhcpv6-client and ssh. We don't want any public services available, right? Only the whitelisted IP's are authorized. So let's remove the two public services.

# firewall-cmd --zone=public --remove-service=ssh --permanent
# firewall-cmd --zone=public --remove-service=dhcpv6-client --permanent

Now, let's whitelist a specific IP which grants access to any port.

#firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="xx.xx.xx.xx" accept'

Now, let's whitelist another IP, that we only want to have access to SSH, http, and https access. No other ports.

#firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="xx.xx.xx.xx" service name="ssh" accept'
#firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="xx.xx.xx.xx" service name="http" accept'
#firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="xx.xx.xx.xx service name="https" accept'

If you're connecting via SSH, be sure to authorize your IP before applying your new rule set. When ready to apply the new rules.

#firewall-cmd --reload
2

You can manage easily by Rich Rule.

First Step

firewall-cmd --permanent --set-default-zone=home
firewall-cmd --permanent --zone=drop --change-interface=eth0

Second Step - Add Rich Rule

firewall-cmd --permanent --zone=home --add-rich-rule='rule family="ipv4" source address="192.168.78.76/32" accept'

All port is accessible by 192.168.2.2 once you add rich rule and blocked every port from other source.

If you will add any port or service by below command then it will accessible by all sources.

firewall-cmd --zone=public --add-service=ssh
firewall-cmd --zone=public --add-port=8080

If you want to open specific port for specific Ip than below command

firewall-cmd --permanent --zone=home --add-rich-rule='rule family="ipv4" port="8080/tcp" source address="192.168.78.76/32" accept'
2

The top answer from dougBTV is wrong. I can't reply to his answer because I don't have the requisite rep points yet so I'll explain here:

He is using the default zone "public". He is tying networks to that zone and then opening ports on that zone. But, on a default config, all traffic goes through the default zone, not just the source networks you tie to it. So his --add-source commands make no difference and his --add-port commands have now allowed the whole world to access those ports.

The 2nd answer by Normunds Kalnberzins is correct. You want to create a separate zone, tie your network/IP's to that zone, and open the ports in that zone.

Alternatively, you can leave everything in the default zone and use firewalld's rich rules to allow access from certain IP's:

firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.2.2" accept'

This allows all traffic from 192.168.2.2 to all ports and because I haven't specified a zone this will be applied to the default zone "public" (use --get-default-zone to verify what your default zone is and --get-active-zones to see which zones are currently in use).

To allow access from this IP only to a specific port I would do:

firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.2.2" port port="1234" protocol="tcp" accept'

Best practice is to run these commands without --permanent (or --perm for short) which affects the currently running firewall. After testing that your rule is working, run it again with --perm appended so that it is remembered on subsequent firewalld reloads.

1

Just to add to Normunds answer:

$ sudo firewall-cmd --permanent --zone=work --add-source=172.16.0.0/12
$ sudo firewall-cmd --permanent --zone=work --add-port=8080-8090/tcp

To block all other traffic:

$ sudo firewall-cmd --set-default-zone=drop

Warning: if you access from remote machine, this may disconnect your login session. If you did not get the 'work' zone IP setup correctly, you will not be able to connect to your server.

To reload the firewall:

$ sudo firewall-cmd --reload

I could not figure out how to add two different IPs with '--add-rich-rule'.

  • For current default FirewallD configuration it may be not enough. See my comment to Normunds's answer for details. – dess Sep 25 at 21:23
  • for multiple IPs use create an ipset like firewall-cmd --permanent --new-ipset=blacklist --type=hash:ip add ips to the ipset with firewall-cmd --ipset=blacklist --add-entry=192.168.1.4 then you can use firewall-cmd --add-rich-rule='rule source ipset=blacklist drop' – fyrye Oct 3 at 2:22
0

I'm surprised the trusted zone answers are not the selected answer. The trusted zone has a default "target: ACCEPT" while the rest are "target: default". While it really does not matter it appears to be the intended method due to its name and default target value.

How to quickly lock down a box so only you can access it:

firewall-cmd --zone=trusted --add-source=1.2.3.4
firewall-cmd --zone=trusted --add-source=5.6.7.8/24
firewall-cmd --zone=drop --change-interface=eth1
firewall-cmd --set-default-zone=drop
firewall-cmd --runtime-to-permanent
firewall-cmd --reload
firewall-cmd --list-all-zones

After listing all zones you should see something like this:

trusted (active)
  target: ACCEPT
  icmp-block-inversion: no
  sources: 1.2.3.4 5.6.7.8/24
  masquerade: no

drop (active)
  target: DROP
  icmp-block-inversion: no
  interfaces: eth1
  masquerade: no

Note: I removed lines with a null/missing value. The important thing is that trusted and drop are both (active) and drop has your public interface.

What this does to iptables for demonstration:

Chain INPUT_ZONES_SOURCE (1 references)
target     prot opt source               destination
IN_trusted  all  --  1.2.3.4         0.0.0.0/0
IN_trusted  all  --  5.6.7.8/24        0.0.0.0/0
Chain INPUT_ZONES (1 references)
target     prot opt source               destination
IN_drop    all  --  0.0.0.0/0            0.0.0.0/0
IN_drop    all  --  0.0.0.0/0            0.0.0.0/0
IN_drop    all  --  0.0.0.0/0            0.0.0.0/0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.