Securing a multi-user Apache Web Server

As part of refining my Apache web server which runs multiple sites I’ve create a user account, database account and home folder per site so for example the site example.com has a user account example, a database account example and a web folder located at:

/home/example/public_html

The corresponding Apache VirtualHost for this site is:

<VirtualHost *:80>
        ServerAdmin admin@example.com
        ServerName www.example.com
        ServerAlias example.com
        ErrorLog /var/log/apache2/error.example.com.log
        LogLevel warn
        CustomLog /var/log/apache2/access.example.com.log combined
        DocumentRoot /home/example/public_html
        <IfModule mod_suexec.c>
                SuexecUserGroup example example
        </IfModule>
</VirtualHost>

Previously to ensure PHP scripts worked I had a Bash cron job to loop over all the user’s public_html folders and set the owner on the public_html folder to the apache user www-data.

Not ideal.

So after a few hours of digging I managed to deploy a solution both secure and flexible, allowing users to logon and edit their web pages without permissioning headaches.

Assuming a basic Apache setup first install the Apache suPHP and suEXEC modules:

sudo apt-get install libapache2-mod-suphp apache2-suexec

Enable the modules:

sudo a2enmod suexec
sudo a2enmod suphp

The suPHP module replaces the Apache PHP4 and PHP5 modules. Having both active prevents suPHP from working properly so you’ll need to disable the PHP4 and PHP5 modules:

sudo a2dismod php4
sudo a2dismod php5

Finally you’ll want to set the permissions on the user folder:

find ~/public_html/ -type f -exec chmod 644 {} \;
find ~/public_html/ -type d -exec chmod 755 {} \;

To get this setup even better I’d ideally like to set those permissions to 600 and 700 respectively but that’s a job for tomorrow.

Addendum:

Awesome link which covers much of the above and then some.

 

Openfire Single Sign On (SSO)

I’m a dabbler, I like to dabble.

While most people are happily using Google Talk, Facebook chat, Skype and the like I’m busy playing around with my own chat server, writing plugins for it and seeing if I can get things like Single Sign On (SSO), DNS Service Records and Federation working. It’s time consuming, frustrating at times but ultimately rewarding. One particularly frustrating problem I recently tackled was single sign on with Openfire (a Jabber/XMPP messaging server).

My basic setup likely mirrors most enterprise-y networks:

  • Windows Active Directory Domain Controller with Windows Support Tools installed
  • Openfire 3.8 bound to the Windows DC
  • Windows XP/Windows Terminal Server Clients running Pandion/Pidgin
  • Mac OS X Clients Running Adium

The first step is to ensure that you have a working Windows AD network alongside a working Openfire installation.

  • AD Domain: EXAMPLE.COM
  • AD Host: DC.EXAMPLE.COM
  • Openfire (XMPP) Domain: EXAMPLE.COM
  • Openfire Host: OPENFIRE.EXAMPLE.COM
  • Keytab account: xmpp-openfire

Ensure you have an A and reverse DNS record for your Openfire server and then setup your DNS Service Records for Openfire like so:

_xmpp-client._tcp.example.com. 86400 IN SRV 0 0 5222 openfire.example.com.
_xmpp-server._tcp.example.com. 86400 IN SRV 0 0 5269 openfire.example.com.

With DNS done create two new Active Directory accounts. Account one is for binding the Openfire server to the domain (skip this account if you’ve already bound Openfire to your domain).

Account two is to associate your Service Principal Name (SPN) so Kerberos clients can find and authenticate using SSO with your Openfire server.

On account two check under Account properties that User cannot change password, Password never expires and Do not require Kerberos preauthentication are checked.

On the Windows Domain Controller you’ll now need to create the SPN and keytab. The SPN (Service Principal Name) is used by clients to lookup the name of the Openfire server for SSO. The keytab contains pairs of Service Principals and encrypted keys which allows a service to automatically authenticate against the Domain Controller without being prompted for a password.

Creating the SPN:

I created two records since it seems some clients lookup xmpp/openfire.example.com@EXAMPLE.COM and some look up xmpp/openfire.example.com.

setspn -A xmpp/openfire.example.com@EXAMPLE.COM xmpp-openfire
setspn -A xmpp/openfire.example.com xmpp-openfire

Mapping the SPN to the keytab account xmpp-openfire and when prompted enter the xmpp-openfire password:

ktpass -princ xmpp/openfire.example.com@EXAMPLE.COM -mapuser xmpp-openfire@EXAMPLE.COM -pass * -ptype KRB5_NT_PRINCIPAL

Create the keytab:

I found that the Java keytab didn’t work on my Openfire system in which case I used the Windows ktpass utility to create it. Some users report the converse, so see whichever works for you:

Java keytab generation:

ktab -k xmpp.keytab -a xmpp/openfire.example.com@EXAMPLE.COM

Windows keytab generation:

ktpass -princ xmpp/openfire.example.com@EXAMPLE.COM -mapuser xmpp-openfire@EXAMPLE.COM -pass * -ptype KRB5_NT_PRINCIPAL -out xmpp.keytab

Copy the keytab to your Openfire directory, typically /usr/share/openfire or /opt/openfire. The full path will look like this:

/usr/share/openfire/xmpp.keytab

Configuring Linux for Active Directory

Configure Kerberos

First we need to install ntp, kerberos and samba:

apt-get install ntp krb5-config krb5-user krb5-doc winbind samba

Enter your workgroup name:

eg. EXAMPLE.COM

Configure /etc/krb5.conf

[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
dns_lookup_realm = true
dns_lookup_kdc = true
ticket_lifetime = 24h
forwardable = yes

[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}

Test connection to Active Directory by entering the following commands:

:~# kinit xmpp-openfire@EXAMPLE.COM

Check if the request for the Active Directory ticket was successful using the kinit command

:~# klist

The result of this command should be something like this:

Ticket cache: FILE:/tmp/krb5cc_0
Default principal: xmpp-openfire@EXAMPLE.COM

Valid starting Expires Service principal
07/11/13 21:41:31 07/12/13 07:41:31 krbtgt/EXAMPLE.COM@EXAMPLE.COM
renew until 07/12/14 21:41:31

Join the domain

Configure your smb.conf like so:

#GLOBAL PARAMETERS
[global]
   workgroup = EXAMPLE
   realm = EXAMPLE.COM
   preferred master = no
   server string = Linux Test Machine
   security = ADS
   encrypt passwords = yes
   log level = 3
   log file = /var/log/samba/%m
   max log size = 50
   printcap name = cups
   printing = cups
   winbind enum users = Yes
   winbind enum groups = Yes
   winbind use default domain = Yes
   winbind nested groups = Yes
   winbind separator = +
   idmap uid = 600-20000
   idmap gid = 600-20000
   ;template primary group = "Domain Users"
   template shell = /bin/bash

[homes]
   comment = Home Direcotries
   valid users = %S
   read only = No
   browseable = No

[printers]
   comment = All Printers
   path = /var/spool/cups
   browseable = no
   printable = yes
   guest ok = yes

Join the domain:

:~# net ads join -U administrator

You will be asked to enter the AD Administrator password.

Verify you can list the user’s and groups on the domain:

:~# wbinfo -u
:~# wbinfo -g

Testing the keytab works:

From your Openfire system run the below command:

  kinit -k -t /usr/share/openfire/resources/xmpp.keytab xmpp/openfire.example.com@EXAMPLE.COM -V

You should see:

Authenticated to Kerberos v5

Then create a GSSAPI configuration file called gss.conf in your Openfire configuration folder normally in /etc/openfire or /opt/openfire/conf. Ensure you set the path to your xmpp.keytab file:

com.sun.security.jgss.accept {
    com.sun.security.auth.module.Krb5LoginModule
    required
    storeKey=true
    keyTab="/usr/share/openfire/xmpp.keytab"
    doNotPrompt=true
    useKeyTab=true
    realm="EXAMPLE.COM"
    principal="xmpp/openfire.example.com@EXAMPLE.COM"
    debug=true
    isInitiator=false;
};

Ensure the file is owned by the openfire user.

Stop Openfire and enable GSSAPI by editing your openfire.xml configuration file which is found in the openfire conf directory:

<!-- sasl configuration -->
<sasl>
    <mechs>GSSAPI</mechs>
    <!-- Set this to your Keberos realm name which is usually your AD domain name in all caps. -->
    <realm>EXAMPLE.COM</realm>
    <gssapi>
        <!-- You can set this to false once you have everything working. -->
        <debug>true</debug>
        <!-- Set this to the location of your gss.conf file created earlier -->
        <!-- "/" is used in the path here not "\" even though this is on Windows. -->
        <config>/etc/openfire/gss.conf</config>
        <useSubjectCredsOnly>false</useSubjectCredsOnly>
    </gssapi>
</sasl>

Or add to System Properties:

sasl.gssapi.config /etc/openfire/gss.conf
sasl.gssapi.debug false
sasl.gssapi.useSubjectCredsOnly false
sasl.mechs GSSAPI
sasl.realm EXAMPLE.COM

Restart Openfire