1

I have setup a subdomain (staging.callkneehill.ca) on my server and have created a .htaccess file with the following parameters:

<RequireAny>
  Require ip 70.65.194.109
</RequireAny>
ErrorDocument 403 "Restricted Access"

When I try accessing the subdomain, the 403 error is being displayed, even though my IP address matches the required value.

If I comment out all of the code in the .htaccess, the index.html loads.

Here is the virtual host configuration for the subdomain:

<VirtualHost *:80>
  ServerName staging.callkneehill.ca
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/staging/
  ErrorLog ${APACHE_LOG_DIR}/staging-error.log
  CustomLog ${APACHE_LOG_DIR}/staging-access.log combined
  RewriteEngine on
  RewriteCond %{SERVER_NAME} =staging.callkneehill.ca
  RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<Directory /var/www/staging/>
  Options Indexes FollowSymLinks
  AllowOverride All
  Require all granted
</Directory>
<FilesMatch \.php$>
  # SetHandler "proxy:unix:/run/php/php7.4-callkneehill-fpm.sock|fcgi://localhost"
</FilesMatch>

The .htaccess file on the root domain, which uses WordPress functions correctly. I have compared the virtual host configuration files and both look to have the same general parameters.

I have also tested using IP blocking for Apache 2.2, but the same issue occurs.

I have reloaded and restarted Apache each time I have updated the configuration file.

Can anyone highlight what I am missing?

Update

Staging access log output

172.68.189.103 - - [18/May/2021:13:57:15 +0000] "GET / HTTP/1.1" 403     5454 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0"
172.68.143.156 - - [18/May/2021:13:57:15 +0000] "GET /favicon.ico HTTP/1.1" 403 5454 "https://staging.callkneehill.ca/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0"
172.68.189.229 - - [18/May/2021:14:02:11 +0000] "GET / HTTP/1.1" 301 598 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"
172.68.132.65 - - [18/May/2021:14:02:11 +0000] "GET / HTTP/1.1" 403 5454 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"
172.68.143.156 - - [18/May/2021:14:02:11 +0000] "GET /favicon.ico HTTP/1.1" 403 5454 "https://staging.callkneehill.ca/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15"
162.158.255.248 - - [18/May/2021:14:03:07 +0000] "GET / HTTP/1.1" 403 5454 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 14_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1"

I looks like my IP address is variable. Would this from using Cloudflare's 1.1.1.1 as my DNS on my MacBook Pro?

The same issue affects my iPhone on the same local network with a VPN enabled/disabled.

Mike Hermary
  • 131
  • 7
  • Are you sure you're checking the correct IP? This is presumably a "local" server? And you are presumably on the same "local" network? But `70.65.194.109` would seem to be a WAN IP address? (You've posted the vHost for port 80, but all that would seem to do is redirect to HTTPS (port 443)... aside: you don't need mod_rewrite here, a simple `Redirect` would suffice.) – MrWhite May 15 '21 at 01:28
  • @MrWhite This is the IP address that is returned when I do an IP search in the browser. Other IP addresses outside my network are being blocked too when I have added them for testing. It has worked when restricting access on other server setups, so I am thinking something is misconfigured on my setup at Digital Ocean. – Mike Hermary May 15 '21 at 12:58
  • 1
    What IP do you see in Apache's logs? – Gerard H. Pille May 16 '21 at 00:41
  • @GerardH.Pille I have tried accessing the `/var/log/apache2` directory, but am receiving **Permission denied** in Terminal. Using `sudo ls -l /var/log/apache2` shows the following permissions for the log files `-rw-r----- 1 root adm`. I am logged in as a sudo user. – Mike Hermary May 17 '21 at 17:32
  • So "sudo less /var/log/apache2/access.log" should work. – Gerard H. Pille May 18 '21 at 10:28
  • @GerardH.Pille Thanks for the command. I have updated my question with some of the output from the staging access log. – Mike Hermary May 18 '21 at 14:05
  • @GerardH.Pille Just updated now – Mike Hermary May 18 '21 at 14:11
  • So, do you recognize those IP addresses? – Gerard H. Pille May 18 '21 at 14:17
  • So, is "your server" perhaps not your server but someone else's? Like a cloud system provider? – Gerard H. Pille May 18 '21 at 14:30
  • @GerardH.Pille Those IP addresses do not match what was listed in my original question. The server is a droplet VPS at Digital Ocean. – Mike Hermary May 18 '21 at 14:33
  • Please read eg. https://www.globo.tech/learning-center/x-forwarded-for-ip-apache-web-server/ and see if it helps. – Gerard H. Pille May 18 '21 at 15:20
  • @MikeHermary I've updated my answer to include an alternative (or "additional") solution using Apache's mod_remoteip – MrWhite May 18 '21 at 16:52

1 Answers1

1

Would this from using Cloudflare's 1.1.1.1 as my DNS on my MacBook Pro

No. (But it would if your domain is pointing to Cloudflare's DNS.)

The IP addresses listed in your "update" are all Cloudflare IPs. This would suggest you are using Cloudflare CDN (ie. the NAMESERVERS on the domain point to Cloudflare) - which acts as a reverse proxy in front of your origin server. In other words, all requests to your origin server come from Cloudflare.

The actual client IP address is passed on to your origin server in various HTTP request headers (depending on how Cloudflare is configured). The defacto-standard header is X-Forwarded-For - but note that this can contain multiple IPs (comma separated) depending on whether the request has passed through multiple proxies. And Cloudflare appends the client IP address, not prefixes it (which goes against the convention). Other Cloudflare-specific (preferred) headers are CF-Connecting-IP and True-Client-IP which contain the single client IP address.

Reference:

<RequireAny>
  Require ip 70.65.194.109
</RequireAny>
ErrorDocument 403 "Restricted Access"

So, instead of the above, you would need to do something like the following instead in your .htaccess file:

ErrorDocument 403 "Restricted Access"
SetEnvIf CF-Connecting-IP "^70\.65\.194\.109$" ALLOWED_IP=1
Require env ALLOWED_IP

<RequireAny> is the default when omitted, so it is not required here.


Alternative using mod_remoteip

However, the above does not resolve the "issue" of Cloudflare IPs being reported in your logs instead of the actual client IP addresses.

Alternatively, you can enable Apache's mod_remoteip to inform Apache how to retrieve the client IP address (for the sake of Require ip and logging).

For example, once mod_remoteip is enabled then in your server config:

# Header that contains the client IP address
RemoteIPHeader CF-Connecting-IP

As added security, you can specify a list of allowed Cloudflare IPs in an external file:

# List of valid user-agent IPs
RemoteIPInternalProxyList /var/www/cloudflare-ips.lst

You then don't need to modify your existing Require ip 70.65.194.109 directive. But you do still need to modify your custom LogFormat to get the client IP logged. You would need to change %h (remote host/ip) at the start of the format string to %a (client IP as set by mod_remoteip).

For example, if you are using the combined log format (which it looks like you are) then:

LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

Reference:


Aside:

RewriteEngine on
RewriteCond %{SERVER_NAME} =staging.callkneehill.ca
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

The vHost config you've posted (for port 80) simply redirects to HTTPS (port 443) - the config for which you've not included.

But you don't need to check the requested SERVER_NAME here, since it can't be anything other than the staging hostname as defined by the ServerName directive (assuming you have a default vHost defined earlier). In fact, you don't need mod_rewrite at all, a simple mod_alias Redirect directive would be preferable instead:

Redirect 301 / https://staging.callkneehill.ca/
MrWhite
  • 12,647
  • 4
  • 29
  • 41
  • Thanks for your help. I updated my .htaccess with the suggested code and added `RemoteIPHeader CF-Connecting-IP` to the `/etc/apache2/apache2.conf` file and restarted Apache. Do I need to enable the logging to enable access or are these separate? These changes have not fixed the issue. – Mike Hermary May 18 '21 at 22:28
  • Check those 3 HTTP request headers as they reach your server/application... what do you see? "enable logging" - Well, I assume you already have a directive of the form `CustomLog "/var/www/logs/access.log" combined` that defines your access log? – MrWhite May 18 '21 at 22:42
  • The staging access log is showing my IPv6 address now. This address matches my home network. I commented out the IPv4 address and used `SetEnvIf CF-Connecting-IP "2604:3d08:ea8c:db00:e178:aad:545a:b2b5" ALLOWED_IP=1`, but same issue. Do IPv6 addresses need special treatment? – Mike Hermary May 18 '21 at 23:23
  • If you are using mod_remoteip then you can revert back to using `Require ip 2604:3d08:...`. Otherwise, you need to check those 3 headers as mentioned above (ie. `X-Forwarded-For`, `CF-Connecting-IP` and `True-Client-IP`). If you know what they contain then you know how to implement the directive. – MrWhite May 19 '21 at 00:16
  • It looks like mod_remoteip is working as I reverted back. Is one option preferred over the other? – Mike Hermary May 19 '21 at 01:19
  • It looks like my IP addresses are variable and are changing everyday. What is the best way to accommodate for this? – Mike Hermary May 19 '21 at 13:06
  • @MikeHermary "Is one option preferred" - Well, mod_remoteip is the only way to get the client IP logged in the server logs which is generally preferable, depending on how you are recording visitor stats. But otherwise, in terms of authorising access by IP, it doesn't really matter. (What do those headers contain?) "my IP addresses are variable" - in that case you can't auth by IP for any period. OR, configure a dynamic DNS service or update some other reference with your IP that can be "looked up" from Apache. OR, use some other kind of authentication method... HTTP authentication, cookies? – MrWhite May 19 '21 at 16:30
  • I have switched to HTTP authentication using htaccess and htpasswd to address the directory permissions. – Mike Hermary May 19 '21 at 19:03
  • @MikeHermary Please consider upvoting/accepting my answer since this does answer your initial question, even though you may be unable to implement this fully due to limitations at your end. – MrWhite May 19 '21 at 19:37