Skip to main content

Access authorization in Apache 2.4

In Apache 2.4 the authorization configuration setup has changed from previous versions. Satisfy, Order, Deny and Allow have all been deprecated and replaced with new Require directives.
Below we've compiled some examples to guide you through the transition.

If you are upgrading a server using the legacy authorization directives you can make them work quickly by enabling (it should be activated by default)mod_access_compat in Apache:

sudo a2enmod access_compat

1. Apache Documentation
The documentation from Apache: Upgrading to 2.4 from 2.2 provides the following basic examples. The old configuration settings are on the left, and the new ones for Apache 2.4 on the right:

All requests are denied:

Order deny,allow Deny from allRequire all denied

All requests are allowed:

Order allow,deny Allow from allRequire all granted

Only hosts in the example.org domain are allowed access:

Order Deny,Allow Deny from all Allow from example.orgRequire host example.org
But this only scratches the surface of what's now available.

2. RequireAll and RequireAny

The most interesting new features are the RequireAllRequireAny and RequireNone authorization containers. They promise to be both more powerful and more human-readable than the old syntax.
By default all Require directives are handled as though contained within a <RequireAny>container directive. In other words, if any of the specified authorization methods succeed, then authorization is granted.
Here is real world example where a website limits access by requiring a Basic Authentication login for certain directories:
AuthType Basic AuthName "Password Protected" AuthUserFile <path_to_your_htpasswd_file> SetEnvIf REQUEST_URI "^/(admin|secure)/" PROTECTED Deny from all Satisfy any Allow from env=!PROTECTED Require valid-userSetEnvIf REQUEST_URI "^/(admin|secure)/" PROTECTED <RequireAny> <RequireAll> Require not env PROTECTED Require all granted </RequireAll> <RequireAll> AuthType Basic AuthName "Password Protected" AuthUserFile <path_to_your_htpasswd_file> Require valid-user </RequireAll> </RequireAny>
In both cases we set an environmental variable PROTECTED when the request is for a file in the /admin/or /secure/ directories. The syntax for this part hasn't changed. If this variable is set, then a password will be required for access.
While the old syntax works, it's not immediately clear how it works. Basically to get access the request has to meet (Satisfy) either the Allow or the Require directive.
In the new syntax this is more explicit. The request needs to pass at least one (RequireAny) of the two RequireAll container rulesets. The first container grants all users access to non-PROTECTED directories, while the second container requires a valid login.
You can keep nesting containers until all possible options are covered.
But seeing as the outer <RequireAny> is already implied, we should be able to remove it. Actually we can remove quite a bit now that we know what we're doing:
AuthType Basic AuthName "Password Protected" AuthUserFile <path_to_your_htpasswd_file> SetEnvIf REQUEST_URI "^/(admin|secure)/" PROTECTED <RequireAll> Require not env PROTECTED </RequireAll> Require valid-user
Note that any Require not directives must always be enclosed in a RequireAll directive. Otherwise you will see an alert logged:
[core:alert] ... negative Require directive has no effect in <RequireAny> directive
See further down the page a version of this example that does away with the ENV variable entirely by using an expr condition.

3. Require authorization providers

The Require directive comes with a number of build-in authorization providers, including some already demonstrated above. Different modules provide different methods.
The following are provided by the mod_authz_core module:

all

Replaces Allow from all and Deny from all in the old syntax:
Require all grantedRequire all denied

env

Require env safe_zone<RequireAll> Require not env PROTECTED </RequireAll>

method

This example allows only GET and HEAD requests unless you are logged in:
<RequireAny> Require method GET HEAD Require valid-user </RequireAny>
The <RequireAny> container is not necessary here, but included for clarity:

expr

Require expr %{HTTP_USER_AGENT} != 'BadBot'
The following options are provided by the mod_authz_host module:

local

Require local

ip

<RequireAll> Require ip 192.168.1.0/24 Require not ip 192.168.1.104 </RequireAll>Require ip 2001:db8:1:1::/64

host

<RequireAll> Require host example.org Require not host blocked.example.org </RequireAll>

4. Working with expressions

After a bit of messing about we were able to further simplify the previous example by removing the SetEnvIf clause and replacing it with a Require expr regular expression condition.
AuthType Basic AuthName "Password Protected" AuthUserFile <path_to_your_htpasswd_file> SetEnvIf REQUEST_URI "^/(admin|secure)/" PROTECTED <RequireAll> Require not env PROTECTED </RequireAll> Require valid-userAuthType Basic AuthName "Password Protected" AuthUserFile <path_to_your_htpasswd_file> Require expr %{REQUEST_URI} !~ m#^/(admin|secure)/# Require valid-user
The tricky part was working out how to include the forward slash / in the regular expression. The solution is instead of the default format which doesn't allow a forward slash in the match:
Require expr %{REQUEST_URI} !~ /expr/
To use the alternative syntax:
Require expr %{REQUEST_URI} !~ m#expr#
For details on other SERVER variables and comparison operators that can be used see the link under References below.

5. Granting local access

Another real world example is granting access only to the local network.
In this case we're defining the local network as the server itself (localhost), plus the 192.168.1.* subnet covering 192.168.1.0 - 192.168.1.255.
<Directory "/path/to/your/website"> Options FollowSymlinks AllowOverride None Order allow,deny Allow from 127.0.0.0/8 192.168.1 ::1 </Directory><Directory "/path/to/your/website"> Options FollowSymlinks AllowOverride None Require local Require ip 192.168.1 </Directory>
The local Require'ment matches requests from the local host over IPv4 or IPv6 (so including 127.0.0.1/8 and ::1). We wrap this, along with Require ip 192.168.1, in a RequireAny authorization container because we want to accept connections that match either condition.
We could also write 192.168.1.0/24 instead of just 192.168.1, but they have the same effect.
If you want to also allow connections from outside the local network, but requiring authentication, the configuration becomes:
<Directory "/path/to/your/website"> Options FollowSymlinks AllowOverride None Require local Require ip 192.168.1 Require valid-user </Directory>
So we're now granting access from localhost and the local network without authentication, plus from all other locations, but then requiring authentication.
You can make this more secure by restricting outside access to only recognised locations:
<Directory "/path/to/your/website"> Options FollowSymlinks AllowOverride None Require local Require ip 192.168.1 <RequireAll> Require host example.org Require not host badhost.example.org Require valid-user </RequireAll> </Directory>
Now an external connection can only come from *.example.org and only in conjunction with a valid login. To specify more than one domain or ip address in addition to example.org they will need to be wrapped in yet another container:
<Directory "/path/to/your/website"> Options FollowSymlinks AllowOverride None Require local Require ip 192.168.1 <RequireAll> <RequireAny> Require host example.org example.com Require ip 8.8.8.8 </RequireAny> Require not host badhost.example.org Require valid-user </RequireAll> </Directory>
For those getting confused, RequireAll means that all the requirements in that container need to be met, while RequireAny means that only one or more of the contained requirements needs to be met:
Require (local) OR (ip 192.168.1) OR [ [ (host example.org) OR (host example.com) OR (ip 8.8.8.8) ] AND (NOT host badhost.example.com) AND (valid-user) ]

6. Public file in Private directory

Thank you to Alfredo for this question - how to have a password-protected directory (or website) but allow access to a specific file.
If you have a directory ~/private/ then you can make the entire directory secure by adding an .htaccess file ~/private/.htaccess with:
AuthType Basic AuthName "Password Required" AuthUserFile /path/to/.htpasswd Require valid-user
But what if there is a file ~/private/public.html that you want to make globally accessible? This wasn't possible in earlier versions of Apache, but can be done now quite simply:
AuthType Basic AuthName "Password Required" AuthUserFile /path/to/.htpasswd Require expr %{REQUEST_URI} = "/private/public\.html" Require valid-user
How does it work? Remember that there is an explicit <RequireAny> wrapped around the two Requirestatements, so it reads as: either the request is for the file public.html or require a password.

Comments

Popular posts from this blog

How to Bind Apache Tomcat to IPv4 (Solved)

Apache Tomcat  is an open source web server and servlet container developed by the  Apache Software Foundation . It implements the Java Servlet, JavaServer Pages (JSP), Java Unified Expression Language and Java WebSocket specifications from Sun Microsystems and provides a web server environment for Java code to run in. Binding Tomcat to IPv4 is necessary if we have our server not working due to the default binding of our tomcat server to IPv6. As we know IPv6 is the modern way of assigning IP address to a device and is not in complete practice these days but may come into practice in soon future. So, currently we don't need to switch our tomcat server to IPv6 due to no use and we should bind it to IPv4. Before thinking to bind to IPv4, we should make sure that we've got tomcat installed in our CentOS 7. Here's is a quick tutorial on  how to install tomcat 8 in CentOS 7.0 Server . 1. Switching to user tomcat First of all, we'll gonna switch user to  tomcat  user. We

IOException parsing XML document from class path resource

Error Exception in thread "main" org . springframework . beans . factory . BeanDefinitionStoreException : IOException parsing XML document from class path resource [ src / main / java / resources / combined2 . xml ]; nested exception is java . io . FileNotFoundException : class path resource [ src / main / java / resources / combined2 . xml ] cannot be opened because it does not exist at org . springframework . beans . factory . xml . XmlBeanDefinitionReader . loadBeanDefinitions ( XmlBeanDefinitionReader . java : 341 ) at org . springframework . beans . factory . xml . XmlBeanDefinitionReader . loadBeanDefinitions ( XmlBeanDefinitionReader . java : 302 ) at org . springframework . beans . factory . support . AbstractBeanDefinitionReader . loadBeanDefinitions ( AbstractBeanDefinitionReader . java : 143 ) at org . springframework . beans . factory . support . AbstractBeanDefinitionReader . loadBeanDefinitions ( AbstractBeanDefinitionReader . java : 178 ) at

Linux Command htop vs top

Those who work with Linux servers, but also those who use Linux as a desktop every day knows that it is very useful to have a list of processes running on the machine with a list of resources they are using, to see who is eating all the memory or is using all the CPU, and the ps command is not exactly the most convenient. So we ‘ll see a series of programs that serve to show what’s happening in our system and who is using our resources. The first tool that you can use to get information is the  ps  command, yes i know, it’s not so handy but you are sure to have it installed in every machine and you can get quickly some information on the system: Get the top 10 CPU consuming process ps aux | sort -n -k 3 | tail -10 Get the top 10 memory consuming process ps aux | sort -n -k 4 | tail -10 Top In most Unix-like operating systems, the top command is a system monitor tool that produces a frequently-updated list of processes. By default, the processes are ordered by