Samba standalone + OpenLDAP

On the web there are many tutorials about setting a Samba server as one’s Domain Controller (DC), but really a few about setting a Standalone Samba server relying on an external OpenLDAP for authentication. Actually quite a simple process, it needs a lot of configuration on both ends, the Samba server and the OpenLDAP one, before it can be functionnal.

This post shows how to set up a Samba 3.6 server to rely on an external OpenLDAP 2.4 server, both being hosted on a CentOS 6.4

The Samba Server

Authorize the use of LDAP system-wide

In order for the Samba server to be able to rely on then OpenLDAP one, the use of LDAP needs to be enabled system-wide. To do so the authconfig configuration needs to be updated the following way

authconfig --enableldap --update

This simply edits the /etc/nsswitch.conf file and append ldap on passwd, shadow, group, netgroup and automount items

Install the samba packages

Simply run

yum install samba samba-common

Note : This article is about Samba 3.6 version and not Samba4. So do install the samba* packages and not the samba4* packages.

Copy and install the Samba schema in the OpenLDAP server

Note : Since those steps need to be done before the smb.conf configuration, this section shows here, even if logically it belongs to “The OpenLDAP server”

By default, the OpenLDAP server doesn’t speak the Samba language. One needs to add samba LDAP schema to it. From the Samba server, once the samba packages installed simply copy the samba.ldif file located at /usr/share/doc/samba-3.6.9/LDAP/samba.ldif to your OpenLDAP cn=schema directory

scp /usr/share/doc/samba-3.6.9/LDAP/samba.ldif user@openldap:/etc/openldap/slapd.d/cn=config/cn=schema

On the OpenLDAP server, the file needs to be renamed with the pattern – cn={X}samba.ldif – where X represents the highest number available + 1. On a default OpenLDAP installation, the highest number available is 11 (cn={11}collective.ldif) thus, the samba.ldif file needs to be renamed cn={12}samba.ldif

Edit the cn={12}samba.ldif file at line 1 and 3 so it look like this

dn: cn={12}samba.ldif
objectClass: olcSchemaConfig
cn: cn={12}samba.ldif

Finally, restart the slapd service so the new schema can be loaded correctly.

The smb.conf

In Samba there are 3 backends storage available per default.

  • smbpasswd – it is deprecated,
  • tdbsam – the one enabled by default.  It relies on a local database of user, filled via the smbpasswd -a command
  • ldapsam –  It relies on an external LDAP directory

To make your standalone Samba server rely on OpenLDAP simply change this chunk of code

security = user
passdb backend = tdbuser

by

security = user
passdb backend = ldapsam:ldap://ldap.serv.er.ip/
ldap suffix = dc=wordpress,dc=com
ldap admin dn = cn=admin,dc=wordpress,dc=com
  • ldap suffix : the suffix of your DIT
  • ldap admin dn : This is optional. If the OpenLDAP server denies anonymous request, then one needs to specify an admin dn entry.  Also if your LDAP tree do not have a SambaDomain entry yet, specifying the ldap admin dn configuration will create it automatically.  If using ldap admin dn, one needs to specify the admin dn password running smbpasswd -W

Save and exit the file, then restart the smb service. After few second one can run net getlocalsid and will be presented with a line looking like

SID for domain SAMBA-SERVER is: S-1-5-21-2844801791-3392433664-1093953107

If you set ldap admin dn in the smb.conf, the SambaDomain was created automatically and net getlocalsid returns this value, if you setted it manually net getlocalsid should return your your SambaDomain informations

Set samba to start automatically at boot time – chkconfig samba on – and the Samba server is all set to receive request from LDAP existing users.

The OpenLDAP server

In order for an OpenLDAP server to be Samba aware, some attributes needs to be added to the appropriate entryies. Make sure the samba schema has been loaded into OpenLDAP, as explained earlier.

SambaDomain

This entry can be automatically created  by the Samba server – if one wants  – and contains general informations about the Samba behavior. The most important information that can be found here is the SID, Security IDentifier for the domain. It will be needed for the configuration of Samba Groups and Users entries.

SambaGroupMapping

This is an auxiliary objectClass  that should be added to all the posixGroup entry that one wants to work with in Samba. It has only  two mandatory attributes, the SambaSID that is a uniqe ID within the SambaDomain ans the SambaGroupType, that define the type or the group.

The SambaSID is composed of the SID + RID

  • SID : From the SambaDomain entry
  • RID : Relative IDentifier, a unique id within the SambaDomain

The defined SambaGroupType are :

  • 2: Domain Group
  • 4: Local Group (alias)
  • 5: Builtin

SambaSamAccount

This is probably the most touchy, yet scriptable part. This is the auxiliary objectClass that should be added to all the posixAccount entry that one wants to work with in Samba. It contains Samba credentials. For Samba to authenticate a LDAP hosted user, the latter needs to have the the following attributes set

  • SambaAcctFlag : define user type (permissions)
  • SambaLMPassword : The LanMan password
  • SambaNTPassword : The NT password
  • SambaPwdLastSet : Timestamp of the last password update
  • SambaSID : The unique identifier within the SambaDomain

To obtain those informations , one can run this script , this needs the perl module Crypt-SmbHash to be installed

Usage : ./script username password

This will give the following outputs

:0:47F9DBCCD37D6B40AAD3B435B51404EE:82E6D500C194BA5B9716495691FB7DD6:[U          ]:LCT-4C18B9FC

  |            LMPassword          |         NTPassword             |   AcctFlags |

For the SambaSID value, refere to the SambaGroupMapping section the same logic apply here.

Once the SambaDomain, SambaGroupMapping and SambaSamAccount applied where it has to, the Samba server is ready to authenticate against the OpenLDAP server

Conclusion

Making a standalone Samba server rely on an external OpenLDAP , is not a difficult process, but it does involve quite a lot of configuration. In this article, neither the IPtables or the SElinux side of things has been adressed, but you should definetly set them up accordingly.  Go ahead add people on your DIT and see how they can access their own Samba Share. QED

Advertisements

Effective backup/recovery process for OpenLDAP

Making sure to never lose any piece of data is a really difficult task. A point-in-time backup (snapshot), in a permanently living and changing environment does not match data loss-less expectations.

In today’s post the focus will be put on OpenLDAP backup/recovery process in order to never lose a bit of data – well maybe the last transaction in case of a power outage.

Most online resources refer to the OpenLDAP backup/recovery process as :

  • For Backup : running a slapcat command and sending the output to a backup server in a cron job
  • For Recovery : getting the last meaningful backup from the backup server and reload it with a slapadd command

Simple isn’t it ? Well it is simple but it simply does not prevent from important data loss. Let’s highlight two cases that demonstrates the limit of this backup plan.

Case 1

Let’s take a moderately busy service that inserts an average of 1,000 new daily users in its dictionary. There are backups made (using the slapcat command) every day at midnight. Now, for some reasons one day at 8.00pm, a hard drive crashes (no RAID) or the filesystem got corrupt or the reason you want to come up with… It is time for recovery. We set a new VM or a new drive, set OpenLDAP again, get back the last meaningful backup and load it with a slapadd command. OpenLDAP server is back to its yesterday state but what about the 900 entries that got inserted today ? Well simply gone. That is why you must have a redundant set of OpenLDAP servers via replication. But replication is not a backup plan in itself.

Case 2

For precaution you set up a master/slave schema (a.k.a Consumer/Provider in the LDAP terms). So even if the main OpenLDAP server crashes you do have an up to date copy. Now since error is human, if an employee inadvertently removes an important set of data, this change will be replicated to all your slaves OpenLDAP servers and the data won’t be recoverable. Recovering yesterday backup will leave you in the same state as Case 1 and data would have been lost.

Solution

Design of an infrastructure effective for backup/recovery process

Design of an infrastructure effective for backup/recovery process

To be able to almost never lose a bit of OpenLDAP data, the infrastructure to deploy will heavily rely on the accesslog module provided by OpenLDAP.

The accesslog overlay is used to keep track of all or selected operations on a particular DIT (the target DIT) by writing details of the operations as entries to another DIT (the accesslog DIT). The accesslog DIT can be searched using standard LDAP queries. Accesslog overlay parameters control whether to log all or a subset of LDAP operations (logops) on the target DIT, to save related information such as the previous contents of attributes or entries (logold and logoldattr) and when to remove log entries from the accesslog DIT

Definition from http://www.zytrax.com/books/ldap/ch6/accesslog.html

Accesslog are mainly used for replication/audit purpose. In the above schema, our slaves will never be master of any other OpenLDAP server, they do use accesslog as a real-time accesslog backup in case the Master OpenLDAP server becomes unavailable for any reason.

Backup Process

As simple as it is described by most resources out there, the backup process will be a slapcat command – run as a cron job – of the needed DIT and their relative Accesslog DIT

#> slapcat -n 2 >  maindit-bk.ldif
#> slapcat -n 3 > maindital-bk.ldif

Recovery Process

This is how the recovery process would work :

  1. Load the last meaningful backup of the needed DIT with the command
  2. Load the accesslog from either the backup or the slave accesslog – which ever fit the most – do not forget to clean the accesslog if you are trying to recover an erroneous action
  3. Set the DIT to be a consumer of the freshly loaded accesslog

Practice

Step 1 : Simulate data loss

#> service slapd stop
#> slapcat -n 2 > maindit-backup.ldif
#> service slapd start
#> ldapadd -x -w 'test' -D 'cn=Manager,dc=domain,dc=com' -f user.ldif
#> service slapd stop
#> slapcat -n 3 > maindit-accesslog-backup.ldif

At this time, there are two backup files :

  • the maindit-backup.ldif that has everything but the last entry
  • the maindit-accesslog-backup.ldif that do have the addition of the user.

Step 2 : Recovering a clean OpenLDAP server

  1. Install a new VM with the appropriate package and configuration [if necesary only]
  2. If you are using a corrupted OpenLDAP server, move all the dbd file of your corrupted database (mv /var/lib/ldap/{yourdbdname}/*.dbd /backup/ldap/{yourdbname})
  3. Enable accesslog and syncprov modules
  4. Reload the needed DIT with slapadd
  5. Create an Accesslog db that will be used as provider
  6. Reload the accesslog db with its backup
  7. Configure syncrepl on the main DIT to be a consumer of the accesslog provider
  8. Restart slapd

At this time you have your OpenLDAP server being back up-to-date data wise and no data has been lost.

Conclusion

Not that simple right ? It needs a bit more than 2 lines of shell scripts. Long time observed behavior is that people/company do backups but do not test recovery. They are tested when the backup plan is created but then left aside and almost never used. Some company, on the other side takes recovery to its extreme and deploy last night backup to production every day. This way the recovery process is well tested and they don’t fear failure. Either way one decides to go, make sure  to always have a data loss-less backup/recovery plan, an up-to-date documentation that goes along with it, and your nagios’ check_ldap plugin up and running. QED


Gitolite + OpenLDAP

While for small project one can easily manage Gitolite authorizaton permissions manually, this task can get really cumbersome as the project grows and different roles get to have different permissions (ie. devel, qa, etc…)

Companies traditionally rely on a centralized system to handle their users, the groups they belong to and as many information as they actually need (or not), one of them being LDAP. The purpose of this post is to see how to make Gitolite rely on informations stored in an LDAP DIT to grant user to perform specific actions on the git repositories.

Prequisite : In order to follow this post you will need to have a working Gitolite installation (v3.0+) and a reachable LDAP directory.

This is the LDIF file that will be used to handle authentication :

dn: cn=john,ou=group,dc=yanisguenane,dc=fr
cn: john
gidNumber: 20001
objectClass: top
objectClass: posixGroup
memberUid: john

dn: cn=jane,ou=group,dc=yanisguenane,dc=fr
cn: jane
gidNumber: 20002
objectClass: top
objectClass: posixGroup
memberUid: jane

dn: cn=devel,ou=group,dc=yanisguenane,dc=fr
cn: devel
gidNumber: 20003
objectClass: top
objectClass: posixGroup
memberUid: john

dn: uid=jane,ou=people,dc=yanisguenane,dc=fr
uid: jane
uidNumber: 10000
gidNumber: 10000
cn: jane
sn: jane
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/jane

dn: uid=john,ou=people,dc=yanisguenane,dc=fr
uid: john
uidNumber: 10001
gidNumber: 10001
cn: john
sn: john
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/john

Make Gitolite LDAP aware

Thought by default Gitolite is LDAP (and any authentication system) unaware, author left an open door for Gitolite to query a specific authentication system one wants. Be it LDAP or any other queriable system.

They are three rules to make that happen :

  • The query to the authentication system should be done via a script
  • The script should take the username as only parameter
  • The script should return a group space separated list the defined user belongs to

An example of an LDAP script can be find here
Note : It should be edited to meet your LDAP DIT configuration, the link posted matches the LDIF used for this post

In order to make Gitolite LDAP aware one needs to edit the file located at $GITOLITE_HOME/.gitolite.rc. Inside the %RC hash, add the following line :

In v3

GROUPLIST_PGM           =>  '/path/to/ldap-query-groups-script',

In v2

$GL_GET_MEMBERSHIPS_PGM => '/path/to/ldap-query-groups-script',
$GL_BIG_CONFIG => 1,

And … done ! Your Gitolite installation is LDAP aware !

How to use it

  • Add the authorized users to Gitolite keychain
  • As you would do with a regular Gitolite setup, you need to add the user to the Gitolie keychain. The name of the public key file (.pub) should match your LDAP username you want to set up.

    Here, they are two ways to deal with it

    • Full LDAP : get the SSH key from querying your LDAP DIT – if they are stored in here for each user
    • Basic : copy the user public key file via your prefered way

  • Define the repositories and permissions
  • Important : Remember that for a given username, the script will return a list of groups the user belongs to. Hence, your repositories configuration should be group based and not user based. A good practice would be that each user has its individual group, so you can grant access to individual user.

    repo test-ldap-devel
        RW+    =    @devel
    
    repo test-ldap-jane
        Rw+    =    @john @jane
    
  • Finally push the chances
  • Once configured to your needs simply push the changes.

Testing

Session 1 – john

john@workstation-john: ssh-keygen -t rsa -b 1024 -N '' -f ~/.ssh/john
john@workstation-john: scp ~/.ssh/john.pub gitolite@gitolite.myserver.com:~/gitolite-admin/keydir
gitolite@gitolite.myserver.com: git add john.pub && git commit -m "john.pub" && git push origin master
john@workstation-john: git clone gitolite@gitolite.myserver.com:test-ldap-devel.git
Cloning into test-ldap-devel...
warning: You appear to have cloned an empty repository.

Session 2 – jane

jane@workstation-jane: ssh-keygen -t rsa -b 1024 -N '' -f ~/.ssh/jane
jane@workstation-jane: scp ~/.ssh/jane.pub gitolite@gitolite.myserver.com:~/gitolite-admin/keydir
gitolite@gitolite.myserver.com: git add jane.pub && git commit -m "jane.pub" && git push origin master
jane@workstation-jane: git clone gitolite@gitolite.myserver.com:test-ldap-devel.git
Cloning into test-ldap-devel...
FATAL: R any test-ldap-devel jane DENIED by fallthru
(or you mis-spelled the reponame)
fatal: The remote end hung up unexpectedly

jane@workstation-jane: git clone gitolite@gitolite.myserver.com:test-ldap-jane.git
Cloning into test-ldap-jane...
warning: You appear to have cloned an empty repository.

Conclusion

As we can see on Jane’s session, her try to clone test-ldap-devel was denied, but the one to clone test-ldap-jane did work. QED