lighttpd and SSL client certificates

Software lighttpd, security, SSL

I recently configured my lighttpd server to enable authentication based on SSL client certificates on a private subdomain. Here’s a quick how-to.

  1. Configure OpenSSL:

    $ cp /etc/ssl/openssl.cnf ./
    $ $EDITOR openssl.cnf  # Edit "dir"
    $ mkdir certs
    $ echo "00" > serial
    $ echo "00" > crlnumber
    $ touch index.txt
    
  2. Create the CA:

    $ openssl genrsa -out ca.key 2048  # Generate a RSA-2048 private key
    $ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt  # Generate a certificate from the private key
    
  3. Create a SSL client certificate:

    $ openssl genrsa -out client.key 2048  # Generate a RSA-2048 private key
    $ openssl req -config openssl.cnf -new -key client.key -out client.csr  # Generate a certificate from the private key
    $ openssl ca -batch -config openssl.cnf -days 3650 -in client.csr -out client.crt -keyfile ca.key -cert ca.crt -policy policy_anything  # Sign the certificate using the CA
    $ openssl pkcs12 -export -in client.crt -inkey client.key -certfile ca.crt -out client.p12  # Convert the certificate to PKCS#12 for browser support
    
  4. Import the certificates in a browser. For example in Firefox:

    1. Go to Settings, “Advanced”, “Certificates” tab, and click “Show certificates”
    2. In “Authorities”, click “Import” and select ca.crt
    3. In “Your certificates”, click “Import” and select client.p12
  5. Configure lighttpd.

    1. If you haven’t configured SSL in lighttpd yet, do it.

    2. Add your newly generated ca.crt as the ssl.ca-file for the private subdomain:

      $ cat /etc/lighttpd/lighttpd.conf | grep ssl.ca-file
          ssl.ca-file = "/etc/lighttpd/ca-client.pem"
      
    3. Enable client certificate verification in lighttpd:

      $HTTP["host"] == "my-ssl-domain" {
          ssl.ca-file = "/etc/lighttpd/ca-client.pem"
          ssl.verifyclient.activate = "enable"
          ssl.verifyclient.enforce = "enable"
          ssl.verifyclient.username = "SSL_CLIENT_S_DN_CN"
      }
      
      • ssl.verifyclient.activate activates the client certificate verification
      • ssl.verifyclient.enforce allows to enforce valid client certificates: if enabled, the SSL connections will fail if no client certificate is provided. If disabled, client certificates will be optional.
      • ssl.verifyclient.username defines which field of the client certificate will be stored in the request environment (which can then be used by scripts run by the server): SSL_CLIENT_S_DN_CN for the Common Name, SSL_CLIENT_S_DN_emailAddress for the email address, etc.
      • more options are described in the official docs.
  6. Restart lighttpd and visit your site: you should be asked which certificate you want to use to connect.

Comments

Join the conversation by sending an email. Your comment will be added here and to the public inbox after moderation.

Luis Jimenez

Which one is the ca-client.pem?

Schnouki

It’s the ca.crt created at step 2.

Patrik Iselind

What’s in your openssl.cnf? After creating my own OpenSSL based CA and creating my certificates i can still not use them as they where not signed by any CA in my target system’s trusted CA bundle. Did you just inject your own CA’s certificate into the trusted CA bundle of your target system?

Craig

What about ssl.verifyclient.depth?

Craig

Mine didn’t work at first but fortunately I left the browser there hanging. Then when I moved to another desktop/workspace to do something else, I accidentally hit the windows key which caused every window in the workspace to miniaturize, and then I saw the Firefox prompt window waiting for me to press OK. To summarize, Firefox put up a query box on another workspace than the browser, and hid it under existing windows. I believe that’s called security by obfuscation.

Schnouki

@Craig: No idea. Never used it, and I don’t even use lighttpd anymore…