4

I'm looking for a good way to block an entire IP range using .htaccess.

The problem I'm having is that the block I need to ban is 100 IP ranges big. From a programmer's perspective it should be easily doable, but I'm failing to get it done without adding 100 lines in my .htaccess file, and other techniques I tried did not work.

The IP Address range I need to block is for HUAWEI Clouds:

  • 159.138.100.* to 159.138.199.*

A keen eye spots that basically 159.138.1 is enough to block.

I tried the following without success:

Order Allow,Deny
Deny from 159.138.1
Allow from all

I also tried:

Order Allow,Deny
Deny from 159.138.1*
Allow from all

How can I block this IP range effectively using .htaccess?

MrWhite
  • 43,224
  • 4
  • 50
  • 90
LPChip
  • 153
  • 1
  • 5

2 Answers2

5

IP ranges are specified in .htaccess using CIDR notation. The simplest rule that you could use would be

Deny from 159.138.0.0/16

Which would block slightly too much:

CIDR Range      159.138.0.0/16
Netmask         255.255.0.0
Wildcard Bits   0.0.255.255
First IP        159.138.0.0
Last IP         159.138.255.255
Total Host      65536

To block the exact range of 159.138.100.0 to 159.138.199.255 you would need several rules:

Deny from 159.138.100.0/22
Deny from 159.138.104.0/21
Deny from 159.138.112.0/20
Deny from 159.138.128.0/18
Deny from 159.138.192.0/21

Source: IP Range to CIDR calculator

I'm not sure where you got the IP range you are working with. This site lists 74 CIDR ranges for Huawai cloud service

Stephen Ostermiller
  • 99,822
  • 18
  • 143
  • 364
4
Deny from 159.138.1

You can specify partial IP addresses, but only so far as whole bytes. So, you could write:

Deny from 159.138

Which is the same as 159.138.0.0/16 using CIDR notation - which "would block slightly too much" (as mentioned).

Failing that, using multiple CIDR ranges, as @Stephen mentions in his answer is probably the way to go.


159.138.100.* - 159.138.199.*

However, this particular example does lend itself to a relatively simple regular expression match on the IP address. For example:

SetEnvIf Remote_Addr ^159\.138\.1\d\d BLOCKIT
Deny from env=BLOCKIT

The SetEnvIf directive sets a BLOCKIT environment variable if the remote IP address matches the stated regex. And the Deny directive is triggered if this env var is set. \d is a shorthand character class that matches any digit 0-9.

basically 159.138.1 is enough to block.

For that you could simplify the regex to ^159\.138\.1 (ie. remove the trailing \d\d). Although this would block an additional 2816 (11 x 256) IP addresses.

Not sure what version of Apache they are using, but they are a company that keep things up-to-date. So I expect that they have one of the latest versions.

In that case you are far more likely to be on Apache 2.4. The "problem" now is that Deny is really an Apache 2.2 directive. It is still available in Apache 2.4 - for backwards compatibility only (and has been moved to a different module) - and is formerly deprecated (so will be removed entirely in future versions).

An added complication is that you should really update all instances of the old Order, Deny, Allow directives to the new Require directive throughout the system otherwise you can get unexpected conflicts (since one does not necessarily override the other).

Reference:
https://httpd.apache.org/docs/2.4/mod/mod_access_compat.html

For example, converting the multiple Deny CIDR directives from @Stephen's answer to use Apache 2.4 Require would give:

<RequireAll>
    Require all granted
    Require not ip 159.138.100.0/22
    Require not ip 159.138.104.0/21
    Require not ip 159.138.112.0/20
    Require not ip 159.138.128.0/18
    Require not ip 159.138.192.0/21
</RequireAll>

(Slightly counter intuitive is that negated directives, eg. Require not ..., cannot themselves allow or deny access, hence why the above needs to be enclosed in a <RequireAll> directive with a Require all granted directive.)

Or, you could use Apache 2.4+ expressions and match the IP address using a regex (similar to above). For example:

<If "%{REMOTE_ADDR} =~ /^159\.138\.1\d\d/">
    Require all denied
</If>

Or, match the "overly general" IP range using CIDR notation:

<If "-R '159.138.0.0/16'">
    Require all denied
</If>

Reference:

MrWhite
  • 43,224
  • 4
  • 50
  • 90