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:
Announcement
You can find all my latest posts on medium.- 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
ServerName www.example.com
ServerAlias example.com
DocumentRoot /var/vhosts/example_com
AllowOverride AuthConfig
ErrorLog /var/log/httpd/example_com_error.log
CustomLog /var/log/httpd/example_com_access.log combined
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
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 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
ServerName www.example.net
ServerAlias example.net
DocumentRoot /var/vhosts/example_net
AllowOverride AuthConfig
ErrorLog /var/log/httpd/example_net_error.log
CustomLog /var/log/httpd/example_net_access.log combined
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 in addition to the AuthUserFile.
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.net401 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.net401 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’.
[post-content post_name=rhsca-quiz]
Quiz about Setting up user-managed 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
$ mkdir -p /var/vhosts/example_com
$ echo ‘hello world’ > /var/vhosts/example_com/index.html
$ chown -R apache:apache /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
$ semanage fcontext -at httpd_sys_content_t “/var/vhosts(/.*)?”
$ semanage fcontext -l | grep ‘/var/vhosts’
/var/vhosts(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
$ restorecon -Rv /var/vhosts
$ 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/.AuthUserFile_for_example_com"
Require user james
$ htpasswd -c /etc/httpd/conf/.AuthUserFile_for_example_com james
$ httpd -d
# and
$ httpd -D DUMP_VHOSTS
restart the httpd daemon.
– updated /etc/hosts file
– use elinks # not very reliable. Better to use curl.
$ curl -u james:password123 http://example.com
# 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
$ groupadd example_com_admin
$ useradd tom
$ useradd jerry
$ usermod -aG example_com_admin tom
$ usermod -aG example_com_admin jerry
$ 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
$ cat /etc/httpd/conf/.allowed_groups_for_example_com
example_com_admin: tom jerry
$ 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
$ httpd -t
$ httpd -D DUMP_VHOSTS
$ systemctl restart httpd