Howto/Spacenet/Setup LDAP

From SpaceFED
Jump to: navigation, search

Before we begin...

Before I start handing out configuration files.. I just remembered we're all hackerspaces here and we are in this thing to learn. You have arrived here because you have been offered the shortcut and chose to do it right. Well, you're going to hate me for this, but I am going to explain LDAP first.

One thing about LDAP you should know is that it is really really simple and easy to understand, but all the used terms are unknown and therefore it can be quite overwhelming. So let's start with clearing that up.


LDAP is just an indexed directory tree. Everything in it is an object. Every object has attributes. To keep the directory clean and not have happen what happens to the windows registry, not every attribute can go on every object. Attributes are assigned to object classes and you just add objectclasses to every object to define which attributes you wish to use.

That's the easy part. This is what has been haunting you. Every object can be addressed by it's path in the directory, this path is unique. But of course, we do not call this the path, but we call this the Distinguished Name or DN. If you are asked to specify a DN, what they're really saying is "What object would you like to use".

When setting up a connection you need to authenticate, this is done in the 'bind' stage of the connection. You can bind anonymously (not recommended for user databases) or as a user/security object in the database.

The directory can be queried using searches, for this you specify where you want to look, this is called the Base DN. It basically means you'll be searching there, one level below, or the entire subtree, depending on the scope you specify. You can also specify a search filter, with a search filter you specify which attribute values you wish to see. Default search is 'objectclass=*', but you can do fancier filters like "(&(objectclass=posixAccount)(uid=wilco))".


  • DN/Distinguished Name: The path in the directory
  • DC/Domain Component: Domain Component, only really used in the root DN, every part between dots in your REALM name gets one (if you have one dot, that means two dc's)
  • Root DN: The root of your directory, usually this will be dc=YOUR,dc=REALM,dc=TLD
  • Binding: Binding means authenticating
  • Bind DN: The DN of the user object you wish to authenticate as. When starting you'll likely want to use cn=admin,dc=YOUR,dc=REALM,dc=TLD.
  • Bind password: Just the userPassword attribute you set for the object specified at Bind DN
  • Base DN: The path to the root of the subtree you wish to search
  • Search filter: A filter to look for objects in the directory.
  • Schema: The schema defines attributes and objectclasses and also the mappings between them.
  • CN/Common Name: Most common attribute in the directory, used for naming your objects

Setting up a directory

You'll want a directory that can run your radius server for spacenet, but why not go the extra step. You're setting up a user directory, why not also support a samba file server, an SSH shell server and your wiki.. or like at revspace add attributes for your door contact ID-buttons.. all on the same user directory. To do this, you'll need to extend your schema. We'll get to that. Now first install slapd on a debian, you now know what you can fill in. Bear in mind that sometimes debian will not ask all the questions you need, try "DEBIAN_PRIORITY=low apt-get install slapd ldap-utils". Please dpkg-reconfigure --priority=low your slapd if you have made a previous attempt. Also, you'll want to install the ldap-utils package so you can test your directory.

Step 1: Verify your directory

After install, you'll have a very basic directory containing two objects.

prompt:~# ldapsearch -x -b dc=spacefed,dc=net

dn: dc=spacefed,dc=net
objectClass: top
objectClass: dcObject
objectClass: organization
o: SpaceFED
dc: spacefed

# admin,
dn: cn=admin,dc=spacefed,dc=net
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

As you'll see, one is the root DN, the second is the security object with which we can bind to the database.

Step 2: Extend your schema

You can already use your directory, but not yet for freeradius, for this, we need an extra schema, the samba schema. It is common practise to use schema's from other people if the data is the same, so you can share data between your applications. You only create new objectclasses for attributes that you cannot already sanely store in the database. But debian does not supply the samba schema by default (only the old schema which we shall not use in samba-doc).

Add the following to tmpsamba.ldif, then type ldapadd -Y EXTERNAL -H ldapi:// < tmpsamba.ldif

dn: cn=samba,cn=schema,cn=config
objectclass: olcSchemaConfig
cn: samba
olcattributetypes: {1}( NAME 'sambaNTPassword' DESC 
 'MD4 hash of the unicode password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6
olcattributetypes: {2}( NAME 'sambaAcctFlags' DESC '
 Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX
 1.26{16} SINGLE-VALUE )
olcattributetypes: {3}( NAME 'sambaPwdLastSet' DESC 
 'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6. SINGLE-VALUE )
olcattributetypes: {4}( NAME 'sambaPwdCanChange' DES
 C 'Timestamp of when the user is allowed to update the password' EQUALITY i
olcattributetypes: {5}( NAME 'sambaPwdMustChange' DE
 SC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTA
olcattributetypes: {6}( NAME 'sambaLogonTime' DESC '
 Timestamp of last logon' EQUALITY integerMatch SYNTAX
 121.1.27 SINGLE-VALUE )
olcattributetypes: {7}( NAME 'sambaLogoffTime' DESC 
 'Timestamp of last logoff' EQUALITY integerMatch SYNTAX SINGLE-VALUE )
olcattributetypes: {8}( NAME 'sambaKickoffTime' DESC
  'Timestamp of when the user will be logged off automatically' EQUALITY int
olcattributetypes: {9}( NAME 'sambaBadPasswordCount'
  DESC 'Bad password attempt count' EQUALITY integerMatch SYNTAX
 .1466. SINGLE-VALUE )
olcattributetypes: {10}( NAME 'sambaBadPasswordTime'
  DESC 'Time of the last bad password attempt' EQUALITY integerMatch SYNTAX SINGLE-VALUE )
olcattributetypes: {11}( NAME 'sambaLogonHours' DESC
  'Logon Hours' EQUALITY caseIgnoreIA5Match SYNTAX
 1.26{42} SINGLE-VALUE )
olcattributetypes: {12}( NAME 'sambaHomeDrive' DESC 
 'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTA
olcattributetypes: {13}( NAME 'sambaLogonScript' DES
 C 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX
 121.1.15{255} SINGLE-VALUE )
olcattributetypes: {14}( NAME 'sambaProfilePath' DES
 C 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX{255} SINGLE-VALUE )
olcattributetypes: {15}( NAME 'sambaUserWorkstations
 ' DESC 'List of user workstations the user is allowed to logon to' EQUALITY
  caseIgnoreMatch SYNTAX{255} SINGLE-VALUE )
olcattributetypes: {16}( NAME 'sambaHomePath' DESC '
 Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX{128} )
olcattributetypes: {17}( NAME 'sambaDomainName' DESC
  'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYN
 TAX{128} )
olcattributetypes: {18}( NAME 'sambaMungedDial' DESC
  'Base64 encoded user parameter string' EQUALITY caseExactMatch SYNTAX 1.3.{1050} )
olcattributetypes: {19}( NAME 'sambaPasswordHistory'
  DESC 'Concatenated MD5 hashes of the salted NT passwords used on this acco
 unt' EQUALITY caseIgnoreIA5Match SYNTAX{32} )
olcattributetypes: {20}( NAME 'sambaSID' DESC 'Secur
 ity ID' EQUALITY caseIgnoreIA5Match SUBSTR caseExactIA5SubstringsMatch SYNT
olcattributetypes: {21}( NAME 'sambaPrimaryGroupSID'
  DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.{64} SINGLE-VALUE )
olcattributetypes: {22}( NAME 'sambaSIDList' DESC 'S
 ecurity ID List' EQUALITY caseIgnoreIA5Match SYNTAX
 1.1.26{64} )
olcattributetypes: {23}( NAME 'sambaGroupType' DESC 
 'NT Group Type' EQUALITY integerMatch SYNTAX 
olcattributetypes: {24}( NAME 'sambaNextUserRid' DES
 C 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX
 4.1.1466. SINGLE-VALUE )
olcattributetypes: {25}( NAME 'sambaNextGroupRid' DE
 SC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6. SINGLE-VALUE )
olcattributetypes: {26}( NAME 'sambaNextRid' DESC 'N
 ext NT rid to give out for anything' EQUALITY integerMatch SYNTAX
 .1.1466. SINGLE-VALUE )
olcattributetypes: {27}( NAME 'sambaAlgorithmicRidBa
 se' DESC 'Base at which the samba RID generation algorithm should operate' 
olcattributetypes: {28}( NAME 'sambaShareName' DESC 
 'Share Name' EQUALITY caseIgnoreMatch SYNTAX 
olcattributetypes: {29}( NAME 'sambaOptionName' DESC
  'Option Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SY
 NTAX{256} )
olcattributetypes: {30}( NAME 'sambaBoolOption' DESC
  'A boolean option' EQUALITY booleanMatch SYNTAX
olcattributetypes: {31}( NAME 'sambaIntegerOption' D
 ESC 'An integer option' EQUALITY integerMatch SYNTAX
 21.1.27 SINGLE-VALUE )
olcattributetypes: {32}( NAME 'sambaStringOption' DE
 SC 'A string option' EQUALITY caseExactIA5Match SYNTAX
 .121.1.26 SINGLE-VALUE )
olcattributetypes: {33}( NAME 'sambaStringListOption
 ' DESC 'A string list option' EQUALITY caseIgnoreMatch SYNTAX
 466. )
olcattributetypes: {34}( NAME 'sambaTrustFlags' DESC
  'Trust Password Flags' EQUALITY caseIgnoreIA5Match SYNTAX
 . )
olcattributetypes: {35}( NAME 'sambaMinPwdLength' DE
 SC 'Minimal password length (default: 5)' EQUALITY integerMatch SYNTAX 1.3. SINGLE-VALUE )
olcattributetypes: {36}( NAME 'sambaPwdHistoryLength
 ' DESC 'Length of Password History Entries (default: 0 => off)' EQUALITY in
olcattributetypes: {37}( NAME 'sambaLogonToChgPwd' D
 ESC 'Force Users to logon for password change (default: 0 => off, 2 => on)'
olcattributetypes: {38}( NAME 'sambaMaxPwdAge' DESC 
 'Maximum password age, in seconds (default: -1 => never expire passwords)' 
olcattributetypes: {39}( NAME 'sambaMinPwdAge' DESC 
 'Minimum password age, in seconds (default: 0 => allow immediate password c
 hange)' EQUALITY integerMatch SYNTAX SINGLE-V
olcattributetypes: {40}( NAME 'sambaLockoutDuration'
  DESC 'Lockout duration in minutes (default: 30, -1 => forever)' EQUALITY i
olcattributetypes: {41}( NAME 'sambaLockoutObservati
 onWindow' DESC 'Reset time after lockout in minutes (default: 30)' EQUALITY
  integerMatch SYNTAX SINGLE-VALUE )
olcattributetypes: {42}( NAME 'sambaLockoutThreshold
 ' DESC 'Lockout users after bad logon attempts (default: 0 => off)' EQUALIT
olcattributetypes: {43}( NAME 'sambaForceLogoff' DES
 C 'Disconnect Users outside logon hours (default: -1 => off, 0 => on)' EQUA
olcattributetypes: {44}( NAME 'sambaRefuseMachinePwd
 Change' DESC 'Allow Machine Password changes (default: 0 => off)' EQUALITY 
olcattributetypes: {45}( NAME 'sambaClearTextPasswor
 d' DESC 'Clear text password (used for trusted domain passwords)' EQUALITY 
 octetStringMatch SYNTAX )
olcattributetypes: {46}( NAME 'sambaPreviousClearTex
 tPassword' DESC 'Previous clear text password (used for trusted domain pass
 words)' EQUALITY octetStringMatch SYNTAX )
olcobjectclasses: {0}( NAME 'sambaSamAccount' DESC 'S
 amba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MA
 Y ( cn $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTi
 me $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustC
 hange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sam
 baLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sa
 mbaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $ sambaBadPasswordCo
 unt $ sambaBadPasswordTime $ sambaPasswordHistory $ sambaLogonHours ) )
olcobjectclasses: {1}( NAME 'sambaGroupMapping' DESC 
 'Samba Group Mapping' SUP top AUXILIARY MUST ( gidNumber $ sambaSID $ samba
 GroupType ) MAY ( displayName $ description $ sambaSIDList ) )
olcobjectclasses: {2}( NAME 'sambaTrustPassword' DES
 C 'Samba Trust Password' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaN
 TPassword $ sambaTrustFlags ) MAY ( sambaSID $ sambaPwdLastSet ) )
olcobjectclasses: {3}( NAME 'sambaTrustedDomainPassw
 ord' DESC 'Samba Trusted Domain Password' SUP top STRUCTURAL MUST ( sambaDo
 mainName $ sambaSID $ sambaClearTextPassword $ sambaPwdLastSet ) MAY sambaP
 reviousClearTextPassword )
olcobjectclasses: {4}( NAME 'sambaDomain' DESC 'Samba
  Domain Information' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaSID )
  MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithm
 icRidBase $ sambaMinPwdLength $ sambaPwdHistoryLength $ sambaLogonToChgPwd 
 $ sambaMaxPwdAge $ sambaMinPwdAge $ sambaLockoutDuration $ sambaLockoutObse
 rvationWindow $ sambaLockoutThreshold $ sambaForceLogoff $ sambaRefuseMachi
 nePwdChange ) )
olcobjectclasses: {5}( NAME 'sambaUnixIdPool' DESC 'P
 ool for allocating UNIX uids/gids' SUP top AUXILIARY MUST ( uidNumber $ gid
 Number ) )
olcobjectclasses: {6}( NAME 'sambaIdmapEntry' DESC 'M
 apping from a SID to an ID' SUP top AUXILIARY MUST sambaSID MAY ( uidNumber
  $ gidNumber ) )
olcobjectclasses: {7}( NAME 'sambaSidEntry' DESC 'Str
 uctural Class for a SID' SUP top STRUCTURAL MUST sambaSID )
olcobjectclasses: {8}( NAME 'sambaConfig' DESC 'Samb
 a Configuration Section' SUP top AUXILIARY MAY description )
olcobjectclasses: {9}( NAME 'sambaShare' DESC 'Samba
  Share Section' SUP top STRUCTURAL MUST sambaShareName MAY description )
olcobjectclasses: {10}( NAME 'sambaConfigOption' DES
 C 'Samba Configuration Option' SUP top STRUCTURAL MUST sambaOptionName MAY 
 ( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringLis
 toption $ description ) )

Step 3: Secure your directory

Make sure that you secure your directory, here's a nice checklist for you :)

Run on port 636, allow only LDAPS

This can be fixed in /etc/default/slapd. Add only ldapi://, ldap://localhost:389 and ldaps:// to SLAPD_SERVICES.

Install a certificate

Save below to file, adjust the paths and run ldapmodify -Y EXTERNAL -H ldapi:// < file.ldif

dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/server-key.pem
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/server-cert.pem

Disallow LDAP v2

This was a dpkg configure question. You should have said no to the question "Allow v2 binds". Anyway, how to fix will be provided here. FIXME

Disable anonymous binds

Add the following attributes to your configuration object (ldapmodify -Y EXTERNAL -H ldapi:// < file.ldif):

dn: cn=config
changetype: modify
add: olcDisallows
olcDisallows: bind_anon

dn: olcDatabase={-1}frontend,cn=config
changetype: modify
add: olcRequires
olcRequires: authc

Protect the sambaNTPassword attribute from being read

Add the following ACL attribute to your database (ldapmodify -Y EXTERNAL -H ldapi:// < file.ldif):

dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to attrs=sambaNTPassword
  by dn="cn=admin,dc=YOUR,dc=REALM,dc=TLD" write
  by dn="cn=read,dc=YOUR,dc=REALM,dc=TLD" read
  by self write
  by * none

Step 4: Add a samba domain Unix ID pool

You need a Unix ID pool from which to allocate uidNumbers and gidNumbers... and for the samba users you need a samba SID pool to allocate SIDs. Add the following 2 objects to your realm (adjust first, use ldapadd -x -D cn=admin,dc=YOUR,dc=REALM,dc=TLD -W < file.ldif)

dn: sambaDomainName=YOURORGANISATION,dc=YOUR,dc=REALM,dc=TLD
sambaSID: S-1-5-21-2919752157-891696647-4172528126 (please change a few numbers in the last 3 groups)
sambaAlgorithmicRidBase: 1000
objectClass: sambaDomain
sambaMinPwdAge: 0
sambaMinPwdLength: 5
sambaLogonToChgPwd: 0
sambaForceLogoff: -1
sambaRefuseMachinePwdChange: 0
sambaLockoutThreshold: 0
sambaMaxPwdAge: -1
sambaNextRid: 20100
sambaPwdHistoryLength: 0

dn: cn=NextFreeUnixId,dc=YOUR,dc=REALM,dc=TLD
objectClass: inetOrgPerson
objectClass: sambaUnixIdPool
cn: NextFreeUnixId
sn: NextFreeUnixId
gidNumber: 10000
uidNumber: 10000

Step 5: Install phpldapadmin and start filling it up

This one is optional, but it is a graphical representation, so it may help you understand what you are doing. Just install phpldapadmin, point it to the directory and you're done (this can be done in "/etc/phpldapadmin/config.php"). Make sure that you create "Samba accounts" and do not *EVER* use the sambaLMPassword (I removed it from the schema above to prevent this).

phpldapadmin version has a bug with making a "Samba: Group Mapping", this is needed for making "Samba accounts". If you install version you won't have that problem. (The version can be found in the "debian squeeze repository")

You might also want to check out JXplorer and Luma is also looking very promising. You may also want to manage your users using smbldap-tools, so you can add users with smbldap-useradd, etc. FIXME: Add links

Step 6: Make sure it makes sense

You can create (objectclass=)OrganizationalUnits objects, under which you can create your users. I suggest your create a unit for your users (OU=Users) and a unit for your groups (OU=Groups).

Step 7: Create a group

FIXME.. but you can do this in phpldapadmin already

Step 8: Create your users

A user will kind of look like this, use SSHA hashes:

dn: uid=wilco,ou=People,dc=YOUR,dc=REALM,dc=TLD
uid: wilco
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
objectClass: sambaSamAccount
uidNumber: 12581
gidNumber: 10407
sambaSID: S-1-5-21-2969752157-892696647-4271518216-101187
cn: Wilco Baan Hofman
displayName: Wilco Baan Hofman
gecos: Wilco Baan Hofman,,,
givenName: Wilco
sn: Baan Hofman
mail: wilco@your.realm.tld
description: Some random guy
loginShell: /bin/bash
homeDirectory: /home/wilco
sambaPwdLastSet: 1262705210
sambaNTPassword: NTHASHHERE
userPassword: {SSHA}HashOfThePasswordHere
sambaPrimaryGroupSID: S-1-5-21-2969752157-892696647-4271518216-513
sambaAcctFlags: [U          ]

Finally: Connect everything to your directory

You can now connect Freeradius to your LDAP directory. But you can now also connect NSS and PAM to add SSH accounts or mail accounts, etc. And you can now also connect your wiki, a samba file server and whatever else you can think of. Just point it to the directory, supply bind DN, bind password and base DN and it will likely just work. Be sure to check out the AllowGroup directive for your SSH server.