Web Authentication + HTTPS
— print (last updated: Nov 13, 2009) print

Select font size:
Personalize this online document by providing values relevant to you.
  1. Replace the generic LOGIN by the actual login:
       
  2. Replace the generic MACHINE name by its actual name:
       

Authentication with ModAuthSASL

Apache supports a form user-based authentication called basic authentication whereby a browser is prompted for a user name and password upon entry into some directory. Once validated, the validation persists throughout the browser session.

Apache also provides many ways, via loadable modules, to perform this authentication. The one we're interested in here is the authentication module for SASL (Simple Authentication and Security Layer). The reason this is useful is that SASL acts, as indicated, as a layer between the Apache user and the actual authentication. SASL permits authentication via a number of standard mechanisms, making the whole scheme more flexible if we want to change authentication mechanisms.

Install and configure SASL

Start with the installation:
$ sudo apt-get install sasl2-bin
The installation creates a "sasl" group, to which all users who want to use the authentication, must belong, so add yourself:
$ sudo adduser LOGIN sasl
Log out and back in to pick up the group change. Look for "sasl" mentioned here:
$ groups
Edit the file /etc/default/saslauthd and look for the line:
START=no
and change it to:
START=yes
Start the SASL service:
$ sudo /etc/init.d/saslauthd start
To test SASL's effectiveness run this (beware that your password is not hidden!)
$ testsaslauthd -u LOGIN  -p password-for-LOGIN
0: OK "Success"
$ clear
$ history -c                       (OK, I'm paranoid)           

Installation and configuration of mod-auth-sasl

The module is installed and enabled like this:
$ sudo apt-get install libapache2-mod-authn-sasl
$ sudo a2enmod authn_sasl
Like you, Apache's user must belong to the sasl group:
$ sudo adduser www-data sasl
Then restart (not reload) Apache:
$ sudo /etc/init.d/apache2 restart
We're going to show how to protect a specific directory of your web space, ~/public_html/protected. Create the directory and create an empty file within:
$ mkdir ~/public_html/protected
$ touch ~/public_html/protected/empty.html
Then edit the configuration file, /etc/apache2/conf.d/admin.conf, which you used to assign yourself greater Apache priviledges in the Apache + Blog document. Add this content at the end of the file:
<Directory /home/LOGIN/public_html/protected>
  AuthType Basic
  AuthName "Restricted"
  AuthBasicProvider sasl
  AuthBasicAuthoritative On
  AuthSaslPwcheckMethod saslauthd
  Require user LOGIN
</Directory>
Then reload Apache:
$ sudo /etc/init.d/apache2 reload
If you do a web directory listing:
http://localhost/~LOGIN/
The protected directory. is hidden until access is authenticated. Go in:
http://localhost/~LOGIN/protected
It's looking for your login and your password (the password is hidden). Going back to
http://localhost/~LOGIN/
reveals the protected directory in the listing.

Authenticated access for "shell-less" users

It is desireable to permit users to authenticate access to a website without having to give them login shell capabilities. This is easy to achieve by simply creating "shell-less" users, much like system users listed in /etc/passwd. We will make modification for the protected directory.

Create a shell-less user, user1:
$ sudo adduser --no-create-home --shell /bin/false user1
The adduser command prompts for account information. You have to give it a password, let's say "user1foo"; other that that you can ignore the remaining fields by hitting Enter. When you're done, see what happened by compare to your login to user1's login:
$ egrep "^(user1|LOGIN):" /etc/passwd
You'll see that user1 is harmless because it is has no shell login and owns no files on the system. Then, modify /etc/apache/conf.d/admin.conf by changing the line:
Require user LOGIN
to one of these two alternatives:
Require user LOGIN user1
or
Require valid-user
Reload Apache:
$ sudo /etc/init.d/apache2 reload
A test of the effectiveness would be this: In any case, we need only "give out" the login/password information for user1 to allow web access, and this permits no shell access.

Alternative use of .htaccess file

There is an alternative way to create the same effect by creating the file
~/public_html/protected/.htaccess
which contains the current contents of directives above, minus the starting and ending Directory tags. The differences in these approaches are:

HTTPS

HTTPS (Secure HTTP) offers encrypted transmission of information on the web. It is based on encrypted the secure socket layer (SSL) protocol applied to the HTTP. Apache/SSL is called ModSSL because it is represents a module for SSL loaded into the Apache server. The website for information is:
http://www.modssl.org/
The heart of ModSSL is the Apache module mod_ssl.so, which is part of the "common" Apache package in Ubuntu. Other Linux distributions provide a standard configuration setup, but it appears that this must be done by hand in Ubuntu.

Digital certificates

HTTPS (as well as other secure protocols) is based on transmission of a X.509 certificate, commonly known as a digital certificate. The web server administrator creates a public/private key pair, sends the public key to a Certificate Authority (CA) which binds the public key to information about the web server (location, DNS entry, etc.) The binding method hashes the subject information, encrypts it using the CA's private key and attaches the encryption along with information about the CA, encryption algorithm, etc. This total conglomeration of information is the certificate. Click on this link to see an example.

An established CA will charge you for such a certificate. The point is that the CA's information and public key is readily available to common browsers and so it can be validated that this certificate was really issued by the CA. Thus the client can be assured that the server's public is valid.

Alternatively, you can create a self-signed certificate, which means that you can act like a CA and use your own private key to create the certificate. The problem is that standard browsers won't recognize you as a legitimate CA and will through a security warning to the user. The problem is legitimate, because how does the browser actually know that the information contained within is valid if not signed by a known CA. This problem is referred to as the non-repudiation problem, namely, being uncertain that the public key received is the legitimate public key of the intended server.

Create and install a self-signed digital certificate

A self-signed certificate will be sufficient for our purposes, especially since you will be the only user of it. Apache with mod_ssl installed holds a valid (but uninformative) self-signed certificate, but we want to generate a new one to show how it is done and give it a bit more authenticity.

The key pair and certificate that we'll create can be used to secure other services, so we'll create it in a special "safe" place, the directory /root/SSL, and save it for other usages.

We want to do most operations as root, so its easiest to become root shell
# cd /usr/local/etc
# mkdir ssl
# chmod 700 ssl
# cd ssl
The important operations are done by use the openssl operation. Executed without parameters, it functions as a command interpreter:
# openssl
OpenSSL> ...
Alternatively, you can execute openssl commands directly from the shell prefacing them with "openssl".

The following sequence generates a private key, my.key, and a certificate request, my.csr:
OpenSSL> req -new -nodes -out my.csr -keyout my.key

Generating a 1024 bit RSA private key
..............................................++++++
.++++++
writing new private key to 'my.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request...
-----
Country Name (2 letter code) [..]:US
State or Province Name (full name) [..]:Pennsylvania
Locality Name (eg, city) [..]:West Chester
Organization Name (eg, company) [..]:WCU
Organizational Unit Name (eg, section) [..]:Computer Science
Common Name (.. your server's hostname) [..]: MACHINE.cs.wcupa.edu
Email Address []:root@MACHINE.cs.wcupa.edu

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:                     (hit Enter)
An optional company name []:                 (hit Enter)
OpenSSL> quit
Let's see what we've got:
# ls
my.key my.csr
# cat my.key
# cat my.csr
For a real certificate, my.csr would be sent to a Certificate Authority for "signing". Instead, we'll sign it ourselves as follows:
# openssl
OpenSSL> x509 -in my.csr -out my.crt -req -signkey my.key -days 9999
OpenSSL> quit
# ls
my.crt  my.csr  my.key
# cat my.crt
The file my.crt is our self-signed certificate. The two files we need are my.key and my.crt. For extra security, make them read-only for root:
# chmod 600 my.*

Configure the default SSL site

$ sudo a2enmod ssl
$ sudo a2ensite default-ssl
Edit the file /etc/apache2/sites-enabled/default-ssl. Look for the lines:
SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
and change the values to:
SSLCertificateFile    /usr/local/etc/ssl/my.crt
SSLCertificateKeyFile /usr/local/etc/ssl/my.key
Restart apache:
$ sudo /etc/init.d/apache2 restart

Test HTTPS

It makes most sense to test this using the name of your machine from an external source, like one of the lab Fedora clients. The most recent versions of IE and Firefox complain bitterly about self-signed certificate and will tend to dissuade only the most resolute user from going ahead. Use the https URL:
https://MACHINE
You'll get the frightening message:

Secure Connection Failed

blah, blah, blah, ...

Of course, we know who signed it, so take a deep breath and ...
  1. Click the Or you can add an exception... hyperlink.
  2. Click the Add Exception button getting another warning popup.
  3. Click the Get Certificate button.
  4. Click the View button.
  5. Click the Confirm Security Exception button.
You can view the certificate you just permanently accepted by going to:
Edit Preferences Advanced Encryption View Certificates Servers

Force HTTPS for Authentication

At issue is the fact that the name/password information going from browser client to web server is unencrypted. If your server really were public, you would need to protect this by using HTTPS. There are two alternatives:

Disallowing HTTP

The SSLRequireSSL directive in the Directory entry will disallow http. Again, using the protected directory as an example, incorporate it by a simple addition to /etc/apache2/conf.d/admin.conf:
<Directory /home/LOGIN/public_html/protected>
  SSLRequireSSL
  ...
</Directory>

Redirect http to https using rewrite rules

Apache rewrite rules are very complex and can be used to perform a huge variety of web server tasks. Documentation of rewrite rules can be found on Apache's wesite at:
Apache 2.2 mod_rewrite.html     and     Apache 2.2 Rewrite Guide
We must first enable the rewrite module by:
$ sudo a2enmod rewrite
$ /etc/init.d/apache2 reload
Again, edit /etc/apache2/conf.d/admin.conf and enter the following lines at the end: Keep SERVER_NAME literally as it is, do not replace it by your actual server's name. Be careful to use curly brackets in the %{..} expression.
# uncomment the RewriteLog.. lines to help debug rewrite rules. 
# Never leave the RewriteLogLevel at "9" in a production system 
# because it will negatively affect performance.

#RewriteLog  /var/log/apache2/rewrite-log
#RewriteLogLevel  9
<Directory /home/LOGIN/public_html>
  RewriteEngine  on
  RewriteCond    %{HTTPS}  off 
  RewriteCond    %{REMOTE_ADDR}  !^127\.0\.0\.
  RewriteRule    ^protected(/.*)?$  \
                 https://%{SERVER_NAME}/~LOGIN/protected$1 [R,L]
</Directory>
Afterwards, reload Apache and test out:
http://localhost/~LOGIN/protected

Explanation of rewrite usage

Apache rewrite rules use syntax very much like that used in Perl when dealing with regular expressions for matching and substitution. The only so-called "rule" is this (the \ is a line continuation character):
RewriteRule    ^protected(/.*)?$  \
               https://%{SERVER_NAME}/~LOGIN/protected$1 [R,L]
Let's say that we're accessing the URL:
http://MACHINE/~LOGIN/protected/empty.html
is accessed these are in effect:


© Robert M. Kline