RHCE – Setting up login based authentication using Apache on CentOS/RHEL 7

In the previous tutorial, we saw how we can host multiple websites on the same box using vhosts. Now we’ll look at how we can configure Apache to password protect some web content. In Apache you can set up authentication so that when a user attempts to access a given folder’s content, then they will get a prompt to enter a valid username and password. There’s 2 ways to do this:

  • Setup user-based security – This is where you only give one user account access to restricted part of your website
  • Setup group-managed content – this is where you give a group of people access to a restricted part of your website

Need to rewrite to article like this: first create 2 users tom and jerry. Then setup to only give tom access. Then modify it to give both tom and jerry access. then modify it again to make use of .htaccess

Setup user-based security

In this approach, we set up authentication so that there is only one user account that’s permitted to access the restricted content. In our example we’ll take the vhost route to set this up. Let’s say the restricted will reside in the folder /var/vhosts/example_com. So let’s start with creating that directory. Then let’s add some dummy content to it:

$ mkdir /var/vhosts
$ mkdir /var/vhosts/example_com
$ chown apache:apache /var/vhosts
$ chown apache:apache /var/vhosts/example_com
$ echo 'TOP SECRET CONTENT for example.com' > /var/vhosts/example_com/index.html
$ chown apache:apache /var/vhosts/example_com/index.html

Now since we created a non-standard folder, we’re likely to encounter SELinux problems, as indicated by the security contexts:

$ ls -lZ /var | grep vhosts
drwxr-xr-x. apache apache unconfined_u:object_r:var_t:s0   vhosts
$ ls -lRZ /var/vhosts
/var/vhosts:
drwxr-xr-x. apache apache unconfined_u:object_r:var_t:s0   example_com
 
/var/vhosts/example_com:
-rw-r--r--. apache apache unconfined_u:object_r:var_t:s0   index.html

Whereas the security context needs to be the same as that of /var/www/html:

$ ls -lZ /var/www | grep html
drwxr-xr-x. root   root   system_u:object_r:httpd_sys_content_t:s0 html

So we need to fix this, in particular the *_t (type). We fix this running the semanage command like this:

$ semanage fcontext -at httpd_sys_content_t "/var/vhosts(/.*)?"

The asterisk in this expression means that this will get applied recursively to all child folders and files. For more info about this command, see man httpd_selinux which comes as part of the selinux-policy-doc rpm.

This policy has now been added in to SELinux’s list policy. You can confirm this by running:

$ semanage fcontext -l | grep '/var/vhosts'
/var/vhosts(/.*)?                                  all files          system_u:object_r:httpd_sys_content_t:s0

Now we need to apply this policy to the folder and files we have created:

$ restorecon -Rv /var/vhosts
restorecon reset /var/vhosts context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
restorecon reset /var/vhosts/example_com context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
restorecon reset /var/vhosts/example_com/index.html context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0

Here we are doing this (R)ecursively and (v)erbosely.

Now if we check again, everything looks better:

$ ls -lZ /var | grep vhosts
drwxr-xr-x. apache apache unconfined_u:object_r:httpd_sys_content_t:s0 vhosts
$ ls -lRZ /var/vhosts
/var/vhosts:
drwxr-xr-x. apache apache unconfined_u:object_r:httpd_sys_content_t:s0 example_com
 
/var/vhosts/example_com:
-rw-r--r--. apache apache unconfined_u:object_r:httpd_sys_content_t:s0 index.html

Next we create the following vhost file to point to our new web content directory:

$ ll /etc/httpd/conf.d/example_com.conf
-rw-r--r--. 1 apache apache 343 Mar  5 21:41 /etc/httpd/conf.d/example_com.conf
 
$ cat /etc/httpd/conf.d/example_com.conf
<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com
    DocumentRoot /var/vhosts/example_com
 
    <Directory /var/vhosts/example_com>
        AllowOverride AuthConfig
    </Directory>
 
 
    ErrorLog /var/log/httpd/example_com_error.log
    CustomLog /var/log/httpd/example_com_access.log combined
</VirtualHost>

I also used apachectl command to confirm that there are no syntax errors. In this file we indicated that we are allowing the .htaccess file to set up the authentication. Note, we could avoid using .htaccess and instead do everything straight inside the Directory directive. However I’m using .htaccess on this occasion just to show .htaccess in action. So next I created the .htaccess in the DocumentRoot:

$ ls -l /var/vhosts/example_com/.htaccess
-rw-r--r--. 1 apache apache 142 Mar  5 22:07 /var/vhosts/example_com/.htaccess
 
$ cat /var/vhosts/example_com/.htaccess
AuthType Basic
AuthName "Please enter a Username and Password"
AuthUserFile "/etc/httpd/conf/.AuthUserFile_for_example_com"
Require user james

By convention and best practice, the AuthUserFile should be a hidden file.

Now we need to generate this user ‘james’, which is done using the htpasswd command:

$ htpasswd -c /etc/httpd/conf/.AuthUserFile_for_example_com james
New password:
Re-type new password:
Adding password for user james

In my case, I set the password to ‘password123’. This ends up (c)reating the following file:

$ ll /etc/httpd/conf/.AuthUserFile_for_example_com
-rw-r--r--. 1 root root 44 Mar  6 09:38 /etc/httpd/conf/.AuthUserFile_for_example_com
 
$ cat /etc/httpd/conf/.AuthUserFile_for_example_com
james:$apr1$I8OrGSri$7SY9/5NIMgv1CTIZ6N.eU/

Now we need to restart httpd:

$ systemctl restart httpd

Next, since we don’t have a public dns for example.com, we need to add a local entry in our hosts file:

$ cat /etc/hosts | grep example.com
10.0.5.10   example.com

Now we can test this by using chrome/firefox/elinks:

$ elinks http://example.com

Or you can just test this using curl:

$ curl http://example.com
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested.  Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
</body></html>
 
 
$ curl -u james:password123 http://example.com
TOP SECRET CONTENT for example.com

At this point our website is now password protected. However we should also be using the https protocol, not http. We’ll cover this later.

Setup group-managed content

So far we have looked at how to password protect a web content with a single login credential. However another common scenarios is to give a group of users access to some restricted web content. Let’s say we want the content of the the website http://example.net to be accessible by only 2 users, ‘Tom’ and ‘Jerry’. To take this approach, we will again take the vhost and .htaccess approach. So let’s create our vhost folder, and put a index.html file in it:

$ mkdir /var/vhosts/example_net
$ chown apache:apache /var/vhosts
$ chown apache:apache /var/vhosts/example_com
$ echo 'SUCCESS!, you have accessed SECRET data that only a small group of users are allow to view in example.net' > /var/vhosts/example_net/index.html
$ chown apache:apache /var/vhosts/example_net/index.html

Lets check the SELinux contexts:

$ ls -lZ /var/vhosts/ | grep example_net
drwxr-xr-x. root   root   unconfined_u:object_r:httpd_sys_content_t:s0 example_net
$ ls -lRZ /var/vhosts/example_net
/var/vhosts/example_net:
-rw-r--r--. apache apache unconfined_u:object_r:httpd_sys_content_t:s0 index.html

This time the type attribute within the SELinux is correct, i.e. it is set to ‘httpd_sys_content_t’. That’s because we already set the recursive SELinux policy at the /var/vhost level earlier on in this article when we were setting up the single user based access.

Now in order to give Tom and Jerry access, they also have to exist as OS level users on the box itself, so we do:

$ useradd tom
$ useradd jerry

We also need to create a OS level group, which we will then whitelist in the apache setup. Let’s call this group ‘example_net_admins’. Then we need to add tom and jerry to that group:

$ groupadd example_net_admins
 
$ usermod -aG example_net_admins tom
$ usermod -aG example_net_admins jerry
 
# let's confirm this has worked:
$ cat /etc/group | grep example_net_admins
example_net_admins:x:1005:tom,jerry

Now we create the vhost config file:

$ ll /etc/httpd/conf.d/example_net.conf
-rw-r--r--. 1 apache apache 342 Mar  6 11:15 /etc/httpd/conf.d/example_net.conf
 
$ cat /etc/httpd/conf.d/example_net.conf
<VirtualHost *:80>
    ServerName www.example.net
    ServerAlias example.net
    DocumentRoot /var/vhosts/example_net
 
    <Directory /var/vhosts/example_net>
        AllowOverride AuthConfig
    </Directory>
 
 
    ErrorLog /var/log/httpd/example_net_error.log
    CustomLog /var/log/httpd/example_net_access.log combined
</VirtualHost>

Now let’s create the .htaccess file:

$ ll /var/vhosts/example_net/.htaccess
-rw-r--r--. 1 root root 231 Mar  6 11:35 /var/vhosts/example_net/.htaccess
$ cat /var/vhosts/example_net/.htaccess
AuthType Basic
AuthName "Please enter a Username and Password"
AuthUserFile "/etc/httpd/conf/.group_access_passwd_db_for_example_net"
AuthGroupFile "/etc/httpd/conf/.allowed_groups_for_example_net"
Require group example_net_admins

Notice this time we used the AuthGroupFile directive.

Next we need to create the 2 files referenced in the .htaccess file.

$ ll /etc/httpd/conf/.allowed_groups_for_example_net
-rw-r--r--. 1 apache apache 30 Mar  6 11:42 /etc/httpd/conf/.allowed_groups_for_example_net
 
$ cat /etc/httpd/conf/.allowed_groups_for_example_net
example_net_admins: tom jerry

Now we create the password file:

$ htpasswd -c /etc/httpd/conf/.group_access_passwd_db_for_example_net tom
New password:
Re-type new password:
Adding password for user tom

here we used the (c)reate option to not only set the password for user tom, but also create the file itself. There when we set the password for the next user, we omit the (c)reate option otherwise it would end up deleting the existing file, and recreating it from scratch. So we do:

 
$ htpasswd /etc/httpd/conf/.group_access_passwd_db_for_example_net jerry
New password:
Re-type new password:
Adding password for user jerry

Now all that’s left doing is restarting the httpd daemon:

$ systemctl restart httpd

Now we are ready to test this setup. so first we need to add a local entry in our hosts file (our box’s ip address is):

$ cat /etc/hosts | grep example.net
10.0.5.10  example.net

Then test with firefox/chrome/elinks/curl:

$ curl http://example.net


401 Unauthorized

Unauthorized

This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.

$ curl -u tom:WrongPassword http://example.net 401 Unauthorized

Unauthorized

This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.

$ curl -u tom:cat http://example.net SUCCESS!, you have accessed SECRET data that only a small group of users are allow to view in example.net $ curl -u jerry:mouse http://example.net SUCCESS!, you have accessed SECRET data that only a small group of users are allow to view in example.net

Note in our example we set the password for tom as ‘cat’ and for jerry we set it as ‘mouse’.

Take the RHCSA Quiz

This article is part of our RHCSA Study guide (click on the yellow tab on the far left). By the end of this article you should be able to answer the following questions:

Quiz about Setting up user-managed content:


What are the various ways to password protect web content?

  • Setup user-based security – Give only one user account access to restricted part of your website
  • Setup group-managed content – Give a group of people access to a restricted part of your website


What are the commands to make the 'hello world' file /var/vhosts/example_com/index.html that can be accessible by the web server (ignore SELinux for now)?

$ mkdir -p /var/vhosts/example_com
$ echo ‘hello world’ > /var/vhosts/example_com/index.html
$ chown -R apache:apache /var/vhosts

What is the command to view file contexts of /var/www and /var/vhosts/?

$ ll -dlZ /var/www
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 /var/www
$# ll -dlZ /var/vhosts/
drwxr-xr-x. apache apache unconfined_u:object_r:var_t:s0 /var/vhosts

What is the command to add a new policy rule to the SELinux manual?

$ semanage fcontext -at httpd_sys_content_t “/var/vhosts(/.*)?”

What is the command to view this new rule in the manual?

$ semanage fcontext -l | grep ‘/var/vhosts’
/var/vhosts(/.*)? all files system_u:object_r:httpd_sys_content_t:s0

What is the command to apply this rule to /var/vhosts?

$ restorecon -Rv /var/vhosts

What vhost file do you need to create and what will it's content be, to give access to single user called james (non .htaccess approach)?

$ cat /etc/httpd/conf.d/example_com.conf
<VirtualHost *:80>
  ServerName www.example.com
  ServerAlias example.com
  ServerAdmin sher@example.com
  DocumentRoot /var/vhosts/example_com
  ErrorLog   /var/log/httpd/example_com_error_log
  CustomLog /var/log/httpd/example_com_access.log combined
 
  <Directory "/var/vhosts/example_com">
    AuthType Basic
    AuthName "Please enter a Username and Password"
    AuthUserFile "/etc/httpd/conf/.AuthUserFile_for_example_com"
    Require user james
  </Directory>
</VirtualHost>


What is the command to generate a password for the 'james' user?

$ htpasswd -c /etc/httpd/conf/.AuthUserFile_for_example_com james

What 2 checks you can perform to check the configs?

$ httpd -d
# and
$ httpd -D DUMP_VHOSTS

Now what do you need to do??

restart the httpd daemon.

What do you need to do to test this setup?

– updated /etc/hosts file
– use elinks # not very reliable. Better to use curl.

What is the curl command to test this?

$ curl -u james:password123 http://example.com

What is the .htaccess to this?

# create the file:
$ cat /var/vhosts/example_com/.htaccess
AuthType Basic
AuthName “Please enter a Username and Password”
AuthUserFile “/etc/httpd/conf/.AuthUserFile_for_example_com”
Require user james

# also replace content inside directory block directive to ‘AllowOverride authconfig’


Quiz about Setting up group-managed content

what are the commands to create to system users tom and jerry and add them to a group called examle_com_admin?

$ groupadd example_com_admin
$ useradd tom
$ useradd jerry
$ usermod -aG example_com_admin tom
$ usermod -aG example_com_admin jerry

What does the vhost file needs to look (non .htaccess approach)?

$ cat /etc/httpd/conf.d/example_com.conf

ServerName www.example.com
ServerAlias example.com
ServerAdmin sher@example.com
DocumentRoot /var/vhosts/example_com
ErrorLog /var/log/httpd/example_com_error_log
CustomLog /var/log/httpd/example_com_access.log combined


AuthType Basic
AuthName “Please enter a Username and Password”
AuthUserFile “/etc/httpd/conf/.group_access_passwd_db_for_example_com”
AuthGroupFile “/etc/httpd/conf/.allowed_groups_for_example_com”
Require group example_com_admin



What is the content of AuthGroupFile?

$ cat /etc/httpd/conf/.allowed_groups_for_example_com
example_com_admin: tom jerry

How do you create the AuthUserFile?

$ htpasswd -c /etc/httpd/conf/.group_access_passwd_db_for_example_com tom
$ htpasswd /etc/httpd/conf/.group_access_passwd_db_for_example_com jerry

now what do you need to do?

$ httpd -t
$ httpd -D DUMP_VHOSTS
$ systemctl restart httpd