The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Tuesday, April 06, 2010

Client certificates in Apache

I've been spending an inordinate amount of time playing around with Apache, starting with mod_lua, which lead me to reconfigure both Apache 2.0.52 (which came installed by default) and Apache 2.3.5 (compiled from source, because mod_lua is only available for Apache 2.3) so they could run at the same time. This lead to using IPv6 because I have almost two dozen “sites” running locally (and as I've found, it's just as easy to use IPv6 addresses as it is IP addresses, although the DNS PTR records get a little silly).

This in turn lead to installing more secure sites locally, because I can (using TinyCA makes it trivial actually), and this lead to a revamp of my secure site (note: the link takes you to an unsecure page—the actual secure site uses a certificate signed by my “certificate authority” which means you'll get a warning which can be avoided by installing the certificate from the unsecure site). And from there, I learned a bit more about authenticating with client certificates. Specifically, isolating certain pages to just individual users.

So, to configure client side certificates, you need to create a client certificate (easy with TinyCA as it's an option when signing a request) and install it in the browser. You then need to install the certificate authority certificate so that Apache can use it to authenticate against the client certificate (um … yeah). In the Apache configuration file, just add:

SSLCACertificateFile	/path/to/ca.crt

Then add the appropriate mod_ssl options to the secure site (client-side authentication only works with secure connections). For example, here's my configuration:

<VirtualHost 66.252.224.242:443>
  ServerName	secure.conman.org
  DocumentRoot	/home/spc/web/sites/secure.conman.org/s-htdocs
  
  # ...

  <Directory /home/spc/web/sites/secure.conman.org/s-htdocs/library>
    SSLRequireSSL
    SSLRequire %{SSL_CLIENT_S_DN_O}  eq "Conman Laboratories" \
           and %{SSL_CLIENT_S_DN_OU} eq Clients"
    SSLVerifyClient	require
    SSLVerifyDepth	5
  </Directory>
</VirtualHost>

And in order to protect a single file with more stringent controls (and here for example, is my bookmarks file):

<VirtualHost 66.252.224.242:443>

  # ... 

  <Location /library/bookmarks.html>
    SSLRequireSSL
    SSLRequire %{SSL_CLIENT_S_DN_O}  eq "Conman Laboratories" \
           and %{SSL_CLIENT_S_DN_CN} eq "Sean Conner"
    SSLVerifyClient	require
    SSLVerifyDepth	5
  </Location>
</VirtualHost>

The <Files> directive in Apache didn't work—I suspect because the <Directory> directive is processed first and it allows anybody from the unit “Clients” access and thus any <Files> directives are ignored, whereas <Location> directives are processed before <Directory> directives, and thus anyone not me is denied access to my bookmarks.

Now, I just need to figure out what to do about some recent updates to Apache, since I have some “old/existing clients” to support (namely, Firefox 2 on my Mac, which I can't upgrade because I'm stuck at 10.3.9 on the system, because the DVD player is borked … )


IF IT AIN'T BROKE DON'T FIX IT!!!!!!!!!

Sigh.

I can fix the client certificate issue if I install the latest Apache 2.2, which has the SSLInsecureRenegotiation option, but that requires OpenSSL 0.9.8m or higher (and all this crap because of a small bug in OpenSSL). So, before mucking with my primary server, I decide to test this all out on my home computer (running the same distribution of Linux as my server).

Well, I notice that OpenSSL just came out with verion 1.0.0, so I decide to snag that version. Download, config (what? No configure still?), make and make install, watch it go into the wrong location (XXXXXX I wanted it in /usr/local/lib/ no /usr/local/openssl/lib!), rerun config with other options and get it where I want it.

Okay.

And hey, while I'm here, might as well download the latest OpenSSH and get that working. I nuke the existing OpenSSH installtion (yum remove openssh) since I won't need it, and start the configure, make and make install, but the configure script bitches about the version of zlib installed (XXXX! I know RedHat is conservative about using the latest and greatest, but come on! It's been five years since version 1.2.3 came out! Sheesh!) so before I can continue, I must do the download, configure, make and make install dance for zlib. Once that is out of the way …

checking OpenSSL header version... 1000000f (OpenSSL 1.0.0 29 Mar 2010)
checking OpenSSL library version... 90701f (OpenSSL 0.9.7a Feb 19 2003)
checking whether OpenSSL's headers match the library... no
configure: error: Your OpenSSL headers do not match your
library. Check config.log for details.
If you are sure your installation is consistent, you can disable the check
by running "./configure --without-openssl-header-check".
Also see contrib/findssl.sh for help identifying header/library mismatches.

Oh XXXXXX XXXX

IT'S IN /usr/local/lib YOU USELESS SCRIPT!

But alas, no amount of options or environment variables work. And no, while I might be willing to debug mod_lua, I am not about to debug a 31,000 line shell script. Might as well reinstall the OpenSSH package …

[root]lucy:~>yum install openssh
Setting up Install Process
Setting up repositories
Segmentation fault (core dumped)

Um … what?

[root]lucy:~>yum install openssh
Setting up Install Process
Setting up repositories
Segmentation fault (core dumped)

What the XXXX?

Oh please oh please oh please don't tell me that yum just assumes you have OpenSSH installed …

Okay, where is this program dying?

[root]lucy:/tmp>gdb /usr/bin/yum core.3783 
GNU gdb Red Hat Linux (6.3.0.0-1.132.EL4rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"..."/usr/bin/yum": not in
executable format: File format not recognized

Core was generated by /usr/bin/python /usr/bin/yum search zlib'.
Program terminated with signal 11, Segmentation fault.
#0  0x007ff3a3 in ?? ()
(gdb) 

Oh … it's Python.

Um ‥ wait a second …

It's … Python! It's a script!

WHAT THE XXXX?

What did I do to cause the Python interpreter to crash?

Aaaaaaaaaaaaaaaaaaaaaaaaaah!

Okay, I managed to find some RPMs of OpenSSH to install. That didn't fix yum.

Okay, don't panic.

Obviously, it's something I've done that caused this.

The only things I've done is to install up libraries in /usr/local/lib.

Okay, keep any programs from loading up anything from /usr/local/lib. That's easy enough—I justed edited /etc/ld.so.conf to remove that directory, and ran ldconfig. Try it again.

Okay, yum works!

And through a process of elimination, I found the culprit—zlib! Apparently, the version of Python I have doesn't like zlib 1.2.4.

Sheesh!

Okay, yes, I bring ths upon myself for not running the latest and greatest. I don't update continously because that way lies madness—things just breaking (in fact, the last thing I did upgrade, which was OpenSSL on my webserver the other day, broke functionality I was using, which prompted this whole mess in the first place!). At least I was able to back out the changes I made, but I have to keep this in mind:

IF IT AIN'T BROKE DON'T FIX IT!!!!!


Write Apache modules quickly in Lua

I really like mod_lua, even in its alpha state. In less than five minutes I had a webpage that would display a different quote each time it was referenced. I was able to modify the Lua based qotd, changing:

QUOTESFILE = "/home/spc/quotes/quotes.txt"
quotes     = {}

do
  local eoln = "\r\n"
  local f    = io.open(QUOTESFILE,"r")
  local s    = ""

  for line in f:lines() do
    if line == "" then
      -- each quote is separated by a blank link
      if #s < 512 then
        table.insert(quotes,s)
      end
      s = ""
    else
      s = s .. line .. eoln
    end
  end

  f:close()
end

math.randomseek(os.time())

function main(socket)
  socket:write(quotes[math.random(#quotes)])
end

to

QUOTESFILE = "/home/spc/quotes/quotes.txt"
quotes     = {}

do
  local eoln = "\r\n"
  local f    = io.open(QUOTESFILE,"r")
  local s    = ""

  for line in f:lines() do
    if line == "" then
      -- each quote is separated by a blank link
      if #s < 512 then
        table.insert(quotes,s)
      end
      s = ""
    else
      s = s .. line .. eoln
    end
  end

  f:close()
end

math.randomseek(os.time())

function handler(r)
  r.content_type = "text/plain"
  r:puts(quotes[math.random(#quotes)])
end

(you can see, it didn't take much), and adding

LuaMapHandler /quote.html /home/spc/web/lua/lib/quote.lua

to the site configuration (what you don't see is the only other line you need, LuaRoot), reload Apache and I now have webpage backed by Lua.

And from there, it isn't much to add some HTML to the output, but it should be clear that adding Apache modules in Lua isn't that hard.

What did take me by surprise is that there's no real way to do the heavy initialization just once. That bit of reading in the quotes file? It's actually done for every request—mod_lua just compiles the code and keeps the compiled version cached and for each request, runs the compiled code. It'd be nice if there was a way to do some persistent initialization once (a feature I use in the current mod_litbook), but as written, mod_lua doesn't have support for that.

I also haven't see any action on my bug report—not a good sign.

I'm wondering if I might not have to pick up the ball mod_lua and run with it …

Obligatory Picture

[It's the most wonderful time of the year!]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: http://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

http://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2021 by Sean Conner. All Rights Reserved.