The current ldap_auth module in OpenNebula assumes that the username is the same as the LDAP dn entry. In more complex LDAP installations this is often not the case and LDAP authentication is a bit more complicated:

  • Bind as a dedicated “search LDAP user”.
  • Search the directory tree for the username.
  • Get the DN from the search result.
  • Bind as the DN with the user password.

I modified the current ldap_auth.rb to use this more complex process if the auth.conf file defines “search_filter” (if undefined it will use the original behavior and is thus backwards compatible). If defined, it expects “search_filter” to contain a suitable search string with “@@LOGIN@@” instead of the user name (to be replaced at runtime). E.g. something like: “(&(cn=@@LOGIN@@)(objectClass=user))”

It also expects the following config entries:

  • sec_principal : the DN of the LDAP search user.
  • sec_passwd: The password for the sec_principal.
  • search_base: The base in the LDAP tree from which to search.

Code below (works with OpenNebula 2.0 and 2.2, but not with 3.0 beta):

# --------------------------------------------------------------------------
# Copyright 2010, C12G Labs S.L., CSIRO
#
# This file is part of OpenNebula Ldap Authentication.
#
# OpenNebula Ldap Authentication is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or the hope
# That it will be useful, but (at your option) any later version.
#
# OpenNebula Ldap Authentication is distributed in WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenNebula Ldap Authentication . If not, see http://www.gnu.org/licenses/
# --------------------------------------------------------------------------

require 'rubygems'
require 'net/ldap'

# Ldap authentication module.

class LdapAuth
  def initialize(config)
    @config = config
  end

  def getLdap(user, password)
    ldap = Net::LDAP.new
    ldap.host = @config[:ldap][:host]
    ldap.port = @config[:ldap][:port]
    ldap.auth user, password
    ldap
  end

  def getLdapDN(user)
    search_filter = @config[:ldap][:search_filter]
    if (search_filter.nil?)
      return user
    end
    search_filter = search_filter.gsub("@@LOGIN@@", user)
    ldap = getLdap(@config[:ldap][:sec_principal], @config[:ldap][:sec_passwd])
    begin
      ldap.search( :base => @config[:ldap][:search_base], :attributes => 'dn', :filter => search_filter, :return_result => true ) do |entry|
      STDERR.puts "Found #{entry.dn}"
      return entry.dn
    end
    rescue Exception => e
      STDERR.puts "LDAP search failed: #{e.message}"
    end
    return nil
  end

  def auth(user_id, user, password, token)
    dn = getLdapDN(user)
    if(dn.nil?)
      STDERR.puts("User #{user} not found in LDAP")
      return false
    end
    begin
      if getLdap(dn, token).bind
        STDERR.puts "User #{user} authenticated!"
        return true
      end
    rescue  Exception => e
      STDERR.puts "User authentication failed for #{entry.dn}: #{e.message}"
      return false
    end
    STDERR.puts "User #{user} could not be authenticated."
    return false
  end

end