How do I configure apache to accept a client ssl certificate (if present) or authenticate using ldap (if the cert is absent)?

12,957

Solution 1

I've figured out a way of doing this for both Apache 2.2 and 2.4, will need some final cleanup for whatever your needs are though, but the tricky bit should be done (or at least done enough for anyone else to figure out where to go from here).

Both configurations result in the same end goal: a client/user may supply a certificate, if none is provided they'll be prompted for LDAP credentials.

You'll need to enable mod_ssl and mod_authnz_ldap. For Apache 2.2 you'll also need to enable mod_rewrite and mod_alias.

Apache 2.4 introduces a general purpose If/ElseIf/Else directive which simplifies this task greatly: http://httpd.apache.org/docs/current/mod/core.html#if

I'm not an Apache guru, so there may be something horribly wrong with what I've done, but it does seem to achieve the state purpose.

Apache 2.2:

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
    ServerName Dummy-testing-kbd
    ServerAdmin webmaster@localhost

    # Normal HTTPS Server Certificate Config
    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/server.key
    # End HTTPS Config

    DocumentRoot /var/www

    Alias /ldap /
    <Location /ldap/>
        # LDAP Authentication Config
        AuthType Basic
        AuthBasicProvider ldap
        AuthzLDAPAuthoritative on
        AuthName "Password Protected. Enter your AD Username and Password"
        AuthLDAPURL "ldaps://ldaps-auth.mydomain.com/OU=People,DC=mydomain"
        Require valid-user
        # End LDAP
    </Location>

    <Location />
        # Client Cert Config
        SSLRequireSSL
        SSLCACertificateFile /etc/ssl/ca/private/ca.crt
        SSLVerifyClient optional
        SSLVerifyDepth 2

        # Populate REMOTE_USER with the value from the client certificate
        SSLUserName SSL_CLIENT_S_DN_CN
        # End Client Cert Config

        # Hacky way to use an internal redirect to force LDAP authentication if the certificate didn't populate the REMOTE_USER variable
        RewriteEngine on
        RewriteCond %{REMOTE_USER} ^$
        RewriteRule (.*) /ldap/$1 [L]
    </Location>

    ErrorLog ${APACHE_LOG_DIR}/cert_or_ldap_error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel debug

    CustomLog ${APACHE_LOG_DIR}/cert_or_ldap_access.log combined
</VirtualHost>
</IfModule>

Apache 2.4:

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
    ServerName Dummy-testing-kbd2
    ServerAdmin webmaster@localhost

    # Normal HTTPS Server Certificate Config
    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/server.key
    # End HTTPS Config

    # Client Cert Config - setup Certificate Authority
    SSLCACertificateFile /etc/ssl/ca/private/ca.crt
    # End Client Cert Config

    DocumentRoot /var/www
    <Location />
        # Client Cert Config
        SSLRequireSSL
        SSLVerifyClient optional
        SSLVerifyDepth 2

        # Populate REMOTE_USER with the value from the client certificate
        SSLUserName SSL_CLIENT_S_DN_CN
        # End Client Cert Config

        # Configuring LDAP:
        # If no REMOTE_USER is defined (by the certificate) then do LDAP authentication
        <If "-z %{REMOTE_USER}">
            AuthType Basic
            AuthBasicProvider ldap
            AuthName "Password Protected. Enter your AD Username and Password"
            AuthLDAPURL "ldaps://ldaps-auth.mydomain.com/OU=People,DC=mydomain"
            Require valid-user
        </If>
        # End LDAP
    </Location>

    ErrorLog ${APACHE_LOG_DIR}/cert_or_ldap_error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel debug

    CustomLog ${APACHE_LOG_DIR}/cert_or_ldap_access.log combined
</VirtualHost>
</IfModule>

Solution 2

This is somewhat of a guess (I don't have any easy way to try this) but perhaps some combination of SSLOptions +FakeBasicAuth and Satisfy Any will see you through?

Share:
12,957

Related videos on Youtube

jmwood051
Author by

jmwood051

Updated on September 18, 2022

Comments

  • jmwood051
    jmwood051 over 1 year

    I have an Apache server that serves up mercurial repositories and it currently authenticates using ldap credentials.

    I want to permit a single user (to start with) to use a SSL client certificate, with all remaining users still able to use the ldap credentials authentication method.

    I have looked through Stack Overflow and other wider (google) searches but can not find information/guidance on how to set this up.

    The following vhost config only allow the client cert through. I comment out the three statemens related to SSL-client; ladp will work.

    <VirtualHost hg.mydomain.com:80>
    
        ServerName hg.mydomain.com:80
        RedirectMatch permanent ^(.*)$ https://hg.mydomain.com$1
    
    </VirtualHost>
    
    <VirtualHost hg.mydomain.com:443>
    
            ServerName hg.mydomain.com:443
            # ServerAdmin [email protected]
            DocumentRoot "/var/hg"
            CustomLog /var/log/httpd/hg-access.log combined
            ErrorLog /var/log/httpd/hg-error.log
    
            ScriptAliasMatch        ^/(.*)        /var/hg/hgweb.cgi$1
      # SSL Stuff...
            SSLEngine on
            SSLCipherSuite AES+HIGH:3DES+HIGH:RC4:!MD5:!EXPORT:!SSLv2:!aNULL:!eNULL:!KRB5
    
            # Server Certificate:
            SSLCertificateFile ssl/hg.mydomain.com.crt
            # Server Private Key:
            SSLCertificateKeyFile ssl/hg.mydomain.com.key
            # SSL Protocol Adjustments:
            BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
    
      <Directory /var/hg>
        Options ExecCGI FollowSymlinks
        AddHandler cgi-script .cgi
    
        AllowOverride AuthConfig
             Order deny,allow
            Allow from all
    
        #SSL-Client Statements
             SSLVerifyClient optional
             SSLVerifyDepth  1
             SSLRequire   %{SSL_CLIENT_S_DN_CN}  eq "robotuser"
    
         AuthName "Developers"
             AuthBasicProvider ldap
             # AuthLDAPEnabled On
             AuthLDAPUrl ldaps://ldap.applied.sec/dc=applied,dc=sec?uid
            AuthzLDAPAuthoritative On
            Require valid-user
      </Directory>
    
        # Taken from http://www.pmwiki.org/wiki/Cookbook/CleanUrls#samedir
        # Used at http://ggap.sf.net/hg/
        RewriteEngine On
        #write base depending on where the base url lives
        #RewriteBase /hg
        RewriteRule ^$ hgweb.cgi  [L]
        # Send requests for files that exist to those files.
        RewriteCond %{REQUEST_FILENAME} !-f
        # Send requests for directories that exist to those directories.
        RewriteCond %{REQUEST_FILENAME} !-d
        # Send requests to hgweb.cgi, appending the rest of url.
        RewriteRule (.*) /hgweb.cgi/$1  [QSA,L]
    
        Include repos.d/repos.*.conf
    


    It seems like I need to somehow create an alias for the directory block and then apply logic to check for the presence/absence of the client certificate. I do not know how to do that.

    If there are other ways to accimplish this. I would love the hear about it.