Netbox AD Authentication – Command Line Parameters Work

active-directorycentosldap

I have set up netbox on a CentOS server, and am trying to get it to authenticate against our AD servers. No matter what I try, the server merely logs this line:

Authentication failed for my_user: user DN/password rejected by LDAP server.

No indication whether it is the bind DN or the user DN that is being rejected.

To start with, I can connect with the netbox super user, browse and enter data. So the installation is functional. I followed the documentation at netbox installation doc, chapter 5 to the letter to enable LDAP authentication. This is my (obfuscated) configuration:

###################################
### LDAP server
###################################
import ldap

# Server URI - ldaps port is 696
AUTH_LDAP_SERVER_URI = "ldaps://ad-ldap.my.domain:696"

AUTH_LDAP_CONNECTION_OPTIONS = {
    ldap.OPT_REFERRALS: 0
}

AUTH_LDAP_BIND_DN = "CN=netbox,OU=Users,DC=My,DC=Domain"
AUTH_LDAP_BIND_PASSWORD = "********"

LDAP_IGNORE_CERT_ERRORS = True

###################################
### User Authentication
###################################
from django_auth_ldap.config import LDAPSearch

AUTH_LDAP_USER_SEARCH = LDAPSearch("OU=Users,DC=My,DC=Domain",
                                    ldap.SCOPE_SUBTREE,
                                    "(sAMAccountName=%(user))")

AUTH_LDAP_USER_DN_TEMPLATE = "None"

AUTH_LDAP_USER_ATTR_MAP = {
    "first_name": "givenName",
    "last_name": "sn",
    "email": "mail",
    "uid": "samaccountname"
}

###################################
### User groups for permissions
###################################
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType

AUTH_LDAP_GROUP_SEARCH = LDAPSearch("OU=Sec - Tree,OU=Groups,DC=My,DC=Domain", ldap.SCOPE_SUBTREE,
                                    "(objectClass=group)")
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()

AUTH_LDAP_REQUIRE_GROUP = "CN=G_Netbox_Users,OU=Sec - Tree,OU=Groups,DC=My,DC=Domain"

AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    "is_active": "CN=G_Netbox_RO,OU=Sec - Tree,OU=Groups,DC=My,DC=Domain",
    "is_staff": "CN=G_Netbox_RW,OU=Sec - Tree,OU=Groups,DC=My,DC=Domain",
    "is_superuser": "CN=G_Netbox_Admins,OU=Sec - Tree,OU=Groups,DC=My,DC=Domain"
}

AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_MIRROR_GROUPS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600

# LDAP Logging
import logging, logging.handlers
logfile = "/var/log/netbox/django-ldap-debug.log"
my_logger = logging.getLogger('django_auth_ldap')
my_logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
logfile, maxBytes=1024 * 500, backupCount=5)
my_logger.addHandler(handler)

What I've tried:

  • change the server name in the uri to a non-existant fqdn, to demonstrate that it indeed does connect to the LDAP server – it does.
  • use ldap instead of ldaps – no effect
  • try on the command line with open-ldap tool ldapsearch with the same bind user, password and base DN as configured for netbox. I also tried binding with the user DN that was returned – both succeeded.
ldapsearch -H ldaps://ad-ldap.my.domain:696 -D "CN=netbox,OU=Users,DC=My,DC=Domain" -w '*******' -b "OU=Users,DC=My,DC=Domain" "(sAMAccountName=my_user)" dn

ldapsearch -H ldaps://ad-ldap.my.domain:696 -D "CN=my user name,OU=Users,DC=My,DC=Domain" -w '*******' -b "OU=Users,DC=My,DC=Domain" "(sAMAccountName=my_user)" cn

  • change the (very) strong password for the netbox user to one with only letters and digits – no effect
  • use a wider base DN in AUTH_LDAP_USER_SEARCH, i.e. removing OUs (my actual base has a number) – no effect

Google also does not help me any further.. I am at a loss, it should work according to the documentation, everything looks right. What am I missing here?

Best Answer

In case anyone else hits this problem, I'll post my solution here.

The root cause is this ambiguous statement in the documentation:

When using Windows Server 2012, AUTH_LDAP_USER_DN_TEMPLATE should be set to None.

We interpreted this as to set the value of this variable to "None", thinking that Netbox/Django needed that to skip using a template (we're no python programmers).

Since Django-auth-ldap only emits user-friendly 1-line warnings and hides all LDAP errors, I had to resort to binding to plain text LDAP and packet sniffing with Wireshark to find out that Django did not in fact bind with the configured Bind DN, but rather attempted a direct bind with the string "None" as the DN and the user's password. Obviously that failed.

Removing the AUTH_LDAP_USER_DN_TEMPLATE definition from the ldap_config.py altogether solved it.