LDAP on Cumulus Linux Using Server 2008 Active Directory

This article provides an example of how to set up LDAP authentication and authorization on Cumulus Linux using Active Directory. It applies to any Debian Wheezy-based server or switch.

The method described in the article applies ONLY to Windows Server 2008. This method does not work as well with Windows Server 2012.

Prerequisites

  • Windows 2008 Active Directory
  • Cumulus Linux 2.x

Active Directory Setup

  • Base DN: CN=Cumulus Admin, DC=RTP, DC=Example, DC=test
  • Base OU: OU=Support, DC=RTP, DC=Example, DC=Test
  • Users and Groups: This example has three users that are allowed to access a Cumulus Linux switch. Two are in the senior network engineer group, one in the junior engineering group. The last user, Mark Smith, does not have access to the switches because he is not a member of the cumuluslnxadm group.
group layout of users

AD group layout of users

layout of users and groups in AD schema

Layout of users and groups in AD schema

Goals

  • Configure LDAP authentication without changing the default AD schema. (There is no need to apply the UNIX identities in Active Directory Domain Services.)
  • Support nested groups. The following should work:
    nested group diagram
  • User directories are automatically created the first time an authorized user logs into the switch.
  • Senior network admins via sudo can run any command on the switch.
  • Junior network admins can:
    • Log in and access any non-privileged command.
    • Run sudo lldpctl without prompting for a password. lldpctl is an important network troubleshooting tool, but requires root access.
    • Use all ip commands (iproute2 commands).
    • Not access FRR via vtysh or non-modal CLI like cl-ospf.

Configure Cumulus Linux

  1. Update PAM to support automatic creation of directories (optional).

    This information is from the Debian wiki. The alternative is to set the “homeDirectory” attribute for users in AD to “/home/cumulus”, and all users map to the pre-defined user account.

    Create the following file /usr/share/pam-configs/mkhomedir:

    Name: Create home directory during login
    Default: yes
    Priority: 900
    Session-Type: Additional
    Session:
            required        pam_mkhomedir.so umask=0022 skel=/etc/skel
    
  2. Add Debian backports to apt sources list (optional).

    libnss-ldapd version 0.9 and higher is needed for nested group support. For Wheezy, and thus Cumulus Linux 2.x, include Debian Wheezy backports to the apt sources list.

  3. Seed debconf with appropriate values (recommended).

    Seeding debconf is helpful because it adds most of the important configuration values in the various applications and prevents the apps from asking questions during the installation.

    While it is possible to execute apt-get in non-interactive mode, you will need to change most of the necessary config later.

    For libnss-ldapd, there are CPU architecture-specific questions to answer.

    root# apt-get install debconf-utils
    
    root# debconf-set-selections <<'zzzEndOfFilezzz'
    
    # LDAP database user. Leave blank will be populated later!
    # This way of setting binddn and bindpw doesn't seem to work.
    # So have to manually do it. But interactive apt-get mode works.
    nslcd nslcd/ldap-binddn  string 
    
    # LDAP user password. Leave blank!
    nslcd nslcd/ldap-bindpw   password 
    
    # LDAP server search base:
    nslcd nslcd/ldap-base string  ou=support,dc=rtp,dc=example,dc=test
    
    # LDAP server URI. Using ldap over ssl.
    nslcd nslcd/ldap-uris string  ldaps://myadserver.rtp.example.test
    
    # New to 0.9. restart cron, exim and others libraries without asking
    nslcd libraries/restart-without-asking: boolean true
    
    # LDAP authentication to use:
    # Choices: none, simple, SASL
    # Using simple because its easy to configure. Security comes by using LDAP over SSL
    # keep /etc/nslcd.conf 'rw' to root for basic security of bindDN password
    nslcd nslcd/ldap-auth-type    select  simple
    
    # Don't set starttls to true
    nslcd nslcd/ldap-starttls     boolean false
    
    # Check server's SSL certificate:
    # Choices: never, allow, try, demand
    nslcd nslcd/ldap-reqcert      select  never
    
    # Choices: Ccreds credential caching - password saving, Unix authentication, LDAP Authentication , Create  home directory on first time login, Ccreds credential caching - password checking
    # This is where "mkhomedir" pam config is activated that allows automatic creation of home directory
    libpam-runtime        libpam-runtime/profiles multiselect     ccreds-save, unix, ldap, mkhomedir , ccreds-check
    
    # for internal use; can be preseeded
    man-db        man-db/auto-update      boolean true
    
    # Name services to configure:
    # Choices: aliases, ethers, group, hosts, netgroup, networks, passwd, protocols, rpc, services,  shadow
    libnss-ldapd  libnss-ldapd/nsswitch   multiselect     group, passwd, shadow
    libnss-ldapd  libnss-ldapd/clean_nsswitch     boolean false
    
    
    zzzEndOfFilezzz
    

    Verify the debconf configuration:

    root# debconf-show nslcd
    * nslcd/ldap-bindpw: (password omitted)
      nslcd/restart-failed:
      nslcd/ldap-sasl-authcid:
      nslcd/restart-services:
      nslcd/ldap-sasl-realm:
    * nslcd/ldap-sasl-mech:
    * nslcd/ldap-starttls: false
    * nslcd/ldap-base:  ou=support,dc=rtp,dc=example,dc=test
      nslcd/ldap-sasl-krb5-ccname: /var/run/nslcd/nslcd.tkt
    * nslcd/ldap-auth-type: none
      nslcd/disable-screensaver:
    * libraries/restart-without-asking: true
    * nslcd/ldap-reqcert: never
      nslcd/ldap-cacertfile: /etc/ssl/certs/ca-certificates.crt
      nslcd/ldap-sasl-authzid:
    * nslcd/ldap-uris:  ldaps://myadserver.rtp.example.test
    * libraries/restart-without-asking:: true
      nslcd/ldap-sasl-secprops:
      nslcd/xdm-needs-restart:
    * nslcd/ldap-binddn:
    
    root# debconf-show libpam-runtime
      libpam-runtime/override: false
      libpam-runtime/profiles: mkhomedir, unix, ldap, capability, auditd
      libpam-runtime/title:
      libpam-runtime/conflicts:
      libpam-runtime/no_profiles_chosen:
    
    root# debconf-show man-db
    * man-db/auto-update: true
      man-db/install-setuid: false
    
    root# debconf-show libnss-ldapd
    * libnss-ldapd/nsswitch:     group, passwd, shadow
    * libnss-ldapd/clean_nsswitch: false
    
  4. Install LDAP apps.

    libnss-ldapd from the Wheezy main repo does not support nested groups. Load the libnss-ldapd app from backports to get the latest available version.

    Do not install libnss-ldap. This is the wrong app, and this explains why.

    root# apt-get -t wheezy-backports install libnss-ldapd ldap-utils
    
  5. Get the Active Directory Domain SID.

    Use the dsquery command on the Windows server to get the domain SID. For more information, read this good reference on Object SIDs.

    dsquery output
  6. Configure nslcd.conf.

    Update /etc/nslcd.conf with the following:

    • Specify the Bind DN path.
    • Specify the Bind DN password.
    • Configure LDAP search filter. The filter states that only users in the cumuluslnxadm group are allowed log in. The search filter performs the nested group lookup.
    • Configure Unix to AD mappings.
    • Configure nested group support.

    Here is the /etc/nslcd.conf config with comments:

    # /etc/nslcd.conf
    # nslcd configuration file. See nslcd.conf(5)
    # for details.
    
    # The user and group nslcd should run as.
    uid nslcd
    gid nslcd
    
    # The location at which the LDAP server(s) should be reachable.
    # If planning to use secure LDAP, use fqdn that matches name 
    # in the server SSL cert sent during negotiation. Debugging 
    # nslcd will show this error if found
    uri ldap://myadserver.rtp.example.test
    
    # The search base that will be used for all queries.
    base ou=support,dc=rtp,dc=example,dc=test
    
    # The LDAP protocol version to use.
    #ldap_version 3
    
    # The DN to bind with for normal lookups.
    # defconf-set-selections doesn't seem to set this. so have to manually set this.
    binddn CN=cumulus admin,CN=Users,DC=rtp,DC=example,DC=test
    bindpw 1Q2w3e4r!
    
    # The DN used for password modifications by root.
    #rootpwmoddn cn=admin,dc=example,dc=com
    
    # SSL options
    #ssl off (default)
    # default tls setting of 'demand' requires CA public cert
    #  for AD server be defined in "tls_cacertfile"
    # use PEM format for CA cert, not DER format.
    tls_reqcert never
    #tls_cacertfile /etc/ssl/certs/ca-certificates.crt
    
    # The search scope.
    #scope sub
    
    # Add nested group support
    # Supported in nslcd 0.9 and higher.
    # default wheezy install of nslcd supports on 0.8. wheezy-backports has 0.9
    nss_nested_groups yes
    
    # Mappings for Active Directory
    # (replace the SIDs in the objectSid mappings with the value for your domain)
    # "dsquery * -filter (samaccountname=testuser1) -attr ObjectSID" 
    pagesize 1000
    referrals off
    idle_timelimit 1000
    
    # Do not allow uids lower than 100 to login (aka Administrator)
    # not needed as pam already has this support
    #  nss_min_uid 1000
    
    # This filter says to get all users who are part of the cumuluslnxadm group. Supports nested groups search filter.
    # Example, mary is part of the snrnetworkadm group which is part of cumuluslnxadm group
    # Ref: http://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx (LDAP_MATCHING_RULE_IN_CHAIN)
    filter passwd (&(Objectclass=user)(!(objectClass=computer))(memberOf:1.2.840.113556.1.4.1941:=cn=cumuluslnxadm,ou=groups,ou=support,dc=rtp,dc=example,dc=test))
    map    passwd uid           sAMAccountName
    map    passwd uidNumber     objectSid:S-1-5-21-1391733952-3059161487-1245441232
    map    passwd gidNumber     objectSid:S-1-5-21-1391733952-3059161487-1245441232
    map    passwd homeDirectory "/home/$sAMAccountName"
    map    passwd gecos         displayName
    map    passwd loginShell    "/bin/bash"
    
    # Filter for any AD group or user in the baseDN. the reason for filtering for the
    # user to make sure group listing for user files don't say '<user> <gid>'. instead will say '<user> <user>'
    # So for cosmetic reasons..nothing more.
    filter group (&(|(objectClass=group)(Objectclass=user))(!(objectClass=computer)))
    map    group gidNumber     objectSid:S-1-5-21-1391733952-3059161487-1245441232
    map    group cn            sAMAccountName
    
  7. Enable Secure LDAP (optional).

    • Copy the LDAP CA certificate (in PEM format) to the directory, specified in the nslcd.conf tls_cacertfile option, on the client device. Ensure the file specified matches the filename copied.
    • Set the tls_reqcert option to demand.
    • Change the URI to use ldaps://…
  8. Confirm that LDAP authentication works.

    • Restart the nscd service using service nscd restart to clear the cache. This can cache a mistake and then you can spend hours troubleshooting a problem that doesn’t exist. Alternatively, clear the nscd cache for passwd and group using the commands:

      root# nscd --invalidate=group
      root# nscd --invalidate=passwd
      
    • Run nslcd in debug mode.

      root# service nslcd stop
      root# nslcd -d
      
    • When it works, it should output as follows: user list: The UID and GIDs are the last 4 digits of the user’s AD object SID. Notice that “Mark Smith” the support manager is not there. This is because the user is not part of the cumuluslnxadm group.

       root# getent passwd
       root:x:0:0:root:/root:/bin/bash
       daemon:x:1:1:daemon:/usr/sbin:/bin/sh
       ...
       ..........
       _lldpd:x:105:107::/var/run/lldpd:/bin/false
       joechen:*:1121:1121:Joe Chen:/home/joechen:/bin/bash
       marydiho:*:1124:1124:Mary Diho:/home/marydiho:/bin/bash
       obidia:*:1128:1128:Obi Dia:/home/obidia:/bin/bash
      

      group list: GIDs of each user should be present. Because nested group support is enabled, all nested groups are mapped to the right users.

       root# getent group
       ..
       .....
       ssh:x:105:
       nslcd:x:106
       _lldpd:x:107:
       joechen:*:1121:
       marydiho:*:1124:
       marksmith:*:1125:
       obidia:*:1128:
       jnrnetworkadm:*:1118:obidia
       cumuluslnxadm:*:1120:obidia,marydiho,joechen
       snrnetworkadm:*:1126:marydiho,joechen
      
  9. Set up command authorization (optional).

    To do authorization with nslcd, creating entries on the client device will match against the LDAP information cached. To accomplish this, it is required to install the sudo-ldap package from the Wheezy repo.

    In this example, we will create the following authorizations:

    • Senior engineers can run any privileged and non-privileged command.
    • Junior engineers can run lldpctl without a password. By default, lldpctl requires root access.

    Add two files to the /etc/sudoers.d directory, one for senior engineers and the other for junior engineers. This can be configured in the /etc/sudoers file, but this keeps things modular.

    /etc/sudoers.d/snrnetworkadm:

    # allow any senior engineer root privilege after they type their password
    %snrnetworkadm ALL=(ALL) ALL
    

    /etc/sudoers.d/jnrnetworkadm:

    Cmnd_Alias LLDP_CMDS = /usr/sbin/lldpctl
    
    # allow any junior network engineer to run lldpctl without root permission
    %jnrnetworkadm ALL=(ALL) NOPASSWD: LLDP_CMDS
    

Automate It with Ansible

An Ansible Playbook is available on GitHub that automates the configuration shown in this article.